feat: a lot of a lot

This commit is contained in:
2024-09-01 01:05:20 +03:00
parent b93bfe39d5
commit 45d80b7c86
38 changed files with 1000 additions and 6 deletions

View File

@@ -0,0 +1,100 @@
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
import {ClientSchema, MarketplaceSchema} from "../../../../client";
import {FC} from "react";
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
import useMarketplacesTableColumns from "./columns.tsx";
import {MRT_TableOptions} from "mantine-react-table";
import {ActionIcon, Button, Flex, rem, Text, Tooltip} from "@mantine/core";
import {modals} from "@mantine/modals";
import {IconEdit, IconRefresh, IconTrash} from "@tabler/icons-react";
type RestProps = {
client?: ClientSchema;
onSynchronize?: (marketplace: MarketplaceSchema) => void;
}
type Props = CRUDTableProps<MarketplaceSchema> & RestProps;
const MarketplacesTable: FC<Props> = ({onDelete, onChange, onCreate, items, client, onSynchronize}) => {
const columns = useMarketplacesTableColumns();
const onDeleteClick = (marketplace: MarketplaceSchema) => {
if (!onDelete) return;
modals.openConfirmModal({
title: 'Удаление маркетплейса',
children: (
<Text size="sm">
Вы уверены что хотите удалить маркетплейс {marketplace.name}
</Text>
),
labels: {confirm: 'Да', cancel: "Нет"},
confirmProps: {color: 'red'},
onConfirm: () => onDelete(marketplace)
});
}
const onEditClick = (marketplace: MarketplaceSchema) => {
if (!onChange) return;
modals.openContextModal({
modal: "marketplaceFormModal",
withCloseButton: false,
innerProps: {
onChange: (event) => onChange(event),
element: marketplace
}
})
}
const onCreateClick = () => {
if (!onCreate || !client) return;
modals.openContextModal({
modal: "marketplaceFormModal",
withCloseButton: false,
innerProps: {
onCreate: (event) => onCreate({...event, client: client})
}
})
}
return (
<BaseTable
data={items}
columns={columns}
restProps={{
enableSorting: false,
enableColumnActions: false,
enableTopToolbar: true,
renderTopToolbar: (
<Flex p={rem(10)} justify="end">
<Button
disabled={!client}
variant={"default"}
onClick={() => onCreateClick()}>
Добавить
</Button>
</Flex>
),
enableRowActions: true,
renderRowActions: ({row}) => (
<Flex gap="md">
<Tooltip label="Редактировать">
<ActionIcon
onClick={() => onEditClick(row.original)}
variant={"default"}>
<IconEdit/>
</ActionIcon>
</Tooltip>
<Tooltip label="Удалить">
<ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
<IconTrash/>
</ActionIcon>
</Tooltip>
<Tooltip label="Синхронизировать">
<ActionIcon onClick={() => onSynchronize && onSynchronize(row.original)}
variant={"default"}>
<IconRefresh/>
</ActionIcon>
</Tooltip>
</Flex>
)
} as MRT_TableOptions<MarketplaceSchema>}
/>
)
}
export default MarketplacesTable;

View File

@@ -0,0 +1,35 @@
import {MarketplaceSchema} from "../../../../client";
import {MRT_ColumnDef} from "mantine-react-table";
import {useMemo} from "react";
import {ActionIcon, Image} from "@mantine/core";
const useMarketplacesTableColumns = () => {
return useMemo<MRT_ColumnDef<MarketplaceSchema>[]>(() => [
{
header: "Маркетплейс",
size: 10,
Cell: ({row}) => (
<ActionIcon variant={"transparent"}>
<Image src={row.original.baseMarketplace?.iconUrl || ""}/>
</ActionIcon>
)
},
{
accessorKey: "name",
header: "Название",
enableSorting: false,
},
{
accessorKey: "client.name",
header: "Клиент",
enableSorting: false,
},
// {
// accessorKey: "authData",
// header: "Данные авторизации",
// enableSorting: false,
// },
], []);
}
export default useMarketplacesTableColumns;

View File

