feat: add visual settings and readiness gate
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
CubeIcon,
|
||||
EyeIcon,
|
||||
EyeSlashIcon,
|
||||
GearSixIcon,
|
||||
ImageIcon,
|
||||
LockSimpleIcon,
|
||||
PaperPlaneTiltIcon,
|
||||
@@ -21,8 +22,9 @@ import {
|
||||
WarningCircleIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import Image from "next/image";
|
||||
import { FormEvent, useMemo, useState } from "react";
|
||||
import { FormEvent, useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { SettingsCenter } from "@/components/settings-center";
|
||||
import {
|
||||
initialLayers,
|
||||
initialMessages,
|
||||
@@ -30,8 +32,10 @@ import {
|
||||
workflowStages,
|
||||
} from "@/lib/demo";
|
||||
import type { ChatMessage } from "@/types/workflow";
|
||||
import type { SettingsReadiness } from "@/types/settings";
|
||||
|
||||
const iconWeight = "regular" as const;
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8000";
|
||||
|
||||
export function WorkflowStudio() {
|
||||
const [layers, setLayers] = useState(initialLayers);
|
||||
@@ -39,12 +43,32 @@ export function WorkflowStudio() {
|
||||
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
|
||||
const [draft, setDraft] = useState("");
|
||||
const [furnitureMode, setFurnitureMode] = useState<"reference" | "remove">("reference");
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [readiness, setReadiness] = useState<SettingsReadiness | null>(null);
|
||||
|
||||
const currentDirection = useMemo(
|
||||
() => styleDirections.find((item) => item.id === selectedDirection) ?? styleDirections[0],
|
||||
[selectedDirection],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadReadiness() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/v1/readiness`, { cache: "no-store" });
|
||||
if (!response.ok) return;
|
||||
const current = await response.json() as SettingsReadiness;
|
||||
setReadiness(current);
|
||||
if (!current.ready && !window.sessionStorage.getItem("settings-intro-seen")) {
|
||||
window.sessionStorage.setItem("settings-intro-seen", "true");
|
||||
setSettingsOpen(true);
|
||||
}
|
||||
} catch {
|
||||
setReadiness(null);
|
||||
}
|
||||
}
|
||||
void loadReadiness();
|
||||
}, []);
|
||||
|
||||
function toggleLayer(id: string) {
|
||||
setLayers((items) =>
|
||||
items.map((item) => (item.id === id ? { ...item, visible: !item.visible } : item)),
|
||||
@@ -84,9 +108,19 @@ export function WorkflowStudio() {
|
||||
|
||||
<div className="topbar-actions">
|
||||
<span className="save-state"><CheckIcon size={14} weight="bold" /> 已保存</span>
|
||||
<button className="button button-secondary settings-button" type="button" onClick={() => setSettingsOpen(true)}>
|
||||
<GearSixIcon size={16} /> 系统设置
|
||||
{readiness && !readiness.ready ? <span className="settings-alert-dot" aria-label="有必备配置未完成" /> : null}
|
||||
</button>
|
||||
<button className="button button-secondary" type="button">版本记录</button>
|
||||
<button className="button button-primary" type="button">
|
||||
确认结构 <ArrowRightIcon size={16} weight="bold" />
|
||||
<button
|
||||
className="button button-primary"
|
||||
type="button"
|
||||
disabled={!readiness?.ready}
|
||||
onClick={() => { if (!readiness?.ready) setSettingsOpen(true); }}
|
||||
title={!readiness?.ready ? "请先完成系统设置" : undefined}
|
||||
>
|
||||
{readiness?.ready ? "确认结构" : "配置后开始"} <ArrowRightIcon size={16} weight="bold" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
@@ -327,6 +361,11 @@ export function WorkflowStudio() {
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
<SettingsCenter
|
||||
open={settingsOpen}
|
||||
onClose={() => setSettingsOpen(false)}
|
||||
onReadinessChange={setReadiness}
|
||||
/>
|
||||
</main>
|
||||
</Tooltip.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user