Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
146 lines
5.5 KiB
TypeScript
146 lines
5.5 KiB
TypeScript
import type { EventWizardFormData } from '@/components/events/create/consumer/types'
|
|
import { createClientId } from '@/lib/createClientId'
|
|
import type { EventAgeRestriction, EventGenderRestriction } from '@/lib/eventAudience'
|
|
import type { CreatedEvent, EventRevisionPayload } from '@/services/events'
|
|
import type { EventFaq, EventMedia } from '@/services/eventDetail'
|
|
|
|
function splitIsoToDateAndMinutes(iso: string): { date: string; minutes: number } {
|
|
const d = new Date(iso)
|
|
|
|
return {
|
|
date: d.toISOString(),
|
|
minutes: d.getHours() * 60 + d.getMinutes(),
|
|
}
|
|
}
|
|
|
|
function mapMediaToStaged(media: EventMedia[]): EventWizardFormData['media'] {
|
|
return media
|
|
.filter((item) => item.mediaType === 'image')
|
|
.map((item, index) => ({
|
|
// نگه داشتن id سرور برای sync create/update/delete در ویرایش non-live
|
|
id: item.id,
|
|
url: item.url,
|
|
isPoster: item.isPoster,
|
|
isSquarePoster: Boolean(item.isSquarePoster),
|
|
sortOrder: item.sortOrder ?? index,
|
|
}))
|
|
}
|
|
|
|
function mapFaqsToStaged(faqs: EventFaq[]): EventWizardFormData['faqs'] {
|
|
return faqs.map((faq) => ({
|
|
id: faq.id,
|
|
question: faq.question,
|
|
answer: faq.answer,
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* Maps an existing event (plus media/FAQs) into edit-wizard form values.
|
|
* Unlike clone, keeps the real title (no clone suffix).
|
|
*/
|
|
export function mapEventToEditWizardData(event: CreatedEvent, media: EventMedia[], faqs: EventFaq[]): EventWizardFormData {
|
|
const start = splitIsoToDateAndMinutes(String(event.startsAt))
|
|
const end = splitIsoToDateAndMinutes(String(event.endsAt))
|
|
|
|
return {
|
|
title: event.title,
|
|
slug: event.slug,
|
|
categoryId: String(event.categoryId),
|
|
shortDescription: event.shortDescription ?? '',
|
|
description: event.description ?? '',
|
|
media: mapMediaToStaged(media),
|
|
startDate: start.date,
|
|
startTime: start.minutes,
|
|
endDate: end.date,
|
|
endTime: end.minutes,
|
|
provinceId: String(event.provinceId),
|
|
cityId: String(event.cityId),
|
|
address: event.address ?? '',
|
|
lat: event.lat ?? 35.6892,
|
|
lng: event.lng ?? 51.389,
|
|
isFree: event.isFree,
|
|
price: event.price,
|
|
capacity: event.capacity,
|
|
reservedCapacity: event.reservedCapacity ?? 0,
|
|
genderRestriction: event.genderRestriction ?? 'open',
|
|
ageRestriction: event.ageRestriction ?? 'open',
|
|
cancellationFeePercent: event.cancellationFeePercent,
|
|
isDiscoverable: event.settings.isDiscoverable,
|
|
autoCreateGroup: event.settings.autoCreateGroup,
|
|
addressVisibility: event.settings.addressVisibility,
|
|
generalArea: event.settings.generalArea ?? '',
|
|
waitlistAutoOffer: event.settings.waitlistAutoOffer,
|
|
sendReviewRequestSms: event.settings.sendReviewRequestSms ?? true,
|
|
faqs: mapFaqsToStaged(faqs),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prefer a pending revision snapshot when hydrating the edit wizard.
|
|
* Falls back to live media/FAQs when the revision omits them.
|
|
*/
|
|
export function mapRevisionPayloadToEditWizardData(
|
|
event: CreatedEvent,
|
|
payload: EventRevisionPayload,
|
|
fallbackMedia: EventMedia[],
|
|
fallbackFaqs: EventFaq[]
|
|
): EventWizardFormData {
|
|
const base = mapEventToEditWizardData(event, fallbackMedia, fallbackFaqs)
|
|
const startsAt = payload.startsAt ? String(payload.startsAt) : String(event.startsAt)
|
|
const endsAt = payload.endsAt ? String(payload.endsAt) : String(event.endsAt)
|
|
const start = splitIsoToDateAndMinutes(startsAt)
|
|
const end = splitIsoToDateAndMinutes(endsAt)
|
|
|
|
const mediaFromPayload = Array.isArray(payload.media)
|
|
? payload.media.map((item, index) => ({
|
|
id: createClientId(),
|
|
url: item.url,
|
|
isPoster: Boolean(item.isPoster),
|
|
isSquarePoster: Boolean(item.isSquarePoster),
|
|
sortOrder: item.sortOrder ?? index,
|
|
}))
|
|
: base.media
|
|
|
|
const faqsFromPayload = Array.isArray(payload.faqs)
|
|
? payload.faqs.map((faq) => ({
|
|
id: createClientId(),
|
|
question: faq.question,
|
|
answer: faq.answer,
|
|
}))
|
|
: base.faqs
|
|
|
|
const settings = payload.settings ?? {}
|
|
|
|
return {
|
|
...base,
|
|
title: payload.title ?? base.title,
|
|
slug: payload.slug ?? base.slug,
|
|
categoryId: payload.categoryId != null ? String(payload.categoryId) : base.categoryId,
|
|
shortDescription: payload.shortDescription ?? base.shortDescription,
|
|
description: payload.description ?? base.description,
|
|
media: mediaFromPayload,
|
|
startDate: start.date,
|
|
startTime: start.minutes,
|
|
endDate: end.date,
|
|
endTime: end.minutes,
|
|
provinceId: payload.provinceId != null ? String(payload.provinceId) : base.provinceId,
|
|
cityId: payload.cityId != null ? String(payload.cityId) : base.cityId,
|
|
address: payload.address ?? base.address,
|
|
lat: payload.lat ?? base.lat,
|
|
lng: payload.lng ?? base.lng,
|
|
isFree: payload.isFree ?? base.isFree,
|
|
price: payload.price ?? base.price,
|
|
capacity: payload.capacity ?? base.capacity,
|
|
genderRestriction: (payload.genderRestriction as EventGenderRestriction | undefined) ?? base.genderRestriction,
|
|
ageRestriction: (payload.ageRestriction as EventAgeRestriction | undefined) ?? base.ageRestriction,
|
|
cancellationFeePercent: payload.cancellationFeePercent ?? base.cancellationFeePercent,
|
|
isDiscoverable: settings.isDiscoverable ?? base.isDiscoverable,
|
|
autoCreateGroup: settings.autoCreateGroup ?? base.autoCreateGroup,
|
|
addressVisibility: settings.addressVisibility ?? base.addressVisibility,
|
|
generalArea: settings.generalArea ?? base.generalArea,
|
|
waitlistAutoOffer: settings.waitlistAutoOffer ?? base.waitlistAutoOffer,
|
|
sendReviewRequestSms: settings.sendReviewRequestSms ?? base.sendReviewRequestSms,
|
|
faqs: faqsFromPayload,
|
|
}
|
|
}
|