Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import vercel from '@astrojs/vercel';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
site: 'https://fav.jpdiaz.dev',
output: 'server', // Puedes cambiar a "static" si no usas SSR
adapter: vercel(),
output: 'static', // Changed from 'server' to prevent excessive serverless function calls
integrations: [
tailwind(),
sitemap({
Expand Down
320 changes: 160 additions & 160 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/pages/artists/[mbid].astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
---
import Layout from '@layouts/Layout.astro';
import ArtistDetails from '@pages/fragments/ArtistDetails/[mbid].astro';
import type { GetStaticPaths } from 'astro';
import artistsData from '@data/artistDetails.json';

// This is required for static site generation
export async function getStaticPaths() {
const artistEntries = artistsData.allArtists || [];
return artistEntries.map((artist) => ({
params: { mbid: artist.id },
}));
}

const { mbid } = Astro.params;
---

<Layout title="Juan's Favorites — Artist"><ArtistDetails /></Layout>
39 changes: 18 additions & 21 deletions src/pages/books/[id].astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,24 @@ import { metadata } from '@config/metadata.js';
const pageTitle = `Book Details — ${metadata.name || "Juan's Favorites"}`;
const pageDescription = `Details for a selected book from ${metadata.name || "Juan's Favorites"}.`;

// For SSR (which this project uses based on cloudflare adapter with output: "server"),
// getStaticPaths is not strictly needed for dynamic routing to work.
// The [id].astro page will be rendered on demand.
// If pre-rendering all book pages at build time was desired, getStaticPaths would be:
// export async function getStaticPaths() {
// try {
// const results = await Astro.glob('@data/books/bookDetails.json');
// if (results && results.length > 0) {
// const bookJsonData = results[0].default || results[0];
// if (bookJsonData && bookJsonData.allBooks) {
// return bookJsonData.allBooks.map(book => ({
// params: { id: book.id || book.slug }, // Ensure 'id' or 'slug' matches what's used in fragment
// // props: { book } // Optionally pass full book data as props to avoid re-fetch in fragment
// }));
// }
// }
// } catch (error) {
// console.error("Error in getStaticPaths for books:", error);
// }
// return []; // Fallback to empty paths if error or no data
// }
// For static site generation, getStaticPaths is required for dynamic routing

Copilot AI Jun 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider passing the full book data as props in getStaticPaths to maintain consistency with other dynamic routes and eliminate potential runtime fetching.

Copilot uses AI. Check for mistakes.
import type { GetStaticPaths } from 'astro';
import booksData from '@data/myFavBooks.json';

// Generate static paths for all books
export const getStaticPaths = (() => {
try {
if (Array.isArray(booksData)) {
return booksData.map(book => ({
params: { id: book.api_id || String(book._id) },
}));
}
return [];
} catch (error) {
console.error("Error in getStaticPaths for books:", error);
return []; // Fallback to empty paths if error or no data
}
}) satisfies GetStaticPaths;
---

<Layout title={pageTitle} description={pageDescription}>
Expand Down
19 changes: 19 additions & 0 deletions src/pages/fragments/BookDetails/[id].astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
---
import type { GetStaticPaths } from 'astro';
import booksData from '@data/myFavBooks.json';

// Generate static paths for all books in the data file
export const getStaticPaths = (() => {
try {
if (Array.isArray(booksData)) {
return booksData.map(book => ({
params: { id: book.api_id || String(book._id) },
props: { bookData: book },
}));
}
return [];
} catch (error) {
console.error("Error in getStaticPaths for book details fragment:", error);
return []; // Fallback to empty paths if error or no data
}
}) satisfies GetStaticPaths;

const { id } = Astro.params; // Get the book's OpenLibrary Work ID from the URL
let book = null;
let fetchError = null;
Expand Down
53 changes: 26 additions & 27 deletions src/pages/fragments/GameDetails/[slug].astro
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
---
import Modal from '@components/Modal.astro';
import gameData from '@data/gameDetails.json';

const { slug } = Astro.params; // Get the game slug from the URL
let game = null;
let fetchError = null;

try {
const results = await Astro.glob('@data/gameDetails.json');
if (results && results.length > 0) {
const gameJsonData = results[0].default || results[0];
if (gameJsonData && gameJsonData.allGames) {
game = gameJsonData.allGames.find((g) => g.slug === slug);
if (!game) {
fetchError = `Game with slug "${slug}" not found.`;
} else {
// Dynamically set page title - this is an experimental way and might not work as expected
// A more common way is to pass title to Layout from the page itself.
// For fragments, this is less direct.
// Astro.props.title = `${game.name} — ${SITE_TITLE}`; // This won't work directly
// If htmx is used for fragment swaps, one might set HX-Set-Title header.
// For now, title is set in src/pages/games/[slug].astro
}
// For static site generation, pre-define all game slugs
export async function getStaticPaths() {
try {
// Generate paths for all games
if (gameData && gameData.allGames && Array.isArray(gameData.allGames)) {
return gameData.allGames.map(game => ({
params: { slug: game.slug },
props: { gameData: game } // Pass the game data as props
}));
} else {
fetchError =
'Game data is not in the expected format or "allGames" key is missing in imported JSON.';
console.error('Error in getStaticPaths for game details fragment: gameData.allGames is not an array');
return [];
}
} else {
fetchError = 'Game data file (gameDetails.json) not found. Please run `npm run fetch-games`.';
} catch (error) {
console.error('Error in getStaticPaths for game details fragment:', error);
return []; // Fallback to empty paths if error
}
} catch (error) {
console.error(`Error loading or processing gameDetails.json for game slug "${slug}":`, error);
fetchError = `Failed to load game data. Details: ${error.message}`;
}

// Get slug and game data from params and props
const { slug } = Astro.params; // Get the game slug from the URL
// Use game data passed from getStaticPaths function
let game = Astro.props.gameData;
let fetchError = null;

// Fallback error handling if props weren't passed correctly
if (!game) {
fetchError = `Game data not available for slug "${slug}". This may be an issue with static generation.`;
}

const placeholderImageUrl = 'https://via.placeholder.com/500x750.png?text=No+Cover+Art'; // For main cover
Expand Down
66 changes: 48 additions & 18 deletions src/pages/fragments/MovieDetails/[id].astro
Original file line number Diff line number Diff line change
@@ -1,40 +1,70 @@
---
import Modal from '@components/Modal.astro';
import movieDetailsJson from '@data/movieDetails.json';

// For static site generation, pre-define all movie IDs
export async function getStaticPaths() {
try {
const allMovies = [];

// Movie data is organized by genre categories
if (movieDetailsJson) {
// Iterate through each genre category
Object.keys(movieDetailsJson).forEach(genre => {
if (Array.isArray(movieDetailsJson[genre])) {
// Add all movies from this genre to our collection
movieDetailsJson[genre].forEach(movie => {
if (movie.id) {
allMovies.push(movie);
}
});
}
});
}

// Generate paths for all movies
return allMovies.map(movie => ({
params: { id: String(movie.id) },
props: { movieData: movie } // Pass the movie data as props
}));
} catch (error) {
console.error('Error in getStaticPaths for MovieDetails:', error);
return []; // Fallback to empty paths if error
}
}

// Get parameters from Astro
const { id } = Astro.params;
const url = `https://api.themoviedb.org/3/movie/${id}?append_to_response=credits,videos,images,recommendations&api_key=${
import.meta.env.TMDB_API_KEY
}`;
const movieData = Astro.props.movieData;

const response = await fetch(url);
const data = await response.json();
// Format movie data for display
const movie = {
...data,
poster_path: data.poster_path
? 'https://image.tmdb.org/t/p/w500/' + data.poster_path
...movieData,
poster_path: movieData.poster_path
? 'https://image.tmdb.org/t/p/w500/' + movieData.poster_path
: 'https://via.placeholder.com/500x750',
vote_average: (data.vote_average * 10).toFixed(2) + '%',
release_date: new Date(data.release_date).toLocaleDateString('en-us', {
vote_average: (movieData.vote_average * 10).toFixed(2) + '%',
release_date: new Date(movieData.release_date).toLocaleDateString('en-us', {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
genres: data.genres.map((g) => g.name).join(', '),
crew: data.credits.crew.slice(0, 3),
cast: data.credits.cast.slice(0, 5).map((c) => ({
genres: movieData.genres.map((g) => g.name).join(', '),
crew: movieData.credits?.crew?.slice(0, 3) || [],
cast: movieData.credits?.cast?.slice(0, 5).map((c) => ({
...c,
profile_path: c.profile_path
? 'https://image.tmdb.org/t/p/w300/' + c.profile_path
: 'https://via.placeholder.com/300x450',
})),
morelike: data.recommendations.results.slice(0, 5).map((m) => ({
})) || [],
morelike: movieData.recommendations?.results?.slice(0, 5).map((m) => ({
...m,
poster_path: m.poster_path
? 'https://image.tmdb.org/t/p/w500/' + m.poster_path
: 'https://via.placeholder.com/500x750',
})),
images: data.images.backdrops.slice(0, 6),
videos: data.videos,
})) || [],
images: movieData.images?.backdrops?.slice(0, 6) || [],
videos: movieData.videos || { results: [] },
};
// console.log("more like this", movie.recommendations.results.slice(0, 5));
// console.log("video", movie.videos.results.slice(0, 1));
Expand Down
40 changes: 40 additions & 0 deletions src/pages/fragments/PersonDetails/[id].astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
---
import movieDetailsJson from '@data/movieDetails.json';

// For static site generation, pre-define all person IDs
export async function getStaticPaths() {
try {
const personIds = new Set();

// Movie data is organized by genre categories
if (movieDetailsJson) {
// Iterate through each genre category
Object.keys(movieDetailsJson).forEach(genre => {
if (Array.isArray(movieDetailsJson[genre])) {
// Add all person IDs from cast and crew
movieDetailsJson[genre].forEach(movie => {
if (movie.credits) {
// Add cast members
movie.credits.cast?.forEach(castMember => {
if (castMember.id) personIds.add(String(castMember.id));
});

// Add crew members
movie.credits.crew?.forEach(crewMember => {
if (crewMember.id) personIds.add(String(crewMember.id));
});
}
});
}
});
}

// Generate paths for all unique person IDs
return Array.from(personIds).map(id => ({
params: { id }
}));
} catch (error) {
console.error('Error in getStaticPaths for PersonDetails:', error);
return []; // Fallback to empty paths if error
}
}

const { id } = Astro.params;
const profileUrl = `https://api.themoviedb.org/3/person/${id}?append_to_response=external_ids,combined_credits&api_key=${
import.meta.env.TMDB_API_KEY
Expand Down
46 changes: 28 additions & 18 deletions src/pages/fragments/PodcastDetails/[id].astro
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
---
const { id } = Astro.params; // Get the podcast ID from the URL
let podcast = null;
let fetchError = null;
import podcastDetailsJson from '@data/podcastDetails.json';

try {
const results = await Astro.glob('@data/podcastDetails.json');
if (results && results.length > 0) {
const podcastJsonData = results[0].default || results[0];
if (podcastJsonData && podcastJsonData.allPodcasts) {
podcast = podcastJsonData.allPodcasts.find(p => p.id.toString() === id.toString());
if (!podcast) {
fetchError = `Podcast with ID "${id}" not found.`;
}
// For static site generation, define all podcast IDs to pre-render
export async function getStaticPaths() {
try {
// Extract all podcast IDs from the JSON data
if (podcastDetailsJson && podcastDetailsJson.allPodcasts) {
return podcastDetailsJson.allPodcasts.map(podcast => ({
params: { id: podcast.id },
props: { podcastData: podcast } // Pass podcast data as props to avoid fetching again
}));
} else {
fetchError = 'Podcast data is not in the expected format or "allPodcasts" key is missing in imported JSON.';
console.error('Podcast data is missing or malformed in podcastDetails.json');
return [];
}
} else {
fetchError = 'Podcast data file not found via Astro.glob.';
} catch (error) {
console.error('Error processing podcast data for getStaticPaths:', error);
return [];
}
} catch (error) {
console.error(`Error loading or processing podcastDetails.json for podcast ${id} via Astro.glob:`, error);
fetchError = `Failed to load podcast data. Details: ${error.message}`;
}

const { id } = Astro.params; // Get the podcast ID from the URL
const { podcastData } = Astro.props; // Get the podcast data passed from getStaticPaths

// Use the passed data directly instead of fetching
let podcast = podcastData;
let fetchError = null;

// Set fetchError if data is somehow missing
if (!podcast) {
fetchError = `Podcast with ID "${id}" not found.`;
console.error(`Podcast data missing for ID: ${id} in static props`);
}

// Placeholder for missing image
Expand Down
30 changes: 9 additions & 21 deletions src/pages/fragments/PodcastList/index.astro
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
---
import PodcastCard from '@components/PodcastCard.astro';
import podcastDetailsJson from '@data/podcastDetails.json';

let allPodcasts = [];
let fetchError = null;

try {
// Use Astro.glob to import the JSON data.
// Note: Astro.glob returns an array of modules. For a single file, we take the first.
// The content of the JSON file is usually the default export.
const results = await Astro.glob('@data/podcastDetails.json');
if (results && results.length > 0) {
// For JSON files, Astro.glob provides the parsed content directly.
// If it were a module with exports, it might be results[0].default or results[0].allPodcasts
// Let's assume it's the direct content or default export which is the object.
const podcastJsonData = results[0].default || results[0];
if (podcastJsonData && podcastJsonData.allPodcasts) {
allPodcasts = podcastJsonData.allPodcasts;
} else {
console.warn(
'No "allPodcasts" key found in the imported podcastDetails.json or data is null/malformed.'
);
fetchError = 'Podcast data is not in the expected format.';
allPodcasts = [];
}
// Use direct import instead of Astro.glob
if (podcastDetailsJson && podcastDetailsJson.allPodcasts) {
allPodcasts = podcastDetailsJson.allPodcasts;
} else {
console.warn('podcastDetails.json file not found via Astro.glob.');
fetchError = 'Podcast data file not found.';
console.warn(
'No "allPodcasts" key found in the imported podcastDetails.json or data is null/malformed.'
);
fetchError = 'Podcast data is not in the expected format.';
allPodcasts = [];
}
} catch (error) {
console.error('Error loading or processing podcastDetails.json via Astro.glob:', error);
console.error('Error processing podcastDetails.json:', error);
fetchError = `Failed to load podcast data. Details: ${error.message}`;
allPodcasts = []; // Ensure it's an array
}
Expand Down
Loading