diff --git a/src/client/index.ts b/src/client/index.ts index d5bdf04..bab211d 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -7,8 +7,6 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise'; export { OpenAPI } from './core/OpenAPI'; export type { OpenAPIConfig } from './core/OpenAPI'; -export type { ActiveWorkShiftSchema } from './models/ActiveWorkShiftSchema'; -export type { ActiveWorkShiftsResponse } from './models/ActiveWorkShiftsResponse'; export type { AuthLoginRequest } from './models/AuthLoginRequest'; export type { AuthLoginResponse } from './models/AuthLoginResponse'; export type { BarcodeAttributeSchema } from './models/BarcodeAttributeSchema'; @@ -30,6 +28,7 @@ export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema'; export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema'; export type { BillPaymentStatus } from './models/BillPaymentStatus'; export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest'; +export type { Body_upload_passport_image } from './models/Body_upload_passport_image'; export type { Body_upload_product_barcode_image } from './models/Body_upload_product_barcode_image'; export type { Body_upload_product_image } from './models/Body_upload_product_image'; export type { CancelDealBillRequest } from './models/CancelDealBillRequest'; @@ -151,6 +150,8 @@ export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWare export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse'; export type { ExpenseSchemaBase } from './models/ExpenseSchemaBase'; export type { ExpenseTagSchema } from './models/ExpenseTagSchema'; +export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftIdResponse'; +export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse'; export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse'; export type { FinishShiftResponse } from './models/FinishShiftResponse'; export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse'; @@ -187,12 +188,14 @@ export type { GetProfitTableDataResponse } from './models/GetProfitTableDataResp export type { GetServiceKitSchema } from './models/GetServiceKitSchema'; export type { GetTimeTrackingRecordsRequest } from './models/GetTimeTrackingRecordsRequest'; export type { GetTimeTrackingRecordsResponse } from './models/GetTimeTrackingRecordsResponse'; +export type { GetWorkShiftsResponse } from './models/GetWorkShiftsResponse'; export type { GroupBillRequestSchema } from './models/GroupBillRequestSchema'; export type { HTTPValidationError } from './models/HTTPValidationError'; export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema'; export type { MarketplaceSchema } from './models/MarketplaceSchema'; export type { NotificationChannel } from './models/NotificationChannel'; export type { PaginationInfoSchema } from './models/PaginationInfoSchema'; +export type { PassportImageSchema } from './models/PassportImageSchema'; export type { PaymentRecordCreateSchema } from './models/PaymentRecordCreateSchema'; export type { PaymentRecordGetSchema } from './models/PaymentRecordGetSchema'; export type { PayRateSchema } from './models/PayRateSchema'; @@ -246,6 +249,8 @@ export type { ServiceUpdateCategoryResponse } from './models/ServiceUpdateCatego export type { ServiceUpdateRequest } from './models/ServiceUpdateRequest'; export type { ServiceUpdateResponse } from './models/ServiceUpdateResponse'; export type { ShippingWarehouseSchema } from './models/ShippingWarehouseSchema'; +export type { StartPauseByShiftIdResponse } from './models/StartPauseByShiftIdResponse'; +export type { StartPauseByUserIdResponse } from './models/StartPauseByUserIdResponse'; export type { StartShiftResponse } from './models/StartShiftResponse'; export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest'; export type { TaskInfoResponse } from './models/TaskInfoResponse'; @@ -271,10 +276,13 @@ export type { UpdateTimeTrackingRecordRequest } from './models/UpdateTimeTrackin export type { UpdateTimeTrackingRecordResponse } from './models/UpdateTimeTrackingRecordResponse'; export type { UpdateUserRequest } from './models/UpdateUserRequest'; export type { UpdateUserResponse } from './models/UpdateUserResponse'; +export type { UploadPassportImageResponse } from './models/UploadPassportImageResponse'; export type { UserCreate } from './models/UserCreate'; export type { UserSchema } from './models/UserSchema'; export type { UserUpdate } from './models/UserUpdate'; export type { ValidationError } from './models/ValidationError'; +export type { WorkShiftRowSchema } from './models/WorkShiftRowSchema'; +export type { WorkShiftSchema } from './models/WorkShiftSchema'; export { AuthService } from './services/AuthService'; export { BarcodeService } from './services/BarcodeService'; diff --git a/src/client/models/ActiveWorkShiftsResponse.ts b/src/client/models/ActiveWorkShiftsResponse.ts deleted file mode 100644 index 37dfb85..0000000 --- a/src/client/models/ActiveWorkShiftsResponse.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ActiveWorkShiftSchema } from './ActiveWorkShiftSchema'; -export type ActiveWorkShiftsResponse = { - shifts: Array; -}; - diff --git a/src/client/models/Body_upload_passport_image.ts b/src/client/models/Body_upload_passport_image.ts new file mode 100644 index 0000000..39425dd --- /dev/null +++ b/src/client/models/Body_upload_passport_image.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Body_upload_passport_image = { + upload_file: Blob; +}; + diff --git a/src/client/models/FinishPauseByShiftIdResponse.ts b/src/client/models/FinishPauseByShiftIdResponse.ts new file mode 100644 index 0000000..ed8d6ee --- /dev/null +++ b/src/client/models/FinishPauseByShiftIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type FinishPauseByShiftIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/FinishPauseByUserIdResponse.ts b/src/client/models/FinishPauseByUserIdResponse.ts new file mode 100644 index 0000000..035abd3 --- /dev/null +++ b/src/client/models/FinishPauseByUserIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type FinishPauseByUserIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/GetWorkShiftsResponse.ts b/src/client/models/GetWorkShiftsResponse.ts new file mode 100644 index 0000000..b6299f9 --- /dev/null +++ b/src/client/models/GetWorkShiftsResponse.ts @@ -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 { WorkShiftRowSchema } from './WorkShiftRowSchema'; +export type GetWorkShiftsResponse = { + shifts: Array; + paginationInfo: PaginationInfoSchema; +}; + diff --git a/src/client/models/PaginationInfoSchema.ts b/src/client/models/PaginationInfoSchema.ts index fe40eb4..9fb7e01 100644 --- a/src/client/models/PaginationInfoSchema.ts +++ b/src/client/models/PaginationInfoSchema.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ export type PaginationInfoSchema = { - totalPages?: number; - totalItems?: number; + totalPages: number; + totalItems: number; }; diff --git a/src/client/models/PassportImageSchema.ts b/src/client/models/PassportImageSchema.ts new file mode 100644 index 0000000..998a1db --- /dev/null +++ b/src/client/models/PassportImageSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PassportImageSchema = { + id: number; + userId: number; + imageUrl: string; +}; + diff --git a/src/client/models/StartPauseByShiftIdResponse.ts b/src/client/models/StartPauseByShiftIdResponse.ts new file mode 100644 index 0000000..d2ddd85 --- /dev/null +++ b/src/client/models/StartPauseByShiftIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type StartPauseByShiftIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/StartPauseByUserIdResponse.ts b/src/client/models/StartPauseByUserIdResponse.ts new file mode 100644 index 0000000..4b6b3ec --- /dev/null +++ b/src/client/models/StartPauseByUserIdResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type StartPauseByUserIdResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/UploadPassportImageResponse.ts b/src/client/models/UploadPassportImageResponse.ts new file mode 100644 index 0000000..7c45a3b --- /dev/null +++ b/src/client/models/UploadPassportImageResponse.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UploadPassportImageResponse = { + ok: boolean; + message: string; + imageUrl?: (string | null); +}; + diff --git a/src/client/models/UserCreate.ts b/src/client/models/UserCreate.ts index c101c25..99180e9 100644 --- a/src/client/models/UserCreate.ts +++ b/src/client/models/UserCreate.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { PassportImageSchema } from './PassportImageSchema'; import type { PayRateSchema } from './PayRateSchema'; export type UserCreate = { telegramId: number; @@ -16,6 +17,8 @@ export type UserCreate = { isDeleted: boolean; roleKey: string; payRate?: (PayRateSchema | null); + passportImageUrl?: (string | null); + passportImages?: (Array | null); positionKey?: (string | null); }; diff --git a/src/client/models/UserSchema.ts b/src/client/models/UserSchema.ts index 7753120..425f963 100644 --- a/src/client/models/UserSchema.ts +++ b/src/client/models/UserSchema.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { PassportImageSchema } from './PassportImageSchema'; import type { PayRateSchema } from './PayRateSchema'; import type { PositionSchema } from './PositionSchema'; import type { RoleSchema } from './RoleSchema'; @@ -18,6 +19,8 @@ export type UserSchema = { isDeleted: boolean; roleKey: string; payRate?: (PayRateSchema | null); + passportImageUrl?: (string | null); + passportImages?: (Array | null); id: number; role: RoleSchema; position?: (PositionSchema | null); diff --git a/src/client/models/UserUpdate.ts b/src/client/models/UserUpdate.ts index 4c66f90..89941bf 100644 --- a/src/client/models/UserUpdate.ts +++ b/src/client/models/UserUpdate.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { PassportImageSchema } from './PassportImageSchema'; import type { PayRateSchema } from './PayRateSchema'; export type UserUpdate = { telegramId: number; @@ -16,6 +17,8 @@ export type UserUpdate = { isDeleted: boolean; roleKey: string; payRate?: (PayRateSchema | null); + passportImageUrl?: (string | null); + passportImages?: (Array | null); id: number; positionKey?: (string | null); }; diff --git a/src/client/models/WorkShiftRowSchema.ts b/src/client/models/WorkShiftRowSchema.ts new file mode 100644 index 0000000..b7f805a --- /dev/null +++ b/src/client/models/WorkShiftRowSchema.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { WorkShiftSchema } from './WorkShiftSchema'; +export type WorkShiftRowSchema = { + workShift: WorkShiftSchema; + totalHours?: (number | null); + pauseHours?: (number | null); +}; + diff --git a/src/client/models/ActiveWorkShiftSchema.ts b/src/client/models/WorkShiftSchema.ts similarity index 71% rename from src/client/models/ActiveWorkShiftSchema.ts rename to src/client/models/WorkShiftSchema.ts index 59d3047..4ca4c97 100644 --- a/src/client/models/ActiveWorkShiftSchema.ts +++ b/src/client/models/WorkShiftSchema.ts @@ -3,9 +3,11 @@ /* tslint:disable */ /* eslint-disable */ import type { UserSchema } from './UserSchema'; -export type ActiveWorkShiftSchema = { +export type WorkShiftSchema = { id: number; startedAt: string; + finishedAt?: (string | null); + isPaused?: (boolean | null); user: UserSchema; }; diff --git a/src/client/services/UserService.ts b/src/client/services/UserService.ts index 3339e4d..d42d134 100644 --- a/src/client/services/UserService.ts +++ b/src/client/services/UserService.ts @@ -2,12 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Body_upload_passport_image } from '../models/Body_upload_passport_image'; import type { CreateUserRequest } from '../models/CreateUserRequest'; import type { CreateUserResponse } from '../models/CreateUserResponse'; import type { GetAllUsersResponse } from '../models/GetAllUsersResponse'; import type { GetManagersResponse } from '../models/GetManagersResponse'; import type { UpdateUserRequest } from '../models/UpdateUserRequest'; import type { UpdateUserResponse } from '../models/UpdateUserResponse'; +import type { UploadPassportImageResponse } from '../models/UploadPassportImageResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; @@ -74,4 +76,29 @@ export class UserService { url: '/user/get-managers', }); } + /** + * Upload Passport Image + * @returns UploadPassportImageResponse Successful Response + * @throws ApiError + */ + public static uploadPassportImage({ + userId, + formData, + }: { + userId: number, + formData: Body_upload_passport_image, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/user/passport-images/upload/{user_id}', + path: { + 'user_id': userId, + }, + formData: formData, + mediaType: 'multipart/form-data', + errors: { + 422: `Validation Error`, + }, + }); + } } diff --git a/src/client/services/WorkShiftsService.ts b/src/client/services/WorkShiftsService.ts index 5be255a..b32dedc 100644 --- a/src/client/services/WorkShiftsService.ts +++ b/src/client/services/WorkShiftsService.ts @@ -2,10 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { ActiveWorkShiftsResponse } from '../models/ActiveWorkShiftsResponse'; import type { DeleteShiftResponse } from '../models/DeleteShiftResponse'; +import type { FinishPauseByShiftIdResponse } from '../models/FinishPauseByShiftIdResponse'; +import type { FinishPauseByUserIdResponse } from '../models/FinishPauseByUserIdResponse'; import type { FinishShiftByIdResponse } from '../models/FinishShiftByIdResponse'; import type { FinishShiftResponse } from '../models/FinishShiftResponse'; +import type { GetWorkShiftsResponse } from '../models/GetWorkShiftsResponse'; +import type { StartPauseByShiftIdResponse } from '../models/StartPauseByShiftIdResponse'; +import type { StartPauseByUserIdResponse } from '../models/StartPauseByUserIdResponse'; import type { StartShiftResponse } from '../models/StartShiftResponse'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; @@ -96,14 +100,32 @@ export class WorkShiftsService { }); } /** - * Get Active Shifts - * @returns ActiveWorkShiftsResponse Successful Response + * Get Shifts + * @returns GetWorkShiftsResponse Successful Response * @throws ApiError */ - public static getActiveShifts(): CancelablePromise { + public static getShifts({ + isActive, + page, + itemsPerPage, + }: { + isActive: boolean, + page?: (number | null), + itemsPerPage?: (number | null), + }): CancelablePromise { return __request(OpenAPI, { method: 'GET', - url: '/work-shifts/get-active-shifts', + url: '/work-shifts/get-shifts/{is_active}', + path: { + 'is_active': isActive, + }, + query: { + 'page': page, + 'items_per_page': itemsPerPage, + }, + errors: { + 422: `Validation Error`, + }, }); } /** @@ -127,4 +149,88 @@ export class WorkShiftsService { }, }); } + /** + * Start Pause By Shift Id + * @returns StartPauseByShiftIdResponse Successful Response + * @throws ApiError + */ + public static startPauseByShiftId({ + shiftId, + }: { + shiftId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/start/{shift_id}', + path: { + 'shift_id': shiftId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Start Pause By User Id + * @returns StartPauseByUserIdResponse Successful Response + * @throws ApiError + */ + public static startPauseByUserId({ + userId, + }: { + userId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/start/for-user/{user_id}', + path: { + 'user_id': userId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Finish Pause By Shift Id + * @returns FinishPauseByShiftIdResponse Successful Response + * @throws ApiError + */ + public static finishPauseByShiftId({ + shiftId, + }: { + shiftId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/finish/{shift_id}', + path: { + 'shift_id': shiftId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Finish Pause By User Id + * @returns FinishPauseByUserIdResponse Successful Response + * @throws ApiError + */ + public static finishPauseByUserId({ + userId, + }: { + userId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/work-shifts/pause/finish/for-user/{shift_id}', + query: { + 'user_id': userId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } } diff --git a/src/components/ImageDropzone/ImageDropzone.tsx b/src/components/ImageDropzone/ImageDropzone.tsx index 3f34ce2..f6825e4 100644 --- a/src/components/ImageDropzone/ImageDropzone.tsx +++ b/src/components/ImageDropzone/ImageDropzone.tsx @@ -1,77 +1,34 @@ import { Dropzone, DropzoneProps, FileWithPath } from "@mantine/dropzone"; -import { FC, useState } from "react"; -import { - Button, - Fieldset, - Flex, - Group, - Image, - Loader, - rem, - Text, -} from "@mantine/core"; +import { FC } from "react"; +import { Button, Fieldset, Flex, Group, Image, Loader, rem, Text } from "@mantine/core"; import { IconPhoto, IconUpload, IconX } from "@tabler/icons-react"; import { omit } from "lodash"; -import { BaseFormInputProps } from "../../types/utils.ts"; -import { notifications } from "../../shared/lib/notifications.ts"; -import { ProductService } from "../../client"; +import UseImageDropzone from "../../types/UseImageDropzone.tsx"; interface RestProps { - imageUrlInputProps?: BaseFormInputProps; - productId?: number; + imageDropzone: UseImageDropzone; + onDrop: (files: FileWithPath[]) => void; } type Props = Omit & RestProps; const ImageDropzone: FC = (props: Props) => { - const [showDropzone, setShowDropzone] = useState( - !( - typeof props.imageUrlInputProps?.value === "string" && - props.imageUrlInputProps.value.trim() !== "" - ) - ); - const [isLoading, setIsLoading] = useState(false); + const { + showDropzone, + setShowDropzone, + isLoading, + imageUrlInputProps, + } = props.imageDropzone; + const restProps = omit(props, [ "imageUrl", "productId", "imageUrlInputProps", ]); - const onDrop = (files: FileWithPath[]) => { - if (!props.productId || !props.imageUrlInputProps) return; - if (files.length > 1) { - notifications.error({ message: "Прикрепите одно изображение" }); - return; - } - const file = files[0]; - // TODO check if file is image - setIsLoading(true); - ProductService.uploadProductImage({ - productId: props.productId, - formData: { - upload_file: file, - }, - }) - .then(({ ok, message, imageUrl }) => { - notifications.guess(ok, { message }); - setIsLoading(false); - if (!ok || !imageUrl) { - setShowDropzone(true); - - return; - } - props.imageUrlInputProps?.onChange(imageUrl); - setShowDropzone(false); - }) - .catch(error => { - notifications.error({ message: error.toString() }); - setShowDropzone(true); - setIsLoading(false); - }); - }; const getBody = () => { - return props.imageUrlInputProps?.value && !showDropzone ? ( - + return imageUrlInputProps?.value && !showDropzone ? ( + ) : ( = (props: Props) => { "image/heic", ]} multiple={false} - onDrop={onDrop}> + onDrop={props.onDrop}> ; +} + +const useImageDropzone = ({ imageUrlInputProps }: Props) => { + const [showDropzone, setShowDropzone] = useState( + !( + typeof imageUrlInputProps?.value === "string" && + imageUrlInputProps.value.trim() !== "" + ), + ); + const [isLoading, setIsLoading] = useState(false); + + return { + showDropzone, + setShowDropzone, + isLoading, + setIsLoading, + imageUrlInputProps, + }; +}; + +export default useImageDropzone; diff --git a/src/pages/AdminPage/components/ActiveShiftsTable/ActiveShiftsTable.tsx b/src/pages/AdminPage/components/ActiveShiftsTable/ActiveShiftsTable.tsx deleted file mode 100644 index f921bc2..0000000 --- a/src/pages/AdminPage/components/ActiveShiftsTable/ActiveShiftsTable.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx"; -import { useActiveShiftsTableColumns } from "./columns.tsx"; -import { ActionIcon, Flex, Stack, Text, Title, Tooltip } from "@mantine/core"; -import { IconCheck, IconTrash } from "@tabler/icons-react"; -import { ActiveWorkShiftSchema, WorkShiftsService } from "../../../../client"; -import { modals } from "@mantine/modals"; -import { formatDate } from "../../../../types/utils.ts"; -import { MRT_TableOptions } from "mantine-react-table"; -import { notifications } from "../../../../shared/lib/notifications.ts"; - - -type Props = { - activeShifts: ActiveWorkShiftSchema[]; - fetchActiveShifts: () => void; -} - -export const ActiveShiftsTable = ({ activeShifts, fetchActiveShifts }: Props) => { - const columns = useActiveShiftsTableColumns(); - - const onDelete = (workShift: ActiveWorkShiftSchema) => { - WorkShiftsService.deleteWorkShift({ - shiftId: workShift.id, - }) - .then(({ ok, message }) => { - notifications.guess(ok, { message }); - fetchActiveShifts(); - }) - .catch(err => { - console.log(err); - }); - }; - - const onDeleteClick = (workShift: ActiveWorkShiftSchema) => { - modals.openConfirmModal({ - title: "Удаление записи о начале смены", - children: ( - - Вы уверены что хотите удалить запись о начале смены работника{" "} - {workShift.user.firstName} {workShift.user.secondName} от{" "} - {formatDate(workShift.startedAt)} - - ), - labels: { confirm: "Да", cancel: "Нет" }, - confirmProps: { color: "red" }, - onConfirm: () => onDelete(workShift), - }); - }; - - const onShiftFinish = (workShift: ActiveWorkShiftSchema) => { - WorkShiftsService.finishWorkShiftById({ - shiftId: workShift.id, - }) - .then(({ ok, message }) => { - notifications.guess(ok, { message }); - fetchActiveShifts(); - }) - .catch(err => console.log(err)); - } - - const onShiftFinishClick = (workShift: ActiveWorkShiftSchema) => { - modals.openConfirmModal({ - title: "Завершение смены", - children: ( - - Вы уверены что хотите завершить смену работника{" "} - {workShift.user.firstName} {workShift.user.secondName} от{" "} - {formatDate(workShift.startedAt)} - - ), - labels: { confirm: "Да", cancel: "Нет" }, - confirmProps: { color: "red" }, - onConfirm: () => onShiftFinish(workShift), - }); - } - - return ( - - - Активные смены - - ( - - - - onDeleteClick(row.original) - } - variant={"default"}> - - - - - - onShiftFinishClick(row.original) - } - variant={"default"}> - - - - - ), - } as MRT_TableOptions - } - /> - - ); -}; \ No newline at end of file diff --git a/src/pages/AdminPage/components/ActiveShiftsTable/columns.tsx b/src/pages/AdminPage/components/ActiveShiftsTable/columns.tsx deleted file mode 100644 index e3b9253..0000000 --- a/src/pages/AdminPage/components/ActiveShiftsTable/columns.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useMemo } from "react"; -import { MRT_ColumnDef } from "mantine-react-table"; -import { ActiveWorkShiftSchema } from "../../../../client"; - -export const useActiveShiftsTableColumns = () => { - return useMemo[]>( - () => [ - { - header: "Начало смены", - accessorKey: "startedAt", - Cell: ({ row }) => - new Date(row.original.startedAt).toLocaleString("ru-RU"), - }, - { - header: "ФИО", - Cell: ({ row }) => - `${row.original.user.firstName} ${row.original.user.secondName}`, - }, - { - header: "Роль", - accessorKey: "user.role.name", - enableSorting: false, - }, - { - header: "Должность", - accessorKey: "user.position.name", - enableSorting: false, - } - ], - [] - ); -}; diff --git a/src/pages/AdminPage/components/PassportImageDropzone/PassportImageDropzone.tsx b/src/pages/AdminPage/components/PassportImageDropzone/PassportImageDropzone.tsx new file mode 100644 index 0000000..925a1fa --- /dev/null +++ b/src/pages/AdminPage/components/PassportImageDropzone/PassportImageDropzone.tsx @@ -0,0 +1,60 @@ +import { DropzoneProps, FileWithPath } from "@mantine/dropzone"; +import { FC } from "react"; +import { notifications } from "../../../../shared/lib/notifications.ts"; +import { UserService } from "../../../../client"; +import { BaseFormInputProps } from "../../../../types/utils.ts"; +import useImageDropzone from "../../../../hooks/useImageDropzone.tsx"; +import ImageDropzone from "../../../../components/ImageDropzone/ImageDropzone.tsx"; + +interface RestProps { + imageUrlInputProps?: BaseFormInputProps; + userId?: number; +} + +type Props = Omit & RestProps; + +const ProductImageDropzone: FC = ({ imageUrlInputProps, userId }: Props) => { + const imageDropzoneProps = useImageDropzone({ + imageUrlInputProps, + }); + + const onDrop = (files: FileWithPath[]) => { + if (!userId || !imageUrlInputProps) return; + if (files.length > 1) { + notifications.error({ message: "Прикрепите одно изображение" }); + return; + } + const { setIsLoading, setShowDropzone } = imageDropzoneProps; + const file = files[0]; + + setIsLoading(true); + UserService.uploadPassportImage({ + userId: userId, + formData: { + upload_file: file, + }, + }) + .then(({ ok, message, imageUrl }) => { + notifications.guess(ok, { message }); + setIsLoading(false); + + if (!ok || !imageUrl) { + setShowDropzone(true); + return; + } + imageUrlInputProps?.onChange(imageUrl); + setShowDropzone(false); + }) + .catch(error => { + notifications.error({ message: error.toString() }); + setShowDropzone(true); + setIsLoading(false); + }); + }; + + return ( + + ); +}; + +export default ProductImageDropzone; diff --git a/src/pages/AdminPage/components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx b/src/pages/AdminPage/components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx new file mode 100644 index 0000000..73fd8a0 --- /dev/null +++ b/src/pages/AdminPage/components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx @@ -0,0 +1,28 @@ +import { SegmentedControl, SegmentedControlProps } from "@mantine/core"; +import { FC } from "react"; + +export enum ShiftsTableType { + ACTIVE, + HISTORY, +} + +type Props = Omit; +const data = [ + { + label: "Активные смены", + value: ShiftsTableType.ACTIVE.toString(), + }, + { + label: "Завершенные смены", + value: ShiftsTableType.HISTORY.toString(), + }, +]; + +export const ShiftsTableSegmentedControl: FC = props => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/pages/AdminPage/modals/UserFormModal/UserFormModal.tsx b/src/pages/AdminPage/modals/UserFormModal/UserFormModal.tsx index e64b213..974d264 100644 --- a/src/pages/AdminPage/modals/UserFormModal/UserFormModal.tsx +++ b/src/pages/AdminPage/modals/UserFormModal/UserFormModal.tsx @@ -10,6 +10,8 @@ import { capitalize } from "lodash"; import { IMaskInput } from "react-imask"; import phone from "phone"; import PayRateSelect from "../../../../components/Selects/PayRateSelect/PayRateSelect.tsx"; +import { BaseFormInputProps } from "../../../../types/utils.ts"; +import PassportImageDropzone from "../../components/PassportImageDropzone/PassportImageDropzone.tsx"; type Props = CreateEditFormProps; const UserFormModal = ({ @@ -107,6 +109,10 @@ const UserFormModal = ({ {...form.getInputProps("phoneNumber")} /> + + +
+ @@ -117,6 +123,18 @@ const UserFormModal = ({ {...form.getInputProps("passportData")} /> + { + isEditing && ( + + } + userId={innerProps?.element.id} + /> + ) + }
diff --git a/src/pages/AdminPage/tabs/WorkShifts/WorkShiftsTab.tsx b/src/pages/AdminPage/tabs/WorkShifts/WorkShiftsTab.tsx index 75ab8a6..0d9a46f 100644 --- a/src/pages/AdminPage/tabs/WorkShifts/WorkShiftsTab.tsx +++ b/src/pages/AdminPage/tabs/WorkShifts/WorkShiftsTab.tsx @@ -1,91 +1,52 @@ -import { Button, Group, Stack } from "@mantine/core"; -import { ActiveWorkShiftSchema, WorkShiftsService } from "../../../../client"; -import { useEffect, useState } from "react"; -import { notifications } from "../../../../shared/lib/notifications.ts"; -import { modals } from "@mantine/modals"; -import { ActiveShiftsTable } from "../../components/ActiveShiftsTable/ActiveShiftsTable.tsx"; +import { Divider, Flex, Pagination, rem, Skeleton, Stack } from "@mantine/core"; +import { ShiftsTable } from "./components/ShiftsTable.tsx"; +import { + ShiftsTableSegmentedControl, +} from "../../components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx"; +import useWorkShiftsTable from "./hooks/useWorkShiftsTable.tsx"; +import WorkShiftInput from "./components/WorkShiftInput.tsx"; export const WorkShiftsTab = () => { - let inputType: "StartShift" | "FinishShift" = "StartShift"; - const [activeShifts, setActiveShifts] = useState([]); - - const fetchActiveShifts = () => { - WorkShiftsService.getActiveShifts() - .then(res => { - setActiveShifts(res.shifts); - }) - .catch(err => console.log(err)); - } - - useEffect(() => { - fetchActiveShifts(); - }, []); - - const onInputFinish = (userIdInput: string) => { - const userId = parseInt(userIdInput); - if (isNaN(userId)) { - notifications.error({ message: "Ошибка, некорректные данные в QR-коде" }); - return; - } - - if (inputType === "StartShift") { - WorkShiftsService.startShift({ - userId: userId!, - }) - .then(async ({ ok, message }) => { - notifications.guess(ok, { message }); - fetchActiveShifts(); - }) - .catch(err => console.log(err)); - return; - } - - WorkShiftsService.finishShift({ - userId: userId!, - }) - .then(async ({ ok, message }) => { - notifications.guess(ok, { message }); - fetchActiveShifts(); - }) - .catch(err => console.log(err)); - }; - - const onScanningStart = () => { - modals.openContextModal({ - modal: "scanningModal", - innerProps: { - label: "Отсканируйте QR-код", - onScan: onInputFinish, - closeOnScan: true, - }, - withCloseButton: false, - }); - }; - - const onShiftStart = () => { - inputType = "StartShift"; - onScanningStart(); - }; - - const onShiftFinish = () => { - inputType = "FinishShift"; - onScanningStart(); - }; + const { + shifts, + shiftsTableType, + setShiftsTableType, + totalPages, + page, + setPage, + fetchShifts, + isLoading, + } = useWorkShiftsTable(); return ( - - - - - - + + + { + setPage(1); + setShiftsTableType(parseInt(event)); + }} /> + + + + {totalPages > 1 && ( + setPage(event)} + value={page} + total={totalPages} + /> + )} + + ); }; diff --git a/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx b/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx new file mode 100644 index 0000000..31e1d70 --- /dev/null +++ b/src/pages/AdminPage/tabs/WorkShifts/components/ShiftsTable.tsx @@ -0,0 +1,175 @@ +import { BaseTable } from "../../../../../components/BaseTable/BaseTable.tsx"; +import { useShiftsTableColumns } from "../hooks/columns.tsx"; +import { ActionIcon, Flex, Text, Tooltip } from "@mantine/core"; +import { IconCheck, IconPlayerPause, IconPlayerPlay, IconTrash } from "@tabler/icons-react"; +import { WorkShiftRowSchema, WorkShiftsService } from "../../../../../client"; +import { modals } from "@mantine/modals"; +import { formatDate } from "../../../../../types/utils.ts"; +import { MRT_Row, MRT_TableOptions } from "mantine-react-table"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; +import { ShiftsTableType } from "../../../components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx"; +import { ReactNode } from "react"; + + +type Props = { + shifts: WorkShiftRowSchema[]; + fetchShifts: () => void; + shiftsTableType: ShiftsTableType; +} + +export const ShiftsTable = ({ + shifts, + fetchShifts, + shiftsTableType, + }: Props) => { + const isActiveShiftsTable = shiftsTableType === ShiftsTableType.ACTIVE; + const columns = useShiftsTableColumns({ isActiveShiftsTable }); + + const onDelete = (workShiftRow: WorkShiftRowSchema) => { + WorkShiftsService.deleteWorkShift({ + shiftId: workShiftRow.workShift.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const onDeleteClick = (workShiftRow: WorkShiftRowSchema) => { + modals.openConfirmModal({ + title: "Удаление смены", + children: ( + + Вы уверены что хотите удалить смену работника{" "} + {workShiftRow.workShift.user.firstName} {workShiftRow.workShift.user.secondName} от{" "} + {formatDate(workShiftRow.workShift.startedAt)} + + ), + labels: { confirm: "Да", cancel: "Нет" }, + confirmProps: { color: "red" }, + onConfirm: () => onDelete(workShiftRow), + }); + }; + + const onShiftFinish = (workShiftRow: WorkShiftRowSchema) => { + WorkShiftsService.finishWorkShiftById({ + shiftId: workShiftRow.workShift.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const onShiftFinishClick = (workShiftRow: WorkShiftRowSchema) => { + modals.openConfirmModal({ + title: "Завершение смены", + children: ( + + Вы уверены что хотите завершить смену работника{" "} + {workShiftRow.workShift.user.firstName} {workShiftRow.workShift.user.secondName} от{" "} + {formatDate(workShiftRow.workShift.startedAt)} + + ), + labels: { confirm: "Да", cancel: "Нет" }, + confirmProps: { color: "red" }, + onConfirm: () => onShiftFinish(workShiftRow), + }); + }; + + const onShiftPauseClick = (workShiftRow: WorkShiftRowSchema) => { + WorkShiftsService.startPauseByShiftId({ + shiftId: workShiftRow.workShift.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const onShiftResumeClick = (workShiftRow: WorkShiftRowSchema) => { + WorkShiftsService.finishPauseByShiftId({ + shiftId: workShiftRow.workShift.id, + }) + .then(({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const getAction = ( + label: string, + func: () => void, + icon: ReactNode, + ) => { + return ( + + + {icon} + + + ); + }; + + const getRowActions = (row: MRT_Row) => { + const actions = [ + getAction("Удалить", () => onDeleteClick(row.original), ), + ]; + + if (isActiveShiftsTable) { + actions.push( + getAction( + "Завершить смену", + () => onShiftFinishClick(row.original), + , + ), + ); + if (row.original.workShift.isPaused) { + actions.push( + getAction( + "Продолжить смену", + () => onShiftResumeClick(row.original), + , + ), + ); + } else { + actions.push( + getAction( + "Поставить смену на паузу", + () => onShiftPauseClick(row.original), + , + ), + ); + } + } + + return actions; + }; + + return ( + { + return ( + + {...getRowActions(row)} + + ); + }, + } as MRT_TableOptions + } + /> + ); +}; \ No newline at end of file diff --git a/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx b/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx new file mode 100644 index 0000000..c40fc01 --- /dev/null +++ b/src/pages/AdminPage/tabs/WorkShifts/components/WorkShiftInput.tsx @@ -0,0 +1,34 @@ +import { Button, Group } from "@mantine/core"; +import useWorkShiftInput from "../hooks/useWorkShiftInput.tsx"; + +type Props = { + fetchShifts: () => void; +} + +const WorkShiftInput = ({ fetchShifts }: Props) => { + const { + onShiftStart, + onShiftFinish, + onShiftResume, + onShiftPause, + } = useWorkShiftInput({ fetchShifts }); + + return ( + + + + + + + ); +}; + +export default WorkShiftInput; diff --git a/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx b/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx new file mode 100644 index 0000000..31f615d --- /dev/null +++ b/src/pages/AdminPage/tabs/WorkShifts/hooks/columns.tsx @@ -0,0 +1,73 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef, MRT_Row } from "mantine-react-table"; +import { WorkShiftRowSchema } from "../../../../../client"; + + +type Props = { + isActiveShiftsTable: boolean; +} + +export const useShiftsTableColumns = ({ isActiveShiftsTable }: Props) => { + const getWorkedHoursString = (seconds: number) => { + const hours = Math.floor(seconds / 3_600); + const minutes = Math.floor(seconds % 3_600 / 60); + if (hours === 0) { + return `${minutes} мин.`; + } + return `${hours} ч. ${minutes} мин.`; + }; + + const getColumnsForHistory = () => { + return isActiveShiftsTable ? [] : [ + { + header: "Конец смены", + accessorKey: "workShift.finishedAt", + Cell: ({ row }: { row: MRT_Row }) => + row.original.workShift.finishedAt && new Date(row.original.workShift.finishedAt).toLocaleString("ru-RU"), + }, + { + header: "Длительность смены", + accessorKey: "totalHours", + Cell: ({ row }: { row: MRT_Row }) => + getWorkedHoursString(row.original.totalHours ?? 0), + }, + { + header: "Перерывы", + accessorKey: "pauseHours", + Cell: ({ row }: { row: MRT_Row }) => + getWorkedHoursString(row.original.pauseHours ?? 0), + }, + { + header: "Отработано", + Cell: ({ row }: { row: MRT_Row }) => + getWorkedHoursString((row.original.totalHours ?? 0) - (row.original.pauseHours ?? 0)), + }, + ] as MRT_ColumnDef[]; + }; + + return useMemo[]>( + () => [ + { + header: "ФИО", + Cell: ({ row }: { row: MRT_Row }) => + `${row.original.workShift.user.firstName} ${row.original.workShift.user.secondName}`, + }, + { + header: "Роль", + accessorKey: "workShift.user.role.name", + }, + { + header: "Должность", + accessorKey: "workShift.user.position.name", + }, + { + header: "Начало смены", + accessorKey: "workShift.startedAt", + Cell: ({ row }) => + new Date(row.original.workShift.startedAt).toLocaleString("ru-RU"), + }, + ...getColumnsForHistory(), + ], + [isActiveShiftsTable], + ); +}; diff --git a/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx new file mode 100644 index 0000000..3dfeb89 --- /dev/null +++ b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftInput.tsx @@ -0,0 +1,81 @@ +import { WorkShiftsService } from "../../../../../client"; +import { notifications } from "../../../../../shared/lib/notifications.ts"; +import { modals } from "@mantine/modals"; + +type Props = { + fetchShifts: () => void; +} + +enum InputType { + START_SHIFT, + FINISH_SHIFT, + RESUME_SHIFT, + PAUSE_SHIFT, +} + +const useWorkShiftInput = ({ fetchShifts }: Props) => { + let inputType: InputType = InputType.START_SHIFT; + + const workShiftMethods = { + [InputType.START_SHIFT]: WorkShiftsService.startShift, + [InputType.FINISH_SHIFT]: WorkShiftsService.finishShift, + [InputType.RESUME_SHIFT]: WorkShiftsService.finishPauseByUserId, + [InputType.PAUSE_SHIFT]: WorkShiftsService.startPauseByUserId, + }; + + const onInputFinish = (userIdInput: string) => { + const userId = parseInt(userIdInput); + if (isNaN(userId)) { + notifications.error({ message: "Ошибка, некорректные данные в QR-коде" }); + return; + } + + workShiftMethods[inputType]({ userId }) + .then(async ({ ok, message }) => { + notifications.guess(ok, { message }); + fetchShifts(); + }) + .catch(err => console.log(err)); + }; + + const onScanningStart = () => { + modals.openContextModal({ + modal: "scanningModal", + innerProps: { + label: "Отсканируйте QR-код", + onScan: onInputFinish, + closeOnScan: true, + }, + withCloseButton: false, + }); + }; + + const onShiftStart = () => { + inputType = InputType.START_SHIFT; + onScanningStart(); + }; + + const onShiftFinish = () => { + inputType = InputType.FINISH_SHIFT; + onScanningStart(); + }; + + const onShiftResume = () => { + inputType = InputType.RESUME_SHIFT; + onScanningStart(); + }; + + const onShiftPause = () => { + inputType = InputType.PAUSE_SHIFT; + onScanningStart(); + }; + + return { + onShiftStart, + onShiftFinish, + onShiftResume, + onShiftPause, + }; +}; + +export default useWorkShiftInput; diff --git a/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx new file mode 100644 index 0000000..8dfda0b --- /dev/null +++ b/src/pages/AdminPage/tabs/WorkShifts/hooks/useWorkShiftsTable.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from "react"; +import { WorkShiftRowSchema, WorkShiftsService } from "../../../../../client"; +import { ShiftsTableType } from "../../../components/ShiftsTableSegmentedControl/ShiftsTableSegmentedControl.tsx"; + + +const useWorkShiftsTable = () => { + const [totalPages, setTotalPages] = useState(1); + const [page, setPage] = useState(1); + const [shifts, setShifts] = useState([]); + const [shiftsTableType, setShiftsTableType] = useState(ShiftsTableType.ACTIVE); + const [isLoading, setIsLoading] = useState(false); + + const fetchShifts = () => { + setIsLoading(true); + WorkShiftsService.getShifts({ + isActive: shiftsTableType === ShiftsTableType.ACTIVE, + page, + itemsPerPage: 10, + }) + .then(res => { + setShifts(res.shifts); + setTotalPages(res.paginationInfo.totalPages); + }) + .catch(err => console.log(err)) + .finally(() => setIsLoading(false)); + }; + + useEffect(() => { + fetchShifts(); + }, [shiftsTableType, page]); + + return { + shifts, + shiftsTableType, + setShiftsTableType, + totalPages, + page, + setPage, + fetchShifts, + isLoading, + }; +}; + +export default useWorkShiftsTable; diff --git a/src/pages/ProductsPage/components/ProductImageDropzone/ProductImageDropzone.tsx b/src/pages/ProductsPage/components/ProductImageDropzone/ProductImageDropzone.tsx new file mode 100644 index 0000000..ce55631 --- /dev/null +++ b/src/pages/ProductsPage/components/ProductImageDropzone/ProductImageDropzone.tsx @@ -0,0 +1,60 @@ +import { DropzoneProps, FileWithPath } from "@mantine/dropzone"; +import { FC } from "react"; +import { notifications } from "../../../../shared/lib/notifications.ts"; +import { ProductService } from "../../../../client"; +import { BaseFormInputProps } from "../../../../types/utils.ts"; +import useImageDropzone from "../../../../hooks/useImageDropzone.tsx"; +import ImageDropzone from "../../../../components/ImageDropzone/ImageDropzone.tsx"; + +interface RestProps { + imageUrlInputProps?: BaseFormInputProps; + productId?: number; +} + +type Props = Omit & RestProps; + +const ProductImageDropzone: FC = ({ imageUrlInputProps, productId }: Props) => { + const imageDropzoneProps = useImageDropzone({ + imageUrlInputProps, + }); + + const onDrop = (files: FileWithPath[]) => { + if (!productId || !imageUrlInputProps) return; + if (files.length > 1) { + notifications.error({ message: "Прикрепите одно изображение" }); + return; + } + const { setIsLoading, setShowDropzone } = imageDropzoneProps; + const file = files[0]; + + setIsLoading(true); + ProductService.uploadProductImage({ + productId, + formData: { + upload_file: file, + }, + }) + .then(({ ok, message, imageUrl }) => { + notifications.guess(ok, { message }); + setIsLoading(false); + + if (!ok || !imageUrl) { + setShowDropzone(true); + return; + } + imageUrlInputProps?.onChange(imageUrl); + setShowDropzone(false); + }) + .catch(error => { + notifications.error({ message: error.toString() }); + setShowDropzone(true); + setIsLoading(false); + }); + }; + + return ( + + ); +}; + +export default ProductImageDropzone; diff --git a/src/pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx b/src/pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx index a04b359..95b495f 100644 --- a/src/pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx +++ b/src/pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx @@ -4,9 +4,9 @@ import { useForm } from "@mantine/form"; import { BaseProduct, CreateProductRequest } from "../../types.ts"; import { ProductSchema } from "../../../../client"; import BarcodeTemplateSelect from "../../../../components/Selects/BarcodeTemplateSelect/BarcodeTemplateSelect.tsx"; -import ImageDropzone from "../../../../components/ImageDropzone/ImageDropzone.tsx"; import { BaseFormInputProps } from "../../../../types/utils.ts"; import BarcodeImageDropzone from "../../../../components/BarcodeImageDropzone/BarcodeImageDropzone.tsx"; +import ProductImageDropzone from "../../components/ProductImageDropzone/ProductImageDropzone.tsx"; type CreateProps = { clientId: number; @@ -117,7 +117,7 @@ const CreateProductModal = ({ isEditProps && ( //
<> - >; + isLoading: boolean; + setIsLoading: Dispatch>; + imageUrlInputProps?: BaseFormInputProps; +} + +export default UseImageDropzone; diff --git a/src/types/utils.ts b/src/types/utils.ts index b917ac1..d30f9c8 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -45,7 +45,7 @@ export function ObjectStateToTableProps( export const floatHoursToHoursAndMinutes = (hours: number): number[] => { const resHours = Math.floor(hours); - const minutes = Math.round((hours - resHours) * 60); + const minutes = Math.floor((hours - resHours) * 60); return [resHours, minutes]; };