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 && (
+
+
+
+
+
+
+
+ Delete Project
+
+
+ Are you sure you want to delete "{name}"? This action
+ cannot be undone and all flowchart data will be permanently
+ removed.
+
+
+
+
+
+
+
+
+
+
+ )}
+ >
+ )
+}
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}
+
+
+
+ )
+}