feat: services kits table in service page
This commit is contained in:
@ -15,6 +15,8 @@
|
||||
--dark-thick-shadow: 1px 1px 13px 1px var(--mantine-color-dark-5);
|
||||
/* Heights */
|
||||
--page-height: calc(100vh - (var(--mantine-spacing-md) * 2));
|
||||
--mobile-footer-height: 70px;
|
||||
--mobile-page-height: calc(100vh - var(--mobile-footer-height));
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
.container {
|
||||
height: var(--mobile-footer-height);
|
||||
|
||||
@mixin light {
|
||||
border-top: solid gray 2px;
|
||||
}
|
||||
@ -9,7 +11,6 @@
|
||||
|
||||
.link {
|
||||
width: 100%;
|
||||
height: rem(50px);
|
||||
border-radius: var(--mantine-radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IconCircleDotted, IconLayoutKanban } from "@tabler/icons-react";
|
||||
import { IconColumns, IconLayoutKanban } from "@tabler/icons-react";
|
||||
import { Box, Flex } from "@mantine/core";
|
||||
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
||||
import { ColorSchemeToggle } from "@/components/ui/ColorSchemeToggle/ColorSchemeToggle";
|
||||
@ -13,9 +13,9 @@ const linksData = [
|
||||
href: "/deals",
|
||||
},
|
||||
{
|
||||
icon: IconCircleDotted,
|
||||
label: "Назад",
|
||||
href: "/oiiai",
|
||||
icon: IconColumns,
|
||||
label: "Услуги",
|
||||
href: "/services",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -20,6 +20,10 @@
|
||||
|
||||
.container-full-height {
|
||||
height: var(--page-height);
|
||||
|
||||
@media (max-width: 48em) {
|
||||
height: var(--mobile-page-height);
|
||||
}
|
||||
}
|
||||
|
||||
.container-no-border-radius {
|
||||
@ -28,8 +32,8 @@
|
||||
|
||||
.container-full-screen-mobile {
|
||||
@media (max-width: 48em) {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
min-height: var(--mobile-page-height);
|
||||
height: var(--mobile-page-height);
|
||||
width: 100vw;
|
||||
border-radius: 0 !important;
|
||||
position: fixed;
|
||||
|
||||
@ -93,7 +93,13 @@ const ObjectMultiSelect = <T,>(props: ObjectMultiSelectProps<T>) => {
|
||||
props.onChange(internalValue);
|
||||
}, [internalValue]);
|
||||
|
||||
const restProps = omit(props, "getValueFn", "getLabelFn", "filterBy");
|
||||
const restProps = omit(
|
||||
props,
|
||||
"getValueFn",
|
||||
"getLabelFn",
|
||||
"filterBy",
|
||||
"groupBy"
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
PolymorphicComponentProps,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
import style from "./ActionIconWithTip.module.css";
|
||||
|
||||
type Props = PolymorphicComponentProps<"button", ActionIconProps> & {
|
||||
@ -15,20 +16,24 @@ const ActionIconWithTip: FC<PropsWithChildren<Props>> = ({
|
||||
children,
|
||||
tipLabel,
|
||||
...props
|
||||
}) => (
|
||||
<Tooltip
|
||||
label={tipLabel}
|
||||
hidden={!tipLabel}
|
||||
h={"max-content"}
|
||||
w={"max-content"}>
|
||||
<ActionIcon
|
||||
variant={"default"}
|
||||
radius={"lg"}
|
||||
className={style.container}
|
||||
{...props}>
|
||||
{children}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
}) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
label={tipLabel}
|
||||
hidden={!tipLabel}
|
||||
h={"max-content"}
|
||||
w={"max-content"}>
|
||||
<ActionIcon
|
||||
variant={isMobile ? "subtle" : "default"}
|
||||
radius={"lg"}
|
||||
className={style.container}
|
||||
{...props}>
|
||||
{children}
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionIconWithTip;
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
|
||||
.table-bg {
|
||||
@mixin light {
|
||||
background-color: white;
|
||||
}
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-8);
|
||||
}
|
||||
}
|
||||
|
||||
.table-border {
|
||||
border-width: 1px;
|
||||
border-radius: var(--mantine-radius-lg);
|
||||
|
||||
@ -9,7 +9,12 @@ function BaseTable<T>(props: DataTableProps<T>) {
|
||||
withRowBorders
|
||||
striped={false}
|
||||
verticalAlign={"center"}
|
||||
backgroundColor={"transparent"}
|
||||
classNames={{
|
||||
root: styles["table-bg"],
|
||||
table: styles["table-bg"],
|
||||
header: styles["table-bg"],
|
||||
footer: styles["table-bg"],
|
||||
}}
|
||||
className={classNames(
|
||||
props.withTableBorder && styles["table-border"]
|
||||
)}
|
||||
|
||||
@ -90,6 +90,10 @@ export type BuiltInModuleTabSchema = {
|
||||
* Id
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* Key
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* Label
|
||||
*/
|
||||
@ -468,9 +472,9 @@ export type CreateServicesKitSchema = {
|
||||
*/
|
||||
serviceType: number;
|
||||
/**
|
||||
* Servicesids
|
||||
* Services
|
||||
*/
|
||||
servicesIds: Array<number>;
|
||||
services: Array<ServiceSchema>;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1487,9 +1491,9 @@ export type UpdateServicesKitSchema = {
|
||||
*/
|
||||
serviceType: number;
|
||||
/**
|
||||
* Servicesids
|
||||
* Services
|
||||
*/
|
||||
servicesIds: Array<number>;
|
||||
services: Array<ServiceSchema>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -17,6 +17,7 @@ export const zBoardSchema = z.object({
|
||||
*/
|
||||
export const zBuiltInModuleTabSchema = z.object({
|
||||
id: z.int(),
|
||||
key: z.string(),
|
||||
label: z.string(),
|
||||
iconName: z.string(),
|
||||
device: z.string(),
|
||||
@ -385,7 +386,7 @@ export const zCreateServiceResponse = z.object({
|
||||
export const zCreateServicesKitSchema = z.object({
|
||||
name: z.string(),
|
||||
serviceType: z.int(),
|
||||
servicesIds: z.array(z.int()),
|
||||
services: z.array(zServiceSchema),
|
||||
});
|
||||
|
||||
/**
|
||||
@ -878,7 +879,7 @@ export const zUpdateServiceResponse = z.object({
|
||||
export const zUpdateServicesKitSchema = z.object({
|
||||
name: z.string(),
|
||||
serviceType: z.int(),
|
||||
servicesIds: z.array(z.int()),
|
||||
services: z.array(zServiceSchema),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import DealsBoardFiltersModal from "@/app/deals/modals/DealsBoardFiltersModal/DealsBoardFiltersModal";
|
||||
import DealsScheduleFiltersModal from "@/app/deals/modals/DealsScheduleFiltersModal/DealsScheduleFiltersModal";
|
||||
import DealsTableFiltersModal from "@/app/deals/modals/DealsTableFiltersModal/DealsTableFiltersModal";
|
||||
import { ServicesKitEditorModal } from "@/app/services/modals";
|
||||
import EnterNameModal from "@/modals/EnterNameModal/EnterNameModal";
|
||||
import {
|
||||
DealProductEditorModal,
|
||||
@ -22,4 +23,5 @@ export const modals = {
|
||||
productServiceEditorModal: ProductServiceEditorModal,
|
||||
duplicateServicesModal: DuplicateServicesModal,
|
||||
servicesKitSelectModal: ServicesKitSelectModal,
|
||||
servicesKitEditorModal: ServicesKitEditorModal,
|
||||
};
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import { useCrudOperations } from "@/hooks/cruds/baseCrud";
|
||||
import {
|
||||
CreateServicesKitSchema,
|
||||
ServicesKitSchema,
|
||||
UpdateServicesKitSchema,
|
||||
} from "@/lib/client";
|
||||
import {
|
||||
createServicesKitMutation,
|
||||
deleteServicesKitMutation,
|
||||
updateServicesKitMutation,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
|
||||
type Props = {
|
||||
queryKey: any[];
|
||||
};
|
||||
|
||||
export type ServicesKitsCrud = {
|
||||
onCreate: (data: Partial<CreateServicesKitSchema>) => void;
|
||||
onUpdate: (
|
||||
servicesKitId: number,
|
||||
servicesKit: UpdateServicesKitSchema
|
||||
) => void;
|
||||
onDelete: (servicesKit: ServicesKitSchema, onSuccess?: () => void) => void;
|
||||
};
|
||||
|
||||
export const useServicesKitsCrud = ({ queryKey }: Props): ServicesKitsCrud => {
|
||||
return useCrudOperations<
|
||||
ServicesKitSchema,
|
||||
UpdateServicesKitSchema,
|
||||
CreateServicesKitSchema
|
||||
>({
|
||||
key: "getServicesKits",
|
||||
queryKey,
|
||||
mutations: {
|
||||
create: createServicesKitMutation(),
|
||||
update: updateServicesKitMutation(),
|
||||
delete: deleteServicesKitMutation(),
|
||||
},
|
||||
getCreateEntity: data => ({
|
||||
name: data.name!,
|
||||
serviceType: data.serviceType!,
|
||||
services: data.services!,
|
||||
}),
|
||||
getUpdateEntity: (old, update) => ({
|
||||
...old,
|
||||
name: update.name ?? old.name,
|
||||
serviceType: update.serviceType ?? old.serviceType,
|
||||
}),
|
||||
getDeleteConfirmTitle: () => "Удаление набора услуг",
|
||||
});
|
||||
};
|
||||
@ -5,7 +5,15 @@ import {
|
||||
getServicesKitsQueryKey,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
|
||||
const useServicesKitsList = () => {
|
||||
export type ServicesKitsList = {
|
||||
servicesKits: ServicesKitSchema[];
|
||||
setServicesKits: (servicesKits: ServicesKitSchema[]) => void;
|
||||
refetch: () => void;
|
||||
queryKey: any[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const useServicesKitsList = (): ServicesKitsList => {
|
||||
const queryClient = useQueryClient();
|
||||
const { data, refetch, isLoading } = useQuery(getServicesKitsOptions());
|
||||
|
||||
|
||||
@ -5,8 +5,15 @@ import {
|
||||
getServicesQueryKey,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
|
||||
export type ServicesList = {
|
||||
services: ServiceSchema[];
|
||||
setServices: (services: ServiceSchema[]) => void;
|
||||
refetch: () => void;
|
||||
queryKey: any[];
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const useServicesList = () => {
|
||||
const useServicesList = (): ServicesList => {
|
||||
const queryClient = useQueryClient();
|
||||
const { data, refetch, isLoading } = useQuery(getServicesOptions());
|
||||
|
||||
|
||||
10
src/theme.ts
10
src/theme.ts
@ -62,5 +62,15 @@ export const theme = createTheme({
|
||||
decimalSeparator: ",",
|
||||
},
|
||||
},
|
||||
MultiSelect: {
|
||||
defaultProps: {
|
||||
radius,
|
||||
},
|
||||
},
|
||||
SegmentedControl: {
|
||||
defaultProps: {
|
||||
radius,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user