Fix server create plugin filtering

This commit is contained in:
npc0-hue
2026-09-07 18:17:33 +08:00
parent 86167a3a1b
commit f69429bfc7
5 changed files with 154 additions and 8 deletions
@@ -3,7 +3,7 @@ import { type ChangeEvent, type FormEvent, useEffect, useMemo, useState } from "
import type { GamePluginResponse, ServerDeploymentResponse, ServerDeploymentRevealResponse } from "../api/types";
import { ManagementDialog } from "./OperationControls";
import { pluginCreateInputDefaults, pluginLabel, type ServerCreateFormState } from "../contracts/serverManagement";
import { pluginCreateInputDefaults, pluginCreateInputValues, pluginLabel, serverCreatePluginOptions, type ServerCreateFormState } from "../contracts/serverManagement";
import { cx } from "../utils/classes";
type WorkflowKind = "create" | "edit";
@@ -25,7 +25,8 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, dep
const [form, setForm] = useState<ServerCreateFormState>(initialForm);
const [revealBusy, setRevealBusy] = useState(false);
const [revealError, setRevealError] = useState("");
const selectedPlugin = useMemo(() => plugins.find((plugin) => plugin.id === form.pluginId), [form.pluginId, plugins]);
const selectablePlugins = useMemo(() => kind === "create" ? serverCreatePluginOptions(plugins) : plugins, [kind, plugins]);
const selectedPlugin = useMemo(() => selectablePlugins.find((plugin) => plugin.id === form.pluginId), [form.pluginId, selectablePlugins]);
const pluginFields = selectedPlugin?.createFields ?? [];
const workflowSteps = kind === "create"
? [{ label: "基本信息", icon: Compass }, { label: "部署方式", icon: ServerCog }, { label: "相关配置", icon: FolderCog }, { label: "确认", icon: Rocket }]
@@ -38,10 +39,10 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, dep
useEffect(() => {
if (!open) return;
setStep(0);
setForm(initialForm);
setForm(normalizeWorkflowForm(initialForm, selectablePlugins, kind === "create"));
setRevealBusy(false);
setRevealError("");
}, [initialForm, kind, open]);
}, [initialForm, kind, open, selectablePlugins]);
useEffect(() => {
if (!open || kind !== "edit" || !onReveal) return;
@@ -52,7 +53,7 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, dep
const { name, value } = event.target;
setForm((current) => {
if (name === "pluginId") {
const plugin = plugins.find((item) => item.id === value);
const plugin = selectablePlugins.find((item) => item.id === value);
return { ...current, pluginId: value, createInputs: pluginCreateInputDefaults(plugin) };
}
return { ...current, [name]: value };
@@ -107,7 +108,7 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, dep
<ol className="deployment-workflow-steps" style={{ gridTemplateColumns: `repeat(${workflowSteps.length}, minmax(0, 1fr))` }} aria-label="部署步骤">{workflowSteps.map((item, index) => { const Icon = item.icon; return <li key={item.label} className={cx(index === step && "deployment-workflow-step-active", index < step && "deployment-workflow-step-complete")}><span>{index < step ? <CheckCircle2 size={15} /> : <Icon size={15} />}</span><strong>{index + 1}. {item.label}</strong></li>; })}</ol>
{step === pluginStep && <div className="deployment-workflow-body">
<div className="workflow-hint-grid"><div className="workflow-hint-card"><strong></strong><span></span></div><div className="workflow-hint-card"><strong></strong><span></span></div><div className="workflow-hint-card"><strong> Run </strong><span>Run </span></div></div>
<div className="form-grid"><label><select name="pluginId" value={form.pluginId} onChange={updateForm} required>{plugins.map((plugin) => <option key={plugin.id} value={plugin.id}>{pluginLabel(plugin, plugin.id)}</option>)}</select></label><label><input name="name" value={form.name} onChange={updateForm} placeholder="Example Survival #3" required /></label></div>
<div className="form-grid"><label><select name="pluginId" value={form.pluginId} onChange={updateForm} required>{selectablePlugins.length === 0 && <option value=""></option>}{selectablePlugins.map((plugin) => <option key={plugin.id} value={plugin.id}>{pluginLabel(plugin, plugin.id)}</option>)}</select></label><label><input name="name" value={form.name} onChange={updateForm} placeholder="Example Survival #3" required /></label></div>
</div>}
{step === modeStep && <div className="deployment-workflow-body"><p className="section-copy"></p><div className="form-guidance"><strong></strong><span>Run </span></div><div className="deployment-mode-grid">
<ModeOption active={form.deploymentMode === "guided-install"} title="新建并安装" copy="按插件的推荐方案安装并写入游戏配置。适合绝大多数新服务器。" onClick={() => setForm((current) => ({ ...current, deploymentMode: "guided-install" }))} />
@@ -127,6 +128,7 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, dep
)}</label>
))}
</div>
{form.deploymentMode === "guided-install" && pluginFields.length === 0 && <div className="form-guidance"><strong></strong><span></span></div>}
{form.deploymentMode === "guided-install" && <GuidedInstallPlan pluginName={pluginLabel(selectedPlugin, form.pluginId)} />}
{form.deploymentMode === "existing-server" && <ExistingServerAdoptionPlan pluginName={pluginLabel(selectedPlugin, form.pluginId)} />}
{form.deploymentMode === "custom-command" && <details className="provider-advanced-settings" open><summary></summary><p className="field-help">Run </p><div className="form-grid"><label><input name="startCommand" value={form.startCommand} onChange={updateForm} placeholder={deployment?.startCommandConfigured ? "留空保持已配置启动命令" : "必填,例如 ./start-server"} autoComplete="off" required={!deployment?.startCommandConfigured} /></label><label><select name="shell" value={form.shell} onChange={updateForm}><option value=""> argv</option><option value="posix-sh">POSIX sh</option><option value="powershell">PowerShell</option><option value="cmd">Windows cmd</option></select></label><label><input name="workingDirectory" value={form.workingDirectory} onChange={updateForm} placeholder={deployment?.workingDirectoryConfigured ? "留空保持已配置执行目录" : "默认使用服务器目录"} autoComplete="off" /></label><label><input name="installCommand" value={form.installCommand} onChange={updateForm} autoComplete="off" placeholder="留空保持原值或不使用" /></label><label><input name="stopCommand" value={form.stopCommand} onChange={updateForm} autoComplete="off" /></label><label><input name="statusCommand" value={form.statusCommand} onChange={updateForm} autoComplete="off" /></label></div></details>}
@@ -137,6 +139,11 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, initialForm, dep
</ManagementDialog>;
}
function normalizeWorkflowForm(form: ServerCreateFormState, plugins: GamePluginResponse[], preferFirstPlugin = false): ServerCreateFormState {
const plugin = preferFirstPlugin ? plugins[0] : plugins.find((item) => item.id === form.pluginId) ?? plugins[0];
return { ...form, pluginId: plugin?.id ?? "", createInputs: pluginCreateInputValues(plugin, plugin?.id === form.pluginId ? form.createInputs : undefined) };
}
function ModeOption({ active, title, copy, onClick }: { active: boolean; title: string; copy: string; onClick: () => void }) { return <button type="button" className={cx("deployment-mode-option", active && "deployment-mode-option-active")} onClick={onClick}><strong>{title}</strong><span>{copy}</span></button>; }
function GuidedInstallPlan({ pluginName }: { pluginName: string }) {