feat: creating chats for cards and clients, sending and deleting text messages
This commit is contained in:
@@ -31,6 +31,7 @@ export type { BaseCardTagSchema } from './models/BaseCardTagSchema';
|
|||||||
export type { BaseEnumListSchema } from './models/BaseEnumListSchema';
|
export type { BaseEnumListSchema } from './models/BaseEnumListSchema';
|
||||||
export type { BaseEnumSchema } from './models/BaseEnumSchema';
|
export type { BaseEnumSchema } from './models/BaseEnumSchema';
|
||||||
export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema';
|
export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema';
|
||||||
|
export type { BaseMessageSchema } from './models/BaseMessageSchema';
|
||||||
export type { BaseProjectSchema } from './models/BaseProjectSchema';
|
export type { BaseProjectSchema } from './models/BaseProjectSchema';
|
||||||
export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema';
|
export type { BaseShippingWarehouseSchema } from './models/BaseShippingWarehouseSchema';
|
||||||
export type { BaseStatusSchema } from './models/BaseStatusSchema';
|
export type { BaseStatusSchema } from './models/BaseStatusSchema';
|
||||||
@@ -114,6 +115,7 @@ export type { CardUpdateServiceQuantityRequest } from './models/CardUpdateServic
|
|||||||
export type { CardUpdateServiceQuantityResponse } from './models/CardUpdateServiceQuantityResponse';
|
export type { CardUpdateServiceQuantityResponse } from './models/CardUpdateServiceQuantityResponse';
|
||||||
export type { CardUpdateServiceRequest } from './models/CardUpdateServiceRequest';
|
export type { CardUpdateServiceRequest } from './models/CardUpdateServiceRequest';
|
||||||
export type { CardUpdateServiceResponse } from './models/CardUpdateServiceResponse';
|
export type { CardUpdateServiceResponse } from './models/CardUpdateServiceResponse';
|
||||||
|
export type { ChatSchema } from './models/ChatSchema';
|
||||||
export type { CityBreakdownFromExcelSchema } from './models/CityBreakdownFromExcelSchema';
|
export type { CityBreakdownFromExcelSchema } from './models/CityBreakdownFromExcelSchema';
|
||||||
export type { ClientCreateRequest } from './models/ClientCreateRequest';
|
export type { ClientCreateRequest } from './models/ClientCreateRequest';
|
||||||
export type { ClientCreateResponse } from './models/ClientCreateResponse';
|
export type { ClientCreateResponse } from './models/ClientCreateResponse';
|
||||||
@@ -142,6 +144,8 @@ export type { CreateCardBillResponse } from './models/CreateCardBillResponse';
|
|||||||
export type { CreateCardGroupRequest } from './models/CreateCardGroupRequest';
|
export type { CreateCardGroupRequest } from './models/CreateCardGroupRequest';
|
||||||
export type { CreateCardsFromExcelRequest } from './models/CreateCardsFromExcelRequest';
|
export type { CreateCardsFromExcelRequest } from './models/CreateCardsFromExcelRequest';
|
||||||
export type { CreateCardsFromExcelResponse } from './models/CreateCardsFromExcelResponse';
|
export type { CreateCardsFromExcelResponse } from './models/CreateCardsFromExcelResponse';
|
||||||
|
export type { CreateChatRequest } from './models/CreateChatRequest';
|
||||||
|
export type { CreateChatResponse } from './models/CreateChatResponse';
|
||||||
export type { CreateDepartmentRequest } from './models/CreateDepartmentRequest';
|
export type { CreateDepartmentRequest } from './models/CreateDepartmentRequest';
|
||||||
export type { CreateDepartmentResponse } from './models/CreateDepartmentResponse';
|
export type { CreateDepartmentResponse } from './models/CreateDepartmentResponse';
|
||||||
export type { CreateDepartmentSectionRequest } from './models/CreateDepartmentSectionRequest';
|
export type { CreateDepartmentSectionRequest } from './models/CreateDepartmentSectionRequest';
|
||||||
@@ -185,6 +189,7 @@ export type { DeleteDepartmentResponse } from './models/DeleteDepartmentResponse
|
|||||||
export type { DeleteDepartmentSectionResponse } from './models/DeleteDepartmentSectionResponse';
|
export type { DeleteDepartmentSectionResponse } from './models/DeleteDepartmentSectionResponse';
|
||||||
export type { DeleteMarketplaceRequest } from './models/DeleteMarketplaceRequest';
|
export type { DeleteMarketplaceRequest } from './models/DeleteMarketplaceRequest';
|
||||||
export type { DeleteMarketplaceResponse } from './models/DeleteMarketplaceResponse';
|
export type { DeleteMarketplaceResponse } from './models/DeleteMarketplaceResponse';
|
||||||
|
export type { DeleteMessageResponse } from './models/DeleteMessageResponse';
|
||||||
export type { DeletePalletResponse } from './models/DeletePalletResponse';
|
export type { DeletePalletResponse } from './models/DeletePalletResponse';
|
||||||
export type { DeletePaymentRecordRequest } from './models/DeletePaymentRecordRequest';
|
export type { DeletePaymentRecordRequest } from './models/DeletePaymentRecordRequest';
|
||||||
export type { DeletePaymentRecordResponse } from './models/DeletePaymentRecordResponse';
|
export type { DeletePaymentRecordResponse } from './models/DeletePaymentRecordResponse';
|
||||||
@@ -243,11 +248,15 @@ export type { GetBoardsResponse } from './models/GetBoardsResponse';
|
|||||||
export type { GetCardBillById } from './models/GetCardBillById';
|
export type { GetCardBillById } from './models/GetCardBillById';
|
||||||
export type { GetCardProductsBarcodesPdfRequest } from './models/GetCardProductsBarcodesPdfRequest';
|
export type { GetCardProductsBarcodesPdfRequest } from './models/GetCardProductsBarcodesPdfRequest';
|
||||||
export type { GetCardProductsBarcodesPdfResponse } from './models/GetCardProductsBarcodesPdfResponse';
|
export type { GetCardProductsBarcodesPdfResponse } from './models/GetCardProductsBarcodesPdfResponse';
|
||||||
|
export type { GetChatRequest } from './models/GetChatRequest';
|
||||||
|
export type { GetChatResponse } from './models/GetChatResponse';
|
||||||
export type { GetClientMarketplacesRequest } from './models/GetClientMarketplacesRequest';
|
export type { GetClientMarketplacesRequest } from './models/GetClientMarketplacesRequest';
|
||||||
export type { GetClientMarketplacesResponse } from './models/GetClientMarketplacesResponse';
|
export type { GetClientMarketplacesResponse } from './models/GetClientMarketplacesResponse';
|
||||||
export type { GetDepartmentSectionsResponse } from './models/GetDepartmentSectionsResponse';
|
export type { GetDepartmentSectionsResponse } from './models/GetDepartmentSectionsResponse';
|
||||||
export type { GetDepartmentsResponse } from './models/GetDepartmentsResponse';
|
export type { GetDepartmentsResponse } from './models/GetDepartmentsResponse';
|
||||||
export type { GetManagersResponse } from './models/GetManagersResponse';
|
export type { GetManagersResponse } from './models/GetManagersResponse';
|
||||||
|
export type { GetMessagesRequest } from './models/GetMessagesRequest';
|
||||||
|
export type { GetMessagesResponse } from './models/GetMessagesResponse';
|
||||||
export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse';
|
export type { GetPaymentRecordsResponse } from './models/GetPaymentRecordsResponse';
|
||||||
export type { GetPlannedWorkShiftsResponse } from './models/GetPlannedWorkShiftsResponse';
|
export type { GetPlannedWorkShiftsResponse } from './models/GetPlannedWorkShiftsResponse';
|
||||||
export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
|
export type { GetProductBarcodePdfRequest } from './models/GetProductBarcodePdfRequest';
|
||||||
@@ -276,6 +285,7 @@ export type { ManageEmployeeRequest } from './models/ManageEmployeeRequest';
|
|||||||
export type { ManageEmployeeResponse } from './models/ManageEmployeeResponse';
|
export type { ManageEmployeeResponse } from './models/ManageEmployeeResponse';
|
||||||
export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema';
|
export type { MarketplaceCreateSchema } from './models/MarketplaceCreateSchema';
|
||||||
export type { MarketplaceSchema } from './models/MarketplaceSchema';
|
export type { MarketplaceSchema } from './models/MarketplaceSchema';
|
||||||
|
export type { MessageSchema } from './models/MessageSchema';
|
||||||
export type { ModuleSchema } from './models/ModuleSchema';
|
export type { ModuleSchema } from './models/ModuleSchema';
|
||||||
export type { NotificationChannel } from './models/NotificationChannel';
|
export type { NotificationChannel } from './models/NotificationChannel';
|
||||||
export type { OptionalShippingWarehouseSchema } from './models/OptionalShippingWarehouseSchema';
|
export type { OptionalShippingWarehouseSchema } from './models/OptionalShippingWarehouseSchema';
|
||||||
@@ -328,6 +338,8 @@ export type { ResidualBoxSchema } from './models/ResidualBoxSchema';
|
|||||||
export type { ResidualPalletSchema } from './models/ResidualPalletSchema';
|
export type { ResidualPalletSchema } from './models/ResidualPalletSchema';
|
||||||
export type { ResidualProductSchema } from './models/ResidualProductSchema';
|
export type { ResidualProductSchema } from './models/ResidualProductSchema';
|
||||||
export type { RoleSchema } from './models/RoleSchema';
|
export type { RoleSchema } from './models/RoleSchema';
|
||||||
|
export type { SendMessageRequest } from './models/SendMessageRequest';
|
||||||
|
export type { SendMessageResponse } from './models/SendMessageResponse';
|
||||||
export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
|
export type { ServiceCategoryReorderRequest } from './models/ServiceCategoryReorderRequest';
|
||||||
export type { ServiceCategoryReorderResponse } from './models/ServiceCategoryReorderResponse';
|
export type { ServiceCategoryReorderResponse } from './models/ServiceCategoryReorderResponse';
|
||||||
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
export type { ServiceCategorySchema } from './models/ServiceCategorySchema';
|
||||||
@@ -359,6 +371,8 @@ export type { SwitchTagRequest } from './models/SwitchTagRequest';
|
|||||||
export type { SwitchTagResponse } from './models/SwitchTagResponse';
|
export type { SwitchTagResponse } from './models/SwitchTagResponse';
|
||||||
export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest';
|
export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest';
|
||||||
export type { TaskInfoResponse } from './models/TaskInfoResponse';
|
export type { TaskInfoResponse } from './models/TaskInfoResponse';
|
||||||
|
export type { TgGroupSchema } from './models/TgGroupSchema';
|
||||||
|
export type { TgUserSchema } from './models/TgUserSchema';
|
||||||
export type { TimeTrackingData } from './models/TimeTrackingData';
|
export type { TimeTrackingData } from './models/TimeTrackingData';
|
||||||
export type { TimeTrackingRecord } from './models/TimeTrackingRecord';
|
export type { TimeTrackingRecord } from './models/TimeTrackingRecord';
|
||||||
export type { TransactionSchemaBase } from './models/TransactionSchemaBase';
|
export type { TransactionSchemaBase } from './models/TransactionSchemaBase';
|
||||||
@@ -435,6 +449,7 @@ export { BoardService } from './services/BoardService';
|
|||||||
export { CardService } from './services/CardService';
|
export { CardService } from './services/CardService';
|
||||||
export { CardGroupService } from './services/CardGroupService';
|
export { CardGroupService } from './services/CardGroupService';
|
||||||
export { CardTagService } from './services/CardTagService';
|
export { CardTagService } from './services/CardTagService';
|
||||||
|
export { ChatService } from './services/ChatService';
|
||||||
export { ClientService } from './services/ClientService';
|
export { ClientService } from './services/ClientService';
|
||||||
export { DepartmentService } from './services/DepartmentService';
|
export { DepartmentService } from './services/DepartmentService';
|
||||||
export { MarketplaceService } from './services/MarketplaceService';
|
export { MarketplaceService } from './services/MarketplaceService';
|
||||||
|
|||||||
9
src/client/models/BaseMessageSchema.ts
Normal file
9
src/client/models/BaseMessageSchema.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type BaseMessageSchema = {
|
||||||
|
text: string;
|
||||||
|
chatId: number;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import type { CardProductSchema } from './CardProductSchema';
|
|||||||
import type { CardServiceSchema } from './CardServiceSchema';
|
import type { CardServiceSchema } from './CardServiceSchema';
|
||||||
import type { CardStatusHistorySchema } from './CardStatusHistorySchema';
|
import type { CardStatusHistorySchema } from './CardStatusHistorySchema';
|
||||||
import type { CardTagSchema } from './CardTagSchema';
|
import type { CardTagSchema } from './CardTagSchema';
|
||||||
|
import type { ChatSchema } from './ChatSchema';
|
||||||
import type { ClientSchema } from './ClientSchema';
|
import type { ClientSchema } from './ClientSchema';
|
||||||
import type { PalletSchema } from './PalletSchema';
|
import type { PalletSchema } from './PalletSchema';
|
||||||
import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
|
import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema';
|
||||||
@@ -42,5 +43,6 @@ export type CardSchema = {
|
|||||||
employees?: Array<CardEmployeesSchema>;
|
employees?: Array<CardEmployeesSchema>;
|
||||||
tags?: Array<CardTagSchema>;
|
tags?: Array<CardTagSchema>;
|
||||||
attributes: Array<CardAttributeSchema>;
|
attributes: Array<CardAttributeSchema>;
|
||||||
|
chat: (ChatSchema | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
12
src/client/models/ChatSchema.ts
Normal file
12
src/client/models/ChatSchema.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { TgGroupSchema } from './TgGroupSchema';
|
||||||
|
export type ChatSchema = {
|
||||||
|
id: number;
|
||||||
|
clientId: (number | null);
|
||||||
|
cardId: (number | null);
|
||||||
|
tgGroup: (TgGroupSchema | null);
|
||||||
|
};
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { BarcodeTemplateSchema } from './BarcodeTemplateSchema';
|
import type { BarcodeTemplateSchema } from './BarcodeTemplateSchema';
|
||||||
|
import type { ChatSchema } from './ChatSchema';
|
||||||
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
||||||
import type { ResidualBoxSchema } from './ResidualBoxSchema';
|
import type { ResidualBoxSchema } from './ResidualBoxSchema';
|
||||||
import type { ResidualPalletSchema } from './ResidualPalletSchema';
|
import type { ResidualPalletSchema } from './ResidualPalletSchema';
|
||||||
@@ -13,6 +14,7 @@ export type ClientDetailedSchema = {
|
|||||||
barcodeTemplate?: (BarcodeTemplateSchema | null);
|
barcodeTemplate?: (BarcodeTemplateSchema | null);
|
||||||
comment?: (string | null);
|
comment?: (string | null);
|
||||||
details?: (ClientDetailsSchema | null);
|
details?: (ClientDetailsSchema | null);
|
||||||
|
chat?: (ChatSchema | null);
|
||||||
pallets?: Array<ResidualPalletSchema>;
|
pallets?: Array<ResidualPalletSchema>;
|
||||||
boxes?: Array<ResidualBoxSchema>;
|
boxes?: Array<ResidualBoxSchema>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { BarcodeTemplateSchema } from './BarcodeTemplateSchema';
|
import type { BarcodeTemplateSchema } from './BarcodeTemplateSchema';
|
||||||
|
import type { ChatSchema } from './ChatSchema';
|
||||||
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
import type { ClientDetailsSchema } from './ClientDetailsSchema';
|
||||||
export type ClientSchema = {
|
export type ClientSchema = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -11,5 +12,6 @@ export type ClientSchema = {
|
|||||||
barcodeTemplate?: (BarcodeTemplateSchema | null);
|
barcodeTemplate?: (BarcodeTemplateSchema | null);
|
||||||
comment?: (string | null);
|
comment?: (string | null);
|
||||||
details?: (ClientDetailsSchema | null);
|
details?: (ClientDetailsSchema | null);
|
||||||
|
chat?: (ChatSchema | null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
9
src/client/models/CreateChatRequest.ts
Normal file
9
src/client/models/CreateChatRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type CreateChatRequest = {
|
||||||
|
clientId: number;
|
||||||
|
cardId: (number | null);
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/CreateChatResponse.ts
Normal file
9
src/client/models/CreateChatResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type CreateChatResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/DeleteMessageResponse.ts
Normal file
9
src/client/models/DeleteMessageResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type DeleteMessageResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/GetChatRequest.ts
Normal file
9
src/client/models/GetChatRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type GetChatRequest = {
|
||||||
|
clientId: number;
|
||||||
|
cardId: (number | null);
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/GetChatResponse.ts
Normal file
9
src/client/models/GetChatResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { ChatSchema } from './ChatSchema';
|
||||||
|
export type GetChatResponse = {
|
||||||
|
chat: (ChatSchema | null);
|
||||||
|
};
|
||||||
|
|
||||||
10
src/client/models/GetMessagesRequest.ts
Normal file
10
src/client/models/GetMessagesRequest.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type GetMessagesRequest = {
|
||||||
|
chatId: number;
|
||||||
|
offset: number;
|
||||||
|
limit: number;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/GetMessagesResponse.ts
Normal file
9
src/client/models/GetMessagesResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { MessageSchema } from './MessageSchema';
|
||||||
|
export type GetMessagesResponse = {
|
||||||
|
messages: Array<MessageSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
14
src/client/models/MessageSchema.ts
Normal file
14
src/client/models/MessageSchema.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { TgUserSchema } from './TgUserSchema';
|
||||||
|
export type MessageSchema = {
|
||||||
|
text: string;
|
||||||
|
chatId: number;
|
||||||
|
id: number;
|
||||||
|
createdAt: string;
|
||||||
|
tgSender: (TgUserSchema | null);
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/SendMessageRequest.ts
Normal file
9
src/client/models/SendMessageRequest.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { BaseMessageSchema } from './BaseMessageSchema';
|
||||||
|
export type SendMessageRequest = {
|
||||||
|
message: BaseMessageSchema;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/SendMessageResponse.ts
Normal file
9
src/client/models/SendMessageResponse.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type SendMessageResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
9
src/client/models/TgGroupSchema.ts
Normal file
9
src/client/models/TgGroupSchema.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type TgGroupSchema = {
|
||||||
|
tgGroupId: number;
|
||||||
|
tgInviteLink: string;
|
||||||
|
};
|
||||||
|
|
||||||
11
src/client/models/TgUserSchema.ts
Normal file
11
src/client/models/TgUserSchema.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type TgUserSchema = {
|
||||||
|
id: number;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
|
||||||
119
src/client/services/ChatService.ts
Normal file
119
src/client/services/ChatService.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { CreateChatRequest } from '../models/CreateChatRequest';
|
||||||
|
import type { CreateChatResponse } from '../models/CreateChatResponse';
|
||||||
|
import type { DeleteMessageResponse } from '../models/DeleteMessageResponse';
|
||||||
|
import type { GetChatRequest } from '../models/GetChatRequest';
|
||||||
|
import type { GetChatResponse } from '../models/GetChatResponse';
|
||||||
|
import type { GetMessagesRequest } from '../models/GetMessagesRequest';
|
||||||
|
import type { GetMessagesResponse } from '../models/GetMessagesResponse';
|
||||||
|
import type { SendMessageRequest } from '../models/SendMessageRequest';
|
||||||
|
import type { SendMessageResponse } from '../models/SendMessageResponse';
|
||||||
|
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||||
|
import { OpenAPI } from '../core/OpenAPI';
|
||||||
|
import { request as __request } from '../core/request';
|
||||||
|
export class ChatService {
|
||||||
|
/**
|
||||||
|
* Send Message
|
||||||
|
* @returns SendMessageResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static sendMessage({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: SendMessageRequest,
|
||||||
|
}): CancelablePromise<SendMessageResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/chat/message',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delete Message
|
||||||
|
* @returns DeleteMessageResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static deleteMessage({
|
||||||
|
messageId,
|
||||||
|
}: {
|
||||||
|
messageId: number,
|
||||||
|
}): CancelablePromise<DeleteMessageResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/chat/message/{message_id}',
|
||||||
|
path: {
|
||||||
|
'message_id': messageId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get Chat
|
||||||
|
* @returns GetChatResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getChat({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: GetChatRequest,
|
||||||
|
}): CancelablePromise<GetChatResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/chat/',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create Chat
|
||||||
|
* @returns CreateChatResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static createChat({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: CreateChatRequest,
|
||||||
|
}): CancelablePromise<CreateChatResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/chat/create',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get Messages
|
||||||
|
* @returns GetMessagesResponse Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static getMessages({
|
||||||
|
requestBody,
|
||||||
|
}: {
|
||||||
|
requestBody: GetMessagesRequest,
|
||||||
|
}): CancelablePromise<GetMessagesResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/chat/messages',
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Validation Error`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/components/ActionIconCopy/ActionIconCopy.tsx
Normal file
45
src/components/ActionIconCopy/ActionIconCopy.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { ActionIcon, rem, Tooltip } from "@mantine/core";
|
||||||
|
import { IconCheck, IconCopy } from "@tabler/icons-react";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { useClipboard } from "@mantine/hooks";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: string;
|
||||||
|
onCopiedLabel: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ActionIconCopy: FC<Props> = ({ onCopiedLabel, value }) => {
|
||||||
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
label={onCopiedLabel}
|
||||||
|
offset={5}
|
||||||
|
position="bottom"
|
||||||
|
radius="xl"
|
||||||
|
transitionProps={{ duration: 100, transition: "slide-down" }}
|
||||||
|
opened={clipboard.copied}>
|
||||||
|
<ActionIcon
|
||||||
|
variant={"default"}
|
||||||
|
size="lg"
|
||||||
|
onClick={() => {
|
||||||
|
console.log("AKLSKLSKSM");
|
||||||
|
return clipboard.copy(value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{clipboard.copied ? (
|
||||||
|
<IconCheck
|
||||||
|
style={{ width: rem(20), height: rem(20) }}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconCopy
|
||||||
|
style={{ width: rem(20), height: rem(20) }}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ActionIconCopy;
|
||||||
20
src/components/Chat/Chat.module.css
Normal file
20
src/components/Chat/Chat.module.css
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
.message {
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-2);
|
||||||
|
}
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-5);
|
||||||
|
}
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other-message {
|
||||||
|
@mixin light {
|
||||||
|
background-color: var(--mantine-color-gray-3);
|
||||||
|
}
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-6);
|
||||||
|
}
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
55
src/components/Chat/Chat.tsx
Normal file
55
src/components/Chat/Chat.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { ScrollArea, Stack } from "@mantine/core";
|
||||||
|
import Message from "./components/Message/Message.tsx";
|
||||||
|
import MessageInput from "./components/MessageInput/MessageInput.tsx";
|
||||||
|
import { useChatContext } from "../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
|
import { MessageSchema } from "../../client";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
import ChatDate from "./components/ChatDate/ChatDate.tsx";
|
||||||
|
|
||||||
|
const Chat = () => {
|
||||||
|
const {
|
||||||
|
messages,
|
||||||
|
scrollRef,
|
||||||
|
onScrollPositionChange,
|
||||||
|
} = useChatContext();
|
||||||
|
|
||||||
|
const getChatElements = (): ReactNode[] => {
|
||||||
|
const elements: ReactNode[] = [];
|
||||||
|
let prevMessage: MessageSchema | null = null;
|
||||||
|
|
||||||
|
for (let i = messages.length - 1; i >= 0; i--) {
|
||||||
|
const currMessage = messages[i];
|
||||||
|
|
||||||
|
if (!prevMessage || prevMessage.createdAt.substring(5, 10) != currMessage.createdAt.substring(5, 10)) {
|
||||||
|
elements.push((
|
||||||
|
<ChatDate
|
||||||
|
key={currMessage.id + "date"}
|
||||||
|
date={new Date(currMessage.createdAt)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
elements.push((
|
||||||
|
<Message key={currMessage.id + "msg"} message={currMessage} />
|
||||||
|
));
|
||||||
|
prevMessage = currMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<ScrollArea
|
||||||
|
viewportRef={scrollRef}
|
||||||
|
onScrollPositionChange={onScrollPositionChange}
|
||||||
|
>
|
||||||
|
<Stack h={"91vh"} pr={"md"} gap={"sm"}>
|
||||||
|
{getChatElements()}
|
||||||
|
</Stack>
|
||||||
|
</ScrollArea>
|
||||||
|
<MessageInput />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Chat;
|
||||||
20
src/components/Chat/components/ChatDate/ChatDate.tsx
Normal file
20
src/components/Chat/components/ChatDate/ChatDate.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Box, Center, Pill } from "@mantine/core";
|
||||||
|
import styles from "../../Chat.module.css";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
date: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChatDate = ({ date }: Props) => {
|
||||||
|
return (
|
||||||
|
<Center>
|
||||||
|
<Pill size={"md"} className={styles["message"]}>
|
||||||
|
<Box>
|
||||||
|
{date.toLocaleDateString("ru-RU", { day: "numeric", month: "long" })}
|
||||||
|
</Box>
|
||||||
|
</Pill>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatDate;
|
||||||
78
src/components/Chat/components/Message/Message.tsx
Normal file
78
src/components/Chat/components/Message/Message.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { MessageSchema } from "../../../../client";
|
||||||
|
import { Box, Center, em, Flex, Group, rem } from "@mantine/core";
|
||||||
|
import { formatDateTime } from "../../../../types/utils.ts";
|
||||||
|
import { IconAlertCircle, IconCheck, IconClock, IconTrash } from "@tabler/icons-react";
|
||||||
|
import { useContextMenu } from "mantine-contextmenu";
|
||||||
|
import useMessage from "./hooks/useMessage.tsx";
|
||||||
|
import styles from "../../Chat.module.css";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
message: MessageSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageStatuses {
|
||||||
|
SUCCESS = "SUCCESS",
|
||||||
|
ERROR = "ERROR",
|
||||||
|
SENDING = "SENDING",
|
||||||
|
}
|
||||||
|
|
||||||
|
const Message = ({ message }: Props) => {
|
||||||
|
const isMine = !message.tgSender;
|
||||||
|
const isSuccess = message.status == MessageStatuses.SUCCESS;
|
||||||
|
const { onDeleteMessageClick } = useMessage();
|
||||||
|
const { showContextMenu } = useContextMenu();
|
||||||
|
|
||||||
|
const contextMenu = () => showContextMenu([
|
||||||
|
{
|
||||||
|
key: "delete",
|
||||||
|
onClick: () => onDeleteMessageClick(message),
|
||||||
|
title: "Удалить",
|
||||||
|
icon: <IconTrash />,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getStatusIcon = () => {
|
||||||
|
const size = em(18);
|
||||||
|
if (message.status == MessageStatuses.ERROR) {
|
||||||
|
return <IconAlertCircle size={size} />;
|
||||||
|
}
|
||||||
|
if (message.status == MessageStatuses.SENDING) {
|
||||||
|
return <IconClock size={size} />;
|
||||||
|
}
|
||||||
|
return <IconCheck size={size} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex justify={isMine ? "flex-end" : "flex-start"}>
|
||||||
|
<Box
|
||||||
|
py={rem(5)}
|
||||||
|
px={rem(15)}
|
||||||
|
className={classNames(isMine ? styles["message"] : styles["other-message"])}
|
||||||
|
onContextMenu={isMine && isSuccess ? contextMenu() : undefined}
|
||||||
|
>
|
||||||
|
{!isMine && (
|
||||||
|
<div>{message.tgSender!.lastName} {message.tgSender!.firstName}</div>
|
||||||
|
)}
|
||||||
|
<div>{message.text}</div>
|
||||||
|
<Group
|
||||||
|
gap={em(5)}
|
||||||
|
justify={"flex-end"}
|
||||||
|
align={"center"}
|
||||||
|
wrap={"nowrap"}
|
||||||
|
>
|
||||||
|
<Center>
|
||||||
|
{formatDateTime(message.createdAt).substring(11, 16)}
|
||||||
|
</Center>
|
||||||
|
{isMine && (
|
||||||
|
<Center>
|
||||||
|
{getStatusIcon()}
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Message;
|
||||||
23
src/components/Chat/components/Message/hooks/useMessage.tsx
Normal file
23
src/components/Chat/components/Message/hooks/useMessage.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { ChatService, MessageSchema } from "../../../../../client";
|
||||||
|
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||||
|
|
||||||
|
const useMessage = () => {
|
||||||
|
|
||||||
|
const onDeleteMessageClick = (message: MessageSchema) => {
|
||||||
|
ChatService.deleteMessage({
|
||||||
|
messageId: message.id,
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onDeleteMessageClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useMessage;
|
||||||
45
src/components/Chat/components/MessageInput/MessageInput.tsx
Normal file
45
src/components/Chat/components/MessageInput/MessageInput.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Button, Group, TextInput, Tooltip } from "@mantine/core";
|
||||||
|
import { IconSend2 } from "@tabler/icons-react";
|
||||||
|
import { useChatContext } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import ActionIconCopy from "../../../ActionIconCopy/ActionIconCopy.tsx";
|
||||||
|
|
||||||
|
export type MessageForm = {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageInput = () => {
|
||||||
|
const { sendMessage, chat } = useChatContext();
|
||||||
|
const form = useForm<MessageForm>({
|
||||||
|
initialValues: {
|
||||||
|
message: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.onSubmit(values => sendMessage(values, form))}>
|
||||||
|
<Group wrap={"nowrap"} align={"center"}>
|
||||||
|
{chat?.tgGroup?.tgInviteLink && (
|
||||||
|
<Tooltip label={"Ссылка-приглашение"}>
|
||||||
|
<ActionIconCopy
|
||||||
|
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||||||
|
value={chat.tgGroup.tgInviteLink}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<TextInput
|
||||||
|
{...form.getInputProps("message")}
|
||||||
|
w={"100%"}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<IconSend2 />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessageInput;
|
||||||
33
src/modules/cardModules/cardEditorTabs/ChatTab/ChatTab.tsx
Normal file
33
src/modules/cardModules/cardEditorTabs/ChatTab/ChatTab.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { ChatContextProvider } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
|
import ChatWrapper from "./components/ChatWrapper.tsx";
|
||||||
|
import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||||
|
import { Stack } from "@mantine/core";
|
||||||
|
import InlineButton from "../../../../components/InlineButton/InlineButton.tsx";
|
||||||
|
import { IconMessagePlus } from "@tabler/icons-react";
|
||||||
|
import useChatTab from "./hooks/useChatTab.tsx";
|
||||||
|
|
||||||
|
const ChatTab = () => {
|
||||||
|
const { selectedCard } = useCardPageContext();
|
||||||
|
const { onChatCreateClick } = useChatTab();
|
||||||
|
|
||||||
|
if (!selectedCard?.chat) {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<InlineButton
|
||||||
|
onClick={onChatCreateClick}
|
||||||
|
>
|
||||||
|
<IconMessagePlus />
|
||||||
|
Создать чат
|
||||||
|
</InlineButton>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatContextProvider>
|
||||||
|
<ChatWrapper />
|
||||||
|
</ChatContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatTab;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { useChatContext } from "../../../../../pages/ClientsPage/contexts/ChatContext.tsx";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||||
|
import Chat from "../../../../../components/Chat/Chat.tsx";
|
||||||
|
|
||||||
|
const ChatWrapper = () => {
|
||||||
|
const { setChat } = useChatContext();
|
||||||
|
const { selectedCard } = useCardPageContext();
|
||||||
|
|
||||||
|
if (!selectedCard) return;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setChat(selectedCard.chat);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Chat />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChatWrapper;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||||
|
import { ChatService } from "../../../../../client";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
const useChatTab = () => {
|
||||||
|
const { selectedCard, refetchCard } = useCardPageContext();
|
||||||
|
|
||||||
|
const createChat = () => {
|
||||||
|
if (!selectedCard?.clientId) {
|
||||||
|
notifications.error({ message: "Ошибка создания чата, не указан клиент" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatService.createChat({
|
||||||
|
requestBody: {
|
||||||
|
clientId: selectedCard.clientId,
|
||||||
|
cardId: selectedCard.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message });
|
||||||
|
refetchCard();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChatCreateClick = () => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Создание чата",
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
Вы уверены что хотите создать чат для сделки {selectedCard?.name}?
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
labels: { confirm: "Да", cancel: "Нет" },
|
||||||
|
confirmProps: { color: "green" },
|
||||||
|
onConfirm: createChat,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onChatCreateClick,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useChatTab;
|
||||||
@@ -128,16 +128,18 @@ const ClientTab = () => {
|
|||||||
value={client}
|
value={client}
|
||||||
onChange={setClient}
|
onChange={setClient}
|
||||||
withLabel
|
withLabel
|
||||||
disabled={!isEqual(initialValues, form.values)}
|
disabled={!isEqual(initialValues, form.values) || !!card?.chat}
|
||||||
/>
|
/>
|
||||||
<Group>
|
{!card?.chat && (
|
||||||
<InlineButton
|
<Group>
|
||||||
onClick={handleSelectClient}
|
<InlineButton
|
||||||
disabled={!isEditorDisabled()}
|
onClick={handleSelectClient}
|
||||||
>
|
disabled={!isEditorDisabled()}
|
||||||
Сохранить
|
>
|
||||||
</InlineButton>
|
Сохранить
|
||||||
</Group>
|
</InlineButton>
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
{clientDataEditor}
|
{clientDataEditor}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import ProductAndServiceTab from "./cardModules/cardEditorTabs/ProductAndService
|
|||||||
import EmployeesTab from "./cardModules/cardEditorTabs/EmployeesTab/EmployeesTab.tsx";
|
import EmployeesTab from "./cardModules/cardEditorTabs/EmployeesTab/EmployeesTab.tsx";
|
||||||
import ShippingTab from "./cardModules/cardEditorTabs/ShippingTab/ShippingTab.tsx";
|
import ShippingTab from "./cardModules/cardEditorTabs/ShippingTab/ShippingTab.tsx";
|
||||||
import ManagerTab from "./cardModules/cardEditorTabs/ManagersTab/ManagersTab.tsx";
|
import ManagerTab from "./cardModules/cardEditorTabs/ManagersTab/ManagersTab.tsx";
|
||||||
|
import ChatTab from "./cardModules/cardEditorTabs/ChatTab/ChatTab.tsx";
|
||||||
|
|
||||||
const connectModules = (modules: ModulesType) => {
|
const connectModules = (modules: ModulesType) => {
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ const connectModules = (modules: ModulesType) => {
|
|||||||
modules[ModuleNames.EMPLOYEES].tab = <EmployeesTab />;
|
modules[ModuleNames.EMPLOYEES].tab = <EmployeesTab />;
|
||||||
modules[ModuleNames.SHIPMENT].tab = <ShippingTab />;
|
modules[ModuleNames.SHIPMENT].tab = <ShippingTab />;
|
||||||
modules[ModuleNames.MANAGERS].tab = <ManagerTab />;
|
modules[ModuleNames.MANAGERS].tab = <ManagerTab />;
|
||||||
|
modules[ModuleNames.CHAT].tab = <ChatTab />;
|
||||||
|
|
||||||
return modules;
|
return modules;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
IconCubeSend,
|
IconCubeSend,
|
||||||
IconUsersGroup,
|
IconUsersGroup,
|
||||||
IconUserCog,
|
IconUserCog,
|
||||||
|
IconMessage,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import ModulesType from "./types.tsx";
|
import ModulesType from "./types.tsx";
|
||||||
import connectModules from "./connectModules.tsx";
|
import connectModules from "./connectModules.tsx";
|
||||||
@@ -14,6 +15,7 @@ export enum ModuleNames {
|
|||||||
SHIPMENT = "shipment",
|
SHIPMENT = "shipment",
|
||||||
EMPLOYEES = "employees",
|
EMPLOYEES = "employees",
|
||||||
MANAGERS = "managers",
|
MANAGERS = "managers",
|
||||||
|
CHAT = "chat",
|
||||||
}
|
}
|
||||||
|
|
||||||
const modules: ModulesType = {
|
const modules: ModulesType = {
|
||||||
@@ -52,6 +54,13 @@ const modules: ModulesType = {
|
|||||||
icon: <IconUserCog />,
|
icon: <IconUserCog />,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
[ModuleNames.CHAT]: {
|
||||||
|
info: {
|
||||||
|
label: "Чат",
|
||||||
|
key: "chat",
|
||||||
|
icon: <IconMessage />,
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MODULES = connectModules(modules);
|
export const MODULES = connectModules(modules);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { Button } from "@mantine/core";
|
|||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
import { ClientSchema, ClientService } from "../../client";
|
import { ClientSchema, ClientService } from "../../client";
|
||||||
import { notifications } from "../../shared/lib/notifications.ts";
|
import { notifications } from "../../shared/lib/notifications.ts";
|
||||||
|
import { ChatContextProvider } from "./contexts/ChatContext.tsx";
|
||||||
|
import ClientChatDrawer from "./drawers/ClientChatDrawer.tsx";
|
||||||
|
|
||||||
const ClientsPage: FC = () => {
|
const ClientsPage: FC = () => {
|
||||||
const { clients, refetch } = useClientsList();
|
const { clients, refetch } = useClientsList();
|
||||||
@@ -62,11 +64,15 @@ const ClientsPage: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
<PageBlock>
|
<PageBlock>
|
||||||
<ClientsTable
|
<ChatContextProvider>
|
||||||
onChange={onChange}
|
<ClientsTable
|
||||||
onDelete={onDelete}
|
onChange={onChange}
|
||||||
items={clients}
|
onDelete={onDelete}
|
||||||
/>
|
items={clients}
|
||||||
|
refetch={refetch}
|
||||||
|
/>
|
||||||
|
<ClientChatDrawer />
|
||||||
|
</ChatContextProvider>
|
||||||
</PageBlock>
|
</PageBlock>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { ChatService, ClientSchema } from "../../../../client";
|
||||||
|
import { ActionIcon, Text, Tooltip } from "@mantine/core";
|
||||||
|
import { IconMessage, IconMessagePlus } from "@tabler/icons-react";
|
||||||
|
import { useChatContext } from "../../contexts/ChatContext.tsx";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { notifications } from "../../../../shared/lib/notifications.ts";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
client: ClientSchema;
|
||||||
|
refetch: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClientChatButton = ({ client, refetch }: Props) => {
|
||||||
|
const { setChat } = useChatContext();
|
||||||
|
|
||||||
|
const createChat = () => {
|
||||||
|
console.log("Creating chat")
|
||||||
|
ChatService.createChat({
|
||||||
|
requestBody: {
|
||||||
|
clientId: client.id,
|
||||||
|
cardId: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
notifications.guess(ok, { message });
|
||||||
|
refetch();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCreateChatClick = () => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Создание чата",
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
Вы уверены что хотите создать чат с {client.name}?
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
labels: { confirm: "Да", cancel: "Нет" },
|
||||||
|
confirmProps: { color: "green" },
|
||||||
|
onConfirm: createChat,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (client.chat) {
|
||||||
|
return (
|
||||||
|
<Tooltip label="Открыть чат">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => setChat(client.chat!)}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconMessage />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip label="Создать чат">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={onCreateChatClick}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconMessagePlus />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClientChatButton;
|
||||||
@@ -7,13 +7,20 @@ import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
|||||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
import { CRUDTableProps } from "../../../../types/CRUDTable.tsx";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
|
import ClientChatButton from "../ClientChatButton/ClientChatButton.tsx";
|
||||||
|
|
||||||
const ClientsTable: FC<CRUDTableProps<ClientSchema>> = ({
|
type RefetchProps = {
|
||||||
items,
|
refetch: () => void;
|
||||||
onDelete,
|
}
|
||||||
onChange,
|
|
||||||
}) => {
|
const ClientsTable: FC<CRUDTableProps<ClientSchema> & RefetchProps> = ({
|
||||||
|
items,
|
||||||
|
onDelete,
|
||||||
|
onChange,
|
||||||
|
refetch,
|
||||||
|
}) => {
|
||||||
const columns = useClientsTableColumns();
|
const columns = useClientsTableColumns();
|
||||||
|
|
||||||
const onEditClick = (client: ClientSchema) => {
|
const onEditClick = (client: ClientSchema) => {
|
||||||
if (!onChange) return;
|
if (!onChange) return;
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
@@ -26,6 +33,7 @@ const ClientsTable: FC<CRUDTableProps<ClientSchema>> = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BaseTable
|
<BaseTable
|
||||||
@@ -38,6 +46,7 @@ const ClientsTable: FC<CRUDTableProps<ClientSchema>> = ({
|
|||||||
enableRowActions: true,
|
enableRowActions: true,
|
||||||
renderRowActions: ({ row }) => (
|
renderRowActions: ({ row }) => (
|
||||||
<Flex gap="md">
|
<Flex gap="md">
|
||||||
|
<ClientChatButton client={row.original} refetch={refetch}/>
|
||||||
<Tooltip label="Редактировать">
|
<Tooltip label="Редактировать">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -50,8 +59,7 @@ const ClientsTable: FC<CRUDTableProps<ClientSchema>> = ({
|
|||||||
<Tooltip label="Удалить">
|
<Tooltip label="Удалить">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (onDelete)
|
if (onDelete) onDelete(row.original);
|
||||||
onDelete(row.original);
|
|
||||||
}}
|
}}
|
||||||
variant={"default"}>
|
variant={"default"}>
|
||||||
<IconTrash />
|
<IconTrash />
|
||||||
|
|||||||
161
src/pages/ClientsPage/contexts/ChatContext.tsx
Normal file
161
src/pages/ClientsPage/contexts/ChatContext.tsx
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import React, { createContext, FC, MutableRefObject, useContext, useEffect, useRef, useState } from "react";
|
||||||
|
import { ChatSchema, ChatService, MessageSchema } from "../../../client";
|
||||||
|
import { notifications } from "../../../shared/lib/notifications.ts";
|
||||||
|
import { MessageForm } from "../../../components/Chat/components/MessageInput/MessageInput.tsx";
|
||||||
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
|
import { useDebouncedState } from "@mantine/hooks";
|
||||||
|
|
||||||
|
type ChatContextState = {
|
||||||
|
chat: ChatSchema | null;
|
||||||
|
setChat: (chat: ChatSchema | null) => void;
|
||||||
|
messages: MessageSchema[];
|
||||||
|
onScrollPositionChange: (values: { x: number, y: number }) => void;
|
||||||
|
scrollRef: MutableRefObject<HTMLDivElement | null>;
|
||||||
|
sendMessage: (values: MessageForm, form: UseFormReturnType<MessageForm>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChatContext = createContext<ChatContextState | undefined>(undefined);
|
||||||
|
|
||||||
|
const useChatContextState = () => {
|
||||||
|
const [chat, setChat] = useState<ChatSchema | null>(null);
|
||||||
|
const [messages, setMessages] = useState<MessageSchema[]>([]);
|
||||||
|
const [offset, setOffset] = useState(0);
|
||||||
|
const limit = 20;
|
||||||
|
const [hasMore, setHasMore] = useState(true);
|
||||||
|
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [isScrollToBottom, setIsScrollToBottom] = useState(true);
|
||||||
|
const [scrollPosition, setScrollPosition] = useDebouncedState<{ x: number, y: number }>({ x: 0, y: 0 }, 400);
|
||||||
|
|
||||||
|
const setChatValue = (chat: ChatSchema | null) => {
|
||||||
|
if (chat) {
|
||||||
|
setOffset(0);
|
||||||
|
setHasMore(true);
|
||||||
|
setScrollPosition({ x: 0, y: 0 });
|
||||||
|
}
|
||||||
|
setChat(chat);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`has more = ${hasMore}`);
|
||||||
|
|
||||||
|
const fetchMessages = () => {
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
ChatService.getMessages({
|
||||||
|
requestBody: {
|
||||||
|
chatId: chat.id,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ messages: newMessages }) => {
|
||||||
|
if (newMessages.length < limit) setHasMore(false);
|
||||||
|
setMessages([...newMessages]);
|
||||||
|
setOffset((prev) => prev + limit);
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMessagesOnScroll = () => {
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
ChatService.getMessages({
|
||||||
|
requestBody: {
|
||||||
|
chatId: chat.id,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ messages: newMessages }) => {
|
||||||
|
console.log(newMessages);
|
||||||
|
if (newMessages.length < limit) setHasMore(false);
|
||||||
|
setMessages((prev) => [...prev, ...newMessages]);
|
||||||
|
setOffset((prev) => prev + limit);
|
||||||
|
if (scrollRef.current) {
|
||||||
|
console.log("FETCH ON SCROLL")
|
||||||
|
const prevPosition = limit / offset * scrollRef.current.scrollHeight;
|
||||||
|
console.log(`PREV POSITION = ${prevPosition}`)
|
||||||
|
scrollRef.current.scrollTo({ top: prevPosition, behavior: "instant" });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchMessages();
|
||||||
|
|
||||||
|
const interval = setInterval(fetchMessages, 2000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [chat]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollRef.current && isScrollToBottom && chat) {
|
||||||
|
setIsScrollToBottom(false);
|
||||||
|
console.log("Scroll to the bottom");
|
||||||
|
console.log(`scrollHeight = ${scrollRef.current.scrollHeight}`);
|
||||||
|
scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
}, [messages, isScrollToBottom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chat) return;
|
||||||
|
console.log(`handleScroll ${Math.abs(scrollPosition.y - 200)}`);
|
||||||
|
if (Math.abs(scrollPosition.y - 200) <= 200 && hasMore) {
|
||||||
|
console.log("handleScroll WORK");
|
||||||
|
fetchMessagesOnScroll();
|
||||||
|
}
|
||||||
|
}, [scrollPosition]);
|
||||||
|
|
||||||
|
const sendMessage = (values: MessageForm, form: UseFormReturnType<MessageForm>) => {
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
ChatService.sendMessage({
|
||||||
|
requestBody: {
|
||||||
|
message: {
|
||||||
|
text: values.message,
|
||||||
|
chatId: chat.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
}
|
||||||
|
form.reset();
|
||||||
|
setIsScrollToBottom(true);
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
chat,
|
||||||
|
setChat: setChatValue,
|
||||||
|
messages,
|
||||||
|
onScrollPositionChange: setScrollPosition,
|
||||||
|
scrollRef,
|
||||||
|
sendMessage,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChatContextProviderProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChatContextProvider: FC<ChatContextProviderProps> = ({ children }) => {
|
||||||
|
const state = useChatContextState();
|
||||||
|
return (
|
||||||
|
<ChatContext.Provider value={state}>
|
||||||
|
{children}
|
||||||
|
</ChatContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useChatContext = () => {
|
||||||
|
const context = useContext(ChatContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useChatContext must be used within a ChatContextProvider",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
24
src/pages/ClientsPage/drawers/ClientChatDrawer.tsx
Normal file
24
src/pages/ClientsPage/drawers/ClientChatDrawer.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { useChatContext } from "../contexts/ChatContext.tsx";
|
||||||
|
import { Drawer } from "@mantine/core";
|
||||||
|
import Chat from "../../../components/Chat/Chat.tsx";
|
||||||
|
|
||||||
|
|
||||||
|
const ClientChatDrawer = () => {
|
||||||
|
const { chat, setChat } = useChatContext();
|
||||||
|
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
opened={!!chat}
|
||||||
|
onClose={() => setChat(null)}
|
||||||
|
position={"right"}
|
||||||
|
size={"calc(50vw)"}
|
||||||
|
withCloseButton={false}
|
||||||
|
>
|
||||||
|
<Chat />
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClientChatDrawer;
|
||||||
Reference in New Issue
Block a user