feat: front update
This commit is contained in:
parent
7303d63198
commit
4f4f86fb91
3
Makefile
3
Makefile
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user