feat: crappy reordering
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --force",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview",
|
||||
@@ -15,7 +15,7 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@hello-pangea/dnd": "^16.6.0",
|
||||
"@hello-pangea/dnd": "^17.0.0",
|
||||
"@mantine/core": "^7.11.2",
|
||||
"@mantine/dates": "^7.11.2",
|
||||
"@mantine/dropzone": "^7.11.2",
|
||||
|
||||
@@ -182,6 +182,8 @@ export type { ProductUpdateResponse } from './models/ProductUpdateResponse';
|
||||
export type { ProductUploadImageResponse } from './models/ProductUploadImageResponse';
|
||||
export type { RoleSchema } from './models/RoleSchema';
|
||||
export type { ServiceCategoryPriceSchema } from './models/ServiceCategoryPriceSchema';
|
||||
export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
|
||||
export type { ServiceCategoryReorderResponse } from './models/ServiceCategoryReorderResponse';
|
||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||
export type { ServiceCreateCategoryRequest } from './models/ServiceCreateCategoryRequest';
|
||||
export type { ServiceCreateCategoryResponse } from './models/ServiceCreateCategoryResponse';
|
||||
@@ -193,6 +195,8 @@ export type { ServiceGetAllCategoriesResponse } from './models/ServiceGetAllCate
|
||||
export type { ServiceGetAllResponse } from './models/ServiceGetAllResponse';
|
||||
export type { ServicePriceCategorySchema } from './models/ServicePriceCategorySchema';
|
||||
export type { ServicePriceRangeSchema } from './models/ServicePriceRangeSchema';
|
||||
export type { ServiceReorderRequest } from './models/ServiceReorderRequest';
|
||||
export type { ServiceReorderResponse } from './models/ServiceReorderResponse';
|
||||
export type { ServiceSchema } from './models/ServiceSchema';
|
||||
export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest';
|
||||
export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse';
|
||||
|
||||
11
src/client/models/ServiceCategoryReorderRequest.ts
Normal file
11
src/client/models/ServiceCategoryReorderRequest.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ServiceCategoryReorderRequest = {
|
||||
moveDown: boolean;
|
||||
moveUp: boolean;
|
||||
categoryId: number;
|
||||
serviceType: number;
|
||||
};
|
||||
|
||||
9
src/client/models/ServiceCategoryReorderResponse.ts
Normal file
9
src/client/models/ServiceCategoryReorderResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ServiceCategoryReorderResponse = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
export type ServiceCategorySchema = {
|
||||
id: number;
|
||||
name: string;
|
||||
dealServiceRank: string;
|
||||
productServiceRank: string;
|
||||
};
|
||||
|
||||
|
||||
9
src/client/models/ServiceReorderRequest.ts
Normal file
9
src/client/models/ServiceReorderRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ServiceReorderRequest = {
|
||||
drainingServiceId: number;
|
||||
hoveredServiceId: number;
|
||||
};
|
||||
|
||||
9
src/client/models/ServiceReorderResponse.ts
Normal file
9
src/client/models/ServiceReorderResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ServiceReorderResponse = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
@@ -14,5 +14,6 @@ export type ServiceSchema = {
|
||||
priceRanges: Array<ServicePriceRangeSchema>;
|
||||
categoryPrices: Array<ServiceCategoryPriceSchema>;
|
||||
cost: (number | null);
|
||||
rank: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import type { DeletePriceCategoryRequest } from '../models/DeletePriceCategoryRe
|
||||
import type { DeletePriceCategoryResponse } from '../models/DeletePriceCategoryResponse';
|
||||
import type { GetAllPriceCategoriesResponse } from '../models/GetAllPriceCategoriesResponse';
|
||||
import type { GetAllServicesKitsResponse } from '../models/GetAllServicesKitsResponse';
|
||||
import type { ServiceCategoryReorderRequest } from '../models/ServiceCategoryReorderRequest';
|
||||
import type { ServiceCategoryReorderResponse } from '../models/ServiceCategoryReorderResponse';
|
||||
import type { ServiceCreateCategoryRequest } from '../models/ServiceCreateCategoryRequest';
|
||||
import type { ServiceCreateCategoryResponse } from '../models/ServiceCreateCategoryResponse';
|
||||
import type { ServiceCreateRequest } from '../models/ServiceCreateRequest';
|
||||
@@ -19,6 +21,8 @@ import type { ServiceDeleteRequest } from '../models/ServiceDeleteRequest';
|
||||
import type { ServiceDeleteResponse } from '../models/ServiceDeleteResponse';
|
||||
import type { ServiceGetAllCategoriesResponse } from '../models/ServiceGetAllCategoriesResponse';
|
||||
import type { ServiceGetAllResponse } from '../models/ServiceGetAllResponse';
|
||||
import type { ServiceReorderRequest } from '../models/ServiceReorderRequest';
|
||||
import type { ServiceReorderResponse } from '../models/ServiceReorderResponse';
|
||||
import type { ServiceUpdateRequest } from '../models/ServiceUpdateRequest';
|
||||
import type { ServiceUpdateResponse } from '../models/ServiceUpdateResponse';
|
||||
import type { UpdatePriceCategoryRequest } from '../models/UpdatePriceCategoryRequest';
|
||||
@@ -100,6 +104,26 @@ export class ServiceService {
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Reorder
|
||||
* @returns ServiceReorderResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static reorderService({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: ServiceReorderRequest,
|
||||
}): CancelablePromise<ServiceReorderResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/service/reorder',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get All Categories
|
||||
* @returns ServiceGetAllCategoriesResponse Successful Response
|
||||
@@ -131,6 +155,26 @@ export class ServiceService {
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Reorder Category
|
||||
* @returns ServiceCategoryReorderResponse Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static reorderServiceCategory({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: ServiceCategoryReorderRequest,
|
||||
}): CancelablePromise<ServiceCategoryReorderResponse> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/service/categories/reorder',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get All Service Types
|
||||
* @returns BaseEnumListSchema Successful Response
|
||||
|
||||
@@ -1,19 +1,42 @@
|
||||
import { ServiceSchema } from "../../../../client";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
|
||||
import { ServiceSchema, ServiceService } from "../../../../client";
|
||||
import { FC } from "react";
|
||||
import { useServicesTableColumns } from "./columns.tsx";
|
||||
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
|
||||
import { MRT_TableOptions } from "mantine-react-table";
|
||||
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
||||
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { IconArrowDown, IconArrowUp, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { ServicesTab } from "../ServiceTypeSegmentedControl/ServiceTypeSegmentedControl.tsx";
|
||||
|
||||
const ServicesTable: FC<CRUDTableProps<ServiceSchema>> = ({
|
||||
items,
|
||||
onDelete,
|
||||
onChange,
|
||||
}) => {
|
||||
type RestProps = {
|
||||
serviceType: ServicesTab;
|
||||
editMode: boolean;
|
||||
}
|
||||
type Props = CRUDTableProps<ServiceSchema> & RestProps;
|
||||
const ServicesTable: FC<Props> = ({
|
||||
items,
|
||||
onDelete,
|
||||
onChange,
|
||||
serviceType,
|
||||
editMode,
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const columns = useServicesTableColumns();
|
||||
const categoryRanks = items.map(item => item.category).map(category => {
|
||||
if (serviceType === ServicesTab.DEAL_SERVICE) {
|
||||
return category.dealServiceRank;
|
||||
}
|
||||
return category.productServiceRank;
|
||||
});
|
||||
const minRank = categoryRanks.sort()[0];
|
||||
const maxRank = categoryRanks.sort()[categoryRanks.length - 1];
|
||||
console.log(minRank, maxRank);
|
||||
|
||||
const onEditClick = (service: ServiceSchema) => {
|
||||
if (!onChange) return;
|
||||
@@ -31,33 +54,125 @@ const ServicesTable: FC<CRUDTableProps<ServiceSchema>> = ({
|
||||
<BaseTable
|
||||
data={items}
|
||||
columns={columns}
|
||||
restProps={
|
||||
{
|
||||
enableGrouping: true,
|
||||
initialState: { grouping: ["category"] },
|
||||
enableColumnActions: false,
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
if (onDelete) onDelete(row.original);
|
||||
}}
|
||||
variant={"default"}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<ServiceSchema>
|
||||
|
||||
restProps={{
|
||||
enableGrouping: true,
|
||||
initialState: {
|
||||
grouping: ["category"],
|
||||
},
|
||||
state: {
|
||||
columnVisibility: {
|
||||
"mrt-row-drag": editMode,
|
||||
},
|
||||
},
|
||||
enableColumnActions: false,
|
||||
enableRowOrdering: true,
|
||||
mantineRowDragHandleProps: ({ table }) => ({
|
||||
onDragEnd: () => {
|
||||
const { draggingRow, hoveredRow } = table.getState();
|
||||
if (!hoveredRow?.original || !draggingRow?.original) return;
|
||||
ServiceService.reorderService({
|
||||
requestBody: {
|
||||
drainingServiceId: draggingRow.original.id,
|
||||
hoveredServiceId: hoveredRow.original.id,
|
||||
},
|
||||
}).then(({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["getAllServices"],
|
||||
}).then(() => {
|
||||
});
|
||||
});
|
||||
},
|
||||
}),
|
||||
displayColumnDefOptions: {
|
||||
"mrt-row-drag": {
|
||||
AggregatedCell: ({ row }) => {
|
||||
const rank = serviceType === ServicesTab.DEAL_SERVICE ? row.original.category.dealServiceRank : row.original.category.productServiceRank;
|
||||
return (
|
||||
<Flex gap={"xs"}>
|
||||
<Tooltip label="Поднять вверх">
|
||||
<ActionIcon
|
||||
disabled={rank === minRank}
|
||||
onClick={() => {
|
||||
ServiceService.reorderServiceCategory({
|
||||
requestBody: {
|
||||
serviceType,
|
||||
categoryId: row.original.category.id,
|
||||
moveDown: false,
|
||||
moveUp: true,
|
||||
},
|
||||
}).then(({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["getAllServices"],
|
||||
}).then(() => {
|
||||
});
|
||||
});
|
||||
}}
|
||||
variant={"default"}>
|
||||
<IconArrowUp />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Опустить вниз">
|
||||
<ActionIcon
|
||||
disabled={rank === maxRank}
|
||||
onClick={() => {
|
||||
ServiceService.reorderServiceCategory({
|
||||
requestBody: {
|
||||
serviceType,
|
||||
categoryId: row.original.category.id,
|
||||
moveDown: true,
|
||||
moveUp: false,
|
||||
},
|
||||
}).then(({ ok, message }) => {
|
||||
if (!ok) {
|
||||
notifications.guess(ok, { message });
|
||||
return;
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["getAllServices"],
|
||||
}).then(() => {
|
||||
});
|
||||
});
|
||||
}}
|
||||
variant={"default"}>
|
||||
<IconArrowDown />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
<Flex gap="xs">
|
||||
<Tooltip label="Редактировать">
|
||||
<ActionIcon
|
||||
onClick={() => onEditClick(row.original)}
|
||||
variant={"default"}>
|
||||
<IconEdit />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label="Удалить">
|
||||
<ActionIcon
|
||||
onClick={() => {
|
||||
if (onDelete) onDelete(row.original);
|
||||
}}
|
||||
variant={"default"}>
|
||||
<IconTrash />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<ServiceSchema>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ export const useServicesTableColumns = () => {
|
||||
<>
|
||||
<List>
|
||||
{service.priceRanges.map(range => (
|
||||
<List.Item>
|
||||
<List.Item key={range.id}>
|
||||
{`${range.fromQuantity} - ${range.toQuantity}: ${range.price}₽`}
|
||||
</List.Item>
|
||||
))}
|
||||
@@ -24,9 +24,9 @@ export const useServicesTableColumns = () => {
|
||||
{
|
||||
accessorKey: "category",
|
||||
header: "Категория",
|
||||
enableGrouping: false,
|
||||
accessorFn: row => `${row.category.name}`,
|
||||
enableColumnOrdering: true,
|
||||
enableSorting: false,
|
||||
accessorFn: row => row.category.name,
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
@@ -47,7 +47,8 @@ export const useServicesTableColumns = () => {
|
||||
enableGrouping: false,
|
||||
enableSorting: false,
|
||||
},
|
||||
|
||||
],
|
||||
[]
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,29 +21,32 @@ import PriceCategoryInput, {
|
||||
|
||||
type Props = CreateEditFormProps<ServiceSchema>;
|
||||
const CreateServiceModal = ({
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
context,
|
||||
id,
|
||||
innerProps,
|
||||
}: ContextModalProps<Props>) => {
|
||||
const [priceType, setPriceType] = useState<ServicePriceType>(
|
||||
ServicePriceType.DEFAULT
|
||||
ServicePriceType.DEFAULT,
|
||||
);
|
||||
const isEditing = "onChange" in innerProps;
|
||||
const initialValues: ServiceSchema = isEditing
|
||||
? innerProps.element
|
||||
: {
|
||||
id: -1,
|
||||
name: "",
|
||||
price: 0,
|
||||
category: {
|
||||
id: -1,
|
||||
name: "",
|
||||
},
|
||||
serviceType: -1,
|
||||
priceRanges: [] as ServicePriceRangeSchema[],
|
||||
cost: null,
|
||||
categoryPrices: [],
|
||||
};
|
||||
id: -1,
|
||||
name: "",
|
||||
price: 0,
|
||||
category: {
|
||||
id: -1,
|
||||
name: "",
|
||||
dealServiceRank: "",
|
||||
productServiceRank: "",
|
||||
},
|
||||
serviceType: -1,
|
||||
priceRanges: [] as ServicePriceRangeSchema[],
|
||||
cost: null,
|
||||
categoryPrices: [],
|
||||
rank: "",
|
||||
};
|
||||
|
||||
const form = useForm<ServiceSchema>({
|
||||
initialValues: initialValues,
|
||||
@@ -82,7 +85,7 @@ const CreateServiceModal = ({
|
||||
return (
|
||||
<RangePriceInput
|
||||
{...(form.getInputProps(
|
||||
"priceRanges"
|
||||
"priceRanges",
|
||||
) as PriceRangeInputType)}
|
||||
/>
|
||||
);
|
||||
@@ -90,7 +93,7 @@ const CreateServiceModal = ({
|
||||
return (
|
||||
<PriceCategoryInput
|
||||
{...(form.getInputProps(
|
||||
"categoryPrices"
|
||||
"categoryPrices",
|
||||
) as PriceCategoryInputProps)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FC, useState } from "react";
|
||||
import ServicesTable from "../components/ServicesTable/ServicesTable.tsx";
|
||||
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
|
||||
import styles from "./ServicesPage.module.css";
|
||||
import { Button } from "@mantine/core";
|
||||
import { Button, Flex, rem, Switch } from "@mantine/core";
|
||||
import ServiceTypeSegmentedControl, {
|
||||
ServicesTab,
|
||||
} from "../components/ServiceTypeSegmentedControl/ServiceTypeSegmentedControl.tsx";
|
||||
@@ -15,6 +15,7 @@ import { ObjectStateToTableProps } from "../../../types/utils.ts";
|
||||
|
||||
export const ServicesPage: FC = () => {
|
||||
const [serviceType, setServiceType] = useState(ServicesTab.DEAL_SERVICE);
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
const {
|
||||
services,
|
||||
onServiceDelete,
|
||||
@@ -38,16 +39,24 @@ export const ServicesPage: FC = () => {
|
||||
/>
|
||||
);
|
||||
case ServicesTab.DEAL_SERVICE:
|
||||
case ServicesTab.PRODUCT_SERVICE:
|
||||
case ServicesTab.PRODUCT_SERVICE: {
|
||||
const servicesOrdered = services.filter(service => service.serviceType === serviceType).sort((a, b) => {
|
||||
if (serviceType === ServicesTab.DEAL_SERVICE) {
|
||||
return a.category.dealServiceRank.localeCompare(b.category.dealServiceRank);
|
||||
}
|
||||
return a.category.productServiceRank.localeCompare(b.category.productServiceRank);
|
||||
},
|
||||
);
|
||||
return (
|
||||
<ServicesTable
|
||||
onDelete={onServiceDelete}
|
||||
onChange={onServiceUpdate}
|
||||
items={services.filter(
|
||||
service => service.serviceType == serviceType
|
||||
)}
|
||||
items={servicesOrdered}
|
||||
serviceType={serviceType}
|
||||
editMode={isEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ServicesTab.SERVICES_PRICE_CATEGORIES:
|
||||
return (
|
||||
<ServicePriceCategoryTable
|
||||
@@ -70,7 +79,7 @@ export const ServicesPage: FC = () => {
|
||||
case ServicesTab.DEAL_SERVICE:
|
||||
case ServicesTab.PRODUCT_SERVICE:
|
||||
return (
|
||||
<>
|
||||
<Flex align={"center"} gap={rem(10)}>
|
||||
<Button
|
||||
onClick={onCreateClick}
|
||||
variant={"default"}>
|
||||
@@ -81,7 +90,14 @@ export const ServicesPage: FC = () => {
|
||||
variant={"default"}>
|
||||
Создать категорию
|
||||
</Button>
|
||||
</>
|
||||
<Switch
|
||||
variant={"default"}
|
||||
label={"Режим редактирования"}
|
||||
checked={isEditMode}
|
||||
onChange={() => setIsEditMode(!isEditMode)}
|
||||
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
case ServicesTab.SERVICES_PRICE_CATEGORIES:
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user