From e782c49e4bc1cb987089be0dc77807ca37960b53 Mon Sep 17 00:00:00 2001 From: Dilmurod Date: Wed, 20 Aug 2025 17:49:38 +0500 Subject: [PATCH] Shift and Cycle & Co-Driver modal Info --- .vscode/launch.json | 13 + src/API/LayoutApi/statistic.ts | 7 +- src/Components/Companies/CompaniesEdit.tsx | 17 +- .../Tasks/ShiftAndCoDriverModal.tsx | 232 ++++++++++++++++++ src/Components/Tasks/TaskModal.tsx | 95 +++++-- src/Components/Tasks/shiftInfoTab.tsx | 141 +++++++++++ src/Hooks/Statistics/index.ts | 16 +- src/types/Statistic/TStat.ts | 11 - src/types/Tasks/TTasks.ts | 2 + 9 files changed, 482 insertions(+), 52 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/Components/Tasks/ShiftAndCoDriverModal.tsx create mode 100644 src/Components/Tasks/shiftInfoTab.tsx diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7619c1b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug React in Chrome", + "type": "pwa-chrome", + "request": "launch", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}/src/", + "breakOnLoad": true + } + ] +} diff --git a/src/API/LayoutApi/statistic.ts b/src/API/LayoutApi/statistic.ts index afcd06d..c04e12b 100644 --- a/src/API/LayoutApi/statistic.ts +++ b/src/API/LayoutApi/statistic.ts @@ -1,9 +1,8 @@ import { TCard, TGeneralChartData, + TGeneralChartGetParams, TStat, - TStatCreators, - TStatTeam, TteamChartData, } from "../../types/Statistic/TStat"; import instance from "../api"; @@ -37,10 +36,6 @@ export type TteamChartGetParams = { start_date?: string; end_date?: string; }; -export type TGeneralChartGetParams = { - start_date?: string; - end_date?: string; -}; export const statController = { async read(filterObject: TStatGetParams) { diff --git a/src/Components/Companies/CompaniesEdit.tsx b/src/Components/Companies/CompaniesEdit.tsx index 9a487c1..91fb976 100644 --- a/src/Components/Companies/CompaniesEdit.tsx +++ b/src/Components/Companies/CompaniesEdit.tsx @@ -16,6 +16,7 @@ import { RadioChangeEvent, Select, theme, + Switch, } from "antd"; import { companyController } from "../../API/LayoutApi/companies"; import { @@ -157,7 +158,7 @@ const CompanyEdit = () => { onFinish={onSubmit} autoComplete="off" > - + { /> - - { + + + + + + + void; + onCancel: () => void; + recordTask?: TTask; +} + +const ShiftAndCoDriverModal: React.FC = ({ + open, + onOk, + onCancel, + recordTask, +}) => { + const [form] = Form.useForm(); + const [needsCycle, setNeedsCycle] = useState(false); + const [needsDriver, setNeedsDriver] = useState(false); + const [needsPickUp, setNeedsPickUp] = useState(false); + + const handleOk = async () => { + try { + const values = await form.validateFields(); + + const formattedValues = Object.fromEntries( + Object.entries(values).map(([key, value]) => [ + key, + dayjs.isDayjs(value) ? value.format("YYYY-MM-DD HH:mm:ss") : value, + ]) + ); + + onOk(formattedValues); + form.resetFields(); + } catch (err) { + console.log("Validation error:", err); + } + }; + + return ( + +
+ {/* SHIFT INFO */} + + + + + + + + + {/* Pick Up */} + + + { + setNeedsPickUp(checked); + if (!checked) { + form.resetFields(["pickup_date", "cycle_location"]); + } + }} + /> + + + {needsPickUp && ( + <> + + + + + + + + + )} + + {/* Cycle info */} + + + { + setNeedsCycle(checked); + if (!checked) { + form.resetFields(["cycle_date", "cycle_location"]); + } + }} + /> + + + {needsCycle && ( + <> + + + + + + + + + )} + + {/* DRIVER INFO */} + + + { + setNeedsDriver(checked); + if (!checked) { + form.resetFields([ + "driver_name", + "co_driver_name", + "co_driver_pickup_date", + "co_driver_pickup_location", + "co_driver_drop_date", + "co_driver_drop_location", + ]); + } + }} + /> + + + {needsDriver && ( + <> + + + + + + + + + + + + + + + + + + + + + + + + )} +
+
+ ); +}; + +export default ShiftAndCoDriverModal; diff --git a/src/Components/Tasks/TaskModal.tsx b/src/Components/Tasks/TaskModal.tsx index 1d18816..e24c1e7 100644 --- a/src/Components/Tasks/TaskModal.tsx +++ b/src/Components/Tasks/TaskModal.tsx @@ -21,6 +21,8 @@ import { ArrowRightOutlined, CaretRightOutlined, CloseOutlined, + CopyOutlined, + DatabaseOutlined, EditOutlined, ForwardOutlined, RotateRightOutlined, @@ -62,6 +64,9 @@ import driverIcon from "../../assets/drivericon.png"; // @ts-ignore import userIcon from "../../assets/userIcon.png"; +import ShiftAndCoDriverModal from "./ShiftAndCoDriverModal"; +import CopyCard from "./shiftInfoTab"; + const TaskModal = ({ modalOpen, setModalOpen, @@ -87,6 +92,8 @@ const TaskModal = ({ const [teamName, setTeamName] = useState(recordTask?.assigned_to?.name); const { data, isLoading } = useTaskHistory(recordTask?.id); + const [isModalOpen, setIsModalOpen] = useState(false); + const { token } = theme.useToken(); const handleCancel = () => { @@ -171,7 +178,27 @@ const TaskModal = ({ { key: "3", label:

Done

, - onClick: () => statuspatch("Done"), + onClick: () => { + if (status === "Done") { + statuspatch("Done"); + return; + } + + if ( + recordTask?.service.title === "Break" || + recordTask?.service.title === "PTI" + ) { + statuspatch("Done"); + return; + } + + if (recordTask?.company?.needs_extra_info === false) { + statuspatch("Done"); + return; + } + + setIsModalOpen(true); + }, }, ]; const getImageSource = (source: string) => { @@ -185,9 +212,20 @@ const TaskModal = ({ } }; - const statuspatch = (status: string) => { + const statuspatch = async (status: string, extraData?: any) => { setStatus(status); - taskController.taskPatch({ status: status }, recordTask?.id); + try { + const response = await taskController.taskPatch( + { status: status, ...extraData }, + recordTask?.id + ); + + if (response.status === 400) { + setIsModalOpen(true); + } + } catch (error) { + console.log(error); + } }; const teampatch = (item: TTeam) => { @@ -395,21 +433,9 @@ const TaskModal = ({

{pti === false ? "Do" : "No need"}

- {/* */} + + + + + + + Shift & Driver Data + + } + key="2" + > + + + @@ -477,7 +522,7 @@ const TaskModal = ({ Attachments } - key="2" + key="3" >

} - key="3" + key="4" >

+ + { + statuspatch("Done", values); + setIsModalOpen(false); + }} + onCancel={() => setIsModalOpen(false)} + /> ); }; diff --git a/src/Components/Tasks/shiftInfoTab.tsx b/src/Components/Tasks/shiftInfoTab.tsx new file mode 100644 index 0000000..2f18206 --- /dev/null +++ b/src/Components/Tasks/shiftInfoTab.tsx @@ -0,0 +1,141 @@ +import { Button, Card, message } from "antd"; +import { CopyOutlined } from "@ant-design/icons"; +import dayjs from "dayjs"; + +interface CopyCardProps { + recordTask?: any; +} + +const CopyCard: React.FC = ({ recordTask }) => { + const formatDateTime = (date?: string) => + date ? dayjs(date).format("DD MMM YYYY HH:mm:ss") : "—"; + + const shiftInfo = { + pickUpDate: formatDateTime(recordTask?.pickup_date) || "—", + pickUpLocation: recordTask?.pickup_location || "—", + shiftDate: formatDateTime(recordTask?.shift_date) || "—", + shiftLocation: recordTask?.shift_location || "—", + cycleDate: formatDateTime(recordTask?.cycle_date) || "—", + cycleLocation: recordTask?.cycle_location || "—", + }; + + const coDriverInfo = { + driverName: recordTask?.driver_name || "—", + coDriverName: recordTask?.co_driver_name || "—", + coDriverPickUpLocation: recordTask?.co_driver_pickup_location || "—", + coDriverPickUpDate: + formatDateTime(recordTask?.co_driver_pickup_date) || "—", + coDriverDropLocation: recordTask?.co_driver_drop_location || "—", + coDriverDropDate: formatDateTime(recordTask?.co_driver_drop_date) || "—", + }; + + const handleCopy = (text: string) => { + navigator.clipboard + .writeText(text) + .then(() => message.success("Data copied successfully!")) + .catch(() => message.error("Failed to copy!")); + }; + + const text = ` +SHIFT INFO +Shift Date: ${shiftInfo.shiftDate} +Shift Location: ${shiftInfo.shiftLocation} + +Pick up Date: ${shiftInfo.pickUpDate} +Pick Up Location: ${shiftInfo.pickUpLocation} + +Cycle Date: ${shiftInfo.cycleDate} +Cycle Location: ${shiftInfo.cycleLocation} + + +CO DRIVER INFO +Driver's name: ${coDriverInfo.driverName} +Co-Driver's name: ${coDriverInfo.coDriverName} + +Co-driver pickup date: ${coDriverInfo.coDriverPickUpDate} +Co-driver pickup location: ${coDriverInfo.coDriverPickUpLocation} + +Co-driver drop date: ${coDriverInfo.coDriverDropDate} +Co-driver drop location: ${coDriverInfo.coDriverDropLocation} + `.trim(); + + const textRu = ` +ИНФОРМАЦИЯ О СМЕНЕ +Дата пикапа: ${shiftInfo.pickUpDate} +Место пикапа: ${shiftInfo.pickUpLocation} + +Дата шифта: ${shiftInfo.shiftDate} +Место шифта: ${shiftInfo.shiftLocation} + +Дата сайкла: ${shiftInfo.cycleDate} +Место сайкла: ${shiftInfo.cycleLocation} + +ИНФОРМАЦИЯ О СО-ВОДИТЕЛЕ +Имя драйвера: ${coDriverInfo.driverName} +Имя ко-драйвера: ${coDriverInfo.coDriverName} + +Время пикапа ко-драйвера: ${coDriverInfo.coDriverPickUpDate} +Место пикапа ко-драйвера: ${coDriverInfo.coDriverPickUpLocation} + +Время высадки ко-драйвера: ${coDriverInfo.coDriverDropDate} +Место высадки ко-драйвера: ${coDriverInfo.coDriverDropLocation} + `.trim(); + + return ( + <> + } onClick={() => handleCopy(text)}> + Copy + + } + style={{ width: "100%", marginBottom: 20 }} + > +

SHIFT INFO

+

Shift Date: {shiftInfo.shiftDate}

+

Shift Location: {shiftInfo.shiftLocation}

+

Pick up Date: {shiftInfo.pickUpDate}

+

Pick Up Location: {shiftInfo.pickUpLocation}

+

Cycle Date: {shiftInfo.cycleDate}

+

Cycle Location: {shiftInfo.cycleLocation}

+ +

CO-DRIVER INFO

+

Driver's name: {coDriverInfo.driverName}

+

Co-driver's name: {coDriverInfo.coDriverName}

+

Co-driver pickup date: {coDriverInfo.coDriverPickUpDate}

+

Co-driver pickup location: {coDriverInfo.coDriverPickUpLocation}

+

Co-driver drop date: {coDriverInfo.coDriverDropDate}

+

Co-driver drop location: {coDriverInfo.coDriverDropLocation}

+
+ + } onClick={() => handleCopy(textRu)}> + Copy + + } + style={{ width: "100%" }} + > +

