Merge remote-tracking branch 'origin/chats'
This commit is contained in:
		@@ -54,6 +54,7 @@
 | 
				
			|||||||
        "react-imask": "^7.6.1",
 | 
					        "react-imask": "^7.6.1",
 | 
				
			||||||
        "react-redux": "^9.1.2",
 | 
					        "react-redux": "^9.1.2",
 | 
				
			||||||
        "react-to-print": "^2.15.1",
 | 
					        "react-to-print": "^2.15.1",
 | 
				
			||||||
 | 
					        "react-virtuoso": "^4.12.6",
 | 
				
			||||||
        "reactflow": "^11.11.4",
 | 
					        "reactflow": "^11.11.4",
 | 
				
			||||||
        "recharts": "^2.13.3",
 | 
					        "recharts": "^2.13.3",
 | 
				
			||||||
        "zod": "^3.23.8"
 | 
					        "zod": "^3.23.8"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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';
 | 
				
			||||||
@@ -39,6 +40,7 @@ export type { BillPaymentStatus } from './models/BillPaymentStatus';
 | 
				
			|||||||
export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
 | 
					export type { BillStatusUpdateRequest } from './models/BillStatusUpdateRequest';
 | 
				
			||||||
export type { BoardSchema } from './models/BoardSchema';
 | 
					export type { BoardSchema } from './models/BoardSchema';
 | 
				
			||||||
export type { Body_parse_deals_excel } from './models/Body_parse_deals_excel';
 | 
					export type { Body_parse_deals_excel } from './models/Body_parse_deals_excel';
 | 
				
			||||||
 | 
					export type { Body_send_messages_with_files } from './models/Body_send_messages_with_files';
 | 
				
			||||||
export type { Body_upload_passport_image } from './models/Body_upload_passport_image';
 | 
					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_barcode_image } from './models/Body_upload_product_barcode_image';
 | 
				
			||||||
export type { Body_upload_product_image } from './models/Body_upload_product_image';
 | 
					export type { Body_upload_product_image } from './models/Body_upload_product_image';
 | 
				
			||||||
@@ -114,6 +116,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 +145,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 +190,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';
 | 
				
			||||||
@@ -211,6 +217,9 @@ export type { DepartmentSchema } from './models/DepartmentSchema';
 | 
				
			|||||||
export type { DepartmentSectionBaseSchema } from './models/DepartmentSectionBaseSchema';
 | 
					export type { DepartmentSectionBaseSchema } from './models/DepartmentSectionBaseSchema';
 | 
				
			||||||
export type { DepartmentSectionBriefSchema } from './models/DepartmentSectionBriefSchema';
 | 
					export type { DepartmentSectionBriefSchema } from './models/DepartmentSectionBriefSchema';
 | 
				
			||||||
export type { DepartmentSectionSchema } from './models/DepartmentSectionSchema';
 | 
					export type { DepartmentSectionSchema } from './models/DepartmentSectionSchema';
 | 
				
			||||||
 | 
					export type { EditMessageRequest } from './models/EditMessageRequest';
 | 
				
			||||||
 | 
					export type { EditMessageResponse } from './models/EditMessageResponse';
 | 
				
			||||||
 | 
					export type { EditMessageSchema } from './models/EditMessageSchema';
 | 
				
			||||||
export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftIdResponse';
 | 
					export type { FinishPauseByShiftIdResponse } from './models/FinishPauseByShiftIdResponse';
 | 
				
			||||||
export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse';
 | 
					export type { FinishPauseByUserIdResponse } from './models/FinishPauseByUserIdResponse';
 | 
				
			||||||
export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse';
 | 
					export type { FinishShiftByIdResponse } from './models/FinishShiftByIdResponse';
 | 
				
			||||||
@@ -243,11 +252,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';
 | 
				
			||||||
@@ -270,12 +283,15 @@ export type { GetWorkShiftsPlanningDataRequest } from './models/GetWorkShiftsPla
 | 
				
			|||||||
export type { GetWorkShiftsResponse } from './models/GetWorkShiftsResponse';
 | 
					export type { GetWorkShiftsResponse } from './models/GetWorkShiftsResponse';
 | 
				
			||||||
export type { GroupBillRequestSchema } from './models/GroupBillRequestSchema';
 | 
					export type { GroupBillRequestSchema } from './models/GroupBillRequestSchema';
 | 
				
			||||||
export type { HTTPValidationError } from './models/HTTPValidationError';
 | 
					export type { HTTPValidationError } from './models/HTTPValidationError';
 | 
				
			||||||
 | 
					export type { LoadMessagesResponse } from './models/LoadMessagesResponse';
 | 
				
			||||||
export type { LoadReceiptRequest } from './models/LoadReceiptRequest';
 | 
					export type { LoadReceiptRequest } from './models/LoadReceiptRequest';
 | 
				
			||||||
export type { LoadReceiptResponse } from './models/LoadReceiptResponse';
 | 
					export type { LoadReceiptResponse } from './models/LoadReceiptResponse';
 | 
				
			||||||
export type { ManageEmployeeRequest } from './models/ManageEmployeeRequest';
 | 
					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 { MessageFileSchema } from './models/MessageFileSchema';
 | 
				
			||||||
 | 
					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';
 | 
				
			||||||
@@ -324,10 +340,15 @@ export type { ProjectGeneralInfoSchema } from './models/ProjectGeneralInfoSchema
 | 
				
			|||||||
export type { ProjectSchema } from './models/ProjectSchema';
 | 
					export type { ProjectSchema } from './models/ProjectSchema';
 | 
				
			||||||
export type { ReceiptBoxSchema } from './models/ReceiptBoxSchema';
 | 
					export type { ReceiptBoxSchema } from './models/ReceiptBoxSchema';
 | 
				
			||||||
export type { ReceiptPalletSchema } from './models/ReceiptPalletSchema';
 | 
					export type { ReceiptPalletSchema } from './models/ReceiptPalletSchema';
 | 
				
			||||||
 | 
					export type { RepeatSendingMessageSchema } from './models/RepeatSendingMessageSchema';
 | 
				
			||||||
 | 
					export type { RepeatSendingTextMessageRequest } from './models/RepeatSendingTextMessageRequest';
 | 
				
			||||||
 | 
					export type { RepeatSendingTextMessageResponse } from './models/RepeatSendingTextMessageResponse';
 | 
				
			||||||
