feat: shipping warehouse page

This commit is contained in:
2024-08-25 04:19:05 +03:00
parent 76d37d54d9
commit 9b71738f24
24 changed files with 475 additions and 9 deletions

4
.env
View File

@@ -1,3 +1,3 @@
VITE_BOT_NAME=DencoFulfillmentTestBot
VITE_BOT_ID=6467915685
VITE_BOT_NAME=DencoCrmTestBot
VITE_BOT_ID=7510759553
VITE_API_URL=http://test.crm.denco.store/api

View File

@@ -24,6 +24,7 @@ export type { BarcodeTemplateUpdateResponse } from './models/BarcodeTemplateUpda
export type { BaseEnumListSchema } from './models/BaseEnumListSchema';
export type { BaseEnumSchema } from './models/BaseEnumSchema';
export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema';
export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema';
export type { BillPaymentStatus } from './models/BillPaymentStatus';
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
export type { Body_upload_product_image } from './models/Body_upload_product_image';
@@ -52,6 +53,8 @@ export type { CreatePositionResponse } from './models/CreatePositionResponse';
export type { CreateServiceKitSchema } from './models/CreateServiceKitSchema';
export type { CreateServicesKitRequest } from './models/CreateServicesKitRequest';
export type { CreateServicesKitResponse } from './models/CreateServicesKitResponse';
export type { CreateShippingWarehouseRequest } from './models/CreateShippingWarehouseRequest';
export type { CreateShippingWarehouseResponse } from './models/CreateShippingWarehouseResponse';
export type { CreateUserRequest } from './models/CreateUserRequest';
export type { CreateUserResponse } from './models/CreateUserResponse';
export type { DealAddKitRequest } from './models/DealAddKitRequest';
@@ -110,6 +113,8 @@ export type { DeletePayRateRequest } from './models/DeletePayRateRequest';
export type { DeletePayRateResponse } from './models/DeletePayRateResponse';
export type { DeletePositionRequest } from './models/DeletePositionRequest';
export type { DeletePositionResponse } from './models/DeletePositionResponse';
export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest';
export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse';
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';
export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse';
@@ -179,6 +184,8 @@ export type { UpdatePayRateResponse } from './models/UpdatePayRateResponse';
export type { UpdateServiceKitSchema } from './models/UpdateServiceKitSchema';
export type { UpdateServicesKitRequest } from './models/UpdateServicesKitRequest';
export type { UpdateServicesKitResponse } from './models/UpdateServicesKitResponse';
export type { UpdateShippingWarehouseRequest } from './models/UpdateShippingWarehouseRequest';
export type { UpdateShippingWarehouseResponse } from './models/UpdateShippingWarehouseResponse';
export type { UpdateTimeTrackingRecordRequest } from './models/UpdateTimeTrackingRecordRequest';
export type { UpdateTimeTrackingRecordResponse } from './models/UpdateTimeTrackingRecordResponse';
export type { UpdateUserRequest } from './models/UpdateUserRequest';

View File

