adjust server detail default and status layout
This commit is contained in:
@@ -35,11 +35,19 @@ describe("ServerDetailPage config write approval", () => {
|
|||||||
it("keeps the detail header focused on a single shared action row", () => {
|
it("keeps the detail header focused on a single shared action row", () => {
|
||||||
expect(serverDetailPageSource).not.toContain("Run 心跳");
|
expect(serverDetailPageSource).not.toContain("Run 心跳");
|
||||||
expect(serverDetailPageSource).not.toContain("已自动附着");
|
expect(serverDetailPageSource).not.toContain("已自动附着");
|
||||||
expect(serverDetailPageSource).toContain('<span>返回列表</span>');
|
const headerSource = serverDetailPageSource.split('<header className="server-detail-header">')[1]?.split('</header>')[0] ?? "";
|
||||||
expect(serverDetailPageSource).toContain('<span>刷新</span>');
|
expect(headerSource).toContain('<span>返回列表</span>');
|
||||||
expect(serverDetailPageSource).toContain('<span>打开终端</span>');
|
expect(headerSource).not.toContain('<span>刷新</span>');
|
||||||
expect(serverDetailPageSource).toContain('<span>启动</span>');
|
expect(headerSource).toContain('<span>打开终端</span>');
|
||||||
expect(serverDetailPageSource).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", () => {
|
it("maps platform diff preview responses into the display diff without losing approval metadata", () => {
|
||||||
|
|||||||
@@ -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 [confirm, setConfirm] = useState<null | { title: string; description: string; danger?: boolean; run: () => Promise<void> }>(null);
|
||||||
const [confirmBusy, setConfirmBusy] = useState(false);
|
const [confirmBusy, setConfirmBusy] = useState(false);
|
||||||
const [terminalOpen, setTerminalOpen] = useState(false);
|
const [terminalOpen, setTerminalOpen] = useState(false);
|
||||||
|
const defaultSectionResolvedRef = useRef(false);
|
||||||
|
|
||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
if (!serverId) {
|
if (!serverId) {
|
||||||
@@ -112,6 +113,7 @@ export function ServerDetailPage(props: PageComponentProps) {
|
|||||||
const detailFreshness = instance.status === "ready" ? runtimeObservationFreshness(instance.data, runEndpoint) : "unverified";
|
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 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 detailSections = useMemo(() => serverDetailSectionEntries(readyPlugin), [readyPlugin]);
|
||||||
|
const defaultPluginPage = readyPlugin?.pages.find((page) => page.key === "players") ?? readyPlugin?.pages[0];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!params.routeKey || !readyPlugin?.pages.some((page) => page.key === params.routeKey)) return;
|
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");
|
setSection(detailSections[0]?.id ?? "manage");
|
||||||
}, [detailSections, section]);
|
}, [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") {
|
function requestLifecycle(current: ServerInstanceResponse, action: "start" | "stop") {
|
||||||
setConfirm({
|
setConfirm({
|
||||||
title: action === "start" ? "启动服务器" : "停止服务器",
|
title: action === "start" ? "启动服务器" : "停止服务器",
|
||||||
@@ -181,11 +190,6 @@ export function ServerDetailPage(props: PageComponentProps) {
|
|||||||
<MoonStar size={16} />
|
<MoonStar size={16} />
|
||||||
<span>返回列表</span>
|
<span>返回列表</span>
|
||||||
</button>
|
</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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="icon-command"
|
className="icon-command"
|
||||||
@@ -214,6 +218,7 @@ export function ServerDetailPage(props: PageComponentProps) {
|
|||||||
<Square size={15} />
|
<Square size={15} />
|
||||||
<span>停止</span>
|
<span>停止</span>
|
||||||
</button>
|
</button>
|
||||||
|
<span className={cx("status-pill", detailFreshness === "fresh" ? statusClass(instance.data.state) : "status-disabled")}>{detailStateText}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Reference in New Issue
Block a user