reworked astro collections

This commit is contained in:
2025-06-22 05:28:44 +02:00
parent 459873f0e5
commit 9628eea874
16 changed files with 381 additions and 169 deletions

View File

@@ -1,87 +0,0 @@
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"],
},
},
});
try {
// Fetch articles from Strapi
const poleElementsData = await fetch(url.href);
if (!poleElementsData.ok) {
throw new Error(`Failed to fetch data from Strapi: ${poleElementsData.status} ${poleElementsData.statusText}`);
}
const response = await poleElementsData.json();
const { data } = response;
// Check if data is null or undefined
if (!data) {
throw new Error("No data received from Strapi API - the response was null or undefined");
}
// Ensure data is an array
const dataArray = Array.isArray(data) ? data : [data];
// Transform the API response into the desired data structure
return dataArray
.filter(item => item !== null && item !== undefined) // Filter out null/undefined items
.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: item.mainImage ? {
id: Number(item.mainImage.id) || 0,
documentId: item.mainImage.documentId || "",
url: item.mainImage.url || "",
alternativeText: item.mainImage.alternativeText || "",
} : {
id: 0,
documentId: "",
url: "",
alternativeText: "",
}
}));
} catch (error) {
console.error("Error loading pole elements from Strapi:", error);
return [];
}
},
// 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;

View File

@@ -1,7 +1,7 @@
---
// Import necessary components and utilities
import MarkdownComponent from "./MardownContent.astro";
import { getStrapiMedia } from "../utils/strapi";
import { getStrapiMedia } from "../lib/strapi";
import { getStrapiBaseUrl } from "../config/strapi";
import type { HTMLAttributes } from "astro/types";
@@ -18,19 +18,20 @@ const BASE_URL = getStrapiBaseUrl();
<div {...otherProps}>
{
elements.map((poleElement) => (
console.log(poleElement),
<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
src={getStrapiMedia(
poleElement.data.mainImage.url,
poleElement.mainImage.url,
BASE_URL,
)}
alt={poleElement.data.mainImage.alternativeText}
alt={poleElement.mainImage.alternativeText}
class="w-24 h-24 object-cover flex-shrink-0"
/>
<div class="p-4">
<h2 class="text-xl font-bold">
{poleElement.data.name}
{poleElement.name}
</h2>
</div>
</article>

View File

@@ -1,6 +0,0 @@
import strapiPoleElementsLoader from "./collections/strapiPoleElementsLoader.mjs";
// Export the collection for use in Astro pages
export const collections = {
poleElements: strapiPoleElementsLoader,
};

3
client/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
interface ImportMetaEnv {
readonly STRAPI_URL: string;
}

View File

@@ -0,0 +1,15 @@
export default interface PoleElement {
id: string;
name: string;
title: string;
description: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
mainImage: {
id: number;
documentId: string;
url: string;
alternativeText: string;
};
}

68
client/src/lib/strapi.ts Normal file
View File

@@ -0,0 +1,68 @@
interface Props {
endpoint: string;
query?: Record<string, string>;
wrappedByKey?: string;
wrappedByList?: boolean;
}
/**
* Fetches data from the Strapi API
* @param endpoint - The endpoint to fetch from
* @param query - The query parameters to add to the url
* @param wrappedByKey - The key to unwrap the response from
* @param wrappedByList - If the response is a list, unwrap it
* @returns
*/
export default async function fetchApi<T>({
endpoint,
query,
wrappedByKey,
wrappedByList,
}: Props): Promise<T> {
if (endpoint.startsWith('/')) {
endpoint = endpoint.slice(1);
}
const strapiUrl = import.meta.env.STRAPI_URL || process.env.STRAPI_URL || "http://localhost:1337";
console.log(strapiUrl);
const url = new URL(`${strapiUrl}/api/${endpoint}`);
if (query) {
Object.entries(query).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
try {
const res = await fetch(url.toString());
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
let data = await res.json();
if (wrappedByKey) {
data = data[wrappedByKey];
}
if (wrappedByList) {
data = data[0];
}
return data as T;
} catch (error) {
console.error('Error fetching from Strapi API:', error);
throw error;
}
}
// Helper function to handle media URLs from Strapi
export function getStrapiMedia(url: string | null, baseUrl: string) {
if (url == null) return null;
// Return as-is if it's a data URL (base64)
if (url.startsWith("data:")) return url;
// Return as-is if it's an absolute URL
if (url.startsWith("http") || url.startsWith("//")) return url;
// Prepend baseUrl for relative URLs
return `${baseUrl}${url}`;
}

View File

@@ -1,14 +1,15 @@
---
// Import necessary components and utilities
import Layout from "../layouts/Layout.astro";
import { getCollection } from "astro:content";
import PoleElementsList from "../components/PoleElementsList.astro";
import { getStrapiBaseUrl } from "../config/strapi";
// Fetch all posts from Strapi using Astro's content collection
const strapiPoleElements = await getCollection("poleElements");
// Get Strapi URL from global config
const BASE_URL = getStrapiBaseUrl();
import fetchApi from '../lib/strapi';
import type PoleElement from "../interfaces/poleElement";
const strapiPoleElements = await fetchApi<PoleElement[]>({
endpoint: 'elements?populate=*', // the content type to fetch
wrappedByKey: 'data', // the key to unwrap the response
});
---
<Layout title="Pole Elements" description="Pole Elements">

View File

@@ -1,20 +1,30 @@
---
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;
---
import fetchApi from '../../lib/strapi';
import type PoleElement from '../../interfaces/poleElement';
<Layout title={entry.data.title} description={entry.data.description}>
<h1>{entry.data.title}</h1>
<MardownContent content={entry.data.description} />
const { id } = Astro.params;
let poleElement: PoleElement;
try {
poleElement = await fetchApi<PoleElement>({
endpoint: 'elements',
wrappedByKey: 'data',
wrappedByList: true,
query: {
'populate': '*',
'filters[id][$eq]': id || '',
},
});
} catch (error) {
return Astro.redirect('/404');
}
---
<Layout title={poleElement.name} description={poleElement.description}>
<h1>{poleElement.name}</h1>
{poleElement.description && <MardownContent content={poleElement.description} />}
</Layout>

View File

@@ -1,10 +0,0 @@
// Helper function to handle media URLs from Strapi
export function getStrapiMedia(url: string | null, baseUrl: string) {
if (url == null) return null;
// Return as-is if it's a data URL (base64)
if (url.startsWith("data:")) return url;
// Return as-is if it's an absolute URL
if (url.startsWith("http") || url.startsWith("//")) return url;
// Prepend baseUrl for relative URLs
return `${baseUrl}${url}`;
}