This commit is contained in:
2024-03-19 09:02:58 +03:00
parent cc14105276
commit c9f3d4ee12
56 changed files with 995 additions and 121 deletions

View File

@@ -0,0 +1,37 @@
import {Select} from "@mantine/core";
import useServiceCategoriesList from "../../hooks/useServiceCategoriesList.tsx";
import {ServiceCategorySchema} from "../../../../client";
import {FC, ReactNode} from "react";
type Props = {
fullWidth?: boolean,
defaultValue?: ServiceCategorySchema
onChange: (category: ServiceCategorySchema) => void
error?: ReactNode
}
const ServiceCategorySelect: FC<Props> = ({defaultValue, onChange, fullWidth, error}) => {
const {categories} = useServiceCategoriesList();
return (
<Select
error={error}
w={fullWidth ? "100%" : undefined}
checkIconPosition={"right"}
label={"Категория услуги"}
placeholder={"Выберите категорию услуги"}
data={categories.map(category => ({
label: category.name,
value: category.id.toString()
}))}
onChange={event => {
if (!event) return;
const category = categories.find(category => category.id == parseInt(event))
if (!category) return;
onChange(category)
}}
value={defaultValue && defaultValue.id.toString()}
/>
)
}
export default ServiceCategorySelect;

View File

@@ -0,0 +1,30 @@
import {ServiceSchema} from "../../../../client";
import {FC, RefObject} from "react";
import {useServicesTableColumns} from "./columns.tsx";
import {BaseTable, BaseTableRef} from "../../../../components/BaseTable/BaseTable.tsx";
import {MRT_TableOptions} from "mantine-react-table";
type Props = {
services: ServiceSchema[];
tableRef?: RefObject<BaseTableRef<ServiceSchema>>
}
const ServicesTable: FC<Props> = ({services, tableRef}) => {
const columns = useServicesTableColumns();
return (
<BaseTable
ref={tableRef}
data={services}
columns={columns}
restProps={{
enableGrouping: true,
initialState: {grouping: ["category"]},
enableColumnActions: false,
mantineCreateRowModalProps: {
transitionProps: {transition: 'rotate-left', duration: 300}
},
} as MRT_TableOptions<ServiceSchema>}
/>
)
}
export default ServicesTable;

View File

@@ -0,0 +1,31 @@
import {useMemo} from "react";
import {MRT_ColumnDef} from "mantine-react-table";
import {ServiceSchema} from "../../../../client";
export const useServicesTableColumns = () => {
return useMemo<MRT_ColumnDef<ServiceSchema>[]>(() => [
{
accessorKey: "category",
header: "Категория",
enableGrouping: false,
enableSorting: false,
accessorFn: (row) => row.category.name
},
{
accessorKey: "name",
header: "Услуга",
enableGrouping: false,
enableSorting: false,
},
{
accessorKey: "price",
header: "Цена",
enableGrouping: false,
enableSorting: false,
},
], []);
}

View File

@@ -0,0 +1,14 @@
import {useQuery} from "@tanstack/react-query";
import {ServiceService} from "../../../client";
const useServiceCategoriesList = () => {
const {isPending, error, data, refetch} = useQuery({
queryKey: ['getAllServiceCategories'],
queryFn: ServiceService.getAllServiceCategories,
});
const categories = isPending || error || !data ? [] : data.categories;
return {categories, refetch}
}
export default useServiceCategoriesList;

View File

@@ -0,0 +1,13 @@
import {useQuery} from "@tanstack/react-query";
import {ServiceService} from "../../../client";
const useServicesList = () => {
const {isPending, error, data, refetch} = useQuery({
queryKey: ['getAllServices'],
queryFn: ServiceService.getAllServices
});
const services = isPending || error || !data ? [] : data.services;
return {services, refetch}
}
export default useServicesList;

View File

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

View File

@@ -0,0 +1,52 @@
import {ServiceCategorySchema} from "../../../client";
import {Button, Flex, rem, TextInput} from "@mantine/core";
import {useForm} from "@mantine/form";
import {ContextModalProps} from "@mantine/modals";
type Props = {
onCreate: (category: ServiceCategorySchema) => void
}
const CreateServiceCategoryModal = ({
context,
id,
innerProps,
}: ContextModalProps<Props>) => {
const form = useForm({
initialValues: {
name: ''
},
validate: {
name: (name) => name.trim() !== '' ? null : "Необходимо ввести название категории",
}
})
const onSubmit = (values: { name: string }) => {
innerProps.onCreate({name: values.name, id: -1});
context.closeContextModal(id);
}
const onCancelClick = () => {
context.closeContextModal(id);
}
return (
<>
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
<Flex gap={rem(10)} direction={"column"}>
<TextInput
placeholder={"Введите название категори"}
label={"Название категории"}
{...form.getInputProps('name')}
/>
<Flex justify={"center"} mt={rem(5)} gap={rem(10)}>
<Button onClick={() => onCancelClick()} variant={"subtle"}>Отменить</Button>
<Button type={"submit"} variant={"default"}>Сохранить</Button>
</Flex>
</Flex>
</form>
</>
)
}
export default CreateServiceCategoryModal;

