diff --git a/cypress/e2e/memberships/deleteItemMembership.cy.ts b/cypress/e2e/memberships/deleteItemMembership.cy.ts index d8c975674..9023501aa 100644 --- a/cypress/e2e/memberships/deleteItemMembership.cy.ts +++ b/cypress/e2e/memberships/deleteItemMembership.cy.ts @@ -1,5 +1,6 @@ import { buildItemPath } from '../../../src/config/paths'; import { + CONFIRM_MEMBERSHIP_DELETE_BUTTON_ID, buildItemMembershipRowDeleteButtonId, buildShareButtonId, } from '../../../src/config/selectors'; @@ -16,6 +17,7 @@ const deleteItemMembership = ({ cy.get(`#${buildShareButtonId(itemId)}`).click(); cy.wait(TABLE_MEMBERSHIP_RENDER_TIME); cy.get(`#${buildItemMembershipRowDeleteButtonId(id)}`).click(); + cy.get(`#${CONFIRM_MEMBERSHIP_DELETE_BUTTON_ID}`).click(); }; describe('Delete Membership', () => { diff --git a/src/components/item/sharing/ConfirmMembership.tsx b/src/components/item/sharing/ConfirmMembership.tsx new file mode 100644 index 000000000..ba61a8a87 --- /dev/null +++ b/src/components/item/sharing/ConfirmMembership.tsx @@ -0,0 +1,110 @@ +import { + Alert, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from '@mui/material'; + +import { ItemMembership, PermissionLevel } from '@graasp/sdk'; +import { ItemRecord } from '@graasp/sdk/frontend'; +import { Button } from '@graasp/ui'; + +import { useCurrentUserContext } from '@/components/context/CurrentUserContext'; + +import { useBuilderTranslation } from '../../../config/i18n'; +import { mutations } from '../../../config/queryClient'; +import { CONFIRM_MEMBERSHIP_DELETE_BUTTON_ID } from '../../../config/selectors'; +import { BUILDER } from '../../../langs/constants'; +import CancelButton from '../../common/CancelButton'; + +const labelId = 'alert-dialog-title'; +const descriptionId = 'alert-dialog-description'; + +type Props = { + open?: boolean; + handleClose: () => void; + item: ItemRecord; + membershipToDelete: ItemMembership | null; + hasOnlyOneAdmin: boolean; +}; + +const DeleteItemDialog = ({ + item, + open = false, + handleClose, + membershipToDelete, + hasOnlyOneAdmin = false, +}: Props): JSX.Element => { + const { t: translateBuilder } = useBuilderTranslation(); + const { data: member } = useCurrentUserContext(); + + const { mutate: deleteItemMembership } = mutations.useDeleteItemMembership(); + + const onDelete = () => { + if (membershipToDelete?.id) { + deleteItemMembership({ id: membershipToDelete.id, itemId: item.id }); + handleClose(); + } + }; + + let dialogText = ''; + const isDeletingLastAdmin = + hasOnlyOneAdmin && membershipToDelete?.permission === PermissionLevel.Admin; + // incase of deleting the only admin + if (isDeletingLastAdmin) { + dialogText = translateBuilder(BUILDER.DELETE_LAST_ADMIN_ALERT_MESSAGE); + } else if (member?.id === membershipToDelete?.member?.id) { + // deleting yourself + dialogText = translateBuilder(BUILDER.DELETE_OWN_MEMBERSHIP_MESSAGE); + } else { + // delete other members + dialogText = translateBuilder(BUILDER.DELETE_MEMBERSHIP_MESSAGE, { + name: membershipToDelete?.member.name, + permissionLevel: membershipToDelete?.permission, + }); + } + return ( + + + {translateBuilder(BUILDER.DELETE_MEMBERSHIP)} + + + {isDeletingLastAdmin ? ( + {dialogText} + ) : ( + {dialogText} + )} + + + + {isDeletingLastAdmin ? ( + + ) : ( + <> + + + + )} + + + ); +}; + +export default DeleteItemDialog; diff --git a/src/components/item/sharing/ItemMembershipsTable.tsx b/src/components/item/sharing/ItemMembershipsTable.tsx index da5777d29..eb636f027 100644 --- a/src/components/item/sharing/ItemMembershipsTable.tsx +++ b/src/components/item/sharing/ItemMembershipsTable.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { Typography } from '@mui/material'; @@ -19,6 +19,7 @@ import { buildItemMembershipRowId, } from '../../../config/selectors'; import { BUILDER } from '../../../langs/constants'; +import DeleteItemDialog from './ConfirmMembership'; import TableRowDeleteButtonRenderer from './TableRowDeleteButtonRenderer'; import TableRowPermissionRenderer from './TableRowPermissionRenderer'; @@ -69,12 +70,23 @@ const ItemMembershipsTable = ({ }: Props): JSX.Element => { const { t: translateBuilder } = useBuilderTranslation(); - const { mutate: deleteItemMembership } = mutations.useDeleteItemMembership(); const { mutate: editItemMembership } = mutations.useEditItemMembership(); const { mutate: shareItem } = mutations.usePostItemMembership(); + const [open, setOpen] = useState(false); + const [membershipToDelete, setMembershipToDelete] = + useState(null); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; const onDelete = ({ instance }: { instance: ItemMembership }) => { - deleteItemMembership({ id: instance.id, itemId: item.id }); + setMembershipToDelete(instance); + handleClickOpen(); }; // never changes, so we can use useMemo @@ -196,16 +208,31 @@ const ItemMembershipsTable = ({ }); return ( - + <> + + {open && ( + per.permission === PermissionLevel.Admin, + ).length === 1 + } + /> + )} + ); }; diff --git a/src/config/selectors.ts b/src/config/selectors.ts index ea9f5a675..8266cbeea 100644 --- a/src/config/selectors.ts +++ b/src/config/selectors.ts @@ -312,6 +312,8 @@ export const EDIT_MODAL_ID = 'editModal'; export const EDIT_ITEM_MODAL_CANCEL_BUTTON_ID = 'editModalCancelButton'; export const FILE_SETTING_MAX_WIDTH_ID = 'fileSettingMaxWidth'; +export const CONFIRM_MEMBERSHIP_DELETE_BUTTON_ID = + 'confirmDeleteMembershipButton'; export const buildDownloadButtonId = (itemId: string): string => `download-button-id-${itemId}`; export const CUSTOM_APP_URL_ID = 'customAppURLId'; diff --git a/src/langs/ar.json b/src/langs/ar.json index e1189a164..f0a880c64 100644 --- a/src/langs/ar.json +++ b/src/langs/ar.json @@ -242,6 +242,12 @@ "USER_SWITCH_SIGN_OUT_BUTTON": "خروج", "USER_SWITCH_SIGNED_OUT_TOOLTIP": "أنت غير مسجل الدخول.", "USER_SWITCH_SWITCH_USER_TEXT": "تسجيل الدخول بحساب آخر", + "DELETE_MEMBERSHIP": "حذف العضوية", + "DELETE_MEMBERSHIP_MODAL_CONFIRM_BUTTON": "تأكيد", + "DELETE_OWN_MEMBERSHIP_MESSAGE": "هل أنت متأكد من إلغاء عضويتك على هذا العنصر؟", + "DELETE_MEMBERSHIP_MESSAGE": "هل أنت متأكد من إزالة عضوية {{name}}ك {{permissionLevel}} على هذا العنصر؟", + "DELETE_LAST_ADMIN_ALERT_MESSAGE": "لا يمكنك إلغاء عضوية هذا الأدمن حيث أنه الأدمن الوحيد", + "APPROVE_BUTTON_TEXT": "موافق", "STATUS_TOOLTIP_IS_PINNED": "مُثبت", "STATUS_TOOLTIP_IS_HIDDEN": "مخفي", "STATUS_TOOLTIP_IS_PUBLIC": "عام", diff --git a/src/langs/constants.ts b/src/langs/constants.ts index 4118097be..3d7e571ac 100644 --- a/src/langs/constants.ts +++ b/src/langs/constants.ts @@ -307,6 +307,13 @@ export const BUILDER = { STATUS_TOOLTIP_IS_COLLAPSIBLE: 'STATUS_TOOLTIP_IS_COLLAPSIBLE', STATUS_TOOLTIP_SHOW_CHATBOX: 'STATUS_TOOLTIP_SHOW_CHATBOX', SETTINGS_FILE_SETTINGS_TITLE: 'SETTINGS_FILE_SETTINGS_TITLE', + DELETE_MEMBERSHIP: 'DELETE_MEMBERSHIP', + DELETE_MEMBERSHIP_MODAL_CONFIRM_BUTTON: + 'DELETE_MEMBERSHIP_MODAL_CONFIRM_BUTTON', + DELETE_MEMBERSHIP_MESSAGE: 'DELETE_MEMBERSHIP_MESSAGE', + DELETE_OWN_MEMBERSHIP_MESSAGE: 'DELETE_OWN_MEMBERSHIP_MESSAGE', + DELETE_LAST_ADMIN_ALERT_MESSAGE: 'DELETE_LAST_ADMIN_ALERT_MESSAGE', + APPROVE_BUTTON_TEXT: 'APPROVE_BUTTON_TEXT', APP_URL: 'APP_URL', CREATE_CUSTOM_APP: 'CREATE_CUSTOM_APP', }; diff --git a/src/langs/de.json b/src/langs/de.json index 81b1cd218..a6382dcdd 100644 --- a/src/langs/de.json +++ b/src/langs/de.json @@ -227,5 +227,10 @@ "USER_SWITCH_PROFILE_BUTTON": "Siehe Profil", "USER_SWITCH_SIGN_OUT_BUTTON": "Abmelden", "USER_SWITCH_SIGNED_OUT_TOOLTIP": "Sie sind nicht angemeldet.", - "USER_SWITCH_SWITCH_USER_TEXT": "Melden Sie sich mit einem anderen Konto an" + "USER_SWITCH_SWITCH_USER_TEXT": "Melden Sie sich mit einem anderen Konto an", + "DELETE_MEMBERSHIP": "Mitgliedschaft löschen", + "DELETE_MEMBERSHIP_MODAL_CONFIRM_BUTTON": "Bestätigen", + "DELETE_OWN_MEMBERSHIP_MESSAGE": "Sind Sie sicher, dass Sie Ihre eigene Berechtigung für dieses Element entfernen möchten? Sie verlieren den Zugriff auf dieses Element, diese Aktion ist irreversibel.", + "DELETE_MEMBERSHIP_MESSAGE": "Sind Sie sicher, dass Sie {{permissionLevel}} Zugang für diesen Artikel für {{name}} entfernen möchten?", + "DELETE_LAST_ADMIN_ALERT_MESSAGE": "Sie dürfen diesen Administrator nicht löschen, da dies der einzige Administrator ist" } diff --git a/src/langs/en.json b/src/langs/en.json index 9d79a21b6..c54a9ebc8 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -249,6 +249,12 @@ "STATUS_TOOLTIP_IS_COLLAPSIBLE": "Collapsible", "STATUS_TOOLTIP_SHOW_CHATBOX": "Chatbox visible", "SETTINGS_FILE_SETTINGS_TITLE": "File Settings", + "DELETE_MEMBERSHIP": "Delete Membership", + "DELETE_MEMBERSHIP_MODAL_CONFIRM_BUTTON": "Confirm", + "DELETE_OWN_MEMBERSHIP_MESSAGE": "Are you sure you want to delete your own permission on this item? You will loose access to this item, this action can not be undone.", + "DELETE_MEMBERSHIP_MESSAGE": "Are you sure you want to remove {{permissionLevel}} access on this item for {{name}}? They will loose access to this item.", + "DELETE_LAST_ADMIN_ALERT_MESSAGE": "You are not allowed to delete this admin as this is the only admin", + "APPROVE_BUTTON_TEXT": "OK", "APP_URL": "App Url", "CREATE_CUSTOM_APP": "Add Your Custom App" } diff --git a/src/langs/fr.json b/src/langs/fr.json index ae8b87a49..8dd10cdd1 100644 --- a/src/langs/fr.json +++ b/src/langs/fr.json @@ -247,5 +247,10 @@ "STATUS_TOOLTIP_IS_PUBLISHED": "Publié", "STATUS_TOOLTIP_IS_COLLAPSIBLE": "Minifié", "STATUS_TOOLTIP_SHOW_CHATBOX": "Chatbox visible", - "SETTINGS_FILE_SETTINGS_TITLE": "Paramètres du fichier" + "SETTINGS_FILE_SETTINGS_TITLE": "Paramètres du fichier", + "DELETE_MEMBERSHIP": "Supprimer l'autorisation", + "DELETE_MEMBERSHIP_MODAL_CONFIRM_BUTTON": "Confirmer", + "DELETE_OWN_MEMBERSHIP_MESSAGE": "Êtes-vous sûr de vouloir supprimer votre propre autorisation sur cet élément ? Vous perdrez l'accès à cet élément, cette action est irréversible.", + "DELETE_MEMBERSHIP_MESSAGE": "Êtes-vous sûr de vouloir supprimer l'accès en {{permissionLevel}} sur cet élément à {{name}} ? Il leur sera impossible d'accéder à cet élément.", + "DELETE_LAST_ADMIN_ALERT_MESSAGE": "Vous n'êtes pas autorisé à supprimer le dernier administrateur de cet élément." }