Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
import type { CreateUserReportDto, ReportReasonResponseDto, UserReportResponseDto } from '@/api/generated/models'
|
|
import {
|
|
createServiceError,
|
|
errorResult,
|
|
handleServiceError,
|
|
type ServiceCallOptions,
|
|
type ServiceResult,
|
|
shouldBubbleErrorToParent,
|
|
successResult,
|
|
} from '@/services/errorHandler'
|
|
import { unwrapApiData } from '@/services/apiResponse'
|
|
import { getUserReports } from '@/api/generated/user-reports/user-reports'
|
|
import { texts } from '@/texts'
|
|
|
|
// docs/workflows/fa/user-blocking-reporting.md, Flow B — purely
|
|
// informational: creating a report never blocks, hides, or notifies
|
|
// anyone by itself. It only creates a `pending` row an admin reviews later.
|
|
|
|
export type ReportReason = ReportReasonResponseDto
|
|
|
|
export type UserReport = UserReportResponseDto
|
|
|
|
const userReportsApi = getUserReports()
|
|
|
|
/**
|
|
* Public, unauthenticated — safe to fetch ahead of time (e.g. when the
|
|
* report menu item is rendered) so the reason list is ready by the time
|
|
* the user opens the report form.
|
|
*/
|
|
export const LIST_REPORT_REASONS = async (options?: ServiceCallOptions): Promise<ServiceResult<ReportReason[]>> => {
|
|
try {
|
|
const res = await userReportsApi.userReportsControllerListReportReasons()
|
|
const payload = res.data
|
|
|
|
if (!payload.success) {
|
|
throw createServiceError(payload.message || texts.chats.listReportReasonsFailed)
|
|
}
|
|
|
|
return successResult(unwrapApiData(payload) ?? [])
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* At least one of `reasonId` / `description` is required — mirrors the
|
|
* backend's `CreateUserReportDto` cross-field rule
|
|
* (`user_reports_content_chk`).
|
|
*/
|
|
export const REPORT_USER = async (
|
|
reportedId: string,
|
|
payload: { reasonId?: number; description?: string },
|
|
options?: ServiceCallOptions
|
|
): Promise<ServiceResult<UserReport>> => {
|
|
try {
|
|
const res = await userReportsApi.userReportsControllerCreate({
|
|
reportedId,
|
|
...payload,
|
|
})
|
|
|
|
const responsePayload = res.data
|
|
|
|
if (!responsePayload.success) {
|
|
throw createServiceError(responsePayload.message || texts.chats.submitReportFailed, responsePayload.code)
|
|
}
|
|
|
|
const report = unwrapApiData(responsePayload)
|
|
|
|
if (!report?.id) {
|
|
throw createServiceError(texts.common.invalidServerResponse)
|
|
}
|
|
|
|
return successResult(report)
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reports one specific chat message. The server derives and persists the
|
|
* conversation, sender, and immutable message snapshot; the client cannot
|
|
* forge those moderation fields.
|
|
*/
|
|
export const REPORT_MESSAGE = async (
|
|
reportedId: string,
|
|
messageId: string,
|
|
payload: { reasonId?: number; description?: string },
|
|
options?: ServiceCallOptions
|
|
): Promise<ServiceResult<UserReport>> => {
|
|
try {
|
|
const request: CreateUserReportDto = {
|
|
reportedId,
|
|
messageId,
|
|
...payload,
|
|
}
|
|
const res = await userReportsApi.userReportsControllerCreate(request)
|
|
const responsePayload = res.data
|
|
|
|
if (!responsePayload.success) {
|
|
throw createServiceError(responsePayload.message || texts.chats.submitReportFailed, responsePayload.code)
|
|
}
|
|
|
|
const report = unwrapApiData(responsePayload)
|
|
|
|
if (!report?.id) {
|
|
throw createServiceError(texts.common.invalidServerResponse)
|
|
}
|
|
|
|
return successResult(report)
|
|
} catch (error) {
|
|
const normalizedError = handleServiceError(error, options)
|
|
|
|
if (shouldBubbleErrorToParent(options)) {
|
|
throw normalizedError
|
|
}
|
|
|
|
return errorResult(normalizedError)
|
|
}
|
|
}
|