diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 15b1464..8b153c5 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -1,23 +1,6 @@ import { createClient } from '@/lib/supabase/server' -import Link from 'next/link' import NewProjectButton from '@/components/NewProjectButton' - -interface Project { - id: string - name: string - updated_at: string -} - -function formatDate(dateString: string): string { - const date = new Date(dateString) - return date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - }) -} +import ProjectList from '@/components/ProjectList' export default async function DashboardPage() { const supabase = await createClient() @@ -60,47 +43,7 @@ export default async function DashboardPage() { - {projects && projects.length > 0 ? ( -
- {projects.map((project: Project) => ( - -

- {project.name} -

-

- Last updated: {formatDate(project.updated_at)} -

- - ))} -
- ) : ( -
- -

- No projects yet -

-

- Get started by creating your first visual novel project. -

-
- )} + ) } diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx new file mode 100644 index 0000000..7d19ac8 --- /dev/null +++ b/src/components/ProjectCard.tsx @@ -0,0 +1,160 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import { createClient } from '@/lib/supabase/client' + +interface ProjectCardProps { + id: string + name: string + updatedAt: string + onDelete: (id: string) => void +} + +function formatDate(dateString: string): string { + const date = new Date(dateString) + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + }) +} + +export default function ProjectCard({ + id, + name, + updatedAt, + onDelete, +}: ProjectCardProps) { + const [showDeleteDialog, setShowDeleteDialog] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) + const router = useRouter() + + const handleCardClick = () => { + router.push(`/editor/${id}`) + } + + const handleDeleteClick = (e: React.MouseEvent) => { + e.stopPropagation() + setShowDeleteDialog(true) + } + + const handleConfirmDelete = async () => { + setIsDeleting(true) + + const supabase = createClient() + const { error } = await supabase.from('projects').delete().eq('id', id) + + if (error) { + setIsDeleting(false) + setShowDeleteDialog(false) + alert('Failed to delete project: ' + error.message) + return + } + + setIsDeleting(false) + setShowDeleteDialog(false) + onDelete(id) + } + + const handleCancelDelete = () => { + if (!isDeleting) { + setShowDeleteDialog(false) + } + } + + return ( + <> +
+ +

+ {name} +

+

+ Last updated: {formatDate(updatedAt)} +

+
+ + {showDeleteDialog && ( +
+ + )} + + ) +} diff --git a/src/components/ProjectList.tsx b/src/components/ProjectList.tsx new file mode 100644 index 0000000..1f0e234 --- /dev/null +++ b/src/components/ProjectList.tsx @@ -0,0 +1,82 @@ +'use client' + +import { useState, useCallback } from 'react' +import ProjectCard from './ProjectCard' +import Toast from './Toast' + +interface Project { + id: string + name: string + updated_at: string +} + +interface ProjectListProps { + initialProjects: Project[] +} + +export default function ProjectList({ initialProjects }: ProjectListProps) { + const [projects, setProjects] = useState(initialProjects) + const [toast, setToast] = useState<{ + message: string + type: 'success' | 'error' + } | null>(null) + + const handleDelete = useCallback((deletedId: string) => { + setProjects((prev) => prev.filter((p) => p.id !== deletedId)) + setToast({ message: 'Project deleted successfully', type: 'success' }) + }, []) + + const handleCloseToast = useCallback(() => { + setToast(null) + }, []) + + if (projects.length === 0) { + return ( +
+ +

+ No projects yet +

+

+ Get started by creating your first visual novel project. +

+
+ ) + } + + return ( + <> +
+ {projects.map((project) => ( + + ))} +
+ {toast && ( + + )} + + ) +} diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx new file mode 100644 index 0000000..18ef18a --- /dev/null +++ b/src/components/Toast.tsx @@ -0,0 +1,84 @@ +'use client' + +import { useEffect } from 'react' + +interface ToastProps { + message: string + type: 'success' | 'error' + onClose: () => void +} + +export default function Toast({ message, type, onClose }: ToastProps) { + useEffect(() => { + const timer = setTimeout(() => { + onClose() + }, 3000) + + return () => clearTimeout(timer) + }, [onClose]) + + const bgColor = + type === 'success' + ? 'bg-green-600 dark:bg-green-700' + : 'bg-red-600 dark:bg-red-700' + + const icon = + type === 'success' ? ( + + + + ) : ( + + + + ) + + return ( +
+
+ {icon} + {message} + +
+
+ ) +}