From 09373eb4a3f792d96d97d81f4fb86539d76e5702 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 30 Jun 2025 19:58:36 +0200 Subject: feat: implement project management features with storage integration, including project creation, deletion, and renaming dialogs --- apps/web/src/app/projects/page.tsx | 488 ++++++++++++++++++++----------------- 1 file changed, 260 insertions(+), 228 deletions(-) (limited to 'apps/web/src/app/projects') diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index 82bae5d1..c983894d 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -1,228 +1,260 @@ -"use client"; - -import Link from "next/link"; -import { useState } from "react"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; -import { - ChevronLeft, - Plus, - Calendar, - MoreHorizontal, - Video, -} from "lucide-react"; -import { TProject } from "@/types/project"; -import Image from "next/image"; -import { - DropdownMenu, - DropdownMenuItem, - DropdownMenuContent, - DropdownMenuTrigger, - DropdownMenuSeparator, -} from "@/components/ui/dropdown-menu"; - -// Hard-coded project data -const mockProjects: TProject[] = [ - { - id: "1", - name: "Summer Vacation Highlights", - createdAt: new Date("2024-12-15"), - updatedAt: new Date("2024-12-20"), - thumbnail: - "https://plus.unsplash.com/premium_photo-1750854354243-81f40af63a73?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", - }, - { - id: "2", - name: "Product Demo Video", - createdAt: new Date("2024-12-10"), - updatedAt: new Date("2024-12-18"), - thumbnail: - "https://images.unsplash.com/photo-1750875936215-0c35c1742cd6?q=80&w=688&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", - }, - { - id: "3", - name: "Wedding Ceremony Edit", - createdAt: new Date("2024-12-05"), - updatedAt: new Date("2024-12-16"), - thumbnail: - "https://images.unsplash.com/photo-1750967991618-7b64a3025381?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", - }, - { - id: "4", - name: "Travel Vlog - Japan", - createdAt: new Date("2024-11-28"), - updatedAt: new Date("2024-12-14"), - thumbnail: - "https://images.unsplash.com/photo-1750639258774-9a714379a093?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", - }, -]; - -// Mock duration data (in seconds) -const mockDurations: Record = { - "1": 245, // 4:05 - "2": 120, // 2:00 - "3": 1800, // 30:00 - "4": 780, // 13:00 - "5": 360, // 6:00 - "6": 180, // 3:00 -}; - -export default function ProjectsPage() { - return ( -
-
- - - Back - -
- -
-
-
-
-
-

- Your Projects -

-

- {mockProjects.length}{" "} - {mockProjects.length === 1 ? "project" : "projects"} -

-
-
- -
-
- - {mockProjects.length === 0 ? ( -
-
-
-

No projects yet

-

- Start creating your first video project. Import media, edit, and - export professional videos. -

- - - -
- ) : ( -
- {mockProjects.map((project, index) => ( - - ))} -
- )} -
-
- ); -} - -function ProjectCard({ project }: { project: TProject }) { - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - - const formatDuration = (seconds: number): string => { - const minutes = Math.floor(seconds / 60); - const remainingSeconds = Math.floor(seconds % 60); - return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`; - }; - - const formatDate = (date: Date): string => { - return date.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - year: "numeric", - }); - }; - - return ( - - -
- {/* Thumbnail preview */} -
- Project thumbnail -
- - {/* Duration badge */} -
- {formatDuration(mockDurations[project.id] || 0)} -
-
- - -
-

- {project.name} -

- - - - - { - e.preventDefault(); - e.stopPropagation(); - console.log("close"); - }} - > - Rename - Duplicate - - - Delete - - - -
- -
-
- - Created {formatDate(project.createdAt)} -
-
-
-
- - ); -} - -function CreateButton() { - return ( - - ); -} +"use client"; + +import Link from "next/link"; +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + ChevronLeft, + Plus, + Calendar, + MoreHorizontal, + Video, + Loader2, +} from "lucide-react"; +import { TProject } from "@/types/project"; +import Image from "next/image"; +import { + DropdownMenu, + DropdownMenuItem, + DropdownMenuContent, + DropdownMenuTrigger, + DropdownMenuSeparator, +} 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"; + +export default function ProjectsPage() { + const { createNewProject, savedProjects, isLoading, isInitialized } = + useProjectStore(); + const router = useRouter(); + + const handleCreateProject = async () => { + const projectId = await createNewProject("New Project"); + console.log("projectId", projectId); + router.push(`/editor/${projectId}`); + }; + + return ( +
+
+ + + Back + +
+ +
+
+
+
+
+

+ Your Projects +

+

+ {savedProjects.length}{" "} + {savedProjects.length === 1 ? "project" : "projects"} +

+
+
+ +
+
+ + {isLoading || !isInitialized ? ( +
+ +
+ ) : savedProjects.length === 0 ? ( + + ) : ( +
+ {savedProjects.map((project) => ( + + ))} +
+ )} +
+
+ ); +} + +function ProjectCard({ project }: { project: TProject }) { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false); + const { deleteProject, renameProject, duplicateProject } = useProjectStore(); + + const formatDate = (date: Date): string => { + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); + }; + + const handleDeleteProject = async () => { + await deleteProject(project.id); + setIsDropdownOpen(false); + }; + + const handleRenameProject = async (newName: string) => { + await renameProject(project.id, newName); + setIsRenameDialogOpen(false); + }; + + const handleDuplicateProject = async () => { + setIsDropdownOpen(false); + await duplicateProject(project.id); + }; + + return ( + <> + + +
+ {/* Thumbnail preview or placeholder */} +
+ {project.thumbnail ? ( + Project thumbnail + ) : ( +
+
+ )} +
+
+ + +
+

+ {project.name} +

+ + + + + { + e.preventDefault(); + e.stopPropagation(); + }} + > + { + e.preventDefault(); + e.stopPropagation(); + setIsDropdownOpen(false); + setIsRenameDialogOpen(true); + }} + > + Rename + + { + e.preventDefault(); + e.stopPropagation(); + handleDuplicateProject(); + }} + > + Duplicate + + + { + e.preventDefault(); + e.stopPropagation(); + setIsDropdownOpen(false); + setIsDeleteDialogOpen(true); + }} + > + Delete + + + +
+ +
+
+ + Created {formatDate(project.createdAt)} +
+
+
+
+ + + + + ); +} + +function CreateButton({ onClick }: { onClick?: () => void }) { + return ( + + ); +} + +function NoProjects({ onCreateProject }: { onCreateProject: () => void }) { + return ( +
+
+
+

No projects yet

+

+ Start creating your first video project. Import media, edit, and export + professional videos. +

+ +
+ ); +} -- cgit v1.3.1