S7.1: Replace div onClick with semantic button in DialogTrigger.tsx
S7.2: Replace role="button" divs with native <button> elements in 12 files
(PlaylistCard, TrackCard, ConversationItem, NotificationMenuItem,
AudioPlayerTrackInfo, SearchPageResults, ProjectsManagerAddCard,
ProjectsManagerCard, GearInventoryGrid, UploadModal, dropdown.tsx,
LibraryPageGrid)
S7.3: Add focus-visible:ring-2 to 14 form inputs with outline-none across
9 modal files (CreateGroupModal, DataExportModal, EditPlaylistModal,
AddToPlaylistModal, BanUserModal, RefundRequestModal, FlashSaleModal,
TipStreamerModal, CreatePostModal)
S7.4: Add semantic landmarks — <section> in DashboardPage, <article> in
PostCard and CourseCard
Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
import { Card } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { MoreVertical } from 'lucide-react';
|
|
import type { Project } from '@/services/projectService';
|
|
|
|
interface ProjectsManagerCardProps {
|
|
project: Project;
|
|
onOpen: () => void;
|
|
onOptionsClick: () => void;
|
|
}
|
|
|
|
export function ProjectsManagerCard({
|
|
project,
|
|
onOpen,
|
|
onOptionsClick,
|
|
}: ProjectsManagerCardProps) {
|
|
const badgeVariant =
|
|
project.daw === 'Ableton'
|
|
? 'cyan'
|
|
: project.daw === 'FL Studio'
|
|
? 'gold'
|
|
: 'magenta';
|
|
|
|
return (
|
|
<Card
|
|
variant="glass"
|
|
className="group cursor-pointer hover:border-primary/50 transition-colors duration-[var(--sumi-duration-normal)]"
|
|
onClick={onOpen}
|
|
>
|
|
<div className="flex justify-between items-start mb-4">
|
|
<Badge label={project.daw} variant={badgeVariant} />
|
|
<button
|
|
type="button"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onOptionsClick();
|
|
}}
|
|
className="appearance-none bg-transparent border-0 p-0 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded"
|
|
aria-label="Project options"
|
|
>
|
|
<MoreVertical className="w-4 h-4 text-muted-foreground hover:text-foreground transition-colors" />
|
|
</button>
|
|
</div>
|
|
|
|
<h3 className="text-xl font-bold text-foreground mb-1 group-hover:text-foreground transition-colors truncate tracking-tight">
|
|
{project.name}
|
|
</h3>
|
|
<p className="text-xs text-muted-foreground mb-4 font-mono">
|
|
Last edited {project.modified}
|
|
</p>
|
|
|
|
<div className="grid grid-cols-2 gap-2 mb-4">
|
|
<div className="bg-muted/50 p-2 rounded-lg text-center border border-border">
|
|
<div className="text-xs text-muted-foreground uppercase font-bold">
|
|
BPM
|
|
</div>
|
|
<div className="font-bold text-foreground">{project.bpm}</div>
|
|
</div>
|
|
<div className="bg-muted/50 p-2 rounded-lg text-center border border-border">
|
|
<div className="text-xs text-muted-foreground uppercase font-bold">
|
|
Key
|
|
</div>
|
|
<div className="font-bold text-foreground">{project.key}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mb-4">
|
|
<div className="flex justify-between text-xs mb-1">
|
|
<span className="text-muted-foreground font-bold">
|
|
{project.status}
|
|
</span>
|
|
<span className="text-muted-foreground">{project.progress}%</span>
|
|
</div>
|
|
<div className="h-1.5 bg-muted rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-primary"
|
|
style={{ width: `${project.progress}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center pt-4 border-t border-border">
|
|
<div className="flex -space-x-2">
|
|
<div className="w-6 h-6 rounded-full bg-muted border border-border" />
|
|
{project.collaborators > 0 && (
|
|
<div className="w-6 h-6 rounded-full bg-card border border-border flex items-center justify-center text-xs text-foreground">
|
|
+{project.collaborators}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<Button variant="ghost" size="sm" className="text-xs h-8">
|
|
OPEN
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|