'use client'; import * as React from 'react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardAction, } from '../../ui/card'; import { Avatar, AvatarFallback, AvatarImage } from '../../ui/avatar'; import { Badge } from '../../ui/badge'; import { Progress } from '../../ui/progress'; import { cn } from '../../shared/utils'; import type { ProgressVariant } from '../../ui/progress'; export type ProjectStatus = 'active' | 'review' | 'done' | 'paused' | 'at-risk'; export interface ProjectMember { name: string; initials: string; avatar?: string; } export interface ProjectCardProps extends React.HTMLAttributes { title: string; description?: string; status: ProjectStatus; progress: number; dueDate?: string; members?: ProjectMember[]; maxMembers?: number; action?: React.ReactNode; } const statusVariantConfig: Record< ProjectStatus, { variant: React.ComponentProps['variant']; progressVariant: ProgressVariant; } > = { active: { variant: 'info', progressVariant: 'info' }, review: { variant: 'warning', progressVariant: 'warning' }, done: { variant: 'success', progressVariant: 'success' }, paused: { variant: 'secondary', progressVariant: 'default' }, 'at-risk': { variant: 'destructive', progressVariant: 'destructive' }, }; export function ProjectCard({ title, description, status, progress, dueDate, members = [], maxMembers = 4, action, className, ...props }: ProjectCardProps) { const { t } = useTranslation(); const statusLabelMap = useMemo>( () => ({ active: t('projectCard.status.active'), review: t('projectCard.status.review'), done: t('projectCard.status.completed'), paused: t('projectCard.status.paused'), 'at-risk': t('projectCard.status.atRisk'), }), [t] ); const { variant, progressVariant } = statusVariantConfig[status]; const label = statusLabelMap[status]; const visible = members.slice(0, maxMembers); const overflow = members.length - maxMembers; return (
{title} {description && {description}}
{label} {action}
{t('projectCard.progress')} {progress}%
{visible.map(m => ( {m.avatar && } {m.initials} ))} {overflow > 0 && (
+{overflow}
)}
{dueDate && {dueDate}}
); }