-
Notifications
You must be signed in to change notification settings - Fork 0
mise en place des pages projets #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Implémentation de la page d'accueil de présentation des projets, de la page de détails des projets, des likes et des filtres
Guide du relecteur par SourceryCette pull request implémente la liste des projets et les pages de détails, y compris la fonctionnalité "j'aime", la recherche et le filtrage, et la prise en charge des images, en récupérant les données depuis Airtable. Elle ajoute également un lien vers la page des projets dans la barre de navigation. Diagramme de séquence pour "Aimer" un projetsequenceDiagram
participant User
participant LikeButton Component
participant /api/projects/like Endpoint
participant Airtable
User->>LikeButton Component: Clic sur le bouton "j'aime"/"je n'aime plus"
LikeButton Component->>/api/projects/like Endpoint: Envoie une requête avec projectId et action (add/remove)
activate /api/projects/like Endpoint
/api/projects/like Endpoint->>Airtable: Récupère les "j'aime" actuels pour le projet
Airtable-->>/api/projects/like Endpoint: Renvoie les "j'aime" actuels
/api/projects/like Endpoint->>Airtable: Met à jour les "j'aime" du projet dans Airtable
Airtable-->>/api/projects/like Endpoint: Renvoie les données du projet mises à jour
/api/projects/like Endpoint-->>LikeButton Component: Renvoie le succès et le nouveau nombre de "j'aime"
deactivate /api/projects/like Endpoint
LikeButton Component->>User: Met à jour l'interface utilisateur avec le nouveau nombre de "j'aime"
LikeButton Component->>LikeButton Component: Met à jour localStorage
LikeButton Component->>LikeButton Component: Actualise les données de la page
Diagramme de classes mis à jour pour le type ProjetclassDiagram
class Projet {
id: string
fields: ProjetFields
}
class ProjetFields {
Nom: string
Description: string
Technologies: string
Lien: string
Visuels: AirtableAttachment[]
Promotion: string
Administrateur: string
Categorie: string[]
Statut: string
Likes: number
}
class AirtableAttachment {
id: string
url: string
filename: string
size: number
type: string
width: number
height: number
thumbnails: Thumbnails
}
class Thumbnails {
small: Thumbnail
large: Thumbnail
}
class Thumbnail {
url: string
width: number
height: number
}
Projet "1" -- "1" ProjetFields : has
ProjetFields "1" -- "n" AirtableAttachment : has
AirtableAttachment "1" -- "1" Thumbnails : has
Thumbnails "1" -- "1" Thumbnail : has small
Thumbnails "1" -- "1" Thumbnail : has large
note for ProjetFields "Added Likes: number field"
Modifications au niveau des fichiers
Conseils et commandesInteragir avec Sourcery
Personnaliser votre expérienceAccédez à votre tableau de bord pour :
Obtenir de l'aide
Original review guide in EnglishReviewer's Guide by SourceryThis pull request implements project listing and detail pages, including like functionality, search and filtering, and image support, fetching data from Airtable. It also adds a link to the projects page in the navigation bar. Sequence Diagram for Liking a ProjectsequenceDiagram
participant User
participant LikeButton Component
participant /api/projects/like Endpoint
participant Airtable
User->>LikeButton Component: Clicks like/unlike button
LikeButton Component->>/api/projects/like Endpoint: Sends request with projectId and action (add/remove)
activate /api/projects/like Endpoint
/api/projects/like Endpoint->>Airtable: Fetches current likes for project
Airtable-->>/api/projects/like Endpoint: Returns current likes
/api/projects/like Endpoint->>Airtable: Updates project likes in Airtable
Airtable-->>/api/projects/like Endpoint: Returns updated project data
/api/projects/like Endpoint-->>LikeButton Component: Returns success and new like count
deactivate /api/projects/like Endpoint
LikeButton Component->>User: Updates UI with new like count
LikeButton Component->>LikeButton Component: Updates localStorage
LikeButton Component->>LikeButton Component: Refreshes page data
Updated Class Diagram for Projet TypeclassDiagram
class Projet {
id: string
fields: ProjetFields
}
class ProjetFields {
Nom: string
Description: string
Technologies: string
Lien: string
Visuels: AirtableAttachment[]
Promotion: string
Administrateur: string
Categorie: string[]
Statut: string
Likes: number
}
class AirtableAttachment {
id: string
url: string
filename: string
size: number
type: string
width: number
height: number
thumbnails: Thumbnails
}
class Thumbnails {
small: Thumbnail
large: Thumbnail
}
class Thumbnail {
url: string
width: number
height: number
}
Projet "1" -- "1" ProjetFields : has
ProjetFields "1" -- "n" AirtableAttachment : has
AirtableAttachment "1" -- "1" Thumbnails : has
Thumbnails "1" -- "1" Thumbnail : has small
Thumbnails "1" -- "1" Thumbnail : has large
note for ProjetFields "Added Likes: number field"
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @Nirdeo - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding a loading state or skeleton UI while fetching data from Airtable to improve user experience.
- It would be beneficial to add error handling for the Airtable API calls to gracefully handle potential failures.
Here's what I looked at during the review
- 🟡 General issues: 1 issue found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 2 issues found
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
@@ -0,0 +1 @@ | |||
const Airtable = require('airtable'); const base = new Airtable({apiKey: process.env.AIRTABLE_KEY}).base(process.env.AIRTABLE_BASE); (async () => { const records = await base('Projets').select().all(); console.log(JSON.stringify(records[0], null, 2)); })(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Remove or relocate temporary debugging code.
This temporary script might be useful during development, but it’s better to remove it or isolate it from production code to avoid unnecessary clutter.
Suggested implementation:
/*
Temporary debugging code has been removed.
If you need to test Airtable functionality, please use a dedicated debugging script.
*/
If this debugging code is needed for development, consider moving it to a dedicated development-only file or use environment variables to conditionally run the debug code.
}; | ||
}; | ||
|
||
export default async function ProjectDetail({ params }: Props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider extracting UI sections and data fetching into separate components to improve legibility and testability, such as creating a dedicated header component and gallery component..
Consider extracting each UI section and even the data fetching into separate components/functions. For example, create a dedicated header component along with a gallery component. This separation improves both legibility and testability while preserving the original behavior.
Example:
Create a ProjectHeader
component:
// components/ProjectHeader.tsx
import Link from "next/link";
import LikeButton from "@/components/LikeButton";
type Props = {
project: Projet;
};
export default function ProjectHeader({ project }: Props) {
return (
<div className="mb-8">
<div className="flex justify-between items-start">
<h1 className="text-3xl font-bold mb-2">{project.fields.Nom}</h1>
<LikeButton
projectId={project.id}
initialLikes={typeof project.fields.Likes === 'number' ? project.fields.Likes : 0}
className="text-lg"
/>
</div>
<div className="flex flex-wrap gap-2 mb-4">
{typeof project.fields.Technologies === 'string' &&
project.fields.Technologies.split(',').map((tech, index) => (
<span
key={index}
className="bg-gray-100 dark:bg-gray-700 px-3 py-1 rounded-full text-sm"
>
{tech.trim()}
</span>
))}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
Promotion: {project.fields.Promotion}
</div>
<Link
href="/"
className="inline-flex items-center mb-6 text-sm font-medium text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
Retour aux projets
</Link>
</div>
);
}
Likewise, create similar components for the gallery, description, and details sections. Then in your main page component, import them and pass down the project
data:
// pages/ProjectDetail.tsx
import ProjectHeader from "@/components/ProjectHeader";
// import other subcomponents as needed
export default async function ProjectDetail({ params }: Props) {
try {
const projectData = await getAirtableProjectById(params.id);
if (!projectData) return notFound();
const project = {
id: projectData.id,
fields: projectData.fields,
} as Projet;
return (
<div className="min-h-screen py-8">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<ProjectHeader project={project} />
{/* Render Gallery, Description, and AdditionalInfo components here */}
</div>
</div>
);
} catch (error) {
console.error("Erreur lors de la récupération du projet:", error);
return notFound();
}
}
By isolating concerns into smaller components, you reduce cognitive load and improve maintainability while keeping functionality intact.
} | ||
}; | ||
|
||
export default function LikeButton({ projectId, initialLikes, className = "" }: LikeButtonProps) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider extracting the localStorage and API logic into separate modules to simplify the component.
Consider extracting the localStorage and API logic from the component to decrease its responsibilities.
Step 1. Extract localStorage utilities
Create a new module (e.g., localStorageUtils.ts
) for getLikedProjects
and saveLikedProjects
:
// localStorageUtils.ts
export const LIKED_PROJECTS_KEY = "likedProjects";
export const getLikedProjects = (): string[] => {
if (typeof window === "undefined") return [];
try {
const likedProjects = localStorage.getItem(LIKED_PROJECTS_KEY);
return likedProjects ? JSON.parse(likedProjects) : [];
} catch (error) {
console.error("Erreur lors de la récupération des projets aimés:", error);
return [];
}
};
export const saveLikedProjects = (projects: string[]): void => {
if (typeof window === "undefined") return;
try {
localStorage.setItem(LIKED_PROJECTS_KEY, JSON.stringify(projects));
} catch (error) {
console.error("Erreur lors de la sauvegarde des projets aimés:", error);
}
};
Then update your component to import these:
import { getLikedProjects, saveLikedProjects } from "./localStorageUtils";
Step 2. Extract the API call
Create an API utility (e.g., likeService.ts
) to encapsulate the API request:
// likeService.ts
export const toggleLikeStatus = async (projectId: string, hasLiked: boolean) => {
const action = hasLiked ? "remove" : "add";
const response = await fetch("/api/projects/like", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ projectId, action }),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || "Une erreur est survenue");
}
return data;
};
Then use it in your component:
import { toggleLikeStatus } from "./likeService";
const handleLikeToggle = async () => {
if (isProcessing) return;
setIsProcessing(true);
setError(null);
try {
const data = await toggleLikeStatus(projectId, hasLiked);
setLikes(data.likes);
const newHasLiked = !hasLiked;
setHasLiked(newHasLiked);
const likedProjects = getLikedProjects();
if (newHasLiked) {
if (!likedProjects.includes(projectId)) {
saveLikedProjects([...likedProjects, projectId]);
}
} else {
saveLikedProjects(likedProjects.filter(id => id !== projectId));
}
router.refresh();
} catch (err) {
setError(err instanceof Error ? err.message : "Une erreur est survenue");
console.error("Erreur lors de la gestion du like:", err);
} finally {
setIsProcessing(false);
}
};
This refactoring isolates different concerns while keeping all functionality intact and reduces the cognitive load within the LikeButton
component.
|
||
// Fonction pour récupérer les projets aimés du localStorage | ||
const getLikedProjects = (): string[] => { | ||
if (typeof window === "undefined") return []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces
)
if (typeof window === "undefined") return []; | |
if (typeof window === "undefined") { |
Explanation
It is recommended to always use braces and create explicit statement blocks.Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).
|
||
// Fonction pour sauvegarder les projets aimés dans le localStorage | ||
const saveLikedProjects = (projects: string[]): void => { | ||
if (typeof window === "undefined") return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces
)
if (typeof window === "undefined") return; | |
if (typeof window === "undefined") { |
Explanation
It is recommended to always use braces and create explicit statement blocks.Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).
}, [projectId]); | ||
|
||
const handleLikeToggle = async () => { | ||
if (isProcessing) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces
)
if (isProcessing) return; | |
if (isProcessing) { |
Explanation
It is recommended to always use braces and create explicit statement blocks.Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).
|
||
return project; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable
)
const project = await base.table("Projets").find(id); | |
return project; | |
return await base.table("Projets").find(id); | |
Explanation
Something that we often see in people's code is assigning to a result variableand then immediately returning it.
Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.
Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.
Implémentation de la page d'accueil de présentation des projets, de la page de détails des projets, des likes et des filtres
Résumé par Sourcery
Implémentation de pages de projet avec des fonctionnalités complètes, notamment la liste des projets, la vue détaillée des projets, la recherche, le filtrage et la fonctionnalité "J'aime".
Nouvelles fonctionnalités :
Améliorations :
Documentation :
Original summary in English
Summary by Sourcery
Implement project pages with comprehensive features including project listing, detailed project view, search, filtering, and like functionality
New Features:
Enhancements:
Documentation: