feat: deal card, deal status, deal new dates, indicator, hide navbar
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/*background-color: green;*/
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<Props> = ({ 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 (
|
||||
<div
|
||||
onClick={() => onDealSummaryClick()}
|
||||
className={styles["container"]}>
|
||||
<Indicator
|
||||
position={"top-end"}
|
||||
withBorder
|
||||
{...getIndicatorProps()}
|
||||
>
|
||||
|
||||
<div
|
||||
className={classNames(
|
||||
styles["flex-row"],
|
||||
styles["flex-row-left"]
|
||||
)}>
|
||||
<div className={styles["flex-item"]}>
|
||||
<Text
|
||||
size={"sm"}
|
||||
c={"gray.6"}>
|
||||
Клиент: {dealSummary.clientName}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={styles["flex-item"]}>
|
||||
<Text
|
||||
size={"md"}
|
||||
c={"blue.5"}>
|
||||
onClick={() => onDealSummaryClick()}
|
||||
className={styles["container"]}>
|
||||
<Flex direction={"column"} flex={1} gap={rem(3)}>
|
||||
<Flex justify={"space-between"}>
|
||||
|
||||
<Text
|
||||
c={"gray.6"}
|
||||
size={"xs"}
|
||||
|
||||
>
|
||||
{dealSummary.clientName}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Text c={"blue.5"} size={"sm"}>
|
||||
{dealSummary.name}
|
||||
</Text>
|
||||
{dealSummary.shipmentWarehouseName && (
|
||||
<Flex
|
||||
// align={"center"}
|
||||
direction={"column"}
|
||||
justify={"space-between"}
|
||||
>
|
||||
<Text
|
||||
size={"sm"}
|
||||
c={"gray.6"}>
|
||||
{dealSummary.shipmentWarehouseName}
|
||||
{dealSummary.shipmentWarehouseName || "Склад не указан"}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles["flex-item"]}>
|
||||
<Text
|
||||
size={"sm"}
|
||||
c={"gray.6"}>
|
||||
{dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "}
|
||||
{dealSummary.totalProducts.toLocaleString("ru-RU")} тов.
|
||||
</Text>
|
||||
</div>
|
||||
<Text
|
||||
c={"gray.6"}
|
||||
size={"sm"}
|
||||
>
|
||||
{dealSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "}
|
||||
</Text>
|
||||
<Text
|
||||
c={"gray.6"}
|
||||
size={"sm"}>
|
||||
{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
|
||||
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>
|
||||
</Indicator>
|
||||
|
||||
);
|
||||
};
|
||||
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 {
|
||||
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) => (
|
||||
<NavbarLink
|
||||
|
||||
{...link}
|
||||
index={index}
|
||||
key={link.label}
|
||||
@@ -130,7 +124,9 @@ export function Navbar() {
|
||||
<nav className={classes.navbar}>
|
||||
<Flex
|
||||
direction={"column"}
|
||||
align={"center"}
|
||||
gap={rem(30)}>
|
||||
|
||||
<Center p={rem(5)}>
|
||||
<Image
|
||||
flex={1}
|
||||
@@ -183,6 +179,15 @@ export function Navbar() {
|
||||
icon={IconLogout}
|
||||
label="Выйти"
|
||||
/>
|
||||
<NavbarLink
|
||||
icon={IconArrowLeft}
|
||||
href={"#"}
|
||||
index={-1}
|
||||
label={"Свернуть"}
|
||||
onClick={() => {
|
||||
dispatch(setHideNavbar(true));
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</nav>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user