This commit is contained in:
2024-07-22 12:46:12 +03:00
parent af05b51d1c
commit 23dbff2891
46 changed files with 1173 additions and 40 deletions

View File

@@ -37,6 +37,10 @@ export type { ClientUpdateRequest } from './models/ClientUpdateRequest';
export type { ClientUpdateResponse } from './models/ClientUpdateResponse'; export type { ClientUpdateResponse } from './models/ClientUpdateResponse';
export type { CreateBarcodeTemplateAttributeRequest } from './models/CreateBarcodeTemplateAttributeRequest'; export type { CreateBarcodeTemplateAttributeRequest } from './models/CreateBarcodeTemplateAttributeRequest';
export type { CreateBarcodeTemplateAttributeResponse } from './models/CreateBarcodeTemplateAttributeResponse'; export type { CreateBarcodeTemplateAttributeResponse } from './models/CreateBarcodeTemplateAttributeResponse';
export type { CreatePaymentRecordRequest } from './models/CreatePaymentRecordRequest';
export type { CreatePaymentRecordResponse } from './models/CreatePaymentRecordResponse';
export type { CreatePayRateRequest } from './models/CreatePayRateRequest';
export type { CreatePayRateResponse } from './models/CreatePayRateResponse';
export type { CreatePositionRequest } from './models/CreatePositionRequest'; export type { CreatePositionRequest } from './models/CreatePositionRequest';
export type { CreatePositionResponse } from './models/CreatePositionResponse'; export type { CreatePositionResponse } from './models/CreatePositionResponse';
export type { DealAddProductRequest } from './models/DealAddProductRequest'; export type { DealAddProductRequest } from './models/DealAddProductRequest';
@@ -80,24 +84,36 @@ export type { DealUpdateServiceQuantityRequest } from './models/DealUpdateServic
export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse'; export type { DealUpdateServiceQuantityResponse } from './models/DealUpdateServiceQuantityResponse';
export type { DealUpdateServiceRequest } from './models/DealUpdateServiceRequest'; export type { DealUpdateServiceRequest } from './models/DealUpdateServiceRequest';
export type { DealUpdateServiceResponse } from './models/DealUpdateServiceResponse'; export type { DealUpdateServiceResponse } from './models/DealUpdateServiceResponse';
export type { DeletePaymentRecordRequest } from './models/DeletePaymentRecordRequest';
export type { DeletePaymentRecordResponse } from './models/DeletePaymentRecordResponse';
export type { DeletePayRateRequest } from './models/DeletePayRateRequest';
export type { DeletePayRateResponse } from './models/DeletePayRateResponse';
export type { DeletePositionRequest } from './models/DeletePositionRequest'; export type { DeletePositionRequest } from './models/DeletePositionRequest';
export type { DeletePositionResponse } from './models/DeletePositionResponse'; export type { DeletePositionResponse } from './models/DeletePositionResponse';
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse'; export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse'; export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';
export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse'; export type { GetAllBarcodeTemplatesResponse } from './models/GetAllBarcodeTemplatesResponse';
export type { GetAllBaseMarketplacesResponse } from './models/GetAllBaseMarketplacesResponse'; export type { GetAllBaseMarketplacesResponse } from './models/GetAllBaseMarketplacesResponse';
export type { GetAllPayRatesResponse } from './models/GetAllPayRatesResponse';
export type { GetAllPayrollSchemeResponse } from './models/GetAllPayrollSchemeResponse';
export type { GetAllPositionsResponse } from './models/GetAllPositionsResponse'; export type { GetAllPositionsResponse } from './models/GetAllPositionsResponse';
export type { GetAllRolesResponse } from './models/GetAllRolesResponse'; export type { GetAllRolesResponse } from './models/GetAllRolesResponse';
export type { GetAllShippingWarehousesResponse } from './models/GetAllShippingWarehousesResponse'; export type { GetAllShippingWarehousesResponse } from './models/GetAllShippingWarehousesResponse';
export type { GetAllUsersResponse } from './models/GetAllUsersResponse'; export type { GetAllUsersResponse } from './models/GetAllUsersResponse';
export type { GetBarcodeTemplateByIdRequest } from './models/GetBarcodeTemplateByIdRequest'; export type { GetBarcodeTemplateByIdRequest } from './models/GetBarcodeTemplateByIdRequest';
export type { GetBarcodeTemplateByIdResponse } from './models/GetBarcodeTemplateByIdResponse'; export type { GetBarcodeTemplateByIdResponse } from './models/GetBarcodeTemplateByIdResponse';
export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse';
export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest'; export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse'; export type { GetProductBarcodePdfResponse } from './models/GetProductBarcodePdfResponse';
export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest'; export type { GetProductBarcodeRequest } from './models/GetProductBarcodeRequest';
export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse'; export type { GetProductBarcodeResponse } from './models/GetProductBarcodeResponse';
export type { HTTPValidationError } from './models/HTTPValidationError'; export type { HTTPValidationError } from './models/HTTPValidationError';
export type { PaginationInfoSchema } from './models/PaginationInfoSchema'; export type { PaginationInfoSchema } from './models/PaginationInfoSchema';
export type { PaymentRecordCreateSchema } from './models/PaymentRecordCreateSchema';
export type { PaymentRecordGetSchema } from './models/PaymentRecordGetSchema';
export type { PayRateSchema } from './models/PayRateSchema';
export type { PayRateSchemaBase } from './models/PayRateSchemaBase';
export type { PayrollSchemeSchema } from './models/PayrollSchemeSchema';
export type { PermissionSchema } from './models/PermissionSchema'; export type { PermissionSchema } from './models/PermissionSchema';
export type { PositionSchema } from './models/PositionSchema'; export type { PositionSchema } from './models/PositionSchema';
export type { ProductAddBarcodeRequest } from './models/ProductAddBarcodeRequest'; export type { ProductAddBarcodeRequest } from './models/ProductAddBarcodeRequest';
@@ -130,6 +146,8 @@ export type { ServiceSchema } from './models/ServiceSchema';
export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest'; export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest';
export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse'; export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse';
export type { ShippingWarehouseSchema } from './models/ShippingWarehouseSchema'; export type { ShippingWarehouseSchema } from './models/ShippingWarehouseSchema';
export type { UpdatePayRateRequest } from './models/UpdatePayRateRequest';
export type { UpdatePayRateResponse } from './models/UpdatePayRateResponse';
export type { UpdateUserRequest } from './models/UpdateUserRequest'; export type { UpdateUserRequest } from './models/UpdateUserRequest';
export type { UpdateUserResponse } from './models/UpdateUserResponse'; export type { UpdateUserResponse } from './models/UpdateUserResponse';
export type { UserSchema } from './models/UserSchema'; export type { UserSchema } from './models/UserSchema';
@@ -141,6 +159,7 @@ export { BarcodeService } from './services/BarcodeService';
export { ClientService } from './services/ClientService'; export { ClientService } from './services/ClientService';
export { DealService } from './services/DealService'; export { DealService } from './services/DealService';
export { MarketplaceService } from './services/MarketplaceService'; export { MarketplaceService } from './services/MarketplaceService';
export { PayrollService } from './services/PayrollService';
export { PositionService } from './services/PositionService'; export { PositionService } from './services/PositionService';
export { ProductService } from './services/ProductService'; export { ProductService } from './services/ProductService';
export { RoleService } from './services/RoleService'; export { RoleService } from './services/RoleService';

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayRateSchemaBase } from './PayRateSchemaBase';
export type CreatePayRateRequest = {
data: PayRateSchemaBase;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type CreatePayRateResponse = {
ok: boolean;
message: string;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PaymentRecordCreateSchema } from './PaymentRecordCreateSchema';
export type CreatePaymentRecordRequest = {
data: PaymentRecordCreateSchema;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type CreatePaymentRecordResponse = {
ok: boolean;
message: string;
};

View File

@@ -0,0 +1,8 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeletePayRateRequest = {
payRateId: number;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeletePayRateResponse = {
ok: boolean;
message: string;
};

View File

@@ -0,0 +1,8 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeletePaymentRecordRequest = {
paymentRecordId: number;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeletePaymentRecordResponse = {
ok: boolean;
message: string;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayRateSchema } from './PayRateSchema';
export type GetAllPayRatesResponse = {
payRates: Array<PayRateSchema>;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayrollSchemeSchema } from './PayrollSchemeSchema';
export type GetAllPayrollSchemeResponse = {
payrollSchemas: Array<PayrollSchemeSchema>;
};

View File

@@ -0,0 +1,11 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PaginationInfoSchema } from './PaginationInfoSchema';
import type { PaymentRecordGetSchema } from './PaymentRecordGetSchema';
export type GetPaymentRecordsResponse = {
paymentRecords: Array<PaymentRecordGetSchema>;
paginationInfo: PaginationInfoSchema;
};

View File

@@ -0,0 +1,14 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayrollSchemeSchema } from './PayrollSchemeSchema';
export type PayRateSchema = {
name: string;
payrollScheme: PayrollSchemeSchema;
baseRate: number;
overtimeRate?: (number | null);
overtimeThreshold?: (number | null);
id: number;
};

View File

@@ -0,0 +1,13 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayrollSchemeSchema } from './PayrollSchemeSchema';
export type PayRateSchemaBase = {
name: string;
payrollScheme: PayrollSchemeSchema;
baseRate: number;
overtimeRate?: (number | null);
overtimeThreshold?: (number | null);
};

View File

@@ -0,0 +1,12 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { UserSchema } from './UserSchema';
export type PaymentRecordCreateSchema = {
startDate: string;
endDate: string;
workUnits: number;
user: UserSchema;
};

View File

@@ -0,0 +1,18 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayrollSchemeSchema } from './PayrollSchemeSchema';
import type { UserSchema } from './UserSchema';
export type PaymentRecordGetSchema = {
startDate: string;
endDate: string;
workUnits: number;
user: UserSchema;
id: number;
createdByUser: UserSchema;
payrollScheme: PayrollSchemeSchema;
amount: number;
createdAt: string;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type PayrollSchemeSchema = {
key: string;
name: string;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PayRateSchema } from './PayRateSchema';
export type UpdatePayRateRequest = {
data: PayRateSchema;
};

View File

@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type UpdatePayRateResponse = {
ok: boolean;
message: string;
};

View File

@@ -2,6 +2,7 @@
/* istanbul ignore file */ /* istanbul ignore file */
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */ /* eslint-disable */
import type { PayRateSchema } from './PayRateSchema';
import type { PositionSchema } from './PositionSchema'; import type { PositionSchema } from './PositionSchema';
import type { RoleSchema } from './RoleSchema'; import type { RoleSchema } from './RoleSchema';
export type UserSchema = { export type UserSchema = {
@@ -15,6 +16,7 @@ export type UserSchema = {
isBlocked: boolean; isBlocked: boolean;
isDeleted: boolean; isDeleted: boolean;
roleKey: string; roleKey: string;
payRate?: (PayRateSchema | null);
role: RoleSchema; role: RoleSchema;
position?: (PositionSchema | null); position?: (PositionSchema | null);
}; };

View File

@@ -2,6 +2,7 @@
/* istanbul ignore file */ /* istanbul ignore file */
/* tslint:disable */ /* tslint:disable */
/* eslint-disable */ /* eslint-disable */
import type { PayRateSchema } from './PayRateSchema';
export type UserUpdate = { export type UserUpdate = {
id: number; id: number;
telegramId: number; telegramId: number;
@@ -13,6 +14,7 @@ export type UserUpdate = {
isBlocked: boolean; isBlocked: boolean;
isDeleted: boolean; isDeleted: boolean;
roleKey: string; roleKey: string;
payRate?: (PayRateSchema | null);
positionKey?: (string | null); positionKey?: (string | null);
}; };

View File

@@ -0,0 +1,168 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { CreatePaymentRecordRequest } from '../models/CreatePaymentRecordRequest';
import type { CreatePaymentRecordResponse } from '../models/CreatePaymentRecordResponse';
import type { CreatePayRateRequest } from '../models/CreatePayRateRequest';
import type { CreatePayRateResponse } from '../models/CreatePayRateResponse';
import type { DeletePaymentRecordRequest } from '../models/DeletePaymentRecordRequest';
import type { DeletePaymentRecordResponse } from '../models/DeletePaymentRecordResponse';
import type { DeletePayRateRequest } from '../models/DeletePayRateRequest';
import type { DeletePayRateResponse } from '../models/DeletePayRateResponse';
import type { GetAllPayRatesResponse } from '../models/GetAllPayRatesResponse';
import type { GetAllPayrollSchemeResponse } from '../models/GetAllPayrollSchemeResponse';
import type { GetPaymentRecordsResponse } from '../models/GetPaymentRecordsResponse';
import type { UpdatePayRateRequest } from '../models/UpdatePayRateRequest';
import type { UpdatePayRateResponse } from '../models/UpdatePayRateResponse';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
export class PayrollService {
/**
* Get All Schemas
* @returns GetAllPayrollSchemeResponse Successful Response
* @throws ApiError
*/
public static getAllPayrollSchemas(): CancelablePromise<GetAllPayrollSchemeResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/payroll/scheme/get-all',
});
}
/**
* Get All Pay Rates
* @returns GetAllPayRatesResponse Successful Response
* @throws ApiError
*/
public static getAllPayRates(): CancelablePromise<GetAllPayRatesResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/payroll/pay-rate/get-all',
});
}
/**
* Create Pay Rate
* @returns CreatePayRateResponse Successful Response
* @throws ApiError
*/
public static createPayRate({
requestBody,
}: {
requestBody: CreatePayRateRequest,
}): CancelablePromise<CreatePayRateResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/payroll/pay-rate/create',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Update Pay Rate
* @returns UpdatePayRateResponse Successful Response
* @throws ApiError
*/
public static updatePayRate({
requestBody,
}: {
requestBody: UpdatePayRateRequest,
}): CancelablePromise<UpdatePayRateResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/payroll/pay-rate/update',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Update Pay Rate
* @returns DeletePayRateResponse Successful Response
* @throws ApiError
*/
public static deletePayRate({
requestBody,
}: {
requestBody: DeletePayRateRequest,
}): CancelablePromise<DeletePayRateResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/payroll/pay-rate/delete',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Get Payment Records
* @returns GetPaymentRecordsResponse Successful Response
* @throws ApiError
*/
public static getPaymentRecords({
page,
itemsPerPage,
}: {
page?: (number | null),
itemsPerPage?: (number | null),
}): CancelablePromise<GetPaymentRecordsResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/payroll/payment-record/get',
query: {
'page': page,
'items_per_page': itemsPerPage,
},
errors: {
422: `Validation Error`,
},
});
}
/**
* Create Payment Records
* @returns CreatePaymentRecordResponse Successful Response
* @throws ApiError
*/
public static createPaymentRecord({
requestBody,
}: {
requestBody: CreatePaymentRecordRequest,
}): CancelablePromise<CreatePaymentRecordResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/payroll/payment-record/create',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
/**
* Delete Payment Record
* @returns DeletePaymentRecordResponse Successful Response
* @throws ApiError
*/
public static deletePaymentRecord({
requestBody,
}: {
requestBody: DeletePaymentRecordRequest,
}): CancelablePromise<DeletePaymentRecordResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/payroll/payment-record/delete',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
});
}
}

