This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { FC, Fragment, ReactNode, useEffect } from 'react'
|
||||
import { clx } from '../helpers/utils'
|
||||
import HeaderModal from './HeaderModal'
|
||||
|
||||
interface Props {
|
||||
@@ -8,6 +9,8 @@ interface Props {
|
||||
isHeader?: boolean,
|
||||
title_header?: string,
|
||||
width?: number
|
||||
/** Raise above popovers (z-80) when confirm opens from nested panels */
|
||||
elevated?: boolean
|
||||
}
|
||||
|
||||
const DefaulModal: FC<Props> = (props: Props) => {
|
||||
@@ -26,7 +29,14 @@ const DefaulModal: FC<Props> = (props: Props) => {
|
||||
{
|
||||
props.open && (
|
||||
<Fragment>
|
||||
<div style={{ maxWidth: props.width }} className='xl:justify-center xl:items-center items-end flex overflow-hidden fixed inset-0 z-[60] h-auto top-0 bottom-0 m-auto outline-none focus:outline-none xl:max-w-xl mx-auto'>
|
||||
<div
|
||||
style={{ maxWidth: props.width }}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
className={clx(
|
||||
'xl:justify-center xl:items-center items-end flex overflow-hidden fixed inset-0 h-auto top-0 bottom-0 m-auto outline-none focus:outline-none xl:max-w-xl mx-auto',
|
||||
props.elevated ? 'z-[100]' : 'z-[60]',
|
||||
)}
|
||||
>
|
||||
<div className='relative max-h-[85vh] bottom-0 left-0 flex xl:items-center items-end w-full xl:my-6 xl:p-2'>
|
||||
<div className='border-0 max-h-[85vh] p-5 lg:min-w-full overflow-y-auto rounded-3xl rounded-b-none xl:rounded-b-3xl relative flex flex-col w-full modalGlass2 outline-none focus:outline-none'>
|
||||
|
||||
@@ -46,7 +56,14 @@ const DefaulModal: FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div onClick={props.close} className='fixed size-full top-0 bottom-0 right-0 modalGlass inset-0 z-50 '></div>
|
||||
<div
|
||||
onClick={props.close}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
className={clx(
|
||||
'fixed size-full top-0 bottom-0 right-0 modalGlass inset-0',
|
||||
props.elevated ? 'z-[90]' : 'z-50',
|
||||
)}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { FC, useState } from 'react'
|
||||
import DefaulModal from './DefaulModal'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from './Button'
|
||||
import DefaulModal from './DefaulModal'
|
||||
import Textarea from './Textarea'
|
||||
|
||||
type Props = {
|
||||
@@ -18,12 +19,13 @@ const ModalConfrim: FC<Props> = (props: Props) => {
|
||||
const { t } = useTranslation('global')
|
||||
const [description, setDescription] = useState<string>('')
|
||||
|
||||
return (
|
||||
return createPortal(
|
||||
<DefaulModal
|
||||
open={props.isOpen}
|
||||
close={props.close}
|
||||
title_header={t('confrim.subject')}
|
||||
isHeader
|
||||
elevated
|
||||
>
|
||||
<div className='mt-6'>
|
||||
<div className='text-sm text-center'>
|
||||
@@ -60,7 +62,8 @@ const ModalConfrim: FC<Props> = (props: Props) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
</DefaulModal>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,13 @@ import TaskDetailTimePopover from "./time/TaskDetailTimePopover";
|
||||
import type { TaskUser } from "./users/types";
|
||||
import type { UserItemType } from "../../../users/types/UserTypes";
|
||||
import type { TaskDetailType } from "../../task/types/TaskTypes";
|
||||
import { useCreateCheckListItem, useCreateTaskAttachment, useCreateTaskRemark, useUpdateTask } from "../../task/hooks/useTaskData";
|
||||
import {
|
||||
useCreateCheckListItem,
|
||||
useCreateTaskAttachment,
|
||||
useCreateTaskRemark,
|
||||
useDeleteTaskRemark,
|
||||
useUpdateTask,
|
||||
} from "../../task/hooks/useTaskData";
|
||||
import { useSingleUpload } from "../../../service/hooks/useServiceData";
|
||||
import { ErrorType } from "../../../../helpers/types";
|
||||
|
||||
@@ -31,6 +37,7 @@ type Props = {
|
||||
const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, taskId }) => {
|
||||
const { t } = useTranslation("global");
|
||||
const createTaskRemark = useCreateTaskRemark();
|
||||
const deleteTaskRemark = useDeleteTaskRemark();
|
||||
const createCheckListItem = useCreateCheckListItem();
|
||||
const createTaskAttachment = useCreateTaskAttachment();
|
||||
const singleUpload = useSingleUpload();
|
||||
@@ -224,6 +231,29 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
|
||||
);
|
||||
};
|
||||
|
||||
const handleDeleteLabel = (id: string) => {
|
||||
const effectiveTaskId = taskId || taskDetail?.id;
|
||||
if (!effectiveTaskId) return;
|
||||
|
||||
deleteTaskRemark.mutate(
|
||||
{ id, taskId: effectiveTaskId },
|
||||
{
|
||||
onSuccess: () => {
|
||||
setLabels((prev) => prev.filter((label) => label.id !== id));
|
||||
setSelectedLabelIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(id);
|
||||
return next;
|
||||
});
|
||||
toast(t("success"), "success");
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error.message[0], "error");
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleChecklistClose = () => {
|
||||
if (isChecklistOpen) onTabChange("checklist");
|
||||
};
|
||||
@@ -338,7 +368,9 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
|
||||
onViewChange={setLabelsView}
|
||||
onToggle={handleLabelToggle}
|
||||
onCreate={handleCreate}
|
||||
onDelete={handleDeleteLabel}
|
||||
isSubmitting={createTaskRemark.isPending}
|
||||
isDeleting={deleteTaskRemark.isPending}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AddCircle, Edit } from "iconsax-react";
|
||||
import { type FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Button from "../../../../../components/Button";
|
||||
import TrashWithConfrim from "../../../../../components/TrashWithConfrim";
|
||||
import TaskDetailLabelCheckbox from "./TaskDetailLabelCheckbox";
|
||||
import type { TaskLabel } from "./types";
|
||||
|
||||
@@ -10,10 +11,20 @@ type Props = {
|
||||
selectedIds: Set<string>;
|
||||
onToggle: (id: string) => void;
|
||||
onEdit: (label: TaskLabel) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onCreateClick: () => void;
|
||||
isDeleting?: boolean;
|
||||
};
|
||||
|
||||
const TaskDetailLabelsList: FC<Props> = ({ labels, selectedIds, onToggle, onEdit, onCreateClick }) => {
|
||||
const TaskDetailLabelsList: FC<Props> = ({
|
||||
labels,
|
||||
selectedIds,
|
||||
onToggle,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onCreateClick,
|
||||
isDeleting = false,
|
||||
}) => {
|
||||
const { t } = useTranslation("global");
|
||||
|
||||
return (
|
||||
@@ -32,7 +43,8 @@ const TaskDetailLabelsList: FC<Props> = ({ labels, selectedIds, onToggle, onEdit
|
||||
{label.title && <span className="text-xs font-medium truncate">{label.title}</span>}
|
||||
</div>
|
||||
|
||||
<Edit onClick={() => onEdit(label)} size={16} color="#0047FF" />
|
||||
<Edit onClick={() => onEdit(label)} size={16} color="#0047FF" className="shrink-0 cursor-pointer" />
|
||||
<TrashWithConfrim onDelete={() => onDelete(label.id)} isLoading={isDeleting} size={16} color="#FF0000" />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -10,7 +10,9 @@ type Props = {
|
||||
onViewChange: (view: LabelsView) => void;
|
||||
onToggle: (id: string) => void;
|
||||
onCreate: (title: string, color: string) => void;
|
||||
onDelete: (id: string) => void;
|
||||
isSubmitting?: boolean;
|
||||
isDeleting?: boolean;
|
||||
};
|
||||
|
||||
const TaskDetailLabelsPopover: FC<Props> = ({
|
||||
@@ -20,7 +22,9 @@ const TaskDetailLabelsPopover: FC<Props> = ({
|
||||
onViewChange,
|
||||
onToggle,
|
||||
onCreate,
|
||||
onDelete,
|
||||
isSubmitting = false,
|
||||
isDeleting = false,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -30,7 +34,9 @@ const TaskDetailLabelsPopover: FC<Props> = ({
|
||||
selectedIds={selectedIds}
|
||||
onToggle={onToggle}
|
||||
onEdit={() => undefined}
|
||||
onDelete={onDelete}
|
||||
onCreateClick={() => onViewChange("create")}
|
||||
isDeleting={isDeleting}
|
||||
/>
|
||||
) : (
|
||||
<TaskDetailNewLabelForm
|
||||
|
||||
@@ -119,6 +119,18 @@ export const useCreateTaskRemark = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteTaskRemark = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id }: { id: string; taskId: string }) => api.deleteTaskRemark(id),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["task-detail", variables.taskId] });
|
||||
queryClient.invalidateQueries({ queryKey: ["project"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateCheckListItem = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -64,6 +64,11 @@ export const createTaskRemark = async (params: CreateTaskRemarkType): Promise<Cr
|
||||
return data;
|
||||
};
|
||||
|
||||
export const deleteTaskRemark = async (id: string) => {
|
||||
const { data } = await axios.delete(`/task-manager/remarks/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createCheckListItem = async (params: CreateCheckListItemType): Promise<CreateCheckListItemResponse> => {
|
||||
const { data } = await axios.post(`/task-manager/check-list-items`, params);
|
||||
return data;
|
||||
|
||||
Reference in New Issue
Block a user