diff --git a/apps/web/lib/features/task-status/delete-status-confirmation-modal.tsx b/apps/web/lib/features/task-status/delete-status-confirmation-modal.tsx new file mode 100644 index 000000000..530b46229 --- /dev/null +++ b/apps/web/lib/features/task-status/delete-status-confirmation-modal.tsx @@ -0,0 +1,100 @@ +import { ITaskStatusItemList, TaskStatusEnum } from '@/app/interfaces'; +import { useTaskStatus, useTeamTasks } from '@app/hooks'; +import { Button, Card, Modal, Text } from 'lib/components'; +import { useTranslations } from 'next-intl'; +import { useCallback, useMemo } from 'react'; + +interface DeleteTaskStatusModalProps { + open: boolean; + closeModal: () => void; + status: ITaskStatusItemList; + onCancel: () => void; +} + +/** + * A confirmation modal before deleting a status that is used by at least one task in the team. + * + * @param {Object} props - The props Object + * @param {boolean} props.open - If true open the modal otherwise close the modal + * @param {() => void} props.closeModal - A function to close the modal + * @param {ITaskStatusItemList} props.status - The status object to be deleted + * @param {() => void} props.onCancel - Callback function when deletion is cancelled + * + * @returns {JSX.Element} The modal element + */ +export function DeleteTaskStatusConfirmationModal(props: DeleteTaskStatusModalProps) { + const { closeModal, open, status, onCancel } = props; + const { deleteTaskStatus, deleteTaskStatusLoading } = useTaskStatus(); + const t = useTranslations(); + const { tasks, updateTask } = useTeamTasks(); + + // Filter tasks that are using the current status + const tasksUsingStatus = useMemo(() => tasks.filter((task) => task.status === status.name), [tasks, status.name]); + + const handleCloseModal = useCallback(() => { + onCancel(); + closeModal(); + }, [closeModal, onCancel]); + + // Function to handle task status deletion + const handleDeleteTaskStatus = useCallback(async () => { + if (!status.id) { + console.error('Status ID is not provided.'); + return; + } + + try { + // Update each task that uses the current status + const updatePromises = tasksUsingStatus.map((task) => updateTask({ ...task, status: TaskStatusEnum.OPEN })); + + await Promise.all(updatePromises); + + // Delete the task status after updating related tasks + await deleteTaskStatus(status.id); + } catch (error) { + console.error('Error while deleting task status:', error); + } finally { + handleCloseModal(); + } + }, [deleteTaskStatus, handleCloseModal, status, tasksUsingStatus, updateTask]); + + return ( + + +
+ + {t('pages.taskStatus.DELETE_STATUS_CONFIRMATION', { statusName: status.name })} + +
+ + +
+
+
+
+ ); +} diff --git a/apps/web/lib/settings/task-statuses-form.tsx b/apps/web/lib/settings/task-statuses-form.tsx index 99ef5c37f..678e9b569 100644 --- a/apps/web/lib/settings/task-statuses-form.tsx +++ b/apps/web/lib/settings/task-statuses-form.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-mixed-spaces-and-tabs */ -import { useModal, useRefetchData, useTaskStatus } from '@app/hooks'; +import { useModal, useRefetchData, useTaskStatus, useTeamTasks } from '@app/hooks'; import { IIcon, ITaskStatusItemList } from '@app/interfaces'; import { userState } from '@app/stores'; import { clsxm } from '@app/utils'; @@ -15,6 +15,7 @@ import IconPopover from './icon-popover'; import { StatusesListCard } from './list-card'; import SortTasksStatusSettings from '@components/pages/kanban/sort-tasks-status-settings'; import { StandardTaskStatusDropDown } from 'lib/features'; +import { DeleteTaskStatusConfirmationModal } from '../features/task-status/delete-status-confirmation-modal'; type StatusForm = { formOnly?: boolean; @@ -152,6 +153,9 @@ export const TaskStatusesForm = ({ ? updateArray.sort((a: any, b: any) => a.order - b.order) : []; const { isOpen, closeModal, openModal } = useModal(); + const {isOpen : isDeleteConfirmationOpen , closeModal : closeDeleteConfirmationModal, openModal : openDeleteConfirmationModal} = useModal() + const [statusToDelete, setStatusToDelete] = useState(null) + const {tasks} = useTeamTasks() return ( <> @@ -282,8 +286,20 @@ export const TaskStatusesForm = ({ setCreateNew(false); setEdit(status); }} - onDelete={() => { - deleteTaskStatus(status.id); + onDelete={async () => { + try { + const isStatusUsed = tasks.find( + (t) => t.status?.toLowerCase() === status.name?.toLowerCase() + ); + if (isStatusUsed) { + setStatusToDelete(status); + openDeleteConfirmationModal(); + } else { + await deleteTaskStatus(status.id); + } + } catch (error) { + console.error(error); + } }} isStatus={true} /> @@ -298,6 +314,7 @@ export const TaskStatusesForm = ({ + {statusToDelete && setStatusToDelete(null)} status={statusToDelete} open={isDeleteConfirmationOpen} closeModal={closeDeleteConfirmationModal}/>} ); }; diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index d977cdb04..e05bce974 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "لا يمكن تغيير نوع المهمة الملحمية.", "TASK_HAS_PARENT": "لا يمكن تغيير نوع المهمة حيث أن لديها بالفعل والد." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "أنت على وشك حذف الحالة {statusName} التي يتم استخدامها من قبل المهام النشطة؛ يرجى تأكيد الإجراء" + }, "auth": { "SEND_CODE": "إرسال الرمز", "RESEND_CODE": "إعادة إرسال الرمز", diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index 8042b15b4..70bebf88c 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Епичният тип задача не може да бъде променен.", "TASK_HAS_PARENT": "Типът задача не може да бъде променен, тъй като задачата вече има родител." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Вие сте на път да изтриете състоянието {statusName}, което се използва от активни задачи; моля, потвърдете действието" + }, "auth": { "SEND_CODE": "изпрати код", "RESEND_CODE": "Изпрати код отново", diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 43c1d47a4..e4938bb7b 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Epische Aufgabentypen können nicht geändert werden.", "TASK_HAS_PARENT": "Der Aufgabentyp kann nicht geändert werden, da die Aufgabe bereits ein übergeordnetes Element hat." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Sie sind dabei, den Status {statusName} zu löschen, der von aktiven Aufgaben verwendet wird; bitte bestätigen Sie die Aktion" + }, "auth": { "SEND_CODE": "Code senden", "RESEND_CODE": "Code erneut senden", diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 52974d479..03961d2a3 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Epic Task Type can not be changed.", "TASK_HAS_PARENT": "Task Type can not be changed as Task has already Parent." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "You are about to Delete the Status {statusName} that is used by active tasks; please confirm action" + }, "auth": { "SEND_CODE": "send code", "RESEND_CODE": "Resend Code", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 98ed6351d..ed2e54490 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "La tarea de tipo Épico no se puede cambiar.", "TASK_HAS_PARENT": "El tipo de tarea no se puede cambiar porque la tarea ya tiene un padre." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Está a punto de eliminar el estado {statusName} que está siendo utilizado por tareas activas; por favor confirme la acción" + }, "auth": { "SEND_CODE": "enviar código", "RESEND_CODE": "Reenviar código", diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index e0ce01e92..69179edce 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Le type de tâche épique ne peut pas être modifié.", "TASK_HAS_PARENT": "Le type de tâche ne peut pas être modifié car la tâche a déjà un parent." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Vous êtes sur le point de supprimer le statut {statusName} qui est utilisé par des tâches actives ; veuillez confirmer l'action" + }, "auth": { "SEND_CODE": "envoyer le code", "RESEND_CODE": "Renvoyer le code", diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 2762188c2..83c9b1312 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "לא ניתן לשנות סוג משימה אפית.", "TASK_HAS_PARENT": "לא ניתן לשנות סוג משימה מכיוון שלמשימה כבר יש הורה." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "אתה עומד למחוק את המצב {statusName} המשמש במשימות פעילות; נא לאשר פעולה" + }, "auth": { "SEND_CODE": "שלח קוד", "RESEND_CODE": "שלח קוד מחדש", diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 79e55f0d3..90b440c68 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Il tipo di Compito Epico non può essere cambiato.", "TASK_HAS_PARENT": "Il tipo di Compito non può essere cambiato poiché il Compito ha già un Genitore." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Stai per eliminare lo stato {statusName} utilizzato da attività attive; per favore conferma l'azione" + }, "auth": { "SEND_CODE": "Invia Codice", "RESEND_CODE": "Reinvia Codice", diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index 8919befc3..90b22103b 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Epic taaktype kan niet worden gewijzigd.", "TASK_HAS_PARENT": "Taaktype kan niet worden gewijzigd omdat de taak al een bovenliggende taak heeft." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "U staat op het punt de status {statusName} te verwijderen die wordt gebruikt door actieve taken; bevestig alstublieft de actie" + }, "auth": { "SEND_CODE": "code verzenden", "RESEND_CODE": "Code opnieuw verzenden", diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index fb9a1170a..afabe281f 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Typ Zadania jako 'Epic' nie może zostać zmieniony.", "TASK_HAS_PARENT": "Typ Zadania nie może zostać zmieniony, ponieważ Zadanie ma już przypisanego rodzica." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Zaraz usuniesz status {statusName}, który jest używany przez aktywne zadania; proszę potwierdzić akcję" + }, "auth": { "SEND_CODE": "wyślij kod", "RESEND_CODE": "Wyślij ponownie kod", diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index f1a2201b2..63c3f6cf7 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "O Tipo de Tarefa Épica não pode ser alterado.", "TASK_HAS_PARENT": "O Tipo de Tarefa não pode ser alterado, pois a Tarefa já possui um Pai." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Está prestes a eliminar o status {statusName} que está a ser usado por tarefas ativas; por favor, confirme a ação" + }, "auth": { "SEND_CODE": "enviar código", "RESEND_CODE": "Reenviar Código", diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index f0afd994e..8f2e0f7e5 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "Тип задачи 'Epic' не может быть изменен.", "TASK_HAS_PARENT": "Тип задачи не может быть изменен, так как у задачи уже есть родитель." }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "Вы собираетесь удалить статус {statusName}, который используется активными задачами; пожалуйста, подтвердите действие" + }, "auth": { "SEND_CODE": "отправить код", "RESEND_CODE": "Отправить код повторно", diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 1b63a666f..4484dfded 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -340,6 +340,9 @@ "TASK_IS_ALREADY_EPIC": "史诗类型任务无法更改。", "TASK_HAS_PARENT": "任务已有父任务,无法更改任务类型。" }, + "taskStatus": { + "DELETE_STATUS_CONFIRMATION": "您即将删除正在被活跃任务使用的状态 {statusName};请确认操作" + }, "auth": { "SEND_CODE": "发送验证码", "RESEND_CODE": "重新发送验证码",