diff --git a/src/app/clients/components/desktop/ClientDesktopHeader/ClientDesktopHeader.tsx b/src/app/clients/components/desktop/ClientDesktopHeader/ClientDesktopHeader.tsx
new file mode 100644
index 0000000..d495c33
--- /dev/null
+++ b/src/app/clients/components/desktop/ClientDesktopHeader/ClientDesktopHeader.tsx
@@ -0,0 +1,23 @@
+import { FC } from "react";
+import { Group, TextInput } from "@mantine/core";
+import { useClientsContext } from "@/app/clients/contexts/ClientsContext";
+import useClientsActions from "@/app/clients/hooks/useClientsActions";
+import InlineButton from "@/components/ui/InlineButton/InlineButton";
+
+const ClientDesktopHeader: FC = () => {
+ const { search, setSearch } = useClientsContext();
+ const { onCreateClick } = useClientsActions();
+
+ return (
+
+ Создать клиента
+ setSearch(e.target.value)}
+ />
+
+ );
+};
+
+export default ClientDesktopHeader;
diff --git a/src/app/clients/components/mobile/ClientMobileHeader/ClientMobileHeader.tsx b/src/app/clients/components/mobile/ClientMobileHeader/ClientMobileHeader.tsx
new file mode 100644
index 0000000..c2f9db3
--- /dev/null
+++ b/src/app/clients/components/mobile/ClientMobileHeader/ClientMobileHeader.tsx
@@ -0,0 +1,31 @@
+import { FC } from "react";
+import { Flex, TextInput } from "@mantine/core";
+import { useClientsContext } from "@/app/clients/contexts/ClientsContext";
+import useClientsActions from "@/app/clients/hooks/useClientsActions";
+import InlineButton from "@/components/ui/InlineButton/InlineButton";
+
+const ClientMobileHeader: FC = () => {
+ const { search, setSearch } = useClientsContext();
+ const { onCreateClick } = useClientsActions();
+
+ return (
+
+
+ Создать клиента
+
+ setSearch(e.target.value)}
+ />
+
+ );
+};
+
+export default ClientMobileHeader;
diff --git a/src/app/clients/components/shared/ClientsTable/ClientsTable.tsx b/src/app/clients/components/shared/ClientsTable/ClientsTable.tsx
new file mode 100644
index 0000000..c455f51
--- /dev/null
+++ b/src/app/clients/components/shared/ClientsTable/ClientsTable.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import { FC } from "react";
+import { useClientsTableColumns } from "@/app/clients/components/shared/ClientsTable/columns";
+import { useClientsContext } from "@/app/clients/contexts/ClientsContext";
+import useClientsActions from "@/app/clients/hooks/useClientsActions";
+import BaseTable from "@/components/ui/BaseTable/BaseTable";
+import useIsMobile from "@/hooks/utils/useIsMobile";
+
+const ClientsTable: FC = () => {
+ const isMobile = useIsMobile();
+ const { clientsCrud, clients } = useClientsContext();
+ const { onUpdateClick } = useClientsActions();
+
+ const columns = useClientsTableColumns({
+ onDelete: clientsCrud.onDelete,
+ onChange: onUpdateClick,
+ });
+
+ return (
+
+ );
+};
+
+export default ClientsTable;
diff --git a/src/app/clients/components/shared/ClientsTable/columns.tsx b/src/app/clients/components/shared/ClientsTable/columns.tsx
new file mode 100644
index 0000000..368c121
--- /dev/null
+++ b/src/app/clients/components/shared/ClientsTable/columns.tsx
@@ -0,0 +1,58 @@
+import { useMemo } from "react";
+import { DataTableColumn } from "mantine-datatable";
+import { Center } from "@mantine/core";
+import UpdateDeleteTableActions from "@/components/ui/BaseTable/components/UpdateDeleteTableActions";
+import { ClientSchema } from "@/lib/client";
+
+type Props = {
+ onChange: (client: ClientSchema) => void;
+ onDelete: (client: ClientSchema) => void;
+};
+
+export const useClientsTableColumns = ({ onChange, onDelete }: Props) => {
+ return useMemo(
+ () =>
+ [
+ {
+ accessor: "actions",
+ title:
Действия,
+ width: "0%",
+ render: client => (
+ onDelete(client)}
+ onChange={() => onChange(client)}
+ />
+ ),
+ },
+ {
+ accessor: "name",
+ title: "Имя",
+ },
+ {
+ accessor: "details.telegram",
+ title: "Телеграм",
+ },
+ {
+ accessor: "details.email",
+ title: "Почта",
+ },
+ {
+ accessor: "details.phoneNumber",
+ title: "Телефон",
+ },
+ {
+ accessor: "details.inn",
+ title: "ИНН",
+ },
+ {
+ accessor: "companyName",
+ title: "Название компании",
+ },
+ {
+ accessor: "comment",
+ title: "Комментарий",
+ },
+ ] as DataTableColumn[],
+ [onChange, onDelete]
+ );
+};
diff --git a/src/app/clients/components/shared/PageBody/PageBody.tsx b/src/app/clients/components/shared/PageBody/PageBody.tsx
new file mode 100644
index 0000000..b7c512d
--- /dev/null
+++ b/src/app/clients/components/shared/PageBody/PageBody.tsx
@@ -0,0 +1,37 @@
+"use client";
+
+import { FC } from "react";
+import { Stack } from "@mantine/core";
+import ClientDesktopHeader from "@/app/clients/components/desktop/ClientDesktopHeader/ClientDesktopHeader";
+import ClientsTable from "@/app/clients/components/shared/ClientsTable/ClientsTable";
+import PageBlock from "@/components/layout/PageBlock/PageBlock";
+import useIsMobile from "@/hooks/utils/useIsMobile";
+import ClientMobileHeader from "@/app/clients/components/mobile/ClientMobileHeader/ClientMobileHeader";
+
+const PageBody: FC = () => {
+ const isMobile = useIsMobile();
+
+ return (
+
+ {!isMobile && (
+
+
+
+ )}
+
+
+ {isMobile && }
+
+
+
+
+
+
+ );
+};
+
+export default PageBody;
diff --git a/src/app/clients/contexts/ClientsContext.tsx b/src/app/clients/contexts/ClientsContext.tsx
new file mode 100644
index 0000000..4b74d14
--- /dev/null
+++ b/src/app/clients/contexts/ClientsContext.tsx
@@ -0,0 +1,39 @@
+"use client";
+
+import { Dispatch, SetStateAction } from "react";
+import {
+ ClientsCrud,
+ useClientsCrud,
+} from "@/app/clients/hooks/useClientsCrud";
+import useClientsFilter from "@/app/clients/hooks/useClientsFilter";
+import useClientsList from "@/app/clients/hooks/useClientsList";
+import { ClientSchema } from "@/lib/client";
+import makeContext from "@/lib/contextFactory/contextFactory";
+
+type ClientsContextState = {
+ clients: ClientSchema[];
+ refetchClients: () => void;
+ search: string;
+ setSearch: Dispatch>;
+ clientsCrud: ClientsCrud;
+};
+
+const useClientsContextState = (): ClientsContextState => {
+ const clientsList = useClientsList();
+
+ const { filteredClients, search, setSearch } =
+ useClientsFilter(clientsList);
+
+ const clientsCrud = useClientsCrud(clientsList);
+
+ return {
+ clients: filteredClients,
+ refetchClients: clientsList.refetch,
+ search,
+ setSearch,
+ clientsCrud,
+ };
+};
+
+export const [ClientsContextProvider, useClientsContext] =
+ makeContext(useClientsContextState, "Clients");
diff --git a/src/app/clients/hooks/useClientsActions.tsx b/src/app/clients/hooks/useClientsActions.tsx
new file mode 100644
index 0000000..a91f56a
--- /dev/null
+++ b/src/app/clients/hooks/useClientsActions.tsx
@@ -0,0 +1,37 @@
+import { modals } from "@mantine/modals";
+import { useClientsContext } from "@/app/clients/contexts/ClientsContext";
+import { ClientSchema } from "@/lib/client";
+
+const useClientsActions = () => {
+ const { clientsCrud } = useClientsContext();
+
+ const onCreateClick = () => {
+ modals.openContextModal({
+ modal: "clientEditorModal",
+ title: "Создание клиента",
+ innerProps: {
+ onCreate: clientsCrud.onCreate,
+ isEditing: false,
+ },
+ });
+ };
+
+ const onUpdateClick = (client: ClientSchema) => {
+ modals.openContextModal({
+ modal: "clientEditorModal",
+ title: "Редактирование клиента",
+ innerProps: {
+ onChange: updates => clientsCrud.onUpdate(client.id, updates),
+ entity: client,
+ isEditing: true,
+ },
+ });
+ };
+
+ return {
+ onCreateClick,
+ onUpdateClick,
+ };
+};
+
+export default useClientsActions;
diff --git a/src/app/clients/hooks/useClientsCrud.tsx b/src/app/clients/hooks/useClientsCrud.tsx
new file mode 100644
index 0000000..ca13248
--- /dev/null
+++ b/src/app/clients/hooks/useClientsCrud.tsx
@@ -0,0 +1,45 @@
+import { useCrudOperations } from "@/hooks/cruds/baseCrud";
+import {
+ ClientSchema,
+ CreateClientSchema,
+ UpdateClientSchema,
+} from "@/lib/client";
+import {
+ createClientMutation,
+ deleteClientMutation,
+ updateClientMutation,
+} from "@/lib/client/@tanstack/react-query.gen";
+
+type UseClientsProps = {
+ queryKey: any[];
+};
+
+export type ClientsCrud = {
+ onCreate: (client: CreateClientSchema) => void;
+ onUpdate: (clientId: number, client: UpdateClientSchema) => void;
+ onDelete: (client: ClientSchema) => void;
+};
+
+export const useClientsCrud = ({ queryKey }: UseClientsProps): ClientsCrud => {
+ return useCrudOperations<
+ ClientSchema,
+ UpdateClientSchema,
+ CreateClientSchema
+ >({
+ key: "getClients",
+ queryKey,
+ mutations: {
+ create: createClientMutation(),
+ update: updateClientMutation(),
+ delete: deleteClientMutation(),
+ },
+ getUpdateEntity: (old, update) => ({
+ ...old,
+ details: update.details ?? old.details,
+ name: update.name ?? old.name,
+ companyName: update.companyName ?? old.companyName,
+ comment: update.comment ?? old.comment,
+ }),
+ getDeleteConfirmTitle: () => "Удаление клиента",
+ });
+};
diff --git a/src/app/clients/hooks/useClientsFilter.tsx b/src/app/clients/hooks/useClientsFilter.tsx
new file mode 100644
index 0000000..7d7aa3f
--- /dev/null
+++ b/src/app/clients/hooks/useClientsFilter.tsx
@@ -0,0 +1,46 @@
+import { useEffect, useState } from "react";
+import { useDebouncedValue } from "@mantine/hooks";
+import { ClientSchema } from "@/lib/client";
+
+type Props = {
+ clients: ClientSchema[];
+};
+
+const useClientsFilter = ({ clients }: Props) => {
+ const [search, setSearch] = useState("");
+ const [debouncedSearch] = useDebouncedValue(search, 400);
+ const [filteredClients, setFilteredClients] = useState([]);
+
+ const filterClients = () => {
+ if (debouncedSearch.length === 0) {
+ setFilteredClients(clients);
+ return;
+ }
+
+ const loweredSearch = debouncedSearch.toLowerCase();
+ const filtered = clients.filter(
+ client =>
+ client.name.toLowerCase().includes(loweredSearch) ||
+ client.details?.inn?.includes(loweredSearch) ||
+ client.details?.email?.toLowerCase().includes(loweredSearch) ||
+ client.details?.telegram
+ ?.toLowerCase()
+ .includes(loweredSearch) ||
+ client.details?.phoneNumber?.includes(loweredSearch) ||
+ client.companyName.toLowerCase().includes(loweredSearch)
+ );
+ setFilteredClients(filtered);
+ };
+
+ useEffect(() => {
+ filterClients();
+ }, [debouncedSearch, clients]);
+
+ return {
+ search,
+ setSearch,
+ filteredClients,
+ };
+};
+
+export default useClientsFilter;
diff --git a/src/app/clients/hooks/useClientsList.tsx b/src/app/clients/hooks/useClientsList.tsx
new file mode 100644
index 0000000..5305ddf
--- /dev/null
+++ b/src/app/clients/hooks/useClientsList.tsx
@@ -0,0 +1,17 @@
+import { useMemo } from "react";
+import { useQuery } from "@tanstack/react-query";
+import {
+ getClientsOptions,
+ getClientsQueryKey,
+} from "@/lib/client/@tanstack/react-query.gen";
+
+const useClientsList = () => {
+ const { data, refetch } = useQuery(getClientsOptions());
+ const clients = useMemo(() => data?.items ?? [], [data]);
+
+ const queryKey = getClientsQueryKey();
+
+ return { clients, refetch, queryKey };
+};
+
+export default useClientsList;
diff --git a/src/app/clients/modals/ClientFormModal/ClientFormModal.tsx b/src/app/clients/modals/ClientFormModal/ClientFormModal.tsx
new file mode 100644
index 0000000..6625920
--- /dev/null
+++ b/src/app/clients/modals/ClientFormModal/ClientFormModal.tsx
@@ -0,0 +1,105 @@
+"use client";
+
+import { Fieldset, Stack, Textarea, TextInput } from "@mantine/core";
+import { useForm } from "@mantine/form";
+import { ContextModalProps } from "@mantine/modals";
+import isValidInn from "@/app/clients/utils/isValidInn";
+import {
+ ClientSchema,
+ CreateClientSchema,
+ UpdateClientSchema,
+} from "@/lib/client";
+import BaseFormModal, {
+ CreateEditFormProps,
+} from "@/modals/base/BaseFormModal/BaseFormModal";
+
+type Props = CreateEditFormProps<
+ ClientSchema,
+ CreateClientSchema,
+ UpdateClientSchema
+>;
+
+const ClientEditorModal = ({
+ context,
+ id,
+ innerProps,
+}: ContextModalProps) => {
+ const initialValues = innerProps.isEditing
+ ? innerProps.entity
+ : ({
+ name: "",
+ companyName: "",
+ details: {
+ telegram: "",
+ phoneNumber: "",
+ email: "",
+ inn: "",
+ },
+ comment: "",
+ } as CreateClientSchema);
+
+ const form = useForm({
+ initialValues,
+ validate: {
+ name: name =>
+ (!name || name.trim() === "") &&
+ "Необходимо ввести название клиента",
+ details: {
+ inn: inn => inn.length > 0 && !isValidInn(inn) && "Некорректный ИНН",
+ },
+ },
+ });
+
+ return (
+ context.closeContextModal(id)}>
+
+
+
+ );
+};
+
+export default ClientEditorModal;
diff --git a/src/app/clients/page.tsx b/src/app/clients/page.tsx
new file mode 100644
index 0000000..d0dece7
--- /dev/null
+++ b/src/app/clients/page.tsx
@@ -0,0 +1,22 @@
+import { Suspense } from "react";
+import { Center, Loader } from "@mantine/core";
+import { ClientsContextProvider } from "@/app/clients/contexts/ClientsContext";
+import PageContainer from "@/components/layout/PageContainer/PageContainer";
+import PageBody from "@/app/clients/components/shared/PageBody/PageBody";
+
+export default async function ClientsPage() {
+ return (
+
+
+
+ }>
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/clients/utils/isValidInn.ts b/src/app/clients/utils/isValidInn.ts
new file mode 100644
index 0000000..869255d
--- /dev/null
+++ b/src/app/clients/utils/isValidInn.ts
@@ -0,0 +1,5 @@
+const isValidInn = (inn: string | null | undefined) => {
+ return inn && inn.match(/^(\d{12}|\d{10})$/);
+};
+
+export default isValidInn;
diff --git a/src/components/layout/Navbar/Navbar.tsx b/src/components/layout/Navbar/Navbar.tsx
index 3ea4d08..8545fd7 100644
--- a/src/components/layout/Navbar/Navbar.tsx
+++ b/src/components/layout/Navbar/Navbar.tsx
@@ -2,6 +2,7 @@ import {
IconColumns,
IconFileBarcode,
IconLayoutKanban,
+ IconUsers,
} from "@tabler/icons-react";
import { Box, Flex } from "@mantine/core";
import PageBlock from "@/components/layout/PageBlock/PageBlock";
@@ -16,6 +17,11 @@ const linksData = [
label: "Сделки",
href: "/deals",
},
+ {
+ icon: IconUsers,
+ label: "Клиенты",
+ href: "/clients",
+ },
{
icon: IconColumns,
label: "Услуги",
diff --git a/src/hooks/cruds/baseCrud/useCrudOperations.tsx b/src/hooks/cruds/baseCrud/useCrudOperations.tsx
index f6f058d..1d55a6c 100644
--- a/src/hooks/cruds/baseCrud/useCrudOperations.tsx
+++ b/src/hooks/cruds/baseCrud/useCrudOperations.tsx
@@ -5,13 +5,13 @@ import { Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import getCommonQueryClient from "@/hooks/cruds/baseCrud/getCommonQueryClient";
import { HttpValidationError } from "@/lib/client";
+import { sortByLexorank } from "@/utils/lexorank/sort";
import {
BaseEntity,
CreateMutationOptions,
DeleteMutationOptions,
UpdateMutationOptions,
} from "./types";
-import { sortByLexorank } from "@/utils/lexorank/sort";
type CrudOperations<
TEntity,
diff --git a/src/lib/client/@tanstack/react-query.gen.ts b/src/lib/client/@tanstack/react-query.gen.ts
index b22178f..8c4aac3 100644
--- a/src/lib/client/@tanstack/react-query.gen.ts
+++ b/src/lib/client/@tanstack/react-query.gen.ts
@@ -13,6 +13,7 @@ import {
addKitToDealProduct,
createBarcodeTemplate,
createBoard,
+ createClient,
createDeal,
createDealProduct,
createDealProductService,
@@ -25,6 +26,7 @@ import {
createStatus,
deleteBarcodeTemplate,
deleteBoard,
+ deleteClient,
deleteDeal,
deleteDealProduct,
deleteDealProductService,
@@ -41,6 +43,7 @@ import {
getBarcodeTemplateSizes,
getBoards,
getBuiltInModules,
+ getClients,
getDealProducts,
getDeals,
getDealServices,
@@ -53,6 +56,7 @@ import {
getStatusHistory,
updateBarcodeTemplate,
updateBoard,
+ updateClient,
updateDeal,
updateDealProduct,
updateDealProductService,
@@ -78,6 +82,9 @@ import type {
CreateBoardData,
CreateBoardError,
CreateBoardResponse2,
+ CreateClientData,
+ CreateClientError,
+ CreateClientResponse2,
CreateDealData,
CreateDealError,
CreateDealProductData,
@@ -114,6 +121,9 @@ import type {
DeleteBoardData,
DeleteBoardError,
DeleteBoardResponse2,
+ DeleteClientData,
+ DeleteClientError,
+ DeleteClientResponse2,
DeleteDealData,
DeleteDealError,
DeleteDealProductData,
@@ -152,6 +162,7 @@ import type {
GetBarcodeTemplateSizesData,
GetBoardsData,
GetBuiltInModulesData,
+ GetClientsData,
GetDealProductsData,
GetDealsData,
GetDealsError,
@@ -172,6 +183,9 @@ import type {
UpdateBoardData,
UpdateBoardError,
UpdateBoardResponse2,
+ UpdateClientData,
+ UpdateClientError,
+ UpdateClientResponse2,
UpdateDealData,
UpdateDealError,
UpdateDealProductData,
@@ -865,6 +879,129 @@ export const getStatusHistoryOptions = (
});
};
+export const getClientsQueryKey = (options?: Options) =>
+ createQueryKey("getClients", options);
+
+/**
+ * Get Clients
+ */
+export const getClientsOptions = (options?: Options) => {
+ return queryOptions({
+ queryFn: async ({ queryKey, signal }) => {
+ const { data } = await getClients({
+ ...options,
+ ...queryKey[0],
+ signal,
+ throwOnError: true,
+ });
+ return data;
+ },
+ queryKey: getClientsQueryKey(options),
+ });
+};
+
+export const createClientQueryKey = (options: Options) =>
+ createQueryKey("createClient", options);
+
+/**
+ * Create Client
+ */
+export const createClientOptions = (options: Options) => {
+ return queryOptions({
+ queryFn: async ({ queryKey, signal }) => {
+ const { data } = await createClient({
+ ...options,
+ ...queryKey[0],
+ signal,
+ throwOnError: true,
+ });
+ return data;
+ },
+ queryKey: createClientQueryKey(options),
+ });
+};
+
+/**
+ * Create Client
+ */
+export const createClientMutation = (
+ options?: Partial>
+): UseMutationOptions<
+ CreateClientResponse2,
+ AxiosError,
+ Options
+> => {
+ const mutationOptions: UseMutationOptions<
+ CreateClientResponse2,
+ AxiosError,
+ Options
+ > = {
+ mutationFn: async localOptions => {
+ const { data } = await createClient({
+ ...options,
+ ...localOptions,
+ throwOnError: true,
+ });
+ return data;
+ },
+ };
+ return mutationOptions;
+};
+
+/**
+ * Delete Product
+ */
+export const deleteClientMutation = (
+ options?: Partial>
+): UseMutationOptions<
+ DeleteClientResponse2,
+ AxiosError,
+ Options
+> => {
+ const mutationOptions: UseMutationOptions<
+ DeleteClientResponse2,
+ AxiosError,
+ Options
+ > = {
+ mutationFn: async localOptions => {
+ const { data } = await deleteClient({
+ ...options,
+ ...localOptions,
+ throwOnError: true,
+ });
+ return data;
+ },
+ };
+ return mutationOptions;
+};
+
+/**
+ * Update Client
+ */
+export const updateClientMutation = (
+ options?: Partial>
+): UseMutationOptions<
+ UpdateClientResponse2,
+ AxiosError,
+ Options
+> => {
+ const mutationOptions: UseMutationOptions<
+ UpdateClientResponse2,
+ AxiosError,
+ Options
+ > = {
+ mutationFn: async localOptions => {
+ const { data } = await updateClient({
+ ...options,
+ ...localOptions,
+ throwOnError: true,
+ });
+ return data;
+ },
+ };
+ return mutationOptions;
+};
+
export const getBarcodeTemplatesQueryKey = (
options?: Options
) => createQueryKey("getBarcodeTemplates", options);
diff --git a/src/lib/client/sdk.gen.ts b/src/lib/client/sdk.gen.ts
index 11a2689..7110544 100644
--- a/src/lib/client/sdk.gen.ts
+++ b/src/lib/client/sdk.gen.ts
@@ -15,6 +15,9 @@ import type {
CreateBoardData,
CreateBoardErrors,
CreateBoardResponses,
+ CreateClientData,
+ CreateClientErrors,
+ CreateClientResponses,
CreateDealData,
CreateDealErrors,
CreateDealProductData,
@@ -51,6 +54,9 @@ import type {
DeleteBoardData,
DeleteBoardErrors,
DeleteBoardResponses,
+ DeleteClientData,
+ DeleteClientErrors,
+ DeleteClientResponses,
DeleteDealData,
DeleteDealErrors,
DeleteDealProductData,
@@ -95,6 +101,8 @@ import type {
GetBoardsResponses,
GetBuiltInModulesData,
GetBuiltInModulesResponses,
+ GetClientsData,
+ GetClientsResponses,
GetDealProductsData,
GetDealProductsErrors,
GetDealProductsResponses,
@@ -127,6 +135,9 @@ import type {
UpdateBoardData,
UpdateBoardErrors,
UpdateBoardResponses,
+ UpdateClientData,
+ UpdateClientErrors,
+ UpdateClientResponses,
UpdateDealData,
UpdateDealErrors,
UpdateDealProductData,
@@ -167,6 +178,8 @@ import {
zCreateBarcodeTemplateResponse2,
zCreateBoardData,
zCreateBoardResponse2,
+ zCreateClientData,
+ zCreateClientResponse2,
zCreateDealData,
zCreateDealProductData,
zCreateDealProductResponse2,
@@ -191,6 +204,8 @@ import {
zDeleteBarcodeTemplateResponse2,
zDeleteBoardData,
zDeleteBoardResponse2,
+ zDeleteClientData,
+ zDeleteClientResponse2,
zDeleteDealData,
zDeleteDealProductData,
zDeleteDealProductResponse2,
@@ -223,6 +238,8 @@ import {
zGetBoardsResponse2,
zGetBuiltInModulesData,
zGetBuiltInModulesResponse,
+ zGetClientsData,
+ zGetClientsResponse2,
zGetDealProductsData,
zGetDealProductsResponse2,
zGetDealsData,
@@ -247,6 +264,8 @@ import {
zUpdateBarcodeTemplateResponse2,
zUpdateBoardData,
zUpdateBoardResponse2,
+ zUpdateClientData,
+ zUpdateClientResponse2,
zUpdateDealData,
zUpdateDealProductData,
zUpdateDealProductResponse2,
@@ -732,6 +751,106 @@ export const getStatusHistory = (
});
};
+/**
+ * Get Clients
+ */
+export const getClients = (
+ options?: Options
+) => {
+ return (options?.client ?? _heyApiClient).get<
+ GetClientsResponses,
+ unknown,
+ ThrowOnError
+ >({
+ requestValidator: async data => {
+ return await zGetClientsData.parseAsync(data);
+ },
+ responseType: "json",
+ responseValidator: async data => {
+ return await zGetClientsResponse2.parseAsync(data);
+ },
+ url: "/modules/clients/client/",
+ ...options,
+ });
+};
+
+/**
+ * Create Client
+ */
+export const createClient = (
+ options: Options
+) => {
+ return (options.client ?? _heyApiClient).post<
+ CreateClientResponses,
+ CreateClientErrors,
+ ThrowOnError
+ >({
+ requestValidator: async data => {
+ return await zCreateClientData.parseAsync(data);
+ },
+ responseType: "json",
+ responseValidator: async data => {
+ return await zCreateClientResponse2.parseAsync(data);
+ },
+ url: "/modules/clients/client/",
+ ...options,
+ headers: {
+ "Content-Type": "application/json",
+ ...options.headers,
+ },
+ });
+};
+
+/**
+ * Delete Product
+ */
+export const deleteClient = (
+ options: Options
+) => {
+ return (options.client ?? _heyApiClient).delete<
+ DeleteClientResponses,
+ DeleteClientErrors,
+ ThrowOnError
+ >({
+ requestValidator: async data => {
+ return await zDeleteClientData.parseAsync(data);
+ },
+ responseType: "json",
+ responseValidator: async data => {
+ return await zDeleteClientResponse2.parseAsync(data);
+ },
+ url: "/modules/clients/client/{pk}",
+ ...options,
+ });
+};
+
+/**
+ * Update Client
+ */
+export const updateClient = (
+ options: Options
+) => {
+ return (options.client ?? _heyApiClient).patch<
+ UpdateClientResponses,
+ UpdateClientErrors,
+ ThrowOnError
+ >({
+ requestValidator: async data => {
+ return await zUpdateClientData.parseAsync(data);
+ },
+ responseType: "json",
+ responseValidator: async data => {
+ return await zUpdateClientResponse2.parseAsync(data);
+ },
+ url: "/modules/clients/client/{pk}",
+ ...options,
+ headers: {
+ "Content-Type": "application/json",
+ ...options.headers,
+ },
+ });
+};
+
/**
* Get Barcode Templates
*/
diff --git a/src/lib/client/types.gen.ts b/src/lib/client/types.gen.ts
index 5c33e76..f36fef2 100644
--- a/src/lib/client/types.gen.ts
+++ b/src/lib/client/types.gen.ts
@@ -171,6 +171,51 @@ export type BuiltInModuleTabSchema = {
device: string;
};
+/**
+ * ClientDetailsSchema
+ */
+export type ClientDetailsSchema = {
+ /**
+ * Telegram
+ */
+ telegram: string;
+ /**
+ * Phonenumber
+ */
+ phoneNumber: string;
+ /**
+ * Inn
+ */
+ inn: string;
+ /**
+ * Email
+ */
+ email: string;
+};
+
+/**
+ * ClientSchema
+ */
+export type ClientSchema = {
+ /**
+ * Name
+ */
+ name: string;
+ /**
+ * Companyname
+ */
+ companyName: string;
+ /**
+ * Comment
+ */
+ comment?: string | null;
+ details: ClientDetailsSchema;
+ /**
+ * Id
+ */
+ id: number;
+};
+
/**
* CreateBarcodeTemplateRequest
*/
@@ -244,6 +289,42 @@ export type CreateBoardSchema = {
lexorank: string;
};
+/**
+ * CreateClientRequest
+ */
+export type CreateClientRequest = {
+ entity: CreateClientSchema;
+};
+
+/**
+ * CreateClientResponse
+ */
+export type CreateClientResponse = {
+ /**
+ * Message
+ */
+ message: string;
+};
+
+/**
+ * CreateClientSchema
+ */
+export type CreateClientSchema = {
+ /**
+ * Name
+ */
+ name: string;
+ /**
+ * Companyname
+ */
+ companyName: string;
+ /**
+ * Comment
+ */
+ comment?: string | null;
+ details: ClientDetailsSchema;
+};
+
/**
* CreateDealProductRequest
*/
@@ -799,6 +880,16 @@ export type DeleteBoardResponse = {
message: string;
};
+/**
+ * DeleteClientResponse
+ */
+export type DeleteClientResponse = {
+ /**
+ * Message
+ */
+ message: string;
+};
+
/**
* DeleteDealProductResponse
*/
@@ -949,6 +1040,16 @@ export type GetBoardsResponse = {
items: Array;
};
+/**
+ * GetClientsResponse
+ */
+export type GetClientsResponse = {
+ /**
+ * Items
+ */
+ items: Array;
+};
+
/**
* GetDealProductsResponse
*/
@@ -1419,6 +1520,42 @@ export type UpdateBoardSchema = {
lexorank?: string | null;
};
+/**
+ * UpdateClientRequest
+ */
+export type UpdateClientRequest = {
+ entity: UpdateClientSchema;
+};
+
+/**
+ * UpdateClientResponse
+ */
+export type UpdateClientResponse = {
+ /**
+ * Message
+ */
+ message: string;
+};
+
+/**
+ * UpdateClientSchema
+ */
+export type UpdateClientSchema = {
+ /**
+ * Name
+ */
+ name?: string | null;
+ /**
+ * Companyname
+ */
+ companyName?: string | null;
+ /**
+ * Comment
+ */
+ comment?: string | null;
+ details?: ClientDetailsSchema | null;
+};
+
/**
* UpdateDealProductRequest
*/
@@ -2357,6 +2494,111 @@ export type GetStatusHistoryResponses = {
export type GetStatusHistoryResponse2 =
GetStatusHistoryResponses[keyof GetStatusHistoryResponses];
+export type GetClientsData = {
+ body?: never;
+ path?: never;
+ query?: never;
+ url: "/modules/clients/client/";
+};
+
+export type GetClientsResponses = {
+ /**
+ * Successful Response
+ */
+ 200: GetClientsResponse;
+};
+
+export type GetClientsResponse2 =
+ GetClientsResponses[keyof GetClientsResponses];
+
+export type CreateClientData = {
+ body: CreateClientRequest;
+ path?: never;
+ query?: never;
+ url: "/modules/clients/client/";
+};
+
+export type CreateClientErrors = {
+ /**
+ * Validation Error
+ */
+ 422: HttpValidationError;
+};
+
+export type CreateClientError = CreateClientErrors[keyof CreateClientErrors];
+
+export type CreateClientResponses = {
+ /**
+ * Successful Response
+ */
+ 200: CreateClientResponse;
+};
+
+export type CreateClientResponse2 =
+ CreateClientResponses[keyof CreateClientResponses];
+
+export type DeleteClientData = {
+ body?: never;
+ path: {
+ /**
+ * Pk
+ */
+ pk: number;
+ };
+ query?: never;
+ url: "/modules/clients/client/{pk}";
+};
+
+export type DeleteClientErrors = {
+ /**
+ * Validation Error
+ */
+ 422: HttpValidationError;
+};
+
+export type DeleteClientError = DeleteClientErrors[keyof DeleteClientErrors];
+
+export type DeleteClientResponses = {
+ /**
+ * Successful Response
+ */
+ 200: DeleteClientResponse;
+};
+
+export type DeleteClientResponse2 =
+ DeleteClientResponses[keyof DeleteClientResponses];
+
+export type UpdateClientData = {
+ body: UpdateClientRequest;
+ path: {
+ /**
+ * Pk
+ */
+ pk: number;
+ };
+ query?: never;
+ url: "/modules/clients/client/{pk}";
+};
+
+export type UpdateClientErrors = {
+ /**
+ * Validation Error
+ */
+ 422: HttpValidationError;
+};
+
+export type UpdateClientError = UpdateClientErrors[keyof UpdateClientErrors];
+
+export type UpdateClientResponses = {
+ /**
+ * Successful Response
+ */
+ 200: UpdateClientResponse;
+};
+
+export type UpdateClientResponse2 =
+ UpdateClientResponses[keyof UpdateClientResponses];
+
export type GetBarcodeTemplatesData = {
body?: never;
path?: never;
diff --git a/src/lib/client/zod.gen.ts b/src/lib/client/zod.gen.ts
index 21b48b0..bd2fba8 100644
--- a/src/lib/client/zod.gen.ts
+++ b/src/lib/client/zod.gen.ts
@@ -89,6 +89,27 @@ export const zBuiltInModuleSchemaOutput = z.object({
tabs: z.array(zBuiltInModuleTabSchema),
});
+/**
+ * ClientDetailsSchema
+ */
+export const zClientDetailsSchema = z.object({
+ telegram: z.string(),
+ phoneNumber: z.string(),
+ inn: z.string(),
+ email: z.string(),
+});
+
+/**
+ * ClientSchema
+ */
+export const zClientSchema = z.object({
+ name: z.string(),
+ companyName: z.string(),
+ comment: z.optional(z.union([z.string(), z.null()])),
+ details: zClientDetailsSchema,
+ id: z.int(),
+});
+
/**
* CreateBarcodeTemplateSchema
*/
@@ -138,6 +159,30 @@ export const zCreateBoardResponse = z.object({
entity: zBoardSchema,
});
+/**
+ * CreateClientSchema
+ */
+export const zCreateClientSchema = z.object({
+ name: z.string(),
+ companyName: z.string(),
+ comment: z.optional(z.union([z.string(), z.null()])),
+ details: zClientDetailsSchema,
+});
+
+/**
+ * CreateClientRequest
+ */
+export const zCreateClientRequest = z.object({
+ entity: zCreateClientSchema,
+});
+
+/**
+ * CreateClientResponse
+ */
+export const zCreateClientResponse = z.object({
+ message: z.string(),
+});
+
/**
* CreateDealProductSchema
*/
@@ -563,6 +608,13 @@ export const zDeleteBoardResponse = z.object({
message: z.string(),
});
+/**
+ * DeleteClientResponse
+ */
+export const zDeleteClientResponse = z.object({
+ message: z.string(),
+});
+
/**
* DeleteDealProductResponse
*/
@@ -668,6 +720,13 @@ export const zGetBoardsResponse = z.object({
items: z.array(zBoardSchema),
});
+/**
+ * GetClientsResponse
+ */
+export const zGetClientsResponse = z.object({
+ items: z.array(zClientSchema),
+});
+
/**
* GetDealProductsResponse
*/
@@ -851,6 +910,30 @@ export const zUpdateBoardResponse = z.object({
message: z.string(),
});
+/**
+ * UpdateClientSchema
+ */
+export const zUpdateClientSchema = z.object({
+ name: z.optional(z.union([z.string(), z.null()])),
+ companyName: z.optional(z.union([z.string(), z.null()])),
+ comment: z.optional(z.union([z.string(), z.null()])),
+ details: z.optional(z.union([zClientDetailsSchema, z.null()])),
+});
+
+/**
+ * UpdateClientRequest
+ */
+export const zUpdateClientRequest = z.object({
+ entity: zUpdateClientSchema,
+});
+
+/**
+ * UpdateClientResponse
+ */
+export const zUpdateClientResponse = z.object({
+ message: z.string(),
+});
+
/**
* UpdateDealProductSchema
*/
@@ -1323,6 +1406,54 @@ export const zGetStatusHistoryData = z.object({
*/
export const zGetStatusHistoryResponse2 = zGetStatusHistoryResponse;
+export const zGetClientsData = z.object({
+ body: z.optional(z.never()),
+ path: z.optional(z.never()),
+ query: z.optional(z.never()),
+});
+
+/**
+ * Successful Response
+ */
+export const zGetClientsResponse2 = zGetClientsResponse;
+
+export const zCreateClientData = z.object({
+ body: zCreateClientRequest,
+ path: z.optional(z.never()),
+ query: z.optional(z.never()),
+});
+
+/**
+ * Successful Response
+ */
+export const zCreateClientResponse2 = zCreateClientResponse;
+
+export const zDeleteClientData = z.object({
+ body: z.optional(z.never()),
+ path: z.object({
+ pk: z.int(),
+ }),
+ query: z.optional(z.never()),
+});
+
+/**
+ * Successful Response
+ */
+export const zDeleteClientResponse2 = zDeleteClientResponse;
+
+export const zUpdateClientData = z.object({
+ body: zUpdateClientRequest,
+ path: z.object({
+ pk: z.int(),
+ }),
+ query: z.optional(z.never()),
+});
+
+/**
+ * Successful Response
+ */
+export const zUpdateClientResponse2 = zUpdateClientResponse;
+
export const zGetBarcodeTemplatesData = z.object({
body: z.optional(z.never()),
path: z.optional(z.never()),
diff --git a/src/modals/modals.ts b/src/modals/modals.ts
index 176ca92..1695416 100644
--- a/src/modals/modals.ts
+++ b/src/modals/modals.ts
@@ -1,7 +1,13 @@
+import BarcodeTemplateEditorModal from "@/app/barcode-templates/modals/BarcodeTemplateFormModal/BarcodeTemplateEditorModal";
+import ClientEditorModal from "@/app/clients/modals/ClientFormModal/ClientFormModal";
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 { ServiceCategoryEditorModal, ServiceEditorModal, ServicesKitEditorModal } from "@/app/services/modals";
+import {
+ ServiceCategoryEditorModal,
+ ServiceEditorModal,
+ ServicesKitEditorModal,
+} from "@/app/services/modals";
import EnterNameModal from "@/modals/EnterNameModal/EnterNameModal";
import {
DealProductEditorModal,
@@ -11,7 +17,6 @@ import {
ProductServiceEditorModal,
ServicesKitSelectModal,
} from "@/modules/dealModularEditorTabs/FulfillmentBase/shared/modals";
-import BarcodeTemplateEditorModal from "@/app/barcode-templates/modals/BarcodeTemplateFormModal/BarcodeTemplateEditorModal";
export const modals = {
enterNameModal: EnterNameModal,
@@ -28,4 +33,5 @@ export const modals = {
serviceCategoryEditorModal: ServiceCategoryEditorModal,
serviceEditorModal: ServiceEditorModal,
barcodeTemplateEditorModal: BarcodeTemplateEditorModal,
+ clientEditorModal: ClientEditorModal,
};