235 lines
9.4 KiB
TypeScript
235 lines
9.4 KiB
TypeScript
import { useState } from "react";
|
|
import type { ApiSettings } from "../../../types";
|
|
import { Button } from "@govoplan/core-webui";
|
|
import { ConfirmDialog } from "@govoplan/core-webui";
|
|
import { DismissibleAlert } from "@govoplan/core-webui";
|
|
import {
|
|
forkCampaignVersion,
|
|
lockCampaignVersionPermanently,
|
|
unlockCampaignVersionUserLock,
|
|
unlockCampaignVersionValidation,
|
|
type CampaignVersionDetail,
|
|
type CampaignVersionListItem } from
|
|
"../../../api/campaigns";
|
|
import {
|
|
canUnlockValidationVersion,
|
|
formatDateTime,
|
|
isFinalLockedVersion,
|
|
isHistoricalCampaignVersion,
|
|
isPermanentUserLockedVersion,
|
|
isTemporaryUserLockedVersion } from
|
|
"../utils/campaignView";
|
|
|
|
type LockedVersionNoticeProps = {
|
|
settings: ApiSettings;
|
|
campaignId: string;
|
|
version: CampaignVersionDetail | CampaignVersionListItem | null;
|
|
currentVersionId?: string | null;
|
|
reload: () => Promise<void>;
|
|
message?: string;
|
|
};
|
|
|
|
type ConfirmAction = "unlock-validation" | "unlock-user" | "permanent" | null;
|
|
|
|
export default function LockedVersionNotice({ settings, campaignId, version, currentVersionId, reload, message }: LockedVersionNoticeProps) {
|
|
const [busy, setBusy] = useState(false);
|
|
const [localError, setLocalError] = useState("");
|
|
const [localMessage, setLocalMessage] = useState("");
|
|
const [confirmAction, setConfirmAction] = useState<ConfirmAction>(null);
|
|
|
|
const historicalVersion = isHistoricalCampaignVersion(version, currentVersionId);
|
|
const validationLock = !historicalVersion && canUnlockValidationVersion(version);
|
|
const temporaryUserLock = !historicalVersion && isTemporaryUserLockedVersion(version);
|
|
const permanentUserLock = isPermanentUserLockedVersion(version);
|
|
const finalLock = isFinalLockedVersion(version);
|
|
const canCreateEditableCopy = !historicalVersion && (permanentUserLock || finalLock);
|
|
const presentation = lockPresentation(version, {
|
|
historicalVersion,
|
|
validationLock,
|
|
temporaryUserLock,
|
|
permanentUserLock,
|
|
finalLock
|
|
});
|
|
|
|
async function runAction(action: Exclude<ConfirmAction, null>) {
|
|
if (!version || busy) return;
|
|
setBusy(true);
|
|
setLocalError("");
|
|
setLocalMessage("");
|
|
try {
|
|
if (action === "unlock-validation") {
|
|
await unlockCampaignVersionValidation(settings, campaignId, version.id);
|
|
setLocalMessage("i18n:govoplan-campaign.validation_lock_removed_validation_build_and_gen.ea26975f");
|
|
} else if (action === "unlock-user") {
|
|
await unlockCampaignVersionUserLock(settings, campaignId, version.id);
|
|
setLocalMessage("i18n:govoplan-campaign.temporary_user_lock_removed_the_version_is_edita.0ca4c587");
|
|
} else {
|
|
await lockCampaignVersionPermanently(settings, campaignId, version.id);
|
|
setLocalMessage("i18n:govoplan-campaign.version_permanently_locked_future_changes_requir.fd718a84");
|
|
}
|
|
await reload();
|
|
} catch (err) {
|
|
setLocalError(err instanceof Error ? err.message : String(err));
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
async function createEditableCopy() {
|
|
if (!version || busy) return;
|
|
setBusy(true);
|
|
setLocalError("");
|
|
setLocalMessage("");
|
|
try {
|
|
const result = await forkCampaignVersion(settings, campaignId, version.id, {
|
|
current_flow: "manual",
|
|
current_step: version.current_step ?? null
|
|
});
|
|
setLocalMessage(`Created editable version #${result.version.version_number}.`);
|
|
await reload();
|
|
} catch (err) {
|
|
setLocalError(err instanceof Error ? err.message : String(err));
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<DismissibleAlert tone="info" dismissible={false} className={`locked-version-notice lock-kind-${presentation.kind}`}>
|
|
<div className="locked-version-copy">
|
|
<strong>{presentation.title}</strong>{" "}
|
|
<span>{presentation.description}</span>
|
|
{message && <span className="locked-version-context"> {message}</span>}
|
|
{presentation.info && <span className="locked-version-reason"> {presentation.info}</span>}
|
|
{localMessage && <span className="locked-version-feedback"> {localMessage}</span>}
|
|
{localError && <span className="locked-version-error"> {localError}</span>}
|
|
</div>
|
|
<div className="button-row compact-actions locked-version-actions">
|
|
{validationLock &&
|
|
<Button onClick={() => setConfirmAction("unlock-validation")} disabled={!version || busy}>
|
|
{busy ? "i18n:govoplan-campaign.working.13b7bfca" : "i18n:govoplan-campaign.unlock_validation.f01952b6"}
|
|
</Button>
|
|
}
|
|
{temporaryUserLock &&
|
|
<>
|
|
<Button onClick={() => setConfirmAction("unlock-user")} disabled={!version || busy}>
|
|
{busy ? "i18n:govoplan-campaign.working.13b7bfca" : "i18n:govoplan-campaign.unlock.1526a17e"}
|
|
</Button>
|
|
<Button variant="danger" onClick={() => setConfirmAction("permanent")} disabled={!version || busy}>
|
|
i18n:govoplan-campaign.lock_permanently.cc0ce9e7
|
|
</Button>
|
|
</>
|
|
}
|
|
{canCreateEditableCopy &&
|
|
<Button variant="primary" onClick={() => void createEditableCopy()} disabled={!version || busy}>
|
|
{busy ? "i18n:govoplan-campaign.creating_copy.4247f587" : "i18n:govoplan-campaign.create_editable_copy.92d6d91b"}
|
|
</Button>
|
|
}
|
|
</div>
|
|
|
|
<ConfirmDialog
|
|
open={confirmAction !== null}
|
|
title={confirmDialogTitle(confirmAction)}
|
|
message={confirmDialogMessage(confirmAction)}
|
|
confirmLabel={confirmDialogLabel(confirmAction)}
|
|
tone={confirmAction === "unlock-user" ? "default" : "danger"}
|
|
busy={busy}
|
|
onCancel={() => setConfirmAction(null)}
|
|
onConfirm={() => {
|
|
const action = confirmAction;
|
|
setConfirmAction(null);
|
|
if (action) void runAction(action);
|
|
}} />
|
|
|
|
</DismissibleAlert>);
|
|
|
|
}
|
|
|
|
type LockFlags = {
|
|
historicalVersion: boolean;
|
|
validationLock: boolean;
|
|
temporaryUserLock: boolean;
|
|
permanentUserLock: boolean;
|
|
finalLock: boolean;
|
|
};
|
|
|
|
function lockPresentation(
|
|
version: CampaignVersionDetail | CampaignVersionListItem | null,
|
|
flags: LockFlags)
|
|
: {kind: string;title: string;description: string;info: string;} {
|
|
if (flags.historicalVersion) {
|
|
return {
|
|
kind: "historical",
|
|
title: "i18n:govoplan-campaign.historical_version.adac5dba",
|
|
description: "i18n:govoplan-campaign.this_version_is_review_only_only_the_campaign_s_.9e68f6a0",
|
|
info: `Version #${version?.version_number ?? "—"}.`
|
|
};
|
|
}
|
|
if (flags.temporaryUserLock) {
|
|
return {
|
|
kind: "temporary-user",
|
|
title: "i18n:govoplan-campaign.temporarily_locked_version.696f85e3",
|
|
description: "i18n:govoplan-campaign.campaign_data_is_read_only_but_an_authorized_use.f2e513b1",
|
|
info: version?.user_locked_at ? `Locked ${formatDateTime(version.user_locked_at)}.` : ""
|
|
};
|
|
}
|
|
if (flags.permanentUserLock) {
|
|
return {
|
|
kind: "permanent-user",
|
|
title: "i18n:govoplan-campaign.permanently_locked_version.248c2eeb",
|
|
description: "i18n:govoplan-campaign.this_user_requested_snapshot_cannot_be_unlocked_.9e91210c",
|
|
info: version?.user_locked_at || version?.published_at ?
|
|
`Locked ${formatDateTime(version.user_locked_at ?? version.published_at)}.` :
|
|
""
|
|
};
|
|
}
|
|
if (flags.validationLock) {
|
|
return {
|
|
kind: "validation",
|
|
title: "i18n:govoplan-campaign.temporarily_locked_after_validation.c558ce56",
|
|
description: "i18n:govoplan-campaign.this_protects_the_validated_build_input_unlockin.ed890772",
|
|
info: version?.locked_at ? `Validated ${formatDateTime(version.locked_at)}.` : ""
|
|
};
|
|
}
|
|
if (flags.finalLock) {
|
|
return {
|
|
kind: "delivery",
|
|
title: "i18n:govoplan-campaign.permanently_locked_delivery_snapshot.c3670daa",
|
|
description: "i18n:govoplan-campaign.delivery_has_started_or_the_version_is_in_a_fina.571e86ed",
|
|
info: version?.locked_at ? `Locked ${formatDateTime(version.locked_at)}.` : ""
|
|
};
|
|
}
|
|
return {
|
|
kind: "other",
|
|
title: "i18n:govoplan-campaign.locked_version.e755b179",
|
|
description: "i18n:govoplan-campaign.this_version_is_read_only_create_an_editable_cop.9e0afd29",
|
|
info: version?.locked_at ? `Locked ${formatDateTime(version.locked_at)}.` : ""
|
|
};
|
|
}
|
|
|
|
function confirmDialogTitle(action: ConfirmAction): string {
|
|
if (action === "unlock-validation") return "i18n:govoplan-campaign.unlock_validation.e3066247";
|
|
if (action === "unlock-user") return "i18n:govoplan-campaign.unlock_temporary_lock.8a3ad468";
|
|
if (action === "permanent") return "i18n:govoplan-campaign.lock_permanently.24d5b74d";
|
|
return "i18n:govoplan-campaign.confirm_action.50649c5c";
|
|
}
|
|
|
|
function confirmDialogMessage(action: ConfirmAction): string {
|
|
if (action === "unlock-validation") {
|
|
return "i18n:govoplan-campaign.this_makes_the_version_editable_and_clears_valid.89a6cd1b";
|
|
}
|
|
if (action === "unlock-user") {
|
|
return "i18n:govoplan-campaign.this_removes_the_reversible_user_lock_campaign_d.fcf8cb1d";
|
|
}
|
|
if (action === "permanent") {
|
|
return "i18n:govoplan-campaign.this_lock_cannot_be_removed_by_any_role_the_vers.3054b076";
|
|
}
|
|
return "i18n:govoplan-campaign.continue.4e6302ed";
|
|
}
|
|
|
|
function confirmDialogLabel(action: ConfirmAction): string {
|
|
if (action === "unlock-validation") return "i18n:govoplan-campaign.unlock_validation.f01952b6";
|
|
if (action === "unlock-user") return "i18n:govoplan-campaign.unlock.1526a17e";
|
|
if (action === "permanent") return "i18n:govoplan-campaign.lock_permanently.cc0ce9e7";
|
|
return "i18n:govoplan-campaign.confirm.04a21221";
|
|
} |