feat: services kits table in service page
This commit is contained in:
@ -0,0 +1,27 @@
|
||||
import { FC } from "react";
|
||||
import { ButtonProps } from "@mantine/core";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useServicesContext } from "@/app/services/contexts/ServicesContext";
|
||||
import InlineButton from "@/components/ui/InlineButton/InlineButton";
|
||||
|
||||
type Props = ButtonProps;
|
||||
|
||||
const CreateServiceKitButton: FC<Props> = () => {
|
||||
const { servicesKitCrud } = useServicesContext();
|
||||
|
||||
const onCreateClick = () => {
|
||||
modals.openContextModal({
|
||||
modal: "servicesKitEditorModal",
|
||||
title: "Создание набора услуг",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
onCreate: servicesKitCrud.onCreate,
|
||||
isEditing: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return <InlineButton onClick={onCreateClick}>Создать набор</InlineButton>;
|
||||
};
|
||||
|
||||
export default CreateServiceKitButton;
|
||||
@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Box, Group } from "@mantine/core";
|
||||
import CreateServiceKitButton from "@/app/services/components/desktop/CreateServiceKitButton/CreateServiceKitButton";
|
||||
import ServiceTabSegmentedControl, {
|
||||
ServicesTab,
|
||||
} from "@/app/services/components/shared/ServiceTabSegmentedControl/ServiceTabSegmentedControl";
|
||||
|
||||
type Props = {
|
||||
serviceTab: ServicesTab;
|
||||
onServiceTabChange: (serviceTab: ServicesTab) => void;
|
||||
};
|
||||
|
||||
const ServicesDesktopHeader: FC<Props> = ({
|
||||
serviceTab,
|
||||
onServiceTabChange,
|
||||
}) => {
|
||||
const getTabActions = () => {
|
||||
switch (serviceTab) {
|
||||
case ServicesTab.DEAL_SERVICE:
|
||||
return <Box />;
|
||||
case ServicesTab.PRODUCT_SERVICE:
|
||||
return <Box />;
|
||||
case ServicesTab.SERVICES_KITS:
|
||||
return <CreateServiceKitButton />;
|
||||
default:
|
||||
return <Box />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Group
|
||||
wrap={"nowrap"}
|
||||
justify={"space-between"}>
|
||||
{getTabActions()}
|
||||
<ServiceTabSegmentedControl
|
||||
value={serviceTab.toString()}
|
||||
onChange={tab => onServiceTabChange(Number(tab))}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesDesktopHeader;
|
||||
@ -0,0 +1,28 @@
|
||||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import ServiceTabSegmentedControl, {
|
||||
ServicesTab,
|
||||
} from "@/app/services/components/shared/ServiceTabSegmentedControl/ServiceTabSegmentedControl";
|
||||
|
||||
type Props = {
|
||||
serviceTab: ServicesTab;
|
||||
onServiceTabChange: (serviceTab: ServicesTab) => void;
|
||||
};
|
||||
|
||||
const ServicesMobileHeader: FC<Props> = ({
|
||||
serviceTab,
|
||||
onServiceTabChange,
|
||||
}) => {
|
||||
return (
|
||||
<ServiceTabSegmentedControl
|
||||
value={serviceTab.toString()}
|
||||
onChange={tab => onServiceTabChange(Number(tab))}
|
||||
w={"100%"}
|
||||
py={"md"}
|
||||
px={"sm"}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesMobileHeader;
|
||||
63
src/app/services/components/shared/PageBody/PageBody.tsx
Normal file
63
src/app/services/components/shared/PageBody/PageBody.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import ServicesDesktopHeader from "@/app/services/components/desktop/ServicesDesktopHeader/ServicesDesktopHeader";
|
||||
import ServicesMobileHeader from "@/app/services/components/mobile/ServicesMobileHeader/ServicesMobileHeader";
|
||||
import ServicesKitsTable from "@/app/services/components/shared/ServicesKitTable/ServicesKitTable";
|
||||
import { ServicesTab } from "@/app/services/components/shared/ServiceTabSegmentedControl/ServiceTabSegmentedControl";
|
||||
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
|
||||
const PageBody = () => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const [servicesTab, setServicesTab] = useState<ServicesTab>(
|
||||
ServicesTab.PRODUCT_SERVICE
|
||||
);
|
||||
|
||||
const getPageBody = () => {
|
||||
switch (servicesTab) {
|
||||
case ServicesTab.PRODUCT_SERVICE:
|
||||
return <></>;
|
||||
case ServicesTab.DEAL_SERVICE:
|
||||
return <></>;
|
||||
case ServicesTab.SERVICES_KITS:
|
||||
return <ServicesKitsTable />;
|
||||
default:
|
||||
return <>-</>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isMobile && (
|
||||
<PageBlock>
|
||||
<ServicesDesktopHeader
|
||||
serviceTab={servicesTab}
|
||||
onServiceTabChange={setServicesTab}
|
||||
/>
|
||||
</PageBlock>
|
||||
)}
|
||||
<PageBlock fullHeight>
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}>
|
||||
{isMobile && (
|
||||
<ServicesMobileHeader
|
||||
serviceTab={servicesTab}
|
||||
onServiceTabChange={setServicesTab}
|
||||
/>
|
||||
)}
|
||||
<div style={{ flex: 1, overflow: "auto" }}>
|
||||
{getPageBody()}
|
||||
</div>
|
||||
</div>
|
||||
</PageBlock>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageBody;
|
||||
@ -0,0 +1,34 @@
|
||||
import { FC } from "react";
|
||||
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
|
||||
|
||||
export enum ServicesTab {
|
||||
DEAL_SERVICE,
|
||||
PRODUCT_SERVICE,
|
||||
SERVICES_KITS,
|
||||
}
|
||||
|
||||
type Props = Omit<SegmentedControlProps, "data">;
|
||||
|
||||
const data = [
|
||||
{
|
||||
label: "Для товара",
|
||||
value: ServicesTab.PRODUCT_SERVICE.toString(),
|
||||
},
|
||||
{
|
||||
label: "Для сделки",
|
||||
value: ServicesTab.DEAL_SERVICE.toString(),
|
||||
},
|
||||
{
|
||||
label: "Наборы услуг",
|
||||
value: ServicesTab.SERVICES_KITS.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
const ServiceTabSegmentedControl: FC<Props> = props => (
|
||||
<SegmentedControl
|
||||
data={data}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export default ServiceTabSegmentedControl;
|
||||
@ -0,0 +1,25 @@
|
||||
import { FC } from "react";
|
||||
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
|
||||
import { ServiceType } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/types/service";
|
||||
|
||||
type Props = Omit<SegmentedControlProps, "data">;
|
||||
|
||||
const data = [
|
||||
{
|
||||
label: "Для сделки",
|
||||
value: ServiceType.DEAL_SERVICE.toString(),
|
||||
},
|
||||
{
|
||||
label: "Для товара",
|
||||
value: ServiceType.PRODUCT_SERVICE.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
const ServiceTypeSegmentedControl: FC<Props> = props => (
|
||||
<SegmentedControl
|
||||
data={data}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export default ServiceTypeSegmentedControl;
|
||||
@ -0,0 +1,42 @@
|
||||
import { modals } from "@mantine/modals";
|
||||
import useServicesKitsTableColumns from "@/app/services/components/shared/ServicesKitTable/hooks/useServicesKitsTableColumns";
|
||||
import { useServicesContext } from "@/app/services/contexts/ServicesContext";
|
||||
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
import { ServicesKitSchema } from "@/lib/client";
|
||||
|
||||
const ServicesKitsTable = () => {
|
||||
const { servicesKitCrud, servicesKitList } = useServicesContext();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const onChange = (kit: ServicesKitSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "servicesKitEditorModal",
|
||||
title: "Редактирование набора услуг",
|
||||
withCloseButton: false,
|
||||
innerProps: {
|
||||
entity: kit,
|
||||
onChange: value => servicesKitCrud.onUpdate(kit.id, value),
|
||||
isEditing: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const columns = useServicesKitsTableColumns({
|
||||
onChange,
|
||||
onDelete: servicesKitCrud.onDelete,
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseTable
|
||||
records={servicesKitList.servicesKits}
|
||||
columns={columns}
|
||||
groups={undefined}
|
||||
withTableBorder
|
||||
verticalSpacing={"xs"}
|
||||
mx={isMobile ? "xs" : 0}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesKitsTable;
|
||||
@ -0,0 +1,68 @@
|
||||
import { useMemo } from "react";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { DataTableColumn } from "mantine-datatable";
|
||||
import { Flex } from "@mantine/core";
|
||||
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
import { ServicesKitSchema } from "@/lib/client";
|
||||
import { ServiceType } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/types/service";
|
||||
|
||||
type Props = {
|
||||
onChange: (kit: ServicesKitSchema) => void;
|
||||
onDelete: (kit: ServicesKitSchema) => void;
|
||||
};
|
||||
|
||||
const useServicesKitsTableColumns = ({ onDelete, onChange }: Props) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
accessor: "actions",
|
||||
title: "Действия",
|
||||
sortable: false,
|
||||
textAlign: "center",
|
||||
width: "0%",
|
||||
render: kit => (
|
||||
<Flex gap={isMobile ? "xs" : "md"}>
|
||||
<ActionIconWithTip
|
||||
onClick={() => onChange(kit)}
|
||||
tipLabel={"Редактировать"}>
|
||||
<IconEdit />
|
||||
</ActionIconWithTip>
|
||||
<ActionIconWithTip
|
||||
color={"red"}
|
||||
onClick={() => onDelete(kit)}
|
||||
tipLabel={"Удалить"}>
|
||||
<IconTrash />
|
||||
</ActionIconWithTip>
|
||||
</Flex>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessor: "name",
|
||||
title: "Название",
|
||||
width: "60%",
|
||||
},
|
||||
{
|
||||
title: "Кол-во услуг",
|
||||
accessor: "services",
|
||||
render: kit => kit.services.length,
|
||||
width: "20%",
|
||||
},
|
||||
{
|
||||
title: "Тип набора",
|
||||
accessor: "serviceType",
|
||||
render: kit =>
|
||||
kit.serviceType === ServiceType.DEAL_SERVICE
|
||||
? "Для сделок"
|
||||
: "Для товаров",
|
||||
width: "20%",
|
||||
},
|
||||
] as DataTableColumn<ServicesKitSchema>[],
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
||||
export default useServicesKitsTableColumns;
|
||||
@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import ObjectMultiSelect, {
|
||||
ObjectMultiSelectProps,
|
||||
} from "@/components/selects/ObjectMultiSelect/ObjectMultiSelect";
|
||||
import { ServiceSchema } from "@/lib/client";
|
||||
import useServicesList from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/lists/useServicesList";
|
||||
|
||||
type Props = Omit<ObjectMultiSelectProps<ServiceSchema>, "data">;
|
||||
|
||||
const ServicesMultiselect: FC<Props> = (props: Props) => {
|
||||
const { services } = useServicesList();
|
||||
|
||||
return (
|
||||
<ObjectMultiSelect
|
||||
data={services}
|
||||
searchable
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesMultiselect;
|
||||
45
src/app/services/contexts/ServicesContext.tsx
Normal file
45
src/app/services/contexts/ServicesContext.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import makeContext from "@/lib/contextFactory/contextFactory";
|
||||
import {
|
||||
ServicesCrud,
|
||||
useServicesCrud,
|
||||
} from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/cruds/useServicesCrud";
|
||||
import {
|
||||
ServicesKitsCrud,
|
||||
useServicesKitsCrud,
|
||||
} from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/cruds/useServicesKitsCrud";
|
||||
import useServicesKitsList, {
|
||||
ServicesKitsList,
|
||||
} from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/lists/useServicesKitsList";
|
||||
import useServicesList, {
|
||||
ServicesList,
|
||||
} from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/hooks/lists/useServicesList";
|
||||
|
||||
type ServicesContextState = {
|
||||
servicesList: ServicesList;
|
||||
servicesCrud: ServicesCrud;
|
||||
servicesKitList: ServicesKitsList;
|
||||
servicesKitCrud: ServicesKitsCrud;
|
||||
};
|
||||
|
||||
const useFulfillmentBaseContextState = (): ServicesContextState => {
|
||||
const servicesList = useServicesList();
|
||||
const servicesCrud = useServicesCrud(servicesList);
|
||||
|
||||
const servicesKitList = useServicesKitsList();
|
||||
const servicesKitCrud = useServicesKitsCrud(servicesKitList);
|
||||
|
||||
return {
|
||||
servicesList,
|
||||
servicesCrud,
|
||||
servicesKitList,
|
||||
servicesKitCrud,
|
||||
};
|
||||
};
|
||||
|
||||
export const [ServicesContextProvider, useServicesContext] =
|
||||
makeContext<ServicesContextState>(
|
||||
useFulfillmentBaseContextState,
|
||||
"Services"
|
||||
);
|
||||
70
src/app/services/modals/ServicesKitEditorModal.tsx
Normal file
70
src/app/services/modals/ServicesKitEditorModal.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
"use client";
|
||||
|
||||
import { TextInput } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ContextModalProps } from "@mantine/modals";
|
||||
import ServicesMultiselect from "@/app/services/components/shared/ServicesMultiselect/ServicesMultiselect";
|
||||
import ServiceTypeSegmentedControl from "@/app/services/components/shared/ServiceTypeSegmentedControl/ServiceTypeSegmentedControl";
|
||||
import { ServicesKitSchema } from "@/lib/client";
|
||||
import BaseFormModal, {
|
||||
CreateEditFormProps,
|
||||
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||
import { ServiceType } from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/types/service";
|
||||
|
||||
type Props = CreateEditFormProps<ServicesKitSchema>;
|
||||
|
||||
const ServicesKitEditorModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const initialValues: Partial<ServicesKitSchema> = innerProps.isEditing
|
||||
? innerProps.entity
|
||||
: {
|
||||
name: "",
|
||||
serviceType: ServiceType.DEAL_SERVICE,
|
||||
services: [],
|
||||
};
|
||||
|
||||
const form = useForm<Partial<ServicesKitSchema>>({
|
||||
initialValues,
|
||||
validate: {
|
||||
name: name => !name && "Введите название",
|
||||
services: services =>
|
||||
(!services || services.length === 0) && "Выберите услуги",
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseFormModal
|
||||
{...innerProps}
|
||||
form={form}
|
||||
closeOnSubmit
|
||||
onClose={() => context.closeContextModal(id)}>
|
||||
<TextInput
|
||||
label={"Название"}
|
||||
placeholder={"Введите название набора услуг"}
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<ServiceTypeSegmentedControl
|
||||
value={form.values.serviceType?.toString()}
|
||||
onChange={tab => {
|
||||
form.setFieldValue("serviceType", Number(tab));
|
||||
form.setFieldValue("services", []);
|
||||
}}
|
||||
mt={"xs"}
|
||||
/>
|
||||
<ServicesMultiselect
|
||||
label={"Услуги"}
|
||||
placeholder={"Выберите услуги"}
|
||||
filterBy={service =>
|
||||
service.serviceType === form.values.serviceType
|
||||
}
|
||||
groupBy={service => service.category.name}
|
||||
{...form.getInputProps("services")}
|
||||
/>
|
||||
</BaseFormModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicesKitEditorModal;
|
||||
1
src/app/services/modals/index.ts
Normal file
1
src/app/services/modals/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as ServicesKitEditorModal } from "@/app/services/modals/ServicesKitEditorModal";
|
||||
22
src/app/services/page.tsx
Normal file
22
src/app/services/page.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { Suspense } from "react";
|
||||
import { Center, Loader } from "@mantine/core";
|
||||
import PageBody from "@/app/services/components/shared/PageBody/PageBody";
|
||||
import { ServicesContextProvider } from "@/app/services/contexts/ServicesContext";
|
||||
import PageContainer from "@/components/layout/PageContainer/PageContainer";
|
||||
|
||||
export default async function ServicesPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<Center h="50vh">
|
||||
<Loader size="lg" />
|
||||
</Center>
|
||||
}>
|
||||
<ServicesContextProvider>
|
||||
<PageContainer>
|
||||
<PageBody />
|
||||
</PageContainer>
|
||||
</ServicesContextProvider>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user