feat: front update

This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2025-03-22 15:38:05 +01:00
parent 7303d63198
commit 4f4f86fb91
5 changed files with 44 additions and 34 deletions

View File

@@ -27,6 +27,9 @@ help: ## Outputs this help screen
build: ## Builds the Docker images build: ## Builds the Docker images
@$(DOCKER_COMP) build --pull --no-cache @$(DOCKER_COMP) build --pull --no-cache
up: ## Start the docker hub
@$(DOCKER_COMP) up -d
start: ## Start the docker hub in detached mode (no logs) start: ## Start the docker hub in detached mode (no logs)
@$(DOCKER_COMP) up --pull always -d --wait @$(DOCKER_COMP) up --pull always -d --wait

View File

@@ -11,7 +11,8 @@ export class Manga {
genres, genres,
status, status,
rating, rating,
description = '' description = '',
createdAt = null
) { ) {
this.id = id; this.id = id;
this.title = title; this.title = title;
@@ -23,6 +24,7 @@ export class Manga {
this.status = status; this.status = status;
this.rating = rating; this.rating = rating;
this.description = description; this.description = description;
this.createdAt = createdAt;
} }
} }
@@ -49,7 +51,8 @@ export class MangaDetail extends Manga {
manga.genres, manga.genres,
manga.status, manga.status,
manga.rating, manga.rating,
manga.description manga.description,
manga.createdAt
); );
this.chapters = this.organizeChaptersByVolume(chapters); this.chapters = this.organizeChaptersByVolume(chapters);
} }

View File

@@ -23,7 +23,9 @@ export class ApiMangaRepository {
item.publicationYear, item.publicationYear,
item.genres, item.genres,
item.status, item.status,
item.rating item.rating,
item.description,
item.createdAt
)); ));
return new MangaCollection( return new MangaCollection(
@@ -55,7 +57,8 @@ export class ApiMangaRepository {
item.genres, item.genres,
item.status, item.status,
item.rating, item.rating,
item.description item.description,
item.createdAt
)); ));
} catch (error) { } catch (error) {
console.error('Error searching mangas:', error); console.error('Error searching mangas:', error);
@@ -90,7 +93,8 @@ export class ApiMangaRepository {
genres: mangaData.genres, genres: mangaData.genres,
status: mangaData.status, status: mangaData.status,
rating: mangaData.rating, rating: mangaData.rating,
description: mangaData.description description: mangaData.description,
createdAt: mangaData.createdAt
}, chapters); }, chapters);
} catch (error) { } catch (error) {
console.error('Error fetching manga details:', error); console.error('Error fetching manga details:', error);

View File

@@ -1,7 +1,5 @@
import React from 'react'; import React from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faStar } from '@fortawesome/free-solid-svg-icons';
export function MangaCard({ manga }) { export function MangaCard({ manga }) {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -10,39 +8,34 @@ export function MangaCard({ manga }) {
navigate(`/manga/${manga.slug}`); navigate(`/manga/${manga.slug}`);
}; };
const formatDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
};
return ( return (
<div <div
className="bg-white rounded-lg shadow-md overflow-hidden cursor-pointer transition-transform hover:scale-105" className="bg-white rounded-lg shadow-md overflow-hidden cursor-pointer transition-transform hover:scale-105"
onClick={handleClick} onClick={handleClick}
> >
<div className="relative h-64"> <div className="relative pb-[150%]">
<img <img
src={manga.imageUrl || 'https://via.placeholder.com/300x400'} src={manga.imageUrl || 'https://via.placeholder.com/300x400'}
alt={manga.title} alt={manga.title}
className="w-full h-full object-cover" className="absolute inset-0 w-full h-full object-contain bg-gray-100"
/> />
</div> </div>
<div className="p-4"> <div className="p-2">
<h3 className="text-lg font-semibold text-gray-800 mb-2">{manga.title}</h3> <h3 className="text-lg font-semibold text-gray-800 mb-1">{manga.title}</h3>
<p className="text-sm text-gray-600 mb-2">By {manga.author}</p> <div className="flex items-center">
<div className="flex items-center justify-between">
<span className="text-sm text-gray-500">{manga.publicationYear}</span> <span className="text-sm text-gray-500">{manga.publicationYear}</span>
{manga.rating && (
<span className="flex items-center text-yellow-500">
<FontAwesomeIcon icon={faStar} className="mr-1" />
{manga.rating}
</span>
)}
</div> </div>
<div className="mt-2 flex flex-wrap gap-1"> <div className="mt-1 text-sm text-gray-500">
{manga.genres.map((genre, index) => ( Added: {formatDate(manga.createdAt)}
<span
key={index}
className="px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full"
>
{genre}
</span>
))}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -47,7 +47,11 @@ function mangaReducer(state, action) {
...state.collection, ...state.collection,
items: state.collection.items.map(manga => items: state.collection.items.map(manga =>
manga.slug === action.payload.slug manga.slug === action.payload.slug
? { ...manga, ...action.payload } ? {
...manga,
...action.payload,
createdAt: manga.createdAt || action.payload.createdAt
}
: manga : manga
) )
} : state.collection; } : state.collection;
@@ -57,7 +61,10 @@ function mangaReducer(state, action) {
collection: updatedCollection, collection: updatedCollection,
detailedMangas: { detailedMangas: {
...state.detailedMangas, ...state.detailedMangas,
[action.payload.slug]: action.payload [action.payload.slug]: {
...action.payload,
createdAt: state.collection?.items.find(m => m.slug === action.payload.slug)?.createdAt || action.payload.createdAt
}
}, },
loading: false, loading: false,
error: null error: null
@@ -98,11 +105,11 @@ export function MangaProvider({ children }) {
const loadCollection = useCallback(async () => { const loadCollection = useCallback(async () => {
// Si nous avons déjà des données, les afficher immédiatement // Si nous avons déjà des données, les afficher immédiatement
if (state.collection) { if (state.collection) {
// Rafraîchir en arrière-plan si les données sont vieilles de plus de 1 minute // Rafraîchir en arrière-plan si les données sont vieilles de plus de 30 secondes
const isStale = state.lastCollectionUpdate && const isStale = state.lastCollectionUpdate &&
(Date.now() - state.lastCollectionUpdate) > 60 * 1000; (Date.now() - state.lastCollectionUpdate) > 30 * 1000;
if (isStale) { if (isStale && !state.isBackgroundLoading) {
refreshCollectionInBackground(); refreshCollectionInBackground();
} }
return; return;
@@ -116,7 +123,7 @@ export function MangaProvider({ children }) {
dispatch({ type: 'SET_ERROR', payload: 'Failed to load manga collection' }); dispatch({ type: 'SET_ERROR', payload: 'Failed to load manga collection' });
console.error(error); console.error(error);
} }
}, [state.collection, state.lastCollectionUpdate, refreshCollectionInBackground]); }, [state.collection, state.lastCollectionUpdate, state.isBackgroundLoading, refreshCollectionInBackground]);
const loadMangaDetail = useCallback(async (slug) => { const loadMangaDetail = useCallback(async (slug) => {
// Retourner les données en cache si disponibles // Retourner les données en cache si disponibles