Files
fast-next-template/frontend/src/components/projects/ProjectsGrid.tsx
Felipe Cardoso 4f24cebf11 chore(frontend): improve code formatting for readability
Standardize multiline formatting across components, tests, and API hooks for better consistency and clarity:
- Adjusted function and object property indentation.
- Updated tests and components to align with clean coding practices.
2026-01-03 01:12:51 +01:00

117 lines
3.0 KiB
TypeScript

/**
* ProjectsGrid Component
*
* Displays projects in either grid or list view with
* loading and empty states.
*
* @see Issue #54
*/
'use client';
import { Folder, Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Link } from '@/lib/i18n/routing';
import { cn } from '@/lib/utils';
import { ProjectCard, ProjectCardSkeleton } from './ProjectCard';
import type { ProjectListItem } from '@/lib/api/hooks/useProjects';
import type { ViewMode } from './ProjectFilters';
export interface ProjectsGridProps {
/** Projects to display */
projects: ProjectListItem[];
/** Whether data is loading */
isLoading?: boolean;
/** Current view mode */
viewMode?: ViewMode;
/** Called when a project card is clicked */
onProjectClick?: (project: ProjectListItem) => void;
/** Called when a project action is selected */
onProjectAction?: (
project: ProjectListItem,
action: 'archive' | 'pause' | 'resume' | 'delete'
) => void;
/** Whether filters are currently applied (affects empty state message) */
hasFilters?: boolean;
/** Additional CSS classes */
className?: string;
}
/**
* Empty state component
*/
function EmptyState({ hasFilters }: { hasFilters: boolean }) {
return (
<div className="py-16 text-center">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-muted">
<Folder className="h-8 w-8 text-muted-foreground" />
</div>
<h3 className="text-lg font-semibold">No projects found</h3>
<p className="mt-1 text-muted-foreground">
{hasFilters
? 'Try adjusting your filters or search query'
: 'Get started by creating your first project'}
</p>
{!hasFilters && (
<Button asChild className="mt-4">
<Link href="/projects/new">
<Plus className="mr-2 h-4 w-4" />
Create Project
</Link>
</Button>
)}
</div>
);
}
/**
* Loading skeleton grid
*/
function LoadingSkeleton({ viewMode }: { viewMode: ViewMode }) {
return (
<div
className={cn(viewMode === 'grid' ? 'grid gap-4 sm:grid-cols-2 lg:grid-cols-3' : 'space-y-4')}
>
{[1, 2, 3, 4, 5, 6].map((i) => (
<ProjectCardSkeleton key={i} />
))}
</div>
);
}
export function ProjectsGrid({
projects,
isLoading = false,
viewMode = 'grid',
onProjectClick,
onProjectAction,
hasFilters = false,
className,
}: ProjectsGridProps) {
if (isLoading) {
return <LoadingSkeleton viewMode={viewMode} />;
}
if (projects.length === 0) {
return <EmptyState hasFilters={hasFilters} />;
}
return (
<div
className={cn(
viewMode === 'grid' ? 'grid gap-4 sm:grid-cols-2 lg:grid-cols-3' : 'space-y-4',
className
)}
>
{projects.map((project) => (
<ProjectCard
key={project.id}
project={project}
onClick={() => onProjectClick?.(project)}
onAction={onProjectAction ? (action) => onProjectAction(project, action) : undefined}
/>
))}
</div>
);
}