View File

@@ -14,7 +14,7 @@
flex-wrap: wrap; flex-wrap: wrap;
@mixin light { @mixin light {
background-color: var(--mantine-color-gray-0); background-color: var(--mantine-color-gray-1);
} }
@mixin dark { @mixin dark {
background-color: var(--mantine-color-dark-5); background-color: var(--mantine-color-dark-5);

View File

@@ -12,7 +12,7 @@ const BaseMarketplaceSelect: FC<Props> = (props) => {
<ObjectSelect <ObjectSelect
renderOption={(baseMarketplace) => renderOption={(baseMarketplace) =>
<> <>
<ActionIcon variant={"transparent"}> <ActionIcon radius={"md"} variant={"transparent"}>
<Image <Image
src={baseMarketplaces.find(el => baseMarketplace.option.value === el.key)?.iconUrl || ""}/> src={baseMarketplaces.find(el => baseMarketplace.option.value === el.key)?.iconUrl || ""}/>
</ActionIcon> </ActionIcon>

View File

@@ -0,0 +1,19 @@
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
import {PayRateSchema} from "../../../client";
import {FC} from "react";
import usePayRatesList from "../../../pages/AdminPage/hooks/usePayRatesList.tsx";
type Props = Omit<ObjectSelectProps<PayRateSchema>, 'data' | 'getValueFn' | 'getLabelFn'>
const PayRateSelect: FC<Props> = (props) => {
const {objects: payRates} = usePayRatesList();
return (
<ObjectSelect
getValueFn={(baseMarketplace) => baseMarketplace.id.toLocaleString()}
getLabelFn={(baseMarketplace) => baseMarketplace.name}
data={payRates}
{...props}
/>
)
}
export default PayRateSelect;

View File

@@ -0,0 +1,19 @@
import ObjectSelect, {ObjectSelectProps} from "../../ObjectSelect/ObjectSelect.tsx";
import {PayrollSchemeSchema} from "../../../client";
import {FC} from "react";
import usePayrollSchemasList from "../../../hooks/usePayrollSchemasList.tsx";
type Props = Omit<ObjectSelectProps<PayrollSchemeSchema>, 'data' | 'getValueFn' | 'getLabelFn'>
const PayrollSchemeSelect: FC<Props> = (props) => {
const {objects: payrollSchemeSchemas} = usePayrollSchemasList();
return (
<ObjectSelect
getValueFn={(baseMarketplace) => baseMarketplace.key}
getLabelFn={(baseMarketplace) => baseMarketplace.name}
data={payrollSchemeSchemas}
{...props}
/>
)
}
export default PayrollSchemeSelect

View File

@@ -1,5 +1,6 @@
import {QueryObserverResult, RefetchOptions, useQuery} from "@tanstack/react-query"; import {QueryObserverResult, RefetchOptions, useQuery} from "@tanstack/react-query";
import {CancelablePromise} from "../client"; import {CancelablePromise, PaginationInfoSchema} from "../client";
import {Pagination} from "../types/Pagination.ts";
type Props<T, K> = { type Props<T, K> = {
queryFn: () => CancelablePromise<T>, queryFn: () => CancelablePromise<T>,
@@ -18,4 +19,33 @@ const ObjectList = <T, K, >(props: Props<T, K>): Response<T, K> => {
const objects = isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data); const objects = isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
return {objects, refetch} return {objects, refetch}
} }
interface ObjectWithPagination {
paginationInfo: PaginationInfoSchema
}
type PropsWithPagination<T extends ObjectWithPagination, K> = {
queryFn: () => CancelablePromise<T>,
getObjectsFn: (response: T) => K[],
queryKey: string,
pagination: Pagination
}
type ResponseWithPagination<T extends ObjectWithPagination, K> = {
objects: K[],
pagination?: PaginationInfoSchema,
refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<T, Error>>
}
export const ObjectListWithPagination = <T extends ObjectWithPagination, K, >(props: PropsWithPagination<T, K>): ResponseWithPagination<T, K> => {
const {isPending, error, data, refetch} = useQuery({
queryKey: [props.queryKey, props, props.pagination.itemsPerPage, props.pagination.page],
queryFn: props.queryFn,
});
const objects = isPending || error || !data ? ([] as K[]) : props.getObjectsFn(data);
return {
objects,
pagination: data?.paginationInfo,
refetch
}
}
export default ObjectList export default ObjectList

View File

@@ -0,0 +1,11 @@
import {ObjectListWithPagination} from "./objectList.tsx";
import {PayrollService} from "../client";
import {Pagination} from "../types/Pagination.ts";
export const usePaymentRecordsList = (pagination: Pagination) => ObjectListWithPagination({
queryFn: () => PayrollService.getPaymentRecords(pagination),
queryKey: "getPaymentRecords",
getObjectsFn: (response) => response.paymentRecords,
pagination
})

View File

@@ -0,0 +1,9 @@
import ObjectList from "./objectList.tsx";
import {PayrollService} from "../client";
const usePayrollSchemasList = () => ObjectList({
queryFn: PayrollService.getAllPayrollSchemas,
getObjectsFn: (response) => response.payrollSchemas,
queryKey: "getAllPayrollSchemas"
})
export default usePayrollSchemasList;

View File

@@ -14,6 +14,8 @@ import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal
import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx"; import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx"; import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx";
import PositionFormModal from "./PositionFormModal/PositionFormModal.tsx"; import PositionFormModal from "./PositionFormModal/PositionFormModal.tsx";
import PayRateFormModal from "../pages/AdminPage/modals/PayRateFormModal/PayRateFormModal.tsx";
import CreatePaymentRecordModal from "../pages/AdminPage/modals/CreatePaymentRecordModal/CreatePaymentRecordModal.tsx";
export const modals = { export const modals = {
enterDeadline: EnterDeadlineModal, enterDeadline: EnterDeadlineModal,
@@ -30,5 +32,7 @@ export const modals = {
userFormModal: UserFormModal, userFormModal: UserFormModal,
employeeSelect: EmployeeSelectModal, employeeSelect: EmployeeSelectModal,
employeeTable: EmployeeTableModal, employeeTable: EmployeeTableModal,
positionForm: PositionFormModal positionForm: PositionFormModal,
payRateForm: PayRateFormModal,
createPaymentRecord: CreatePaymentRecordModal
} }

View File

@@ -1,10 +1,11 @@
import styles from './AdminPage.module.css'; import styles from './AdminPage.module.css';
import {Tabs} from "@mantine/core"; import {Tabs} from "@mantine/core";
import PageBlock from "../../components/PageBlock/PageBlock.tsx"; import PageBlock from "../../components/PageBlock/PageBlock.tsx";
import {IconBriefcase, IconUser} from "@tabler/icons-react"; import {IconBriefcase, IconCurrencyDollar, IconUser} from "@tabler/icons-react";
import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx"; import RolesAndPositionsTab from "./tabs/RolesAndPositions/RolesAndPositionsTab.tsx";
import UsersTab from "./tabs/Users/UsersTab.tsx"; import UsersTab from "./tabs/Users/UsersTab.tsx";
import {motion} from "framer-motion"; import {motion} from "framer-motion";
import FinancesTab from "./tabs/Finances/FinancesTab.tsx";
const AdminPage = () => { const AdminPage = () => {
@@ -16,13 +17,15 @@ const AdminPage = () => {
<Tabs.Tab value={"users"} leftSection={<IconUser/>}> <Tabs.Tab value={"users"} leftSection={<IconUser/>}>
Пользователи Пользователи
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab value={"finances"} leftSection={<IconCurrencyDollar/>}>
Финансы
</Tabs.Tab>
<Tabs.Tab value={"rolesAndPositions"} leftSection={<IconBriefcase/>}> <Tabs.Tab value={"rolesAndPositions"} leftSection={<IconBriefcase/>}>
Должности Должности
</Tabs.Tab> </Tabs.Tab>
{/*<Tabs.Tab value={"employees"} leftSection={<IconUsersGroup/>}>*/}
{/* Сотрудники*/}
{/*</Tabs.Tab>*/}
</Tabs.List> </Tabs.List>
<Tabs.Panel value={"users"}> <Tabs.Panel value={"users"}>
<motion.div <motion.div
initial={{opacity: 0}} initial={{opacity: 0}}
@@ -31,7 +34,6 @@ const AdminPage = () => {
> >
<UsersTab/> <UsersTab/>
</motion.div> </motion.div>
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value={"rolesAndPositions"}> <Tabs.Panel value={"rolesAndPositions"}>
@@ -43,6 +45,15 @@ const AdminPage = () => {
<RolesAndPositionsTab/> <RolesAndPositionsTab/>
</motion.div> </motion.div>
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value={"finances"}>
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.2}}
>
<FinancesTab/>
</motion.div>
</Tabs.Panel>
</Tabs> </Tabs>
</PageBlock> </PageBlock>

View File

@@ -0,0 +1,94 @@
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
import {PayRateSchema} from "../../../../client";
import {FC} from "react";
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
import {usePayRatesTableColumns} from "./columns.tsx";
import {ActionIcon, Button, Flex, rem, Text, Tooltip} from "@mantine/core";
import {modals} from "@mantine/modals";
import {IconEdit, IconTrash} from "@tabler/icons-react";
import {MRT_TableOptions} from "mantine-react-table";
type Props = CRUDTableProps<PayRateSchema>;
const PayRateTable: FC<Props> = ({items, onCreate, onChange, onDelete}) => {
const columns = usePayRatesTableColumns();
const onCreateClick = () => {
if (!onCreate) return;
modals.openContextModal({
modal: "payRateForm",
withCloseButton: false,
innerProps: {
onCreate: onCreate
}
})
}
const onEditClick = (payRate: PayRateSchema) => {
if (!onChange) return;
modals.openContextModal({
modal: "payRateForm",
withCloseButton: false,
innerProps: {
onChange: (event) => onChange({...event, id: payRate.id}),
element: payRate
}
})
}
const onDeleteClick = (payRate: PayRateSchema) => {
if (!onDelete) return;
modals.openConfirmModal({
title: 'Удаление тарифа',
children: (
<Text size="sm">
Вы уверены что хотите удалить тариф {payRate.name}
</Text>
),
labels: {confirm: 'Да', cancel: "Нет"},
confirmProps: {color: 'red'},
onConfirm: () => onDelete(payRate)
});
}
return (
<BaseTable
data={items}
columns={columns}
restProps={{
enableSorting: false,
enableColumnActions: false,
enableTopToolbar: true,
renderTopToolbar: (
<Flex p={rem(10)}>
<Button
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>
</Flex>
)
} as MRT_TableOptions<PayRateSchema>}
/>
)
}
export default PayRateTable;

View File

@@ -0,0 +1,32 @@
import {useMemo} from "react";
import {MRT_ColumnDef} from "mantine-react-table";
import {PayRateSchema} from "../../../../client";
export const usePayRatesTableColumns = () => {
return useMemo<MRT_ColumnDef<PayRateSchema>[]>(() => [
{
accessorKey: "name",
header: "Название тарифа"
},
{
accessorKey: "payrollScheme.name",
header: "Система оплаты"
},
{
accessorKey: "baseRate",
header: "Базовая ставка",
Cell: ({row}) => `${row.original.baseRate.toLocaleString("ru")}`
},
{
accessorKey: "overtimeThreshold",
header: "Порог сверхурочных"
},
{
accessorKey: "overtimeRate",
header: "Сверхурочная ставка",
Cell: ({row}) => row.original.overtimeRate && `${row.original.overtimeRate.toLocaleString("ru")}`
}
], []);
}

View File

@@ -0,0 +1,123 @@
import {FC, useEffect, useState} from "react";
import {ActionIcon, Button, Flex, Pagination, rem, Text, Tooltip} from "@mantine/core";
import {usePaymentRecordsList} from "../../../../hooks/usePaymentRecordsList.tsx";
import {BaseTable} from "../../../../components/BaseTable/BaseTable.tsx";
import {usePaymentRecordsTableColumns} from "./columns.tsx";
import {modals} from "@mantine/modals";
import {PaymentRecordCreateSchema, PaymentRecordGetSchema, PayrollService} from "../../../../client";
import {notifications} from "../../../../shared/lib/notifications.ts";
import {IconTrash} from "@tabler/icons-react";
import {MRT_TableOptions} from "mantine-react-table";
import {formatDate} from "../../../../types/utils.ts";
const PaymentRecordsTable: FC = () => {
const [totalPages, setTotalPages] = useState(10);
const [page, setPage] = useState(1);
const {
pagination: paginationInfo,
objects: paymentRecords,
refetch
} = usePaymentRecordsList({page: page, itemsPerPage: 10});
useEffect(() => {
if (!paginationInfo) return;
setTotalPages(paginationInfo.totalPages);
}, [paginationInfo]);
const onCreate = (request: PaymentRecordCreateSchema) => {
PayrollService.createPaymentRecord({
requestBody: {
data: request
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
})
}
const onCreateClick = () => {
modals.openContextModal({
modal: "createPaymentRecord",
title: "Создание начисления",
innerProps: {
onCreate: onCreate
},
})
}
const onDelete = (record: PaymentRecordGetSchema) => {
PayrollService.deletePaymentRecord({
requestBody: {
paymentRecordId: record.id
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
})
}
const onDeleteClick = (record: PaymentRecordGetSchema) => {
modals.openConfirmModal({
title: 'Удаление начисления',
children: (
<Text size="sm">
Вы уверены что хотите удалить начисление
пользователю {record.user.firstName} {record.user.secondName} от {formatDate(record.createdAt)}
</Text>
),
labels: {confirm: 'Да', cancel: "Нет"},
confirmProps: {color: 'red'},
onConfirm: () => onDelete(record)
})
}
const columns = usePaymentRecordsTableColumns();
return (
<Flex
direction={"column"}
h={"100%"}
gap={rem(10)}
>
<BaseTable
data={paymentRecords}
columns={columns}
restProps={{
enableSorting: false,
enableColumnActions: false,
enableTopToolbar: true,
renderTopToolbar: (
<Flex p={rem(10)}>
<Button
variant={"default"}
onClick={() => onCreateClick()}
>
Создать начисление
</Button>
</Flex>
),
enableRowActions: true,
renderRowActions: ({row}) => (
<Flex gap="md">
<Tooltip label="Удалить">
<ActionIcon onClick={() => onDeleteClick(row.original)} variant={"default"}>
<IconTrash/>
</ActionIcon>
</Tooltip>
</Flex>
)
} as MRT_TableOptions<PaymentRecordGetSchema>}
/>
{totalPages > 1 &&
<Pagination
style={{alignSelf: "flex-end"}}
withEdges
onChange={event => setPage(event)}
value={page}
total={totalPages}
/>
}
</Flex>
)
}
export default PaymentRecordsTable;

View File

@@ -0,0 +1,58 @@
import {useMemo} from "react";
import {MRT_ColumnDef} from "mantine-react-table";
import {PaymentRecordGetSchema} from "../../../../client";
import {PaySchemeType} from "../../../../shared/enums/PaySchemeType.ts";
import {getPluralForm} from "../../../../shared/lib/utils.ts";
import {formatDate} from "../../../../types/utils.ts";
import {isEqual} from "lodash";
export const usePaymentRecordsTableColumns = () => {
const getWorkUnitsText = (paymentRecord: PaymentRecordGetSchema) => {
const payrollScheme = paymentRecord.payrollScheme;
if (payrollScheme.key === PaySchemeType.HOURLY) {
return getPluralForm(paymentRecord.workUnits, "час", "часа", "часов")
} else if (
payrollScheme.key === PaySchemeType.DAILY
) {
return getPluralForm(paymentRecord.workUnits, "день", "дня", "дней")
} else if (
payrollScheme.key === PaySchemeType.MONTHLY
) {
return getPluralForm(paymentRecord.workUnits, "месяц", "месяца", "месяцев");
}
return "";
}
const getDateRangesText = (paymentRecord: PaymentRecordGetSchema) => {
if (paymentRecord.endDate && !isEqual(paymentRecord.startDate, paymentRecord.endDate)) {
return `${formatDate(paymentRecord.startDate)} - ${formatDate(paymentRecord.endDate)}`
}
return `${formatDate(paymentRecord.startDate)}`;
}
return useMemo<MRT_ColumnDef<PaymentRecordGetSchema>[]>(() => [
{
header: "Дата начисления",
Cell: ({row}) => new Date(row.original.createdAt).toLocaleString('ru-RU')
},
{
header: "Получил начисление",
Cell: ({row}) => `${row.original.user.firstName} ${row.original.user.secondName}`
},
{
header: "Создал начисление",
Cell: ({row}) => `${row.original.createdByUser.firstName} ${row.original.createdByUser.secondName}`
},
{
header: "Количество",
Cell: ({row}) => `${row.original.workUnits} ${getWorkUnitsText(row.original)}`
},
{
header: "Сумма начисления",
Cell: ({row}) => row.original.amount.toLocaleString("ru-RU")
},
{
header: "Временной промежуток",
Cell: ({row}) => getDateRangesText(row.original)
}
], [])
}

View File

@@ -52,13 +52,7 @@ const UsersTable: FC<Props> = ({items, onChange, onDelete}) => {
enableRowActions: true, enableRowActions: true,
renderRowActions: ({row}) => ( renderRowActions: ({row}) => (
<Flex gap="md"> <Flex gap="md">
<Tooltip onClick={() => {
onDeleteClick(row.original);
}} label="Удалить">
<ActionIcon variant={"default"}>
<IconTrash/>
</ActionIcon>
</Tooltip>
<Tooltip <Tooltip
onClick={() => { onClick={() => {
onEditClick(row.original) onEditClick(row.original)
@@ -69,6 +63,13 @@ const UsersTable: FC<Props> = ({items, onChange, onDelete}) => {
<IconEdit/> <IconEdit/>
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip onClick={() => {
onDeleteClick(row.original);
}} label="Удалить">
<ActionIcon variant={"default"}>
<IconTrash/>
</ActionIcon>
</Tooltip>
</Flex> </Flex>
), ),
} as MRT_TableOptions<UserSchema>} } as MRT_TableOptions<UserSchema>}

View File

@@ -5,17 +5,10 @@ import {IconCheck, IconX} from "@tabler/icons-react";
export const useUsersTableColumns = () => { export const useUsersTableColumns = () => {
return useMemo<MRT_ColumnDef<UserSchema>[]>(() => [ return useMemo<MRT_ColumnDef<UserSchema>[]>(() => [
{ {
accessorKey: "firstName", header: "ФИО",
header: "Имя" Cell: ({row}) => `${row.original.firstName} ${row.original.secondName}`
},
{
accessorKey: "secondName",
header: "Фамилия"
},
{
accessorKey: "telegramId",
header: "ID Телеграм"
}, },
{ {
accessorKey: "phoneNumber", accessorKey: "phoneNumber",
@@ -25,6 +18,14 @@ export const useUsersTableColumns = () => {
accessorKey: "role.name", accessorKey: "role.name",
header: "Роль" header: "Роль"
}, },
{
accessorKey: "position.name",
header: "Должность"
},
{
accessorKey: "payRate.name",
header: "Тариф"
},
{ {
accessorKey: "comment", accessorKey: "comment",
header: "Дополнительная информация" header: "Дополнительная информация"

View File

@@ -0,0 +1,9 @@
import {PayrollService} from "../../../client";
import ObjectList from "../../../hooks/objectList.tsx";
const usePayRatesList = () => ObjectList({
queryFn: PayrollService.getAllPayRates,
getObjectsFn: response => response.payRates,
queryKey: "getAllPayRates"
})
export default usePayRatesList;

View File

@@ -0,0 +1,130 @@
import BaseFormModal, {CreateProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
import {PaymentRecordCreateSchema} from "../../../../client";
import {ContextModalProps} from "@mantine/modals";
import {useForm} from "@mantine/form";
import {Flex, NumberInput, rem} from "@mantine/core";
import {DatePickerInput, MonthPickerInput} from "@mantine/dates";
import {useEffect, useState} from "react";
import {dateWithoutTimezone} from "../../../../shared/lib/utils.ts";
import UserSelect from "../../../../components/Selects/UserSelect/UserSelect.tsx";
import {PaySchemeType} from "../../../../shared/enums/PaySchemeType.ts";
import {motion} from "framer-motion";
type Props = CreateProps<PaymentRecordCreateSchema>;
const CreatePaymentRecordModal = ({
context,
id,
innerProps
}: ContextModalProps<Props>) => {
const form = useForm<Partial<PaymentRecordCreateSchema>>({
validate: {
user: (user) => !user && "Необходимо выбрать сотрудника",
startDate: (startDate) => !startDate && "Необходимо указать временной промежуток",
workUnits: (workUnits) => !workUnits && "Укажите количество"
}
})
const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([null, null]);
useEffect(() => {
const setDates = (start: string | undefined, end: string | undefined) => {
form.setFieldValue("startDate", start);
form.setFieldValue("endDate", end);
};
if (dateRange.every(dr => dr == null)) {
setDates(undefined, undefined);
return
} else {
const notNullValues = dateRange.filter((dr): dr is Date => dr !== null).map(dateWithoutTimezone);
const startDate = notNullValues[0];
const endDate = notNullValues[1] || startDate;
setDates(startDate, endDate);
}
}, [dateRange]);
const getDateRangeInput = () => {
if (!form.values.user) return <></>
const payRate = form.values.user.payRate;
if (!payRate) return <></>;
if (payRate.payrollScheme.key == PaySchemeType.MONTHLY)
return (
<MonthPickerInput
error={form.getInputProps("startDate").error}
label={"Временной промежуток"}
placeholder={"Выберите временной промежуток"}
type={"range"}
value={dateRange}
onChange={setDateRange}
allowSingleDateInRange
/>
)
return (<DatePickerInput
error={form.getInputProps("startDate").error}
label={"Временной промежуток"}
placeholder={"Выберите временной промежуток"}
type={"range"}
allowSingleDateInRange
value={dateRange}
onChange={setDateRange}
/>);
}
const getAmountLabel = () => {
const user = form.values.user;
if (!user) return "";
const payRate = user?.payRate;
if (!payRate) return "";
if (payRate.payrollScheme.key == PaySchemeType.HOURLY) return "Количество часов";
if (payRate.payrollScheme.key == PaySchemeType.MONTHLY) return "Количество месяцев";
if (payRate.payrollScheme.key == PaySchemeType.DAILY) return "Количество дней";
return "";
}
const getAmountPlaceholder = () => {
return "Укажите " + getAmountLabel().toLowerCase();
}
return (<BaseFormModal
form={form}
closeOnSubmit
onClose={() => context.closeContextModal(id)}
{...innerProps}
>
<BaseFormModal.Body>
<>
<Flex direction={"column"} gap={rem(10)}>
<UserSelect
label={"Сотрудник"}
placeholder={"Выберите сотрудника"}
searchable
filterBy={(user) => !!user.payRate}
{...form.getInputProps("user")}
/>
{form.values.user &&
<>
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.3}}
>
<Flex
direction={"column"}
gap={rem(10)}>
{getDateRangeInput()}
<NumberInput
label={getAmountLabel()}
placeholder={getAmountPlaceholder()}
hideControls
{...form.getInputProps("workUnits")}
/>
</Flex>
</motion.div>
</>
}
</Flex>
</>
</BaseFormModal.Body>
</BaseFormModal>)
}
export default CreatePaymentRecordModal;

View File

@@ -0,0 +1,98 @@
import BaseFormModal, {CreateEditFormProps} from "../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
import {PayRateSchemaBase} from "../../../../client";
import {ContextModalProps} from "@mantine/modals";
import {useForm} from "@mantine/form";
import {Fieldset, Flex, NumberInput, rem, TextInput} from "@mantine/core";
import PayrollSchemeSelect from "../../../../components/Selects/PayrollSchemeSelect/PayrollSchemeSelect.tsx";
import {PaySchemeType} from "../../../../shared/enums/PaySchemeType.ts";
type Props = CreateEditFormProps<PayRateSchemaBase>
const PayRateFormModal = ({
context,
id,
innerProps
}: ContextModalProps<Props>) => {
const isEditing = 'element' in innerProps;
const initialValue: Partial<PayRateSchemaBase> = isEditing ? innerProps.element : {};
const form = useForm<Partial<PayRateSchemaBase>>({
initialValues: initialValue,
validate: {
name: (name) => !name && "Необходимо указать название тарифа",
payrollScheme: (scheme) => !scheme && "Необходимо выбрать систему оплаты",
baseRate: (baseRate) => !baseRate && "Небходимо указать базовую ставку"
}
});
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")}
/>
<PayrollSchemeSelect
label={"Система оплаты"}
placeholder={"Выберите систему оплаты"}
{...form.getInputProps("payrollScheme")}
/>
</Flex>
</Fieldset>
<Fieldset>
<Flex direction={"column"} gap={rem(10)}>
<NumberInput
allowNegative={false}
hideControls
decimalScale={2}
label={"Базовая ставка"}
placeholder={"Выберите базовую ставку"}
thousandSeparator={" "}
suffix={"₽"}
{...form.getInputProps("baseRate")}
/>
{form.values.payrollScheme?.key === PaySchemeType.HOURLY &&
<>
<NumberInput
allowNegative={false}
hideControls
allowDecimal={false}
label={"Порог сверхурочных"}
placeholder={"Введите порог сверхурочных"}
{...form.getInputProps("overtimeThreshold")}
/>
<NumberInput
allowNegative={false}
hideControls
decimalScale={2}
label={"Сверхурочная ставка"}
placeholder={"Выберите сверхурочную ставку"}
thousandSeparator={" "}
suffix={"₽"}
{...form.getInputProps("overtimeRate")}
/>
</>
}
</Flex>
</Fieldset>
</>
</BaseFormModal.Body>
</BaseFormModal>
)
}
export default PayRateFormModal;

View File

@@ -9,6 +9,7 @@ import {UserRoleEnum} from "../../../../shared/enums/UserRole.ts";
import {capitalize} from "lodash"; import {capitalize} from "lodash";
import {IMaskInput} from "react-imask"; import {IMaskInput} from "react-imask";
import phone from "phone"; import phone from "phone";
import PayRateSelect from "../../../../components/Selects/PayRateSelect/PayRateSelect.tsx";
type Props = EditProps<UserSchema>; type Props = EditProps<UserSchema>;
const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => { const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
@@ -65,21 +66,27 @@ const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
</Fieldset> </Fieldset>
<Fieldset legend={"Роль и должность"}> <Fieldset legend={"Роль и должность"}>
<Stack> <Stack>
<RoleSelect <RoleSelect
label={"Роль пользователя"} label={"Роль пользователя"}
placeholder={"Выберите роль пользователя"} placeholder={"Выберите роль пользователя"}
{...form.getInputProps('role')} {...form.getInputProps('role')}
/> />
{form.values.role.key === UserRoleEnum.EMPLOYEE && {form.values.role.key === UserRoleEnum.EMPLOYEE &&
<PositionSelect <>
label={"Должность сотрудника"} <PositionSelect
placeholder={"Выберите должность сотрудника"} label={олжность сотрудника"}
{...form.getInputProps('position')} placeholder={"Выберите должность сотрудника"}
/> {...form.getInputProps('position')}
/>
<PayRateSelect
label={"Тариф"}
placeholder={"Выберите тариф сотрудника"}
{...form.getInputProps("payRate")}
/>
</>
} }
</Stack> </Stack>
</Fieldset> </Fieldset>
<Fieldset legend={"Дополнительные параметры"}> <Fieldset legend={"Дополнительные параметры"}>
<Stack> <Stack>

View File

@@ -0,0 +1,97 @@
import {Tabs} from "@mantine/core";
import {IconBusinessplan, IconHistory} from "@tabler/icons-react";
import {motion} from "framer-motion";
import PayRateTable from "../../components/PayRateTable/PayRateTable.tsx";
import usePayRatesList from "../../hooks/usePayRatesList.tsx";
import {CRUDTableProps} from "../../../../types/CRUDTable.tsx";
import {PayRateSchema, PayRateSchemaBase, PayrollService} from "../../../../client";
import {notifications} from "../../../../shared/lib/notifications.ts";
import PaymentRecordsTable from "../../components/PaymentRecordsTable/PaymentRecordsTable.tsx";
const payRateTableState = (): CRUDTableProps<PayRateSchema> => {
const {objects: items, refetch} = usePayRatesList();
const onCreate = (item: PayRateSchemaBase) => {
PayrollService.createPayRate({
requestBody: {
data: item
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
});
}
const onChange = (item: PayRateSchema) => {
PayrollService.updatePayRate({
requestBody: {
data: item
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
});
}
const onDelete = (item: PayRateSchema) => {
PayrollService.deletePayRate({
requestBody: {
payRateId: item.id
}
}).then(async ({ok, message}) => {
notifications.guess(ok, {message});
if (!ok) return;
await refetch();
});
}
return {items, onCreate, onChange, onDelete};
}
const FinancesTab = () => {
const payRateState = payRateTableState();
return (
<>
<Tabs
keepMounted={false}
defaultValue={"paymentRecords"}
color={"gray.6"}
>
<Tabs.List
justify={"center"}
grow
>
<Tabs.Tab value={"paymentRecords"} leftSection={<IconHistory/>}>
Начисления
</Tabs.Tab>
<Tabs.Tab value={"tariffs"} leftSection={<IconBusinessplan/>}>
Тарифы
</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value={"tariffs"}>
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.2}}
>
<PayRateTable
{...payRateState}
/>
</motion.div>
</Tabs.Panel>
<Tabs.Panel value={"paymentRecords"}>
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.2}}
>
<PaymentRecordsTable/>
</motion.div>
</Tabs.Panel>
</Tabs>
</>
)
}
export default FinancesTab

View File

@@ -1,7 +1,4 @@
import {createLazyFileRoute} from "@tanstack/react-router"; import {createLazyFileRoute} from "@tanstack/react-router";
import {Button} from '@mantine/core';
import {modals} from "@mantine/modals";
import BaseMarketplaceSelect from "../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
export const Route = createLazyFileRoute('/test')({ export const Route = createLazyFileRoute('/test')({
component: TestPage component: TestPage
@@ -9,12 +6,9 @@ export const Route = createLazyFileRoute('/test')({
function TestPage() { function TestPage() {
return ( return (
<> <>
<BaseMarketplaceSelect
onChange={() => {
}}
/>
</> </>
); );
} }

View File

@@ -0,0 +1,5 @@
export enum PaySchemeType {
HOURLY = 'hourly',
DAILY = 'daily',
MONTHLY = 'monthly'
}

4
src/types/Pagination.ts Normal file
View File

@@ -0,0 +1,4 @@
export type Pagination = {
itemsPerPage: number,
page: number
}

View File

@@ -8,3 +8,6 @@ export type BaseFormInputProps<T> = {
value: T; value: T;
error?: string | null; error?: string | null;
} }
export const formatDate = (date: string) => {
return new Date(date).toLocaleDateString("ru-RU");
}