Release v0.1.7
This commit is contained in:
@@ -26,6 +26,9 @@ import {
|
||||
type ModuleInstallerRunListResponse,
|
||||
type ModuleInstallPlanItem,
|
||||
type ModuleInstallPlanResponse,
|
||||
type ModuleInstallTargetItem,
|
||||
type ModuleMigrationPlanStep,
|
||||
type ModuleMigrationTaskPlanItem,
|
||||
type ModuleLicenseDiagnostics,
|
||||
type ModulePackageCatalogResponse,
|
||||
type ModulePackageCatalogItem } from
|
||||
@@ -414,15 +417,25 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
{item.python_package && <span>{item.python_package}</span>}
|
||||
{item.webui_package && <span>{item.webui_package}</span>}
|
||||
{item.tags.length > 0 && <span>{item.tags.join(", ")}</span>}
|
||||
{item.dependencies.length > 0 && <span>i18n:govoplan-admin.dependencies.9f4f78d1 {item.dependencies.join(", ")}</span>}
|
||||
{item.migration_safety !== "automatic" && <span>i18n:govoplan-admin.migration_safety.729bdb3c {migrationSafetyLabel(item.migration_safety)}</span>}
|
||||
{item.migration_after.length > 0 && <span>i18n:govoplan-admin.after.695c89be {item.migration_after.join(", ")}</span>}
|
||||
{item.migration_before.length > 0 && <span>i18n:govoplan-admin.before.a11cf31f {item.migration_before.join(", ")}</span>}
|
||||
{item.bridge_release && <span>i18n:govoplan-admin.bridge_release.176cf241</span>}
|
||||
{(item.current_version_min || item.current_version_max_exclusive) && <span>i18n:govoplan-admin.current_window.73e25c9f {currentWindowLabel(item)}</span>}
|
||||
{item.recovery_tested && <span>i18n:govoplan-admin.recovery_tested.e831e2f1</span>}
|
||||
{item.license_features.length > 0 && <span>i18n:govoplan-admin.license.de13bf1a {item.license_features.join(", ")}</span>}
|
||||
{item.license_missing_features.length > 0 && <span>i18n:govoplan-admin.missing.feb2bbaa {item.license_missing_features.join(", ")}</span>}
|
||||
{item.provides_interfaces.length > 0 && <span>i18n:govoplan-admin.provides.221b70d9 {providedInterfacesLabel(item)}</span>}
|
||||
{item.requires_interfaces.length > 0 && <span>i18n:govoplan-admin.requires.a4fc9357 {requiredInterfacesLabel(item)}</span>}
|
||||
</div>
|
||||
{item.description && <p className="module-package-catalog-description">{item.description}</p>}
|
||||
{item.migration_notes && <p className="module-package-catalog-description">{item.migration_notes}</p>}
|
||||
{item.bridge_notes && <p className="module-package-catalog-description">{item.bridge_notes}</p>}
|
||||
{item.recovery_notes && <p className="module-package-catalog-description">{item.recovery_notes}</p>}
|
||||
{item.license_reason && <p className={`module-package-catalog-description${item.license_allowed ? "" : " alert warning"}`}>{item.license_reason}</p>}
|
||||
</div>
|
||||
<Button onClick={() => void addCatalogItem(item)} disabled={!canWrite || planBusy || !packageCatalog.valid || !item.license_allowed || item.action !== "install"}>i18n:govoplan-admin.plan_install.e82bffe6</Button>
|
||||
<Button onClick={() => void addCatalogItem(item)} disabled={!canWrite || planBusy || !packageCatalog.valid || !item.license_allowed || !["install", "update"].includes(item.action)}>{catalogPlanButtonLabel(item, catalog)}</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
@@ -450,6 +463,43 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
<span>{installPlan.preflight.restart_required ? "i18n:govoplan-admin.restart_reload_required_after_package_changes.3a99cd63" : "i18n:govoplan-admin.no_package_restart_pending.efe6b8ce"}</span>
|
||||
{installPlan.preflight.frontend_rebuild_required && <span>i18n:govoplan-admin.webui_rebuild_required.010ce49f</span>}
|
||||
</div>
|
||||
{installPlan.preflight.target_plan.length > 0 && <div className="module-install-checklist">
|
||||
<strong>i18n:govoplan-admin.target_plan.524ea0c8</strong>
|
||||
{installPlan.preflight.target_plan.map((target) =>
|
||||
<div key={`${target.module_id}-${target.action}`} className={`module-install-checklist-item status-${targetStatus(target)}`}>
|
||||
<strong>{target.module_id} - {target.action}</strong>
|
||||
<span>{targetVersionLabel(target)}</span>
|
||||
<p>{migrationSafetyLabel(target.migration_safety)}{target.data_safety_acknowledged ? " - i18n:govoplan-admin.acknowledged.d08d7c6d" : ""}</p>
|
||||
{target.bridge_release && <p>i18n:govoplan-admin.bridge_release.176cf241</p>}
|
||||
{target.recovery_tested && <p>i18n:govoplan-admin.recovery_tested.e831e2f1</p>}
|
||||
{target.python_ref && <p>{target.python_ref}</p>}
|
||||
{target.migration_notes && <p>{target.migration_notes}</p>}
|
||||
{target.bridge_notes && <p>{target.bridge_notes}</p>}
|
||||
{target.recovery_notes && <p>{target.recovery_notes}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
{(installPlan.preflight.migration_plan.steps.length > 0 || installPlan.preflight.migration_plan.tasks.length > 0) && <div className="module-install-checklist">
|
||||
<strong>i18n:govoplan-admin.migration_plan.f42b9d90</strong>
|
||||
<span>i18n:govoplan-admin.enabled_modules.3c38e9ff {installPlan.preflight.migration_plan.enabled_modules.join(", ")}</span>
|
||||
{installPlan.preflight.migration_plan.steps.map((step, index) =>
|
||||
<div key={`${step.module_id}-${step.phase}-${index}`} className={`module-install-checklist-item status-${migrationStepStatus(step)}`}>
|
||||
<strong>{index + 1}. {step.module_id} - {migrationPhaseLabel(step.phase)}</strong>
|
||||
<span>{migrationSourceLabel(step.source)}{step.metadata_pending ? " - i18n:govoplan-admin.metadata_pending.25190d87" : ""}</span>
|
||||
<p>{migrationSafetyLabel(step.migration_safety)}</p>
|
||||
{step.reason && <p>{step.reason}</p>}
|
||||
</div>
|
||||
)}
|
||||
{installPlan.preflight.migration_plan.tasks.length > 0 && <strong>i18n:govoplan-admin.migration_tasks.a4410d3a</strong>}
|
||||
{installPlan.preflight.migration_plan.tasks.map((task) =>
|
||||
<div key={`${task.module_id}-${task.task_id}-${task.phase}`} className={`module-install-checklist-item status-${migrationTaskStatus(task)}`}>
|
||||
<strong>{task.module_id} - {migrationTaskPhaseLabel(task.phase)}</strong>
|
||||
<span>{task.summary}</span>
|
||||
<p>{migrationSourceLabel(task.source)}{task.metadata_pending ? " - i18n:govoplan-admin.executor_pending.7633225b" : ""}{task.has_executor ? " - i18n:govoplan-admin.executor_available.507de429" : ""}</p>
|
||||
<p>{migrationSafetyLabel(task.safety)} - {task.idempotent ? "i18n:govoplan-admin.idempotent.47a0f2d6" : "i18n:govoplan-admin.not_idempotent.d3014c71"} - i18n:govoplan-admin.task_version.f0f26b92 {task.task_version}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
{installPlan.preflight.issues.length > 0 && <div className="module-install-preflight-issues">
|
||||
{installPlan.preflight.issues.map((issue, index) =>
|
||||
<div key={`${issue.code}-${issue.module_id ?? "global"}-${index}`} className={`module-install-preflight-issue severity-${issue.severity}`}>
|
||||
@@ -479,10 +529,11 @@ export default function ModuleManagementPanel({ settings, canWrite, canAccessMai
|
||||
{item.catalog?.sequence !== null && item.catalog?.sequence !== undefined && <StatusBadge status="inactive" label={i18nMessage("i18n:govoplan-admin.seq_value.0ae5fa47", { value0: item.catalog.sequence })} />}
|
||||
{item.catalog?.key_id && <StatusBadge status="inactive" label={i18nMessage("i18n:govoplan-admin.key_value.847b914f", { value0: String(item.catalog.key_id) })} />}
|
||||
</div>
|
||||
<label><span>i18n:govoplan-admin.action.97c89a4d</span><select value={item.action} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { action: event.target.value as ModuleInstallPlanItem["action"] })}><option value="install">i18n:govoplan-admin.install.fd6c3ebf</option><option value="uninstall">i18n:govoplan-admin.uninstall.a735da1d</option></select></label>
|
||||
<label><span>i18n:govoplan-admin.action.97c89a4d</span><select value={item.action} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { action: event.target.value as ModuleInstallPlanItem["action"] })}><option value="install">i18n:govoplan-admin.install.fd6c3ebf</option><option value="update">i18n:govoplan-admin.update.503a059f</option><option value="uninstall">i18n:govoplan-admin.uninstall.a735da1d</option></select></label>
|
||||
<label><span>i18n:govoplan-admin.module.b8ff0289</span><input value={item.module_id} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { module_id: event.target.value })} placeholder="files" /></label>
|
||||
<label><span>i18n:govoplan-admin.python_package.34591c71</span><input value={item.python_package ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { python_package: event.target.value })} placeholder="govoplan-files" /></label>
|
||||
<ToggleSwitch label="i18n:govoplan-admin.destroy_data.34557c3c" checked={Boolean(item.destroy_data)} onChange={(checked) => updatePlanItem(index, { destroy_data: checked })} disabled={!canWrite || planBusy || item.action !== "uninstall"} />
|
||||
<ToggleSwitch label="i18n:govoplan-admin.data_safety_reviewed.b4774edc" checked={Boolean(item.data_safety_acknowledged)} onChange={(checked) => updatePlanItem(index, { data_safety_acknowledged: checked })} disabled={!canWrite || planBusy || item.action === "uninstall"} />
|
||||
<label className="wide"><span>i18n:govoplan-admin.python_ref.1af35d0d</span><input value={item.python_ref ?? ""} disabled={!canWrite || planBusy || item.action === "uninstall"} onChange={(event) => updatePlanItem(index, { python_ref: event.target.value })} placeholder="govoplan-files @ git+ssh://...@v0.1.4" /></label>
|
||||
<label><span>i18n:govoplan-admin.webui_package.19645536</span><input value={item.webui_package ?? ""} disabled={!canWrite || planBusy} onChange={(event) => updatePlanItem(index, { webui_package: event.target.value })} placeholder="@govoplan/files-webui" /></label>
|
||||
<label className="wide"><span>i18n:govoplan-admin.webui_ref.cbae3559</span><input value={item.webui_ref ?? ""} disabled={!canWrite || planBusy || item.action === "uninstall"} onChange={(event) => updatePlanItem(index, { webui_ref: event.target.value })} placeholder="git+ssh://...#v0.1.4" /></label>
|
||||
@@ -644,6 +695,65 @@ function requiredInterfacesLabel(item: ModulePackageCatalogItem): string {
|
||||
}).join(", ");
|
||||
}
|
||||
|
||||
function migrationSafetyLabel(value: ModulePackageCatalogItem["migration_safety"] | ModuleInstallTargetItem["migration_safety"]): string {
|
||||
if (value === "requires_review") return "i18n:govoplan-admin.requires_review.8ba0b8c6";
|
||||
if (value === "forward_only") return "i18n:govoplan-admin.forward_only.6c107a46";
|
||||
if (value === "destructive") return "i18n:govoplan-admin.destructive.0051026c";
|
||||
return "i18n:govoplan-admin.automatic.d9a7260f";
|
||||
}
|
||||
|
||||
function targetStatus(target: ModuleInstallTargetItem): "done" | "warning" | "blocked" {
|
||||
if (target.migration_safety === "destructive" && !target.data_safety_acknowledged) return "blocked";
|
||||
if (target.migration_safety === "forward_only" && !target.data_safety_acknowledged) return "blocked";
|
||||
if (target.migration_safety !== "automatic") return "warning";
|
||||
return "done";
|
||||
}
|
||||
|
||||
function targetVersionLabel(target: ModuleInstallTargetItem): string {
|
||||
const current = target.current_version ?? "none";
|
||||
const next = target.action === "uninstall" ? "removed" : target.target_version ?? "unknown";
|
||||
return `${current} -> ${next}`;
|
||||
}
|
||||
|
||||
function migrationPhaseLabel(value: ModuleMigrationPlanStep["phase"]): string {
|
||||
if (value === "retirement") return "i18n:govoplan-admin.retirement.6b680dd2";
|
||||
return "i18n:govoplan-admin.upgrade.12c5007d";
|
||||
}
|
||||
|
||||
function migrationTaskPhaseLabel(value: ModuleMigrationTaskPlanItem["phase"]): string {
|
||||
if (value === "pre_migration_check") return "i18n:govoplan-admin.pre_migration_check.f654164a";
|
||||
if (value === "pre_migration_prepare") return "i18n:govoplan-admin.pre_migration_prepare.d18fdd26";
|
||||
if (value === "post_migration_backfill") return "i18n:govoplan-admin.post_migration_backfill.21043d74";
|
||||
return "i18n:govoplan-admin.post_migration_verify.6af9f28b";
|
||||
}
|
||||
|
||||
function migrationSourceLabel(value: ModuleMigrationPlanStep["source"] | ModuleMigrationTaskPlanItem["source"]): string {
|
||||
if (value === "catalog") return "i18n:govoplan-admin.catalog_metadata.9b96c41a";
|
||||
if (value === "pending") return "i18n:govoplan-admin.pending_metadata.a24cfeb0";
|
||||
return "i18n:govoplan-admin.manifest_metadata.b9ae362a";
|
||||
}
|
||||
|
||||
function migrationStepStatus(step: ModuleMigrationPlanStep): "done" | "pending" | "warning" | "blocked" {
|
||||
if (step.metadata_pending) return "warning";
|
||||
if (step.migration_safety !== "automatic") return "warning";
|
||||
return "pending";
|
||||
}
|
||||
|
||||
function migrationTaskStatus(task: ModuleMigrationTaskPlanItem): "done" | "pending" | "warning" | "blocked" {
|
||||
if (!task.idempotent) return "blocked";
|
||||
if (!task.metadata_pending && !task.has_executor) return "blocked";
|
||||
if (task.safety === "forward_only" || task.safety === "destructive") return "warning";
|
||||
if (task.metadata_pending || task.safety === "requires_review") return "warning";
|
||||
return "pending";
|
||||
}
|
||||
|
||||
function currentWindowLabel(item: Pick<ModulePackageCatalogItem, "current_version_min" | "current_version_max_exclusive">): string {
|
||||
return [
|
||||
item.current_version_min ? `>=${item.current_version_min}` : "",
|
||||
item.current_version_max_exclusive ? `<${item.current_version_max_exclusive}` : "",
|
||||
].filter(Boolean).join(" ") || "any";
|
||||
}
|
||||
|
||||
function ModuleStatus({ module, desiredEnabled }: {module: ModuleCatalogItem;desiredEnabled: boolean;}) {
|
||||
if (module.current_enabled && !desiredEnabled) return <StatusBadge status="warning" label="i18n:govoplan-admin.will_disable.14bb193a" />;
|
||||
if (!module.current_enabled && desiredEnabled) return <StatusBadge status="warning" label="i18n:govoplan-admin.will_enable.5d52d70b" />;
|
||||
@@ -651,6 +761,12 @@ function ModuleStatus({ module, desiredEnabled }: {module: ModuleCatalogItem;des
|
||||
return <StatusBadge status="inactive" label="i18n:govoplan-admin.inactive.09af574c" />;
|
||||
}
|
||||
|
||||
function catalogPlanButtonLabel(item: ModulePackageCatalogItem, catalog: ModuleCatalogResponse | null): string {
|
||||
if (item.action === "update") return "i18n:govoplan-admin.plan_update.86e6857a";
|
||||
const installed = catalog?.modules.some((module) => module.id === item.module_id && module.installed) ?? false;
|
||||
return installed ? "i18n:govoplan-admin.plan_update.86e6857a" : "i18n:govoplan-admin.plan_install.e82bffe6";
|
||||
}
|
||||
|
||||
function sameSet(left: ReadonlySet<string>, right: ReadonlySet<string>) {
|
||||
if (left.size !== right.size) return false;
|
||||
for (const item of left) {
|
||||
@@ -669,6 +785,7 @@ function emptyPlanItem(): ModuleInstallPlanItem {
|
||||
python_ref: "",
|
||||
webui_package: "",
|
||||
webui_ref: "",
|
||||
data_safety_acknowledged: false,
|
||||
destroy_data: false,
|
||||
status: "planned",
|
||||
notes: ""
|
||||
@@ -683,9 +800,10 @@ function normalizePlanItems(items: ModuleInstallPlanItem[]): ModuleInstallPlanIt
|
||||
source: item.source === "catalog" ? "catalog" : "manual",
|
||||
catalog: item.source === "catalog" ? item.catalog ?? null : null,
|
||||
python_package: cleanOptional(item.python_package),
|
||||
python_ref: item.action === "install" ? cleanOptional(item.python_ref) : null,
|
||||
python_ref: item.action !== "uninstall" ? cleanOptional(item.python_ref) : null,
|
||||
webui_package: cleanOptional(item.webui_package),
|
||||
webui_ref: item.action === "install" ? cleanOptional(item.webui_ref) : null,
|
||||
webui_ref: item.action !== "uninstall" ? cleanOptional(item.webui_ref) : null,
|
||||
data_safety_acknowledged: item.action !== "uninstall" && Boolean(item.data_safety_acknowledged),
|
||||
destroy_data: item.action === "uninstall" && Boolean(item.destroy_data),
|
||||
status: item.status,
|
||||
notes: cleanOptional(item.notes)
|
||||
@@ -701,10 +819,10 @@ function cleanOptional(value?: string | null): string | null {
|
||||
function planValidationError(items: ModuleInstallPlanItem[]): string {
|
||||
for (const item of normalizePlanItems(items)) {
|
||||
if (!item.module_id) return "i18n:govoplan-admin.every_plan_item_needs_a_module_id.23a7f206";
|
||||
if (item.action === "install" && !item.python_ref) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_reference.950b898e", { value0: item.module_id });
|
||||
if (item.action === "install" && item.python_ref && !item.python_package) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_name_for_rollback.6c89ed75", { value0: item.module_id });
|
||||
if (item.action !== "uninstall" && !item.python_ref) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_reference.950b898e", { value0: item.module_id });
|
||||
if (item.action !== "uninstall" && item.python_ref && !item.python_package) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_name_for_rollback.6c89ed75", { value0: item.module_id });
|
||||
if (item.action === "uninstall" && !item.python_package) return i18nMessage("i18n:govoplan-admin.value_needs_a_python_package_name_for_uninstall.004ba6fa", { value0: item.module_id });
|
||||
if (item.action === "install" && Boolean(item.webui_package) !== Boolean(item.webui_ref)) return i18nMessage("i18n:govoplan-admin.value_needs_both_webui_package_and_webui_referen.d2bab073", { value0: item.module_id });
|
||||
if (item.action !== "uninstall" && Boolean(item.webui_package) !== Boolean(item.webui_ref)) return i18nMessage("i18n:govoplan-admin.value_needs_both_webui_package_and_webui_referen.d2bab073", { value0: item.module_id });
|
||||
if (item.action === "uninstall" && item.webui_ref && !item.webui_package) return i18nMessage("i18n:govoplan-admin.value_has_a_webui_reference_but_no_webui_package.18f44149", { value0: item.module_id });
|
||||
}
|
||||
return "";
|
||||
|
||||
Reference in New Issue
Block a user