admin/features/events/eventSlug.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

60 lines
1.7 KiB
TypeScript

import { isPublicEventSlug } from '@/lib/routing/eventRoute'
export const EVENT_SLUG_MAX_LENGTH = 220
export const EVENT_SLUG_FALLBACK = 'event'
/** Previous wizard default: `event-` + first 8 chars of a UUID. */
const RANDOM_PLACEHOLDER_SLUG = /^event-[0-9a-f]{8}$/i
// Same single-pass map as backend/src/modules/events/event-slug.util.ts.
const PERSIAN_TRANSLIT_FROM = 'ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیآأإئءؤةيك '
const PERSIAN_TRANSLIT_TO = 'abptsjchxdzrzjsssztzaqfqkglmnvhyaaaeaotyk-'
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
export function slugifyEventTitle(title: string): string {
const trimmed = title.trim()
if (!trimmed) return ''
let withLatinDigits = ''
for (const char of trimmed) {
const digit = PERSIAN_DIGITS.indexOf(char)
withLatinDigits += digit === -1 ? char : String(digit)
}
let raw = ''
for (const char of withLatinDigits) {
const index = PERSIAN_TRANSLIT_FROM.indexOf(char)
raw += index === -1 ? char : PERSIAN_TRANSLIT_TO[index]
}
const slug = raw
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/-+/g, '-')
.replace(/(^-+|-+$)/g, '')
.slice(0, EVENT_SLUG_MAX_LENGTH)
.replace(/-+$/g, '')
if (!slug || !isPublicEventSlug(slug)) return EVENT_SLUG_FALLBACK
return slug
}
/**
* True when the slug field should keep tracking the title: empty, leftover
* random placeholder, or still equal to the slug generated from `title`.
*/
export function shouldAutogenerateEventSlug(currentSlug: string, title: string): boolean {
const slug = currentSlug.trim()
if (!slug) return true
if (RANDOM_PLACEHOLDER_SLUG.test(slug)) return true
return slug === slugifyEventTitle(title)
}