- Replaced `LIST_PUBLIC_CATEGORIES` with `LIST_ADMIN_CATEGORIES_FLAT` in `ArticleFormModal.tsx`. - Removed the "Add Event" button from the dashboard page. - Simplified the `EventsPage` by removing the button and adjusting the layout. - Cleaned up the `AdminEventEditPage` by removing unnecessary props. - Deleted unused layout and page files related to event creation. - Updated `AdminAuthContent` to use `useAdminCitiesQuery` instead of `useDiscoveryCitiesQuery`. - Refactored `EventGuestListAccessPanel` to remove the `accessMode` prop and adjust API calls accordingly. - Removed several unused components and tests related to event creation, enhancing project maintainability.
215 lines
7.9 KiB
TypeScript
215 lines
7.9 KiB
TypeScript
import type {
|
|
CreateEventDto,
|
|
CreateEventFaqDto,
|
|
CreateEventMediaDto,
|
|
EventCategoryResponseDto,
|
|
EventResponseDto,
|
|
} from '@/api/generated/models'
|
|
import { getAdminEventCategories } from '@/api/generated/admin-event-categories/admin-event-categories'
|
|
import { getAdminEvents } from '@/api/generated/admin-events/admin-events'
|
|
import { getEventExtras } from '@/api/generated/event-extras/event-extras'
|
|
import { unwrapApiPayload } from '@/helpers/listResponse'
|
|
import axiosInstance from '@/config/axios'
|
|
import { API_ROUTES } from '@/services/config'
|
|
|
|
export type EventCategory = EventCategoryResponseDto
|
|
export type CreateEventPayload = CreateEventDto
|
|
export type CreatedEvent = EventResponseDto
|
|
export type CreateEventMediaPayload = CreateEventMediaDto
|
|
export type CreateEventFaqPayload = CreateEventFaqDto
|
|
|
|
const adminEventsApi = getAdminEvents()
|
|
const adminEventCategoriesApi = getAdminEventCategories()
|
|
const eventExtrasApi = getEventExtras()
|
|
|
|
/** Full category tree for admin edit forms — admin list, not public discovery. */
|
|
export async function fetchEventCategories(): Promise<EventCategory[]> {
|
|
const items: EventCategory[] = []
|
|
let page = 1
|
|
let totalPages = 1
|
|
|
|
while (page <= totalPages) {
|
|
const response = await adminEventCategoriesApi.adminEventCategoriesControllerList({
|
|
page,
|
|
pageSize: 100,
|
|
sort: 'sortOrder',
|
|
})
|
|
const payload = unwrapApiPayload<{ items?: EventCategory[]; response?: { totalPages?: number } }>(response.data)
|
|
const batch = Array.isArray(payload.items) ? payload.items : Array.isArray(payload) ? (payload as EventCategory[]) : []
|
|
|
|
items.push(...batch)
|
|
totalPages = Number(payload.response?.totalPages ?? 1)
|
|
page += 1
|
|
}
|
|
|
|
return items
|
|
}
|
|
|
|
/** Admin approval is one half of the two-key publication workflow. */
|
|
export async function approveEventAsAdmin(eventId: string): Promise<CreatedEvent> {
|
|
const response = await adminEventsApi.adminEventsControllerApprove(eventId)
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
/** Force-publish bypass: skips the request/approve flow entirely. */
|
|
export async function publishEventAsAdmin(eventId: string): Promise<CreatedEvent> {
|
|
const response = await adminEventsApi.adminEventsControllerPublish(eventId)
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
export async function rejectEventAsAdmin(eventId: string, rejectionReason: string): Promise<CreatedEvent> {
|
|
const response = await adminEventsApi.adminEventsControllerReject(eventId, { rejectionReason })
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
export async function cancelEventAsAdmin(eventId: string): Promise<CreatedEvent> {
|
|
const response = await adminEventsApi.adminEventsControllerCancel(eventId)
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
export async function completeEventAsAdmin(eventId: string): Promise<CreatedEvent> {
|
|
const response = await adminEventsApi.adminEventsControllerComplete(eventId)
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
export async function deleteEventAsAdmin(eventId: string): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.delete(API_ROUTES.EVENTS.ADMIN_DELETE(eventId))
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
export async function updateEventReservedCapacity(eventId: string, reservedCapacity: number): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.patch(API_ROUTES.EVENTS.ADMIN_RESERVED_CAPACITY(eventId), { reservedCapacity })
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
/** Admin can edit any organizer's event (same field locks as host). */
|
|
export async function updateEventAsAdmin(eventId: string, payload: Partial<CreateEventPayload>): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.patch(API_ROUTES.EVENTS.ADMIN_DETAIL(eventId), payload)
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
/** Admin-only: set (or, with null, clear) this event's platform commission override. */
|
|
export async function updateEventCommissionAsAdmin(eventId: string, commissionPercent: number | null): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.patch(API_ROUTES.EVENTS.ADMIN_COMMISSION(eventId), { commissionPercent })
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
/**
|
|
* Full event for edit forms. Backend authorizes admin (and owner);
|
|
* admin panel uses this for the edit screen.
|
|
*/
|
|
export async function fetchEventForEdit(eventId: string): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.get(API_ROUTES.EVENTS.FOR_EDIT(eventId))
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
/** Admin detail including bookmarkCount + organizer contact. */
|
|
export async function fetchAdminEventDetail(eventId: string): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.get(API_ROUTES.EVENTS.ADMIN_DETAIL(eventId))
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
/**
|
|
* Media/FAQ mutations share `/events/:id/...` extras endpoints; backend
|
|
* authorizes event owner or admin. No separate admin CRUD routes exist.
|
|
*/
|
|
export async function createEventMedia(eventId: string, payload: CreateEventMediaPayload): Promise<void> {
|
|
await eventExtrasApi.eventMediaControllerCreate(eventId, payload)
|
|
}
|
|
|
|
export async function updateEventMedia(eventId: string, mediaId: string, payload: Partial<CreateEventMediaPayload>): Promise<void> {
|
|
await eventExtrasApi.eventMediaControllerUpdate(eventId, mediaId, payload)
|
|
}
|
|
|
|
export async function deleteEventMedia(eventId: string, mediaId: string): Promise<void> {
|
|
await eventExtrasApi.eventMediaControllerRemove(eventId, mediaId)
|
|
}
|
|
|
|
export async function createEventFaq(eventId: string, payload: CreateEventFaqPayload): Promise<void> {
|
|
await eventExtrasApi.eventFaqsControllerCreate(eventId, payload)
|
|
}
|
|
|
|
export async function updateEventFaq(eventId: string, faqId: string, payload: Partial<CreateEventFaqPayload>): Promise<void> {
|
|
await eventExtrasApi.eventFaqsControllerUpdate(eventId, faqId, payload)
|
|
}
|
|
|
|
export async function deleteEventFaq(eventId: string, faqId: string): Promise<void> {
|
|
await eventExtrasApi.eventFaqsControllerRemove(eventId, faqId)
|
|
}
|
|
|
|
export interface EventRevisionPayload {
|
|
title?: string
|
|
slug?: string
|
|
shortDescription?: string
|
|
description?: string
|
|
categoryId?: number
|
|
startsAt?: string
|
|
endsAt?: string
|
|
provinceId?: number
|
|
cityId?: number
|
|
address?: string
|
|
lat?: number
|
|
lng?: number
|
|
isFree?: boolean
|
|
price?: number
|
|
capacity?: number
|
|
genderRestriction?: string
|
|
ageRestriction?: string
|
|
cancellationFeePercent?: number
|
|
cancellationFeePercent12To24Hours?: number
|
|
cancellationFeePercentMoreThan24Hours?: number
|
|
settings?: CreateEventPayload['settings']
|
|
media: CreateEventMediaPayload[]
|
|
faqs?: CreateEventFaqPayload[]
|
|
}
|
|
|
|
export interface EventRevision {
|
|
id: string
|
|
eventId: string
|
|
status: string
|
|
payload: EventRevisionPayload & Record<string, unknown>
|
|
createdBy: string
|
|
reviewedBy: string | null
|
|
rejectionReason: string | null
|
|
submittedAt: string
|
|
decidedAt: string | null
|
|
}
|
|
|
|
export async function fetchAdminPendingEventRevision(eventId: string): Promise<EventRevision | null> {
|
|
try {
|
|
const response = await axiosInstance.get(API_ROUTES.EVENTS.ADMIN_REVISION_PENDING(eventId))
|
|
|
|
return unwrapApiPayload<EventRevision>(response.data)
|
|
} catch (error) {
|
|
const status = (error as { response?: { status?: number } })?.response?.status
|
|
|
|
if (status === 404) return null
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export async function approveEventRevisionAsAdmin(eventId: string, revisionId: string): Promise<CreatedEvent> {
|
|
const response = await axiosInstance.patch(API_ROUTES.EVENTS.ADMIN_REVISION_APPROVE(eventId, revisionId))
|
|
|
|
return unwrapApiPayload<CreatedEvent>(response.data)
|
|
}
|
|
|
|
export async function rejectEventRevisionAsAdmin(eventId: string, revisionId: string, rejectionReason: string): Promise<EventRevision> {
|
|
const response = await axiosInstance.patch(API_ROUTES.EVENTS.ADMIN_REVISION_REJECT(eventId, revisionId), {
|
|
rejectionReason,
|
|
})
|
|
|
|
return unwrapApiPayload<EventRevision>(response.data)
|
|
}
|