k
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import styles from './AdminPage.module.css';
|
||||
import {Tabs} from "@mantine/core";
|
||||
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 UsersTab from "./tabs/Users/UsersTab.tsx";
|
||||
import {motion} from "framer-motion";
|
||||
import FinancesTab from "./tabs/Finances/FinancesTab.tsx";
|
||||
|
||||
const AdminPage = () => {
|
||||
|
||||
@@ -16,13 +17,15 @@ const AdminPage = () => {
|
||||
<Tabs.Tab value={"users"} leftSection={<IconUser/>}>
|
||||
Пользователи
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value={"finances"} leftSection={<IconCurrencyDollar/>}>
|
||||
Финансы
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab value={"rolesAndPositions"} leftSection={<IconBriefcase/>}>
|
||||
Должности
|
||||
</Tabs.Tab>
|
||||
{/*<Tabs.Tab value={"employees"} leftSection={<IconUsersGroup/>}>*/}
|
||||
{/* Сотрудники*/}
|
||||
{/*</Tabs.Tab>*/}
|
||||
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value={"users"}>
|
||||
<motion.div
|
||||
initial={{opacity: 0}}
|
||||
@@ -31,7 +34,6 @@ const AdminPage = () => {
|
||||
|
||||
>
|
||||
<UsersTab/>
|
||||
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"rolesAndPositions"}>
|
||||
@@ -43,6 +45,15 @@ const AdminPage = () => {
|
||||
<RolesAndPositionsTab/>
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value={"finances"}>
|
||||
<motion.div
|
||||
initial={{opacity: 0}}
|
||||
animate={{opacity: 1}}
|
||||
transition={{duration: 0.2}}
|
||||
>
|
||||
<FinancesTab/>
|
||||
</motion.div>
|
||||
</Tabs.Panel>
|
||||
|
||||
</Tabs>
|
||||
</PageBlock>
|
||||
|
||||
94
src/pages/AdminPage/components/PayRateTable/PayRateTable.tsx
Normal file
94
src/pages/AdminPage/components/PayRateTable/PayRateTable.tsx
Normal 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;
|
||||
32
src/pages/AdminPage/components/PayRateTable/columns.tsx
Normal file
32
src/pages/AdminPage/components/PayRateTable/columns.tsx
Normal 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")}₽`
|
||||
|
||||
}
|
||||
], []);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
}
|
||||
], [])
|
||||
}
|
||||
@@ -52,13 +52,7 @@ const UsersTable: FC<Props> = ({items, onChange, onDelete}) => {
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({row}) => (
|
||||
<Flex gap="md">
|
||||
<Tooltip onClick={() => {
|
||||
onDeleteClick(row.original);
|
||||
}} label="Удалить">
|
||||
<ActionIcon variant={"default"}>
|
||||
<IconTrash/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip
|
||||
onClick={() => {
|
||||
onEditClick(row.original)
|
||||
@@ -69,6 +63,13 @@ const UsersTable: FC<Props> = ({items, onChange, onDelete}) => {
|
||||
<IconEdit/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip onClick={() => {
|
||||
onDeleteClick(row.original);
|
||||
}} label="Удалить">
|
||||
<ActionIcon variant={"default"}>
|
||||
<IconTrash/>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
),
|
||||
} as MRT_TableOptions<UserSchema>}
|
||||
|
||||
@@ -5,17 +5,10 @@ import {IconCheck, IconX} from "@tabler/icons-react";
|
||||
|
||||
export const useUsersTableColumns = () => {
|
||||
return useMemo<MRT_ColumnDef<UserSchema>[]>(() => [
|
||||
|
||||
{
|
||||
accessorKey: "firstName",
|
||||
header: "Имя"
|
||||
},
|
||||
{
|
||||
accessorKey: "secondName",
|
||||
header: "Фамилия"
|
||||
},
|
||||
{
|
||||
accessorKey: "telegramId",
|
||||
header: "ID Телеграм"
|
||||
header: "ФИО",
|
||||
Cell: ({row}) => `${row.original.firstName} ${row.original.secondName}`
|
||||
},
|
||||
{
|
||||
accessorKey: "phoneNumber",
|
||||
@@ -25,6 +18,14 @@ export const useUsersTableColumns = () => {
|
||||
accessorKey: "role.name",
|
||||
header: "Роль"
|
||||
},
|
||||
{
|
||||
accessorKey: "position.name",
|
||||
header: "Должность"
|
||||
},
|
||||
{
|
||||
accessorKey: "payRate.name",
|
||||
header: "Тариф"
|
||||
},
|
||||
{
|
||||
accessorKey: "comment",
|
||||
header: "Дополнительная информация"
|
||||
|
||||
9
src/pages/AdminPage/hooks/usePayRatesList.tsx
Normal file
9
src/pages/AdminPage/hooks/usePayRatesList.tsx
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -9,6 +9,7 @@ import {UserRoleEnum} from "../../../../shared/enums/UserRole.ts";
|
||||
import {capitalize} from "lodash";
|
||||
import {IMaskInput} from "react-imask";
|
||||
import phone from "phone";
|
||||
import PayRateSelect from "../../../../components/Selects/PayRateSelect/PayRateSelect.tsx";
|
||||
|
||||
type Props = EditProps<UserSchema>;
|
||||
const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
|
||||
@@ -65,21 +66,27 @@ const UserFormModal = ({context, id, innerProps}: ContextModalProps<Props>) => {
|
||||
</Fieldset>
|
||||
<Fieldset legend={"Роль и должность"}>
|
||||
<Stack>
|
||||
|
||||
<RoleSelect
|
||||
label={"Роль пользователя"}
|
||||
placeholder={"Выберите роль пользователя"}
|
||||
{...form.getInputProps('role')}
|
||||
/>
|
||||
{form.values.role.key === UserRoleEnum.EMPLOYEE &&
|
||||
<PositionSelect
|
||||
label={"Должность сотрудника"}
|
||||
placeholder={"Выберите должность сотрудника"}
|
||||
{...form.getInputProps('position')}
|
||||
/>
|
||||
<>
|
||||
<PositionSelect
|
||||
label={"Должность сотрудника"}
|
||||
placeholder={"Выберите должность сотрудника"}
|
||||
{...form.getInputProps('position')}
|
||||
/>
|
||||
<PayRateSelect
|
||||
label={"Тариф"}
|
||||
placeholder={"Выберите тариф сотрудника"}
|
||||
{...form.getInputProps("payRate")}
|
||||
/>
|
||||
</>
|
||||
|
||||
}
|
||||
</Stack>
|
||||
|
||||
</Fieldset>
|
||||
<Fieldset legend={"Дополнительные параметры"}>
|
||||
<Stack>
|
||||
|
||||
97
src/pages/AdminPage/tabs/Finances/FinancesTab.tsx
Normal file
97
src/pages/AdminPage/tabs/Finances/FinancesTab.tsx
Normal 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
|
||||
Reference in New Issue
Block a user