diff --git a/src/client/index.ts b/src/client/index.ts index bab211d..3dd9453 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -31,6 +31,7 @@ export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest'; export type { Body_upload_passport_image } from './models/Body_upload_passport_image'; export type { Body_upload_product_barcode_image } from './models/Body_upload_product_barcode_image'; export type { Body_upload_product_image } from './models/Body_upload_product_image'; +export type { BoxSchema } from './models/BoxSchema'; export type { CancelDealBillRequest } from './models/CancelDealBillRequest'; export type { CancelDealBillResponse } from './models/CancelDealBillResponse'; export type { ClientCreateRequest } from './models/ClientCreateRequest'; @@ -45,11 +46,14 @@ export type { ClientUpdateRequest } from './models/ClientUpdateRequest'; export type { ClientUpdateResponse } from './models/ClientUpdateResponse'; export type { CreateBarcodeTemplateAttributeRequest } from './models/CreateBarcodeTemplateAttributeRequest'; export type { CreateBarcodeTemplateAttributeResponse } from './models/CreateBarcodeTemplateAttributeResponse'; +export type { CreateBoxInDealSchema } from './models/CreateBoxInDealSchema'; +export type { CreateBoxInPalletSchema } from './models/CreateBoxInPalletSchema'; export type { CreateDealBillRequest } from './models/CreateDealBillRequest'; export type { CreateDealBillResponse } from './models/CreateDealBillResponse'; export type { CreateExpenseTagRequest } from './models/CreateExpenseTagRequest'; export type { CreateMarketplaceRequest } from './models/CreateMarketplaceRequest'; export type { CreateMarketplaceResponse } from './models/CreateMarketplaceResponse'; +export type { CreatePalletResponse } from './models/CreatePalletResponse'; export type { CreatePaymentRecordRequest } from './models/CreatePaymentRecordRequest'; export type { CreatePaymentRecordResponse } from './models/CreatePaymentRecordResponse'; export type { CreatePayRateRequest } from './models/CreatePayRateRequest'; @@ -61,6 +65,7 @@ export type { CreatePriceCategoryResponse } from './models/CreatePriceCategoryRe export type { CreateServiceKitSchema } from './models/CreateServiceKitSchema'; export type { CreateServicesKitRequest } from './models/CreateServicesKitRequest'; export type { CreateServicesKitResponse } from './models/CreateServicesKitResponse'; +export type { CreateShippingProductSchema } from './models/CreateShippingProductSchema'; export type { CreateShippingWarehouseRequest } from './models/CreateShippingWarehouseRequest'; export type { CreateShippingWarehouseResponse } from './models/CreateShippingWarehouseResponse'; export type { CreateTaskResponse } from './models/CreateTaskResponse'; @@ -133,10 +138,12 @@ export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServic export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse'; export type { DealUpdateServiceRequest } from './models/DealUpdateServiceRequest'; export type { DealUpdateServiceResponse } from './models/DealUpdateServiceResponse'; +export type { DeleteBoxResponse } from './models/DeleteBoxResponse'; export type { DeleteExpenseResponse } from './models/DeleteExpenseResponse'; export type { DeleteExpenseTagResponse } from './models/DeleteExpenseTagResponse'; export type { DeleteMarketplaceRequest } from './models/DeleteMarketplaceRequest'; export type { DeleteMarketplaceResponse } from './models/DeleteMarketplaceResponse'; +export type { DeletePalletResponse } from './models/DeletePalletResponse'; export type { DeletePaymentRecordRequest } from './models/DeletePaymentRecordRequest'; export type { DeletePaymentRecordResponse } from './models/DeletePaymentRecordResponse'; export type { DeletePayRateRequest } from './models/DeletePayRateRequest'; @@ -146,6 +153,7 @@ export type { DeletePositionResponse } from './models/DeletePositionResponse'; export type { DeletePriceCategoryRequest } from './models/DeletePriceCategoryRequest'; export type { DeletePriceCategoryResponse } from './models/DeletePriceCategoryResponse'; export type { DeleteShiftResponse } from './models/DeleteShiftResponse'; +export type { DeleteShippingProductResponse } from './models/DeleteShippingProductResponse'; export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest'; export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse'; export type { ExpenseSchemaBase } from './models/ExpenseSchemaBase'; @@ -195,6 +203,7 @@ export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema'; export type { MarketplaceSchema } from './models/MarketplaceSchema'; export type { NotificationChannel } from './models/NotificationChannel'; export type { PaginationInfoSchema } from './models/PaginationInfoSchema'; +export type { PalletSchema } from './models/PalletSchema'; export type { PassportImageSchema } from './models/PassportImageSchema'; export type { PaymentRecordCreateSchema } from './models/PaymentRecordCreateSchema'; export type { PaymentRecordGetSchema } from './models/PaymentRecordGetSchema'; @@ -248,6 +257,7 @@ export type { ServiceUpdateCategoryRequest } from './models/ServiceUpdateCategor export type { ServiceUpdateCategoryResponse } from './models/ServiceUpdateCategoryResponse'; export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest'; export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse'; +export type { ShippingProductSchema } from './models/ShippingProductSchema'; export type { ShippingWarehouseSchema } from './models/ShippingWarehouseSchema'; export type { StartPauseByShiftIdResponse } from './models/StartPauseByShiftIdResponse'; export type { StartPauseByUserIdResponse } from './models/StartPauseByUserIdResponse'; @@ -256,6 +266,9 @@ export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketpl export type { TaskInfoResponse } from './models/TaskInfoResponse'; export type { TimeTrackingData } from './models/TimeTrackingData'; export type { TimeTrackingRecord } from './models/TimeTrackingRecord'; +export type { UpdateBoxRequest } from './models/UpdateBoxRequest'; +export type { UpdateBoxResponse } from './models/UpdateBoxResponse'; +export type { UpdateBoxSchema } from './models/UpdateBoxSchema'; export type { UpdateExpenseRequest } from './models/UpdateExpenseRequest'; export type { UpdateExpenseResponse } from './models/UpdateExpenseResponse'; export type { UpdateExpenseSchema } from './models/UpdateExpenseSchema'; @@ -270,6 +283,9 @@ export type { UpdatePriceCategoryResponse } from './models/UpdatePriceCategoryRe export type { UpdateServiceKitSchema } from './models/UpdateServiceKitSchema'; export type { UpdateServicesKitRequest } from './models/UpdateServicesKitRequest'; export type { UpdateServicesKitResponse } from './models/UpdateServicesKitResponse'; +export type { UpdateShippingProductRequest } from './models/UpdateShippingProductRequest'; +export type { UpdateShippingProductResponse } from './models/UpdateShippingProductResponse'; +export type { UpdateShippingProductSchema } from './models/UpdateShippingProductSchema'; export type { UpdateShippingWarehouseRequest } from './models/UpdateShippingWarehouseRequest'; export type { UpdateShippingWarehouseResponse } from './models/UpdateShippingWarehouseResponse'; export type { UpdateTimeTrackingRecordRequest } from './models/UpdateTimeTrackingRecordRequest'; @@ -296,6 +312,7 @@ export { PositionService } from './services/PositionService'; export { ProductService } from './services/ProductService'; export { RoleService } from './services/RoleService'; export { ServiceService } from './services/ServiceService'; +export { ShippingService } from './services/ShippingService'; export { ShippingWarehouseService } from './services/ShippingWarehouseService'; export { StatisticsService } from './services/StatisticsService'; export { TaskService } from './services/TaskService'; diff --git a/src/client/models/BoxSchema.ts b/src/client/models/BoxSchema.ts new file mode 100644 index 0000000..541ae96 --- /dev/null +++ b/src/client/models/BoxSchema.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProductSchema } from './ProductSchema'; +export type BoxSchema = { + id: number; + quantity: number; + product: ProductSchema; + palletId: (number | null); + dealId: (number | null); +}; + diff --git a/src/client/models/CreateBoxInDealSchema.ts b/src/client/models/CreateBoxInDealSchema.ts new file mode 100644 index 0000000..dea6305 --- /dev/null +++ b/src/client/models/CreateBoxInDealSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type CreateBoxInDealSchema = { + productId: (number | null); + quantity: (number | null); + dealId: (number | null); +}; + diff --git a/src/client/models/CreateBoxInPalletSchema.ts b/src/client/models/CreateBoxInPalletSchema.ts new file mode 100644 index 0000000..8bb8e42 --- /dev/null +++ b/src/client/models/CreateBoxInPalletSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type CreateBoxInPalletSchema = { + productId: (number | null); + quantity: (number | null); + palletId: (number | null); +}; + diff --git a/src/client/models/CreatePalletResponse.ts b/src/client/models/CreatePalletResponse.ts new file mode 100644 index 0000000..5b6fe7b --- /dev/null +++ b/src/client/models/CreatePalletResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type CreatePalletResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/CreateShippingProductSchema.ts b/src/client/models/CreateShippingProductSchema.ts new file mode 100644 index 0000000..6ebe1b5 --- /dev/null +++ b/src/client/models/CreateShippingProductSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type CreateShippingProductSchema = { + productId: (number | null); + quantity: (number | null); + palletId: number; +}; + diff --git a/src/client/models/DealSchema.ts b/src/client/models/DealSchema.ts index bf518a8..cadc5ac 100644 --- a/src/client/models/DealSchema.ts +++ b/src/client/models/DealSchema.ts @@ -2,12 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { BoxSchema } from './BoxSchema'; import type { ClientSchema } from './ClientSchema'; import type { DealBillRequestSchema } from './DealBillRequestSchema'; import type { DealGroupSchema } from './DealGroupSchema'; import type { DealProductSchema } from './DealProductSchema'; import type { DealServiceSchema } from './DealServiceSchema'; import type { DealStatusHistorySchema } from './DealStatusHistorySchema'; +import type { PalletSchema } from './PalletSchema'; import type { ServicePriceCategorySchema } from './ServicePriceCategorySchema'; import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema'; import type { UserSchema } from './UserSchema'; @@ -30,6 +32,8 @@ export type DealSchema = { category?: (ServicePriceCategorySchema | null); group?: (DealGroupSchema | null); manager?: (UserSchema | null); + pallets?: Array; + boxes?: Array; deliveryDate?: (string | null); receivingSlotDate?: (string | null); }; diff --git a/src/client/models/DeleteBoxResponse.ts b/src/client/models/DeleteBoxResponse.ts new file mode 100644 index 0000000..91039a1 --- /dev/null +++ b/src/client/models/DeleteBoxResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DeleteBoxResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/DeletePalletResponse.ts b/src/client/models/DeletePalletResponse.ts new file mode 100644 index 0000000..d4a5470 --- /dev/null +++ b/src/client/models/DeletePalletResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DeletePalletResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/DeleteShippingProductResponse.ts b/src/client/models/DeleteShippingProductResponse.ts new file mode 100644 index 0000000..3152e92 --- /dev/null +++ b/src/client/models/DeleteShippingProductResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DeleteShippingProductResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/PalletSchema.ts b/src/client/models/PalletSchema.ts new file mode 100644 index 0000000..cff1499 --- /dev/null +++ b/src/client/models/PalletSchema.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BoxSchema } from './BoxSchema'; +import type { ShippingProductSchema } from './ShippingProductSchema'; +export type PalletSchema = { + id: number; + boxes: Array; + shippingProducts: Array; +}; + diff --git a/src/client/models/ShippingProductSchema.ts b/src/client/models/ShippingProductSchema.ts new file mode 100644 index 0000000..858c323 --- /dev/null +++ b/src/client/models/ShippingProductSchema.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProductSchema } from './ProductSchema'; +export type ShippingProductSchema = { + id: number; + quantity: number; + product: ProductSchema; + palletId: number; +}; + diff --git a/src/client/models/UpdateBoxRequest.ts b/src/client/models/UpdateBoxRequest.ts new file mode 100644 index 0000000..8bfbaa8 --- /dev/null +++ b/src/client/models/UpdateBoxRequest.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CreateBoxInDealSchema } from './CreateBoxInDealSchema'; +import type { CreateBoxInPalletSchema } from './CreateBoxInPalletSchema'; +import type { UpdateBoxSchema } from './UpdateBoxSchema'; +export type UpdateBoxRequest = { + data: (CreateBoxInDealSchema | CreateBoxInPalletSchema | UpdateBoxSchema); +}; + diff --git a/src/client/models/UpdateBoxResponse.ts b/src/client/models/UpdateBoxResponse.ts new file mode 100644 index 0000000..da73fca --- /dev/null +++ b/src/client/models/UpdateBoxResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UpdateBoxResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/UpdateBoxSchema.ts b/src/client/models/UpdateBoxSchema.ts new file mode 100644 index 0000000..6870bbd --- /dev/null +++ b/src/client/models/UpdateBoxSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UpdateBoxSchema = { + productId: (number | null); + quantity: (number | null); + boxId: (number | null); +}; + diff --git a/src/client/models/UpdateShippingProductRequest.ts b/src/client/models/UpdateShippingProductRequest.ts new file mode 100644 index 0000000..efaa0b1 --- /dev/null +++ b/src/client/models/UpdateShippingProductRequest.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CreateShippingProductSchema } from './CreateShippingProductSchema'; +import type { UpdateShippingProductSchema } from './UpdateShippingProductSchema'; +export type UpdateShippingProductRequest = { + data: (CreateShippingProductSchema | UpdateShippingProductSchema); +}; + diff --git a/src/client/models/UpdateShippingProductResponse.ts b/src/client/models/UpdateShippingProductResponse.ts new file mode 100644 index 0000000..a47fd09 --- /dev/null +++ b/src/client/models/UpdateShippingProductResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UpdateShippingProductResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/UpdateShippingProductSchema.ts b/src/client/models/UpdateShippingProductSchema.ts new file mode 100644 index 0000000..28d615f --- /dev/null +++ b/src/client/models/UpdateShippingProductSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UpdateShippingProductSchema = { + productId: (number | null); + quantity: (number | null); + shippingProductId: number; +}; + diff --git a/src/client/services/ShippingService.ts b/src/client/services/ShippingService.ts new file mode 100644 index 0000000..d79994b --- /dev/null +++ b/src/client/services/ShippingService.ts @@ -0,0 +1,183 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CreatePalletResponse } from '../models/CreatePalletResponse'; +import type { DeleteBoxResponse } from '../models/DeleteBoxResponse'; +import type { DeletePalletResponse } from '../models/DeletePalletResponse'; +import type { DeleteShippingProductResponse } from '../models/DeleteShippingProductResponse'; +import type { UpdateBoxRequest } from '../models/UpdateBoxRequest'; +import type { UpdateBoxResponse } from '../models/UpdateBoxResponse'; +import type { UpdateShippingProductRequest } from '../models/UpdateShippingProductRequest'; +import type { UpdateShippingProductResponse } from '../models/UpdateShippingProductResponse'; +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; +export class ShippingService { + /** + * Create Pallet + * @returns CreatePalletResponse Successful Response + * @throws ApiError + */ + public static createPallet({ + dealId, + }: { + dealId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/shipping/pallet/{deal_id}', + path: { + 'deal_id': dealId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Delete Pallet + * @returns DeletePalletResponse Successful Response + * @throws ApiError + */ + public static deletePallet({ + palletId, + }: { + palletId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/shipping/pallet/{pallet_id}', + path: { + 'pallet_id': palletId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Update Shipping Product + * @returns UpdateShippingProductResponse Successful Response + * @throws ApiError + */ + public static updateShippingProduct({ + requestBody, + }: { + requestBody: UpdateShippingProductRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/shipping/product', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Delete Shipping Product + * @returns DeleteShippingProductResponse Successful Response + * @throws ApiError + */ + public static deleteShippingProduct({ + shippingProductId, + }: { + shippingProductId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/shipping/product/{shipping_product_id}', + path: { + 'shipping_product_id': shippingProductId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Update Box + * @returns UpdateBoxResponse Successful Response + * @throws ApiError + */ + public static updateBox({ + requestBody, + }: { + requestBody: UpdateBoxRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/shipping/box', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Delete Box + * @returns DeleteBoxResponse Successful Response + * @throws ApiError + */ + public static deleteBox({ + boxId, + }: { + boxId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/shipping/box/{box_id}', + path: { + 'box_id': boxId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Generate Deal Qr Code + * @returns any Successful Response + * @throws ApiError + */ + public static getDealQrCodePdf({ + dealId, + }: { + dealId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/shipping/pdf/deal/{deal_id}', + path: { + 'deal_id': dealId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Generate Deal Qr Code + * @returns any Successful Response + * @throws ApiError + */ + public static getPalletsPdf({ + dealId, + }: { + dealId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/shipping/pdf/pallets/{deal_id}', + path: { + 'deal_id': dealId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } +} diff --git a/src/modals/modals.ts b/src/modals/modals.ts index c84c907..3a17a85 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -24,6 +24,7 @@ import ServicePriceCategoryForm from "../pages/ServicesPage/modals/ServicePriceC import ScanningModal from "./ScanningModal/ScanningModal.tsx"; import ExpenseFormModal from "../pages/AdminPage/tabs/Expenses/modals/ExpenseFormModal.tsx"; import ExpenseTagsModal from "../pages/AdminPage/tabs/Expenses/modals/ExpenseTagsModal.tsx"; +import ShippingProductModal from "../pages/LeadsPage/tabs/ShippingTab/modals/ShippingProductModal.tsx"; export const modals = { enterDeadline: EnterDeadlineModal, @@ -52,4 +53,5 @@ export const modals = { scanningModal: ScanningModal, expenseFormModal: ExpenseFormModal, expenseTagsModal: ExpenseTagsModal, + shippingProductModal: ShippingProductModal, }; diff --git a/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx b/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx index 46ce31d..d1005ff 100644 --- a/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx +++ b/src/pages/LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx @@ -1,13 +1,14 @@ import { Box, Drawer, rem, Tabs } from "@mantine/core"; import { FC, useEffect } from "react"; import { useDealPageContext } from "../../contexts/DealPageContext.tsx"; -import { IconBox, IconCalendarUser, IconSettings, IconUser } from "@tabler/icons-react"; +import { IconBox, IconCalendarUser, IconCubeSend, IconSettings, IconUser } from "@tabler/icons-react"; import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx"; import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx"; import { useQueryClient } from "@tanstack/react-query"; import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx"; import { motion } from "framer-motion"; import ClientTab from "./tabs/ClientTab.tsx"; +import ShippingTab from "../../tabs/ShippingTab/ShippingTab.tsx"; // import styles from './DealEditDrawer.module.css'; // const useDealServicesTableState = () => { @@ -348,6 +349,11 @@ const DealEditDrawer: FC = () => { leftSection={}> Товары и услуги + }> + Отгрузка + { + + + + + + + ); diff --git a/src/pages/LeadsPage/tabs/ShippingTab/ShippingTab.tsx b/src/pages/LeadsPage/tabs/ShippingTab/ShippingTab.tsx new file mode 100644 index 0000000..ee3de4c --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/ShippingTab.tsx @@ -0,0 +1,51 @@ +import ShippingTree from "./components/ShippingTree.tsx"; +import { Button, Group, rem, ScrollArea, Stack } from "@mantine/core"; +import useShipping from "./hooks/useShipping.tsx"; +import useShippingQrs from "./hooks/useShippingQrs.tsx"; +import { IconPrinter, IconQrcode } from "@tabler/icons-react"; +import { ReactNode } from "react"; + + +const ShippingTab = () => { + const { + onCreateBoxInDealClick, + onCreatePalletClick, + } = useShipping(); + + const { + onGetDealQrPdfClick, + onGetPalletsPdfClick, + onGetBoxesPdfClick, + } = useShippingQrs(); + + const getButton = (label: string, func: () => void, icon?: ReactNode) => { + return ( + + ); + }; + + return ( + + + {getButton("Добавить паллет", onCreatePalletClick)} + {getButton("Добавить короб", onCreateBoxInDealClick)} + {getButton("Сделка", onGetDealQrPdfClick, )} + {getButton("Паллеты", onGetPalletsPdfClick, )} + {getButton("Короба", onGetBoxesPdfClick, )} + + + + + + ); +}; + +export default ShippingTab; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/components/BoxesTable.tsx b/src/pages/LeadsPage/tabs/ShippingTab/components/BoxesTable.tsx new file mode 100644 index 0000000..cfeb4bf --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/components/BoxesTable.tsx @@ -0,0 +1,87 @@ +import useShippingTableColumns from "../hooks/shippingTableColumns.tsx"; +import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; +import { BoxSchema, ShippingService } from "../../../../../client"; +import { ActionIcon, Flex, Tooltip } from "@mantine/core"; +import { IconEdit, IconTrash } from "@tabler/icons-react"; +import { MRT_TableOptions } from "mantine-react-table"; +import { modals } from "@mantine/modals"; +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; +import useUpdateDeal from "../hooks/useUpdateDeal.tsx"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; + + +type Props = { + items: BoxSchema[]; +} + +const BoxesTable = ({ items }: Props) => { + const columns = useShippingTableColumns(); + const { update } = useUpdateDeal(); + const { selectedDeal: deal } = useDealPageContext(); + + const onDeleteClick = (box: BoxSchema) => { + ShippingService.deleteBox({ + boxId: box.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + if (ok) update(); + }) + .catch(err => console.log(err)); + }; + + const onEditClick = (box: BoxSchema) => { + if (!deal) return; + modals.openContextModal({ + modal: "shippingProductModal", + title: "Редактирование короба", + withCloseButton: false, + innerProps: { + deal, + updateOnSubmit: update, + isBox: true, + shippingData: { + boxId: box.id, + productId: box.product.id, + quantity: box.quantity, + }, + }, + }); + }; + + return ( + ( + + + onDeleteClick(row.original)} + variant={"default"}> + + + + + onEditClick(row.original)} + variant={"default"}> + + + + + ), + } as MRT_TableOptions + } + /> + ); +}; + +export default BoxesTable; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/components/ShippingProductsTable.tsx b/src/pages/LeadsPage/tabs/ShippingTab/components/ShippingProductsTable.tsx new file mode 100644 index 0000000..e26903a --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/components/ShippingProductsTable.tsx @@ -0,0 +1,87 @@ +import useShippingTableColumns from "../hooks/shippingTableColumns.tsx"; +import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; +import { ShippingProductSchema, ShippingService } from "../../../../../client"; +import { ActionIcon, Flex, Tooltip } from "@mantine/core"; +import { IconEdit, IconTrash } from "@tabler/icons-react"; +import { MRT_TableOptions } from "mantine-react-table"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; +import useUpdateDeal from "../hooks/useUpdateDeal.tsx"; +import { modals } from "@mantine/modals"; +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; + + +type Props = { + items: ShippingProductSchema[]; +} + +const ShippingProductsTable = ({ items }: Props) => { + const columns = useShippingTableColumns(); + const { update } = useUpdateDeal(); + const { selectedDeal: deal } = useDealPageContext(); + + const onDeleteClick = (shippingProduct: ShippingProductSchema) => { + ShippingService.deleteShippingProduct({ + shippingProductId: shippingProduct.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + update(); + }) + .catch(err => console.log(err)); + }; + + const onEditClick = (shippingProduct: ShippingProductSchema) => { + if (!deal) return; + modals.openContextModal({ + modal: "shippingProductModal", + title: "Редактирование товара на паллете", + withCloseButton: false, + innerProps: { + deal, + updateOnSubmit: update, + isBox: false, + shippingData: { + shippingProductId: shippingProduct.id, + productId: shippingProduct.product.id, + quantity: shippingProduct.quantity, + } + }, + }); + }; + + return ( + ( + + + onDeleteClick(row.original)} + variant={"default"}> + + + + + onEditClick(row.original)} + variant={"default"}> + + + + + ), + } as MRT_TableOptions + } + /> + ); +}; + +export default ShippingProductsTable; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/components/ShippingTree.tsx b/src/pages/LeadsPage/tabs/ShippingTab/components/ShippingTree.tsx new file mode 100644 index 0000000..a0e176a --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/components/ShippingTree.tsx @@ -0,0 +1,151 @@ +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; +import { Accordion, ActionIcon, Button, Center, Group, rem, Stack, Title, Tooltip } from "@mantine/core"; +import { BoxSchema, PalletSchema, ShippingProductSchema } from "../../../../../client"; +import ShippingProductsTable from "./ShippingProductsTable.tsx"; +import BoxesTable from "./BoxesTable.tsx"; +import { IconBox, IconPlus, IconSpace, IconTrash } from "@tabler/icons-react"; +import useShipping from "../hooks/useShipping.tsx"; + +const ShippingTree = () => { + const { selectedDeal: deal } = useDealPageContext(); + + const { + onCreateBoxInPallet, + onCreateShippingProduct, + onDeletePalletClick, + palletIds, + } = useShipping(); + + const sortById = (data?: PalletSchema[] | BoxSchema[] | ShippingProductSchema[]) => { + return data?.sort((a, b) => a.id - b.id); + }; + + const getPallets = () => { + const sortedPallets = sortById(deal?.pallets) as PalletSchema[]; + const pallets = sortedPallets?.map(((pallet, index) => { + palletIds.push(pallet.id.toString()); + return ( + +
+ }> + Паллет {index + 1} + + {removePalletButton(pallet.id)} +
+ + {getPalletContent(pallet)} + +
+ ); + })) ?? []; + + if (deal?.boxes && deal?.boxes.length > 0) { + const boxes = deal?.boxes.sort((b1, b2) => (b1.id - b2.id)); + const itemValue = "noPallets"; + const boxesWithoutPallet = ( + + }> + Короба без паллетов + + + + + + ); + pallets.unshift(boxesWithoutPallet); + palletIds.push(itemValue); + } + + return pallets; + }; + + const removePalletButton = (palletId: number) => { + return ( + + onDeletePalletClick(palletId)} + mx={"md"} + > + + + + ); + }; + + const createBoxOrShippingProductButton = (palletId: number, isBox: boolean) => { + const createButtonLabel = isBox ? "Добавить короб" : "Добавить товар"; + + return ( + + ); + }; + + const getPalletContent = (pallet: PalletSchema) => { + const isEmpty = pallet.boxes.length === 0 && pallet.shippingProducts.length === 0; + const isBox = pallet.boxes.length > 0; + const title = isEmpty ? "Пустой" : isBox ? "Короба" : "Товары"; + + let palletButtons; + if (isEmpty) { + palletButtons = [ + createBoxOrShippingProductButton(pallet.id, true), + createBoxOrShippingProductButton(pallet.id, false), + ]; + } else { + palletButtons = [ + createBoxOrShippingProductButton(pallet.id, isBox), + ]; + } + + const boxes = sortById(pallet.boxes) as BoxSchema[]; + const shippingProducts = sortById(pallet.shippingProducts) as ShippingProductSchema[]; + + let table; + if (!isEmpty) { + if (isBox) { + table = (); + } else { + table = (); + } + } + + return ( + + + {title} + + {...palletButtons} + + + {table} + + ); + }; + + return ( + + {getPallets()} + + ); +}; + +export default ShippingTree; \ No newline at end of file diff --git a/src/pages/LeadsPage/tabs/ShippingTab/hooks/shippingTableColumns.tsx b/src/pages/LeadsPage/tabs/ShippingTab/hooks/shippingTableColumns.tsx new file mode 100644 index 0000000..9c8f4b9 --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/hooks/shippingTableColumns.tsx @@ -0,0 +1,28 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef, MRT_RowData } from "mantine-react-table"; + +const useShippingTableColumns = () => { + return useMemo[]>( + () => [ + { + header: "Название", + accessorKey: "product.name", + }, + { + header: "Артикул", + accessorKey: "product.article", + }, + { + header: "Размер", + accessorKey: "product.size", + }, + { + header: "Количество", + accessorKey: "quantity", + }, + ], + [], + ); +}; + +export default useShippingTableColumns; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/hooks/useShipping.tsx b/src/pages/LeadsPage/tabs/ShippingTab/hooks/useShipping.tsx new file mode 100644 index 0000000..a8eb5cb --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/hooks/useShipping.tsx @@ -0,0 +1,108 @@ +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; +import { ShippingService } from "../../../../../client"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; +import { modals } from "@mantine/modals"; +import { Text } from "@mantine/core"; +import useUpdateDeal from "./useUpdateDeal.tsx"; + +const useShipping = () => { + const { selectedDeal: deal } = useDealPageContext(); + const { update } = useUpdateDeal(); + const palletIds: string[] = []; + + const onCreatePalletClick = () => { + if (!deal) return; + + ShippingService.createPallet({ + dealId: deal.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + update(); + }) + .catch(err => console.log(err)); + }; + + const onDeletePallet = (palletId: number) => { + ShippingService.deletePallet({ + palletId: palletId, + }) + .then(({ ok, message }) => { + if (!ok) notifications.guess(ok, { message }); + update(); + }) + .catch(err => console.log(err)); + }; + + const onDeletePalletClick = (palletId: number) => { + if (!deal) return; + modals.openConfirmModal({ + title: "Удаление паллета", + children: Вы уверены что хотите удалить паллет?, + labels: { confirm: "Да", cancel: "Нет" }, + confirmProps: { color: "red" }, + onConfirm: () => onDeletePallet(palletId), + }); + }; + + const openShippingModal = ( + title: string, + isBox: boolean, + palletId?: number, + dealId?: number, + ) => { + if (!deal) return; + modals.openContextModal({ + modal: "shippingProductModal", + title, + withCloseButton: false, + innerProps: { + deal, + updateOnSubmit: update, + isBox, + shippingData: { + dealId, + palletId, + productId: null, + quantity: null, + }, + }, + }); + }; + + const onCreateBoxInDealClick = () => { + openShippingModal( + "Добавление короба", + true, + undefined, + deal?.id, + ); + }; + + const onCreateBoxInPallet = (palletId: number) => { + openShippingModal( + "Добавление короба", + true, + palletId, + ); + }; + + const onCreateShippingProduct = (palletId: number) => { + openShippingModal( + "Добавление товара на паллет", + false, + palletId, + ); + }; + + return { + onCreateBoxInDealClick, + onCreateBoxInPallet, + onCreateShippingProduct, + onCreatePalletClick, + onDeletePalletClick, + palletIds, + }; +}; + +export default useShipping; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/hooks/useShippingQrs.tsx b/src/pages/LeadsPage/tabs/ShippingTab/hooks/useShippingQrs.tsx new file mode 100644 index 0000000..b86e9cb --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/hooks/useShippingQrs.tsx @@ -0,0 +1,35 @@ +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; + + +const useShippingQrs = () => { + const { selectedDeal: deal } = useDealPageContext(); + + const basePdfUrl = `${import.meta.env.VITE_API_URL}/shipping/pdf`; + + const getPdf = (url: string) => { + if (!deal) return; + const pdfWindow = window.open(url); + if (!pdfWindow) return; + pdfWindow.print(); + }; + + const onGetDealQrPdfClick = () => { + getPdf(`${basePdfUrl}/deal/${deal?.id}`); + }; + + const onGetPalletsPdfClick = () => { + getPdf(`${basePdfUrl}/pallets/${deal?.id}`); + }; + + const onGetBoxesPdfClick = () => { + getPdf(`${basePdfUrl}/boxes/${deal?.id}`); + }; + + return { + onGetDealQrPdfClick, + onGetPalletsPdfClick, + onGetBoxesPdfClick, + }; +}; + +export default useShippingQrs; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/hooks/useUpdateDeal.tsx b/src/pages/LeadsPage/tabs/ShippingTab/hooks/useUpdateDeal.tsx new file mode 100644 index 0000000..17bf1e5 --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/hooks/useUpdateDeal.tsx @@ -0,0 +1,18 @@ +import { useDealPageContext } from "../../../contexts/DealPageContext.tsx"; +import { DealService } from "../../../../../client"; + +const useUpdateDeal = () => { + const { selectedDeal: deal, setSelectedDeal } = useDealPageContext(); + + const update = () => { + if (!deal) return; + DealService.getDealById({ dealId: deal.id }) + .then(data => { + setSelectedDeal(data); + }); + }; + + return { update }; +}; + +export default useUpdateDeal; diff --git a/src/pages/LeadsPage/tabs/ShippingTab/modals/ShippingProductModal.tsx b/src/pages/LeadsPage/tabs/ShippingTab/modals/ShippingProductModal.tsx new file mode 100644 index 0000000..be4b677 --- /dev/null +++ b/src/pages/LeadsPage/tabs/ShippingTab/modals/ShippingProductModal.tsx @@ -0,0 +1,156 @@ +import { useForm } from "@mantine/form"; +import { ContextModalProps } from "@mantine/modals"; +import { Button, Flex, NumberInput, rem, Select, Text } from "@mantine/core"; +import getRestProducts from "../utils/getRestProducts.tsx"; +import { + CreateBoxInDealSchema, + CreateBoxInPalletSchema, + CreateShippingProductSchema, + DealProductSchema, + DealSchema, + ShippingService, + UpdateBoxSchema, + UpdateShippingProductSchema, +} from "../../../../../client"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; +import { ShippingData, ShippingModalForm, ShippingProductOption } from "../types/ShippingProductData.tsx"; +import { useEffect, useState } from "react"; + + + +type Props = { + updateOnSubmit: () => void; + deal: DealSchema; + isBox: boolean; + shippingData: Partial; +} + +const ShippingProductModal = ({ + context, + id, + innerProps, + }: ContextModalProps) => { + const [restProducts, setRestProducts] = useState>(new Map()); + const [restProductsSelectData, setRestProductSelectData] = useState([]); + + const getRestProductQuantity = () => { + if (form.values.productId) { + const restProduct = restProducts.get(Number(form.values.productId)); + if (restProduct) { + return restProduct.quantity; + } + } + return 10000; + }; + + const initialValues: ShippingModalForm = { + quantity: innerProps.shippingData.quantity ?? 0, + productId: innerProps.shippingData.productId, + }; + const form = useForm({ + initialValues, + validate: { + productId: productId => !productId && "Необходимо выбрать товар", + quantity: quantity => quantity > getRestProductQuantity() ? "Слишком много товара" : + quantity === 0 && "Слишком мало товара", + }, + }); + + useEffect(() => { + const data = getRestProducts({ + deal: innerProps.deal, + unaccountedValues: innerProps.shippingData as UpdateShippingProductSchema | UpdateBoxSchema, + }); + setRestProducts(data.restProducts); + setRestProductSelectData(data.restProductsSelectData); + }, [innerProps.deal]); + + const updateBox = () => { + const data = { + ...innerProps.shippingData, + ...form.values, + } as CreateBoxInPalletSchema | CreateBoxInDealSchema | UpdateBoxSchema; + + ShippingService.updateBox({ + requestBody: { data }, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message: message }); + innerProps.updateOnSubmit(); + if (ok) context.closeContextModal(id); + }) + .catch(err => console.log(err)); + }; + + const updateShippingProduct = () => { + const data = { + ...innerProps.shippingData, + ...form.values, + } as CreateShippingProductSchema | UpdateShippingProductSchema; + + ShippingService.updateShippingProduct({ + requestBody: { data }, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message: message }); + innerProps.updateOnSubmit(); + if (ok) context.closeContextModal(id); + }) + .catch(err => console.log(err)); + }; + + const onSubmit = () => { + if (innerProps.isBox) { + updateBox(); + } else { + updateShippingProduct(); + } + }; + + const getRestQuantityText = () => { + if (!form.values.productId) return; + + const restQuantityInDeal = getRestProductQuantity(); + const restQuantity = restQuantityInDeal - form.values.quantity; + + if (restQuantity >= 0) { + return Осталось распределить {restQuantity} шт.; + } + return Введено слишком большое количество.
Доступно {restQuantityInDeal} шт.
; + }; + + return ( +
onSubmit())}> + +