Rename user archive action to deactivate
"Archive" was misleading for users: the action sets a profile to DEACTIVATED while keeping the person in the organization. Rename it to "deactivate" across the API, CLI, MCP, n8n, and console UI. Consolidate the two overlapping operations into a single deactivateUser backed by the fuller, guarded logic (SCIM guard, last-active-owner guard, invitation expiry, signature cancellation, membership update, webhook) and authorized via iam:membership-profile:deactivate. Remove the archiveUser surface and the thin state-only deactivate path. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -1361,10 +1361,10 @@
|
||||
"peoplePage": { "title": "People", "actions": { "add": "Add Person" } },
|
||||
"personPage": {
|
||||
"breadcrumb": { "people": "People" },
|
||||
"messages": { "archived": "Person archived successfully", "removed": "Person removed successfully" },
|
||||
"errors": { "archive": "Failed to archive person", "remove": "Failed to remove person" },
|
||||
"confirmations": { "archive": "Are you sure you want to archive {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
||||
"actions": { "archive": "Archive", "remove": "Remove" }
|
||||
"messages": { "deactivated": "Person deactivated successfully", "removed": "Person removed successfully" },
|
||||
"errors": { "deactivate": "Failed to deactivate person", "remove": "Failed to remove person" },
|
||||
"confirmations": { "deactivate": "Are you sure you want to deactivate {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
||||
"actions": { "deactivate": "Deactivate", "remove": "Remove" }
|
||||
},
|
||||
"addPersonDialog": { "title": "Add Person" },
|
||||
"peopleList": {
|
||||
@@ -1374,10 +1374,10 @@
|
||||
"filters": { "allStatuses": "All statuses", "pending": "Pending", "active": "Active", "deactivated": "Deactivated", "allRoles": "All roles", "allTypes": "All types" }
|
||||
},
|
||||
"peopleListItem": {
|
||||
"messages": { "invitationSent": "Invitation sent successfully", "roleUpdated": "Role updated successfully", "archived": "Person archived successfully", "removed": "Person removed successfully" },
|
||||
"errors": { "sendInvitation": "Failed to send invitation", "updateRole": "Failed to update role", "archive": "Failed to archive person", "remove": "Failed to remove person" },
|
||||
"confirmations": { "sendActivationEmail": "Send the activation email to {{name}}?", "archive": "Are you sure you want to archive {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
||||
"actions": { "send": "Send", "sendActivationMail": "Send activation mail", "resendActivationMail": "Resend activation mail", "archivePerson": "Archive person", "removePerson": "Remove person" }
|
||||
"messages": { "invitationSent": "Invitation sent successfully", "roleUpdated": "Role updated successfully", "deactivated": "Person deactivated successfully", "removed": "Person removed successfully" },
|
||||
"errors": { "sendInvitation": "Failed to send invitation", "updateRole": "Failed to update role", "deactivate": "Failed to deactivate person", "remove": "Failed to remove person" },
|
||||
"confirmations": { "sendActivationEmail": "Send the activation email to {{name}}?", "deactivate": "Are you sure you want to deactivate {{name}}?", "remove": "Are you sure you want to remove {{name}}?" },
|
||||
"actions": { "send": "Send", "sendActivationMail": "Send activation mail", "resendActivationMail": "Resend activation mail", "deactivatePerson": "Deactivate person", "removePerson": "Remove person" }
|
||||
},
|
||||
"personForm": {
|
||||
"messages": { "created": "Person created successfully.", "updated": "Person updated successfully." },
|
||||
|
||||
@@ -2427,19 +2427,19 @@
|
||||
"people": "Personnes"
|
||||
},
|
||||
"messages": {
|
||||
"archived": "Personne archivée avec succès",
|
||||
"deactivated": "Personne désactivée avec succès",
|
||||
"removed": "Personne supprimée avec succès"
|
||||
},
|
||||
"errors": {
|
||||
"archive": "Échec de l’archivage de la personne",
|
||||
"deactivate": "Échec de la désactivation de la personne",
|
||||
"remove": "Échec de la suppression de la personne"
|
||||
},
|
||||
"confirmations": {
|
||||
"archive": "Voulez-vous vraiment archiver {{name}} ?",
|
||||
"deactivate": "Voulez-vous vraiment désactiver {{name}} ?",
|
||||
"remove": "Voulez-vous vraiment supprimer {{name}} ?"
|
||||
},
|
||||
"actions": {
|
||||
"archive": "Archiver",
|
||||
"deactivate": "Désactiver",
|
||||
"remove": "Supprimer"
|
||||
}
|
||||
},
|
||||
@@ -2469,25 +2469,25 @@
|
||||
"messages": {
|
||||
"invitationSent": "Invitation envoyée avec succès",
|
||||
"roleUpdated": "Rôle mis à jour avec succès",
|
||||
"archived": "Personne archivée avec succès",
|
||||
"deactivated": "Personne désactivée avec succès",
|
||||
"removed": "Personne supprimée avec succès"
|
||||
},
|
||||
"errors": {
|
||||
"sendInvitation": "Échec de l’envoi de l’invitation",
|
||||
"updateRole": "Échec de la mise à jour du rôle",
|
||||
"archive": "Échec de l’archivage de la personne",
|
||||
"deactivate": "Échec de la désactivation de la personne",
|
||||
"remove": "Échec de la suppression de la personne"
|
||||
},
|
||||
"confirmations": {
|
||||
"sendActivationEmail": "Envoyer l’e-mail d’activation à {{name}} ?",
|
||||
"archive": "Voulez-vous vraiment archiver {{name}} ?",
|
||||
"deactivate": "Voulez-vous vraiment désactiver {{name}} ?",
|
||||
"remove": "Voulez-vous vraiment supprimer {{name}} ?"
|
||||
},
|
||||
"actions": {
|
||||
"send": "Envoyer",
|
||||
"sendActivationMail": "Envoyer l’e-mail d’activation",
|
||||
"resendActivationMail": "Renvoyer l’e-mail d’activation",
|
||||
"archivePerson": "Archiver la personne",
|
||||
"deactivatePerson": "Désactiver la personne",
|
||||
"removePerson": "Supprimer la personne"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ export const personPageQuery = graphql`
|
||||
emailAddress
|
||||
source
|
||||
state
|
||||
canDelete: permission(action: "iam:membership-profile:delete")
|
||||
canDeactivate: permission(action: "iam:membership-profile:deactivate")
|
||||
canRemoveMember: permission(action: "iam:membership:delete")
|
||||
...PersonFormFragment
|
||||
}
|
||||
@@ -58,12 +58,12 @@ const removeUserMutation = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const archiveUserMutation = graphql`
|
||||
mutation PersonPage_archiveMutation(
|
||||
$input: ArchiveUserInput!
|
||||
const deactivateUserMutation = graphql`
|
||||
mutation PersonPage_deactivateMutation(
|
||||
$input: DeactivateUserInput!
|
||||
) {
|
||||
archiveUser(input: $input) {
|
||||
archivedProfileId
|
||||
deactivateUser(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -81,11 +81,11 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
||||
throw new Error("invalid type for node");
|
||||
}
|
||||
|
||||
const [archiveUser, isArchiving] = useMutationWithToasts(
|
||||
archiveUserMutation,
|
||||
const [deactivateUser, isDeactivating] = useMutationWithToasts(
|
||||
deactivateUserMutation,
|
||||
{
|
||||
successMessage: t("personPage.messages.archived"),
|
||||
errorMessage: t("personPage.errors.archive"),
|
||||
successMessage: t("personPage.messages.deactivated"),
|
||||
errorMessage: t("personPage.errors.deactivate"),
|
||||
},
|
||||
);
|
||||
const [removeUser, isRemoving] = useMutationWithToasts(
|
||||
@@ -95,12 +95,12 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
||||
errorMessage: t("personPage.errors.remove"),
|
||||
},
|
||||
);
|
||||
const isMutating = isArchiving || isRemoving;
|
||||
const isMutating = isDeactivating || isRemoving;
|
||||
|
||||
const handleArchive = () => {
|
||||
const handleDeactivate = () => {
|
||||
confirm(
|
||||
() => {
|
||||
return archiveUser({
|
||||
return deactivateUser({
|
||||
variables: {
|
||||
input: {
|
||||
profileId: person.id,
|
||||
@@ -113,7 +113,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
||||
});
|
||||
},
|
||||
{
|
||||
message: t("personPage.confirmations.archive", { name: person.fullName }),
|
||||
message: t("personPage.confirmations.deactivate", { name: person.fullName }),
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -139,7 +139,7 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
||||
);
|
||||
};
|
||||
|
||||
const canArchive = person.canDelete && person.source !== "SCIM" && person.state !== "DEACTIVATED";
|
||||
const canDeactivate = person.canDeactivate && person.source !== "SCIM" && person.state !== "DEACTIVATED";
|
||||
const canRemove = person.canRemoveMember && person.source !== "SCIM";
|
||||
|
||||
return (
|
||||
@@ -166,15 +166,15 @@ export function PersonPage(props: { queryRef: PreloadedQuery<PersonPageQuery> })
|
||||
<div className="text-lg text-txt-secondary">{person.emailAddress}</div>
|
||||
</div>
|
||||
</div>
|
||||
{(canArchive || canRemove) && (
|
||||
{(canDeactivate || canRemove) && (
|
||||
<ActionDropdown variant="secondary">
|
||||
{canArchive && (
|
||||
{canDeactivate && (
|
||||
<DropdownItem
|
||||
icon={IconArchive}
|
||||
onClick={handleArchive}
|
||||
onClick={handleDeactivate}
|
||||
disabled={isMutating}
|
||||
>
|
||||
{t("personPage.actions.archive")}
|
||||
{t("personPage.actions.deactivate")}
|
||||
</DropdownItem>
|
||||
)}
|
||||
{canRemove && (
|
||||
|
||||
@@ -71,7 +71,7 @@ const fragment = graphql`
|
||||
createdAt
|
||||
canUpdate: permission(action: "iam:membership-profile:update")
|
||||
canInvite: permission(action: "iam:invitation:create")
|
||||
canDelete: permission(action: "iam:membership-profile:delete")
|
||||
canDeactivate: permission(action: "iam:membership-profile:deactivate")
|
||||
canRemoveMember: permission(action: "iam:membership:delete")
|
||||
}
|
||||
`;
|
||||
@@ -116,10 +116,10 @@ const removeUserMutation = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const archiveUserMutation = graphql`
|
||||
mutation PeopleListItem_archiveMutation($input: ArchiveUserInput!) {
|
||||
archiveUser(input: $input) {
|
||||
archivedProfileId
|
||||
const deactivateUserMutation = graphql`
|
||||
mutation PeopleListItem_deactivateMutation($input: DeactivateUserInput!) {
|
||||
deactivateUser(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -149,7 +149,7 @@ export function PeopleListItem(props: {
|
||||
const isInactive = !isActive;
|
||||
|
||||
const canSendActivationMail = !isActive && profile.source !== "SCIM" && profile.canInvite;
|
||||
const canArchive = profile.canDelete && profile.source !== "SCIM" && profile.state !== "DEACTIVATED";
|
||||
const canDeactivate = profile.canDeactivate && profile.source !== "SCIM" && profile.state !== "DEACTIVATED";
|
||||
const canRemove = profile.canRemoveMember && profile.source !== "SCIM";
|
||||
|
||||
const [inviteUser]
|
||||
@@ -164,11 +164,11 @@ export function PeopleListItem(props: {
|
||||
errorMessage: t("peopleListItem.errors.updateRole"),
|
||||
},
|
||||
);
|
||||
const [archiveUser, isArchiving] = useMutationWithToasts(
|
||||
archiveUserMutation,
|
||||
const [deactivateUser, isDeactivating] = useMutationWithToasts(
|
||||
deactivateUserMutation,
|
||||
{
|
||||
successMessage: t("peopleListItem.messages.archived"),
|
||||
errorMessage: t("peopleListItem.errors.archive"),
|
||||
successMessage: t("peopleListItem.messages.deactivated"),
|
||||
errorMessage: t("peopleListItem.errors.deactivate"),
|
||||
},
|
||||
);
|
||||
const [removeUser, isRemoving] = useMutationWithToasts(
|
||||
@@ -178,7 +178,7 @@ export function PeopleListItem(props: {
|
||||
errorMessage: t("peopleListItem.errors.remove"),
|
||||
},
|
||||
);
|
||||
const isMutating = isArchiving || isRemoving;
|
||||
const isMutating = isDeactivating || isRemoving;
|
||||
|
||||
const handleInvite = () => {
|
||||
confirm(
|
||||
@@ -211,10 +211,10 @@ export function PeopleListItem(props: {
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleArchive = () => {
|
||||
const handleDeactivate = () => {
|
||||
confirm(
|
||||
() => {
|
||||
return archiveUser({
|
||||
return deactivateUser({
|
||||
variables: {
|
||||
input: {
|
||||
profileId: profile.id,
|
||||
@@ -227,7 +227,7 @@ export function PeopleListItem(props: {
|
||||
});
|
||||
},
|
||||
{
|
||||
message: t("peopleListItem.confirmations.archive", { name: profile.fullName }),
|
||||
message: t("peopleListItem.confirmations.deactivate", { name: profile.fullName }),
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -307,7 +307,7 @@ export function PeopleListItem(props: {
|
||||
{dateFormat(i18n.language, profile.createdAt)}
|
||||
</Td>
|
||||
<Td noLink width={160} className="text-end">
|
||||
{(canSendActivationMail || canArchive || canRemove) && (
|
||||
{(canSendActivationMail || canDeactivate || canRemove) && (
|
||||
<ActionDropdown>
|
||||
{canSendActivationMail && (
|
||||
<DropdownItem
|
||||
@@ -317,12 +317,12 @@ export function PeopleListItem(props: {
|
||||
{lastInvitation ? t("peopleListItem.actions.resendActivationMail") : t("peopleListItem.actions.sendActivationMail")}
|
||||
</DropdownItem>
|
||||
)}
|
||||
{canArchive && (
|
||||
{canDeactivate && (
|
||||
<DropdownItem
|
||||
onClick={handleArchive}
|
||||
onClick={handleDeactivate}
|
||||
icon={IconArchive}
|
||||
>
|
||||
{t("peopleListItem.actions.archivePerson")}
|
||||
{t("peopleListItem.actions.deactivatePerson")}
|
||||
</DropdownItem>
|
||||
)}
|
||||
{canRemove && (
|
||||
|
||||
Reference in New Issue
Block a user