feat: warehouse places accounting
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { WarehouseManagementTabContextProvider } from "./placeType/contexts/WarehouseManagementTabContext.tsx";
|
||||
import { WmsPage, WmsSegmentedControl } from "./placeType/components/WmsSegmentedControl.tsx";
|
||||
import { useState } from "react";
|
||||
import PlaceTypesEditor from "./placeType/components/PlaceTypesEditor.tsx";
|
||||
import PlacesEditor from "./place/components/PlacesEditor.tsx";
|
||||
|
||||
const WarehouseManagementTab = () => {
|
||||
const [page, setPage] = useState(WmsPage.PLACE);
|
||||
|
||||
return (
|
||||
<WarehouseManagementTabContextProvider>
|
||||
<WmsSegmentedControl
|
||||
w={"100%"}
|
||||
value={page.toString()}
|
||||
onChange={event => {
|
||||
setPage(parseInt(event));
|
||||
}}
|
||||
/>
|
||||
{page === WmsPage.PLACE_TYPE ? (
|
||||
<PlaceTypesEditor />
|
||||
) : (
|
||||
<PlacesEditor />
|
||||
)}
|
||||
</WarehouseManagementTabContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarehouseManagementTab;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import { IconChevronRight, IconSpace } from "@tabler/icons-react";
|
||||
import clsx from "clsx";
|
||||
import classes from "../../../OrganizationalStructureTab/components/DepartmentsTree/DepartmentsTree.module.css";
|
||||
import { useState } from "react";
|
||||
import { PlaceSchema } from "../../../../../../client";
|
||||
import PlaceActions from "./PlaceActions.tsx";
|
||||
|
||||
type Props = {
|
||||
place: PlaceSchema;
|
||||
}
|
||||
|
||||
const Place = ({ place }: Props) => {
|
||||
const [placeTypeIds, setPlaceTypeIds] = useState<number[]>([]);
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
noHeader
|
||||
columns={[
|
||||
{
|
||||
accessor: "name",
|
||||
title: "Место",
|
||||
noWrap: true,
|
||||
render: ({ id, number, placeType }) => (
|
||||
<>
|
||||
<IconChevronRight
|
||||
className={clsx(classes.icon, classes.expandIcon, {
|
||||
[classes.expandIconRotated]: placeTypeIds?.includes(id),
|
||||
})}
|
||||
/>
|
||||
<IconSpace className={classes.icon} />
|
||||
<span>{placeType.name} {number}</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: (place) => (
|
||||
<PlaceActions place={place} />
|
||||
),
|
||||
},
|
||||
]}
|
||||
records={place.children?.sort((a, b) => a.id - b.id)}
|
||||
rowExpansion={{
|
||||
allowMultiple: true,
|
||||
expanded: { recordIds: placeTypeIds, onRecordIdsChange: setPlaceTypeIds },
|
||||
content: ({ record }) => (
|
||||
<Place place={record} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Place;
|
||||
@@ -0,0 +1,69 @@
|
||||
import { PlaceSchema } from "../../../../../../client";
|
||||
import { IconBoxAlignBottom, IconPlaylistAdd, IconQrcode, IconTrash } from "@tabler/icons-react";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import React, { ReactNode } from "react";
|
||||
import { useWarehouseManagementTabContext } from "../../placeType/contexts/WarehouseManagementTabContext.tsx";
|
||||
|
||||
|
||||
type Props = {
|
||||
place: PlaceSchema;
|
||||
}
|
||||
|
||||
const PlaceActions = ({ place }: Props) => {
|
||||
const { placeCrud, onCreatePlace, generateQrCode } = useWarehouseManagementTabContext();
|
||||
|
||||
const getAction = (
|
||||
label: string,
|
||||
func: () => void,
|
||||
icon: ReactNode,
|
||||
disabled: boolean = false,
|
||||
) => {
|
||||
return (
|
||||
<Tooltip label={label} key={label}>
|
||||
<ActionIcon
|
||||
variant={"default"}
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
func();
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
{icon}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const actions = [
|
||||
getAction(
|
||||
"Распечатать QR-код",
|
||||
() => generateQrCode(place, false),
|
||||
<IconQrcode />,
|
||||
),
|
||||
getAction(
|
||||
"Распечатать QR-код ",
|
||||
() => generateQrCode(place, true),
|
||||
<IconBoxAlignBottom />,
|
||||
),
|
||||
getAction(
|
||||
"Добавить",
|
||||
() => onCreatePlace(place),
|
||||
<IconPlaylistAdd />,
|
||||
place.placeType.childCount === 0,
|
||||
),
|
||||
getAction(
|
||||
"Удалить",
|
||||
() => placeCrud.onDelete && placeCrud.onDelete(place),
|
||||
<IconTrash />,
|
||||
place.children?.length !== 0,
|
||||
),
|
||||
];
|
||||
|
||||
return (
|
||||
<Flex gap={"md"} mx={"md"} direction={"row"}>
|
||||
{...actions}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceActions;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { FC } from "react";
|
||||
import { FlatPlaceTypeSchema } from "../../../../../../client";
|
||||
import ObjectSelect, { ObjectSelectProps } from "../../../../../../components/ObjectSelect/ObjectSelect.tsx";
|
||||
|
||||
type Props = Omit<
|
||||
ObjectSelectProps<FlatPlaceTypeSchema | null>,
|
||||
"getValueFn" | "getLabelFn"
|
||||
>;
|
||||
|
||||
const PlaceTypeSelect: FC<Props> = (props) => {
|
||||
return (
|
||||
<ObjectSelect
|
||||
getLabelFn={(placeType: FlatPlaceTypeSchema) => placeType.name}
|
||||
getValueFn={(placeType: FlatPlaceTypeSchema) => placeType.id.toString()}
|
||||
clearable
|
||||
searchable
|
||||
{...props}
|
||||
onClear={() => props.onChange(null)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceTypeSelect;
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Button, Group, Stack } from "@mantine/core";
|
||||
import { IconChevronRight, IconSpace } from "@tabler/icons-react";
|
||||
import clsx from "clsx";
|
||||
import classes from "../../../OrganizationalStructureTab/components/DepartmentsTree/DepartmentsTree.module.css";
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import { useState } from "react";
|
||||
import { useWarehouseManagementTabContext } from "../../placeType/contexts/WarehouseManagementTabContext.tsx";
|
||||
import PlaceActions from "./PlaceActions.tsx";
|
||||
import Place from "./Place.tsx";
|
||||
|
||||
const PlacesEditor = () => {
|
||||
const { places, onCreatePlace } = useWarehouseManagementTabContext();
|
||||
const [placeIds, setPlaceIds] = useState<number[]>([]);
|
||||
|
||||
return (
|
||||
<Stack w={"100%"} mt={"md"}>
|
||||
<Group>
|
||||
<Button
|
||||
variant={"default"}
|
||||
onClick={() => onCreatePlace()}
|
||||
>
|
||||
Добавить
|
||||
</Button>
|
||||
</Group>
|
||||
<DataTable
|
||||
noHeader
|
||||
withTableBorder
|
||||
columns={[
|
||||
{
|
||||
accessor: "name",
|
||||
title: "Место",
|
||||
noWrap: true,
|
||||
render: ({ id, number, placeType }) => (
|
||||
<>
|
||||
<IconChevronRight
|
||||
className={clsx(classes.icon, classes.expandIcon, {
|
||||
[classes.expandIconRotated]: placeIds?.includes(id),
|
||||
})}
|
||||
/>
|
||||
<IconSpace className={classes.icon} />
|
||||
<span>{placeType.name} {number}</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: (place) => (
|
||||
<PlaceActions place={place} />
|
||||
),
|
||||
},
|
||||
]}
|
||||
records={places.sort((a, b) => a.id - b.id)}
|
||||
rowExpansion={{
|
||||
allowMultiple: true,
|
||||
expanded: { recordIds: placeIds, onRecordIdsChange: setPlaceIds },
|
||||
content: ({ record }) => (
|
||||
<Place place={record} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlacesEditor;
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useCRUD } from "../../../../../../hooks/useCRUD.tsx";
|
||||
import { BasePlaceSchema, PlaceSchema, WmsService } from "../../../../../../client";
|
||||
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||
|
||||
|
||||
export type PlaceCrud = {
|
||||
onCreate: (element: BasePlaceSchema) => void,
|
||||
onDelete: (element: PlaceSchema) => void,
|
||||
onChange: (element: PlaceSchema) => void
|
||||
}
|
||||
|
||||
type Props = {
|
||||
fetchPlaces: () => void;
|
||||
}
|
||||
|
||||
const usePlacesCrud = ({ fetchPlaces }: Props): PlaceCrud => {
|
||||
return useCRUD<PlaceSchema, BasePlaceSchema>({
|
||||
onChange: (place: PlaceSchema) => {
|
||||
WmsService.editPlace({
|
||||
requestBody: { place },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
fetchPlaces();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onDelete: (place: PlaceSchema) => {
|
||||
WmsService.deletePlace({
|
||||
placeId: place.id,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
fetchPlaces();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onCreate: (place: BasePlaceSchema) => {
|
||||
WmsService.createPlace({
|
||||
requestBody: {
|
||||
place,
|
||||
},
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
fetchPlaces();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default usePlacesCrud;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { WmsService } from "../../../../../../client";
|
||||
import ObjectList from "../../../../../../hooks/objectList.tsx";
|
||||
|
||||
const usePlacesList = () =>
|
||||
ObjectList({
|
||||
queryFn: WmsService.getPlaces,
|
||||
getObjectsFn: response => response.places,
|
||||
queryKey: "getAllPlaces",
|
||||
});
|
||||
|
||||
export default usePlacesList;
|
||||
@@ -0,0 +1,65 @@
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { FlatPlaceTypeSchema, PlaceSchema } from "../../../../../../client";
|
||||
import { Button, Stack } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { PlaceCrud } from "../hooks/usePlacesCrud.tsx";
|
||||
import PlaceTypeSelect from "../components/PlaceTypeSelect.tsx";
|
||||
|
||||
type Props = {
|
||||
placeCrud: PlaceCrud;
|
||||
parent?: PlaceSchema;
|
||||
placeTypes: FlatPlaceTypeSchema[];
|
||||
}
|
||||
|
||||
type PlaceModalForm = {
|
||||
placeType: PlaceSchema | null;
|
||||
}
|
||||
|
||||
const PlaceModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const { parent, placeCrud, placeTypes } = innerProps;
|
||||
|
||||
const closeModal = () => {
|
||||
context.closeContextModal(id);
|
||||
};
|
||||
|
||||
const initialValues: PlaceModalForm = {
|
||||
placeType: null,
|
||||
};
|
||||
const form = useForm<PlaceModalForm>({
|
||||
initialValues,
|
||||
validate: {
|
||||
placeType: placeType => !placeType && "Необходимо указать тип",
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (values: PlaceModalForm) => {
|
||||
if (!values.placeType) return;
|
||||
placeCrud.onCreate({
|
||||
placeTypeId: values.placeType.id,
|
||||
parentId: parent?.id || null,
|
||||
});
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(values => onSubmit(values))}>
|
||||
<Stack>
|
||||
<PlaceTypeSelect
|
||||
label={"Тип места на складе"}
|
||||
{...form.getInputProps("placeType")}
|
||||
data={placeTypes}
|
||||
/>
|
||||
|
||||
<Button variant={"default"} type={"submit"}>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceModal;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { PlaceTypeSchema } from "../../../../../../client";
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import { IconChevronRight, IconSpace } from "@tabler/icons-react";
|
||||
import clsx from "clsx";
|
||||
import classes from "../../../OrganizationalStructureTab/components/DepartmentsTree/DepartmentsTree.module.css";
|
||||
import { useState } from "react";
|
||||
import PlaceTypeActions from "./PlaceTypeActions.tsx";
|
||||
|
||||
type Props = {
|
||||
placeType: PlaceTypeSchema;
|
||||
}
|
||||
|
||||
const PlaceType = ({ placeType }: Props) => {
|
||||
const [placeTypeIds, setPlaceTypeIds] = useState<number[]>([]);
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
noHeader
|
||||
columns={[
|
||||
{
|
||||
accessor: "name",
|
||||
title: "Место",
|
||||
noWrap: true,
|
||||
render: ({ id, name }) => (
|
||||
<>
|
||||
<IconChevronRight
|
||||
className={clsx(classes.icon, classes.expandIcon, {
|
||||
[classes.expandIconRotated]: placeTypeIds?.includes(id),
|
||||
})}
|
||||
/>
|
||||
<IconSpace className={classes.icon} />
|
||||
<span>{name}</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: (placeType) => (
|
||||
<PlaceTypeActions placeType={placeType} />
|
||||
),
|
||||
},
|
||||
]}
|
||||
records={placeType.children?.sort((a, b) => a.id - b.id)}
|
||||
rowExpansion={{
|
||||
allowMultiple: true,
|
||||
expanded: { recordIds: placeTypeIds, onRecordIdsChange: setPlaceTypeIds },
|
||||
content: ({ record }) => (
|
||||
<PlaceType placeType={record} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceType;
|
||||
@@ -0,0 +1,84 @@
|
||||
import { PlaceTypeSchema } from "../../../../../../client";
|
||||
import { IconEdit, IconPlaylistAdd, IconTrash } from "@tabler/icons-react";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import React, { ReactNode } from "react";
|
||||
import { useWarehouseManagementTabContext } from "../contexts/WarehouseManagementTabContext.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
|
||||
|
||||
type Props = {
|
||||
placeType: PlaceTypeSchema;
|
||||
}
|
||||
|
||||
const PlaceTypeActions = ({ placeType }: Props) => {
|
||||
const { placeTypeCrud } = useWarehouseManagementTabContext();
|
||||
|
||||
const getAction = (
|
||||
label: string,
|
||||
func: () => void,
|
||||
icon: ReactNode,
|
||||
disabled: boolean = false,
|
||||
) => {
|
||||
return (
|
||||
<Tooltip label={label} key={label}>
|
||||
<ActionIcon
|
||||
disabled={disabled}
|
||||
variant={"default"}
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
func();
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const actions = [
|
||||
getAction(
|
||||
"Добавить",
|
||||
() => {
|
||||
modals.openContextModal({
|
||||
modal: "placeTypeModal",
|
||||
title: "Создание типа места на складе",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
placeTypeCrud,
|
||||
parent: placeType,
|
||||
},
|
||||
});
|
||||
},
|
||||
<IconPlaylistAdd />,
|
||||
),
|
||||
getAction(
|
||||
"Редактировать",
|
||||
() => {
|
||||
modals.openContextModal({
|
||||
modal: "placeTypeModal",
|
||||
title: "Редактирование типа места на складе",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
placeTypeCrud,
|
||||
element: placeType,
|
||||
},
|
||||
});
|
||||
},
|
||||
<IconEdit />,
|
||||
),
|
||||
getAction(
|
||||
"Удалить",
|
||||
() => placeTypeCrud.onDelete && placeTypeCrud.onDelete(placeType),
|
||||
<IconTrash />,
|
||||
placeType.placesCount !== 0 || placeType.children?.length !== 0,
|
||||
),
|
||||
];
|
||||
|
||||
return (
|
||||
<Flex gap={"md"} mx={"md"} direction={"row"}>
|
||||
{...actions}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceTypeActions;
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Button, Group, Stack } from "@mantine/core";
|
||||
import { useWarehouseManagementTabContext } from "../contexts/WarehouseManagementTabContext.tsx";
|
||||
import { IconChevronRight, IconSpace } from "@tabler/icons-react";
|
||||
import clsx from "clsx";
|
||||
import classes from "../../../OrganizationalStructureTab/components/DepartmentsTree/DepartmentsTree.module.css";
|
||||
import { DataTable } from "mantine-datatable";
|
||||
import { useState } from "react";
|
||||
import PlaceType from "./PlaceType.tsx";
|
||||
import PlaceTypeActions from "./PlaceTypeActions.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
|
||||
const PlaceTypesEditor = () => {
|
||||
const { placeTypes, placeTypeCrud } = useWarehouseManagementTabContext();
|
||||
const [placeTypeIds, setPlaceTypeIds] = useState<number[]>([]);
|
||||
|
||||
return (
|
||||
<Stack w={"100%"} mt={"md"}>
|
||||
<Group>
|
||||
<Button
|
||||
variant={"default"}
|
||||
onClick={() => {
|
||||
modals.openContextModal({
|
||||
modal: "placeTypeModal",
|
||||
title: "Создание типа места на складе",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
placeTypeCrud,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Добавить
|
||||
</Button>
|
||||
</Group>
|
||||
<DataTable
|
||||
noHeader
|
||||
withTableBorder
|
||||
columns={[
|
||||
{
|
||||
accessor: "name",
|
||||
title: "Место",
|
||||
noWrap: true,
|
||||
render: ({ id, name }) => (
|
||||
<>
|
||||
<IconChevronRight
|
||||
className={clsx(classes.icon, classes.expandIcon, {
|
||||
[classes.expandIconRotated]: placeTypeIds?.includes(id),
|
||||
})}
|
||||
/>
|
||||
<IconSpace className={classes.icon} />
|
||||
<span>{name}</span>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "",
|
||||
width: "0%",
|
||||
render: (placeType) => (
|
||||
<PlaceTypeActions placeType={placeType} />
|
||||
),
|
||||
},
|
||||
]}
|
||||
records={placeTypes.sort((a, b) => a.id - b.id)}
|
||||
rowExpansion={{
|
||||
allowMultiple: true,
|
||||
expanded: { recordIds: placeTypeIds, onRecordIdsChange: setPlaceTypeIds },
|
||||
content: ({ record }) => (
|
||||
<PlaceType placeType={record} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceTypesEditor;
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
|
||||
import { FC } from "react";
|
||||
|
||||
export enum WmsPage {
|
||||
PLACE,
|
||||
PLACE_TYPE,
|
||||
}
|
||||
|
||||
type Props = Omit<SegmentedControlProps, "data">;
|
||||
const data = [
|
||||
{
|
||||
label: "Места на складе",
|
||||
value: WmsPage.PLACE.toString(),
|
||||
},
|
||||
{
|
||||
label: "Типы мест на складе",
|
||||
value: WmsPage.PLACE_TYPE.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
export const WmsSegmentedControl: FC<Props> = props => {
|
||||
return (
|
||||
<SegmentedControl
|
||||
data={data}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
import React, { createContext, FC, useContext } from "react";
|
||||
import { PlaceSchema, PlaceTypeSchema, WmsService } from "../../../../../../client";
|
||||
import usePlaceTypesList from "../hooks/usePlaceTypesList.tsx";
|
||||
import usePlaceTypesCrud, { PlaceTypeCrud } from "../hooks/usePlaceTypesCrud.tsx";
|
||||
import usePlacesList from "../../place/hooks/usePlacesList.tsx";
|
||||
import usePlacesCrud, { PlaceCrud } from "../../place/hooks/usePlacesCrud.tsx";
|
||||
import { modals } from "@mantine/modals";
|
||||
|
||||
type WarehouseManagementTabContextState = {
|
||||
refetchPlaceTypes: () => void;
|
||||
placeTypeCrud: PlaceTypeCrud;
|
||||
placeTypes: PlaceTypeSchema[];
|
||||
refetchPlaces: () => void;
|
||||
placeCrud: PlaceCrud;
|
||||
places: PlaceSchema[];
|
||||
onCreatePlace: (place?: PlaceSchema) => void;
|
||||
generateQrCode: (place: PlaceSchema, isShort: boolean) => void;
|
||||
};
|
||||
|
||||
const WarehouseManagementTabContext = createContext<WarehouseManagementTabContextState | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const useWarehouseManagementTabContextState = () => {
|
||||
const { objects: placeTypes, refetch: refetchPlaceTypes } = usePlaceTypesList();
|
||||
const { objects: places, refetch: refetchPlaces } = usePlacesList();
|
||||
|
||||
const placeTypeCrud: PlaceTypeCrud = usePlaceTypesCrud({
|
||||
fetchPlaceTypes: refetchPlaceTypes,
|
||||
});
|
||||
|
||||
const placeCrud: PlaceCrud = usePlacesCrud({
|
||||
fetchPlaces: refetchPlaces,
|
||||
});
|
||||
|
||||
const onCreatePlace = (place?: PlaceSchema) => {
|
||||
WmsService.getFlatPlaceTypes({
|
||||
parentPlaceTypeId: place?.placeTypeId || -1,
|
||||
})
|
||||
.then(({ placeTypes }) => {
|
||||
if (placeTypes.length === 1) {
|
||||
placeCrud.onCreate({
|
||||
parentId: place?.id || null,
|
||||
placeTypeId: placeTypes[0].id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
modals.openContextModal({
|
||||
modal: "placeModal",
|
||||
title: "Создание места на складе",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
placeCrud,
|
||||
parent: place,
|
||||
placeTypes,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
const generateQrCode = (place: PlaceSchema, isShort: boolean) => {
|
||||
const pdfWindow = window.open(
|
||||
`${import.meta.env.VITE_API_URL}/wms/place/pdf/${place.id}/${isShort}`,
|
||||
);
|
||||
if (!pdfWindow) return;
|
||||
pdfWindow.print();
|
||||
};
|
||||
|
||||
return {
|
||||
placeTypes,
|
||||
placeTypeCrud,
|
||||
refetchPlaceTypes,
|
||||
places,
|
||||
placeCrud,
|
||||
refetchPlaces,
|
||||
onCreatePlace,
|
||||
generateQrCode,
|
||||
};
|
||||
};
|
||||
|
||||
type WarehouseManagementTabContextProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const WarehouseManagementTabContextProvider: FC<WarehouseManagementTabContextProviderProps> = ({ children }) => {
|
||||
const state = useWarehouseManagementTabContextState();
|
||||
return (
|
||||
<WarehouseManagementTabContext.Provider value={state}>
|
||||
{children}
|
||||
</WarehouseManagementTabContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useWarehouseManagementTabContext = () => {
|
||||
const context = useContext(WarehouseManagementTabContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useWarehouseManagementTabContext must be used within a WarehouseManagementTabContextProvider",
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { WmsService } from "../../../../../../client";
|
||||
import ObjectList from "../../../../../../hooks/objectList.tsx";
|
||||
|
||||
const useFlatPlaceTypesList = (parentPlaceTypeId: number = -1) =>
|
||||
ObjectList({
|
||||
queryFn: () => WmsService.getFlatPlaceTypes({ parentPlaceTypeId }),
|
||||
getObjectsFn: response => response.placeTypes,
|
||||
queryKey: "getFlatPlaceTypes",
|
||||
});
|
||||
|
||||
export default useFlatPlaceTypesList;
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useCRUD } from "../../../../../../hooks/useCRUD.tsx";
|
||||
import { BasePlaceTypeSchema, FlatPlaceTypeSchema, WmsService } from "../../../../../../client";
|
||||
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||
|
||||
|
||||
export type PlaceTypeCrud = {
|
||||
onCreate: (element: BasePlaceTypeSchema) => void,
|
||||
onDelete: (element: FlatPlaceTypeSchema) => void,
|
||||
onChange: (element: FlatPlaceTypeSchema) => void
|
||||
}
|
||||
|
||||
type Props = {
|
||||
fetchPlaceTypes: () => void;
|
||||
}
|
||||
|
||||
const usePlaceTypesCrud = ({ fetchPlaceTypes }: Props): PlaceTypeCrud => {
|
||||
return useCRUD<FlatPlaceTypeSchema, BasePlaceTypeSchema>({
|
||||
onChange: (placeType: FlatPlaceTypeSchema) => {
|
||||
WmsService.editPlaceType({
|
||||
requestBody: { placeType },
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
fetchPlaceTypes();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onDelete: (placeType: FlatPlaceTypeSchema) => {
|
||||
WmsService.deletePlaceType({
|
||||
placeTypeId: placeType.id,
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
fetchPlaceTypes();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
onCreate: (placeType: BasePlaceTypeSchema) => {
|
||||
WmsService.createPlaceType({
|
||||
requestBody: {
|
||||
placeType,
|
||||
},
|
||||
})
|
||||
.then(({ ok, message }) => {
|
||||
fetchPlaceTypes();
|
||||
if (ok) return;
|
||||
notifications.error({ message });
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default usePlaceTypesCrud;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { WmsService } from "../../../../../../client";
|
||||
import ObjectList from "../../../../../../hooks/objectList.tsx";
|
||||
|
||||
const usePlaceTypesList = () =>
|
||||
ObjectList({
|
||||
queryFn: WmsService.getPlaceTypes,
|
||||
getObjectsFn: response => response.placeTypes,
|
||||
queryKey: "getAllPlaceTypes",
|
||||
});
|
||||
|
||||
export default usePlaceTypesList;
|
||||
@@ -0,0 +1,76 @@
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import { BasePlaceTypeSchema, PlaceTypeSchema } from "../../../../../../client";
|
||||
import { PlaceTypeCrud } from "../hooks/usePlaceTypesCrud.tsx";
|
||||
import { Button, Stack, TextInput } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
|
||||
type Props = {
|
||||
placeTypeCrud: PlaceTypeCrud;
|
||||
element?: PlaceTypeSchema & BasePlaceTypeSchema;
|
||||
parent?: PlaceTypeSchema;
|
||||
}
|
||||
|
||||
type PlaceTypeModalForm = {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const PlaceTypeModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const { element, parent, placeTypeCrud } = innerProps;
|
||||
|
||||
const closeModal = () => {
|
||||
context.closeContextModal(id);
|
||||
};
|
||||
|
||||
const initialValues: PlaceTypeModalForm = {
|
||||
name: innerProps.element?.name ?? "",
|
||||
};
|
||||
const form = useForm<PlaceTypeModalForm>({
|
||||
initialValues,
|
||||
validate: {
|
||||
name: name => !name && "Необходимо указать название",
|
||||
},
|
||||
});
|
||||
|
||||
const isChanged = (): boolean => {
|
||||
return initialValues.name !== form.values.name;
|
||||
};
|
||||
|
||||
const onSubmit = (values: PlaceTypeModalForm) => {
|
||||
if (element) {
|
||||
if (isChanged()) {
|
||||
placeTypeCrud.onChange({
|
||||
name: values.name ?? "",
|
||||
id: element?.id,
|
||||
parentId: element?.parentId ?? null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
placeTypeCrud.onCreate({
|
||||
name: values.name ?? "",
|
||||
parentId: parent?.id ?? null,
|
||||
});
|
||||
}
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(values => onSubmit(values))}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label={"Название"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
|
||||
<Button variant={"default"} type={"submit"}>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaceTypeModal;
|
||||
Reference in New Issue
Block a user