adjust server detail default and status layout

This commit is contained in:
npc0-hue
2026-08-27 10:17:04 +08:00
parent 2937ece1bc
commit 0940780058
2 changed files with 23 additions and 10 deletions
+13 -5
View File
@@ -35,11 +35,19 @@ describe("ServerDetailPage config write approval", () => {
it("keeps the detail header focused on a single shared action row", () => {
expect(serverDetailPageSource).not.toContain("Run 心跳");
expect(serverDetailPageSource).not.toContain("已自动附着");
expect(serverDetailPageSource).toContain('<span>返回列表</span>');
expect(serverDetailPageSource).toContain('<span>刷新</span>');
expect(serverDetailPageSource).toContain('<span>打开终端</span>');
expect(serverDetailPageSource).toContain('<span>启动</span>');
expect(serverDetailPageSource).toContain('<span>停止</span>');
const headerSource = serverDetailPageSource.split('<header className="server-detail-header">')[1]?.split('</header>')[0] ?? "";
expect(headerSource).toContain('<span>返回列表</span>');
expect(headerSource).not.toContain('<span>刷新</span>');
expect(headerSource).toContain('<span>打开终端</span>');
expect(headerSource).toContain('<span>启动</span>');
expect(headerSource).toContain('<span>停止</span>');
const actionStripSource = headerSource.split('<div className="action-strip">')[1]?.split('</div>')[0] ?? "";
expect(actionStripSource.indexOf('<span>停止</span>')).toBeLessThan(actionStripSource.indexOf('detailStateText'));
});
it("opens the plugin user management page by default when available", () => {
expect(serverDetailPageSource).toContain('page.key === "players"');
expect(serverDetailPageSource).toContain('setSection(`plugin:${defaultPluginPage.key}`)');
});
it("maps platform diff preview responses into the display diff without losing approval metadata", () => {
+10 -5
View File
@@ -52,6 +52,7 @@ export function ServerDetailPage(props: PageComponentProps) {
const [confirm, setConfirm] = useState<null | { title: string; description: string; danger?: boolean; run: () => Promise<void> }>(null);
const [confirmBusy, setConfirmBusy] = useState(false);
const [terminalOpen, setTerminalOpen] = useState(false);
const defaultSectionResolvedRef = useRef(false);
const refresh = useCallback(async () => {
if (!serverId) {
@@ -112,6 +113,7 @@ export function ServerDetailPage(props: PageComponentProps) {
const detailFreshness = instance.status === "ready" ? runtimeObservationFreshness(instance.data, runEndpoint) : "unverified";
const detailStateText = instance.status === "ready" && detailFreshness === "fresh" ? stateLabel(instance.data.state) : instance.status === "ready" ? `最后观测:${stateLabel(instance.data.state)}Run 未验证)` : "未验证";
const detailSections = useMemo(() => serverDetailSectionEntries(readyPlugin), [readyPlugin]);
const defaultPluginPage = readyPlugin?.pages.find((page) => page.key === "players") ?? readyPlugin?.pages[0];
useEffect(() => {
if (!params.routeKey || !readyPlugin?.pages.some((page) => page.key === params.routeKey)) return;
@@ -123,6 +125,13 @@ export function ServerDetailPage(props: PageComponentProps) {
setSection(detailSections[0]?.id ?? "manage");
}, [detailSections, section]);
useEffect(() => {
if (params.routeKey || !defaultPluginPage || defaultSectionResolvedRef.current) return;
defaultSectionResolvedRef.current = true;
if (section !== "manage") return;
setSection(`plugin:${defaultPluginPage.key}`);
}, [defaultPluginPage, params.routeKey, section]);
function requestLifecycle(current: ServerInstanceResponse, action: "start" | "stop") {
setConfirm({
title: action === "start" ? "启动服务器" : "停止服务器",
@@ -181,11 +190,6 @@ export function ServerDetailPage(props: PageComponentProps) {
<MoonStar size={16} />
<span></span>
</button>
<button type="button" className="icon-command" onClick={() => void refresh()}>
<Sparkles size={16} />
<span></span>
</button>
<span className={cx("status-pill", detailFreshness === "fresh" ? statusClass(instance.data.state) : "status-disabled")}>{detailStateText}</span>
<button
type="button"
className="icon-command"
@@ -214,6 +218,7 @@ export function ServerDetailPage(props: PageComponentProps) {
<Square size={15} />
<span></span>
</button>
<span className={cx("status-pill", detailFreshness === "fresh" ? statusClass(instance.data.state) : "status-disabled")}>{detailStateText}</span>
</div>
</div>
</header>