import { useState } from "react"; import { h, invokeCapability, useLiveField, type LiveFieldProp, type ObjectRef, type ReactComponentImplementationProps, } from "@quixos/web-studio-react-runtime"; import { TodoComponent, type TodoComponentProps, type TodoReactComponentAction, } from "./gen/quixos.todo.TodoReactComponent.react.js"; import type { ProjectReactComponentRenderProps } from "./gen/quixos.todo.ProjectReactComponent.react.js"; type TaskRef = TodoComponentProps["forObject"]; type ProjectCaminoProps = { project_id: ObjectRef<"atom:project">; name: LiveFieldProp; tasks: TaskRef[]; }; type ProjectComponentProps = ReactComponentImplementationProps< ProjectCaminoProps, ProjectReactComponentRenderProps, Record >; const colors = { ink: "#28362d", muted: "#72786f", cream: "#fffdf5", paper: "#f6f0df", line: "#d9d0ba", moss: "#5f7f63", honey: "#d99b3d", }; export default function ProjectComponent({ camino, render }: ProjectComponentProps) { const [name, setName] = useLiveField(camino.name); const [selectedTask, setSelectedTask] = useState(null); const [newTaskTitle, setNewTaskTitle] = useState(""); const [addingTask, setAddingTask] = useState(false); const [addTaskError, setAddTaskError] = useState(""); const handleTaskAction = (task: TaskRef, action: TodoReactComponentAction) => { if (action.type === "SELECT") setSelectedTask(task); }; const addTask = async () => { if (addingTask || !newTaskTitle.trim()) return; setAddingTask(true); setAddTaskError(""); try { await invokeCapability( camino.project_id, "interface:project-actions@1", "operation:project-actions:add-task", newTaskTitle, ); setNewTaskTitle(""); } catch (error) { setAddTaskError(error instanceof Error ? error.message : String(error)); } finally { setAddingTask(false); } }; return (
void setName(event.currentTarget.value)} style={{ minWidth: 0, width: "100%", border: "none", borderBottom: `1px solid ${colors.line}`, background: "transparent", color: colors.ink, fontFamily: "Georgia, ui-serif, serif", fontSize: render.compact ? "17px" : "22px", fontWeight: 700, padding: "3px 0 5px", }} /> A little clearing for useful things.
{camino.tasks.length} {camino.tasks.length === 1 ? "task" : "tasks"}
{ event.preventDefault(); void addTask(); }} style={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr) auto", gap: "8px" }} > setNewTaskTitle(event.currentTarget.value)} placeholder="What wants doing?" disabled={addingTask} style={{ minWidth: 0, border: `1px solid ${colors.line}`, borderRadius: "9px", background: "#fff", padding: "8px 10px" }} /> {addTaskError ?
{addTaskError}
: null}
{camino.tasks.length === 0 ? (
Your project is quiet. Add the first task when it arrives.
) : camino.tasks.map((task) => ( handleTaskAction(task, action)} fallback={
Waking task…
} /> ))}
); }