Implement support ticket system with role request and bug report functionalities
- Introduced a new support ticket system allowing users to submit bug reports and role requests. - Implemented endpoints for creating, updating, and managing support tickets, including file attachments. - Enhanced Telegram bot integration to handle role requests directly within the bot, enabling admins to approve or reject requests without accessing the website. - Updated database schema to include support ticket entities and their relationships. - Improved API documentation to reflect new support ticket endpoints and their usage. - Added necessary localization for support ticket features in both Russian and English.
This commit is contained in:
@@ -91,3 +91,36 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
|
||||
const text = await response.text()
|
||||
return (text ? JSON.parse(text) : undefined) as T
|
||||
}
|
||||
|
||||
type UploadOptions = {
|
||||
method?: 'POST' | 'PUT'
|
||||
skipRefresh?: boolean
|
||||
}
|
||||
|
||||
/** Как apiRequest, но для multipart/form-data (вложения к тикетам) — без JSON.stringify и
|
||||
* без Content-Type (браузер сам проставляет boundary). */
|
||||
export async function apiUpload<T>(path: string, formData: FormData, options: UploadOptions = {}): Promise<T> {
|
||||
const headers: Record<string, string> = {}
|
||||
if (accessToken) headers.Authorization = `Bearer ${accessToken}`
|
||||
|
||||
const response = await fetch(`/api${path}`, {
|
||||
method: options.method ?? 'POST',
|
||||
headers,
|
||||
credentials: 'include',
|
||||
body: formData,
|
||||
})
|
||||
|
||||
if (response.status === 401 && !options.skipRefresh) {
|
||||
const refreshed = await refreshAccessToken()
|
||||
if (refreshed) return apiUpload<T>(path, formData, { ...options, skipRefresh: true })
|
||||
onUnauthorized?.()
|
||||
throw await parseError(response)
|
||||
}
|
||||
|
||||
if (!response.ok) throw await parseError(response)
|
||||
|
||||
if (response.status === 204) return undefined as T
|
||||
|
||||
const text = await response.text()
|
||||
return (text ? JSON.parse(text) : undefined) as T
|
||||
}
|
||||
|
||||
@@ -238,3 +238,48 @@ export type AuditLogDto = {
|
||||
source: AuditSource
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type TicketType = 'BugReport' | 'RoleRequest'
|
||||
export type TicketStatus = 'Open' | 'Resolved' | 'Closed'
|
||||
|
||||
export type TicketAttachmentDto = {
|
||||
id: string
|
||||
fileName: string
|
||||
contentType: string
|
||||
sizeBytes: number
|
||||
}
|
||||
|
||||
export type TicketCommentDto = {
|
||||
id: string
|
||||
authorId: string
|
||||
authorName: string
|
||||
body: string
|
||||
createdAt: string
|
||||
attachments: TicketAttachmentDto[]
|
||||
}
|
||||
|
||||
/** Строка списка тикетов — один DTO для своего списка и админского (видит только свои userId/userName). */
|
||||
export type TicketSummaryDto = {
|
||||
id: string
|
||||
userId: string
|
||||
userName: string
|
||||
type: TicketType
|
||||
status: TicketStatus
|
||||
createdAt: string
|
||||
lastActivityAt: string
|
||||
}
|
||||
|
||||
export type TicketDetailDto = {
|
||||
id: string
|
||||
userId: string
|
||||
userName: string
|
||||
type: TicketType
|
||||
status: TicketStatus
|
||||
requestedRoleId: string | null
|
||||
requestedRoleName: string | null
|
||||
proposedRoleName: string | null
|
||||
proposedMaxConfigs: number | null
|
||||
proposedMaxIpLimit: number | null
|
||||
createdAt: string
|
||||
comments: TicketCommentDto[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user