feat: multi-media attachments, linkify URLs, shared MessageMediaGrid

- Add media_items array support to ticket messages (types, API clients)
- Create shared MessageMediaGrid component with photo grid, fullscreen
  viewer, keyboard nav, video/document rendering
- Replace per-page AdminMessageMedia/MessageMedia with MessageMediaGrid
- Add linkifyText util (DOMPurify-sanitized) for auto-linking URLs
- Support multi-file upload (up to 10) in AdminTickets and Support pages
- Remove unused ticketsApi import from AdminUserDetail
This commit is contained in:
Fringg
2026-04-29 08:45:15 +03:00
parent 9b1e26d4ec
commit 6d3010b621
8 changed files with 666 additions and 553 deletions

View File

@@ -8,6 +8,12 @@ export interface AdminTicketUser {
last_name: string | null;
}
export interface AdminTicketMediaItem {
type: 'photo' | 'video' | 'document';
file_id: string;
caption?: string | null;
}
export interface AdminTicketMessage {
id: number;
message_text: string;
@@ -16,6 +22,7 @@ export interface AdminTicketMessage {
media_type: string | null;
media_file_id: string | null;
media_caption: string | null;
media_items?: AdminTicketMediaItem[] | null;
created_at: string;
}
@@ -118,7 +125,12 @@ export const adminApi = {
replyToTicket: async (
ticketId: number,
message: string,
media?: { media_type?: string; media_file_id?: string; media_caption?: string },
media?: {
media_type?: string;
media_file_id?: string;
media_caption?: string;
media_items?: AdminTicketMediaItem[];
},
): Promise<AdminTicketMessage> => {
const response = await apiClient.post(`/cabinet/admin/tickets/${ticketId}/reply`, {
message,

View File

@@ -1,5 +1,11 @@
import apiClient from './client';
import type { Ticket, TicketDetail, TicketMessage, PaginatedResponse } from '../types';
import type {
Ticket,
TicketDetail,
TicketMessage,
TicketMediaItem,
PaginatedResponse,
} from '../types';
// Media upload response type
interface MediaUploadResponse {
@@ -14,6 +20,7 @@ interface MediaParams {
media_type?: string;
media_file_id?: string;
media_caption?: string;
media_items?: TicketMediaItem[];
}
export const ticketsApi = {