feat: invite code

This commit is contained in:
2025-03-05 19:36:35 +03:00
parent 6cc61dbf08
commit f8feea07c4
5 changed files with 73 additions and 59 deletions

View File

@@ -200,6 +200,7 @@ export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftId
export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse'; export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse';
export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse'; export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse';
export type { FinishShiftResponse } from './models/FinishShiftResponse'; export type { FinishShiftResponse } from './models/FinishShiftResponse';
export type { GenerateInviteCodeRequest } from './models/GenerateInviteCodeRequest';
export type { GenerateInviteCodeResponse } from './models/GenerateInviteCodeResponse'; export type { GenerateInviteCodeResponse } from './models/GenerateInviteCodeResponse';
export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse'; export type { GetAllBarcodeTemplateAttributesResponse } from './models/GetAllBarcodeTemplateAttributesResponse';
export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse'; export type { GetAllBarcodeTemplateSizesResponse } from './models/GetAllBarcodeTemplateSizesResponse';

View File

@@ -5,6 +5,7 @@
import type { Body_upload_passport_image } from '../models/Body_upload_passport_image'; import type { Body_upload_passport_image } from '../models/Body_upload_passport_image';
import type { CreateUserRequest } from '../models/CreateUserRequest'; import type { CreateUserRequest } from '../models/CreateUserRequest';
import type { CreateUserResponse } from '../models/CreateUserResponse'; import type { CreateUserResponse } from '../models/CreateUserResponse';
import type { GenerateInviteCodeRequest } from '../models/GenerateInviteCodeRequest';
import type { GenerateInviteCodeResponse } from '../models/GenerateInviteCodeResponse'; import type { GenerateInviteCodeResponse } from '../models/GenerateInviteCodeResponse';
import type { GetAllUsersResponse } from '../models/GetAllUsersResponse'; import type { GetAllUsersResponse } from '../models/GetAllUsersResponse';
import type { GetManagersResponse } from '../models/GetManagersResponse'; import type { GetManagersResponse } from '../models/GetManagersResponse';
@@ -134,10 +135,19 @@ export class UserService {
* @returns GenerateInviteCodeResponse Successful Response * @returns GenerateInviteCodeResponse Successful Response
* @throws ApiError * @throws ApiError
*/ */
public static generateInviteCode(): CancelablePromise<GenerateInviteCodeResponse> { public static generateInviteCode({
requestBody,
}: {
requestBody: GenerateInviteCodeRequest,
}): CancelablePromise<GenerateInviteCodeResponse> {
return __request(OpenAPI, { return __request(OpenAPI, {
method: 'POST', method: 'POST',
url: '/user/generate-invite-code', url: '/user/generate-invite-code',
body: requestBody,
mediaType: 'application/json',
errors: {
422: `Validation Error`,
},
}); });
} }
} }

View File

@@ -1,18 +1,15 @@
import { UserSchema, UserService } from "../../../../client"; import { UserSchema } from "../../../../client";
import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx"; import { BaseTable } from "../../../../components/BaseTable/BaseTable.tsx";
import { ActionIcon, Badge, Button, Flex, Input, rem, Text, Tooltip } from "@mantine/core"; import { ActionIcon, Button, Flex, rem, Text, Tooltip } from "@mantine/core";
import { useUsersTableColumns } from "./columns.tsx"; import { useUsersTableColumns } from "./columns.tsx";
import { IconEdit, IconQrcode, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconTrash } from "@tabler/icons-react";
import { modals } from "@mantine/modals"; import { modals } from "@mantine/modals";
import { MRT_TableOptions } from "mantine-react-table"; import { MRT_TableOptions } from "mantine-react-table";
import { useUsersTabContext } from "../../tabs/Users/contexts/UsersTabContext.tsx"; import { useUsersTabContext } from "../../tabs/Users/contexts/UsersTabContext.tsx";
import { notifications } from "../../../../shared/lib/notifications.ts";
import { useClipboard } from "@mantine/hooks";
const UsersTable = () => { const UsersTable = () => {
const columns = useUsersTableColumns(); const columns = useUsersTableColumns();
const clipboard = useClipboard();
const { const {
usersCrud: { usersCrud: {
items, items,
@@ -66,40 +63,7 @@ const UsersTable = () => {
size: "md", size: "md",
}); });
}; };
const onGenerateQrClick = (user: UserSchema) => {
const pdfWindow = window.open(
`${import.meta.env.VITE_API_URL}/work-shifts/generate-qr-code/${user.id}`,
);
if (!pdfWindow) return;
pdfWindow.print();
};
const onCreateInviteCodeClick = async () => {
const { inviteCode, ok, message } = await UserService.generateInviteCode();
if (!ok || !inviteCode) {
notifications.error({ message });
return;
}
modals.openConfirmModal({
withCloseButton: false,
children: (
<Flex gap={rem(10)} direction={"column"} justify={"center"} align={"center"}>
<Text>
Ваш код приглашения!
</Text>
<Badge variant={"default"} style={{cursor:"text"}} radius={"sm"} size={"xl"}>{inviteCode}</Badge>
<Input.Description>
Код действителен в течении 30 минут
</Input.Description>
</Flex>
),
labels: { confirm: "Скопировать", cancel: "Закрыть" },
onConfirm: () => {
clipboard.copy(inviteCode);
},
});
};
return ( return (
<BaseTable <BaseTable
data={items} data={items}
@@ -117,14 +81,7 @@ const UsersTable = () => {
onClick={() => onCreateClick()}> onClick={() => onCreateClick()}>
Создать пользователя Создать пользователя
</Button> </Button>
<Button
onClick={() => onCreateInviteCodeClick()}
variant={"default"}
>
Создать код приглашения
</Button>
</Flex> </Flex>
</Flex> </Flex>
), ),
enableRowActions: true, enableRowActions: true,
@@ -148,15 +105,7 @@ const UsersTable = () => {
<IconTrash /> <IconTrash />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
<Tooltip
onClick={() => {
onGenerateQrClick(row.original);
}}
label="QR-код">
<ActionIcon variant={"default"}>
<IconQrcode />
</ActionIcon>
</Tooltip>
</Flex> </Flex>
), ),
} as MRT_TableOptions<UserSchema> } as MRT_TableOptions<UserSchema>

