diff --git a/bun.lockb b/bun.lockb
index 263389b0..8d317c47 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/web/app/(pages)/tasks/page.tsx b/web/app/(pages)/tasks/page.tsx
new file mode 100644
index 00000000..31c7823c
--- /dev/null
+++ b/web/app/(pages)/tasks/page.tsx
@@ -0,0 +1,5 @@
+import { TaskRoute } from "@/components/routes/task/TaskRoute"
+
+export default function TaskPage() {
+ return
+}
diff --git a/web/components/custom/sidebar/partial/task-section.tsx b/web/components/custom/sidebar/partial/task-section.tsx
new file mode 100644
index 00000000..5f725eab
--- /dev/null
+++ b/web/components/custom/sidebar/partial/task-section.tsx
@@ -0,0 +1,107 @@
+import Link from "next/link"
+import { usePathname } from "next/navigation"
+import { useAccount } from "@/lib/providers/jazz-provider"
+import { cn } from "@/lib/utils"
+import { ListOfTasks } from "@/lib/schema/tasks"
+import { LaIcon } from "../../la-icon"
+
+export const TaskSection: React.FC<{ pathname: string }> = ({ pathname }) => {
+ const me = { root: { tasks: [{ id: "1", title: "Test Task" }] } }
+
+ const taskCount = me?.root.tasks?.length || 0
+ const isActive = pathname === "/tasks"
+
+ if (!me) return null
+
+ return (
+
+
+
+
+ )
+}
+
+interface TaskSectionHeaderProps {
+ taskCount: number
+ isActive: boolean
+}
+
+const TaskSectionHeader: React.FC = ({ taskCount, isActive }) => (
+
+
+
+ Tasks
+ {taskCount > 0 && {taskCount}}
+
+
+
+ //
+ //
+ //
+)
+
+interface ListProps {
+ tasks: ListOfTasks
+}
+
+const List: React.FC = ({ tasks }) => {
+ const pathname = usePathname()
+
+ return (
+
+
+
+ )
+}
+
+interface ListItemProps {
+ label: string
+ href: string
+ count: number
+ isActive: boolean
+}
+
+const ListItem: React.FC = ({ label, href, count, isActive }) => (
+
+
+
+
+
+ {count > 0 && (
+
{count}
+ )}
+
+
+)
diff --git a/web/components/custom/sidebar/sidebar.tsx b/web/components/custom/sidebar/sidebar.tsx
index 3679128a..8bbf2637 100644
--- a/web/components/custom/sidebar/sidebar.tsx
+++ b/web/components/custom/sidebar/sidebar.tsx
@@ -14,6 +14,7 @@ import { LinkSection } from "./partial/link-section"
import { PageSection } from "./partial/page-section"
import { TopicSection } from "./partial/topic-section"
import { ProfileSection } from "./partial/profile-section"
+import { TaskSection } from "./partial/task-section"
import { useAccountOrGuest } from "@/lib/providers/jazz-provider"
interface SidebarContextType {
@@ -124,6 +125,8 @@ const SidebarContent: React.FC = React.memo(() => {
{me._type === "Account" && }
{me._type === "Account" && }
{me._type === "Account" && }
+ {/* {me._type === "Account" && } */}
+
diff --git a/web/components/routes/task/TaskForm.tsx b/web/components/routes/task/TaskForm.tsx
new file mode 100644
index 00000000..567df5e8
--- /dev/null
+++ b/web/components/routes/task/TaskForm.tsx
@@ -0,0 +1,43 @@
+"use client"
+
+import React, { useState } from "react"
+import { Task } from "@/lib/schema/tasks"
+import { Input } from "@/components/ui/input"
+import { useAccount } from "@/lib/providers/jazz-provider"
+import { LaIcon } from "@/components/custom/la-icon"
+
+interface TaskFormProps {
+ onAddTask: (task: Task) => void
+}
+
+export const TaskForm: React.FC = ({ onAddTask }) => {
+ const [title, setTitle] = useState("")
+ const { me } = useAccount()
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault()
+ if (title.trim() && me) {
+ const newTask = Task.create(
+ {
+ title,
+ description: "",
+ status: "todo",
+ createdAt: new Date(),
+ updatedAt: new Date()
+ },
+ { owner: me._owner }
+ )
+ onAddTask(newTask)
+ setTitle("")
+ }
+ }
+
+ return (
+
+ )
+}
diff --git a/web/components/routes/task/TaskItem.tsx b/web/components/routes/task/TaskItem.tsx
new file mode 100644
index 00000000..38160141
--- /dev/null
+++ b/web/components/routes/task/TaskItem.tsx
@@ -0,0 +1,21 @@
+import React from "react"
+import { Task } from "@/lib/schema/tasks"
+import { Checkbox } from "@/components/ui/checkbox"
+
+interface TaskItemProps {
+ task: Task
+ onUpdateTask: (taskId: string, updates: Partial) => void
+}
+
+export const TaskItem: React.FC = ({ task, onUpdateTask }) => {
+ const statusChange = (checked: boolean) => {
+ onUpdateTask(task.id, { status: checked ? "done" : "todo" })
+ }
+
+ return (
+
+
+ {task.title}
+
+ )
+}
diff --git a/web/components/routes/task/TaskList.tsx b/web/components/routes/task/TaskList.tsx
new file mode 100644
index 00000000..c5497b52
--- /dev/null
+++ b/web/components/routes/task/TaskList.tsx
@@ -0,0 +1,18 @@
+import React from "react"
+import { Task } from "@/lib/schema/tasks"
+import { TaskItem } from "./TaskItem"
+
+interface TaskListProps {
+ tasks: Task[]
+ onUpdateTask: (taskId: string, updates: Partial) => void
+}
+
+export const TaskList: React.FC = ({ tasks, onUpdateTask }) => {
+ return (
+
+ {tasks.map(task => (
+
+ ))}
+
+ )
+}
diff --git a/web/components/routes/task/TaskRoute.tsx b/web/components/routes/task/TaskRoute.tsx
new file mode 100644
index 00000000..7ef2d180
--- /dev/null
+++ b/web/components/routes/task/TaskRoute.tsx
@@ -0,0 +1,34 @@
+"use client"
+
+import { useAccount } from "@/lib/providers/jazz-provider"
+import { Task } from "@/lib/schema/tasks"
+import { TaskList } from "./TaskList"
+import { TaskForm } from "./TaskForm"
+
+export const TaskRoute: React.FC = () => {
+ const { me } = useAccount({ root: { tasks: [] } })
+ const tasks = me?.root?.tasks || []
+
+ const addTask = (newTask: Task) => {
+ if (me?.root?.tasks) {
+ me.root.tasks.push(newTask)
+ }
+ }
+
+ const updateTask = (taskId: string, updates: Partial) => {
+ if (me?.root?.tasks) {
+ const taskIndex = me.root.tasks.findIndex(task => task?.id === taskId)
+ if (taskIndex !== -1) {
+ Object.assign(me.root.tasks[taskIndex]!, updates)
+ }
+ }
+ }
+
+ return (
+
+
Tasks
+
+
+
+ )
+}
diff --git a/web/lib/schema/index.ts b/web/lib/schema/index.ts
index 12ebd60e..8b15d634 100644
--- a/web/lib/schema/index.ts
+++ b/web/lib/schema/index.ts
@@ -12,6 +12,7 @@ import { CoMap, co, Account, Profile } from "jazz-tools"
import { PersonalPageLists } from "./personal-page"
import { PersonalLinkLists } from "./personal-link"
import { ListOfTopics } from "./master/topic"
+import { ListOfTasks } from "./tasks"
declare module "jazz-tools" {
interface Profile {
@@ -32,6 +33,8 @@ export class UserRoot extends CoMap {
topicsWantToLearn = co.ref(ListOfTopics)
topicsLearning = co.ref(ListOfTopics)
topicsLearned = co.ref(ListOfTopics)
+
+ tasks = co.ref(ListOfTasks)
}
export class LaAccount extends Account {
@@ -59,7 +62,9 @@ export class LaAccount extends Account {
topicsWantToLearn: ListOfTopics.create([], { owner: this }),
topicsLearning: ListOfTopics.create([], { owner: this }),
- topicsLearned: ListOfTopics.create([], { owner: this })
+ topicsLearned: ListOfTopics.create([], { owner: this }),
+
+ tasks: ListOfTasks.create([], { owner: this })
},
{ owner: this }
)
diff --git a/web/lib/schema/tasks.ts b/web/lib/schema/tasks.ts
new file mode 100644
index 00000000..71cf643f
--- /dev/null
+++ b/web/lib/schema/tasks.ts
@@ -0,0 +1,11 @@
+import { co, CoList, CoMap, Encoders } from "jazz-tools"
+
+export class Task extends CoMap {
+ title = co.string
+ description = co.optional.string
+ status = co.literal("todo", "in_progress", "done")
+ createdAt = co.encoded(Encoders.Date)
+ updatedAt = co.encoded(Encoders.Date)
+}
+
+export class ListOfTasks extends CoList.Of(co.ref(Task)) {}