import { useCallback, useEffect, useMemo, useState } from "react"; import { Eye, GitCompareArrows, Play, RefreshCw, X } from "lucide-react"; import { Button, Card, ConfirmDialog, DataGrid, Dialog, DismissibleAlert, FormField, LoadingFrame, MetricCard, StatusBadge, TableActionGroup, adminErrorMessage, hasAnyScope, type ApiSettings, type AuthInfo, type DataGridColumn } from "@govoplan/core-webui"; import { applyOrganizationModelUpgrade, cancelOrganizationModelUpgrade, getOrganizationModelUpgrades, getOrganizationTemplateCatalog, previewOrganizationModelUpgrade, type OrganizationModelInstantiation, type OrganizationModelUpgrade, type OrganizationTemplateCatalogItem, type OrganizationTemplateVersion, type OrganizationUpgradeDecisionAction, type OrganizationUpgradeDiffEntry } from "../../api/organizations"; type Decision = { action: OrganizationUpgradeDecisionAction; target_key?: string }; export default function OrganizationTemplateUpgradePanel({ settings, auth }: { settings: ApiSettings; auth: AuthInfo }) { const canRead = hasAnyScope(auth, ["organizations:model:read", "admin:settings:read"]); const canWrite = hasAnyScope(auth, ["organizations:model:write"]); const [templates, setTemplates] = useState([]); const [current, setCurrent] = useState(null); const [upgrades, setUpgrades] = useState([]); const [targetVersionId, setTargetVersionId] = useState(""); const [selected, setSelected] = useState(null); const [decisions, setDecisions] = useState>({}); const [changeRequestId, setChangeRequestId] = useState(""); const [applyConfirmation, setApplyConfirmation] = useState(false); const [cancelTarget, setCancelTarget] = useState(null); const [loading, setLoading] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const load = useCallback(async () => { if (!canRead) return; setLoading(true); setError(""); try { const [catalog, state] = await Promise.all([ getOrganizationTemplateCatalog(settings), getOrganizationModelUpgrades(settings) ]); setTemplates(catalog.templates); setCurrent(state.current_instantiation ?? null); setUpgrades(state.upgrades); const available = availableVersions(catalog.templates, state.current_instantiation ?? null); setTargetVersionId((value) => available.some((item) => item.id === value) ? value : available[0]?.id ?? ""); setSelected((value) => value ? state.upgrades.find((item) => item.id === value.id) ?? null : null); } catch (caught) { setError(adminErrorMessage(caught)); } finally { setLoading(false); } }, [canRead, settings.accessToken, settings.apiBaseUrl]); useEffect(() => { void load(); }, [load]); const versions = useMemo(() => versionMap(templates), [templates]); const targets = useMemo(() => availableVersions(templates, current), [templates, current]); const currentVersion = current ? versions.get(current.template_version_id) : undefined; const pending = upgrades.filter((item) => item.status === "previewed"); const upgradeColumns = useMemo[]>(() => [ { id: "source", header: "Source version", width: 150, sortable: true, filterable: true, render: (row) => versions.get(row.source_template_version_id)?.version ?? row.source_template_version_id, value: (row) => versions.get(row.source_template_version_id)?.version ?? row.source_template_version_id }, { id: "target", header: "Target version", width: 150, sortable: true, filterable: true, render: (row) => versions.get(row.target_template_version_id)?.version ?? row.target_template_version_id, value: (row) => versions.get(row.target_template_version_id)?.version ?? row.target_template_version_id }, { id: "changes", header: "Changes", width: 105, sortable: true, filterable: true, filterType: "integer", render: (row) => row.preview.entries.length, value: (row) => row.preview.entries.length }, { id: "decisions", header: "Decisions", width: 110, sortable: true, filterable: true, filterType: "integer", render: (row) => row.preview.requires_decisions, value: (row) => row.preview.requires_decisions }, { id: "invalid", header: "Invalid refs", width: 110, sortable: true, filterable: true, filterType: "integer", render: (row) => row.preview.blocking_invalid_references, value: (row) => row.preview.blocking_invalid_references }, { id: "status", header: "Status", width: 120, sortable: true, filterable: true, render: (row) => , value: (row) => row.status }, { id: "updated", header: "Updated", width: 175, sortable: true, filterable: true, filterType: "date", render: (row) => formatDateTime(row.updated_at), value: (row) => row.updated_at }, { id: "actions", header: "Actions", width: 120, sticky: "end", align: "right", render: (row) =>