From 1adaef5d18fced92bdc9fd191cbff790c268f996 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Sun, 9 Aug 2026 12:58:45 +0330 Subject: [PATCH] Add Khanegi uiVariant modular/newTheme with get_user payload split. Co-authored-by: Cursor --- .../migration.sql | 14 ++++++++++++++ prisma/schema.prisma | 8 ++++++++ src/get-user/get-user.service.ts | 16 ++++++++++++---- src/tenants/dto/create-tenant.dto.ts | 7 ++++++- src/tenants/tenants.service.ts | 6 ++++++ 5 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 prisma/migrations/20260809120000_khanegi_ui_variant/migration.sql diff --git a/prisma/migrations/20260809120000_khanegi_ui_variant/migration.sql b/prisma/migrations/20260809120000_khanegi_ui_variant/migration.sql new file mode 100644 index 0000000..20a7036 --- /dev/null +++ b/prisma/migrations/20260809120000_khanegi_ui_variant/migration.sql @@ -0,0 +1,14 @@ +-- Khanegi appearance family: modular (default) vs newTheme. +CREATE TYPE "KhanegiUiVariant" AS ENUM ('modular', 'newTheme'); + +ALTER TABLE "tenants" +ADD COLUMN "ui_variant" "KhanegiUiVariant" NOT NULL DEFAULT 'modular'; + +-- Pilot already on new theme +UPDATE "tenants" +SET "ui_variant" = 'newTheme' +WHERE "product" = 'khanegi' + AND ( + "domain" = 'app.aygingold.ir' + OR ("domains"::text ILIKE '%app.aygingold.ir%') + ); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1be2c23..1bb0a67 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,6 +13,12 @@ enum ProductType { khanegi } +/// Khanegi front-end appearance family (ignored for hamkar). +enum KhanegiUiVariant { + modular + newTheme +} + enum UserRole { super_admin viewer @@ -63,6 +69,8 @@ model Tenant { smsSyncedAt DateTime? @map("sms_synced_at") /// Full Khanegi UI JSON (theme, navigation, pages). Hamkar: unused for now. appUiConfig Json? @map("app_ui_config") + /// Khanegi only: which appearance editor / get_user payload shape to use. + uiVariant KhanegiUiVariant @default(modular) @map("ui_variant") /// Client cache-bust / force-reload signal for this sold version (GET /get_version). appVersion String @default("1") @map("app_version") isActive Boolean @default(true) @map("is_active") diff --git a/src/get-user/get-user.service.ts b/src/get-user/get-user.service.ts index 64cf15b..f5247c5 100644 --- a/src/get-user/get-user.service.ts +++ b/src/get-user/get-user.service.ts @@ -43,19 +43,23 @@ export class GetUserService { ? registryMode : 'dynamicMode' + const isKhanegi = tenant.product === 'khanegi'; + const uiVariant = + isKhanegi && tenant.uiVariant === 'newTheme' ? 'newTheme' : 'modular'; + const color = hamkarBrand?.primaryColor || - khanegiColors?.primary || + (uiVariant === 'newTheme' ? khanegiColors?.primary : null) || tenant.primaryColor || null const secondary = hamkarBrand?.secondaryColor || - khanegiColors?.secondary || + (uiVariant === 'newTheme' ? khanegiColors?.secondary : null) || tenant.secondaryColor || null const background = hamkarBrand?.backgroundColor || - khanegiColors?.background || + (uiVariant === 'newTheme' ? khanegiColors?.background : null) || tenant.backgroundColor || null @@ -75,8 +79,12 @@ export class GetUserService { product: tenant.product, theme: themeMode, mode: themeMode, + /** Khanegi front-end family for consumers */ + ui_variant: isKhanegi ? uiVariant : undefined, app_ui_config: - tenant.product === 'khanegi' ? this.stripKhanegiSaMeta(ui) : undefined, + isKhanegi && uiVariant === 'newTheme' + ? this.stripKhanegiSaMeta(ui) + : undefined, /** Laravel-compatible overlay; Hamkar merges only when present */ ui_settings: uiSettings.length ? uiSettings : undefined, }; diff --git a/src/tenants/dto/create-tenant.dto.ts b/src/tenants/dto/create-tenant.dto.ts index 990b0c7..6e06e3e 100644 --- a/src/tenants/dto/create-tenant.dto.ts +++ b/src/tenants/dto/create-tenant.dto.ts @@ -8,7 +8,7 @@ import { MaxLength, MinLength, } from 'class-validator'; -import { ProductType } from '@prisma/client'; +import { KhanegiUiVariant, ProductType } from '@prisma/client'; export class CreateTenantDto { @IsEnum(ProductType) @@ -80,6 +80,11 @@ export class CreateTenantDto { @IsObject() appUiConfig?: Record; + /** Khanegi only; default modular. */ + @IsOptional() + @IsEnum(KhanegiUiVariant) + uiVariant?: KhanegiUiVariant; + @IsOptional() @IsString() @MaxLength(64) diff --git a/src/tenants/tenants.service.ts b/src/tenants/tenants.service.ts index 21498c1..475ad5d 100644 --- a/src/tenants/tenants.service.ts +++ b/src/tenants/tenants.service.ts @@ -38,6 +38,7 @@ export class TenantsService { appUrl: true, adminUrl: true, appUiConfig: true, + uiVariant: true, appVersion: true, isActive: true, notes: true, @@ -81,6 +82,10 @@ export class TenantsService { appUiConfig: dto.appUiConfig ? (dto.appUiConfig as Prisma.InputJsonValue) : undefined, + uiVariant: + dto.product === ProductType.khanegi + ? dto.uiVariant ?? 'modular' + : 'modular', appVersion: dto.appVersion?.trim() || '1', isActive: dto.isActive ?? true, notes: dto.notes, @@ -144,6 +149,7 @@ export class TenantsService { ...(dto.appUiConfig !== undefined && { appUiConfig: dto.appUiConfig as Prisma.InputJsonValue, }), + ...(dto.uiVariant !== undefined && { uiVariant: dto.uiVariant }), ...(dto.appVersion !== undefined && { appVersion: dto.appVersion.trim() || '1', }),