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 t = useTranslations(); @@ -75,6 +80,7 @@ export const ProfileUsersAccessTable = ({ isLoading={isLoading} roles={roles} invites={invites} + processName={processName} /> ); } @@ -88,6 +94,7 @@ export const ProfileUsersAccessTable = ({ isLoading={isLoading} roles={roles} invites={invites} + processName={processName} /> ); }; @@ -103,16 +110,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: () => { @@ -126,6 +138,19 @@ const ProfileUserRoleSelect = ({ }, }); + const removeUser = trpc.profile.removeUser.useMutation({ + onSuccess: () => { + toast.success({ message: t('User removed from process') }); + void utils.profile.listUsers.invalidate({ profileId }); + setIsRemoveModalOpen(false); + }, + onError: (error) => { + toast.error({ + message: error.message || t('Failed to remove user'), + }); + }, + }); + const handleRoleChange = (roleId: string) => { if (roleId && roleId !== currentRoleId) { updateRoles.mutate({ @@ -135,21 +160,73 @@ const ProfileUserRoleSelect = ({ } }; + const isPending = updateRoles.isPending || removeUser.isPending; + return ( - { + const keyStr = key as string; + if (keyStr === 'remove') { + setIsRemoveModalOpen(true); + } else { + handleRoleChange(keyStr); + } + }} + isDisabled={isPending} + size="small" + className={className} + > + {roles.map((role) => ( + + {role.name} + + ))} + + {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 }, + )} +

+
+ + + + +
+
+ ); }; @@ -158,16 +235,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: () => { @@ -181,6 +263,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({ @@ -190,21 +285,73 @@ const InviteRoleSelect = ({ } }; + const isPending = updateInvite.isPending || deleteInvite.isPending; + return ( - { + const keyStr = key as string; + if (keyStr === 'remove') { + setIsRemoveModalOpen(true); + } else { + handleRoleChange(keyStr); + } + }} + isDisabled={isPending} + size="small" + className={className} + > + {roles.map((role) => ( + + {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 }, + )} +

+
+ + + + +
+
+ ); }; @@ -212,10 +359,12 @@ const MobileProfileUserCard = ({ profileUser, profileId, roles, + processName, }: { profileUser: ProfileUser; profileId: string; roles: { id: string; name: string }[]; + processName?: string; }) => { const displayName = profileUser.profile?.name || @@ -256,6 +405,8 @@ const MobileProfileUserCard = ({ currentRoleId={currentRole?.id} profileId={profileId} roles={roles} + userName={displayName} + processName={processName} className="w-full" /> @@ -266,10 +417,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; @@ -293,6 +446,8 @@ const MobileInviteCard = ({ currentRoleId={invite.accessRoleId} profileId={profileId} roles={roles} + inviteeName={displayName} + processName={processName} className="w-full" /> @@ -305,12 +460,14 @@ const MobileProfileUsersContent = ({ isLoading, roles, invites, + processName, }: { profileUsers: ProfileUser[]; profileId: string; isLoading: boolean; roles: { id: string; name: string }[]; invites: ProfileInvite[]; + processName?: string; }) => { return (
@@ -323,6 +480,7 @@ const MobileProfileUsersContent = ({ invite={invite} profileId={profileId} roles={roles} + processName={processName} /> ))} {profileUsers.map((profileUser) => ( @@ -331,6 +489,7 @@ const MobileProfileUsersContent = ({ profileUser={profileUser} profileId={profileId} roles={roles} + processName={processName} /> ))} @@ -348,6 +507,7 @@ const ProfileUsersAccessTableContent = ({ isLoading, roles, invites, + processName, }: { profileUsers: ProfileUser[]; profileId: string; @@ -356,6 +516,7 @@ const ProfileUsersAccessTableContent = ({ isLoading: boolean; roles: { id: string; name: string }[]; invites: ProfileInvite[]; + processName?: string; }) => { const t = useTranslations(); @@ -413,6 +574,8 @@ const ProfileUsersAccessTableContent = ({ currentRoleId={invite.accessRoleId} profileId={profileId} roles={roles} + inviteeName={displayName} + processName={processName} /> @@ -462,6 +625,8 @@ const ProfileUsersAccessTableContent = ({ currentRoleId={currentRole?.id} profileId={profileId} roles={roles} + userName={displayName} + processName={processName} /> diff --git a/apps/app/src/lib/i18n/dictionaries/bn.json b/apps/app/src/lib/i18n/dictionaries/bn.json index a2e9e19fc..97c06d395 100644 --- a/apps/app/src/lib/i18n/dictionaries/bn.json +++ b/apps/app/src/lib/i18n/dictionaries/bn.json @@ -228,6 +228,10 @@ "Change to Member": "সদস্যে পরিবর্তন করুন", "Change to Admin": "প্রশাসক করুন", "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": "ব্যবহারকারীকে সফলভাবে সদস্যে পরিবর্তন করা হয়েছে", @@ -584,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 888431b00..2cb64f097 100644 --- a/apps/app/src/lib/i18n/dictionaries/en.json +++ b/apps/app/src/lib/i18n/dictionaries/en.json @@ -228,6 +228,10 @@ "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", + "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", @@ -577,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 b40d0ebd7..f4c562612 100644 --- a/apps/app/src/lib/i18n/dictionaries/es.json +++ b/apps/app/src/lib/i18n/dictionaries/es.json @@ -227,6 +227,10 @@ "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", + "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", @@ -576,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 a3d5f3b7f..bac51d4bd 100644 --- a/apps/app/src/lib/i18n/dictionaries/fr.json +++ b/apps/app/src/lib/i18n/dictionaries/fr.json @@ -228,6 +228,10 @@ "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", + "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", @@ -577,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 04059e535..17eecaac3 100644 --- a/apps/app/src/lib/i18n/dictionaries/pt.json +++ b/apps/app/src/lib/i18n/dictionaries/pt.json @@ -228,6 +228,10 @@ "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", + "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", @@ -577,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",