added main page config

This commit is contained in:
2026-10-13 19:28:08 +03:30
parent 5f3102f875
commit f99af90328
12 changed files with 3020 additions and 1 deletions
+4
View File
@@ -1,3 +1,5 @@
import { applyKhanegiLayoutVariants, createDefaultKhanegiLayoutVariants } from '@/config/khanegiLayoutVariants'
export type ThemeColors = {
primary: string
secondary: string
@@ -112,6 +114,7 @@ export function createDefaultKhanegiUiConfig() {
showProfile: true,
brandSubtitle: 'پنل کاربری',
},
layoutVariants: createDefaultKhanegiLayoutVariants(),
},
pages: {} as Record<string, unknown>,
}
@@ -303,6 +306,7 @@ export function normalizeKhanegiUiConfigForClient<T extends Record<string, any>>
if (app.header && typeof app.header === 'object') {
app.header.variant = 'default'
}
applyKhanegiLayoutVariants(app)
if (app.navigation && typeof app.navigation === 'object') {
if (app.navigation.type === 'sidebar+bottom') app.navigation.type = 'both'
app.navigation.sidebar = mapLegacyNavItems(app.navigation.sidebar)
+145
View File
@@ -0,0 +1,145 @@
/**
* چیدمان المان‌های تم جدید.
* با controlpanel_server/src/get-user/khanegi-layout-variants.ts یکی بماند.
* variant شناسهٔ themeN است. جزئیات المان داخل options می‌ماند.
*/
export const LAYOUT_VARIANT_ID = /^theme[1-9]\d*$/
export const LAYOUT_OPTION_KEY = /^[a-zA-Z][a-zA-Z0-9_]*$/
export type LayoutThemeDef = {
id: string
label: string
}
export type LayoutElementConfig = {
id: string
variant: string
options: Record<string, unknown>
}
export type KhanegiLayoutVariants = {
schemaId: 'layout-variants'
version: '1.0.0'
themes: LayoutThemeDef[]
header: LayoutElementConfig
footer: LayoutElementConfig
home: {
assets: LayoutElementConfig
livePrice: LayoutElementConfig
quickAccess: LayoutElementConfig
recentTransactions: LayoutElementConfig
[elementId: string]: LayoutElementConfig
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}
export function isLayoutVariantId(value: unknown): value is string {
return typeof value === 'string' && LAYOUT_VARIANT_ID.test(value)
}
export function nextLayoutThemeId(ids: string[]): string {
let n = 1
const used = new Set(ids)
while (used.has(`theme${n}`)) n += 1
return `theme${n}`
}
function defaultThemes(): LayoutThemeDef[] {
return [
{ id: 'theme1', label: 'تم ۱' },
{ id: 'theme2', label: 'تم ۲' },
]
}
function element(id: string): LayoutElementConfig {
return { id, variant: 'theme1', options: {} }
}
export function mergeLayoutThemes(stored: unknown, usedIds: string[] = []): LayoutThemeDef[] {
const parsed: LayoutThemeDef[] = []
const seen = new Set<string>()
if (Array.isArray(stored)) {
for (const row of stored) {
if (!isRecord(row) || !isLayoutVariantId(row.id) || seen.has(row.id)) continue
seen.add(row.id)
const label = typeof row.label === 'string' && row.label.trim()
? row.label.trim().slice(0, 40)
: row.id
parsed.push({ id: row.id, label })
}
}
const list = parsed.length ? parsed : defaultThemes()
for (const id of usedIds) {
if (!isLayoutVariantId(id) || list.some((theme) => theme.id === id)) continue
list.push({ id, label: id })
}
return list
}
export function createDefaultKhanegiLayoutVariants(): KhanegiLayoutVariants {
return {
schemaId: 'layout-variants',
version: '1.0.0',
themes: defaultThemes(),
header: element('header'),
footer: element('footer'),
home: {
assets: element('assets'),
livePrice: element('livePrice'),
quickAccess: element('quickAccess'),
recentTransactions: element('recentTransactions'),
},
}
}
export function mergeLayoutElement(id: string, stored: unknown): LayoutElementConfig {
const base = element(id)
if (!isRecord(stored)) return base
const options = isRecord(stored.options) ? { ...stored.options } : {}
return {
...stored,
id,
variant: isLayoutVariantId(stored.variant) ? stored.variant : 'theme1',
options,
}
}
/** تم انتخاب‌شده و کلید options پاک نمی‌شود. فهرست تم خالی به تم ۱ و ۲ برمی‌گردد. */
export function mergeKhanegiLayoutVariants(stored: unknown): KhanegiLayoutVariants {
const defaults = createDefaultKhanegiLayoutVariants()
const src = isRecord(stored) ? stored : {}
const homeSrc = isRecord(src.home) ? src.home : {}
const knownHome = Object.keys(defaults.home)
const home: KhanegiLayoutVariants['home'] = { ...defaults.home }
for (const key of Object.keys(homeSrc)) {
if (knownHome.includes(key)) continue
home[key] = mergeLayoutElement(key, homeSrc[key])
}
for (const key of knownHome) {
home[key] = mergeLayoutElement(key, homeSrc[key])
}
const header = mergeLayoutElement('header', src.header)
const footer = mergeLayoutElement('footer', src.footer)
const usedIds = [header.variant, footer.variant, ...Object.values(home).map((item) => item.variant)]
return {
...src,
schemaId: 'layout-variants',
version: '1.0.0',
themes: mergeLayoutThemes(src.themes, usedIds),
header,
footer,
home,
}
}
export function applyKhanegiLayoutVariants(app: { layoutVariants?: unknown } | null | undefined) {
if (!app || typeof app !== 'object') return
app.layoutVariants = mergeKhanegiLayoutVariants(app.layoutVariants)
}