Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 4x 4x 4x 16x 16x 16x 16x 16x 16x 14x 4x | import React, { useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { UpdateProjectModal } from '@/features/project-update/model';
import { DeleteProjectModal } from '@/features/project-delete/model';
import { ProjectCard, type ProjectCardData } from '@/entities/project/ui/_shared';
interface Props {
projects: ProjectCardData[];
}
const fadeVariants = {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
};
const gridClasses =
'grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 justify-items-center';
function renderCard(
project: ProjectCardData,
onEdit: (p: ProjectCardData) => void,
onDelete: (p: ProjectCardData) => void,
) {
return (
<div key={project.id || project.name} className="w-full max-w-[280px]">
<ProjectCard
project={project}
data-testid="project-card"
onEditClick={onEdit}
onDeleteClick={onDelete}
/>
</div>
);
}
export const ProjectListCardsViewComponent = ({ projects }: Props) => {
const [editingProject, setEditingProject] = useState<ProjectCardData | null>(null);
const [deletingProject, setDeletingProject] = useState<ProjectCardData | null>(null);
const handleCloseEdit = () => setEditingProject(null);
const handleCloseDelete = () => setDeletingProject(null);
const isDev = import.meta.env.MODE === 'development';
const projectsWithFake: ProjectCardData[] = isDev
? [
...projects,
{
id: '', // empty id for ghost project
name: 'Ghost Project',
createdAt: Date.now(),
updatedAt: Date.now(),
},
]
: projects;
return (
<div className="relative min-h-[400px]" data-testid="project-list">
<AnimatePresence mode="wait">
<motion.div
key="cards"
{...fadeVariants}
transition={{ duration: 0.4 }}
className={gridClasses}
>
{projectsWithFake.map((p) =>
renderCard(p, setEditingProject, setDeletingProject),
)}
</motion.div>
</AnimatePresence>
{editingProject && (
<UpdateProjectModal
projectId={editingProject.id}
initialName={editingProject.name}
onClose={handleCloseEdit}
/>
)}
{deletingProject && (
<DeleteProjectModal
projectId={deletingProject.id}
projectName={deletingProject.name}
onClose={handleCloseDelete}
/>
)}
</div>
);
};
export const ProjectListCardsView = React.memo(ProjectListCardsViewComponent);
|