Files
writer-work-flow/apps/web/components/foreshadow/ForeshadowBoard.tsx
Yaojia Wang 8779530806 feat(ux): T4-a 全局 AI 工具条 — 项目页顶部常驻写本章/审稿/大纲/设定库/工具箱
新 lib/nav/ai-tools.ts (aiToolItems) + components/AiToolbar.tsx 服务端组件;AppShell 顶栏下渲染(仅项目页),用 --chrome CSS 变量统一 chrome 高度,6 个固定高度页改用 calc(100vh-var(--chrome,4rem)) 适配。TDD: ai-tools 测。前端门禁绿: lint/tsc/vitest/build。
2026-06-20 18:19:46 +02:00

73 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useMemo } from "react";
import { AppShell } from "@/components/AppShell";
import type { ForeshadowView, ProjectResponse } from "@/lib/api/types";
import {
LANES,
groupByStatus,
type ForeshadowStatus,
} from "@/lib/foreshadow/board";
import { useForeshadow } from "@/lib/foreshadow/useForeshadow";
import { KanbanColumn } from "./KanbanColumn";
import { RegisterForm } from "./RegisterForm";
interface ForeshadowBoardProps {
project: ProjectResponse;
initialItems: ForeshadowView[];
}
// 伏笔看板主体UX §6.8):四泳道 + 登记/状态变更交互。
// RSC 取全量种入Client 承载登记/转移(乐观 + 回滚 + Toast
export function ForeshadowBoard({
project,
initialItems,
}: ForeshadowBoardProps) {
const { items, busy, register, transition } = useForeshadow(initialItems);
const lanes = useMemo(() => groupByStatus(items), [items]);
const onTransition = (
code: string,
toStatus: ForeshadowStatus | null,
progressNote: string,
): void => {
void transition(code, {
projectId: project.id,
toStatus,
progressEntry: progressNote ? { note: progressNote } : null,
});
};
return (
<AppShell
title={`${project.title}`}
subtitle="伏笔看板"
projectId={project.id}
activeNav="foreshadow"
>
<div className="flex h-[calc(100vh-var(--chrome,4rem))] flex-col p-4">
<div className="mb-4 flex items-start justify-between gap-4">
<h1 className="font-serif text-lg text-ink"></h1>
<RegisterForm
projectId={project.id}
busy={busy}
onRegister={register}
/>
</div>
<div className="grid min-h-0 flex-1 grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4">
{LANES.map((status) => (
<KanbanColumn
key={status}
status={status}
items={lanes[status]}
busy={busy}
onTransition={onTransition}
/>
))}
</div>
</div>
</AppShell>
);
}