diff --git a/src/client/index.ts b/src/client/index.ts index d0066ea..1072fbc 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -25,7 +25,6 @@ export type { BaseEnumListSchema } from './models/BaseEnumListSchema'; export type { BaseEnumSchema } from './models/BaseEnumSchema'; export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema'; export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema'; -export type { BillPaymentInfo } from './models/BillPaymentInfo'; export type { BillPaymentStatus } from './models/BillPaymentStatus'; export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest'; export type { Body_upload_product_image } from './models/Body_upload_product_image'; diff --git a/src/client/models/BillStatusUpdateRequest.ts b/src/client/models/BillStatusUpdateRequest.ts index e1249f4..e1a5848 100644 --- a/src/client/models/BillStatusUpdateRequest.ts +++ b/src/client/models/BillStatusUpdateRequest.ts @@ -2,12 +2,11 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { BillPaymentInfo } from './BillPaymentInfo'; import type { BillPaymentStatus } from './BillPaymentStatus'; import type { NotificationChannel } from './NotificationChannel'; export type BillStatusUpdateRequest = { listenerTransactionId: number; channel: NotificationChannel; - info: (BillPaymentInfo | BillPaymentStatus); + info: BillPaymentStatus; }; diff --git a/src/client/models/DealSchema.ts b/src/client/models/DealSchema.ts index c8145f4..da00c3c 100644 --- a/src/client/models/DealSchema.ts +++ b/src/client/models/DealSchema.ts @@ -26,5 +26,7 @@ export type DealSchema = { shippingWarehouse?: (ShippingWarehouseSchema | string | null); billRequest?: (DealBillRequestSchema | null); category?: (ServicePriceCategorySchema | null); + deliveryDate?: (string | null); + receivingSlotDate?: (string | null); }; diff --git a/src/client/models/DealSummary.ts b/src/client/models/DealSummary.ts index f046e69..3f6c781 100644 --- a/src/client/models/DealSummary.ts +++ b/src/client/models/DealSummary.ts @@ -17,5 +17,7 @@ export type DealSummary = { totalProducts: number; shipmentWarehouseId: (number | null); shipmentWarehouseName: (string | null); + deliveryDate?: (string | null); + receivingSlotDate?: (string | null); }; diff --git a/src/components/Dnd/Board/Board.module.css b/src/components/Dnd/Board/Board.module.css index e7de44a..e4a3bef 100644 --- a/src/components/Dnd/Board/Board.module.css +++ b/src/components/Dnd/Board/Board.module.css @@ -2,7 +2,7 @@ width: 100%; display: flex; flex-direction: column; - /*background-color: green;*/ + flex: 1; } .header { diff --git a/src/components/Dnd/DealSummaryCard/DealSummaryCard.module.css b/src/components/Dnd/DealSummaryCard/DealSummaryCard.module.css index fbbd3f6..65b35ca 100644 --- a/src/components/Dnd/DealSummaryCard/DealSummaryCard.module.css +++ b/src/components/Dnd/DealSummaryCard/DealSummaryCard.module.css @@ -12,7 +12,7 @@ padding-bottom: rem(5); font-size: var(--mantine-font-size-sm); flex-wrap: wrap; - + /*border-color: red;*/ @mixin light { background-color: var(--mantine-color-gray-1); } diff --git a/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx b/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx index 4d35368..3eaf97e 100644 --- a/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx +++ b/src/components/Dnd/DealSummaryCard/DealSummaryCard.tsx @@ -8,14 +8,16 @@ import { CopyButton, Flex, Image, + Indicator, + IndicatorProps, Popover, rem, Text, } from "@mantine/core"; -import classNames from "classnames"; import { useDealPageContext } from "../../../pages/LeadsPage/contexts/DealPageContext.tsx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheck } from "@fortawesome/free-solid-svg-icons"; +import { DealStatus } from "../../../shared/enums/DealStatus.ts"; type Props = { dealSummary: DealSummary; @@ -28,120 +30,149 @@ const DealSummaryCard: FC = ({ dealSummary }) => { setSelectedDeal(deal); }); }; - const getDeadlineTextColor = (deadline: string) => { - // generate three colors, yellow for 1 day, red for 0 days, green for more than 1 day - const deadlineDate = new Date(deadline); + const getIndicatorProps = (): IndicatorProps => { + // check if deal status is less then in delivery and delivery_date - current date is less then 3 days, then show indicator + // and if current status is less then in_delivery, then show indicator + if (!dealSummary.deliveryDate) + return { disabled: true }; + + const deliveryDate = new Date(dealSummary.deliveryDate); const currentDate = new Date(); - const diff = deadlineDate.getTime() - currentDate.getTime(); + const diff = deliveryDate.getTime() - currentDate.getTime(); const diffDays = Math.ceil(diff / (1000 * 3600 * 24)); - if (diffDays < 0) return "red.8"; // for past deadlines - if (diffDays === 1) { - return "yellow.8"; + + if (dealSummary.status < DealStatus.IN_DELIVERY) { + // if less than 2 days left, show yellow, if less than 1 day left, show red + if (diffDays <= 2 && diffDays > 1) { + return { + color: "yellow", + }; + } + if (diffDays <= 1) { + return { + color: "red", + }; + } + + return { + disabled: false, + }; } - if (diffDays === 0) { - return "orange.8"; - } - return "green.8"; + return { disabled: true }; }; return ( -
onDealSummaryClick()} - className={styles["container"]}> + +
-
- - Клиент: {dealSummary.clientName} - -
-
- + onClick={() => onDealSummaryClick()} + className={styles["container"]}> + + + + + {dealSummary.clientName} + + + + {dealSummary.name} - {dealSummary.shipmentWarehouseName && ( + - {dealSummary.shipmentWarehouseName} + {dealSummary.shipmentWarehouseName || "Склад не указан"} - )} -
-
- - {dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "} - {dealSummary.totalProducts.toLocaleString("ru-RU")} тов. - -
+ + {dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "} + + + {dealSummary.totalProducts.toLocaleString("ru-RU")} тов. + + + + {dealSummary.deliveryDate && ( + + Доставка: {(new Date(dealSummary.deliveryDate)).toLocaleDateString("ru-RU")} + + )} + {dealSummary.receivingSlotDate && ( + + Приемка: {(new Date(dealSummary.receivingSlotDate)).toLocaleDateString("ru-RU")} + + )} + + + + {({ copy, copied }) => ( + + +
{ + e.stopPropagation(); + copy(); + }} + className={styles["flex-item"]}> + + ID: {dealSummary.id} + +
+
+ + + + + ID сделки скопирован + + + +
+ )} +
+ + + +
+ + +
-
-
- - - -
-
- - {new Date(dealSummary.deadline) - .toLocaleString("ru-RU") - .slice(0, -3)} - -
- - {({ copy, copied }) => ( - - -
{ - e.stopPropagation(); - copy(); - }} - className={styles["flex-item"]}> - - ID: {dealSummary.id} - -
-
- - - - - ID сделки скопирован - - - -
- )} -
-
-
+ + ); }; export default DealSummaryCard; diff --git a/src/components/HideNavbarAffix/HideNavbarAffix.module.css b/src/components/HideNavbarAffix/HideNavbarAffix.module.css new file mode 100644 index 0000000..ab8dc97 --- /dev/null +++ b/src/components/HideNavbarAffix/HideNavbarAffix.module.css @@ -0,0 +1,10 @@ +.icon { + box-shadow: 3px 3px 20px 2px rgb(0 0 0 / 8%), -3px -3px 20px 2px rgba(0, 0, 0, 0.08); + @mixin dark { + background-color: #454545; + } + @mixin light { + background-color: #f1f6f8; + color: var(--mantine-color-dark-3); + } +} \ No newline at end of file diff --git a/src/components/HideNavbarAffix/HideNavbarAffix.tsx b/src/components/HideNavbarAffix/HideNavbarAffix.tsx new file mode 100644 index 0000000..7daf3a7 --- /dev/null +++ b/src/components/HideNavbarAffix/HideNavbarAffix.tsx @@ -0,0 +1,52 @@ +import { ActionIcon, Affix, AffixProps, rem, Transition } from "@mantine/core"; +import { IconArrowRight } from "@tabler/icons-react"; +import { useSelector } from "react-redux"; +import { RootState, useAppDispatch } from "../../redux/store.ts"; +import { setHideNavbar } from "../../features/uiSlice.ts"; +import { FC } from "react"; +import stylesCss from "./HideNavbarAffix.module.css"; + +type Props = AffixProps; +const HideNavbarAffix: FC = (props) => { + const uiState = useSelector((state: RootState) => state.ui); + const dispatch = useAppDispatch(); + return ( + <> + {/*{uiState.hideNavbar && (*/} + + {styles => ( + + { + dispatch(setHideNavbar(false)); + + }} + className={stylesCss["icon"]} + // color={"#2d2d2d"} + radius="xl" + size={45}> + + + )} + + {/*)*/} + {/*}*/} + + + ); +}; + +export default HideNavbarAffix; \ No newline at end of file diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index 348d9d6..dc4ecbf 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,14 +1,6 @@ +import { Center, Flex, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme } from "@mantine/core"; import { - Center, - Flex, - Image, - rem, - Stack, - Tooltip, - UnstyledButton, - useMantineColorScheme, -} from "@mantine/core"; -import { + IconArrowLeft, IconBarcode, IconBox, IconBuildingWarehouse, @@ -26,6 +18,7 @@ import classes from "./Navbar.module.css"; import { useAppDispatch } from "../../redux/store.ts"; import { logout } from "../../features/authSlice.ts"; import { useNavigate, useRouterState } from "@tanstack/react-router"; +import { setHideNavbar } from "../../features/uiSlice.ts"; interface NavbarLinkProps { icon: typeof IconHome2; @@ -118,6 +111,7 @@ export function Navbar() { }; const links = mockdata.map((link, index) => ( +
+ { + dispatch(setHideNavbar(true)); + }} + /> ); diff --git a/src/features/uiSlice.ts b/src/features/uiSlice.ts index 2615839..3547088 100644 --- a/src/features/uiSlice.ts +++ b/src/features/uiSlice.ts @@ -2,10 +2,12 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; interface UIState { isLoading: boolean; + hideNavbar: boolean; } const initialState: UIState = { isLoading: false, + hideNavbar: false, }; const uiSlice = createSlice({ @@ -15,8 +17,11 @@ const uiSlice = createSlice({ setIsLoading: (state, action: PayloadAction) => { state.isLoading = action.payload; }, + setHideNavbar: (state, action: PayloadAction) => { + state.hideNavbar = action.payload; + }, }, }); export default uiSlice.reducer; -export const { setIsLoading } = uiSlice.actions; +export const { setIsLoading, setHideNavbar} = uiSlice.actions; diff --git a/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx b/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx index 64a8a40..0598782 100644 --- a/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx +++ b/src/pages/LeadsPage/drawers/DealEditDrawer/tabs/DealEditDrawerGeneralTab.tsx @@ -25,10 +25,11 @@ import { ButtonCopyControlled } from "../../../../../components/ButtonCopyContro import { useClipboard } from "@mantine/hooks"; import ButtonCopy from "../../../../../components/ButtonCopy/ButtonCopy.tsx"; import FileSaver from "file-saver"; -import { getCurrentDateTimeForFilename } from "../../../../../shared/lib/date.ts"; +import { dateWithoutTimezone, getCurrentDateTimeForFilename } from "../../../../../shared/lib/date.ts"; import { IconBarcode, IconPrinter } from "@tabler/icons-react"; import styles from "../../../ui/LeadsPage.module.css"; import { base64ToBlob } from "../../../../../shared/lib/utils.ts"; +import { DatePickerInput } from "@mantine/dates"; type Props = { deal: DealSchema; @@ -41,7 +42,17 @@ const Content: FC = ({ deal }) => { const clipboard = useClipboard(); const queryClient = useQueryClient(); - const initialValues: FormType = deal; + // ignore typescript + + const initialValues: FormType = { + ...deal, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + deliveryDate: deal.deliveryDate ? new Date(deal.deliveryDate) : null, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + receivingSlotDate: deal.receivingSlotDate ? new Date(deal.receivingSlotDate) : null, + }; const form = useForm({ initialValues: initialValues, validate: { @@ -85,7 +96,14 @@ const Content: FC = ({ deal }) => { await updateClientInfo(values); } // updating deal info - await updateDealInfo(values); + // get shimpent warehouse name from object if its object, otherwise just pass it + const shippingWarehouse = isShippingWarehouse(values.shippingWarehouse) ? values.shippingWarehouse.name : values.shippingWarehouse; + await updateDealInfo({ + ...values, + deliveryDate: values.deliveryDate ? dateWithoutTimezone(new Date(values.deliveryDate)) : null, + receivingSlotDate: values.receivingSlotDate ? dateWithoutTimezone(new Date(values.receivingSlotDate)) : null, + shippingWarehouse: shippingWarehouse, + }); }; const isShippingWarehouse = ( value: ShippingWarehouseSchema | string | null | undefined, @@ -171,6 +189,21 @@ const Content: FC = ({ deal }) => { ).onChange(event); }} /> + + { const { summariesRaw, refetch } = useDealSummaries(); const [summaries, setSummaries] = useState(summariesRaw); const [displayMode, setDisplayMode] = useState( - DisplayMode.BOARD + DisplayMode.BOARD, ); const [isDragEnded, setIsDragEnded] = useState(true); - const [selectedDeals, setSelectedDeals] = useState([]); + // const [selectedDeals, setSelectedDeals] = useState([]); useEffect(() => { setSummaries(summariesRaw); @@ -129,19 +130,18 @@ export const LeadsPage: FC = () => { }); return; } - modals.openContextModal({ - modal: "enterDeadline", - title: "Необходимо указать дедлайн", - innerProps: { - onSubmit: event => - DealService.reorderDealSummaries({ - requestBody: event, - }).then(async response => { - setSummaries(response.summaries); - await refetch(); - }), - request: request, + + DealService.reorderDealSummaries({ + requestBody: { + dealId: dealId, + status: status, + index: result.destination.index, + comment: "", + deadline: dateWithoutTimezone(new Date()), }, + }).then(async response => { + setSummaries(response.summaries); + await refetch(); }); }; const getTableBody = () => { @@ -151,7 +151,9 @@ export const LeadsPage: FC = () => { initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.2 }}> - + ); }; @@ -188,6 +190,15 @@ export const LeadsPage: FC = () => { droppableId={"AWAITING_ACCEPTANCE"} color={"#4A90E2"} /> + + summary.status == DealStatus.READY_FOR_WORK, + )} + title={"Готов к работе"} + droppableId={"READY_FOR_WORK"} + color={"#D3D3D3"} + /> @@ -207,6 +218,15 @@ export const LeadsPage: FC = () => { droppableId={"AWAITING_SHIPMENT"} color={"#7ED321"} /> + + summary.status == DealStatus.IN_DELIVERY, + )} + title={"В доставке"} + droppableId={"IN_DELIVERY"} + color={"#6A0DAD"} + /> diff --git a/src/pages/PageWrapper/PageWrapper.tsx b/src/pages/PageWrapper/PageWrapper.tsx index 82b7e57..ef6c51b 100644 --- a/src/pages/PageWrapper/PageWrapper.tsx +++ b/src/pages/PageWrapper/PageWrapper.tsx @@ -4,12 +4,14 @@ import { useSelector } from "react-redux"; import { RootState } from "../../redux/store.ts"; import styles from "./PageWrapper.module.css"; import { Navbar } from "../../components/Navbar/Navbar.tsx"; +import HideNavbarAffix from "../../components/HideNavbarAffix/HideNavbarAffix.tsx"; export type Props = { children: ReactNode; }; const PageWrapper: FC = ({ children }) => { const authState = useSelector((state: RootState) => state.auth); + const uiState = useSelector((state: RootState) => state.ui); return ( = ({ children }) => { navbar={ authState.isAuthorized && !authState.isGuest ? { - width: "130px", - breakpoint: "sm", - } + width: "130px", + breakpoint: "sm", + collapsed: { desktop: uiState.hideNavbar }, + } : undefined + }> {authState.isAuthorized && !authState.isGuest && ( @@ -42,6 +46,7 @@ const PageWrapper: FC = ({ children }) => { } className={styles["main-container"]}>
{children}
+
); diff --git a/src/shared/enums/DealStatus.ts b/src/shared/enums/DealStatus.ts index 89e75d4..e79dcd7 100644 --- a/src/shared/enums/DealStatus.ts +++ b/src/shared/enums/DealStatus.ts @@ -1,11 +1,13 @@ export enum DealStatus { CREATED = 0, AWAITING_ACCEPTANCE = 1, - PACKAGING = 2, - AWAITING_SHIPMENT = 3, - AWAITING_PAYMENT = 4, - COMPLETED = 5, - CANCELLED = 6, + READY_FOR_WORK = 2, + PACKAGING = 3, + AWAITING_SHIPMENT = 4, + IN_DELIVERY = 5, + AWAITING_PAYMENT = 6, + COMPLETED = 7, + CANCELLED = 8, } export const getDealStatusByName = (name: string): DealStatus => { @@ -14,8 +16,10 @@ export const getDealStatusByName = (name: string): DealStatus => { export const DealStatusDictionary = { [DealStatus.CREATED]: "Создан", [DealStatus.AWAITING_ACCEPTANCE]: "Ожидает приемки", + [DealStatus.READY_FOR_WORK]: "Готов к работе", [DealStatus.PACKAGING]: "Упаковка", [DealStatus.AWAITING_SHIPMENT]: "Ожидает отгрузки", + [DealStatus.IN_DELIVERY]: "В доставке", [DealStatus.AWAITING_PAYMENT]: "Ожидает оплаты", [DealStatus.COMPLETED]: "Завершена", [DealStatus.CANCELLED]: "Отменена", @@ -25,7 +29,7 @@ export type DealStatusType = { name: string; }; export const DealStatuses: DealStatusType[] = Object.entries( - DealStatusDictionary + DealStatusDictionary, ).map(([key, value]) => { return { id: parseInt(key), name: value }; });