setting date
This commit is contained in:
@@ -3,12 +3,9 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { useGetUsers } from "../../../users/hooks/useUserData";
|
import { useGetUsers } from "../../../users/hooks/useUserData";
|
||||||
import TaskDetailAttachmentPopover from "./attachment/TaskDetailAttachmentPopover";
|
import TaskDetailAttachmentPopover from "./attachment/TaskDetailAttachmentPopover";
|
||||||
import { DEFAULT_DATE_RANGE } from "./date/constants";
|
|
||||||
import TaskDetailDatePopover from "./date/TaskDetailDatePopover";
|
import TaskDetailDatePopover from "./date/TaskDetailDatePopover";
|
||||||
|
import { parseApiDate, toApiDate } from "./date/utils";
|
||||||
import type DateObject from "react-date-object";
|
import type DateObject from "react-date-object";
|
||||||
import DateObjectClass from "react-date-object";
|
|
||||||
import persian from "react-date-object/calendars/persian";
|
|
||||||
import persian_fa from "react-date-object/locales/persian_fa";
|
|
||||||
import TaskDetailChecklistPopover from "./checklist/TaskDetailChecklistPopover";
|
import TaskDetailChecklistPopover from "./checklist/TaskDetailChecklistPopover";
|
||||||
import TaskDetailLabelsPopover from "./labels/TaskDetailLabelsPopover";
|
import TaskDetailLabelsPopover from "./labels/TaskDetailLabelsPopover";
|
||||||
import type { LabelsView, TaskLabel } from "./labels/types";
|
import type { LabelsView, TaskLabel } from "./labels/types";
|
||||||
@@ -28,12 +25,6 @@ type Props = {
|
|||||||
taskId: string;
|
taskId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseDate = (value?: string): DateObject | null => {
|
|
||||||
if (!value) return null;
|
|
||||||
const date = new DateObjectClass({ date: value, calendar: persian, locale: persian_fa });
|
|
||||||
return date.isValid ? date : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, taskId }) => {
|
const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, taskId }) => {
|
||||||
const { t } = useTranslation("global");
|
const { t } = useTranslation("global");
|
||||||
const createTaskRemark = useCreateTaskRemark();
|
const createTaskRemark = useCreateTaskRemark();
|
||||||
@@ -46,8 +37,8 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
|
|||||||
const [labels, setLabels] = useState<TaskLabel[]>([]);
|
const [labels, setLabels] = useState<TaskLabel[]>([]);
|
||||||
const [selectedLabelIds, setSelectedLabelIds] = useState<Set<string>>(new Set());
|
const [selectedLabelIds, setSelectedLabelIds] = useState<Set<string>>(new Set());
|
||||||
const [selectedUserIds, setSelectedUserIds] = useState<Set<string>>(new Set());
|
const [selectedUserIds, setSelectedUserIds] = useState<Set<string>>(new Set());
|
||||||
const [startDate, setStartDate] = useState<DateObject | null>(DEFAULT_DATE_RANGE.startDate);
|
const [startDate, setStartDate] = useState<DateObject | null>(null);
|
||||||
const [endDate, setEndDate] = useState<DateObject | null>(DEFAULT_DATE_RANGE.endDate);
|
const [endDate, setEndDate] = useState<DateObject | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!taskDetail) return;
|
if (!taskDetail) return;
|
||||||
@@ -68,10 +59,8 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
|
|||||||
setSelectedUserIds(new Set());
|
setSelectedUserIds(new Set());
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedStartDate = parseDate(taskDetail.startDate);
|
setStartDate(parseApiDate(taskDetail.startDate));
|
||||||
const parsedEndDate = parseDate(taskDetail.endDate);
|
setEndDate(parseApiDate(taskDetail.endDate));
|
||||||
if (parsedStartDate) setStartDate(parsedStartDate);
|
|
||||||
if (parsedEndDate) setEndDate(parsedEndDate);
|
|
||||||
}, [taskDetail]);
|
}, [taskDetail]);
|
||||||
|
|
||||||
const selectedLabels = labels.filter((label) => selectedLabelIds.has(label.id));
|
const selectedLabels = labels.filter((label) => selectedLabelIds.has(label.id));
|
||||||
@@ -252,9 +241,33 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDateSubmit = (data: { startDate: DateObject | null; endDate: DateObject | null }) => {
|
const handleDateSubmit = (data: { startDate: DateObject | null; endDate: DateObject | null }) => {
|
||||||
|
const effectiveTaskId = taskId || taskDetail?.id;
|
||||||
|
if (!effectiveTaskId) return;
|
||||||
|
|
||||||
|
const previousStartDate = startDate;
|
||||||
|
const previousEndDate = endDate;
|
||||||
|
const params = {
|
||||||
|
startDate: data.startDate ? toApiDate(data.startDate) : null,
|
||||||
|
endDate: data.endDate ? toApiDate(data.endDate) : null,
|
||||||
|
};
|
||||||
|
|
||||||
setStartDate(data.startDate);
|
setStartDate(data.startDate);
|
||||||
setEndDate(data.endDate);
|
setEndDate(data.endDate);
|
||||||
handleDateClose();
|
|
||||||
|
updateTask.mutate(
|
||||||
|
{ id: effectiveTaskId, params },
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
handleDateClose();
|
||||||
|
toast.success(t("success"));
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error.message[0]);
|
||||||
|
setStartDate(previousStartDate);
|
||||||
|
setEndDate(previousEndDate);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -306,7 +319,13 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
|
|||||||
}
|
}
|
||||||
datePopover={
|
datePopover={
|
||||||
isDateOpen ? (
|
isDateOpen ? (
|
||||||
<TaskDetailDatePopover onClose={handleDateClose} onSubmit={handleDateSubmit} />
|
<TaskDetailDatePopover
|
||||||
|
onClose={handleDateClose}
|
||||||
|
onSubmit={handleDateSubmit}
|
||||||
|
initialStartDate={startDate}
|
||||||
|
initialEndDate={endDate}
|
||||||
|
isSubmitting={updateTask.isPending}
|
||||||
|
/>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,14 +12,23 @@ import { formatDateLabel } from "./utils";
|
|||||||
type Props = {
|
type Props = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSubmit: (data: { startDate: DateObject | null; endDate: DateObject | null }) => void;
|
onSubmit: (data: { startDate: DateObject | null; endDate: DateObject | null }) => void;
|
||||||
|
initialStartDate?: DateObject | null;
|
||||||
|
initialEndDate?: DateObject | null;
|
||||||
|
isSubmitting?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TaskDetailDateForm: FC<Props> = ({ onClose, onSubmit }) => {
|
const TaskDetailDateForm: FC<Props> = ({
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
initialStartDate = null,
|
||||||
|
initialEndDate = null,
|
||||||
|
isSubmitting = false,
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation("global");
|
const { t } = useTranslation("global");
|
||||||
const [startDate, setStartDate] = useState<DateObject | null>(null);
|
const [startDate, setStartDate] = useState<DateObject | null>(initialStartDate);
|
||||||
const [endDate, setEndDate] = useState<DateObject | null>(null);
|
const [endDate, setEndDate] = useState<DateObject | null>(initialEndDate);
|
||||||
const [startEnabled, setStartEnabled] = useState(true);
|
const [startEnabled, setStartEnabled] = useState(Boolean(initialStartDate) || !initialEndDate);
|
||||||
const [endEnabled, setEndEnabled] = useState(false);
|
const [endEnabled, setEndEnabled] = useState(Boolean(initialEndDate));
|
||||||
const [activeField, setActiveField] = useState<TaskDateField>("start");
|
const [activeField, setActiveField] = useState<TaskDateField>("start");
|
||||||
|
|
||||||
const calendarValue = (() => {
|
const calendarValue = (() => {
|
||||||
@@ -135,10 +144,21 @@ const TaskDetailDateForm: FC<Props> = ({ onClose, onSubmit }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<Button type="button" onClick={onClose} className="h-8 bg-[#E8E4F0] text-[#292D32] rounded-xl text-xs w-[95px]">
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={onClose}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="h-8 bg-[#E8E4F0] text-[#292D32] rounded-xl text-xs w-[95px]"
|
||||||
|
>
|
||||||
{t("cancel")}
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" onClick={handleSubmit} disabled={!canSubmit} className="h-8 bg-[#292D32] text-white rounded-xl text-xs disabled:opacity-50 w-[95px]">
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
disabled={!canSubmit || isSubmitting}
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
className="h-8 bg-[#292D32] text-white rounded-xl text-xs disabled:opacity-50 w-[95px]"
|
||||||
|
>
|
||||||
{t("taskmanager.task_detail.insert")}
|
{t("taskmanager.task_detail.insert")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,10 +5,27 @@ import TaskDetailDateForm from "./TaskDetailDateForm";
|
|||||||
type Props = {
|
type Props = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSubmit: (data: { startDate: DateObject | null; endDate: DateObject | null }) => void;
|
onSubmit: (data: { startDate: DateObject | null; endDate: DateObject | null }) => void;
|
||||||
|
initialStartDate?: DateObject | null;
|
||||||
|
initialEndDate?: DateObject | null;
|
||||||
|
isSubmitting?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TaskDetailDatePopover: FC<Props> = ({ onClose, onSubmit }) => {
|
const TaskDetailDatePopover: FC<Props> = ({
|
||||||
return <TaskDetailDateForm onClose={onClose} onSubmit={onSubmit} />;
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
initialStartDate,
|
||||||
|
initialEndDate,
|
||||||
|
isSubmitting,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<TaskDetailDateForm
|
||||||
|
onClose={onClose}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
initialStartDate={initialStartDate}
|
||||||
|
initialEndDate={initialEndDate}
|
||||||
|
isSubmitting={isSubmitting}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TaskDetailDatePopover;
|
export default TaskDetailDatePopover;
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
import DateObject from "react-date-object";
|
import DateObject from "react-date-object";
|
||||||
|
import gregorian from "react-date-object/calendars/gregorian";
|
||||||
import persian from "react-date-object/calendars/persian";
|
import persian from "react-date-object/calendars/persian";
|
||||||
|
import gregorian_en from "react-date-object/locales/gregorian_en";
|
||||||
import persian_fa from "react-date-object/locales/persian_fa";
|
import persian_fa from "react-date-object/locales/persian_fa";
|
||||||
|
|
||||||
|
export const parseApiDate = (value?: string | null): DateObject | null => {
|
||||||
|
if (!value) return null;
|
||||||
|
const date = new DateObject({ date: value, calendar: gregorian });
|
||||||
|
return date.isValid ? date : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toApiDate = (date: DateObject): string => {
|
||||||
|
return date.convert(gregorian, gregorian_en).format("YYYY-MM-DD");
|
||||||
|
};
|
||||||
|
|
||||||
export const formatDateLabel = (date: DateObject | null) => {
|
export const formatDateLabel = (date: DateObject | null) => {
|
||||||
if (!date) return "";
|
if (!date) return "";
|
||||||
return date.convert(persian, persian_fa).format("D MMMM");
|
return date.convert(persian, persian_fa).format("D MMMM");
|
||||||
|
|||||||
Reference in New Issue
Block a user