Complete guided Core configuration patterns
This commit is contained in:
@@ -40,6 +40,8 @@ import { usePlatformLanguage } from "../i18n/LanguageContext";
|
||||
import type {
|
||||
CredentialReferenceSelectorsUiCapability
|
||||
} from "./ReferenceSelect";
|
||||
import ActionBlockerHint, { type ActionBlockerReason } from "./ActionBlockerHint";
|
||||
import DocumentationHelpLink from "./help/DocumentationHelpLink";
|
||||
|
||||
export type CredentialEnvelopeTargetOption = {
|
||||
id: string;
|
||||
@@ -95,6 +97,11 @@ const EMPTY_DRAFT: CredentialDraft = {
|
||||
retainedPublicData: {}
|
||||
};
|
||||
|
||||
const CREDENTIAL_DOCUMENTATION = {
|
||||
contextId: "access.credentials",
|
||||
documentationType: "admin" as const
|
||||
};
|
||||
|
||||
export default function CredentialEnvelopeManager({
|
||||
settings,
|
||||
scopeType,
|
||||
@@ -377,6 +384,27 @@ export default function CredentialEnvelopeManager({
|
||||
const saveDisabled = saving || !canWrite || !draft.name.trim() ||
|
||||
(editing === "new" && !draft.secret.trim()) ||
|
||||
(kindChanged && !draft.secret.trim());
|
||||
const managerBlocker = credentialManagerBlocker({ canWrite, scopeReady, scopeType });
|
||||
const writeDisabledReason = saving
|
||||
? "A credential change is already being saved."
|
||||
: !scopeReady
|
||||
? "Select a credential owner before changing credentials."
|
||||
: !canWrite
|
||||
? "Your account may inspect credentials at this scope but cannot change them."
|
||||
: undefined;
|
||||
const saveDisabledReason = saving
|
||||
? "The credential is already being saved."
|
||||
: !canWrite
|
||||
? "Your account may inspect this credential but cannot change it."
|
||||
: !scopeReady
|
||||
? "Select a credential owner before saving."
|
||||
: !draft.name.trim()
|
||||
? "Enter a credential name before saving."
|
||||
: editing === "new" && !draft.secret.trim()
|
||||
? "Enter a secret before creating the credential."
|
||||
: kindChanged && !draft.secret.trim()
|
||||
? "Enter a replacement secret when changing the credential type."
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div className="credential-envelope-manager">
|
||||
@@ -397,16 +425,24 @@ export default function CredentialEnvelopeManager({
|
||||
)}
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
||||
{notice && !error && <DismissibleAlert tone="success" resetKey={notice}>{notice}</DismissibleAlert>}
|
||||
{managerBlocker && (
|
||||
<ActionBlockerHint
|
||||
reason={managerBlocker}
|
||||
documentation={CREDENTIAL_DOCUMENTATION}
|
||||
/>
|
||||
)}
|
||||
<Card
|
||||
title={title}
|
||||
actions={
|
||||
<div className="button-row compact-actions">
|
||||
<DocumentationHelpLink reference={CREDENTIAL_DOCUMENTATION} label="i18n:govoplan-core.open_admin_documentation.6adbdae3" />
|
||||
<Button
|
||||
type="button"
|
||||
title="Reload credentials"
|
||||
aria-label="Reload credentials"
|
||||
onClick={() => void loadCredentials()}
|
||||
disabled={loading}
|
||||
disabled={loading || !scopeReady}
|
||||
disabledReason={loading ? "Credentials are already loading." : !scopeReady ? "Select a credential owner before reloading." : undefined}
|
||||
>
|
||||
<RefreshCw size={16} />
|
||||
</Button>
|
||||
@@ -414,7 +450,8 @@ export default function CredentialEnvelopeManager({
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={openCreate}
|
||||
disabled={!canWrite || !scopeReady || loading}
|
||||
disabled={Boolean(writeDisabledReason) || loading}
|
||||
disabledReason={loading ? "Wait until credentials have loaded." : writeDisabledReason}
|
||||
>
|
||||
<Plus size={16} /> Add credential
|
||||
</Button>
|
||||
@@ -435,7 +472,8 @@ export default function CredentialEnvelopeManager({
|
||||
label: `Edit ${credential.name}`,
|
||||
icon: <Pencil size={15} />,
|
||||
onClick: () => openEdit(credential),
|
||||
disabled: !canWrite || saving
|
||||
disabled: !canWrite || saving,
|
||||
disabledReason: writeDisabledReason
|
||||
},
|
||||
{
|
||||
id: "delete",
|
||||
@@ -443,6 +481,7 @@ export default function CredentialEnvelopeManager({
|
||||
icon: <Trash2 size={15} />,
|
||||
onClick: () => setDeleting(credential),
|
||||
disabled: !canWrite || saving,
|
||||
disabledReason: writeDisabledReason,
|
||||
variant: "danger"
|
||||
}
|
||||
]}
|
||||
@@ -461,8 +500,8 @@ export default function CredentialEnvelopeManager({
|
||||
footerClassName="button-row compact-actions"
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={closeEditor} disabled={saving}>Cancel</Button>
|
||||
<Button variant="primary" onClick={() => void saveDraft()} disabled={saveDisabled}>
|
||||
<Button onClick={closeEditor} disabled={saving} disabledReason={saving ? "Wait for the credential save to finish." : undefined}>Cancel</Button>
|
||||
<Button variant="primary" onClick={() => void saveDraft()} disabled={saveDisabled} disabledReason={saveDisabledReason}>
|
||||
<KeyRound size={16} /> {saving ? "Saving" : "Save credential"}
|
||||
</Button>
|
||||
</>
|
||||
@@ -555,6 +594,35 @@ export default function CredentialEnvelopeManager({
|
||||
);
|
||||
}
|
||||
|
||||
function credentialManagerBlocker({
|
||||
canWrite,
|
||||
scopeReady,
|
||||
scopeType
|
||||
}: {
|
||||
canWrite: boolean;
|
||||
scopeReady: boolean;
|
||||
scopeType: CredentialEnvelopeManagerProps["scopeType"];
|
||||
}): ActionBlockerReason | null {
|
||||
if (!scopeReady) {
|
||||
return {
|
||||
summary: "A credential owner is required.",
|
||||
details: `No ${scopeType} target is currently selected.`,
|
||||
requiredAction: "Select the user or group that will own the reusable credential.",
|
||||
actor: "An administrator with access to this settings surface",
|
||||
target: "The target selector above"
|
||||
};
|
||||
}
|
||||
if (!canWrite) {
|
||||
return {
|
||||
summary: "You can inspect reusable credentials but cannot change them.",
|
||||
requiredAction: "Ask for credential-management authority if a change is required.",
|
||||
actor: "A scope or access administrator",
|
||||
target: "Access and role administration"
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function credentialDraft(credential: CredentialEnvelopeSummary): CredentialDraft {
|
||||
return {
|
||||
...EMPTY_DRAFT,
|
||||
|
||||
Reference in New Issue
Block a user