From 35a5011f242e4fe5d6210e5b422c59598edf5500 Mon Sep 17 00:00:00 2001 From: Ederson Date: Mon, 30 Sep 2024 10:33:05 -0400 Subject: [PATCH] feat: allow user remove themselves from a workspace --- src/app/core/services/workspace.service.ts | 8 +++ src/app/i18n/locales/de.json | 11 +++- src/app/i18n/locales/en.json | 7 +++ src/app/i18n/locales/es.json | 7 +++ src/app/i18n/locales/fr.json | 7 +++ src/app/i18n/locales/it.json | 7 +++ src/app/i18n/locales/ru.json | 7 +++ src/app/i18n/locales/tw.json | 7 +++ src/app/i18n/locales/zh.json | 7 +++ .../Members/components/LeaveModal.tsx | 37 +++++++++++++ .../containers/MemberDetailsContainer.tsx | 53 +++++++++++++++++++ 11 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 src/app/newSettings/Sections/Workspace/Members/components/LeaveModal.tsx diff --git a/src/app/core/services/workspace.service.ts b/src/app/core/services/workspace.service.ts index 816ed491e..8e49f85fd 100644 --- a/src/app/core/services/workspace.service.ts +++ b/src/app/core/services/workspace.service.ts @@ -381,6 +381,13 @@ export function getWorkspace(workspaceId: string): Promise { }); } +export function leaveWorkspace(workspaceId: string): Promise { + const workspaceClient = SdkFactory.getNewApiInstance().createWorkspacesClient(); + return workspaceClient.leaveWorkspace(workspaceId).catch((error) => { + throw errorService.castError(error); + }); +} + const workspacesService = { getWorkspaces, getWorkspacesMembers, @@ -422,6 +429,7 @@ const workspacesService = { removeMember, getUsage, getWorkspace, + leaveWorkspace, }; export default workspacesService; diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json index e8d9217d7..028f00c9a 100644 --- a/src/app/i18n/locales/de.json +++ b/src/app/i18n/locales/de.json @@ -1488,8 +1488,14 @@ "removalModal": { "title": "Mitglied entfernen", "description": "{{name}} verliert den Zugriff auf den Arbeitsbereich. Alle ihre Dateien und Ordner bleiben unverändert.", - "remove": "Entfernen", - "removing": "Entfernen läuft" + "leave": "Entfernen", + "leaving": "Entfernen läuft" + }, + "leaveModal": { + "title": "Arbeitsbereich verlassen", + "description": "{{name}}, Sie verlieren den Zugriff auf den Arbeitsbereich. Alle ihre Dateien und Ordner bleiben unverändert.", + "remove": "Verlassen", + "removing": "Jetzt verlassen" }, "changePasswordModal": { "title": "Passwortänderung anfordern", @@ -1549,6 +1555,7 @@ "deactivate": "Mitglied deaktivieren", "reactivate": "Mitglied reaktivieren", "remove": "Mitglied entfernen", + "leave": "Arbeitsbereich verlassen", "passwordChange": "Passwortänderung anfordern" } }, diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index e2d90173b..d2d009b20 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -1549,6 +1549,12 @@ "remove": "Remove", "removing": "Removing" }, + "leaveModal": { + "title": "Leave workspace", + "description": "{{name}}, you will lose access to the Workspace. All their files and folders will stay the same.", + "leave": "Leave", + "leaving": "Leaving" + }, "changePasswordModal": { "title": "Request password change", "description": "User will receive a recommendation to update their personal account password. Use this function in case you believe their password may have been breached.", @@ -1607,6 +1613,7 @@ "deactivate": "Deactivate member", "reactivate": "Reactivate member", "remove": "Remove member", + "leave": "Leave workspace", "passwordChange": "Request password change" } }, diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json index 1a355ffd1..0ba4f6b33 100644 --- a/src/app/i18n/locales/es.json +++ b/src/app/i18n/locales/es.json @@ -1527,6 +1527,12 @@ "remove": "Eliminar", "removing": "Eliminando" }, + "leaveModal": { + "title": "Abandonar espacio de trabajo", + "description": "{{name}}, perderás acceso al espacio de trabajo. Todos sus archivos y carpetas permanecerán iguales.", + "leave": "Abandonar", + "leaving": "Abandonando" + }, "changePasswordModal": { "title": "Solicitar cambio de contraseña", "description": "El usuario recibirá una recomendación para actualizar la contraseña de su cuenta personal. Utilice esta función si cree que su contraseña podría haber sido comprometida.", @@ -1585,6 +1591,7 @@ "deactivate": "Desactivar miembro", "reactivate": "Reactivar miembro", "remove": "Eliminar miembro", + "leave": "Abandonar espacio de trabajo", "passwordChange": "Solicitar cambio de contraseña" } }, diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json index b85c78c8a..dcbbeb9bd 100644 --- a/src/app/i18n/locales/fr.json +++ b/src/app/i18n/locales/fr.json @@ -1476,6 +1476,12 @@ "remove": "Supprimer", "removing": "Suppression" }, + "leaveModal": { + "title": "Quitter l'espace de travail", + "description": "{{name}}, vous perdrez l'accès à l'espace de travail. Tous leurs fichiers et dossiers resteront inchangés.", + "leave": "Partir", + "leaving": "Sortie" + }, "changePasswordModal": { "title": "Demander un changement de mot de passe", "description": "L'utilisateur recevra une recommandation pour mettre à jour le mot de passe de son compte personnel. Utilisez cette fonction si vous pensez que son mot de passe pourrait avoir été compromis.", @@ -1534,6 +1540,7 @@ "deactivate": "Désactiver le membre", "reactivate": "Réactiver le membre", "remove": "Supprimer le membre", + "leave": "Quitter l'espace de travail", "passwordChange": "Demande de changement de mot de passe" } }, diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json index 94a59647e..3a4fa28cc 100644 --- a/src/app/i18n/locales/it.json +++ b/src/app/i18n/locales/it.json @@ -1536,6 +1536,12 @@ "remove": "Rimuovi", "removing": "Rimuovendo" }, + "leaveModal": { + "title": "Lascia spazio di lavoro", + "description": "{{name}}, perderai l'accesso al spazio di lavoro. Tutti i loro file e cartelle rimarranno gli stessi.", + "leave": "Lascia", + "leaving": "Lascia" + }, "changePasswordModal": { "title": "Richiedi cambio password", "description": "L'utente riceverà una raccomandazione per aggiornare la password del proprio account personale. Utilizza questa funzione se ritieni che la sua password possa essere stata compromessa.", @@ -1594,6 +1600,7 @@ "deactivate": "Disattiva membro", "reactivate": "Riattiva membro", "remove": "Rimuovi membro", + "leave": "Lascia spazio di lavoro", "passwordChange": "Richiedi la modifica della password" } }, diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json index 28060ec8d..840ad6f89 100644 --- a/src/app/i18n/locales/ru.json +++ b/src/app/i18n/locales/ru.json @@ -1516,6 +1516,12 @@ "remove": "Удалить", "removing": "Удаление" }, + "leaveModal": { + "title": "Покинуть рабочее пространство", + "description": "{{name}}, ты потеряешь доступ к Рабочему пространству. Все ваши файлы и папки останутся прежними.", + "leave": "Покидать", + "leaving": "Покидать" + }, "changePasswordModal": { "title": "Запрос на изменение пароля", "description": "Пользователь получит рекомендацию обновить пароль своей личной учетной записи. Используйте эту функцию, если считаете, что его пароль мог быть скомпрометирован.", @@ -1574,6 +1580,7 @@ "deactivate": "Деактивировать участника", "reactivate": "Активировать участника", "remove": "Удалить участника", + "leave": "Покинуть рабочее пространство", "passwordChange": "Запросить смену пароля" } }, diff --git a/src/app/i18n/locales/tw.json b/src/app/i18n/locales/tw.json index 1a2c5d5ee..865b63992 100644 --- a/src/app/i18n/locales/tw.json +++ b/src/app/i18n/locales/tw.json @@ -1510,6 +1510,12 @@ "remove": "删除", "removing": "去除" }, + "leaveModal": { + "title": "离开工作区", + "description": "{{name}},您将无法访问工作区。其所有文件和文件夹将保持不变。", + "leave": "离开", + "leaving": "离开" + }, "changePasswordModal": { "title": "要求更改密碼", "description": "用戶將收到更新個人帳戶密碼的建議。如果您認為其密碼可能已被洩露,請使用此功能。", @@ -1568,6 +1574,7 @@ "deactivate": "停用成員", "reactivate": "重新啟用成員", "remove": "移除成員", + "leave": "离开工作区", "passwordChange": "要求更改密碼" } }, diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json index 41ea1f55e..6cdeda99c 100644 --- a/src/app/i18n/locales/zh.json +++ b/src/app/i18n/locales/zh.json @@ -1549,6 +1549,12 @@ "remove": "删除", "removing": "去除" }, + "leaveModal": { + "title": "离开工作区", + "description": "{{name}},您将无法访问工作区。其所有文件和文件夹将保持不变。", + "leave": "离开", + "leaving": "离开" + }, "changePasswordModal": { "title": "请求更改密码", "description": "用户将收到一个建议更新其个人帐户密码的通知。如果您认为他们的密码可能已被泄露,请使用此功能。", @@ -1607,6 +1613,7 @@ "deactivate": "停用会员", "reactivate": "重新激活成员", "remove": "移除成員", + "leave": "离开工作区", "passwordChange": "请求更改密码" } }, diff --git a/src/app/newSettings/Sections/Workspace/Members/components/LeaveModal.tsx b/src/app/newSettings/Sections/Workspace/Members/components/LeaveModal.tsx new file mode 100644 index 000000000..f8e0ec9cd --- /dev/null +++ b/src/app/newSettings/Sections/Workspace/Members/components/LeaveModal.tsx @@ -0,0 +1,37 @@ +import { useTranslationContext } from '../../../../../i18n/provider/TranslationProvider'; + +import ActionModal from './ActionModal'; + +const LeaveMemberModal = ({ + isOpen, + onClose, + name, + onLeave, + isLoading, +}: { + name: string; + isOpen: boolean; + onLeave: () => void; + onClose: () => void; + isLoading: boolean; +}) => { + const { translate } = useTranslationContext(); + + return ( + + ); +}; + +export default LeaveMemberModal; diff --git a/src/app/newSettings/Sections/Workspace/Members/containers/MemberDetailsContainer.tsx b/src/app/newSettings/Sections/Workspace/Members/containers/MemberDetailsContainer.tsx index 9ac30ddcc..b76099728 100644 --- a/src/app/newSettings/Sections/Workspace/Members/containers/MemberDetailsContainer.tsx +++ b/src/app/newSettings/Sections/Workspace/Members/containers/MemberDetailsContainer.tsx @@ -16,6 +16,10 @@ import RequestPasswordChangeModal from '../components/RequestPasswordModal'; import TeamsTab from '../components/TeamsTab'; import UserCard from '../components/UserCard'; import RemoveMemberModal from '../components/RemoveModal'; +import LeaveMemberModal from '../components/LeaveModal'; +import { workspaceThunks } from 'app/store/slices/workspaces/workspacesStore'; +import { planThunks } from 'app/store/slices/plan'; +import { useDispatch } from 'react-redux'; interface MemberDetailsContainer { member: WorkspaceUser; @@ -25,13 +29,16 @@ interface MemberDetailsContainer { } const MemberDetailsContainer = ({ member, getWorkspacesMembers, isOwner, deselectMember }: MemberDetailsContainer) => { + const dispatch = useDispatch(); const { translate } = useTranslationContext(); const [isOptionsOpen, setIsOptionsOpen] = useState(false); const [isDeactivateModalOpen, setIsDeactivateModalOpen] = useState(false); + const [isLeaveModalOpen, setIsLeaveModalOpen] = useState(false); const [isDeactivatingMember, setIsDeactivatingMember] = useState(false); const [isReactivateModalOpen, setIsReactivateModalOpen] = useState(false); const [isReactivatingMember, setIsReactivatingMember] = useState(false); const [isRemovingMember, setIsRemovingMember] = useState(false); + const [isLeavingMember, setIsLeavingMember] = useState(false); const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false); const [isRequestChangePasswordModalOpen, setIsRequestChangePasswordModalOpen] = useState(false); const [isSendingPasswordRequest, setIsSendingPasswordRequest] = useState(false); @@ -84,6 +91,22 @@ const MemberDetailsContainer = ({ member, getWorkspacesMembers, isOwner, deselec } }; + const leaveMember = async () => { + try { + setIsLeavingMember(true); + await workspacesService.leaveWorkspace(member.workspaceId); + } catch (error) { + errorService.reportError(error); + } finally { + setIsLeavingMember(false); + setIsLeaveModalOpen(false); + deselectMember(); + dispatch(workspaceThunks.setSelectedWorkspace({ workspaceId: null })); + dispatch(workspaceThunks.fetchWorkspaces()); + dispatch(planThunks.fetchBusinessLimitUsageThunk()); + } + }; + // MOCK DATA TO BE IMPLENTED const isActivityEnabled = Math.random() < 0.5; const activity = [ @@ -196,6 +219,29 @@ const MemberDetailsContainer = ({ member, getWorkspacesMembers, isOwner, deselec )} )} + + {!isOwner && !member.isOwner && ( +
+ + {isOptionsOpen && ( + +
+ + )} + + )} {member ? ( @@ -247,6 +293,13 @@ const MemberDetailsContainer = ({ member, getWorkspacesMembers, isOwner, deselec onRemove={removeMember} isLoading={isRemovingMember} /> + setIsLeaveModalOpen(false)} + onLeave={leaveMember} + isLoading={isLeavingMember} + /> ); };