Checkpoint: Initial stable CheckFlow base before i18n and demo mode

This commit is contained in:
2026-08-20 14:01:12 +09:00
parent ab601d90ac
commit 3e56120b00
42 changed files with 4727 additions and 167 deletions
+106
View File
@@ -0,0 +1,106 @@
"use client";
import { useState, useCallback } from "react";
import { Sidebar } from "./Sidebar";
import { TaskList } from "../tasks/TaskList";
import { TaskDetail } from "../tasks/TaskDetail";
interface User { id: string; name?: string | null; email?: string | null; }
interface List { id: string; name: string; color: string; icon: string; _count?: { tasks: number } }
interface Task {
id: string; listId: string; parentId: string | null; title: string; note: string | null;
completed: boolean; completedAt: string | null; dueDate: string | null; priority: number;
sortOrder: number; createdAt: string; updatedAt: string;
children: Task[]; tags: { tag: { id: string; name: string; color: string } }[];
}
export function AppShell({ user }: { user: User }) {
const [lists, setLists] = useState<List[]>([]);
const [selectedListId, setSelectedListId] = useState<string | null>(null);
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const [tasks, setTasks] = useState<Task[]>([]);
const [sidebarOpen, setSidebarOpen] = useState(false);
const [showCompleted, setShowCompleted] = useState(false);
const [refreshKey, setRefreshKey] = useState(0);
const refresh = useCallback(() => setRefreshKey((k) => k + 1), []);
const handleTaskSelect = useCallback((task: Task | null) => {
setSelectedTask(task);
}, []);
const handleTaskUpdate = useCallback((updated: Task) => {
setTasks((prev) => prev.map((t) => (t.id === updated.id ? updated : t)));
setSelectedTask(updated);
}, []);
const handleListSelect = useCallback((id: string) => {
setSelectedListId(id);
setSelectedTask(null);
setSidebarOpen(false);
}, []);
return (
<div className="app-layout">
{/* Mobile overlay */}
{sidebarOpen && (
<div
className="modal-overlay"
style={{ zIndex: 25 }}
onClick={() => setSidebarOpen(false)}
/>
)}
<Sidebar
user={user}
lists={lists}
setLists={setLists}
selectedListId={selectedListId}
onListSelect={handleListSelect}
mobileOpen={sidebarOpen}
onClose={() => setSidebarOpen(false)}
/>
<div className="main-content">
<TaskList
key={`${selectedListId}-${refreshKey}`}
user={user}
listId={selectedListId}
lists={lists}
tasks={tasks}
setTasks={setTasks}
selectedTaskId={selectedTask?.id ?? null}
onTaskSelect={handleTaskSelect}
showCompleted={showCompleted}
onToggleCompleted={() => setShowCompleted((p) => !p)}
onMenuOpen={() => setSidebarOpen(true)}
onRefresh={refresh}
/>
</div>
{selectedTask && (
<TaskDetail
task={selectedTask}
onClose={() => setSelectedTask(null)}
onUpdate={handleTaskUpdate}
onDelete={() => { setSelectedTask(null); refresh(); }}
listId={selectedTask.listId}
/>
)}
{/* Mobile FAB */}
<button
className="fab"
id="mobile-add-task"
aria-label="Add task"
onClick={() => {
// trigger add task from task list — use custom event
document.dispatchEvent(new CustomEvent("checkflow:addTask"));
}}
>
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
</svg>
</button>
</div>
);
}