From 145c34755994f39754a7d558c7b0369fb992f285 Mon Sep 17 00:00:00 2001 From: Scott Cazan Date: Thu, 5 Mar 2026 22:28:33 +0100 Subject: [PATCH 1/6] Remove user from a process --- .../decisions/ProfileUsersAccessTable.tsx | 74 +++++++++++++++---- apps/app/src/lib/i18n/dictionaries/bn.json | 2 + apps/app/src/lib/i18n/dictionaries/en.json | 2 + apps/app/src/lib/i18n/dictionaries/es.json | 2 + apps/app/src/lib/i18n/dictionaries/fr.json | 2 + apps/app/src/lib/i18n/dictionaries/pt.json | 2 + 6 files changed, 69 insertions(+), 15 deletions(-) diff --git a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx index 2640f5c76..2a5043e4c 100644 --- a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx +++ b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx @@ -4,6 +4,8 @@ import { trpc } from '@op/api/client'; import type { ProfileInvite, ProfileUser } from '@op/api/encoders'; import { Button } from '@op/ui/Button'; import { EmptyState } from '@op/ui/EmptyState'; +import { Menu, MenuItem, MenuSeparator, MenuTrigger } from '@op/ui/Menu'; +import { Popover } from '@op/ui/Popover'; import { Select, SelectItem } from '@op/ui/Select'; import { Skeleton } from '@op/ui/Skeleton'; import { toast } from '@op/ui/Toast'; @@ -16,7 +18,7 @@ import { TableRow, } from '@op/ui/ui/table'; import type { SortDescriptor } from 'react-aria-components'; -import { LuUsers } from 'react-icons/lu'; +import { LuCheck, LuChevronDown, LuUsers } from 'react-icons/lu'; import { Link, useTranslations } from '@/lib/i18n'; @@ -126,6 +128,18 @@ const ProfileUserRoleSelect = ({ }, }); + const removeUser = trpc.profile.removeUser.useMutation({ + onSuccess: () => { + toast.success({ message: t('User removed from process') }); + void utils.profile.listUsers.invalidate({ profileId }); + }, + onError: (error) => { + toast.error({ + message: error.message || t('Failed to remove user'), + }); + }, + }); + const handleRoleChange = (roleId: string) => { if (roleId && roleId !== currentRoleId) { updateRoles.mutate({ @@ -135,21 +149,51 @@ const ProfileUserRoleSelect = ({ } }; + const isPending = updateRoles.isPending || removeUser.isPending; + const currentRoleName = + roles.find((r) => r.id === currentRoleId)?.name ?? t('Role'); + return ( - + + + + { + const keyStr = key as string; + if (keyStr === 'remove') { + removeUser.mutate({ profileUserId }); + } else { + handleRoleChange(keyStr); + } + }} + > + {roles.map((role) => { + const isSelected = currentRoleId === role.id; + return ( + + {isSelected && } + + {role.name} + + + ); + })} + + + {t('Remove from process')} + + + + ); }; diff --git a/apps/app/src/lib/i18n/dictionaries/bn.json b/apps/app/src/lib/i18n/dictionaries/bn.json index a2e9e19fc..929fff424 100644 --- a/apps/app/src/lib/i18n/dictionaries/bn.json +++ b/apps/app/src/lib/i18n/dictionaries/bn.json @@ -228,6 +228,8 @@ "Change to Member": "সদস্যে পরিবর্তন করুন", "Change to Admin": "প্রশাসক করুন", "Remove from organization": "সংস্থা থেকে সরান", + "Remove from process": "প্রক্রিয়া থেকে সরান", + "User removed from process": "ব্যবহারকারী প্রক্রিয়া থেকে সরানো হয়েছে", "Member": "সদস্য", "User changed to Admin successfully": "ব্যবহারকারীকে সফলভাবে প্রশাসকে পরিবর্তন করা হয়েছে", "User changed to Member successfully": "ব্যবহারকারীকে সফলভাবে সদস্যে পরিবর্তন করা হয়েছে", diff --git a/apps/app/src/lib/i18n/dictionaries/en.json b/apps/app/src/lib/i18n/dictionaries/en.json index 888431b00..8a71907cc 100644 --- a/apps/app/src/lib/i18n/dictionaries/en.json +++ b/apps/app/src/lib/i18n/dictionaries/en.json @@ -228,6 +228,8 @@ "Change to Member": "Change to Member", "Change to Admin": "Change to Admin", "Remove from organization": "Remove from organization", + "Remove from process": "Remove from process", + "User removed from process": "User removed from process", "Member": "Member", "User changed to Admin successfully": "User changed to Admin successfully", "User changed to Member successfully": "User changed to Member successfully", diff --git a/apps/app/src/lib/i18n/dictionaries/es.json b/apps/app/src/lib/i18n/dictionaries/es.json index b40d0ebd7..ae3b19788 100644 --- a/apps/app/src/lib/i18n/dictionaries/es.json +++ b/apps/app/src/lib/i18n/dictionaries/es.json @@ -227,6 +227,8 @@ "Change to Member": "Cambiar a Miembro", "Change to Admin": "Cambiar a Administrador", "Remove from organization": "Eliminar de la organización", + "Remove from process": "Eliminar del proceso", + "User removed from process": "Usuario eliminado del proceso", "Member": "Miembro", "User changed to Admin successfully": "Usuario cambiado a Administrador exitosamente", "User changed to Member successfully": "Usuario cambiado a Miembro exitosamente", diff --git a/apps/app/src/lib/i18n/dictionaries/fr.json b/apps/app/src/lib/i18n/dictionaries/fr.json index a3d5f3b7f..97f2f6b1e 100644 --- a/apps/app/src/lib/i18n/dictionaries/fr.json +++ b/apps/app/src/lib/i18n/dictionaries/fr.json @@ -228,6 +228,8 @@ "Change to Member": "Changer en Membre", "Change to Admin": "Changer en Administrateur", "Remove from organization": "Supprimer de l'organisation", + "Remove from process": "Retirer du processus", + "User removed from process": "Utilisateur retiré du processus", "Member": "Membre", "User changed to Admin successfully": "Utilisateur changé en Administrateur avec succès", "User changed to Member successfully": "Utilisateur changé en Membre avec succès", diff --git a/apps/app/src/lib/i18n/dictionaries/pt.json b/apps/app/src/lib/i18n/dictionaries/pt.json index 04059e535..87eb3bfbd 100644 --- a/apps/app/src/lib/i18n/dictionaries/pt.json +++ b/apps/app/src/lib/i18n/dictionaries/pt.json @@ -228,6 +228,8 @@ "Change to Member": "Mudar para Membro", "Change to Admin": "Mudar para Administrador", "Remove from organization": "Remover da organização", + "Remove from process": "Remover do processo", + "User removed from process": "Usuário removido do processo", "Member": "Membro", "User changed to Admin successfully": "Usuário alterado para Administrador com sucesso", "User changed to Member successfully": "Usuário alterado para Membro com sucesso", From 3572c508bd6be3c7ba85872117e99c83f6eba01b Mon Sep 17 00:00:00 2001 From: Scott Cazan Date: Thu, 5 Mar 2026 22:45:03 +0100 Subject: [PATCH 2/6] Better accessibility --- .../decisions/ProfileUsersAccessTable.tsx | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx index 2a5043e4c..8a8e591e2 100644 --- a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx +++ b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx @@ -18,7 +18,7 @@ import { TableRow, } from '@op/ui/ui/table'; import type { SortDescriptor } from 'react-aria-components'; -import { LuCheck, LuChevronDown, LuUsers } from 'react-icons/lu'; +import { LuChevronDown, LuUsers } from 'react-icons/lu'; import { Link, useTranslations } from '@/lib/i18n'; @@ -167,26 +167,29 @@ const ProfileUserRoleSelect = ({ { + const selected = [...keys][0] as string | undefined; + if (selected) { + handleRoleChange(selected); + } + }} onAction={(key) => { - const keyStr = key as string; - if (keyStr === 'remove') { + if (key === 'remove') { removeUser.mutate({ profileUserId }); - } else { - handleRoleChange(keyStr); } }} > - {roles.map((role) => { - const isSelected = currentRoleId === role.id; - return ( - - {isSelected && } - - {role.name} - - - ); - })} + {roles.map((role) => ( + + {role.name} + + ))} {t('Remove from process')} From 4a40b52caa759129ab82a7cbbb615a349466394c Mon Sep 17 00:00:00 2001 From: Scott Cazan Date: Thu, 5 Mar 2026 22:57:32 +0100 Subject: [PATCH 3/6] Add remove modal --- .../decisions/ProfileUsersAccessTable.tsx | 136 ++++++++++++------ 1 file changed, 95 insertions(+), 41 deletions(-) diff --git a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx index 8a8e591e2..9f8ee4bff 100644 --- a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx +++ b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx @@ -3,8 +3,10 @@ import { trpc } from '@op/api/client'; import type { ProfileInvite, ProfileUser } from '@op/api/encoders'; import { Button } from '@op/ui/Button'; +import { DialogTrigger } from '@op/ui/Dialog'; import { EmptyState } from '@op/ui/EmptyState'; import { Menu, MenuItem, MenuSeparator, MenuTrigger } from '@op/ui/Menu'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from '@op/ui/Modal'; import { Popover } from '@op/ui/Popover'; import { Select, SelectItem } from '@op/ui/Select'; import { Skeleton } from '@op/ui/Skeleton'; @@ -17,6 +19,7 @@ import { TableHeader, TableRow, } from '@op/ui/ui/table'; +import { useState } from 'react'; import type { SortDescriptor } from 'react-aria-components'; import { LuChevronDown, LuUsers } from 'react-icons/lu'; @@ -36,6 +39,7 @@ export const ProfileUsersAccessTable = ({ roles, isMobile, invites, + processName, }: { profileUsers: ProfileUser[]; profileId: string; @@ -47,6 +51,7 @@ export const ProfileUsersAccessTable = ({ roles: { id: string; name: string }[]; isMobile: boolean; invites: ProfileInvite[]; + processName?: string; }) => { const t = useTranslations(); @@ -77,6 +82,7 @@ export const ProfileUsersAccessTable = ({ isLoading={isLoading} roles={roles} invites={invites} + processName={processName} /> ); } @@ -90,6 +96,7 @@ export const ProfileUsersAccessTable = ({ isLoading={isLoading} roles={roles} invites={invites} + processName={processName} /> ); }; @@ -105,16 +112,21 @@ const ProfileUserRoleSelect = ({ currentRoleId, profileId, roles, + userName, + processName, className = 'sm:w-32', }: { profileUserId: string; currentRoleId?: string; profileId: string; roles: { id: string; name: string }[]; + userName: string; + processName?: string; className?: string; }) => { const t = useTranslations(); const utils = trpc.useUtils(); + const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false); const updateRoles = trpc.profile.updateUserRoles.useMutation({ onSuccess: () => { @@ -132,6 +144,7 @@ const ProfileUserRoleSelect = ({ onSuccess: () => { toast.success({ message: t('User removed from process') }); void utils.profile.listUsers.invalidate({ profileId }); + setIsRemoveModalOpen(false); }, onError: (error) => { toast.error({ @@ -154,49 +167,90 @@ const ProfileUserRoleSelect = ({ roles.find((r) => r.id === currentRoleId)?.name ?? t('Role'); return ( - - - - { - const selected = [...keys][0] as string | undefined; - if (selected) { - handleRoleChange(selected); - } - }} - onAction={(key) => { - if (key === 'remove') { - removeUser.mutate({ profileUserId }); - } - }} + <> + + + + { + const selected = [...keys][0] as string | undefined; + if (selected) { + handleRoleChange(selected); + } + }} + onAction={(key) => { + if (key === 'remove') { + setIsRemoveModalOpen(true); + } + }} + > + {roles.map((role) => ( + + {role.name} + + ))} + + + {t('Remove from process')} - ))} - - - {t('Remove from process')} - - - - + + + + + + + {t('Remove {name}', { name: userName })} + + +

+ {processName + ? t( + 'Are you sure you want to remove {name} from "{processName}"?', + { name: userName, processName }, + ) + : t('Are you sure you want to remove {name} from this process?', { + name: userName, + })} +

+
+ + + + +
+
+ ); }; From 98fc196bbebc6ce01146769e6f87eb55dd8d2fbf Mon Sep 17 00:00:00 2001 From: Scott Cazan Date: Thu, 5 Mar 2026 23:16:19 +0100 Subject: [PATCH 4/6] Custom Menu for the dropdown --- .../participants/ParticipantsSection.tsx | 6 +- .../decisions/ProfileUsersAccess.tsx | 9 +- .../decisions/ProfileUsersAccessTable.tsx | 203 ++++++++++++++---- 3 files changed, 170 insertions(+), 48 deletions(-) diff --git a/apps/app/src/components/decisions/ProcessBuilder/stepContent/participants/ParticipantsSection.tsx b/apps/app/src/components/decisions/ProcessBuilder/stepContent/participants/ParticipantsSection.tsx index 1a0029f02..988a00738 100644 --- a/apps/app/src/components/decisions/ProcessBuilder/stepContent/participants/ParticipantsSection.tsx +++ b/apps/app/src/components/decisions/ProcessBuilder/stepContent/participants/ParticipantsSection.tsx @@ -4,11 +4,15 @@ import type { SectionProps } from '../../contentRegistry'; export default function ParticipantsSection({ decisionProfileId, + decisionName, }: SectionProps) { return (
- +
); diff --git a/apps/app/src/components/decisions/ProfileUsersAccess.tsx b/apps/app/src/components/decisions/ProfileUsersAccess.tsx index d79c6ea92..de179bcdf 100644 --- a/apps/app/src/components/decisions/ProfileUsersAccess.tsx +++ b/apps/app/src/components/decisions/ProfileUsersAccess.tsx @@ -23,7 +23,13 @@ type SortColumn = 'name' | 'email' | 'role'; const ITEMS_PER_PAGE = 25; -export const ProfileUsersAccess = ({ profileId }: { profileId: string }) => { +export const ProfileUsersAccess = ({ + profileId, + processName, +}: { + profileId: string; + processName?: string; +}) => { const t = useTranslations(); const isMobile = useMediaQuery(`(max-width: ${screens.md})`); const [searchQuery, setSearchQuery] = useState(''); @@ -119,6 +125,7 @@ export const ProfileUsersAccess = ({ profileId }: { profileId: string }) => { roles={roles} isMobile={isMobile} invites={invites ?? []} + processName={processName} /> { - const selected = [...keys][0] as string | undefined; - if (selected) { - handleRoleChange(selected); - } - }} onAction={(key) => { - if (key === 'remove') { + const keyStr = key as string; + if (keyStr === 'remove') { setIsRemoveModalOpen(true); + } else { + handleRoleChange(keyStr); } }} > - {roles.map((role) => ( - - {role.name} - - ))} - + {roles.map((role) => { + const isSelected = currentRoleId === role.id; + return ( + + + {isSelected && ( + + )} + + + {role.name} + + + ); + })} + {t('Remove from process')} @@ -216,9 +215,7 @@ const ProfileUserRoleSelect = ({ onOpenChange={setIsRemoveModalOpen} > - - {t('Remove {name}', { name: userName })} - + {t('Remove {name}', { name: userName })}

{processName @@ -226,9 +223,12 @@ const ProfileUserRoleSelect = ({ 'Are you sure you want to remove {name} from "{processName}"?', { name: userName, processName }, ) - : t('Are you sure you want to remove {name} from this process?', { - name: userName, - })} + : t( + 'Are you sure you want to remove {name} from this process?', + { + name: userName, + }, + )}

@@ -259,16 +259,21 @@ const InviteRoleSelect = ({ currentRoleId, profileId, roles, + inviteeName, + processName, className = 'sm:w-32', }: { inviteId: string; currentRoleId: string; profileId: string; roles: { id: string; name: string }[]; + inviteeName: string; + processName?: string; className?: string; }) => { const t = useTranslations(); const utils = trpc.useUtils(); + const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false); const updateInvite = trpc.profile.updateProfileInvite.useMutation({ onSuccess: () => { @@ -282,6 +287,19 @@ const InviteRoleSelect = ({ }, }); + const deleteInvite = trpc.profile.deleteProfileInvite.useMutation({ + onSuccess: () => { + toast.success({ message: t('Invite removed from process') }); + void utils.profile.listProfileInvites.invalidate({ profileId }); + setIsRemoveModalOpen(false); + }, + onError: (error) => { + toast.error({ + message: error.message || t('Failed to remove invite'), + }); + }, + }); + const handleRoleChange = (roleId: string) => { if (roleId && roleId !== currentRoleId) { updateInvite.mutate({ @@ -291,21 +309,96 @@ const InviteRoleSelect = ({ } }; + const isPending = updateInvite.isPending || deleteInvite.isPending; + const currentRoleName = + roles.find((r) => r.id === currentRoleId)?.name ?? t('Role'); + return ( - + <> + + + + { + const keyStr = key as string; + if (keyStr === 'remove') { + setIsRemoveModalOpen(true); + } else { + handleRoleChange(keyStr); + } + }} + > + {roles.map((role) => { + const isSelected = currentRoleId === role.id; + return ( + + + {isSelected && ( + + )} + + + {role.name} + + + ); + })} + + + {t('Remove from process')} + + + + + + + {t('Remove {name}', { name: inviteeName })} + +

+ {processName + ? t( + 'Are you sure you want to remove {name} from "{processName}"?', + { name: inviteeName, processName }, + ) + : t( + 'Are you sure you want to remove {name} from this process?', + { + name: inviteeName, + }, + )} +

+
+ + + + +
+
+ ); }; @@ -313,10 +406,12 @@ const MobileProfileUserCard = ({ profileUser, profileId, roles, + processName, }: { profileUser: ProfileUser; profileId: string; roles: { id: string; name: string }[]; + processName?: string; }) => { const displayName = profileUser.profile?.name || @@ -357,6 +452,8 @@ const MobileProfileUserCard = ({ currentRoleId={currentRole?.id} profileId={profileId} roles={roles} + userName={displayName} + processName={processName} className="w-full" /> @@ -367,10 +464,12 @@ const MobileInviteCard = ({ invite, profileId, roles, + processName, }: { invite: ProfileInvite; profileId: string; roles: { id: string; name: string }[]; + processName?: string; }) => { const t = useTranslations(); const displayName = invite.inviteeProfile?.name ?? invite.email; @@ -394,6 +493,8 @@ const MobileInviteCard = ({ currentRoleId={invite.accessRoleId} profileId={profileId} roles={roles} + inviteeName={displayName} + processName={processName} className="w-full" /> @@ -406,12 +507,14 @@ const MobileProfileUsersContent = ({ isLoading, roles, invites, + processName, }: { profileUsers: ProfileUser[]; profileId: string; isLoading: boolean; roles: { id: string; name: string }[]; invites: ProfileInvite[]; + processName?: string; }) => { return (
@@ -424,6 +527,7 @@ const MobileProfileUsersContent = ({ invite={invite} profileId={profileId} roles={roles} + processName={processName} /> ))} {profileUsers.map((profileUser) => ( @@ -432,6 +536,7 @@ const MobileProfileUsersContent = ({ profileUser={profileUser} profileId={profileId} roles={roles} + processName={processName} /> ))} @@ -449,6 +554,7 @@ const ProfileUsersAccessTableContent = ({ isLoading, roles, invites, + processName, }: { profileUsers: ProfileUser[]; profileId: string; @@ -457,6 +563,7 @@ const ProfileUsersAccessTableContent = ({ isLoading: boolean; roles: { id: string; name: string }[]; invites: ProfileInvite[]; + processName?: string; }) => { const t = useTranslations(); @@ -514,6 +621,8 @@ const ProfileUsersAccessTableContent = ({ currentRoleId={invite.accessRoleId} profileId={profileId} roles={roles} + inviteeName={displayName} + processName={processName} /> @@ -563,6 +672,8 @@ const ProfileUsersAccessTableContent = ({ currentRoleId={currentRole?.id} profileId={profileId} roles={roles} + userName={displayName} + processName={processName} /> From fdaf603249c11d58ae73c9c6643735dead265bf9 Mon Sep 17 00:00:00 2001 From: Scott Cazan Date: Thu, 5 Mar 2026 23:16:27 +0100 Subject: [PATCH 5/6] Add translation keys --- apps/app/src/lib/i18n/dictionaries/bn.json | 4 ++++ apps/app/src/lib/i18n/dictionaries/en.json | 4 ++++ apps/app/src/lib/i18n/dictionaries/es.json | 4 ++++ apps/app/src/lib/i18n/dictionaries/fr.json | 4 ++++ apps/app/src/lib/i18n/dictionaries/pt.json | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/apps/app/src/lib/i18n/dictionaries/bn.json b/apps/app/src/lib/i18n/dictionaries/bn.json index 929fff424..97c06d395 100644 --- a/apps/app/src/lib/i18n/dictionaries/bn.json +++ b/apps/app/src/lib/i18n/dictionaries/bn.json @@ -230,6 +230,8 @@ "Remove from organization": "সংস্থা থেকে সরান", "Remove from process": "প্রক্রিয়া থেকে সরান", "User removed from process": "ব্যবহারকারী প্রক্রিয়া থেকে সরানো হয়েছে", + "Invite removed from process": "আমন্ত্রণ প্রক্রিয়া থেকে সরানো হয়েছে", + "Failed to remove invite": "আমন্ত্রণ সরাতে ব্যর্থ", "Member": "সদস্য", "User changed to Admin successfully": "ব্যবহারকারীকে সফলভাবে প্রশাসকে পরিবর্তন করা হয়েছে", "User changed to Member successfully": "ব্যবহারকারীকে সফলভাবে সদস্যে পরিবর্তন করা হয়েছে", @@ -586,6 +588,8 @@ "Selected members": "নির্বাচিত সদস্যরা", "No email addresses to invite": "আমন্ত্রণ করার জন্য কোনো ইমেইল ঠিকানা নেই", "Remove {name}": "{name} সরান", + "Are you sure you want to remove {name} from \"{processName}\"?": "আপনি কি নিশ্চিত যে আপনি {name} কে \"{processName}\" থেকে সরাতে চান?", + "Are you sure you want to remove {name} from this process?": "আপনি কি নিশ্চিত যে আপনি {name} কে এই প্রক্রিয়া থেকে সরাতে চান?", "Select a role": "একটি ভূমিকা নির্বাচন করুন", "Add role": "ভূমিকা যোগ করুন", "Admin": "অ্যাডমিন", diff --git a/apps/app/src/lib/i18n/dictionaries/en.json b/apps/app/src/lib/i18n/dictionaries/en.json index 8a71907cc..2cb64f097 100644 --- a/apps/app/src/lib/i18n/dictionaries/en.json +++ b/apps/app/src/lib/i18n/dictionaries/en.json @@ -230,6 +230,8 @@ "Remove from organization": "Remove from organization", "Remove from process": "Remove from process", "User removed from process": "User removed from process", + "Invite removed from process": "Invite removed from process", + "Failed to remove invite": "Failed to remove invite", "Member": "Member", "User changed to Admin successfully": "User changed to Admin successfully", "User changed to Member successfully": "User changed to Member successfully", @@ -579,6 +581,8 @@ "Selected members": "Selected members", "No email addresses to invite": "No email addresses to invite", "Remove {name}": "Remove {name}", + "Are you sure you want to remove {name} from \"{processName}\"?": "Are you sure you want to remove {name} from \"{processName}\"?", + "Are you sure you want to remove {name} from this process?": "Are you sure you want to remove {name} from this process?", "Select a role": "Select a role", "Add role": "Add role", "Admin": "Admin", diff --git a/apps/app/src/lib/i18n/dictionaries/es.json b/apps/app/src/lib/i18n/dictionaries/es.json index ae3b19788..f4c562612 100644 --- a/apps/app/src/lib/i18n/dictionaries/es.json +++ b/apps/app/src/lib/i18n/dictionaries/es.json @@ -229,6 +229,8 @@ "Remove from organization": "Eliminar de la organización", "Remove from process": "Eliminar del proceso", "User removed from process": "Usuario eliminado del proceso", + "Invite removed from process": "Invitación eliminada del proceso", + "Failed to remove invite": "Error al eliminar la invitación", "Member": "Miembro", "User changed to Admin successfully": "Usuario cambiado a Administrador exitosamente", "User changed to Member successfully": "Usuario cambiado a Miembro exitosamente", @@ -578,6 +580,8 @@ "Selected members": "Miembros seleccionados", "No email addresses to invite": "No hay direcciones de correo para invitar", "Remove {name}": "Eliminar {name}", + "Are you sure you want to remove {name} from \"{processName}\"?": "¿Estás seguro de que quieres eliminar a {name} de \"{processName}\"?", + "Are you sure you want to remove {name} from this process?": "¿Estás seguro de que quieres eliminar a {name} de este proceso?", "Select a role": "Seleccionar un rol", "Add role": "Agregar rol", "Admin": "Admin", diff --git a/apps/app/src/lib/i18n/dictionaries/fr.json b/apps/app/src/lib/i18n/dictionaries/fr.json index 97f2f6b1e..bac51d4bd 100644 --- a/apps/app/src/lib/i18n/dictionaries/fr.json +++ b/apps/app/src/lib/i18n/dictionaries/fr.json @@ -230,6 +230,8 @@ "Remove from organization": "Supprimer de l'organisation", "Remove from process": "Retirer du processus", "User removed from process": "Utilisateur retiré du processus", + "Invite removed from process": "Invitation retirée du processus", + "Failed to remove invite": "Échec de la suppression de l'invitation", "Member": "Membre", "User changed to Admin successfully": "Utilisateur changé en Administrateur avec succès", "User changed to Member successfully": "Utilisateur changé en Membre avec succès", @@ -579,6 +581,8 @@ "Selected members": "Membres sélectionnés", "No email addresses to invite": "Aucune adresse courriel à inviter", "Remove {name}": "Supprimer {name}", + "Are you sure you want to remove {name} from \"{processName}\"?": "Êtes-vous sûr de vouloir retirer {name} de \"{processName}\" ?", + "Are you sure you want to remove {name} from this process?": "Êtes-vous sûr de vouloir retirer {name} de ce processus ?", "Select a role": "Sélectionner un rôle", "Add role": "Ajouter un rôle", "Admin": "Admin", diff --git a/apps/app/src/lib/i18n/dictionaries/pt.json b/apps/app/src/lib/i18n/dictionaries/pt.json index 87eb3bfbd..17eecaac3 100644 --- a/apps/app/src/lib/i18n/dictionaries/pt.json +++ b/apps/app/src/lib/i18n/dictionaries/pt.json @@ -230,6 +230,8 @@ "Remove from organization": "Remover da organização", "Remove from process": "Remover do processo", "User removed from process": "Usuário removido do processo", + "Invite removed from process": "Convite removido do processo", + "Failed to remove invite": "Falha ao remover convite", "Member": "Membro", "User changed to Admin successfully": "Usuário alterado para Administrador com sucesso", "User changed to Member successfully": "Usuário alterado para Membro com sucesso", @@ -579,6 +581,8 @@ "Selected members": "Membros selecionados", "No email addresses to invite": "Nenhum endereço de e-mail para convidar", "Remove {name}": "Remover {name}", + "Are you sure you want to remove {name} from \"{processName}\"?": "Tem certeza de que deseja remover {name} de \"{processName}\"?", + "Are you sure you want to remove {name} from this process?": "Tem certeza de que deseja remover {name} deste processo?", "Select a role": "Selecionar um cargo", "Add role": "Adicionar função", "Admin": "Admin", From f2217f4e7d03aa4464d5e2ee38b5bc0f575bf529 Mon Sep 17 00:00:00 2001 From: Scott Cazan Date: Thu, 5 Mar 2026 23:33:29 +0100 Subject: [PATCH 6/6] Simplify it --- .../decisions/ProfileUsersAccessTable.tsx | 151 ++++++------------ 1 file changed, 52 insertions(+), 99 deletions(-) diff --git a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx index 3fffae3ae..326397b87 100644 --- a/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx +++ b/apps/app/src/components/decisions/ProfileUsersAccessTable.tsx @@ -5,9 +5,8 @@ import type { ProfileInvite, ProfileUser } from '@op/api/encoders'; import { Button } from '@op/ui/Button'; import { DialogTrigger } from '@op/ui/Dialog'; import { EmptyState } from '@op/ui/EmptyState'; -import { Menu, MenuItem, MenuTrigger } from '@op/ui/Menu'; import { Modal, ModalBody, ModalFooter, ModalHeader } from '@op/ui/Modal'; -import { Popover } from '@op/ui/Popover'; +import { Select, SelectItem } from '@op/ui/Select'; import { Skeleton } from '@op/ui/Skeleton'; import { toast } from '@op/ui/Toast'; import { @@ -20,7 +19,7 @@ import { } from '@op/ui/ui/table'; import { useState } from 'react'; import type { SortDescriptor } from 'react-aria-components'; -import { LuCheck, LuChevronDown, LuUsers } from 'react-icons/lu'; +import { LuUsers } from 'react-icons/lu'; import { Link, useTranslations } from '@/lib/i18n'; @@ -162,54 +161,33 @@ const ProfileUserRoleSelect = ({ }; const isPending = updateRoles.isPending || removeUser.isPending; - const currentRoleName = - roles.find((r) => r.id === currentRoleId)?.name ?? t('Role'); return ( <> - - - - { - const keyStr = key as string; - if (keyStr === 'remove') { - setIsRemoveModalOpen(true); - } else { - handleRoleChange(keyStr); - } - }} - > - {roles.map((role) => { - const isSelected = currentRoleId === role.id; - return ( - - - {isSelected && ( - - )} - - - {role.name} - - - ); - })} - - - {t('Remove from process')} - - - - + @@ -310,54 +286,33 @@ const InviteRoleSelect = ({ }; const isPending = updateInvite.isPending || deleteInvite.isPending; - const currentRoleName = - roles.find((r) => r.id === currentRoleId)?.name ?? t('Role'); return ( <> - - - - { - const keyStr = key as string; - if (keyStr === 'remove') { - setIsRemoveModalOpen(true); - } else { - handleRoleChange(keyStr); - } - }} - > - {roles.map((role) => { - const isSelected = currentRoleId === role.id; - return ( - - - {isSelected && ( - - )} - - - {role.name} - - - ); - })} - - - {t('Remove from process')} - - - - +