feat: deal card, deal status, deal new dates, indicator, hide navbar
This commit is contained in:
@@ -25,7 +25,6 @@ export type { BaseEnumListSchema } from './models/BaseEnumListSchema';
|
|||||||
export type { BaseEnumSchema } from './models/BaseEnumSchema';
|
export type { BaseEnumSchema } from './models/BaseEnumSchema';
|
||||||
export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema';
|
export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema';
|
||||||
export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema';
|
export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema';
|
||||||
export type { BillPaymentInfo } from './models/BillPaymentInfo';
|
|
||||||
export type { BillPaymentStatus } from './models/BillPaymentStatus';
|
export type { BillPaymentStatus } from './models/BillPaymentStatus';
|
||||||
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
|
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
|
||||||
export type { Body_upload_product_image } from './models/Body_upload_product_image';
|
export type { Body_upload_product_image } from './models/Body_upload_product_image';
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { BillPaymentInfo } from './BillPaymentInfo';
|
|
||||||
import type { BillPaymentStatus } from './BillPaymentStatus';
|
import type { BillPaymentStatus } from './BillPaymentStatus';
|
||||||
import type { NotificationChannel } from './NotificationChannel';
|
import type { NotificationChannel } from './NotificationChannel';
|
||||||
export type BillStatusUpdateRequest = {
|
export type BillStatusUpdateRequest = {
|
||||||
listenerTransactionId: number;
|
listenerTransactionId: number;
|
||||||
channel: NotificationChannel;
|
channel: NotificationChannel;
|
||||||
info: (BillPaymentInfo | BillPaymentStatus);
|
info: BillPaymentStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,5 +26,7 @@ export type DealSchema = {
|
|||||||
shippingWarehouse?: (ShippingWarehouseSchema | string | null);
|
shippingWarehouse?: (ShippingWarehouseSchema | string | null);
|
||||||
billRequest?: (DealBillRequestSchema | null);
|
billRequest?: (DealBillRequestSchema | null);
|
||||||
category?: (ServicePriceCategorySchema | null);
|
category?: (ServicePriceCategorySchema | null);
|
||||||
|
deliveryDate?: (string | null);
|
||||||
|
receivingSlotDate?: (string | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,5 +17,7 @@ export type DealSummary = {
|
|||||||
totalProducts: number;
|
totalProducts: number;
|
||||||
shipmentWarehouseId: (number | null);
|
shipmentWarehouseId: (number | null);
|
||||||
shipmentWarehouseName: (string | null);
|
shipmentWarehouseName: (string | null);
|
||||||
|
deliveryDate?: (string | null);
|
||||||
|
receivingSlotDate?: (string | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
/*background-color: green;*/
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
padding-bottom: rem(5);
|
padding-bottom: rem(5);
|
||||||
font-size: var(--mantine-font-size-sm);
|
font-size: var(--mantine-font-size-sm);
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
/*border-color: red;*/
|
||||||
@mixin light {
|
@mixin light {
|
||||||
background-color: var(--mantine-color-gray-1);
|
background-color: var(--mantine-color-gray-1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ import {
|
|||||||
CopyButton,
|
CopyButton,
|
||||||
Flex,
|
Flex,
|
||||||
Image,
|
Image,
|
||||||
|
Indicator,
|
||||||
|
IndicatorProps,
|
||||||
Popover,
|
Popover,
|
||||||
rem,
|
rem,
|
||||||
Text,
|
Text,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import classNames from "classnames";
|
|
||||||
import { useDealPageContext } from "../../../pages/LeadsPage/contexts/DealPageContext.tsx";
|
import { useDealPageContext } from "../../../pages/LeadsPage/contexts/DealPageContext.tsx";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faCheck } from "@fortawesome/free-solid-svg-icons";
|
import { faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { DealStatus } from "../../../shared/enums/DealStatus.ts";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dealSummary: DealSummary;
|
dealSummary: DealSummary;
|
||||||
@@ -28,120 +30,149 @@ const DealSummaryCard: FC<Props> = ({ dealSummary }) => {
|
|||||||
setSelectedDeal(deal);
|
setSelectedDeal(deal);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const getDeadlineTextColor = (deadline: string) => {
|
const getIndicatorProps = (): IndicatorProps => {
|
||||||
// generate three colors, yellow for 1 day, red for 0 days, green for more than 1 day
|
// check if deal status is less then in delivery and delivery_date - current date is less then 3 days, then show indicator
|
||||||
const deadlineDate = new Date(deadline);
|
// 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 currentDate = new Date();
|
||||||
const diff = deadlineDate.getTime() - currentDate.getTime();
|
const diff = deliveryDate.getTime() - currentDate.getTime();
|
||||||
const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
|
const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
|
||||||
if (diffDays < 0) return "red.8"; // for past deadlines
|
|
||||||
if (diffDays === 1) {
|
if (dealSummary.status < DealStatus.IN_DELIVERY) {
|
||||||
return "yellow.8";
|
// 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 { disabled: true };
|
||||||
return "orange.8";
|
|
||||||
}
|
|
||||||
return "green.8";
|
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<Indicator
|
||||||
onClick={() => onDealSummaryClick()}
|
position={"top-end"}
|
||||||
className={styles["container"]}>
|
withBorder
|
||||||
|
{...getIndicatorProps()}
|
||||||
|
>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
onClick={() => onDealSummaryClick()}
|
||||||
styles["flex-row"],
|
className={styles["container"]}>
|
||||||
styles["flex-row-left"]
|
<Flex direction={"column"} flex={1} gap={rem(3)}>
|
||||||
)}>
|
<Flex justify={"space-between"}>
|
||||||
<div className={styles["flex-item"]}>
|
|
||||||
<Text
|
<Text
|
||||||
size={"sm"}
|
c={"gray.6"}
|
||||||
c={"gray.6"}>
|
size={"xs"}
|
||||||
Клиент: {dealSummary.clientName}
|
|
||||||
</Text>
|
>
|
||||||
</div>
|
{dealSummary.clientName}
|
||||||
<div className={styles["flex-item"]}>
|
</Text>
|
||||||
<Text
|
</Flex>
|
||||||
size={"md"}
|
|
||||||
c={"blue.5"}>
|
<Text c={"blue.5"} size={"sm"}>
|
||||||
{dealSummary.name}
|
{dealSummary.name}
|
||||||
</Text>
|
</Text>
|
||||||
{dealSummary.shipmentWarehouseName && (
|
<Flex
|
||||||
|
// align={"center"}
|
||||||
|
direction={"column"}
|
||||||
|
justify={"space-between"}
|
||||||
|
>
|
||||||
<Text
|
<Text
|
||||||
size={"sm"}
|
size={"sm"}
|
||||||
c={"gray.6"}>
|
c={"gray.6"}>
|
||||||
{dealSummary.shipmentWarehouseName}
|
{dealSummary.shipmentWarehouseName || "Склад не указан"}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
<Text
|
||||||
</div>
|
c={"gray.6"}
|
||||||
<div className={styles["flex-item"]}>
|
size={"sm"}
|
||||||
<Text
|
>
|
||||||
size={"sm"}
|
{dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "}
|
||||||
c={"gray.6"}>
|
</Text>
|
||||||
{dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "}
|
<Text
|
||||||
{dealSummary.totalProducts.toLocaleString("ru-RU")} тов.
|
c={"gray.6"}
|
||||||
</Text>
|
size={"sm"}>
|
||||||
</div>
|
{dealSummary.totalProducts.toLocaleString("ru-RU")} тов.
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex direction={"column"}>
|
||||||
|
{dealSummary.deliveryDate && (
|
||||||
|
<Text
|
||||||
|
c={"gray.6"}
|
||||||
|
size={"sm"}>
|
||||||
|
Доставка: {(new Date(dealSummary.deliveryDate)).toLocaleDateString("ru-RU")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{dealSummary.receivingSlotDate && (
|
||||||
|
<Text
|
||||||
|
c={"gray.6"}
|
||||||
|
size={"sm"}>
|
||||||
|
Приемка: {(new Date(dealSummary.receivingSlotDate)).toLocaleDateString("ru-RU")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
<Flex align={"center"} justify={"space-between"}>
|
||||||
|
<CopyButton value={dealSummary.id.toString()}>
|
||||||
|
{({ copy, copied }) => (
|
||||||
|
<Popover
|
||||||
|
opened={copied}
|
||||||
|
withArrow>
|
||||||
|
<Popover.Target>
|
||||||
|
<div
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
copy();
|
||||||
|
}}
|
||||||
|
className={styles["flex-item"]}>
|
||||||
|
<Badge
|
||||||
|
variant={"light"}
|
||||||
|
radius={"sm"}>
|
||||||
|
ID: {dealSummary.id}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<Flex
|
||||||
|
justify={"center"}
|
||||||
|
align={"center"}
|
||||||
|
gap={rem(5)}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
bounce
|
||||||
|
style={{ animationIterationCount: 1 }}
|
||||||
|
icon={faCheck}
|
||||||
|
/>
|
||||||
|
<Text size={"xs"}>
|
||||||
|
ID сделки скопирован
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</CopyButton>
|
||||||
|
<ActionIcon variant={"transparent"}>
|
||||||
|
<Image
|
||||||
|
src={dealSummary.baseMarketplace?.iconUrl || ""}
|
||||||
|
/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
</Flex>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
</Indicator>
|
||||||
className={classNames(
|
|
||||||
styles["flex-row"],
|
|
||||||
styles["flex-row-right"]
|
|
||||||
)}>
|
|
||||||
<div className={styles["flex-item"]}>
|
|
||||||
<ActionIcon variant={"transparent"}>
|
|
||||||
<Image
|
|
||||||
src={dealSummary.baseMarketplace?.iconUrl || ""}
|
|
||||||
/>
|
|
||||||
</ActionIcon>
|
|
||||||
</div>
|
|
||||||
<div className={styles["flex-item"]}>
|
|
||||||
<Text
|
|
||||||
size={"sm"}
|
|
||||||
c={getDeadlineTextColor(dealSummary.deadline)}>
|
|
||||||
{new Date(dealSummary.deadline)
|
|
||||||
.toLocaleString("ru-RU")
|
|
||||||
.slice(0, -3)}
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
<CopyButton value={"https://google.com"}>
|
|
||||||
{({ copy, copied }) => (
|
|
||||||
<Popover
|
|
||||||
opened={copied}
|
|
||||||
withArrow>
|
|
||||||
<Popover.Target>
|
|
||||||
<div
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
copy();
|
|
||||||
}}
|
|
||||||
className={styles["flex-item"]}>
|
|
||||||
<Badge
|
|
||||||
variant={"light"}
|
|
||||||
radius={"sm"}>
|
|
||||||
ID: {dealSummary.id}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</Popover.Target>
|
|
||||||
<Popover.Dropdown>
|
|
||||||
<Flex
|
|
||||||
justify={"center"}
|
|
||||||
align={"center"}
|
|
||||||
gap={rem(5)}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
bounce
|
|
||||||
style={{ animationIterationCount: 1 }}
|
|
||||||
icon={faCheck}
|
|
||||||
/>
|
|
||||||
<Text size={"xs"}>
|
|
||||||
ID сделки скопирован
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Popover.Dropdown>
|
|
||||||
</Popover>
|
|
||||||
)}
|
|
||||||
</CopyButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default DealSummaryCard;
|
export default DealSummaryCard;
|
||||||
|
|||||||
10
src/components/HideNavbarAffix/HideNavbarAffix.module.css
Normal file
10
src/components/HideNavbarAffix/HideNavbarAffix.module.css
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/components/HideNavbarAffix/HideNavbarAffix.tsx
Normal file
52
src/components/HideNavbarAffix/HideNavbarAffix.tsx
Normal file
@@ -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> = (props) => {
|
||||||
|
const uiState = useSelector((state: RootState) => state.ui);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/*{uiState.hideNavbar && (*/}
|
||||||
|
<Transition
|
||||||
|
transition={"slide-right"}
|
||||||
|
mounted={uiState.hideNavbar}
|
||||||
|
>
|
||||||
|
{styles => (
|
||||||
|
<Affix
|
||||||
|
style={styles}
|
||||||
|
position={{
|
||||||
|
top: "50%",
|
||||||
|
left: rem(20),
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(setHideNavbar(false));
|
||||||
|
|
||||||
|
}}
|
||||||
|
className={stylesCss["icon"]}
|
||||||
|
// color={"#2d2d2d"}
|
||||||
|
radius="xl"
|
||||||
|
size={45}>
|
||||||
|
<IconArrowRight
|
||||||
|
stroke={1.5}
|
||||||
|
// size={30}
|
||||||
|
/>
|
||||||
|
</ActionIcon>
|
||||||
|
</Affix>)}
|
||||||
|
</Transition>
|
||||||
|
{/*)*/}
|
||||||
|
{/*}*/}
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HideNavbarAffix;
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
|
import { Center, Flex, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme } from "@mantine/core";
|
||||||
import {
|
import {
|
||||||
Center,
|
IconArrowLeft,
|
||||||
Flex,
|
|
||||||
Image,
|
|
||||||
rem,
|
|
||||||
Stack,
|
|
||||||
Tooltip,
|
|
||||||
UnstyledButton,
|
|
||||||
useMantineColorScheme,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import {
|
|
||||||
IconBarcode,
|
IconBarcode,
|
||||||
IconBox,
|
IconBox,
|
||||||
IconBuildingWarehouse,
|
IconBuildingWarehouse,
|
||||||
@@ -26,6 +18,7 @@ import classes from "./Navbar.module.css";
|
|||||||
import { useAppDispatch } from "../../redux/store.ts";
|
import { useAppDispatch } from "../../redux/store.ts";
|
||||||
import { logout } from "../../features/authSlice.ts";
|
import { logout } from "../../features/authSlice.ts";
|
||||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||||
|
import { setHideNavbar } from "../../features/uiSlice.ts";
|
||||||
|
|
||||||
interface NavbarLinkProps {
|
interface NavbarLinkProps {
|
||||||
icon: typeof IconHome2;
|
icon: typeof IconHome2;
|
||||||
@@ -118,6 +111,7 @@ export function Navbar() {
|
|||||||
};
|
};
|
||||||
const links = mockdata.map((link, index) => (
|
const links = mockdata.map((link, index) => (
|
||||||
<NavbarLink
|
<NavbarLink
|
||||||
|
|
||||||
{...link}
|
{...link}
|
||||||
index={index}
|
index={index}
|
||||||
key={link.label}
|
key={link.label}
|
||||||
@@ -130,7 +124,9 @@ export function Navbar() {
|
|||||||
<nav className={classes.navbar}>
|
<nav className={classes.navbar}>
|
||||||
<Flex
|
<Flex
|
||||||
direction={"column"}
|
direction={"column"}
|
||||||
|
align={"center"}
|
||||||
gap={rem(30)}>
|
gap={rem(30)}>
|
||||||
|
|
||||||
<Center p={rem(5)}>
|
<Center p={rem(5)}>
|
||||||
<Image
|
<Image
|
||||||
flex={1}
|
flex={1}
|
||||||
@@ -183,6 +179,15 @@ export function Navbar() {
|
|||||||
icon={IconLogout}
|
icon={IconLogout}
|
||||||
label="Выйти"
|
label="Выйти"
|
||||||
/>
|
/>
|
||||||
|
<NavbarLink
|
||||||
|
icon={IconArrowLeft}
|
||||||
|
href={"#"}
|
||||||
|
index={-1}
|
||||||
|
label={"Свернуть"}
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(setHideNavbar(true));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|||||||
|
|
||||||
interface UIState {
|
interface UIState {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
hideNavbar: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: UIState = {
|
const initialState: UIState = {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
hideNavbar: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uiSlice = createSlice({
|
const uiSlice = createSlice({
|
||||||
@@ -15,8 +17,11 @@ const uiSlice = createSlice({
|
|||||||
setIsLoading: (state, action: PayloadAction<boolean>) => {
|
setIsLoading: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isLoading = action.payload;
|
state.isLoading = action.payload;
|
||||||
},
|
},
|
||||||
|
setHideNavbar: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.hideNavbar = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default uiSlice.reducer;
|
export default uiSlice.reducer;
|
||||||
export const { setIsLoading } = uiSlice.actions;
|
export const { setIsLoading, setHideNavbar} = uiSlice.actions;
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ import { ButtonCopyControlled } from "../../../../../components/ButtonCopyContro
|
|||||||
import { useClipboard } from "@mantine/hooks";
|
import { useClipboard } from "@mantine/hooks";
|
||||||
import ButtonCopy from "../../../../../components/ButtonCopy/ButtonCopy.tsx";
|
import ButtonCopy from "../../../../../components/ButtonCopy/ButtonCopy.tsx";
|
||||||
import FileSaver from "file-saver";
|
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 { IconBarcode, IconPrinter } from "@tabler/icons-react";
|
||||||
import styles from "../../../ui/LeadsPage.module.css";
|
import styles from "../../../ui/LeadsPage.module.css";
|
||||||
import { base64ToBlob } from "../../../../../shared/lib/utils.ts";
|
import { base64ToBlob } from "../../../../../shared/lib/utils.ts";
|
||||||
|
import { DatePickerInput } from "@mantine/dates";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
deal: DealSchema;
|
deal: DealSchema;
|
||||||
@@ -41,7 +42,17 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
const queryClient = useQueryClient();
|
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<FormType>({
|
const form = useForm<FormType>({
|
||||||
initialValues: initialValues,
|
initialValues: initialValues,
|
||||||
validate: {
|
validate: {
|
||||||
@@ -85,7 +96,14 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
await updateClientInfo(values);
|
await updateClientInfo(values);
|
||||||
}
|
}
|
||||||
// updating deal info
|
// 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 = (
|
const isShippingWarehouse = (
|
||||||
value: ShippingWarehouseSchema | string | null | undefined,
|
value: ShippingWarehouseSchema | string | null | undefined,
|
||||||
@@ -171,6 +189,21 @@ const Content: FC<Props> = ({ deal }) => {
|
|||||||
).onChange(event);
|
).onChange(event);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<DatePickerInput
|
||||||
|
minDate={new Date()}
|
||||||
|
placeholder={"Укажите дату передачи в доставку"}
|
||||||
|
label={"Дата передачи в доставку"}
|
||||||
|
{...form.getInputProps("deliveryDate")}
|
||||||
|
|
||||||
|
/>
|
||||||
|
<DatePickerInput
|
||||||
|
minDate={new Date()}
|
||||||
|
|
||||||
|
placeholder={"Укажите слот приемки"}
|
||||||
|
label={"Слот приемки"}
|
||||||
|
{...form.getInputProps("receivingSlotDate")}
|
||||||
|
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
<Flex
|
<Flex
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
|||||||
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
|
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
|
||||||
import { DealPageContextProvider } from "../contexts/DealPageContext.tsx";
|
import { DealPageContextProvider } from "../contexts/DealPageContext.tsx";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
import { DealService, DealSummary, DealSummaryReorderRequest } from "../../../client";
|
import { DealService, DealSummaryReorderRequest } from "../../../client";
|
||||||
import { ActionIcon, Flex, NumberInput, rem, Text } from "@mantine/core";
|
import { ActionIcon, Flex, NumberInput, rem, Text } from "@mantine/core";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { notifications } from "../../../shared/lib/notifications.ts";
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
@@ -19,6 +19,7 @@ import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSe
|
|||||||
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
|
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
|
||||||
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
|
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
import { dateWithoutTimezone } from "../../../shared/lib/date.ts";
|
||||||
|
|
||||||
enum DisplayMode {
|
enum DisplayMode {
|
||||||
BOARD,
|
BOARD,
|
||||||
@@ -31,11 +32,11 @@ export const LeadsPage: FC = () => {
|
|||||||
const { summariesRaw, refetch } = useDealSummaries();
|
const { summariesRaw, refetch } = useDealSummaries();
|
||||||
const [summaries, setSummaries] = useState(summariesRaw);
|
const [summaries, setSummaries] = useState(summariesRaw);
|
||||||
const [displayMode, setDisplayMode] = useState<DisplayMode>(
|
const [displayMode, setDisplayMode] = useState<DisplayMode>(
|
||||||
DisplayMode.BOARD
|
DisplayMode.BOARD,
|
||||||
);
|
);
|
||||||
const [isDragEnded, setIsDragEnded] = useState(true);
|
const [isDragEnded, setIsDragEnded] = useState(true);
|
||||||
|
|
||||||
const [selectedDeals, setSelectedDeals] = useState<DealSummary[]>([]);
|
// const [selectedDeals, setSelectedDeals] = useState<DealSummary[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSummaries(summariesRaw);
|
setSummaries(summariesRaw);
|
||||||
@@ -129,19 +130,18 @@ export const LeadsPage: FC = () => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modals.openContextModal({
|
|
||||||
modal: "enterDeadline",
|
DealService.reorderDealSummaries({
|
||||||
title: "Необходимо указать дедлайн",
|
requestBody: {
|
||||||
innerProps: {
|
dealId: dealId,
|
||||||
onSubmit: event =>
|
status: status,
|
||||||
DealService.reorderDealSummaries({
|
index: result.destination.index,
|
||||||
requestBody: event,
|
comment: "",
|
||||||
}).then(async response => {
|
deadline: dateWithoutTimezone(new Date()),
|
||||||
setSummaries(response.summaries);
|
|
||||||
await refetch();
|
|
||||||
}),
|
|
||||||
request: request,
|
|
||||||
},
|
},
|
||||||
|
}).then(async response => {
|
||||||
|
setSummaries(response.summaries);
|
||||||
|
await refetch();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const getTableBody = () => {
|
const getTableBody = () => {
|
||||||
@@ -151,7 +151,9 @@ export const LeadsPage: FC = () => {
|
|||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 0.2 }}>
|
transition={{ duration: 0.2 }}>
|
||||||
<DealsTable items={data} onSelectionChange={setSelectedDeals} />
|
<DealsTable items={data}
|
||||||
|
// onSelectionChange={setSelectedDeals}
|
||||||
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -188,6 +190,15 @@ export const LeadsPage: FC = () => {
|
|||||||
droppableId={"AWAITING_ACCEPTANCE"}
|
droppableId={"AWAITING_ACCEPTANCE"}
|
||||||
color={"#4A90E2"}
|
color={"#4A90E2"}
|
||||||
/>
|
/>
|
||||||
|
<Board
|
||||||
|
summaries={summaries.filter(
|
||||||
|
summary =>
|
||||||
|
summary.status == DealStatus.READY_FOR_WORK,
|
||||||
|
)}
|
||||||
|
title={"Готов к работе"}
|
||||||
|
droppableId={"READY_FOR_WORK"}
|
||||||
|
color={"#D3D3D3"}
|
||||||
|
/>
|
||||||
<Board
|
<Board
|
||||||
summaries={summaries.filter(
|
summaries={summaries.filter(
|
||||||
summary =>
|
summary =>
|
||||||
@@ -207,6 +218,15 @@ export const LeadsPage: FC = () => {
|
|||||||
droppableId={"AWAITING_SHIPMENT"}
|
droppableId={"AWAITING_SHIPMENT"}
|
||||||
color={"#7ED321"}
|
color={"#7ED321"}
|
||||||
/>
|
/>
|
||||||
|
<Board
|
||||||
|
summaries={summaries.filter(
|
||||||
|
summary =>
|
||||||
|
summary.status == DealStatus.IN_DELIVERY,
|
||||||
|
)}
|
||||||
|
title={"В доставке"}
|
||||||
|
droppableId={"IN_DELIVERY"}
|
||||||
|
color={"#6A0DAD"}
|
||||||
|
/>
|
||||||
<Board
|
<Board
|
||||||
summaries={summaries.filter(
|
summaries={summaries.filter(
|
||||||
summary =>
|
summary =>
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import { useSelector } from "react-redux";
|
|||||||
import { RootState } from "../../redux/store.ts";
|
import { RootState } from "../../redux/store.ts";
|
||||||
import styles from "./PageWrapper.module.css";
|
import styles from "./PageWrapper.module.css";
|
||||||
import { Navbar } from "../../components/Navbar/Navbar.tsx";
|
import { Navbar } from "../../components/Navbar/Navbar.tsx";
|
||||||
|
import HideNavbarAffix from "../../components/HideNavbarAffix/HideNavbarAffix.tsx";
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
const PageWrapper: FC<Props> = ({ children }) => {
|
const PageWrapper: FC<Props> = ({ children }) => {
|
||||||
const authState = useSelector((state: RootState) => state.auth);
|
const authState = useSelector((state: RootState) => state.auth);
|
||||||
|
const uiState = useSelector((state: RootState) => state.ui);
|
||||||
return (
|
return (
|
||||||
<AppShell
|
<AppShell
|
||||||
layout={"alt"}
|
layout={"alt"}
|
||||||
@@ -17,10 +19,12 @@ const PageWrapper: FC<Props> = ({ children }) => {
|
|||||||
navbar={
|
navbar={
|
||||||
authState.isAuthorized && !authState.isGuest
|
authState.isAuthorized && !authState.isGuest
|
||||||
? {
|
? {
|
||||||
width: "130px",
|
width: "130px",
|
||||||
breakpoint: "sm",
|
breakpoint: "sm",
|
||||||
}
|
collapsed: { desktop: uiState.hideNavbar },
|
||||||
|
}
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
}>
|
}>
|
||||||
<AppShell.Navbar>
|
<AppShell.Navbar>
|
||||||
{authState.isAuthorized && !authState.isGuest && (
|
{authState.isAuthorized && !authState.isGuest && (
|
||||||
@@ -42,6 +46,7 @@ const PageWrapper: FC<Props> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
className={styles["main-container"]}>
|
className={styles["main-container"]}>
|
||||||
<div className={styles["container"]}>{children}</div>
|
<div className={styles["container"]}>{children}</div>
|
||||||
|
<HideNavbarAffix />
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
export enum DealStatus {
|
export enum DealStatus {
|
||||||
CREATED = 0,
|
CREATED = 0,
|
||||||
AWAITING_ACCEPTANCE = 1,
|
AWAITING_ACCEPTANCE = 1,
|
||||||
PACKAGING = 2,
|
READY_FOR_WORK = 2,
|
||||||
AWAITING_SHIPMENT = 3,
|
PACKAGING = 3,
|
||||||
AWAITING_PAYMENT = 4,
|
AWAITING_SHIPMENT = 4,
|
||||||
COMPLETED = 5,
|
IN_DELIVERY = 5,
|
||||||
CANCELLED = 6,
|
AWAITING_PAYMENT = 6,
|
||||||
|
COMPLETED = 7,
|
||||||
|
CANCELLED = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDealStatusByName = (name: string): DealStatus => {
|
export const getDealStatusByName = (name: string): DealStatus => {
|
||||||
@@ -14,8 +16,10 @@ export const getDealStatusByName = (name: string): DealStatus => {
|
|||||||
export const DealStatusDictionary = {
|
export const DealStatusDictionary = {
|
||||||
[DealStatus.CREATED]: "Создан",
|
[DealStatus.CREATED]: "Создан",
|
||||||
[DealStatus.AWAITING_ACCEPTANCE]: "Ожидает приемки",
|
[DealStatus.AWAITING_ACCEPTANCE]: "Ожидает приемки",
|
||||||
|
[DealStatus.READY_FOR_WORK]: "Готов к работе",
|
||||||
[DealStatus.PACKAGING]: "Упаковка",
|
[DealStatus.PACKAGING]: "Упаковка",
|
||||||
[DealStatus.AWAITING_SHIPMENT]: "Ожидает отгрузки",
|
[DealStatus.AWAITING_SHIPMENT]: "Ожидает отгрузки",
|
||||||
|
[DealStatus.IN_DELIVERY]: "В доставке",
|
||||||
[DealStatus.AWAITING_PAYMENT]: "Ожидает оплаты",
|
[DealStatus.AWAITING_PAYMENT]: "Ожидает оплаты",
|
||||||
[DealStatus.COMPLETED]: "Завершена",
|
[DealStatus.COMPLETED]: "Завершена",
|
||||||
[DealStatus.CANCELLED]: "Отменена",
|
[DealStatus.CANCELLED]: "Отменена",
|
||||||
@@ -25,7 +29,7 @@ export type DealStatusType = {
|
|||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
export const DealStatuses: DealStatusType[] = Object.entries(
|
export const DealStatuses: DealStatusType[] = Object.entries(
|
||||||
DealStatusDictionary
|
DealStatusDictionary,
|
||||||
).map(([key, value]) => {
|
).map(([key, value]) => {
|
||||||
return { id: parseInt(key), name: value };
|
return { id: parseInt(key), name: value };
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user