@@ -0,0 +1,115 @@
import {useEffect, useState} from "react";
import {ClientSchema, MarketplaceSchema, MarketplaceService, TaskService} from "../../../client";
import {notifications} from "../../../shared/lib/notifications.ts";
import {RootState, useAppDispatch} from "../../../redux/store.ts";
import {addTask} from "../../../features/tasksSlice.tsx";
import {useSelector} from "react-redux";
const useMarketplacesPageState = () => {
const dispatch = useAppDispatch();
const tasks = useSelector((state: RootState) => state.tasks.tasks);
const [client, setClient] = useState<ClientSchema | undefined>();
const [items, setItems] = useState<MarketplaceSchema[]>([]);
const fetchMarketplaces = async () => {
if (!client) return;
MarketplaceService.getClientMarketplaces({
requestBody: {
clientId: client.id
}
}).then((response) => {
setItems(response.marketplaces);
})
}
const onCreate = (marketplace: MarketplaceSchema) => {
MarketplaceService.createMarketplace({
requestBody: {
marketplace: {
...marketplace,
clientId: marketplace.client.id,
baseMarketplaceKey: marketplace.baseMarketplace.key
}
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await fetchMarketplaces();
})
}
const onDelete = (marketplace: MarketplaceSchema) => {
MarketplaceService.deleteMarketplace({
requestBody: {
marketplaceId: marketplace.id
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await fetchMarketplaces();
})
}
const onChange = (marketplace: MarketplaceSchema) => {
MarketplaceService.updateMarketplace({
requestBody: {
marketplace: marketplace
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await fetchMarketplaces();
})
}
const onSynchronize = (marketplace: MarketplaceSchema) => {
// If there is already synchronization task for this marketplace show notifications.error()
const task = tasks.find(task => task.info.marketplaceId === marketplace.id);
if (task) {
notifications.error({
title: 'Ошибка',
message: `Синхронизация маркетплейса ${marketplace.name} уже запущена`
});
return;
}
TaskService.createSynchronizeMarketplaceTask({
requestBody: {
marketplaceId:
marketplace.id
}
}).then(({taskId}) => {
dispatch(addTask({
id: taskId,
config: {
onErrorData: {
title: 'Ошибка',
message: `Ошибка синхронизации маркетплейса: ${marketplace.name}`
},
onLoadingData: {
title: 'Синхронизация',
message: `Синхронизация маркетплейса: ${marketplace.name}`
},
onSuccessData: {
title: 'Успех',
message: `Маркетплейс ${marketplace.name} успешно синхронизирован`
}
},
info: {
marketplaceId: marketplace.id
}
}));
})
}
useEffect(() => {
fetchMarketplaces();
}, [client]);
return {
client,
setClient,
items,
onDelete,
onChange,
onCreate,
onSynchronize
}
}
export default useMarketplacesPageState;

View File

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

View File

@@ -0,0 +1,71 @@
import {TextInput} from "@mantine/core";
import {BaseFormInputProps} from "../../../../types/utils.ts";
import {FC} from "react";
import {BaseMarketplaceSchema} from "../../../../client";
import {BaseMarketplaceType} from "../../../../shared/enums/BaseMarketplaceType.ts";
type RestProps = {
baseMarketplace: BaseMarketplaceSchema;
}
type Props = BaseFormInputProps<Record<string, string>> & RestProps;
const MarketplaceAuthDataInput: FC<Props> = (props: Props) => {
console.log(props.baseMarketplace);
const getWildberriesInputs = () => {
// return input that sets record "Authorization" to value
return <TextInput
{...props}
label={"Ключ авторизации"}
placeholder={"Введите ключ авторизации"}
value={props.value["Authorization"] || ""}
onChange={(value) => props.onChange({...props.value, Authorization: value.target.value})}
/>
}
const getOzonInputs = () => {
// return input that sets record "Client-Id" and "Api-Key" to value
return (
<>
<TextInput
{...props}
label={"Client-Id"}
placeholder={"Введите Client-Id"}
value={props.value["Client-Id"] || ""}
onChange={(value) => props.onChange({...props.value, "Client-Id": value.target.value})}
/>
<TextInput
{...props}
label={"Api-Key"}
placeholder={"Введите Api-Key"}
value={props.value["Api-Key"] || ""}
onChange={(value) => props.onChange({...props.value, "Api-Key": value.target.value})}
/>
</>
)
}
const getYandexMarketInputs = () => {
}
const getInputs = () => {
if (props.baseMarketplace.key === BaseMarketplaceType.WILDBERRIES) {
return getWildberriesInputs();
}
if (props.baseMarketplace.key === BaseMarketplaceType.OZON) {
return getOzonInputs();
}
if (props.baseMarketplace.key === BaseMarketplaceType.YANDEX_MARKET) {
return getYandexMarketInputs();
}
return <></>
}
return (
<>
{getInputs()}
</>
)
}
export default MarketplaceAuthDataInput;

View File

@@ -0,0 +1,70 @@
import {ContextModalProps} from "@mantine/modals";
import BaseFormModal, {CreateEditFormProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
import {MarketplaceSchema} from "../../../../client";
import {useForm} from "@mantine/form";
import {Fieldset, Flex, rem, TextInput} from "@mantine/core";
import BaseMarketplaceSelect from "../../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import MarketplaceAuthDataInput from "./MarketplaceAuthDataInput.tsx";
type Props = CreateEditFormProps<MarketplaceSchema>
const MarketplaceFormModal = ({
context,
id,
innerProps
}: ContextModalProps<Props>) => {
const isEditing = 'element' in innerProps;
const initialValue: Partial<MarketplaceSchema> = isEditing ? innerProps.element : {
authData: {
Authorization: '',
"Client-Id": '',
"Api-Key": ''
},
};
const form = useForm<Partial<MarketplaceSchema>>({
initialValues: initialValue,
validate: {
baseMarketplace: (baseMarketplace) => !baseMarketplace && "Необходимо указать базовый маркетплейс",
name: (name) => !name && "Необходимо указать название маркетплейса",
authData: (authData) => !authData && "Необходимо указать данные авторизации"
}
});
return (
<BaseFormModal
form={form}
closeOnSubmit
onClose={() => context.closeContextModal(id)}
{...innerProps}
>
<BaseFormModal.Body>
<>
<Fieldset legend={"Общие параметры"}>
<Flex direction={"column"} gap={rem(10)}>
<TextInput
label={"Название"}
placeholder={"Введите название маркетплейса"}
{...form.getInputProps("name")}
/>
<BaseMarketplaceSelect
label={"Базовый маркетплейс"}
placeholder={"Выберите базовый маркетплейс"}
{...form.getInputProps("baseMarketplace")}
/>
{form.values.baseMarketplace &&
<MarketplaceAuthDataInput
baseMarketplace={form.values.baseMarketplace}
value={form.values.authData as Record<string, string>}
onChange={(value) => form.setFieldValue("authData", value)}
error={form.getInputProps("authData").error}
/>
}
</Flex>
</Fieldset>
</>
</BaseFormModal.Body>
</BaseFormModal>
)
}
export default MarketplaceFormModal;

View File

@@ -0,0 +1,17 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
gap: rem(10);
}
.top-panel {
padding: rem(5);
gap: rem(10);
display: flex;
}
.top-panel-last-item {
margin-left: auto;
}

View File

@@ -0,0 +1,29 @@
import styles from './MarketplacesPage.module.css';
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
import useMarketplacesPageState from "../hooks/useMarketplacesPageState.tsx";
import MarketplacesTable from "../components/MarketplacesTable/MarketplacesTable.tsx";
export const MarketplacesPage = () => {
const state = useMarketplacesPageState();
return (
<div className={styles['container']}>
<PageBlock>
<div className={styles['top-panel']}>
<ClientSelectNew
placeholder={'Выберите клиента'}
onChange={state.setClient}
/>
</div>
</PageBlock>
<PageBlock>
<>
<MarketplacesTable
{...state}
/>
</>
</PageBlock>
</div>
)
}