@@ -0,0 +1,8 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type BaseShippingWarehouseSchema = {
name: string;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { BaseShippingWarehouseSchema } from './BaseShippingWarehouseSchema';
export type CreateShippingWarehouseRequest = {
shippingWarehouse: BaseShippingWarehouseSchema;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type CreateShippingWarehouseResponse = {
ok: boolean;
message: string;
};

View File

@@ -14,5 +14,6 @@ export type DealSummary = {
totalPrice: number;
rank: number;
baseMarketplace?: (BaseMarketplaceSchema | null);
shipmentWarehouseId: (number | null);
};

View File

@@ -0,0 +1,8 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeleteShippingWarehouseRequest = {
shippingWarehouseId: number;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeleteShippingWarehouseResponse = {
ok: boolean;
message: string;
};

View File

@@ -3,7 +3,7 @@
/* tslint:disable */
/* eslint-disable */
export type ShippingWarehouseSchema = {
id: number;
name: string;
id: number;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
export type UpdateShippingWarehouseRequest = {
shippingWarehouse: ShippingWarehouseSchema;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type UpdateShippingWarehouseResponse = {
ok: boolean;
message: string;
};

View File

@@ -2,7 +2,13 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { CreateShippingWarehouseRequest } from '../models/CreateShippingWarehouseRequest';
import type { CreateShippingWarehouseResponse } from '../models/CreateShippingWarehouseResponse';
import type { DeleteShippingWarehouseRequest } from '../models/DeleteShippingWarehouseRequest';
import type { DeleteShippingWarehouseResponse } from '../models/DeleteShippingWarehouseResponse';
import type { GetAllShippingWarehousesResponse } from '../models/GetAllShippingWarehousesResponse';
import type { UpdateShippingWarehouseRequest } from '../models/UpdateShippingWarehouseRequest';
import type { UpdateShippingWarehouseResponse } from '../models/UpdateShippingWarehouseResponse';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
@@ -18,4 +24,64 @@ export class ShippingWarehouseService {
url: '/shipping-warehouse/get-all',
});
}
/**
* Create
* @returns CreateShippingWarehouseResponse Successful Response
* @throws ApiError
*/
public static createShippingWarehouse({
requestBody,
}: {
requestBody: CreateShippingWarehouseRequest,
}): CancelablePromise<CreateShippingWarehouseResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/shipping-warehouse/create',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Update
* @returns UpdateShippingWarehouseResponse Successful Response
* @throws ApiError
*/
public static updateShippingWarehouse({
requestBody,
}: {
requestBody: UpdateShippingWarehouseRequest,
}): CancelablePromise<UpdateShippingWarehouseResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/shipping-warehouse/update',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Delete
* @returns DeleteShippingWarehouseResponse Successful Response
* @throws ApiError
*/
public static deleteShippingWarehouse({
requestBody,
}: {
requestBody: DeleteShippingWarehouseRequest,
}): CancelablePromise<DeleteShippingWarehouseResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/shipping-warehouse/delete',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
}

View File

@@ -1,7 +1,7 @@
import {Center, Flex, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme} from '@mantine/core';
import {
IconBarcode,
IconBox,
IconBox, IconBuildingWarehouse,
IconCash,
IconDashboard,
IconFileBarcode,
@@ -71,6 +71,11 @@ const mockdata = [
icon: IconFileBarcode,
label: 'Штрихкоды',
href: '/barcode'
},
{
icon: IconBuildingWarehouse,
label: 'Склады отгрузки',
href: '/shipping_warehouses'
}
];

20
src/hooks/useCRUD.tsx Normal file
View File

@@ -0,0 +1,20 @@
type Props<T> = {
onCreate: (element: T) => void;
onChange: (element: T) => void;
onDelete: (element: T) => void;
};
export function useCRUD<T>(props: Props<T>) {
const onCreate = (element: T) => {
props.onCreate(element);
}
const onChange = (element: T) => {
props.onChange(element);
}
const onDelete = (element: T) => {
props.onDelete(element);
}
return {onCreate, onChange, onDelete};
}

View File

@@ -19,6 +19,7 @@ import CreatePaymentRecordModal from "../pages/AdminPage/modals/CreatePaymentRec
import ServiceKitModalForm from "../pages/ServicesPage/modals/ServicesKitModalForm.tsx";
import ServicesKitSelectModal from "./ServicesKitSelectModal/ServicesKitSelectModal.tsx";
import SelectDealProductsModal from "../pages/LeadsPage/modals/SelectDealProductsModal.tsx";
import ShippingWarehouseForm from "../pages/ShippingWarehousesPage/modals/ShippingWarehouseForm.tsx";
export const modals = {
enterDeadline: EnterDeadlineModal,
@@ -40,5 +41,6 @@ export const modals = {
createPaymentRecord: CreatePaymentRecordModal,
serviceKitModalForm: ServiceKitModalForm,
servicesKitSelectModal: ServicesKitSelectModal,
selectDealProductsModal: SelectDealProductsModal
selectDealProductsModal: SelectDealProductsModal,
shippingWarehouseForm: ShippingWarehouseForm
}

View File

@@ -8,9 +8,12 @@ import {IconEdit} from "@tabler/icons-react";
import {MRT_TableOptions} from "mantine-react-table";
import {useDealPageContext} from "../../../LeadsPage/contexts/DealPageContext.tsx";
type Props = CRUDTableProps<DealSummary>;
type RestProps = {
viewOnly?: boolean;
}
type Props = CRUDTableProps<DealSummary> & RestProps;
const DealsTable: FC<Props> = ({items}) => {
const DealsTable: FC<Props> = ({items, viewOnly = true}) => {
const columns = useDealsTableColumns();
const {setSelectedDeal} = useDealPageContext();
const onEditClick = (dealSummary: DealSummary) => {
@@ -26,14 +29,15 @@ const DealsTable: FC<Props> = ({items}) => {
restProps={{
enableSorting: true,
enableColumnActions: false,
enablePagination: true,
enableBottomToolbar: true,
enablePagination: !viewOnly,
enableBottomToolbar: !viewOnly,
paginationDisplayMode: "pages",
enableRowActions: true,
renderRowActions: ({row}) => (
<Flex gap="md">
<Tooltip label="Редактировать">
<ActionIcon
disabled={viewOnly}
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit/>

View File

@@ -0,0 +1,83 @@
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
import {DealSummary, ShippingWarehouseSchema} from "../../../../client";
import {FC} from "react";
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
import useShippingWarehouseTableColumns from "./columns.tsx";
import {ActionIcon, Flex, Text, Tooltip} from "@mantine/core";
import {IconEdit, IconTrash} from "@tabler/icons-react";
import {MRT_TableOptions} from "mantine-react-table";
import {modals} from "@mantine/modals";
type RestProps = {
summaries: DealSummary[];
};
type Props = CRUDTableProps<ShippingWarehouseSchema> & RestProps;
const ShippingWarehouseTable: FC<Props> = ({
onChange,
onDelete,
items,
summaries
}) => {
const columns = useShippingWarehouseTableColumns();
const onEditClick = (element: ShippingWarehouseSchema) => {
if (!onChange) return;
modals.openContextModal({
modal: "shippingWarehouseForm",
title: 'Редактирование склада отгрузки',
withCloseButton: false,
innerProps: {
element,
onChange,
summaries: summaries.filter(s => s.shipmentWarehouseId === element.id).slice(0, 5)
},
size: "xl"
})
}
const onDeleteClick = (element: ShippingWarehouseSchema) => {
if (!onDelete) return;
modals.openConfirmModal({
title: 'Удаление склада отгрузки',
children: (
<Text size="sm">
Вы уверены что хотите удалить склад отгрузки {element.name}
</Text>
),
labels: {confirm: 'Да', cancel: "Нет"},
confirmProps: {color: 'red'},
onConfirm: () => {
onDelete(element);
}
});
}
return (
<BaseTable
data={items}
columns={columns}
restProps={{
enableColumnActions: false,
enableSorting: false,
enableRowActions: true,
renderRowActions: ({row}) => (
<Flex gap="md">
<Tooltip label="Редактировать">
<ActionIcon
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit/>
</ActionIcon>
</Tooltip>
<Tooltip label="Удалить">
<ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
<IconTrash/>
</ActionIcon>
</Tooltip>
</Flex>
)
} as MRT_TableOptions<ShippingWarehouseSchema>}
/>
)
}
export default ShippingWarehouseTable;

View File

@@ -0,0 +1,18 @@
import {useMemo} from "react";
import {MRT_ColumnDef} from "mantine-react-table";
import {ShippingWarehouseSchema} from "../../../../client";
const useShippingWarehouseTableColumns = () => {
return useMemo<MRT_ColumnDef<ShippingWarehouseSchema>[]>(() => [
{
accessorKey:"id",
header:"ID"
},
{
accessorKey:"name",
header:"Название"
}
], []);
}
export default useShippingWarehouseTableColumns;

View File

@@ -0,0 +1 @@
export {ShippingWarehousesPage} from './ui/ShippingWarehousesPage.tsx';

View File

@@ -0,0 +1,64 @@
import BaseFormModal, {CreateEditFormProps} from "../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
import {DealSummary, ShippingWarehouseSchema} from "../../../client";
import {ContextModalProps} from "@mantine/modals";
import {useForm} from "@mantine/form";
import {Input, TextInput} from "@mantine/core";
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
import {DealPageContextProvider} from "../../LeadsPage/contexts/DealPageContext.tsx";
import DealEditDrawer from "../../LeadsPage/drawers/DealEditDrawer/DealEditDrawer.tsx";
type RestProps = {
summaries: DealSummary[];
}
type Props = CreateEditFormProps<ShippingWarehouseSchema> & RestProps;
const ShippingWarehouseForm = ({
context,
innerProps,
id
}: ContextModalProps<Props>) => {
const isEditing = 'onChange' in innerProps;
const form = useForm<ShippingWarehouseSchema>({
initialValues: isEditing ? innerProps.element : {
id: -1,
name: ""
}
});
return (
<DealPageContextProvider>
<BaseFormModal
{...innerProps}
closeOnSubmit
form={form}
onClose={() => context.closeContextModal(id)}
>
<BaseFormModal.Body>
<>
<TextInput
label={"Склад отгрузки"}
placeholder={"Введите название склада отгрузки"}
{...form.getInputProps("name")}
/>
{isEditing &&
<Input.Wrapper
label={"Последние 5 сделок"}
error={form.getInputProps("phoneNumber").error}
>
<DealsTable
viewOnly={true}
items={innerProps.summaries}/>
</Input.Wrapper>
}
</>
</BaseFormModal.Body>
</BaseFormModal>
<DealEditDrawer
/>
</DealPageContextProvider>
)
}
export default ShippingWarehouseForm;

View File

@@ -0,0 +1,17 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
gap: rem(10);
}
.top-panel {
padding: rem(5);
gap: rem(10);
display: flex;
}
.top-panel-last-item {
margin-left: auto;
}

View File

@@ -0,0 +1,91 @@
import styles from "../../ServicesPage/ui/ServicesPage.module.css";
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import ShippingWarehousesTable from "../components/ShippingWarehousesTable/ShippingWarehousesTable.tsx";
import useShippingWarehousesList
from "../../../components/Selects/ShippingWarehouseAutocomplete/hooks/useShippingWarehousesList.tsx";
import {Button} from "@mantine/core";
import {modals} from "@mantine/modals";
import {useCRUD} from "../../../hooks/useCRUD.tsx";
import {ShippingWarehouseSchema, ShippingWarehouseService} from "../../../client";
import {notifications} from "../../../shared/lib/notifications.ts";
import {useDealSummariesFull} from "../../LeadsPage/hooks/useDealSummaries.tsx";
export const ShippingWarehousesPage = () => {
const {shippingWarehouses, refetch} = useShippingWarehousesList();
const {objects: summaries} = useDealSummariesFull();
const crud = useCRUD<ShippingWarehouseSchema>({
onChange: (element) => {
ShippingWarehouseService
.updateShippingWarehouse({
requestBody: {
shippingWarehouse: element
}
})
.then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
})
},
onCreate: (element) => {
ShippingWarehouseService
.createShippingWarehouse({
requestBody: {
shippingWarehouse: element
}
})
.then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
})
},
onDelete: (element) => {
ShippingWarehouseService
.deleteShippingWarehouse({
requestBody: {
shippingWarehouseId: element.id
}
})
.then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
})
}
});
const onCreateClick = () => {
modals.openContextModal({
modal: "shippingWarehouseForm",
title: 'Редактирование склада отгрузки',
withCloseButton: false,
innerProps: {
onCreate: crud.onCreate,
summaries: []
},
})
}
return (
<div className={styles['container']}>
<PageBlock>
<div className={styles['top-panel']}>
<Button
onClick={onCreateClick}
variant={"default"}
>
Создать склад отгрузки
</Button>
</div>
</PageBlock>
<PageBlock>
<ShippingWarehousesTable
items={shippingWarehouses}
summaries={summaries}
{...crud}
/>
</PageBlock>
</div>
)
}

View File

@@ -18,6 +18,7 @@ import { Route as DealsDealIdImport } from './routes/deals.$dealId'
// Create Virtual Routes
const TestLazyImport = createFileRoute('/test')()
const ShippingwarehousesLazyImport = createFileRoute('/shipping_warehouses')()
const ServicesLazyImport = createFileRoute('/services')()
const ProductsLazyImport = createFileRoute('/products')()
const LoginLazyImport = createFileRoute('/login')()
@@ -34,6 +35,13 @@ const TestLazyRoute = TestLazyImport.update({
getParentRoute: () => rootRoute,
} as any).lazy(() => import('./routes/test.lazy').then((d) => d.Route))
const ShippingwarehousesLazyRoute = ShippingwarehousesLazyImport.update({
path: '/shipping_warehouses',
getParentRoute: () => rootRoute,
} as any).lazy(() =>
import('./routes/shipping_warehouses.lazy').then((d) => d.Route),
)
const ServicesLazyRoute = ServicesLazyImport.update({
path: '/services',
getParentRoute: () => rootRoute,
@@ -139,6 +147,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ServicesLazyImport
parentRoute: typeof rootRoute
}
'/shipping_warehouses': {
id: '/shipping_warehouses'
path: '/shipping_warehouses'
fullPath: '/shipping_warehouses'
preLoaderRoute: typeof ShippingwarehousesLazyImport
parentRoute: typeof rootRoute
}
'/test': {
id: '/test'
path: '/test'
@@ -167,6 +182,7 @@ export const routeTree = rootRoute.addChildren({
LoginLazyRoute,
ProductsLazyRoute,
ServicesLazyRoute,
ShippingwarehousesLazyRoute,
TestLazyRoute,
DealsDealIdRoute,
})
@@ -187,6 +203,7 @@ export const routeTree = rootRoute.addChildren({
"/login",
"/products",
"/services",
"/shipping_warehouses",
"/test",
"/deals/$dealId"
]
@@ -215,6 +232,9 @@ export const routeTree = rootRoute.addChildren({
"/services": {
"filePath": "services.lazy.tsx"
},
"/shipping_warehouses": {
"filePath": "shipping_warehouses.lazy.tsx"
},
"/test": {
"filePath": "test.lazy.tsx"
},

View File

@@ -0,0 +1,6 @@
import {createLazyFileRoute} from "@tanstack/react-router";
import {ShippingWarehousesPage} from "../pages/ShippingWarehousesPage";
export const Route = createLazyFileRoute('/shipping_warehouses')({
component: ShippingWarehousesPage
});