Checkpoint: Initial stable CheckFlow base before i18n and demo mode
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user