View File

@@ -17,12 +17,13 @@ const UserFormModal = ({
id, id,
innerProps, innerProps,
}: ContextModalProps<Props>) => { }: ContextModalProps<Props>) => {
const isEditing = "element" in innerProps; const isEditing = "element" in innerProps;
const [modalTab, setModalTab] = useState<ModalTab>(ModalTab.COMMON); const [modalTab, setModalTab] = useState<ModalTab>(ModalTab.COMMON);
const closeModal = () => { const closeModal = () => {
context.closeContextModal(id); context.closeContextModal(id);
} };
if (!isEditing) { if (!isEditing) {
return ( return (

View File

@@ -1,9 +1,9 @@
import { UserRoleEnum } from "../../../../../../shared/enums/UserRole.ts"; import { UserRoleEnum } from "../../../../../../shared/enums/UserRole.ts";
import { useForm } from "@mantine/form"; import { useForm } from "@mantine/form";
import { UserSchema } from "../../../../../../client"; import { UserSchema, UserService } from "../../../../../../client";
import phone from "phone"; import phone from "phone";
import BaseFormModal, { CreateEditFormProps } from "../../../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx"; import BaseFormModal, { CreateEditFormProps } from "../../../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx";
import { Checkbox, Fieldset, Input, Stack, Textarea, TextInput } from "@mantine/core"; import { Badge, Button, Checkbox, Fieldset, Flex, Input, rem, Stack, Text, Textarea, TextInput } from "@mantine/core";
import { capitalize } from "lodash"; import { capitalize } from "lodash";
import { IMaskInput } from "react-imask"; import { IMaskInput } from "react-imask";
import PassportImageDropzone from "../../../../components/PassportImageDropzone/PassportImageDropzone.tsx"; import PassportImageDropzone from "../../../../components/PassportImageDropzone/PassportImageDropzone.tsx";
@@ -11,6 +11,9 @@ import { BaseFormInputProps } from "../../../../../../types/utils.ts";
import RoleSelect from "../../../../components/RoleSelect/RoleSelect.tsx"; import RoleSelect from "../../../../components/RoleSelect/RoleSelect.tsx";
import PositionSelect from "../../../../components/PositionSelect/PositionSelect.tsx"; import PositionSelect from "../../../../components/PositionSelect/PositionSelect.tsx";
import PayRateSelect from "../../../../../../components/Selects/PayRateSelect/PayRateSelect.tsx"; import PayRateSelect from "../../../../../../components/Selects/PayRateSelect/PayRateSelect.tsx";
import { useClipboard } from "@mantine/hooks";
import { notifications } from "../../../../../../shared/lib/notifications.ts";
import { modals } from "@mantine/modals";
type Props = { type Props = {
@@ -23,6 +26,47 @@ const CommonTab = ({
formProps, formProps,
}: Props) => { }: Props) => {
const isEditing = "element" in formProps; const isEditing = "element" in formProps;
const clipboard = useClipboard();
const onCreateInviteCodeClick = async () => {
if (!isEditing) return;
const {
inviteCode,
ok,
message,
} = await UserService.generateInviteCode({ requestBody: { userId: formProps.element.id } });
if (!ok || !inviteCode) {
notifications.error({ message });
return;
}
modals.openConfirmModal({
withCloseButton: false,
children: (
<Flex gap={rem(10)} direction={"column"} justify={"center"} align={"center"}>
<Text>
Ваш код приглашения!
</Text>
<Badge variant={"default"} style={{ cursor: "text" }} radius={"sm"} size={"xl"}>{inviteCode}</Badge>
<Input.Description>
Код действителен в течении 30 минут
</Input.Description>
</Flex>
),
labels: { confirm: "Скопировать", cancel: "Закрыть" },
onConfirm: () => {
clipboard.copy(inviteCode);
},
});
};
const onGenerateQrClick = () => {
if (!isEditing) return;
const pdfWindow = window.open(
`${import.meta.env.VITE_API_URL}/work-shifts/generate-qr-code/${formProps.element.id}`,
);
if (!pdfWindow) return;
pdfWindow.print();
};
const initialValues = isEditing const initialValues = isEditing
? formProps.element ? formProps.element
: { : {
@@ -189,6 +233,15 @@ const CommonTab = ({
/> />
</Stack> </Stack>
</Fieldset> </Fieldset>
{isEditing && (
<Fieldset pt={rem(20)}>
<Flex h={"100%"} justify={"center"} gap={rem(10)} direction={"column"}>
<Button onClick={() => onCreateInviteCodeClick()} variant={"default"}>Код
приглашения</Button>
<Button onClick={() => onGenerateQrClick()} variant={"default"}>QR Код</Button>
</Flex>
</Fieldset>)
}
</> </>
</BaseFormModal.Body> </BaseFormModal.Body>
</BaseFormModal> </BaseFormModal>