import { useState } from "react";
import type { AgendaView, HandoffCard, SignalView, SummaryView } from "../../data";
import type { Matrix, MatrixItem } from "../../matrix";
import { Badge } from "../components/badge";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../components/table";
import { tenths } from "../format";
import { Empty, Panel, Pending } from "../frame";
import { type Loaded, useLoaded } from "../lib/api";
import { cn } from "../lib/cn";
import { GoalsList, HandoffList, ItemCard, MovesList, ReasonTags } from "../parts";
export const LAYOUTS = [
{ value: "matrix", label: "matrix" },
{ value: "list", label: "list" },
{ value: "briefing", label: "briefing" },
] as const;
export type Layout = (typeof LAYOUTS)[number]["value"];
export function isLayout(value: string | null): value is Layout {
return LAYOUTS.some((l) => l.value === value);
}
const QUADRANTS = [
{ id: "now", title: "Do now", note: "matters and cannot wait", lit: true },
{
id: "plan",
title: "Give it a slot",
note: "matters, nothing forcing it",
lit: false,
},
{
id: "noise",
title: "Loud, not load-bearing",
note: "pressing but serves nothing",
lit: false,
},
{ id: "later", title: "Let it sit", note: "neither", lit: false },
] as const;
const FORTNIGHT_DAYS = 14;
function goalsOf(matrix: Matrix): MatrixItem[] {
return [...matrix.now, ...matrix.plan, ...matrix.noise, ...matrix.later].filter(
(i) => i.kind === "goal"
);
}
function rankedOf(matrix: Matrix): { item: MatrixItem; quadrant: string }[] {
return QUADRANTS.flatMap(({ id, title }) =>
(matrix[id] as MatrixItem[]).map((item) => ({ item, quadrant: title }))
);
}
function Quadrant({
title,
note,
lit,
items,
onSaved,
compact,
}: {
title: string;
note: string;
lit: boolean;
items: MatrixItem[];
onSaved: () => void;
compact?: boolean;
}) {
return (
{title}
{note}
{items.length === 0 ? (
nothing here
) : (
items.map((item) => (
))
)}
);
}
function Fortnight() {
const view = useLoaded(`/api/summary?days=${FORTNIGHT_DAYS}`);
const s = view.state === "ready" ? view.data : null;
return (
);
}
function Figure({ n, label }: { n: string; label: string }) {
return (
{n}
{label}
);
}
function DueBadges() {
const signal = useLoaded("/api/signal");
if (signal.state !== "ready") return ;
const due = Object.entries(signal.data.due).filter(([, b]) => b.state === "due");
if (due.length === 0) return Nothing is due. ;
return (
{due.map(([name, badge]) => (
{name}
{badge.detail}
))}
);
}
function Sidebar({ matrix, handoffs }: { matrix: Matrix; handoffs: HandoffCard[] }) {
return (
);
}
function MatrixLayout({
matrix,
handoffs,
moves,
onSaved,
}: {
matrix: Matrix;
handoffs: HandoffCard[];
moves: AgendaView["moves"];
onSaved: () => void;
}) {
return (
{moves.map((move, i) => (
{String(i + 1).padStart(2, "0")}
{move.move}
{move.because}
))}
urgent
not urgent
important
{QUADRANTS.slice(0, 2).map((q) => (
))}
not important
{QUADRANTS.slice(2).map((q) => (
))}
);
}
function ListLayout({
matrix,
handoffs,
moves,
}: {
matrix: Matrix;
handoffs: HandoffCard[];
moves: AgendaView["moves"];
onSaved: () => void;
}) {
return (
{["Item", "Placed", "Why", "Next"].map((column) => (
{column}
))}
{rankedOf(matrix).map(({ item, quadrant }) => (
{item.label}
{quadrant}
{item.detail}
))}
{QUADRANTS.map((q) => (
{(matrix[q.id] as MatrixItem[]).length}
{q.title}
))}
);
}
function BriefingLayout({
matrix,
handoffs,
moves,
onSaved,
}: {
matrix: Matrix;
handoffs: HandoffCard[];
moves: AgendaView["moves"];
onSaved: () => void;
}) {
return (
If you only do three things
Where you left off
The grid
{QUADRANTS.map((q) => (
))}
);
}
function Heading({ agenda, matrix }: { agenda: AgendaView; matrix: Matrix }) {
const written = agenda.generatedAt
? `agenda written ${agenda.ageHours ?? 0}h ago at session stop`
: "no agenda written yet";
return (
{new Date().toLocaleDateString(undefined, {
weekday: "long",
day: "numeric",
month: "long",
})}
{written} ยท {matrix.now.length} items need you
{matrix.unranked > 0 && (
{matrix.unranked} project(s) with no purpose on record
)}
);
}
export function Today({ layout }: { layout: Layout }) {
const [nonce, setNonce] = useState(0);
const matrix = useLoaded(`/api/matrix?v=${nonce}`);
const agenda = useLoaded("/api/agenda");
const handoffs = useLoaded("/api/handoffs");
const reload = () => setNonce((n) => n + 1);
const waiting: Loaded[] = [matrix, agenda, handoffs];
const notReady = waiting.find((v) => v.state !== "ready");
if (notReady || matrix.state !== "ready") return ;
if (agenda.state !== "ready" || handoffs.state !== "ready") return null;
const shared = {
matrix: matrix.data,
handoffs: handoffs.data,
moves: agenda.data.moves,
onSaved: reload,
};
return (
{layout === "matrix" && }
{layout === "list" && }
{layout === "briefing" && }
);
}