ИНФОРМАЦИЯ О СМЕНЕ

+

Дата шифта: {shiftInfo.shiftDate}

+

Место шифта: {shiftInfo.shiftLocation}

+

Дата сайкла: {shiftInfo.cycleDate}

+

Место сайкла: {shiftInfo.cycleLocation}

+

Дата пикапа: {shiftInfo.pickUpDate}

+

Место пикапа: {shiftInfo.pickUpLocation}

+ +

ИНФОРМАЦИЯ О СО-ВОДИТЕЛЕ

+

Имя драйвера: {coDriverInfo.driverName}

+

Имя ко-драйвера: {coDriverInfo.coDriverName}

+

Место пикапа ко-драйвера: {coDriverInfo.coDriverPickUpLocation}

+

Время пикапа ко-драйвера {coDriverInfo.coDriverPickUpDate}

+

Место высадки ко-драйвера:{coDriverInfo.coDriverDropLocation}

+

Время высадки ко-драйвера: {coDriverInfo.coDriverDropDate}

+
+ + ); +}; + +export default CopyCard; diff --git a/src/Hooks/Statistics/index.ts b/src/Hooks/Statistics/index.ts index 03b1428..d452d0f 100644 --- a/src/Hooks/Statistics/index.ts +++ b/src/Hooks/Statistics/index.ts @@ -1,11 +1,13 @@ import { - TGeneralChartGetParams, TStatCreatorsGetParams, TteamChartGetParams, } from "./../../API/LayoutApi/statistic"; import { useQuery } from "react-query"; import { TStatGetParams, statController } from "../../API/LayoutApi/statistic"; -import { TGeneralChartData } from "../../types/Statistic/TStat"; +import { + TGeneralChartData, + TGeneralChartGetParams, +} from "../../types/Statistic/TStat"; export const useStatsData = ({ search, @@ -85,16 +87,6 @@ export const useTeamChartData = ({ { refetchOnWindowFocus: false } ); }; -// export const useGeneralChartData = ({ -// start_date, -// end_date, -// }: TGeneralChartGetParams) => { -// return useQuery( -// [`stats/general-stats/`, start_date, end_date], -// () => statController.generalChart({ start_date, end_date }), -// { refetchOnWindowFocus: false } -// ); -// }; export const useCardData = ({ start_date, end_date }: TStatGetParams) => { return useQuery( diff --git a/src/types/Statistic/TStat.ts b/src/types/Statistic/TStat.ts index 4a4febf..0aec969 100644 --- a/src/types/Statistic/TStat.ts +++ b/src/types/Statistic/TStat.ts @@ -29,17 +29,6 @@ export type TteamChartData = { [category: string]: TaskPerformance | string; }; -export type TGeneralDailyStat = { - task_date: string; - total_tasks: number; - completed_tasks: number; - incomplete_tasks: number; -}; - -export type TGeneralWrapper = { - daily_stats: TGeneralDailyStat[]; -}; - export type TCard = { all_tasks: number; active_tasks: number; diff --git a/src/types/Tasks/TTasks.ts b/src/types/Tasks/TTasks.ts index 1f32b70..7bd03e6 100644 --- a/src/types/Tasks/TTasks.ts +++ b/src/types/Tasks/TTasks.ts @@ -17,6 +17,7 @@ export type TTask = { in_charge: InCharge; forwarded_from: { id: number; name: string }; attachment_set?: TAttachment[]; + needs_extra_info: boolean; }; export type TAttachment = { @@ -40,6 +41,7 @@ export interface Company { id: number; name: string; source: string; + needs_extra_info?: boolean; } export interface Service {