summaryrefslogtreecommitdiff
path: root/apps/web/src/app/projects/page.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/app/projects/page.tsx')
-rw-r--r--apps/web/src/app/projects/page.tsx80
1 files changed, 48 insertions, 32 deletions
diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx
index f4df5882..a1a61d14 100644
--- a/apps/web/src/app/projects/page.tsx
+++ b/apps/web/src/app/projects/page.tsx
@@ -1,36 +1,33 @@
"use client";
-import Link from "next/link";
-import { useState, useEffect, useCallback } from "react";
-import { Button } from "@/components/ui/button";
-import { Card, CardContent } from "@/components/ui/card";
-import { Checkbox } from "@/components/ui/checkbox";
-import { Input } from "@/components/ui/input";
import {
- ChevronLeft,
- Plus,
Calendar,
+ ChevronLeft,
+ Loader2,
MoreHorizontal,
+ Plus,
+ Search,
+ Trash2,
Video,
- Loader2,
X,
- Trash2,
- Search,
} from "lucide-react";
-import { TProject } from "@/types/project";
import Image from "next/image";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useCallback, useEffect, useState } from "react";
+import { DeleteProjectDialog } from "@/components/delete-project-dialog";
+import { RenameProjectDialog } from "@/components/rename-project-dialog";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent } from "@/components/ui/card";
+import { Checkbox } from "@/components/ui/checkbox";
import {
DropdownMenu,
- DropdownMenuItem,
DropdownMenuContent,
- DropdownMenuTrigger,
+ DropdownMenuItem,
DropdownMenuSeparator,
+ DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
-import { useProjectStore } from "@/stores/project-store";
-import { useRouter } from "next/navigation";
-import { DeleteProjectDialog } from "@/components/delete-project-dialog";
-import { RenameProjectDialog } from "@/components/rename-project-dialog";
-import { useTimelineStore } from "@/stores/timeline-store";
+import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
@@ -39,6 +36,9 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
+import { useProjectStore } from "@/stores/project-store";
+import { useTimelineStore } from "@/stores/timeline-store";
+import type { TProject } from "@/types/project";
export default function ProjectsPage() {
const {
@@ -52,13 +52,9 @@ export default function ProjectsPage() {
const [thumbnailCache, setThumbnailCache] = useState<
Record<string, string | null>
>({});
- const [loadingThumbnails, setLoadingThumbnails] = useState<Set<string>>(
+ const [_loadingThumbnails, setLoadingThumbnails] = useState<Set<string>>(
new Set(),
);
- const [selectedProject, setSelectedProject] = useState<TProject | null>(null);
- const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
- const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
- const [sortBy, setSortBy] = useState<"name" | "date">("date");
const [isSelectionMode, setIsSelectionMode] = useState(false);
const [selectedProjects, setSelectedProjects] = useState<Set<string>>(
new Set(),
@@ -246,9 +242,17 @@ export default function ProjectsPage() {
</div>
{isSelectionMode && sortedProjects.length > 0 && (
- <div
+ <button
+ type="button"
onClick={() => handleSelectAll(!allSelected)}
- className="hover:cursor-pointer gap-2 mb-6 p-4 bg-muted/30 rounded-lg border items-center flex"
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ handleSelectAll(!allSelected);
+ }
+ }}
+ className="w-full hover:cursor-pointer gap-2 mb-6 p-4 bg-muted/30 rounded-lg border items-center flex"
+ tabIndex={0}
>
<Checkbox checked={someSelected ? "indeterminate" : allSelected} />
<span className="text-sm font-medium">
@@ -257,14 +261,14 @@ export default function ProjectsPage() {
<span className="text-sm text-muted-foreground">
({selectedProjects.size} of {sortedProjects.length} selected)
</span>
- </div>
+ </button>
)}
{isLoading || !isInitialized ? (
<div className="grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-6">
- {Array.from({ length: 8 }).map((_, index) => (
+ {Array.from({ length: 8 }, (_, index) => (
<div
- key={index}
+ key={`skeleton-${index}-${Date.now()}`}
className="overflow-hidden bg-background border-none p-0"
>
<Skeleton className="aspect-square w-full bg-muted/50" />
@@ -343,7 +347,7 @@ function ProjectCard({
}
};
loadThumbnail();
- }, [project.id]);
+ }, [project.id, getProjectThumbnail]);
const formatDate = (date: Date): string => {
return date.toLocaleDateString("en-US", {
@@ -375,6 +379,13 @@ function ProjectCard({
}
};
+ const handleCardKeyDown = (e: React.KeyboardEvent) => {
+ if (isSelectionMode && (e.key === "Enter" || e.key === " ")) {
+ e.preventDefault();
+ onSelect?.(project.id, !isSelected);
+ }
+ };
+
const cardContent = (
<Card
className={`overflow-hidden bg-background border-none p-0 transition-all ${
@@ -501,9 +512,14 @@ function ProjectCard({
return (
<>
{isSelectionMode ? (
- <div onClick={handleCardClick} className="block group cursor-pointer">
+ <button
+ type="button"
+ onClick={handleCardClick}
+ onKeyDown={handleCardKeyDown}
+ className="block group cursor-pointer w-full text-left"
+ >
{cardContent}
- </div>
+ </button>
) : (
<Link href={`/editor/${project.id}`} className="block group">
{cardContent}