feat: cards, attributes and modules
This commit is contained in:
31
src/components/CardAttributeFields/CardAttributeFields.tsx
Normal file
31
src/components/CardAttributeFields/CardAttributeFields.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ProjectSchema } from "../../client";
|
||||
import { UseFormReturnType } from "@mantine/form";
|
||||
import { rem, Stack } from "@mantine/core";
|
||||
import { ReactNode } from "react";
|
||||
import CardAttributeField from "./components/CardAttributeField.tsx";
|
||||
import { CardGeneralFormType } from "../../pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx";
|
||||
|
||||
type Props = {
|
||||
project: ProjectSchema;
|
||||
form: UseFormReturnType<CardGeneralFormType>;
|
||||
}
|
||||
|
||||
const CardAttributeFields = ({ project, form }: Props) => {
|
||||
const fields: ReactNode[] = project.attributes.map(attribute => {
|
||||
return (
|
||||
<CardAttributeField
|
||||
key={attribute.id}
|
||||
attribute={attribute}
|
||||
form={form}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Stack gap={rem(10)}>
|
||||
{...fields}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardAttributeFields;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { AttributeSchema } from "../../../client";
|
||||
import { Checkbox, Tooltip } from "@mantine/core";
|
||||
import { UseFormReturnType } from "@mantine/form";
|
||||
import { ReactNode } from "react";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { CardGeneralFormType } from "../../../pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx";
|
||||
|
||||
type Props = {
|
||||
attribute: AttributeSchema;
|
||||
form: UseFormReturnType<CardGeneralFormType>;
|
||||
}
|
||||
|
||||
const CardAttributeField = ({ attribute, form }: Props) => {
|
||||
const tooltipLabel = attribute.isApplicableToGroup
|
||||
? "Применяется ко всей группе"
|
||||
: "Применяется только к данной сделке";
|
||||
|
||||
let component: ReactNode;
|
||||
|
||||
if (attribute.type.type === "bool") {
|
||||
component = (
|
||||
<Checkbox
|
||||
label={attribute.label}
|
||||
{...form.getInputProps(attribute.name, { type: "checkbox" })}
|
||||
// defaultChecked={form.getInputProps(attribute.name, { type: "checkbox"}).value}
|
||||
/>
|
||||
);
|
||||
}
|
||||
else if (attribute.type.type === "datetime" || attribute.type.type === "date") {
|
||||
console.log("value = ");
|
||||
console.log(form.getInputProps(attribute.name).value);
|
||||
console.log("type = ")
|
||||
console.log(typeof form.getInputProps(attribute.name).value);
|
||||
|
||||
component = (
|
||||
<DatePickerInput
|
||||
label={attribute.label}
|
||||
{...form.getInputProps(attribute.name)}
|
||||
clearable={attribute.isNullable}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltipLabel}>
|
||||
{component}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardAttributeField;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { CardSchema } from "../../../client";
|
||||
|
||||
const DATETIME_TYPES = ["datetime", "date"];
|
||||
|
||||
const getAttributesFromCard = (card: CardSchema) => {
|
||||
console.log("getAttributesFromCard");
|
||||
|
||||
return card.attributes.reduce(
|
||||
(values, cardAttribute) => {
|
||||
let value: boolean | number | string | null | Date = cardAttribute.value;
|
||||
console.log(cardAttribute.attribute.type.type);
|
||||
if (DATETIME_TYPES.includes(cardAttribute.attribute.type.type) && value !== null) {
|
||||
value = new Date(value as string);
|
||||
}
|
||||
|
||||
console.log("cardAttrValue", value);
|
||||
console.log("value type", typeof value);
|
||||
return {
|
||||
...values,
|
||||
[cardAttribute.attribute.name]: value,
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
};
|
||||
|
||||
export default getAttributesFromCard;
|
||||
@@ -13,7 +13,7 @@ type Props = OtherProps & SelectProps;
|
||||
const DealStatusSelect: FC<Props> = ({ board, ...props}) => {
|
||||
const [isInitial, setIsInitial] = useState<boolean>(true);
|
||||
|
||||
const filteredData = board?.dealStatuses.filter(
|
||||
const filteredData = board?.statuses.filter(
|
||||
status => !status.isDeleted,
|
||||
) ?? [];
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { Box, Center, Group, Stack } from "@mantine/core";
|
||||
import { DragDropContext } from "@hello-pangea/dnd";
|
||||
import { BoardSchema, DealSummary, ProjectSchema } from "../../../../client";
|
||||
import { BoardSchema, CardSummary } from "../../../../client";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import useBoards from "./hooks/useBoards.tsx";
|
||||
import Statuses from "../../Statuses/Statuses/Statuses.tsx";
|
||||
import Board from "../Board/Board.tsx";
|
||||
import useBoardsDnd from "./hooks/useBoardsDnd.tsx";
|
||||
import PrefillDealsWithExcelDrawer
|
||||
from "../../../../pages/DealsPage/drawers/PrefillDealWithExcelDrawer/PrefillDealsWithExcelDrawer.tsx";
|
||||
import PrefillCardsWithExcelDrawer
|
||||
from "../../../../pages/CardsPage/drawers/PrefillCardWithExcelDrawer/PrefillCardsWithExcelDrawer.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
project: ProjectSchema | null;
|
||||
summariesRaw: DealSummary[];
|
||||
summariesRaw: CardSummary[];
|
||||
refetchSummaries: () => void;
|
||||
boards: BoardSchema[];
|
||||
refetchBoards: () => void;
|
||||
@@ -82,7 +81,7 @@ const Boards = (props: Props) => {
|
||||
selectedBoard={selectedBoard}
|
||||
{...props}
|
||||
/>
|
||||
<PrefillDealsWithExcelDrawer board={selectedBoard}/>
|
||||
<PrefillCardsWithExcelDrawer board={selectedBoard} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { BoardSchema, BoardService, ProjectSchema } from "../../../../../client";
|
||||
import { BoardSchema, BoardService } from "../../../../../client";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||
|
||||
type Props = {
|
||||
boards: BoardSchema[];
|
||||
refetchBoards: () => void;
|
||||
project: ProjectSchema | null;
|
||||
}
|
||||
|
||||
const useBoards = ({ boards, refetchBoards, project }: Props) => {
|
||||
const useBoards = ({ boards, refetchBoards }: Props) => {
|
||||
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(null);
|
||||
const { selectedProject: project } = useCardPageContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (boards.length > 0 && selectedBoard === null) {
|
||||
|
||||
@@ -32,11 +32,11 @@ const useBoardsDnd = ({ boards, refetchBoards }: Props) => {
|
||||
// If there is no changes
|
||||
if (!result.destination || result.destination == result.source) return;
|
||||
|
||||
// Checking for valid dealId
|
||||
// Checking for valid cardId
|
||||
const boardId = parseInt(result.draggableId);
|
||||
if (isNaN(boardId)) return;
|
||||
|
||||
// Checking for valid deal
|
||||
// Checking for valid card
|
||||
const board = boards.find(board => board.id == boardId);
|
||||
if (!board) return;
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import { DealGroupSchema, DealGroupService, DealSummary } from "../../../../client";
|
||||
import { CardGroupSchema, CardGroupService, CardSummary } from "../../../../client";
|
||||
import { FC, useEffect, useMemo, useState } from "react";
|
||||
import DealSummaryCard from "../DealSummaryCard/DealSummaryCard.tsx";
|
||||
import CardSummaryItem from "../CardSummaryItem/CardSummaryItem.tsx";
|
||||
import { Flex, rem, Text, TextInput, useMantineColorScheme } from "@mantine/core";
|
||||
import { IconGripHorizontal } from "@tabler/icons-react";
|
||||
import { useDebouncedValue } from "@mantine/hooks";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
|
||||
type Props = {
|
||||
deals: DealSummary[];
|
||||
group: DealGroupSchema;
|
||||
cards: CardSummary[];
|
||||
group: CardGroupSchema;
|
||||
}
|
||||
|
||||
export const DealGroupView: FC<Props> = ({ deals, group }) => {
|
||||
export const CardGroupView: FC<Props> = ({ cards, group }) => {
|
||||
const theme = useMantineColorScheme();
|
||||
const [name, setName] = useState<string>(group.name || "");
|
||||
const [debouncedName] = useDebouncedValue(name, 200);
|
||||
const totalPrice = useMemo(() => deals.reduce((acc, deal) => acc + deal.totalPrice, 0), [deals]);
|
||||
const totalProducts = useMemo(() => deals.reduce((acc, deal) => acc + deal.totalProducts, 0), [deals]);
|
||||
const totalPrice = useMemo(() => cards.reduce((acc, card) => acc + card.totalPrice, 0), [cards]);
|
||||
const totalProducts = useMemo(() => cards.reduce((acc, card) => acc + card.totalProducts, 0), [cards]);
|
||||
const updateName = () => {
|
||||
if (debouncedName === group.name) return;
|
||||
DealGroupService.updateDealGroup({
|
||||
CardGroupService.updateCardGroup({
|
||||
requestBody: {
|
||||
data: {
|
||||
...group,
|
||||
@@ -62,11 +62,11 @@ export const DealGroupView: FC<Props> = ({ deals, group }) => {
|
||||
<IconGripHorizontal />
|
||||
</Flex>
|
||||
<Flex direction={"column"} gap={rem(10)}>
|
||||
{deals.map(deal => (
|
||||
<DealSummaryCard
|
||||
{cards.map(card => (
|
||||
<CardSummaryItem
|
||||
color={theme.colorScheme === "dark" ? "var(--mantine-color-dark-6)" : "var(--mantine-color-gray-2)"}
|
||||
key={deal.id}
|
||||
dealSummary={deal}
|
||||
key={card.id}
|
||||
cardSummary={card}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
@@ -1,54 +1,57 @@
|
||||
import { FC } from "react";
|
||||
import { DealService, DealSummary } from "../../../../client";
|
||||
import styles from "./DealSummaryCard.module.css";
|
||||
import { CardService, CardSummary } from "../../../../client";
|
||||
import styles from "./CardSummaryItem.module.css";
|
||||
|
||||
import { ActionIcon, Badge, CopyButton, Flex, Image, Popover, rem, Text, ThemeIcon, Tooltip } from "@mantine/core";
|
||||
import { useDealPageContext } from "../../../../pages/DealsPage/contexts/DealPageContext.tsx";
|
||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { IconCheck, IconLayoutGridRemove, IconTrash } from "@tabler/icons-react";
|
||||
import { useContextMenu } from "mantine-contextmenu";
|
||||
import useDealSummaryState from "./useDealSummaryState.tsx";
|
||||
import useCardSummaryState from "./useCardSummaryState.tsx";
|
||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||
|
||||
type Props = {
|
||||
dealSummary: DealSummary;
|
||||
cardSummary: CardSummary;
|
||||
color?: string
|
||||
};
|
||||
|
||||
const DealSummaryCard: FC<Props> = ({ dealSummary, color }) => {
|
||||
const CardSummaryItem: FC<Props> = ({ cardSummary, color }) => {
|
||||
const { showContextMenu } = useContextMenu();
|
||||
const { setSelectedDeal } = useDealPageContext();
|
||||
const { onDelete, onComplete, onDeleteFromGroup } = useDealSummaryState();
|
||||
const { selectedProject, setSelectedCard } = useCardPageContext();
|
||||
const { onDelete, onComplete, onDeleteFromGroup } = useCardSummaryState();
|
||||
|
||||
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||
|
||||
const onDealSummaryClick = () => {
|
||||
DealService.getDealById({ dealId: dealSummary.id }).then(deal => {
|
||||
setSelectedDeal(deal);
|
||||
CardService.getCardById({ cardId: cardSummary.id }).then(card => {
|
||||
setSelectedCard(card);
|
||||
});
|
||||
};
|
||||
const isPaid = () => {
|
||||
return dealSummary.billRequest?.paid || dealSummary.group?.billRequest?.paid;
|
||||
return cardSummary.billRequest?.paid || cardSummary.group?.billRequest?.paid;
|
||||
};
|
||||
const isLockedInsideGroup = () => {
|
||||
return dealSummary.group && !dealSummary.group.billRequest;
|
||||
return cardSummary.group && !cardSummary.group.billRequest;
|
||||
};
|
||||
return (
|
||||
<div
|
||||
onContextMenu={showContextMenu([
|
||||
...isLockedInsideGroup() ? [{
|
||||
key: "removeFromGroup",
|
||||
onClick: () => onDeleteFromGroup(dealSummary),
|
||||
onClick: () => onDeleteFromGroup(cardSummary),
|
||||
title: "Убрать из группы",
|
||||
icon: <IconLayoutGridRemove />,
|
||||
}] : [],
|
||||
{
|
||||
key: "complete",
|
||||
onClick: () => onComplete(dealSummary),
|
||||
onClick: () => onComplete(cardSummary),
|
||||
title: "Завершить",
|
||||
icon: <IconCheck />,
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
onClick: () => onDelete(dealSummary),
|
||||
onClick: () => onDelete(cardSummary),
|
||||
title: "Удалить",
|
||||
icon: <IconTrash />,
|
||||
},
|
||||
@@ -65,56 +68,36 @@ const DealSummaryCard: FC<Props> = ({ dealSummary, color }) => {
|
||||
size={"xs"}
|
||||
|
||||
>
|
||||
{dealSummary.clientName}
|
||||
{cardSummary.clientName}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Text
|
||||
c={"blue.5"}
|
||||
size={"sm"}>
|
||||
{dealSummary.name}
|
||||
{cardSummary.name}
|
||||
</Text>
|
||||
<Flex
|
||||
// align={"center"}
|
||||
direction={"column"}
|
||||
justify={"space-between"}
|
||||
>
|
||||
<Text
|
||||
size={"sm"}
|
||||
c={"gray.6"}>
|
||||
{dealSummary.shipmentWarehouseName || "Склад не указан"}
|
||||
</Text>
|
||||
<Text
|
||||
c={"gray.6"}
|
||||
size={"sm"}
|
||||
{isServicesAndProductsIncluded && (
|
||||
<Flex
|
||||
direction={"column"}
|
||||
justify={"space-between"}
|
||||
>
|
||||
{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={"blue.5"}
|
||||
size={"sm"}>
|
||||
Доставка: {(new Date(dealSummary.deliveryDate)).toLocaleDateString("ru-RU")}
|
||||
c={"gray.6"}
|
||||
size={"sm"}
|
||||
>
|
||||
{cardSummary.totalPrice.toLocaleString("ru-RU")} руб,{" "}
|
||||
</Text>
|
||||
)}
|
||||
{dealSummary.receivingSlotDate && (
|
||||
<Text
|
||||
c={"blue.5"}
|
||||
c={"gray.6"}
|
||||
size={"sm"}>
|
||||
Слот: {(new Date(dealSummary.receivingSlotDate)).toLocaleDateString("ru-RU")}
|
||||
{cardSummary.totalProducts.toLocaleString("ru-RU")} тов.
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
<Flex align={"center"} justify={"space-between"}>
|
||||
<Flex align={"center"} gap={rem(5)}>
|
||||
<CopyButton value={dealSummary.id.toString()}>
|
||||
<CopyButton value={cardSummary.id.toString()}>
|
||||
{({ copy, copied }) => (
|
||||
<Popover
|
||||
opened={copied}
|
||||
@@ -129,7 +112,7 @@ const DealSummaryCard: FC<Props> = ({ dealSummary, color }) => {
|
||||
<Badge
|
||||
variant={"light"}
|
||||
radius={"sm"}>
|
||||
ID: {dealSummary.id}
|
||||
ID: {cardSummary.id}
|
||||
</Badge>
|
||||
</div>
|
||||
</Popover.Target>
|
||||
@@ -162,7 +145,7 @@ const DealSummaryCard: FC<Props> = ({ dealSummary, color }) => {
|
||||
|
||||
<ActionIcon variant={"transparent"}>
|
||||
<Image
|
||||
src={dealSummary.baseMarketplace?.iconUrl || ""}
|
||||
src={cardSummary.baseMarketplace?.iconUrl || ""}
|
||||
/>
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
@@ -170,4 +153,4 @@ const DealSummaryCard: FC<Props> = ({ dealSummary, color }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default DealSummaryCard;
|
||||
export default CardSummaryItem;
|
||||
@@ -1,91 +1,88 @@
|
||||
import { DealGroupService, DealService, DealSummary } from "../../../../client";
|
||||
import { useDealPageContext } from "../../../../pages/DealsPage/contexts/DealPageContext.tsx";
|
||||
import { CardGroupService, CardService, CardSummary } from "../../../../client";
|
||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { Text } from "@mantine/core";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
|
||||
const useDealSummaryState = () => {
|
||||
const { refetchDeals } = useDealPageContext();
|
||||
const useCardSummaryState = () => {
|
||||
const { refetchCards } = useCardPageContext();
|
||||
|
||||
const recalculate = async (dealId: number) => {
|
||||
return DealService.recalculateDealPrice({
|
||||
const recalculate = async (cardId: number) => {
|
||||
return CardService.recalculateCardPrice({
|
||||
requestBody: {
|
||||
dealId: dealId,
|
||||
cardId,
|
||||
},
|
||||
}).then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
});
|
||||
};
|
||||
|
||||
const onDelete = (summary: DealSummary) => {
|
||||
const onDelete = (summary: CardSummary) => {
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление сделки",
|
||||
title: "Удаление карточки",
|
||||
children: (
|
||||
<Text>
|
||||
Вы уверены что хотите удалить сделку "{summary.name}"?
|
||||
Вы уверены что хотите удалить карточку "{summary.name}"?
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: "Да", cancel: "Нет" },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => {
|
||||
DealService.deleteDeal({
|
||||
CardService.deleteCard({
|
||||
requestBody: {
|
||||
dealId: summary.id,
|
||||
cardId: summary.id,
|
||||
},
|
||||
}).then(async (response) => {
|
||||
notifications.guess(response.ok, { message: response.message });
|
||||
if (response.ok) await refetchDeals();
|
||||
if (response.ok) await refetchCards();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onComplete = (summary: DealSummary) => {
|
||||
const onComplete = (summary: CardSummary) => {
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление сделки",
|
||||
title: "Удаление карточки",
|
||||
children: (
|
||||
<Text>
|
||||
Вы уверены что хотите завершить сделку "{summary.name}"?
|
||||
Вы уверены что хотите завершить карточку "{summary.name}"?
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: "Да", cancel: "Нет" },
|
||||
confirmProps: { color: "green" },
|
||||
onConfirm: () => {
|
||||
DealService.completeDeal({
|
||||
CardService.completeCard({
|
||||
requestBody: {
|
||||
dealId: summary.id,
|
||||
cardId: summary.id,
|
||||
},
|
||||
}).then(async (response) => {
|
||||
notifications.guess(response.ok, { message: response.message });
|
||||
if (response.ok) await refetchDeals();
|
||||
|
||||
|
||||
if (response.ok) await refetchCards();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onDeleteFromGroup = (summary: DealSummary) => {
|
||||
const onDeleteFromGroup = (summary: CardSummary) => {
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление сделки",
|
||||
title: "Удаление карточки",
|
||||
children: (
|
||||
<Text>
|
||||
Вы уверены что хотите удалить сделку "{summary.name}" из группы?
|
||||
Вы уверены что хотите удалить карточку "{summary.name}" из группы?
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: "Да", cancel: "Нет" },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => {
|
||||
DealGroupService.removeDeal({
|
||||
CardGroupService.removeCard({
|
||||
requestBody: {
|
||||
dealId: summary.id,
|
||||
cardId: summary.id,
|
||||
},
|
||||
}).then(async (response) => {
|
||||
notifications.guess(response.ok, { message: response.message });
|
||||
if (response.ok) await refetchDeals();
|
||||
if (response.ok) await refetchCards();
|
||||
await recalculate(summary.id);
|
||||
if (response.ok) await refetchDeals();
|
||||
|
||||
if (response.ok) await refetchCards();
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -98,4 +95,4 @@ const useDealSummaryState = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export default useDealSummaryState;
|
||||
export default useCardSummaryState;
|
||||
@@ -1,111 +1,83 @@
|
||||
import { FC } from "react";
|
||||
import styles from "./DealsDndColumn.module.css";
|
||||
import { Draggable, Droppable } from "@hello-pangea/dnd";
|
||||
import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
|
||||
import { DealGroupSchema, DealSummary, StatusSchema } from "../../../../client";
|
||||
import DealSummaryCard from "../DealSummaryCard/DealSummaryCard.tsx";
|
||||
import CreateCardButton from "../CreateCardButton/CreateCardButton.tsx";
|
||||
import { CardGroupSchema, CardSummary, StatusSchema } from "../../../../client";
|
||||
import CardSummaryItem from "../CardSummaryItem/CardSummaryItem.tsx";
|
||||
import classNames from "classnames";
|
||||
import { groupBy, has, uniq } from "lodash";
|
||||
import { DealGroupView } from "../DealGroupView/DealGroupView.tsx";
|
||||
import CreateDealsFromFileButton from "../CreateDealsFromFileButton/CreateDealsFromFileButton.tsx";
|
||||
import DragState from "../../../../pages/DealsPage/enums/DragState.ts";
|
||||
import { CardGroupView } from "../CardGroupView/CardGroupView.tsx";
|
||||
import CreateDealsFromFileButton from "../CreateCardsFromFileButton/CreateDealsFromFileButton.tsx";
|
||||
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
|
||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||
|
||||
type Props = {
|
||||
status: StatusSchema;
|
||||
withCreateButton?: boolean;
|
||||
summaries: DealSummary[];
|
||||
summaries: CardSummary[];
|
||||
dragState: DragState;
|
||||
};
|
||||
|
||||
type GroupWithDeals = { group: DealGroupSchema, deals: DealSummary[] }
|
||||
type GroupWithCards = { group: CardGroupSchema, cards: CardSummary[] }
|
||||
|
||||
export const DealsDndColumn: FC<Props> = ({
|
||||
export const CardsDndColumn: FC<Props> = ({
|
||||
status,
|
||||
summaries,
|
||||
dragState,
|
||||
withCreateButton = false,
|
||||
}) => {
|
||||
const { selectedProject } = useCardPageContext();
|
||||
const isCreatingDealFromFileEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||
const isDropDisabled = dragState === DragState.DRAG_STATUS;
|
||||
const droppableId = status.id.toString();
|
||||
|
||||
const isGroup = (obj: GroupWithDeals | DealSummary): obj is GroupWithDeals => {
|
||||
return has(obj, "deals");
|
||||
const isGroup = (obj: GroupWithCards | CardSummary): obj is GroupWithCards => {
|
||||
return has(obj, "cards");
|
||||
};
|
||||
|
||||
const getDealGroups = (): GroupWithDeals[] => {
|
||||
const groups = uniq<DealGroupSchema>(summaries.filter(s => s.group).map(summary => summary.group) as DealGroupSchema[]);
|
||||
const getDealGroups = (): GroupWithCards[] => {
|
||||
const groups = uniq<CardGroupSchema>(summaries.filter(s => s.group).map(summary => summary.group) as CardGroupSchema[]);
|
||||
if (groups.length === 0) return [];
|
||||
const groupedSummaries = groupBy(summaries, "group.id");
|
||||
const groupDict = groups.reduce((acc, group) => {
|
||||
acc[group.id] = group;
|
||||
return acc;
|
||||
}
|
||||
, {} as { [key: number]: DealGroupSchema });
|
||||
return Object.entries(groupedSummaries).reduce((acc, [groupId, deals]) => {
|
||||
, {} as { [key: number]: CardGroupSchema });
|
||||
return Object.entries(groupedSummaries).reduce((acc, [groupId, cards]) => {
|
||||
if (!groupId) return acc;
|
||||
const group = groupDict[parseInt(groupId)];
|
||||
if (!group) return acc;
|
||||
acc.push({
|
||||
group,
|
||||
deals,
|
||||
cards,
|
||||
});
|
||||
return acc;
|
||||
}, [] as { group: DealGroupSchema; deals: DealSummary[] }[]);
|
||||
}, [] as { group: CardGroupSchema; cards: CardSummary[] }[]);
|
||||
};
|
||||
|
||||
const getDateFromDealOrGroup = (obj: GroupWithDeals | DealSummary) => {
|
||||
// if is group get group with the delivery earliest date
|
||||
if (isGroup(obj)) {
|
||||
const dates = obj.deals.map(d => d.deliveryDate || d.createdAt).filter(Boolean);
|
||||
if (dates.length === 0) return null;
|
||||
return new Date(Math.min(...dates.map(d => {
|
||||
return new Date(d).getTime();
|
||||
})));
|
||||
}
|
||||
// if is deal get deal delivery date
|
||||
return new Date(obj.deliveryDate || obj.createdAt);
|
||||
};
|
||||
|
||||
const getDealsAndGroups = (): (GroupWithDeals | DealSummary)[] => {
|
||||
const getDealsAndGroups = (): (GroupWithCards | CardSummary)[] => {
|
||||
const groups = getDealGroups();
|
||||
const deals = summaries.filter(s => !s.group);
|
||||
const dealsWithDeliveryDate = deals.filter(d => d.deliveryDate).sort((a, b) => {
|
||||
if (!a.deliveryDate || !b.deliveryDate) return 0;
|
||||
return new Date(a.deliveryDate).getTime() - new Date(b.deliveryDate).getTime();
|
||||
});
|
||||
const dealsWithoutDeliveryDate = deals.filter(d => !d.deliveryDate).sort((a, b) => {
|
||||
const cards = summaries.filter(s => !s.group).sort((a, b) => {
|
||||
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||||
});
|
||||
const groupsWithDeliveryDate = groups.filter(g => g.deals.some(d => d.deliveryDate));
|
||||
const groupsWithoutDeliveryDate = groups.filter(g => g.deals.every(d => !d.deliveryDate));
|
||||
const firstRankData = [...dealsWithDeliveryDate, ...groupsWithDeliveryDate].sort((a, b) => {
|
||||
const aDate = getDateFromDealOrGroup(a);
|
||||
const bDate = getDateFromDealOrGroup(b);
|
||||
if (!aDate || !bDate) return 0;
|
||||
return aDate.getTime() - bDate.getTime();
|
||||
});
|
||||
const secondRankData = [...dealsWithoutDeliveryDate, ...groupsWithoutDeliveryDate].sort((a, b) => {
|
||||
const aDate = getDateFromDealOrGroup(a);
|
||||
const bDate = getDateFromDealOrGroup(b);
|
||||
if (!aDate || !bDate) return 0;
|
||||
return aDate.getTime() - bDate.getTime();
|
||||
});
|
||||
return [...firstRankData, ...secondRankData].map((obj, index) => {
|
||||
return [...cards, ...groups].map((obj, index) => {
|
||||
if (isGroup(obj)) {
|
||||
obj.deals[0].rank = index;
|
||||
obj.cards[0].rank = index;
|
||||
return obj;
|
||||
}
|
||||
obj.rank = index;
|
||||
return obj;
|
||||
}) as (GroupWithDeals | DealSummary)[];
|
||||
}) as (GroupWithCards | CardSummary)[];
|
||||
};
|
||||
|
||||
const renderDeal = (deal: DealSummary) => {
|
||||
const renderCard = (card: CardSummary) => {
|
||||
return (
|
||||
<Draggable
|
||||
draggableId={deal.id.toString()}
|
||||
index={deal.rank}
|
||||
key={deal.id}>
|
||||
draggableId={card.id.toString()}
|
||||
index={card.rank}
|
||||
key={card.id}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
{...provided.draggableProps}
|
||||
@@ -118,22 +90,19 @@ export const DealsDndColumn: FC<Props> = ({
|
||||
color: snapshot.combineWith ? "red" : "black",
|
||||
}}
|
||||
>
|
||||
<DealSummaryCard
|
||||
dealSummary={deal}
|
||||
/>
|
||||
<CardSummaryItem cardSummary={card} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
const renderGroup = (obj: GroupWithDeals) => {
|
||||
const { deals, group } = obj;
|
||||
const renderGroup = (obj: GroupWithCards) => {
|
||||
const { cards, group } = obj;
|
||||
return (
|
||||
<Draggable
|
||||
draggableId={"group-" + group.id}
|
||||
index={deals[0].rank}
|
||||
index={cards[0].rank}
|
||||
key={"group-" + group.id}
|
||||
>
|
||||
{(provided) => (
|
||||
@@ -142,8 +111,8 @@ export const DealsDndColumn: FC<Props> = ({
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
<DealGroupView
|
||||
deals={deals}
|
||||
<CardGroupView
|
||||
cards={cards}
|
||||
group={group}
|
||||
/>
|
||||
</div>
|
||||
@@ -168,15 +137,18 @@ export const DealsDndColumn: FC<Props> = ({
|
||||
{...provided.droppableProps}>
|
||||
{withCreateButton && (
|
||||
<>
|
||||
<CreateDealButton status={status}/>
|
||||
<CreateDealsFromFileButton />
|
||||
<CreateCardButton status={status} />
|
||||
{
|
||||
isCreatingDealFromFileEnabled &&
|
||||
(<CreateDealsFromFileButton />)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
{getDealsAndGroups().map(obj => {
|
||||
if (isGroup(obj)) {
|
||||
return renderGroup(obj);
|
||||
}
|
||||
return renderDeal(obj);
|
||||
return renderCard(obj);
|
||||
})}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
@@ -185,5 +157,5 @@ export const DealsDndColumn: FC<Props> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default DealsDndColumn;
|
||||
export default CardsDndColumn;
|
||||
|
||||
@@ -2,17 +2,17 @@ import { modals } from "@mantine/modals";
|
||||
import { Flex } from "@mantine/core";
|
||||
import { DropResult } from "@hello-pangea/dnd";
|
||||
import { useEffect, useState } from "react";
|
||||
import { DealGroupService, DealService, DealSummary, DealSummaryReorderRequest } from "../../../../../client";
|
||||
import { CardGroupService, CardService, CardSummary, CardSummaryReorderRequest } from "../../../../../client";
|
||||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||
import { dateWithoutTimezone } from "../../../../../shared/lib/date.ts";
|
||||
|
||||
|
||||
type Props = {
|
||||
summariesRaw: DealSummary[];
|
||||
summariesRaw: CardSummary[];
|
||||
refetchSummaries: () => void;
|
||||
}
|
||||
|
||||
const useDealsDnd = ({
|
||||
const useCardsDnd = ({
|
||||
summariesRaw,
|
||||
refetchSummaries,
|
||||
}: Props) => {
|
||||
@@ -22,18 +22,18 @@ const useDealsDnd = ({
|
||||
setSummaries(summariesRaw);
|
||||
}, [summariesRaw]);
|
||||
|
||||
const recalculate = async (dealId: number) => {
|
||||
return DealService.recalculateDealPrice({
|
||||
const recalculate = async (cardId: number) => {
|
||||
return CardService.recalculateCardPrice({
|
||||
requestBody: {
|
||||
dealId: dealId,
|
||||
cardId,
|
||||
},
|
||||
}).then(({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
});
|
||||
};
|
||||
|
||||
const onDelete = (dealId: number) => {
|
||||
const summary = summaries.find(summary => summary.id == dealId);
|
||||
const onDelete = (cardId: number) => {
|
||||
const summary = summaries.find(summary => summary.id == cardId);
|
||||
if (!summary) return;
|
||||
modals.openConfirmModal({
|
||||
title: "Удаление сделки",
|
||||
@@ -43,8 +43,8 @@ const useDealsDnd = ({
|
||||
</Flex>
|
||||
),
|
||||
onConfirm: () => {
|
||||
DealService.deleteDeal({
|
||||
requestBody: { dealId: dealId },
|
||||
CardService.deleteCard({
|
||||
requestBody: { cardId },
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
@@ -58,8 +58,8 @@ const useDealsDnd = ({
|
||||
});
|
||||
};
|
||||
|
||||
const onSuccess = (dealId: number) => {
|
||||
const summary = summaries.find(summary => summary.id == dealId);
|
||||
const onSuccess = (cardId: number) => {
|
||||
const summary = summaries.find(summary => summary.id == cardId);
|
||||
if (!summary) return;
|
||||
modals.openConfirmModal({
|
||||
title: "Завершение сделки",
|
||||
@@ -69,8 +69,8 @@ const useDealsDnd = ({
|
||||
</Flex>
|
||||
),
|
||||
onConfirm: () => {
|
||||
DealService.completeDeal({
|
||||
requestBody: { dealId: dealId },
|
||||
CardService.completeCard({
|
||||
requestBody: { cardId },
|
||||
}).then(async ({ ok, message }) => {
|
||||
notifications.guess(ok, { message });
|
||||
if (!ok) return;
|
||||
@@ -91,9 +91,9 @@ const useDealsDnd = ({
|
||||
const sourceId = parseInt(source);
|
||||
if (destination.includes("group")) {
|
||||
const groupId = parseInt(destination.split("-")[1]);
|
||||
DealGroupService.addDeal({
|
||||
CardGroupService.addCard({
|
||||
requestBody: {
|
||||
dealId: sourceId,
|
||||
cardId: sourceId,
|
||||
groupId: groupId,
|
||||
},
|
||||
}).then(async response => {
|
||||
@@ -108,10 +108,10 @@ const useDealsDnd = ({
|
||||
} else {
|
||||
const destinationId = parseInt(destination);
|
||||
// creating new group
|
||||
DealGroupService.createDealGroup({
|
||||
CardGroupService.createCardGroup({
|
||||
requestBody: {
|
||||
draggingDealId: sourceId,
|
||||
hoveredDealId: destinationId,
|
||||
draggingCardId: sourceId,
|
||||
hoveredCardId: destinationId,
|
||||
},
|
||||
}).then(async response => {
|
||||
if (!response.ok) {
|
||||
@@ -130,7 +130,7 @@ const useDealsDnd = ({
|
||||
const destination = result.destination?.droppableId;
|
||||
if (!destination) return;
|
||||
const statusId = parseInt(destination);
|
||||
DealGroupService.changeStatus({
|
||||
CardGroupService.changeStatus({
|
||||
requestBody: {
|
||||
groupId: groupId,
|
||||
newStatus: statusId,
|
||||
@@ -152,37 +152,37 @@ const useDealsDnd = ({
|
||||
// If there is no changes
|
||||
if (!result.destination || result.destination == result.source) return;
|
||||
|
||||
// Checking for valid dealId
|
||||
// Checking for valid cardId
|
||||
if (result.draggableId.includes("group")) {
|
||||
return moveGroup(result);
|
||||
}
|
||||
const dealId = parseInt(result.draggableId);
|
||||
if (isNaN(dealId)) return;
|
||||
const cardId = parseInt(result.draggableId);
|
||||
if (isNaN(cardId)) return;
|
||||
|
||||
// Checking for valid deal
|
||||
const summary = summaries.find(summary => summary.id == dealId);
|
||||
// Checking for valid card
|
||||
const summary = summaries.find(summary => summary.id == cardId);
|
||||
if (!summary) return;
|
||||
|
||||
// Checking if it is custom actions
|
||||
const droppableId = result.destination.droppableId;
|
||||
if (droppableId === "DELETE") {
|
||||
onDelete(dealId);
|
||||
onDelete(cardId);
|
||||
return;
|
||||
}
|
||||
if (droppableId === "SUCCESS") {
|
||||
onSuccess(dealId);
|
||||
onSuccess(cardId);
|
||||
return;
|
||||
}
|
||||
|
||||
const statusId = Number.parseInt(droppableId);
|
||||
const request: Partial<DealSummaryReorderRequest> = {
|
||||
dealId,
|
||||
const request: Partial<CardSummaryReorderRequest> = {
|
||||
cardId,
|
||||
index: result.destination.index,
|
||||
statusId,
|
||||
};
|
||||
if (statusId == summary.status.id) {
|
||||
DealService.reorderDealSummaries({
|
||||
requestBody: request as DealSummaryReorderRequest,
|
||||
CardService.reorderCardSummaries({
|
||||
requestBody: request as CardSummaryReorderRequest,
|
||||
}).then(async response => {
|
||||
setSummaries(response.summaries);
|
||||
await refetchSummaries();
|
||||
@@ -190,9 +190,9 @@ const useDealsDnd = ({
|
||||
return;
|
||||
}
|
||||
|
||||
DealService.reorderDealSummaries({
|
||||
CardService.reorderCardSummaries({
|
||||
requestBody: {
|
||||
dealId,
|
||||
cardId,
|
||||
statusId,
|
||||
index: result.destination.index,
|
||||
comment: "",
|
||||
@@ -210,4 +210,4 @@ const useDealsDnd = ({
|
||||
};
|
||||
};
|
||||
|
||||
export default useDealsDnd;
|
||||
export default useCardsDnd;
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Flex, rem } from "@mantine/core";
|
||||
import classNames from "classnames";
|
||||
import styles from "../../../../pages/DealsPage/ui/DealsPage.module.css";
|
||||
import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css";
|
||||
import { Droppable } from "@hello-pangea/dnd";
|
||||
import DragState from "../../../../pages/DealsPage/enums/DragState.ts";
|
||||
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
|
||||
|
||||
type Props = {
|
||||
dragState: DragState;
|
||||
}
|
||||
|
||||
const DealsDndFooter = ({ dragState }: Props) => {
|
||||
const CardsDndFooter = ({ dragState }: Props) => {
|
||||
const isDealDragEnded = dragState === DragState.DRAG_ENDED;
|
||||
|
||||
return (
|
||||
@@ -63,4 +63,4 @@ const DealsDndFooter = ({ dragState }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DealsDndFooter;
|
||||
export default CardsDndFooter;
|
||||
@@ -2,21 +2,26 @@ import { useState } from "react";
|
||||
|
||||
import styles from "./CreateDealButton.module.css";
|
||||
import { Text, Transition } from "@mantine/core";
|
||||
import CreateDealFrom from "../CreateDealForm/CreateDealFrom.tsx";
|
||||
import { DealService, StatusSchema } from "../../../../client";
|
||||
import CreateCardForm from "../CreateCardForm/CreateCardForm.tsx";
|
||||
import { CardService, StatusSchema } from "../../../../client";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { dateWithoutTimezone } from "../../../../shared/lib/date.ts";
|
||||
import { usePrefillDealContext } from "../../../../pages/DealsPage/contexts/PrefillDealContext.tsx";
|
||||
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
|
||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||
|
||||
type Props = {
|
||||
status: StatusSchema;
|
||||
}
|
||||
|
||||
const CreateDealButton = ({ status }: Props) => {
|
||||
const CreateCardButton = ({ status }: Props) => {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [isTransitionEnded, setIsTransitionEnded] = useState(true);
|
||||
const queryClient = useQueryClient();
|
||||
const { prefillDeal, setPrefillDeal } = usePrefillDealContext();
|
||||
const { prefillCard, setPrefillCard } = usePrefillCardContext();
|
||||
|
||||
const { selectedProject } = useCardPageContext();
|
||||
const isPrefillingDealEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -35,13 +40,13 @@ const CreateDealButton = ({ status }: Props) => {
|
||||
onExited={() => setIsTransitionEnded(true)}>
|
||||
{styles => (
|
||||
<div style={styles}>
|
||||
<CreateDealFrom
|
||||
<CreateCardForm
|
||||
onCancel={() => {
|
||||
setPrefillDeal(undefined);
|
||||
setPrefillCard(undefined);
|
||||
setIsCreating(false);
|
||||
}}
|
||||
onSubmit={quickDeal => {
|
||||
DealService.quickCreateDealQuickCreatePost({
|
||||
CardService.quickCreateCardQuickCreatePost({
|
||||
requestBody: {
|
||||
...quickDeal,
|
||||
acceptanceDate: dateWithoutTimezone(
|
||||
@@ -50,19 +55,19 @@ const CreateDealButton = ({ status }: Props) => {
|
||||
statusId: status.id,
|
||||
},
|
||||
}).then(async (result) => {
|
||||
if (prefillDeal) {
|
||||
DealService.prefillDeal({
|
||||
if (isPrefillingDealEnabled && prefillCard) {
|
||||
CardService.prefillCard({
|
||||
requestBody: {
|
||||
oldDealId: prefillDeal.id,
|
||||
newDealId: result.dealId,
|
||||
oldCardId: prefillCard.id,
|
||||
newCardId: result.cardId,
|
||||
},
|
||||
});
|
||||
}
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["getDealSummaries"],
|
||||
queryKey: ["getCardSummaries"],
|
||||
});
|
||||
setIsCreating(false);
|
||||
setPrefillDeal(undefined);
|
||||
setPrefillCard(undefined);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -72,4 +77,4 @@ const CreateDealButton = ({ status }: Props) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default CreateDealButton;
|
||||
export default CreateCardButton;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button, rem, Textarea, TextInput } from "@mantine/core";
|
||||
import { QuickDeal } from "../../../../types/QuickDeal.ts";
|
||||
import { QuickCard } from "../../../../types/QuickCard.ts";
|
||||
import { FC } from "react";
|
||||
import { useForm } from "@mantine/form";
|
||||
import styles from "./CreateDealForm.module.css";
|
||||
@@ -8,15 +8,21 @@ import { DateTimePicker } from "@mantine/dates";
|
||||
import ShippingWarehouseAutocomplete
|
||||
from "../../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||||
import BaseMarketplaceSelect from "../../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
|
||||
import { usePrefillDealContext } from "../../../../pages/DealsPage/contexts/PrefillDealContext.tsx";
|
||||
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
|
||||
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||
import isModuleInProject, { Modules } from "../../../../pages/CardsPage/utils/isModuleInProject.ts";
|
||||
|
||||
type Props = {
|
||||
onSubmit: (quickDeal: QuickDeal) => void;
|
||||
onSubmit: (quickDeal: QuickCard) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||
const { prefillOnOpen, prefillDeal } = usePrefillDealContext();
|
||||
const form = useForm<QuickDeal>({
|
||||
|
||||
const CreateCardForm: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||
const { selectedProject } = useCardPageContext();
|
||||
const isPrefillingDealEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject);
|
||||
|
||||
const { prefillOnOpen, prefillCard } = usePrefillCardContext();
|
||||
const form = useForm<QuickCard>({
|
||||
initialValues: {
|
||||
name: "",
|
||||
clientName: "",
|
||||
@@ -32,7 +38,7 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||
},
|
||||
});
|
||||
|
||||
const prefillButtonLabel = prefillDeal ? `Предзаполнено [ID: ${prefillDeal.id}]` : "Предзаполнить";
|
||||
const prefillButtonLabel = prefillCard ? `Предзаполнено [ID: ${prefillCard.id}]` : "Предзаполнить";
|
||||
|
||||
return (
|
||||
<form
|
||||
@@ -83,14 +89,16 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||
{...form.getInputProps("acceptanceDate")}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["button-prefill"]}>
|
||||
<Button
|
||||
style={{ whiteSpace: "wrap" }}
|
||||
variant={"outline"}
|
||||
onClick={() => prefillOnOpen()}>
|
||||
{prefillButtonLabel}
|
||||
</Button>
|
||||
</div>
|
||||
{isPrefillingDealEnabled && (
|
||||
<div className={styles["button-prefill"]}>
|
||||
<Button
|
||||
style={{ whiteSpace: "wrap" }}
|
||||
variant={"outline"}
|
||||
onClick={() => prefillOnOpen()}>
|
||||
{prefillButtonLabel}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles["buttons"]}>
|
||||
<Button type={"submit"}>Добавить</Button>
|
||||
<Button
|
||||
@@ -104,4 +112,4 @@ const CreateDealFrom: FC<Props> = ({ onSubmit, onCancel }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateDealFrom;
|
||||
export default CreateCardForm;
|
||||
@@ -1,9 +1,9 @@
|
||||
import styles from "./CreateDealsFromFileButton.module.css";
|
||||
import { Text } from "@mantine/core";
|
||||
import { usePrefillDealsWithExcelContext } from "../../../../pages/DealsPage/contexts/PrefillDealsWithExcelContext.tsx";
|
||||
import { usePrefillCardsWithExcelContext } from "../../../../pages/CardsPage/contexts/PrefillDealsWithExcelContext.tsx";
|
||||
|
||||
const CreateDealsFromFileButton = () => {
|
||||
const { prefillWithExcelOnOpen } = usePrefillDealsWithExcelContext();
|
||||
const { prefillWithExcelOnOpen } = usePrefillCardsWithExcelContext();
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -1,11 +1,11 @@
|
||||
import styles from "../../../../pages/DealsPage/ui/DealsPage.module.css";
|
||||
import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css";
|
||||
import { Divider, Text, Title } from "@mantine/core";
|
||||
import getColumnColor from "../../Deals/DealsDndColumn/utils/getColumnColor.ts";
|
||||
import { DealSummary, StatusSchema } from "../../../../client";
|
||||
import getColumnColor from "../../Cards/CardsDndColumn/utils/getColumnColor.ts";
|
||||
import { CardSummary, StatusSchema } from "../../../../client";
|
||||
import { getPluralForm } from "../../../../shared/lib/utils.ts";
|
||||
import { sum } from "lodash";
|
||||
import { Draggable, Droppable } from "@hello-pangea/dnd";
|
||||
import DragState from "../../../../pages/DealsPage/enums/DragState.ts";
|
||||
import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
|
||||
import { useContextMenu } from "mantine-contextmenu";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import useStatus from "./hooks/useStatus.tsx";
|
||||
@@ -14,7 +14,7 @@ import useStatus from "./hooks/useStatus.tsx";
|
||||
type Props = {
|
||||
status: StatusSchema;
|
||||
index: number;
|
||||
summaries: DealSummary[];
|
||||
summaries: CardSummary[];
|
||||
dragState: DragState;
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { BoardSchema, DealSummary, StatusSchema } from "../../../../client";
|
||||
import DealsDndColumn from "../../Deals/DealsDndColumn/DealsDndColumn.tsx";
|
||||
import styles from "../../../../pages/DealsPage/ui/DealsPage.module.css";
|
||||
import DealsDndFooter from "../../Deals/DealsDndFooter/DealsDndFooter.tsx";
|
||||
import { BoardSchema, CardSummary, StatusSchema } from "../../../../client";
|
||||
import CardsDndColumn from "../../Cards/CardsDndColumn/CardsDndColumn.tsx";
|
||||
import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css";
|
||||
import CardsDndFooter from "../../Cards/CardsDndFooter/CardsDndFooter.tsx";
|
||||
import { Flex, rem, Stack } from "@mantine/core";
|
||||
import { DragDropContext } from "@hello-pangea/dnd";
|
||||
import useDnd from "../../../../pages/DealsPage/hooks/useDnd.tsx";
|
||||
import useDnd from "../../../../pages/CardsPage/hooks/useDnd.tsx";
|
||||
import Status from "../Status/Status.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
selectedBoard: BoardSchema | null;
|
||||
summariesRaw: DealSummary[];
|
||||
summariesRaw: CardSummary[];
|
||||
refetchSummaries: () => void;
|
||||
refetchBoards: () => void;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ const Statuses = ({
|
||||
dragState={dragState}
|
||||
refetch={refetchBoards}
|
||||
/>
|
||||
<DealsDndColumn
|
||||
<CardsDndColumn
|
||||
withCreateButton={index === 0}
|
||||
summaries={filteredSummaries}
|
||||
status={status}
|
||||
@@ -57,7 +57,7 @@ const Statuses = ({
|
||||
);
|
||||
};
|
||||
|
||||
const statuses = selectedBoard?.dealStatuses
|
||||
const statuses = selectedBoard?.statuses
|
||||
.filter(status => !status.isDeleted)
|
||||
.sort((a, b) => a.ordinalNumber - b.ordinalNumber) ?? [];
|
||||
|
||||
@@ -77,7 +77,7 @@ const Statuses = ({
|
||||
}))
|
||||
}
|
||||
</Flex>
|
||||
<DealsDndFooter dragState={dragState} />
|
||||
<CardsDndFooter dragState={dragState} />
|
||||
</Flex>
|
||||
</DragDropContext>
|
||||
);
|
||||
|
||||
@@ -30,13 +30,13 @@ const useStatusesDnd = ({ refetch, board }: Props) => {
|
||||
// If there is no changes
|
||||
if (!result.destination || result.destination == result.source) return;
|
||||
|
||||
// Checking for valid dealId
|
||||
// Checking for valid cardId
|
||||
const statusIdStr = result.draggableId.replace("status-", "");
|
||||
const statusId = parseInt(statusIdStr);
|
||||
if (isNaN(statusId)) return;
|
||||
|
||||
// Checking for valid deal
|
||||
const status = board.dealStatuses.find(board => board.id == statusId);
|
||||
// Checking for valid card
|
||||
const status = board.statuses.find(board => board.id == statusId);
|
||||
if (!status) return;
|
||||
|
||||
updateStatusOrder(status, result.destination.index);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from "react";
|
||||
import ObjectSelect, { ObjectSelectProps } from "../ObjectSelect/ObjectSelect.tsx";
|
||||
import { UserSchema } from "../../client";
|
||||
import useManagersList from "../../pages/DealsPage/hooks/useManagersList.tsx";
|
||||
import useManagersList from "../../pages/CardsPage/hooks/useManagersList.tsx";
|
||||
|
||||
type Props = Omit<
|
||||
ObjectSelectProps<UserSchema | null>,
|
||||
|
||||
@@ -58,14 +58,9 @@ function NavbarLink(props: NavbarLinkProps) {
|
||||
const mockdata = [
|
||||
{
|
||||
icon: IconCash,
|
||||
label: "Сделки",
|
||||
label: "Карточки",
|
||||
href: "/leads",
|
||||
},
|
||||
// {
|
||||
// icon: IconTable,
|
||||
// label: 'Таблица сделок',
|
||||
// href: '/deals'
|
||||
// },
|
||||
{
|
||||
icon: IconMan,
|
||||
label: "Клиенты",
|
||||
|
||||
Reference in New Issue
Block a user