Expand SuperAdmin web for SMS, servers, plugins, admins, and UI config.

Align SMS tenant panel with Hamkar, add Jalali dates, and support hamkar/khanegi appearance editing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-30 18:19:54 +03:30
co-authored by Cursor
parent 3371015b96
commit cfee6b0546
27 changed files with 4528 additions and 408 deletions
+63
View File
@@ -0,0 +1,63 @@
/** Iran timezone + Persian (Jalali) calendar helpers for SuperAdmin web. */
export const IRAN_TZ = 'Asia/Tehran'
function toDate(value?: string | Date | number | null): Date | null {
if (value == null || value === '') return null
const d = value instanceof Date ? value : new Date(value)
return Number.isNaN(d.getTime()) ? null : d
}
/** تاریخ شمسی به وقت ایران — مثلاً ۱۴۰۵/۰۵/۰۵ */
export function formatIranDate(value?: string | Date | number | null): string {
const d = toDate(value)
if (!d) return '—'
return new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
timeZone: IRAN_TZ,
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).format(d)
}
/** تاریخ و ساعت شمسی به وقت ایران */
export function formatIranDateTime(value?: string | Date | number | null): string {
const d = toDate(value)
if (!d) return '—'
return new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
timeZone: IRAN_TZ,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
}).format(d)
}
/** YYYY-MM-DD (میلادی) برای روز تقویمی ایران */
export function iranYmd(value?: string | Date | number | null): string {
const d = toDate(value) ?? new Date()
return new Intl.DateTimeFormat('en-CA', {
timeZone: IRAN_TZ,
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).format(d)
}
/** ISO از تاریخ روز ایران (ظهر محلی تا جابه‌جایی DST کم‌اثر باشد) */
export function iranDateInputToIso(ymd: string | null | undefined): string | null {
if (!ymd || !/^\d{4}-\d{2}-\d{2}$/.test(ymd)) return null
// Interpret as Iran calendar day noon via fixed offset +03:30 (Iran standard; DST abolished)
const d = new Date(`${ymd}T12:00:00+03:30`)
return Number.isNaN(d.getTime()) ? null : d.toISOString()
}
export function isoToIranDateInput(iso?: string | null): string {
if (!iso) return ''
return iranYmd(iso)
}
export function todayIranDateInput(): string {
return iranYmd(new Date())
}