export type { ResidualBoxSchema } from './models/ResidualBoxSchema';
 | 
					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 { SendTextMessageRequest } from './models/SendTextMessageRequest';
 | 
				
			||||||
 | 
					export type { SendTextMessageResponse } from './models/SendTextMessageResponse';
 | 
				
			||||||
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 +380,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 +458,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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/client/models/Body_send_messages_with_files.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/client/models/Body_send_messages_with_files.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type Body_send_messages_with_files = {
 | 
				
			||||||
 | 
					    files: Array<Blob>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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/EditMessageRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/EditMessageRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { EditMessageSchema } from './EditMessageSchema';
 | 
				
			||||||
 | 
					export type EditMessageRequest = {
 | 
				
			||||||
 | 
					    message: EditMessageSchema;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/EditMessageResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/EditMessageResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type EditMessageResponse = {
 | 
				
			||||||
 | 
					    ok: boolean;
 | 
				
			||||||
 | 
					    message: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/client/models/EditMessageSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/client/models/EditMessageSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type EditMessageSchema = {
 | 
				
			||||||
 | 
					    text: string;
 | 
				
			||||||
 | 
					    chatId: number;
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										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>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/LoadMessagesResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/LoadMessagesResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type LoadMessagesResponse = {
 | 
				
			||||||
 | 
					    ok: boolean;
 | 
				
			||||||
 | 
					    message: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/client/models/MessageFileSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/client/models/MessageFileSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type MessageFileSchema = {
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					    filePath: string;
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
 | 
					    fileName: string;
 | 
				
			||||||
 | 
					    fileSize: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/client/models/MessageSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/client/models/MessageSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { MessageFileSchema } from './MessageFileSchema';
 | 
				
			||||||
 | 
					import type { TgUserSchema } from './TgUserSchema';
 | 
				
			||||||
 | 
					import type { UserSchema } from './UserSchema';
 | 
				
			||||||
 | 
					export type MessageSchema = {
 | 
				
			||||||
 | 
					    text: string;
 | 
				
			||||||
 | 
					    chatId: number;
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					    createdAt: string;
 | 
				
			||||||
 | 
					    tgSender: (TgUserSchema | null);
 | 
				
			||||||
 | 
					    crmSender: (UserSchema | null);
 | 
				
			||||||
 | 
					    status: string;
 | 
				
			||||||
 | 
					    isEdited: boolean;
 | 
				
			||||||
 | 
					    file?: (MessageFileSchema | null);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/client/models/RepeatSendingMessageSchema.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/client/models/RepeatSendingMessageSchema.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type RepeatSendingMessageSchema = {
 | 
				
			||||||
 | 
					    text: string;
 | 
				
			||||||
 | 
					    chatId: number;
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/RepeatSendingTextMessageRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/RepeatSendingTextMessageRequest.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { RepeatSendingMessageSchema } from './RepeatSendingMessageSchema';
 | 
				
			||||||
 | 
					export type RepeatSendingTextMessageRequest = {
 | 
				
			||||||
 | 
					    message: RepeatSendingMessageSchema;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/RepeatSendingTextMessageResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/RepeatSendingTextMessageResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type RepeatSendingTextMessageResponse = {
 | 
				
			||||||
 | 
					    ok: boolean;
 | 
				
			||||||
 | 
					    message: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/SendTextMessageRequest.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/SendTextMessageRequest.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 SendTextMessageRequest = {
 | 
				
			||||||
 | 
					    message: BaseMessageSchema;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/client/models/SendTextMessageResponse.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/client/models/SendTextMessageResponse.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type SendTextMessageResponse = {
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										214
									
								
								src/client/services/ChatService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/client/services/ChatService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,214 @@
 | 
				
			|||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { Body_send_messages_with_files } from '../models/Body_send_messages_with_files';
 | 
				
			||||||
 | 
					import type { CreateChatRequest } from '../models/CreateChatRequest';
 | 
				
			||||||
 | 
					import type { CreateChatResponse } from '../models/CreateChatResponse';
 | 
				
			||||||
 | 
					import type { DeleteMessageResponse } from '../models/DeleteMessageResponse';
 | 
				
			||||||
 | 
					import type { EditMessageRequest } from '../models/EditMessageRequest';
 | 
				
			||||||
 | 
					import type { EditMessageResponse } from '../models/EditMessageResponse';
 | 
				
			||||||
 | 
					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 { LoadMessagesResponse } from '../models/LoadMessagesResponse';
 | 
				
			||||||
 | 
					import type { RepeatSendingTextMessageRequest } from '../models/RepeatSendingTextMessageRequest';
 | 
				
			||||||
 | 
					import type { RepeatSendingTextMessageResponse } from '../models/RepeatSendingTextMessageResponse';
 | 
				
			||||||
 | 
					import type { SendTextMessageRequest } from '../models/SendTextMessageRequest';
 | 
				
			||||||
 | 
					import type { SendTextMessageResponse } from '../models/SendTextMessageResponse';
 | 
				
			||||||
 | 
					import type { CancelablePromise } from '../core/CancelablePromise';
 | 
				
			||||||
 | 
					import { OpenAPI } from '../core/OpenAPI';
 | 
				
			||||||
 | 
					import { request as __request } from '../core/request';
 | 
				
			||||||
 | 
					export class ChatService {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Send Text Message
 | 
				
			||||||
 | 
					     * @returns SendTextMessageResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static sendTextMessage({
 | 
				
			||||||
 | 
					        requestBody,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        requestBody: SendTextMessageRequest,
 | 
				
			||||||
 | 
					    }): CancelablePromise<SendTextMessageResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            url: '/chat/message',
 | 
				
			||||||
 | 
					            body: requestBody,
 | 
				
			||||||
 | 
					            mediaType: 'application/json',
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Validation Error`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Edit Message
 | 
				
			||||||
 | 
					     * @returns EditMessageResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static editMessage({
 | 
				
			||||||
 | 
					        requestBody,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        requestBody: EditMessageRequest,
 | 
				
			||||||
 | 
					    }): CancelablePromise<EditMessageResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'PATCH',
 | 
				
			||||||
 | 
					            url: '/chat/message',
 | 
				
			||||||
 | 
					            body: requestBody,
 | 
				
			||||||
 | 
					            mediaType: 'application/json',
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Validation Error`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Repeat Sending Text Message
 | 
				
			||||||
 | 
					     * @returns RepeatSendingTextMessageResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static repeatSendingTextMessage({
 | 
				
			||||||
 | 
					        requestBody,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        requestBody: RepeatSendingTextMessageRequest,
 | 
				
			||||||
 | 
					    }): CancelablePromise<RepeatSendingTextMessageResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            url: '/chat/message/repeat',
 | 
				
			||||||
 | 
					            body: requestBody,
 | 
				
			||||||
 | 
					            mediaType: 'application/json',
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Validation Error`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Send Messages With Files
 | 
				
			||||||
 | 
					     * @returns LoadMessagesResponse Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static sendMessagesWithFiles({
 | 
				
			||||||
 | 
					        chatId,
 | 
				
			||||||
 | 
					        caption,
 | 
				
			||||||
 | 
					        formData,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        chatId: number,
 | 
				
			||||||
 | 
					        caption: string,
 | 
				
			||||||
 | 
					        formData: Body_send_messages_with_files,
 | 
				
			||||||
 | 
					    }): CancelablePromise<LoadMessagesResponse> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            url: '/chat/message/files',
 | 
				
			||||||
 | 
					            query: {
 | 
				
			||||||
 | 
					                'chat_id': chatId,
 | 
				
			||||||
 | 
					                'caption': caption,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            formData: formData,
 | 
				
			||||||
 | 
					            mediaType: 'multipart/form-data',
 | 
				
			||||||
 | 
					            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`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get Tg File
 | 
				
			||||||
 | 
					     * @returns any Successful Response
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static getTgFileChatTgFileFileIdGet({
 | 
				
			||||||
 | 
					        fileId,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					        fileId: number,
 | 
				
			||||||
 | 
					    }): CancelablePromise<any> {
 | 
				
			||||||
 | 
					        return __request(OpenAPI, {
 | 
				
			||||||
 | 
					            method: 'GET',
 | 
				
			||||||
 | 
					            url: '/chat/tg-file/{file_id}',
 | 
				
			||||||
 | 
					            path: {
 | 
				
			||||||
 | 
					                'file_id': fileId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Validation Error`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/components/ActionIconCopy/ActionIconCopy.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/components/ActionIconCopy/ActionIconCopy.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					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}
 | 
				
			||||||
 | 
					            radius="xl"
 | 
				
			||||||
 | 
					            transitionProps={{ duration: 100, transition: "slide-down" }}
 | 
				
			||||||
 | 
					            opened={clipboard.copied}>
 | 
				
			||||||
 | 
					            <ActionIcon
 | 
				
			||||||
 | 
					                variant={"default"}
 | 
				
			||||||
 | 
					                size="lg"
 | 
				
			||||||
 | 
					                onClick={() => 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								src/components/Chat/Chat.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/components/Chat/Chat.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					import Message from "./components/Message/Message.tsx";
 | 
				
			||||||
 | 
					import { useChatContext } from "../../pages/ClientsPage/contexts/ChatContext.tsx";
 | 
				
			||||||
 | 
					import { MessageSchema } from "../../client";
 | 
				
			||||||
 | 
					import { ReactNode, useCallback } from "react";
 | 
				
			||||||
 | 
					import ChatDate from "./components/ChatDate/ChatDate.tsx";
 | 
				
			||||||
 | 
					import MessageInput from "./components/MessageInput/MessageInput.tsx";
 | 
				
			||||||
 | 
					import { Virtuoso } from "react-virtuoso";
 | 
				
			||||||
 | 
					import { Stack } from "@mantine/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Chat = () => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        messages,
 | 
				
			||||||
 | 
					        lastMessage,
 | 
				
			||||||
 | 
					        firstItemIndex,
 | 
				
			||||||
 | 
					        fetchMoreMessages,
 | 
				
			||||||
 | 
					    } = useChatContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onFollowOutputHandler = useCallback(
 | 
				
			||||||
 | 
					        (atBottom: boolean) => {
 | 
				
			||||||
 | 
					            if (atBottom) {
 | 
				
			||||||
 | 
					                return "auto";
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        [lastMessage],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const itemContent = useCallback(
 | 
				
			||||||
 | 
					        (index: number, sessionData: MessageSchema) => {
 | 
				
			||||||
 | 
					            let dateComponent: ReactNode | null = null;
 | 
				
			||||||
 | 
					            const msgArrayIdx = index - firstItemIndex;
 | 
				
			||||||
 | 
					            if (msgArrayIdx < 0 || msgArrayIdx > messages.length - 1) return;
 | 
				
			||||||
 | 
					            const currMessage = messages[msgArrayIdx];
 | 
				
			||||||
 | 
					            let prevMessage = null;
 | 
				
			||||||
 | 
					            if (msgArrayIdx > 0) {
 | 
				
			||||||
 | 
					                prevMessage = messages[msgArrayIdx - 1];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!prevMessage || prevMessage.createdAt.substring(5, 10) != currMessage.createdAt.substring(5, 10)) {
 | 
				
			||||||
 | 
					                dateComponent = (
 | 
				
			||||||
 | 
					                    <ChatDate
 | 
				
			||||||
 | 
					                        key={currMessage.id + "date"}
 | 
				
			||||||
 | 
					                        date={new Date(currMessage.createdAt)}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					                <Stack mb={"xs"} mr={"xs"}>
 | 
				
			||||||
 | 
					                    {dateComponent}
 | 
				
			||||||
 | 
					                    <Message
 | 
				
			||||||
 | 
					                        key={`${sessionData.id}${index}`}
 | 
				
			||||||
 | 
					                        message={sessionData}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                </Stack>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        [messages],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (messages.length === 0) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <Stack h={"96vh"} justify={"flex-end"}>
 | 
				
			||||||
 | 
					                <MessageInput />
 | 
				
			||||||
 | 
					            </Stack>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <Stack h={"96vh"}>
 | 
				
			||||||
 | 
					            <Virtuoso
 | 
				
			||||||
 | 
					                data={messages}
 | 
				
			||||||
 | 
					                followOutput={onFollowOutputHandler}
 | 
				
			||||||
 | 
					                firstItemIndex={firstItemIndex}
 | 
				
			||||||
 | 
					                initialTopMostItemIndex={messages.length - 1}
 | 
				
			||||||
 | 
					                itemContent={itemContent}
 | 
				
			||||||
 | 
					                startReached={fetchMoreMessages}
 | 
				
			||||||
 | 
					                height={"100%"}
 | 
				
			||||||
 | 
					                increaseViewportBy={200}
 | 
				
			||||||
 | 
					                alignToBottom
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <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;
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/components/Chat/components/ChatFile/ChatFile.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/components/Chat/components/ChatFile/ChatFile.module.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					.file-circe {
 | 
				
			||||||
 | 
					    @mixin light {
 | 
				
			||||||
 | 
					        background-color: var(--mantine-color-gray-0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    @mixin dark {
 | 
				
			||||||
 | 
					        background-color: var(--mantine-color-dark-4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    border-radius: 50%;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    justify-items: center;
 | 
				
			||||||
 | 
					    padding: 0.8rem;
 | 
				
			||||||
 | 
					    width: 3rem;
 | 
				
			||||||
 | 
					    height: 3rem;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/components/Chat/components/ChatFile/ChatFile.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/components/Chat/components/ChatFile/ChatFile.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					import { MessageFileSchema } from "../../../../client";
 | 
				
			||||||
 | 
					import ChatDocument from "./components/ChatDocument.tsx";
 | 
				
			||||||
 | 
					import ChatPhoto from "./components/ChatPhoto.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    file: MessageFileSchema;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChatFile = ({ file }: Props) => {
 | 
				
			||||||
 | 
					    if (file.type === "photo") {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <ChatPhoto file={file} />
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <ChatDocument file={file} />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ChatFile;
 | 
				
			||||||
@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					import { Center, Group, rem, Stack, Text } from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconFileFilled } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { MessageFileSchema } from "../../../../../client";
 | 
				
			||||||
 | 
					import styles from "../ChatFile.module.css";
 | 
				
			||||||
 | 
					import classNames from "classnames";
 | 
				
			||||||
 | 
					import downloadFile from "../utils/downloadFile.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    file: MessageFileSchema;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChatDocument = ({ file }: Props) => {
 | 
				
			||||||
 | 
					    const round = (value: number) => Math.round(value * 10) / 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getFileSize = () => {
 | 
				
			||||||
 | 
					        const BYTES_IN_KB = 1_024;
 | 
				
			||||||
 | 
					        const BYTES_IN_MB = 1_048_576;
 | 
				
			||||||
 | 
					        const BYTES_IN_GB = 1_073_741_824;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (file.fileSize < BYTES_IN_KB) {
 | 
				
			||||||
 | 
					            return `${file.fileSize} B`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (file.fileSize < BYTES_IN_MB) {
 | 
				
			||||||
 | 
					            return `${round(file.fileSize / BYTES_IN_KB)} KB`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (file.fileSize < BYTES_IN_GB) {
 | 
				
			||||||
 | 
					            return `${round(file.fileSize / BYTES_IN_MB)} MB`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return `${round(file.fileSize / BYTES_IN_GB)} GB`;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <Group justify={"center"} my={"sm"}>
 | 
				
			||||||
 | 
					            <Center
 | 
				
			||||||
 | 
					                className={classNames(styles["file-circe"])}
 | 
				
			||||||
 | 
					                onClick={() => downloadFile(file)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <IconFileFilled />
 | 
				
			||||||
 | 
					            </Center>
 | 
				
			||||||
 | 
					            <Stack gap={rem(1)} my={rem(1)}>
 | 
				
			||||||
 | 
					                <Text>{file.fileName}</Text>
 | 
				
			||||||
 | 
					                <Text>{getFileSize()}</Text>
 | 
				
			||||||
 | 
					            </Stack>
 | 
				
			||||||
 | 
					        </Group>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ChatDocument;
 | 
				
			||||||
@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					import { MessageFileSchema } from "../../../../../client";
 | 
				
			||||||
 | 
					import { Box, Image, Modal } from "@mantine/core";
 | 
				
			||||||
 | 
					import getDocLink from "../utils/getDocLink.ts";
 | 
				
			||||||
 | 
					import { useDisclosure } from "@mantine/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    file: MessageFileSchema;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChatPhoto = ({ file }: Props) => {
 | 
				
			||||||
 | 
					    const [opened, { open, close }] = useDisclosure(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const image = (
 | 
				
			||||||
 | 
					        <Image
 | 
				
			||||||
 | 
					            src={getDocLink(file.id)}
 | 
				
			||||||
 | 
					            radius={"md"}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					            <Modal
 | 
				
			||||||
 | 
					                opened={opened}
 | 
				
			||||||
 | 
					                onClose={close}
 | 
				
			||||||
 | 
					                size={"auto"}
 | 
				
			||||||
 | 
					                withCloseButton={false}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <Box mah={"90vh"} maw={"90vw"}>
 | 
				
			||||||
 | 
					                    {image}
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					            </Modal>
 | 
				
			||||||
 | 
					            <Box
 | 
				
			||||||
 | 
					                my={"sm"}
 | 
				
			||||||
 | 
					                onClick={open}
 | 
				
			||||||
 | 
					                style={{ cursor: "pointer" }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                {image}
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ChatPhoto;
 | 
				
			||||||
@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import { MessageFileSchema } from "../../../../../client";
 | 
				
			||||||
 | 
					import getDocLink from "./getDocLink.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const downloadFile = (file: MessageFileSchema) => {
 | 
				
			||||||
 | 
					    const link = document.createElement("a");
 | 
				
			||||||
 | 
					    link.href = getDocLink(file.id);
 | 
				
			||||||
 | 
					    link.download = file.fileName;
 | 
				
			||||||
 | 
					    document.body.appendChild(link);
 | 
				
			||||||
 | 
					    link.click();
 | 
				
			||||||
 | 
					    document.body.removeChild(link);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default downloadFile;
 | 
				
			||||||
@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					const getDocLink = (fileId: number) => {
 | 
				
			||||||
 | 
					    return `${import.meta.env.VITE_API_URL}/chat/tg-file/${fileId}`;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default getDocLink;
 | 
				
			||||||
							
								
								
									
										115
									
								
								src/components/Chat/components/Message/Message.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/components/Chat/components/Message/Message.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					import { MessageSchema } from "../../../../client";
 | 
				
			||||||
 | 
					import { Box, Center, em, Flex, Group, rem } from "@mantine/core";
 | 
				
			||||||
 | 
					import { formatDateTime } from "../../../../types/utils.ts";
 | 
				
			||||||
 | 
					import { IconAlertCircle, IconBrandTelegram, IconCheck, IconClock, IconEdit, 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";
 | 
				
			||||||
 | 
					import ChatFile from "../ChatFile/ChatFile.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 isError = message.status == MessageStatuses.ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        onDeleteMessageClick,
 | 
				
			||||||
 | 
					        onEditMessageClick,
 | 
				
			||||||
 | 
					        onRepeatSendingClick,
 | 
				
			||||||
 | 
					    } = useMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { showContextMenu } = useContextMenu();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const contextMenuSuccessMsg = () => showContextMenu([
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            key: "delete",
 | 
				
			||||||
 | 
					            onClick: () => onDeleteMessageClick(message),
 | 
				
			||||||
 | 
					            title: "Удалить",
 | 
				
			||||||
 | 
					            icon: <IconTrash />,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            key: "edit",
 | 
				
			||||||
 | 
					            onClick: () => onEditMessageClick(message),
 | 
				
			||||||
 | 
					            title: "Редактировать",
 | 
				
			||||||
 | 
					            icon: <IconEdit />,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const contextMenuErrorMsg = () => showContextMenu([
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            key: "repeatSending",
 | 
				
			||||||
 | 
					            onClick: () => onRepeatSendingClick(message),
 | 
				
			||||||
 | 
					            title: "Повторить отправку",
 | 
				
			||||||
 | 
					            icon: <IconBrandTelegram />,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getContext = () => {
 | 
				
			||||||
 | 
					        if (!isMine || message.file) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isSuccess) return contextMenuSuccessMsg();
 | 
				
			||||||
 | 
					        if (isError) return contextMenuErrorMsg();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getStatusIcon = () => {
 | 
				
			||||||
 | 
					        const size = em(18);
 | 
				
			||||||
 | 
					        if (message.status == MessageStatuses.ERROR) {
 | 
				
			||||||
 | 
					            return <IconAlertCircle size={size} color={"red"}/>;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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={getContext()}
 | 
				
			||||||
 | 
					                maw={em(600)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                {!isMine && (
 | 
				
			||||||
 | 
					                    <div>{message.tgSender!.lastName} {message.tgSender!.firstName}</div>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					                {message.file && (
 | 
				
			||||||
 | 
					                    <Group>
 | 
				
			||||||
 | 
					                        <ChatFile file={message.file} />
 | 
				
			||||||
 | 
					                    </Group>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					                <div>{message.text}</div>
 | 
				
			||||||
 | 
					                <Group
 | 
				
			||||||
 | 
					                    gap={em(5)}
 | 
				
			||||||
 | 
					                    justify={"flex-end"}
 | 
				
			||||||
 | 
					                    align={"center"}
 | 
				
			||||||
 | 
					                    wrap={"nowrap"}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    {message.isEdited && "ред."}
 | 
				
			||||||
 | 
					                    <Center>
 | 
				
			||||||
 | 
					                        {formatDateTime(message.createdAt).substring(11, 16)}
 | 
				
			||||||
 | 
					                    </Center>
 | 
				
			||||||
 | 
					                    {isMine && (
 | 
				
			||||||
 | 
					                        <Center>
 | 
				
			||||||
 | 
					                            {getStatusIcon()}
 | 
				
			||||||
 | 
					                        </Center>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                </Group>
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					        </Flex>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Message;
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/components/Chat/components/Message/hooks/useMessage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/components/Chat/components/Message/hooks/useMessage.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					import { ChatService, MessageSchema } from "../../../../../client";
 | 
				
			||||||
 | 
					import { notifications } from "../../../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { useChatContext } from "../../../../../pages/ClientsPage/contexts/ChatContext.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useMessage = () => {
 | 
				
			||||||
 | 
					    const { form } = useChatContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onDeleteMessageClick = (message: MessageSchema) => {
 | 
				
			||||||
 | 
					        ChatService.deleteMessage({
 | 
				
			||||||
 | 
					            messageId: message.id,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                if (!ok) {
 | 
				
			||||||
 | 
					                    notifications.error({ message });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onEditMessageClick = (message: MessageSchema) => {
 | 
				
			||||||
 | 
					        form.setValues({
 | 
				
			||||||
 | 
					            messageId: message.id,
 | 
				
			||||||
 | 
					            message: message.text,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onRepeatSendingClick = (message: MessageSchema) => {
 | 
				
			||||||
 | 
					        ChatService.repeatSendingTextMessage({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                message,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                if (!ok) {
 | 
				
			||||||
 | 
					                    notifications.error({ message });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        onDeleteMessageClick,
 | 
				
			||||||
 | 
					        onEditMessageClick,
 | 
				
			||||||
 | 
					        onRepeatSendingClick,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useMessage;
 | 
				
			||||||
							
								
								
									
										75
									
								
								src/components/Chat/components/MessageInput/MessageInput.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/components/Chat/components/MessageInput/MessageInput.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					import { ActionIcon, Button, Divider, Group, Stack, Textarea, Tooltip } from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconCheck, IconPaperclip, IconSend2, IconX } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { useChatContext } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
 | 
				
			||||||
 | 
					import ActionIconCopy from "../../../ActionIconCopy/ActionIconCopy.tsx";
 | 
				
			||||||
 | 
					import SelectedFile from "../SelectedFile/SelectedFile.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const MessageInput = () => {
 | 
				
			||||||
 | 
					    const {
 | 
				
			||||||
 | 
					        submitMessage,
 | 
				
			||||||
 | 
					        chat,
 | 
				
			||||||
 | 
					        form,
 | 
				
			||||||
 | 
					        files,
 | 
				
			||||||
 | 
					        fileDialog,
 | 
				
			||||||
 | 
					        isMessageSending,
 | 
				
			||||||
 | 
					    } = useChatContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const getFiles = files.map(file => (
 | 
				
			||||||
 | 
					        <SelectedFile key={file.name} file={file} />
 | 
				
			||||||
 | 
					    ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <form onSubmit={form.onSubmit(values => submitMessage(values))}>
 | 
				
			||||||
 | 
					            <Stack gap={"xs"}>
 | 
				
			||||||
 | 
					                <Divider />
 | 
				
			||||||
 | 
					                {getFiles}
 | 
				
			||||||
 | 
					                <Group wrap={"nowrap"} align={"flex-end"}>
 | 
				
			||||||
 | 
					                    {chat?.tgGroup?.tgInviteLink && (
 | 
				
			||||||
 | 
					                        <ActionIconCopy
 | 
				
			||||||
 | 
					                            onCopiedLabel={"Ссылка на чат скопирована в буфер обмена"}
 | 
				
			||||||
 | 
					                            value={chat.tgGroup.tgInviteLink}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                    <Tooltip label={"Прикрепить файлы"}>
 | 
				
			||||||
 | 
					                        <ActionIcon
 | 
				
			||||||
 | 
					                            variant={"default"}
 | 
				
			||||||
 | 
					                            onClick={fileDialog.open}
 | 
				
			||||||
 | 
					                            size="lg"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            <IconPaperclip />
 | 
				
			||||||
 | 
					                        </ActionIcon>
 | 
				
			||||||
 | 
					                    </Tooltip>
 | 
				
			||||||
 | 
					                    <Textarea
 | 
				
			||||||
 | 
					                        {...form.getInputProps("message")}
 | 
				
			||||||
 | 
					                        w={"100%"}
 | 
				
			||||||
 | 
					                        minRows={1}
 | 
				
			||||||
 | 
					                        maxRows={4}
 | 
				
			||||||
 | 
					                        autosize
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                    {form.values.messageId && (
 | 
				
			||||||
 | 
					                        <Button
 | 
				
			||||||
 | 
					                            variant={"default"}
 | 
				
			||||||
 | 
					                            onClick={form.reset}
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                            <IconX />
 | 
				
			||||||
 | 
					                        </Button>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                    <Button
 | 
				
			||||||
 | 
					                        variant={"default"}
 | 
				
			||||||
 | 
					                        type="submit"
 | 
				
			||||||
 | 
					                        disabled={isMessageSending}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        {form.values.messageId ? (
 | 
				
			||||||
 | 
					                            <IconCheck />
 | 
				
			||||||
 | 
					                        ) : (
 | 
				
			||||||
 | 
					                            <IconSend2 />
 | 
				
			||||||
 | 
					                        )}
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                </Group>
 | 
				
			||||||
 | 
					            </Stack>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default MessageInput;
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/components/Chat/components/SelectedFile/SelectedFile.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/components/Chat/components/SelectedFile/SelectedFile.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import { Box, Group, Text } from "@mantine/core";
 | 
				
			||||||
 | 
					import { IconX } from "@tabler/icons-react";
 | 
				
			||||||
 | 
					import { useChatContext } from "../../../../pages/ClientsPage/contexts/ChatContext.tsx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    file: File;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SelectedFile = ({ file }: Props) => {
 | 
				
			||||||
 | 
					    const { files, filesHandlers } = useChatContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onCancelFileClick = () => {
 | 
				
			||||||
 | 
					        const idx = files.findIndex(f => f.name === file.name && f.type === file.type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (idx < 0) return;
 | 
				
			||||||
 | 
					        filesHandlers.remove(idx);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <Box>
 | 
				
			||||||
 | 
					            <Group justify={"space-between"}>
 | 
				
			||||||
 | 
					                <Text>
 | 
				
			||||||
 | 
					                    {file.name}
 | 
				
			||||||
 | 
					                </Text>
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                    onClick={onCancelFileClick}
 | 
				
			||||||
 | 
					                    style={{
 | 
				
			||||||
 | 
					                        cursor: "pointer",
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <IconX />
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					            </Group>
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SelectedFile;
 | 
				
			||||||
@@ -3,13 +3,16 @@ import CreateServiceCategoryModal from "../pages/ServicesPage/modals/CreateServi
 | 
				
			|||||||
import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
 | 
					import CreateServiceModal from "../pages/ServicesPage/modals/CreateServiceModal.tsx";
 | 
				
			||||||
import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
 | 
					import createProductModal from "../pages/ProductsPage/modals/CreateProductModal/CreateProductModal.tsx";
 | 
				
			||||||
import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
 | 
					import ProductFormModal from "../pages/ClientsPage/modals/ClientFormModal/ClientFormModal.tsx";
 | 
				
			||||||
import AddCardServiceModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardServiceModal.tsx";
 | 
					import AddCardServiceModal
 | 
				
			||||||
import AddCardProductModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardProductModal.tsx";
 | 
					    from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardServiceModal.tsx";
 | 
				
			||||||
 | 
					import AddCardProductModal
 | 
				
			||||||
 | 
					    from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/AddCardProductModal.tsx";
 | 
				
			||||||
import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
 | 
					import PrintBarcodeModal from "./PrintBarcodeModal/PrintBarcodeModal.tsx";
 | 
				
			||||||
import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
 | 
					import AddBarcodeModal from "./AddBarcodeModal/AddBarcodeModal.tsx";
 | 
				
			||||||
import BarcodeTemplateFormModal
 | 
					import BarcodeTemplateFormModal
 | 
				
			||||||
    from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
 | 
					    from "../pages/BarcodePage/modals/BarcodeTemplateFormModal/BarcodeTemplateFormModal.tsx";
 | 
				
			||||||
import ProductServiceFormModal from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/ProductServiceFormModal.tsx";
 | 
					import ProductServiceFormModal
 | 
				
			||||||
 | 
					    from "../modules/cardModules/cardEditorTabs/ProductAndServiceTab/modals/ProductServiceFormModal.tsx";
 | 
				
			||||||
import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
 | 
					import UserFormModal from "../pages/AdminPage/modals/UserFormModal/UserFormModal.tsx";
 | 
				
			||||||
import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
 | 
					import EmployeeSelectModal from "./EmployeeSelectModal/EmployeeSelectModal.tsx";
 | 
				
			||||||
import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx";
 | 
					import EmployeeTableModal from "./EmployeeTableModal/EmployeeTableModal.tsx";
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								src/modules/cardModules/cardEditorTabs/ChatTab/ChatTab.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/modules/cardModules/cardEditorTabs/ChatTab/ChatTab.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					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, isRequestSending } = useChatTab();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!selectedCard?.chat) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <Stack>
 | 
				
			||||||
 | 
					                <InlineButton
 | 
				
			||||||
 | 
					                    onClick={onChatCreateClick}
 | 
				
			||||||
 | 
					                    disabled={isRequestSending}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <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,54 @@
 | 
				
			|||||||
 | 
					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";
 | 
				
			||||||
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useChatTab = () => {
 | 
				
			||||||
 | 
					    const { selectedCard, refetchCard } = useCardPageContext();
 | 
				
			||||||
 | 
					    const [isRequestSending, setIsRequestSending] = useState<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const createChat = () => {
 | 
				
			||||||
 | 
					        if (!selectedCard?.clientId) {
 | 
				
			||||||
 | 
					            notifications.error({ message: "Ошибка создания чата, не указан клиент" });
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setIsRequestSending(true);
 | 
				
			||||||
 | 
					        ChatService.createChat({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                clientId: selectedCard.clientId,
 | 
				
			||||||
 | 
					                cardId: selectedCard.id,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                notifications.guess(ok, { message });
 | 
				
			||||||
 | 
					                refetchCard();
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsRequestSending(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onChatCreateClick = () => {
 | 
				
			||||||
 | 
					        modals.openConfirmModal({
 | 
				
			||||||
 | 
					            title: "Создание чата",
 | 
				
			||||||
 | 
					            children: (
 | 
				
			||||||
 | 
					                <Text size="sm">
 | 
				
			||||||
 | 
					                    Вы уверены что хотите создать чат для сделки {selectedCard?.name}?
 | 
				
			||||||
 | 
					                </Text>
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            labels: { confirm: "Да", cancel: "Нет" },
 | 
				
			||||||
 | 
					            confirmProps: { color: "green" },
 | 
				
			||||||
 | 
					            onConfirm: createChat,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        onChatCreateClick,
 | 
				
			||||||
 | 
					        isRequestSending,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default useChatTab;
 | 
				
			||||||
@@ -146,8 +146,9 @@ const ClientTab = () => {
 | 
				
			|||||||
                        value={client}
 | 
					                        value={client}
 | 
				
			||||||
                        onChange={setClient}
 | 
					                        onChange={setClient}
 | 
				
			||||||
                        withLabel
 | 
					                        withLabel
 | 
				
			||||||
                        disabled={!isEqual(initialValues, form.values)}
 | 
					                        disabled={!isEqual(initialValues, form.values) || !!card?.chat}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
 | 
					                    {!card?.chat && (
 | 
				
			||||||
                        <Group>
 | 
					                        <Group>
 | 
				
			||||||
                            <InlineButton
 | 
					                            <InlineButton
 | 
				
			||||||
                                onClick={handleSelectClient}
 | 
					                                onClick={handleSelectClient}
 | 
				
			||||||
@@ -156,6 +157,7 @@ const ClientTab = () => {
 | 
				
			|||||||
                                Сохранить
 | 
					                                Сохранить
 | 
				
			||||||
                            </InlineButton>
 | 
					                            </InlineButton>
 | 
				
			||||||
                        </Group>
 | 
					                        </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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,7 @@ const generateRows = (modules: Module[]) => {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        const tsxContent = template(data);
 | 
					        const tsxContent = template(data);
 | 
				
			||||||
        fs.writeFileSync(OUTPUT_PATH, tsxContent);
 | 
					        fs.writeFileSync(OUTPUT_PATH, tsxContent);
 | 
				
			||||||
        console.log("File successfully generated.");
 | 
					        console.log("ChatFile successfully generated.");
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
        console.error(error);
 | 
					        console.error(error);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>
 | 
				
			||||||
 | 
					                <ChatContextProvider>
 | 
				
			||||||
                    <ClientsTable
 | 
					                    <ClientsTable
 | 
				
			||||||
                        onChange={onChange}
 | 
					                        onChange={onChange}
 | 
				
			||||||
                        onDelete={onDelete}
 | 
					                        onDelete={onDelete}
 | 
				
			||||||
                        items={clients}
 | 
					                        items={clients}
 | 
				
			||||||
 | 
					                        refetch={refetch}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
 | 
					                    <ClientChatDrawer />
 | 
				
			||||||
 | 
					                </ChatContextProvider>
 | 
				
			||||||
            </PageBlock>
 | 
					            </PageBlock>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					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";
 | 
				
			||||||
 | 
					import { useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					    client: ClientSchema;
 | 
				
			||||||
 | 
					    refetch: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ClientChatButton = ({ client, refetch }: Props) => {
 | 
				
			||||||
 | 
					    const { setChat } = useChatContext();
 | 
				
			||||||
 | 
					    const [isRequestSending, setIsRequestSending] = useState<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const createChat = () => {
 | 
				
			||||||
 | 
					        setIsRequestSending(true);
 | 
				
			||||||
 | 
					        ChatService.createChat({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                clientId: client.id,
 | 
				
			||||||
 | 
					                cardId: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                notifications.guess(ok, { message });
 | 
				
			||||||
 | 
					                refetch();
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsRequestSending(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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"}
 | 
				
			||||||
 | 
					                disabled={isRequestSending}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <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 = {
 | 
				
			||||||
 | 
					    refetch: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ClientsTable: FC<CRUDTableProps<ClientSchema> & RefetchProps> = ({
 | 
				
			||||||
                                                                           items,
 | 
					                                                                           items,
 | 
				
			||||||
                                                                           onDelete,
 | 
					                                                                           onDelete,
 | 
				
			||||||
                                                                           onChange,
 | 
					                                                                           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 />
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										234
									
								
								src/pages/ClientsPage/contexts/ChatContext.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/pages/ClientsPage/contexts/ChatContext.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,234 @@
 | 
				
			|||||||
 | 
					import React, { createContext, FC, useCallback, useContext, useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import { ChatSchema, ChatService, MessageSchema } from "../../../client";
 | 
				
			||||||
 | 
					import { notifications } from "../../../shared/lib/notifications.ts";
 | 
				
			||||||
 | 
					import { useForm, UseFormReturnType } from "@mantine/form";
 | 
				
			||||||
 | 
					import { useFileDialog, useListState, UseListStateHandlers } from "@mantine/hooks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type MessageForm = {
 | 
				
			||||||
 | 
					    message: string;
 | 
				
			||||||
 | 
					    messageId?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FileDialog = {
 | 
				
			||||||
 | 
					    files: FileList | null;
 | 
				
			||||||
 | 
					    open: () => void;
 | 
				
			||||||
 | 
					    reset: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ChatContextState = {
 | 
				
			||||||
 | 
					    chat: ChatSchema | null;
 | 
				
			||||||
 | 
					    setChat: (chat: ChatSchema | null) => void;
 | 
				
			||||||
 | 
					    messages: MessageSchema[];
 | 
				
			||||||
 | 
					    lastMessage: MessageSchema | null;
 | 
				
			||||||
 | 
					    firstItemIndex: number;
 | 
				
			||||||
 | 
					    form: UseFormReturnType<MessageForm>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submitMessage: (values: MessageForm) => void;
 | 
				
			||||||
 | 
					    fetchMoreMessages: () => void;
 | 
				
			||||||
 | 
					    files: Array<File>;
 | 
				
			||||||
 | 
					    filesHandlers: UseListStateHandlers<File>;
 | 
				
			||||||
 | 
					    fileDialog: FileDialog;
 | 
				
			||||||
 | 
					    isMessageSending: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChatContext = createContext<ChatContextState | undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useChatContextState = () => {
 | 
				
			||||||
 | 
					    const [chat, setChat] = useState<ChatSchema | null>(null);
 | 
				
			||||||
 | 
					    const [messages, setMessages] = useState<MessageSchema[]>([]);
 | 
				
			||||||
 | 
					    const lastMessage = messages?.length ? messages[messages?.length - 1] : null;
 | 
				
			||||||
 | 
					    const [firstItemIndex, setFirstItemIndex] = useState(10000);
 | 
				
			||||||
 | 
					    const limit: number = 30;
 | 
				
			||||||
 | 
					    let offset: number = 0;
 | 
				
			||||||
 | 
					    const [hasMore, setHasMore] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [files, filesHandlers] = useListState<File>([]);
 | 
				
			||||||
 | 
					    const fileDialog = useFileDialog();
 | 
				
			||||||
 | 
					    const [isMessageSending, setIsMessageSending] = useState<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        filesHandlers.append(...Array.from(fileDialog.files ?? []));
 | 
				
			||||||
 | 
					    }, [fileDialog.files]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const form = useForm<MessageForm>({
 | 
				
			||||||
 | 
					        initialValues: {
 | 
				
			||||||
 | 
					            message: "",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const setChatValue = (chat: ChatSchema | null) => {
 | 
				
			||||||
 | 
					        if (chat) {
 | 
				
			||||||
 | 
					            setFirstItemIndex(10000);
 | 
				
			||||||
 | 
					            offset = 0;
 | 
				
			||||||
 | 
					            setHasMore(true);
 | 
				
			||||||
 | 
					            filesHandlers.setState([]);
 | 
				
			||||||
 | 
					            form.reset();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        setChat(chat);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fetchMoreMessages = useCallback(() => {
 | 
				
			||||||
 | 
					        return setTimeout(() => {
 | 
				
			||||||
 | 
					            if (!chat || !hasMore) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            offset += limit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ChatService.getMessages({
 | 
				
			||||||
 | 
					                requestBody: {
 | 
				
			||||||
 | 
					                    chatId: chat.id,
 | 
				
			||||||
 | 
					                    offset,
 | 
				
			||||||
 | 
					                    limit,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					                .then(({ messages: newMessages }) => {
 | 
				
			||||||
 | 
					                    setFirstItemIndex(prev => prev - newMessages.length);
 | 
				
			||||||
 | 
					                    setMessages(prev => [...newMessages.reverse(), ...prev]);
 | 
				
			||||||
 | 
					                    setHasMore(newMessages.length === limit);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .catch(err => console.log(err));
 | 
				
			||||||
 | 
					        }, 500);
 | 
				
			||||||
 | 
					    }, [setMessages, chat, offset, hasMore, setHasMore]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fetchMessages = () => {
 | 
				
			||||||
 | 
					        if (!chat) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ChatService.getMessages({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                chatId: chat.id,
 | 
				
			||||||
 | 
					                offset: 0,
 | 
				
			||||||
 | 
					                limit: limit + offset,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ messages: newMessages }) => {
 | 
				
			||||||
 | 
					                setMessages(newMessages.reverse());
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        fetchMessages();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const interval = setInterval(fetchMessages, 2000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return () => clearInterval(interval);
 | 
				
			||||||
 | 
					    }, [chat]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const sendMessageWithFiles = (values: MessageForm) => {
 | 
				
			||||||
 | 
					        if (!chat) return;
 | 
				
			||||||
 | 
					        setIsMessageSending(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ChatService.sendMessagesWithFiles({
 | 
				
			||||||
 | 
					            formData: {
 | 
				
			||||||
 | 
					                files,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            chatId: chat.id,
 | 
				
			||||||
 | 
					            caption: values.message,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                if (!ok) {
 | 
				
			||||||
 | 
					                    notifications.error({ message });
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                filesHandlers.setState([]);
 | 
				
			||||||
 | 
					                form.reset();
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsMessageSending(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const sendTextMessage = (values: MessageForm) => {
 | 
				
			||||||
 | 
					        if (!chat) return;
 | 
				
			||||||
 | 
					        setIsMessageSending(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ChatService.sendTextMessage({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                message: {
 | 
				
			||||||
 | 
					                    text: values.message,
 | 
				
			||||||
 | 
					                    chatId: chat.id,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                if (!ok) {
 | 
				
			||||||
 | 
					                    notifications.error({ message });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                form.reset();
 | 
				
			||||||
 | 
					                setIsMessageSending(false);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsMessageSending(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const editMessage = (values: MessageForm) => {
 | 
				
			||||||
 | 
					        if (!chat) return;
 | 
				
			||||||
 | 
					        setIsMessageSending(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ChatService.editMessage({
 | 
				
			||||||
 | 
					            requestBody: {
 | 
				
			||||||
 | 
					                message: {
 | 
				
			||||||
 | 
					                    id: values.messageId!,
 | 
				
			||||||
 | 
					                    text: values.message,
 | 
				
			||||||
 | 
					                    chatId: chat.id,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					            .then(({ ok, message }) => {
 | 
				
			||||||
 | 
					                if (!ok) {
 | 
				
			||||||
 | 
					                    notifications.error({ message });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                form.reset();
 | 
				
			||||||
 | 
					                setIsMessageSending(false);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(err => console.log(err))
 | 
				
			||||||
 | 
					            .finally(() => setIsMessageSending(false));
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const submitMessage = (values: MessageForm) => {
 | 
				
			||||||
 | 
					        if (values.messageId) {
 | 
				
			||||||
 | 
					            editMessage(values);
 | 
				
			||||||
 | 
					        } else if (files.length === 0) {
 | 
				
			||||||
 | 
					            sendTextMessage(values);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            sendMessageWithFiles(values);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        chat,
 | 
				
			||||||
 | 
					        setChat: setChatValue,
 | 
				
			||||||
 | 
					        messages,
 | 
				
			||||||
 | 
					        lastMessage,
 | 
				
			||||||
 | 
					        firstItemIndex,
 | 
				
			||||||
 | 
					        form,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        submitMessage,
 | 
				
			||||||
 | 
					        fetchMoreMessages,
 | 
				
			||||||
 | 
					        files,
 | 
				
			||||||
 | 
					        filesHandlers,
 | 
				
			||||||
 | 
					        fileDialog,
 | 
				
			||||||
 | 
					        isMessageSending,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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