setting date

This commit is contained in:
hamid zarghami
2026-07-12 12:15:55 +03:30
parent c11c972c7c
commit e1fe030316
4 changed files with 95 additions and 27 deletions
@@ -3,12 +3,9 @@ import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";
import { useGetUsers } from "../../../users/hooks/useUserData";
import TaskDetailAttachmentPopover from "./attachment/TaskDetailAttachmentPopover";
import { DEFAULT_DATE_RANGE } from "./date/constants";
import TaskDetailDatePopover from "./date/TaskDetailDatePopover";
import { parseApiDate, toApiDate } from "./date/utils";
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 TaskDetailLabelsPopover from "./labels/TaskDetailLabelsPopover";
import type { LabelsView, TaskLabel } from "./labels/types";
@@ -28,12 +25,6 @@ type Props = {
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 { t } = useTranslation("global");
const createTaskRemark = useCreateTaskRemark();
@@ -46,8 +37,8 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
const [labels, setLabels] = useState<TaskLabel[]>([]);
const [selectedLabelIds, setSelectedLabelIds] = useState<Set<string>>(new Set());
const [selectedUserIds, setSelectedUserIds] = useState<Set<string>>(new Set());
const [startDate, setStartDate] = useState<DateObject | null>(DEFAULT_DATE_RANGE.startDate);
const [endDate, setEndDate] = useState<DateObject | null>(DEFAULT_DATE_RANGE.endDate);
const [startDate, setStartDate] = useState<DateObject | null>(null);
const [endDate, setEndDate] = useState<DateObject | null>(null);
useEffect(() => {
if (!taskDetail) return;
@@ -68,10 +59,8 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
setSelectedUserIds(new Set());
}
const parsedStartDate = parseDate(taskDetail.startDate);
const parsedEndDate = parseDate(taskDetail.endDate);
if (parsedStartDate) setStartDate(parsedStartDate);
if (parsedEndDate) setEndDate(parsedEndDate);
setStartDate(parseApiDate(taskDetail.startDate));
setEndDate(parseApiDate(taskDetail.endDate));
}, [taskDetail]);
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 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);
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 (
@@ -306,7 +319,13 @@ const TaskDetailToolbar: FC<Props> = ({ activeTab, onTabChange, taskDetail, task
}
datePopover={
isDateOpen ? (
<TaskDetailDatePopover onClose={handleDateClose} onSubmit={handleDateSubmit} />
<TaskDetailDatePopover
onClose={handleDateClose}
onSubmit={handleDateSubmit}
initialStartDate={startDate}
initialEndDate={endDate}
isSubmitting={updateTask.isPending}
/>
) : null
}
/>
@@ -12,14 +12,23 @@ import { formatDateLabel } from "./utils";
type Props = {
onClose: () => 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 [startDate, setStartDate] = useState<DateObject | null>(null);
const [endDate, setEndDate] = useState<DateObject | null>(null);
const [startEnabled, setStartEnabled] = useState(true);
const [endEnabled, setEndEnabled] = useState(false);
const [startDate, setStartDate] = useState<DateObject | null>(initialStartDate);
const [endDate, setEndDate] = useState<DateObject | null>(initialEndDate);
const [startEnabled, setStartEnabled] = useState(Boolean(initialStartDate) || !initialEndDate);
const [endEnabled, setEndEnabled] = useState(Boolean(initialEndDate));
const [activeField, setActiveField] = useState<TaskDateField>("start");
const calendarValue = (() => {
@@ -135,10 +144,21 @@ const TaskDetailDateForm: FC<Props> = ({ onClose, onSubmit }) => {
</div>
<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")}
</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")}
</Button>
</div>
@@ -5,10 +5,27 @@ import TaskDetailDateForm from "./TaskDetailDateForm";
type Props = {
onClose: () => void;
onSubmit: (data: { startDate: DateObject | null; endDate: DateObject | null }) => void;
initialStartDate?: DateObject | null;
initialEndDate?: DateObject | null;
isSubmitting?: boolean;
};
const TaskDetailDatePopover: FC<Props> = ({ onClose, onSubmit }) => {
return <TaskDetailDateForm onClose={onClose} onSubmit={onSubmit} />;
const TaskDetailDatePopover: FC<Props> = ({
onClose,
onSubmit,
initialStartDate,
initialEndDate,
isSubmitting,
}) => {
return (
<TaskDetailDateForm
onClose={onClose}
onSubmit={onSubmit}
initialStartDate={initialStartDate}
initialEndDate={initialEndDate}
isSubmitting={isSubmitting}
/>
);
};
export default TaskDetailDatePopover;
@@ -1,7 +1,19 @@
import DateObject from "react-date-object";
import gregorian from "react-date-object/calendars/gregorian";
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";
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) => {
if (!date) return "";
return date.convert(persian, persian_fa).format("D MMMM");