View File

@@ -0,0 +1,76 @@
import {ServiceSchema} from "../../../client";
import {Button, Flex, NumberInput, rem, TextInput} from "@mantine/core";
import ServiceCategorySelect from "../components/ServiceCategorySelect/ServiceCategorySelect.tsx";
import {useForm} from "@mantine/form";
import {ContextModalProps} from "@mantine/modals";
type Props = {
onCreate: (service: ServiceSchema) => void
}
const CreateServiceModal = ({
context,
id,
innerProps,
}: ContextModalProps<Props>) => {
const form = useForm({
initialValues: {
category: {
id: -1,
name: ''
},
name: '',
price: NaN
},
validate: {
category: (category) => category.id >= 0 ? null : "Необходимо выбрать категорию",
name: (name) => name.trim() !== '' ? null : "Необходимо ввести название услуги",
price: (price) => !isNaN(price) ? null : "Небходимо ввести стоимость услуги"
}
})
const onSubmit = (values: { category: { id: number; name: string; }; name: string; price: number; }) => {
innerProps.onCreate({...values, id: -1});
context.closeContextModal(id);
}
const onCancelClick = () => {
context.closeContextModal(id);
}
return (
<>
<form onSubmit={form.onSubmit((values) => onSubmit(values))}>
<Flex gap={rem(10)} direction={"column"}>
<ServiceCategorySelect
fullWidth
onChange={event => {
form.setFieldValue("category", event)
}}
error={form.getInputProps("category").error}
/>
<TextInput
placeholder={"Введите название услуги"}
label={"Название услуги"}
{...form.getInputProps('name')}
/>
<NumberInput
placeholder={"Введите стоимость услуги"}
label={"Стоимость услуги"}
hideControls
decimalScale={2}
{...form.getInputProps('price')}
/>
<Flex justify={"center"} mt={rem(5)} gap={rem(10)}>
<Button onClick={() => onCancelClick()} variant={"subtle"}>Отменить</Button>
<Button type={"submit"} variant={"default"}>Сохранить</Button>
</Flex>
</Flex>
</form>
</>
)
}
export default CreateServiceModal;

View File

@@ -0,0 +1,12 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
gap: rem(10);
}
.top-panel {
padding: rem(5);
gap: rem(10);
display: flex;
}

View File

@@ -0,0 +1,67 @@
import {FC, useRef} from "react";
import ServicesTable from "../components/ServicesTable/ServicesTable.tsx";
import useServicesList from "../hooks/useServicesList.tsx";
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import styles from './ServicesPage.module.css';
import {Button} from "@mantine/core";
import {BaseTableRef} from "../../../components/BaseTable/BaseTable.tsx";
import {ServiceCategorySchema, ServiceSchema, ServiceService} from "../../../client";
import {notifications} from "../../../shared/lib/notifications.ts";
import {modals} from "@mantine/modals";
export const ServicesPage: FC = () => {
const {services, refetch} = useServicesList();
const tableRef = useRef<BaseTableRef<ServiceSchema>>(null);
const onCreateClick = () => {
modals.openContextModal({
modal: 'createService',
title: 'Создание услуги',
withCloseButton: false,
innerProps: {
onCreate
}
})
}
const onCreate = (values: ServiceSchema) => {
ServiceService.createService({requestBody: {service: values}})
.then(({ok, message}) => {
notifications.guess(ok, {message: message});
if (!ok) return;
refetch();
})
}
const onCreateCategoryClick = () => {
modals.openContextModal({
modal: "createServiceCategory",
title: 'Создание категории',
withCloseButton: false,
innerProps: {
onCreate: onCategoryCreate
}
})
}
const onCategoryCreate = (category: ServiceCategorySchema) => {
ServiceService.createServiceCategory({requestBody: {category: category}})
.then(({ok, message}) =>
notifications.guess(ok, {message: message}))
}
return (
<div className={styles['container']}>
<PageBlock>
<div className={styles['top-panel']}>
<Button onClick={onCreateClick} variant={"default"}>Создать услугу</Button>
<Button onClick={onCreateCategoryClick} variant={"default"}>Создать категорию</Button>
</div>
</PageBlock>
<PageBlock>
<ServicesTable
tableRef={tableRef}
services={services}
/>
</PageBlock>
</div>
)
}