detail view, linked list, layout tweaks
This commit is contained in:
61
client/src/collections/strapiPoleElementsLoader.mjs
Normal file
61
client/src/collections/strapiPoleElementsLoader.mjs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { defineCollection, z } from "astro:content";
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
|
// Define a custom content collection that loads data from Strapi
|
||||||
|
const strapiPoleElementsLoader = defineCollection({
|
||||||
|
// Async loader function that fetches data from Strapi API
|
||||||
|
loader: async () => {
|
||||||
|
// Get Strapi URL from environment variables or fallback to localhost
|
||||||
|
const BASE_URL = import.meta.env.STRAPI_URL || "http://localhost:1337";
|
||||||
|
const path = "/api/elements";
|
||||||
|
const url = new URL(path, BASE_URL);
|
||||||
|
|
||||||
|
// Build query parameters using qs to populate cover image data
|
||||||
|
url.search = qs.stringify({
|
||||||
|
populate: {
|
||||||
|
mainImage: {
|
||||||
|
fields: ["url", "alternativeText"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch articles from Strapi
|
||||||
|
const poleElementsData = await fetch(url.href);
|
||||||
|
const { data }= await poleElementsData.json();
|
||||||
|
|
||||||
|
// Transform the API response into the desired data structure
|
||||||
|
return data.map((item) => ({
|
||||||
|
id: item.id.toString(),
|
||||||
|
name: item.name,
|
||||||
|
title: item.name,
|
||||||
|
description: item.description,
|
||||||
|
createdAt: item.createdAt,
|
||||||
|
updatedAt: item.updatedAt,
|
||||||
|
publishedAt: item.publishedAt,
|
||||||
|
mainImage: {
|
||||||
|
id: Number(item.mainImage.id),
|
||||||
|
documentId: item.mainImage.documentId,
|
||||||
|
url: item.mainImage.url,
|
||||||
|
alternativeText: item.mainImage.alternativeText,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
// Define the schema for type validation using Zod
|
||||||
|
schema: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
createdAt: z.string(),
|
||||||
|
updatedAt: z.string(),
|
||||||
|
publishedAt: z.string(),
|
||||||
|
mainImage: z.object({
|
||||||
|
id: z.number(),
|
||||||
|
documentId: z.string(),
|
||||||
|
url: z.string(),
|
||||||
|
alternativeText: z.string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default strapiPoleElementsLoader;
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
---
|
|
||||||
// BigCard component - configurable card with link
|
|
||||||
interface Props {
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { url } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="max-w-4xl mx-auto">
|
|
||||||
<a href={url} class="block group">
|
|
||||||
<div class="bg-gradient-to-br from-purple-600 to-pink-600 rounded-2xl shadow-2xl overflow-hidden transform transition-all duration-300 group-hover:scale-105 group-hover:shadow-3xl">
|
|
||||||
<div class="p-12 text-center text-white">
|
|
||||||
<!-- Icon -->
|
|
||||||
<div class="mb-6">
|
|
||||||
<svg class="w-24 h-24 mx-auto text-white/90" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Title -->
|
|
||||||
<h2 class="text-4xl font-bold mb-4 group-hover:text-white transition-colors">
|
|
||||||
Pole Elements
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<!-- Description -->
|
|
||||||
<p class="text-xl text-white/90 mb-8 max-w-2xl mx-auto leading-relaxed">
|
|
||||||
Explore a comprehensive collection of pole dance elements, techniques, and combinations.
|
|
||||||
From basic spins to advanced tricks, discover everything you need to master pole sport.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- CTA Button -->
|
|
||||||
<div class="inline-flex items-center space-x-2 bg-white/20 backdrop-blur-sm rounded-full px-8 py-4 group-hover:bg-white/30 transition-all duration-300">
|
|
||||||
<span class="text-lg font-semibold">Explore Elements</span>
|
|
||||||
<svg class="w-5 h-5 transform group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Decorative elements -->
|
|
||||||
<div class="absolute top-4 right-4 w-16 h-16 bg-white/10 rounded-full"></div>
|
|
||||||
<div class="absolute bottom-8 left-8 w-8 h-8 bg-white/10 rounded-full"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
@@ -18,7 +18,8 @@ const BASE_URL = getStrapiBaseUrl();
|
|||||||
<div {...otherProps}>
|
<div {...otherProps}>
|
||||||
{
|
{
|
||||||
elements.map((poleElement) => (
|
elements.map((poleElement) => (
|
||||||
<article class="flex items-center bg-white rounded-lg shadow-lg overflow-hidden">
|
<a href={`/elements/${poleElement.id}`} class="block">
|
||||||
|
<article class="flex items-center bg-white rounded-lg shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-200">
|
||||||
<img
|
<img
|
||||||
src={getStrapiMedia(
|
src={getStrapiMedia(
|
||||||
poleElement.data.mainImage.url,
|
poleElement.data.mainImage.url,
|
||||||
@@ -33,6 +34,7 @@ const BASE_URL = getStrapiBaseUrl();
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
</a>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,64 +1,6 @@
|
|||||||
import { defineCollection, z } from "astro:content";
|
import strapiPoleElementsLoader from "./collections/strapiPoleElementsLoader.mjs";
|
||||||
import qs from "qs";
|
|
||||||
|
|
||||||
// Define a custom content collection that loads data from Strapi
|
|
||||||
const strapiPoleElementsLoader = defineCollection({
|
|
||||||
// Async loader function that fetches data from Strapi API
|
|
||||||
loader: async () => {
|
|
||||||
// Get Strapi URL from environment variables or fallback to localhost
|
|
||||||
const BASE_URL = import.meta.env.STRAPI_URL || "http://localhost:1337";
|
|
||||||
const path = "/api/elements";
|
|
||||||
const url = new URL(path, BASE_URL);
|
|
||||||
|
|
||||||
// Build query parameters using qs to populate cover image data
|
|
||||||
url.search = qs.stringify({
|
|
||||||
populate: {
|
|
||||||
mainImage: {
|
|
||||||
fields: ["url", "alternativeText"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch articles from Strapi
|
|
||||||
const poleElementsData = await fetch(url.href);
|
|
||||||
const { data }= await poleElementsData.json();
|
|
||||||
|
|
||||||
// Transform the API response into the desired data structure
|
|
||||||
return data.map((item) => ({
|
|
||||||
id: item.id.toString(),
|
|
||||||
name: item.name,
|
|
||||||
title: item.name,
|
|
||||||
description: item.description,
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
updatedAt: item.updatedAt,
|
|
||||||
publishedAt: item.publishedAt,
|
|
||||||
mainImage: {
|
|
||||||
id: Number(item.mainImage.id),
|
|
||||||
documentId: item.mainImage.documentId,
|
|
||||||
url: item.mainImage.url,
|
|
||||||
alternativeText: item.mainImage.alternativeText,
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
// Define the schema for type validation using Zod
|
|
||||||
schema: z.object({
|
|
||||||
id: z.string(),
|
|
||||||
name: z.string(),
|
|
||||||
title: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
createdAt: z.string(),
|
|
||||||
updatedAt: z.string(),
|
|
||||||
publishedAt: z.string(),
|
|
||||||
mainImage: z.object({
|
|
||||||
id: z.number(),
|
|
||||||
documentId: z.string(),
|
|
||||||
url: z.string(),
|
|
||||||
alternativeText: z.string(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Export the collection for use in Astro pages
|
// Export the collection for use in Astro pages
|
||||||
export const collections = {
|
export const collections = {
|
||||||
strapiPoleElementsLoader,
|
poleElements: strapiPoleElementsLoader,
|
||||||
};
|
};
|
||||||
@@ -9,7 +9,9 @@ const { title = "Just a title", description = "Adescription" } = Astro.props;
|
|||||||
<Head {title} {description} />
|
<Head {title} {description} />
|
||||||
<body>
|
<body>
|
||||||
<Navigation />
|
<Navigation />
|
||||||
|
<div class="container max-w-4xl mx-auto p-4">
|
||||||
<slot />
|
<slot />
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import PoleElementsList from "../components/PoleElementsList.astro";
|
|||||||
import { getStrapiBaseUrl } from "../config/strapi";
|
import { getStrapiBaseUrl } from "../config/strapi";
|
||||||
|
|
||||||
// Fetch all posts from Strapi using Astro's content collection
|
// Fetch all posts from Strapi using Astro's content collection
|
||||||
const strapiPoleElements = await getCollection("strapiPoleElementsLoader");
|
const strapiPoleElements = await getCollection("poleElements");
|
||||||
// Get Strapi URL from global config
|
// Get Strapi URL from global config
|
||||||
const BASE_URL = getStrapiBaseUrl();
|
const BASE_URL = getStrapiBaseUrl();
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Pole Elements" description="Pole Elements">
|
<Layout title="Pole Elements" description="Pole Elements">
|
||||||
<div class="container mx-auto p-4">
|
|
||||||
<!-- Main heading -->
|
<!-- Main heading -->
|
||||||
<h1 class="text-3xl font-bold mb-8">
|
<h1 class="text-3xl font-bold mb-8">
|
||||||
Pole Elements
|
Pole Elements
|
||||||
@@ -24,5 +23,4 @@ const BASE_URL = getStrapiBaseUrl();
|
|||||||
id="pole-elements-list"
|
id="pole-elements-list"
|
||||||
data-testid="pole-elements"
|
data-testid="pole-elements"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
20
client/src/pages/elements/[...id].astro
Normal file
20
client/src/pages/elements/[...id].astro
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
import { getCollection } from 'astro:content';
|
||||||
|
import MardownContent from '../../components/MardownContent.astro';
|
||||||
|
import Layout from '../../layouts/Layout.astro';
|
||||||
|
|
||||||
|
// 1. Genera una nueva ruta para cada entrada de colección
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const poleElements = await getCollection('poleElements');
|
||||||
|
return poleElements.map(entry => ({
|
||||||
|
params: { id: entry.id }, props: { entry },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// 2. Para tu plantilla, puedes obtener la entrada directamente de la prop
|
||||||
|
const { entry } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout title={entry.data.title} description={entry.data.description}>
|
||||||
|
<h1>{entry.data.title}</h1>
|
||||||
|
<MardownContent content={entry.data.description} />
|
||||||
|
</Layout>
|
||||||
Reference in New Issue
Block a user