Add Hamkar UI reset-to-default snapshot and restore button.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,28 @@ export type HamkarPricingBoxTheme = '1' | '2' | '3' | '4'
|
|||||||
export type HamkarOrderUi = 'new' | 'old'
|
export type HamkarOrderUi = 'new' | 'old'
|
||||||
export type HamkarPriceFontSize = 'کوچک' | 'متوسط' | 'بزرگ' | 'بزرگتر'
|
export type HamkarPriceFontSize = 'کوچک' | 'متوسط' | 'بزرگ' | 'بزرگتر'
|
||||||
|
|
||||||
|
export type HamkarBrandColors = {
|
||||||
|
/** Registry `color` → CSS `--bg2` (only when mode=dynamicMode) */
|
||||||
|
primaryColor: string
|
||||||
|
/** Registry `secondary_color` → CSS `--colorPrimary` (dynamicMode) */
|
||||||
|
secondaryColor: string
|
||||||
|
/** Registry `background_color` → CSS `--bg` (dynamicMode) */
|
||||||
|
backgroundColor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HamkarUiSettings = {
|
||||||
|
pricing_box_theme: HamkarPricingBoxTheme
|
||||||
|
price_font_size: HamkarPriceFontSize
|
||||||
|
orderUI: HamkarOrderUi
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Immutable factory look — set once, never overwritten on normal save */
|
||||||
|
export type HamkarAppearanceSnapshot = {
|
||||||
|
registryMode: string
|
||||||
|
brand: HamkarBrandColors
|
||||||
|
settings: HamkarUiSettings
|
||||||
|
}
|
||||||
|
|
||||||
export type HamkarUiConfig = {
|
export type HamkarUiConfig = {
|
||||||
kind: 'hamkar'
|
kind: 'hamkar'
|
||||||
/**
|
/**
|
||||||
@@ -11,19 +33,10 @@ export type HamkarUiConfig = {
|
|||||||
settingsManaged: boolean
|
settingsManaged: boolean
|
||||||
/** Named CSS mode from live registry (Montpar, limangold, …) or dynamicMode */
|
/** Named CSS mode from live registry (Montpar, limangold, …) or dynamicMode */
|
||||||
registryMode: string
|
registryMode: string
|
||||||
brand: {
|
brand: HamkarBrandColors
|
||||||
/** Registry `color` → CSS `--bg2` (only when mode=dynamicMode) */
|
settings: HamkarUiSettings
|
||||||
primaryColor: string
|
/** Original appearance before SuperAdmin color edits (reset target) */
|
||||||
/** Registry `secondary_color` → CSS `--colorPrimary` (dynamicMode) */
|
defaultSnapshot?: HamkarAppearanceSnapshot
|
||||||
secondaryColor: string
|
|
||||||
/** Registry `background_color` → CSS `--bg` (dynamicMode) */
|
|
||||||
backgroundColor: string
|
|
||||||
}
|
|
||||||
settings: {
|
|
||||||
pricing_box_theme: HamkarPricingBoxTheme
|
|
||||||
price_font_size: HamkarPriceFontSize
|
|
||||||
orderUI: HamkarOrderUi
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HAMKAR_PRICING_BOX_THEME_OPTIONS: {
|
export const HAMKAR_PRICING_BOX_THEME_OPTIONS: {
|
||||||
@@ -58,6 +71,34 @@ function asColor(value: unknown): string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeSettings(raw: unknown): HamkarUiSettings {
|
||||||
|
const base = createDefaultHamkarUiConfig().settings
|
||||||
|
const settings = raw && typeof raw === 'object' ? (raw as Record<string, any>) : {}
|
||||||
|
const theme = String(settings.pricing_box_theme ?? base.pricing_box_theme)
|
||||||
|
const orderUI = String(settings.orderUI ?? base.orderUI)
|
||||||
|
const font = String(settings.price_font_size ?? base.price_font_size)
|
||||||
|
return {
|
||||||
|
pricing_box_theme: (['1', '2', '3', '4'].includes(theme)
|
||||||
|
? theme
|
||||||
|
: base.pricing_box_theme) as HamkarPricingBoxTheme,
|
||||||
|
orderUI: (orderUI === 'old' || orderUI === 'new'
|
||||||
|
? orderUI
|
||||||
|
: base.orderUI) as HamkarOrderUi,
|
||||||
|
price_font_size: (HAMKAR_PRICE_FONT_SIZE_OPTIONS.some((o) => o.value === font)
|
||||||
|
? font
|
||||||
|
: base.price_font_size) as HamkarPriceFontSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeBrand(raw: unknown): HamkarBrandColors {
|
||||||
|
const brand = raw && typeof raw === 'object' ? (raw as Record<string, any>) : {}
|
||||||
|
return {
|
||||||
|
primaryColor: asColor(brand.primaryColor),
|
||||||
|
secondaryColor: asColor(brand.secondaryColor),
|
||||||
|
backgroundColor: asColor(brand.backgroundColor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function createDefaultHamkarUiConfig(): HamkarUiConfig {
|
export function createDefaultHamkarUiConfig(): HamkarUiConfig {
|
||||||
return {
|
return {
|
||||||
kind: 'hamkar',
|
kind: 'hamkar',
|
||||||
@@ -76,55 +117,74 @@ export function createDefaultHamkarUiConfig(): HamkarUiConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cloneHamkarSnapshot(config: {
|
||||||
|
registryMode: string
|
||||||
|
brand: HamkarBrandColors
|
||||||
|
settings: HamkarUiSettings
|
||||||
|
}): HamkarAppearanceSnapshot {
|
||||||
|
return {
|
||||||
|
registryMode: String(config.registryMode || 'dynamicMode').trim() || 'dynamicMode',
|
||||||
|
brand: { ...config.brand },
|
||||||
|
settings: { ...config.settings },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeHamkarUiConfig(raw: unknown): HamkarUiConfig {
|
export function normalizeHamkarUiConfig(raw: unknown): HamkarUiConfig {
|
||||||
const base = createDefaultHamkarUiConfig()
|
const base = createDefaultHamkarUiConfig()
|
||||||
if (!raw || typeof raw !== 'object') return base
|
if (!raw || typeof raw !== 'object') return base
|
||||||
|
|
||||||
const input = raw as Record<string, any>
|
const input = raw as Record<string, any>
|
||||||
const brand = input.brand && typeof input.brand === 'object' ? input.brand : {}
|
|
||||||
const settings =
|
|
||||||
input.settings && typeof input.settings === 'object' ? input.settings : {}
|
|
||||||
|
|
||||||
const theme = String(settings.pricing_box_theme ?? base.settings.pricing_box_theme)
|
|
||||||
const orderUI = String(settings.orderUI ?? base.settings.orderUI)
|
|
||||||
const font = String(settings.price_font_size ?? base.settings.price_font_size)
|
|
||||||
const registryMode = String(input.registryMode || base.registryMode).trim() || 'dynamicMode'
|
const registryMode = String(input.registryMode || base.registryMode).trim() || 'dynamicMode'
|
||||||
|
const settings = normalizeSettings(input.settings)
|
||||||
|
const brand = normalizeBrand(input.brand)
|
||||||
|
|
||||||
|
let defaultSnapshot: HamkarAppearanceSnapshot | undefined
|
||||||
|
if (input.defaultSnapshot && typeof input.defaultSnapshot === 'object') {
|
||||||
|
const snap = input.defaultSnapshot as Record<string, any>
|
||||||
|
defaultSnapshot = {
|
||||||
|
registryMode: String(snap.registryMode || registryMode).trim() || 'dynamicMode',
|
||||||
|
brand: normalizeBrand(snap.brand),
|
||||||
|
settings: normalizeSettings(snap.settings ?? settings),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: 'hamkar',
|
kind: 'hamkar',
|
||||||
settingsManaged: Boolean(input.settingsManaged),
|
settingsManaged: Boolean(input.settingsManaged),
|
||||||
registryMode,
|
registryMode,
|
||||||
brand: {
|
brand,
|
||||||
// Do not invent colors — empty means null in registry/get_user
|
settings,
|
||||||
primaryColor: asColor(brand.primaryColor),
|
defaultSnapshot,
|
||||||
secondaryColor: asColor(brand.secondaryColor),
|
|
||||||
backgroundColor: asColor(brand.backgroundColor),
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
pricing_box_theme: (['1', '2', '3', '4'].includes(theme)
|
|
||||||
? theme
|
|
||||||
: base.settings.pricing_box_theme) as HamkarPricingBoxTheme,
|
|
||||||
orderUI: (orderUI === 'old' || orderUI === 'new'
|
|
||||||
? orderUI
|
|
||||||
: base.settings.orderUI) as HamkarOrderUi,
|
|
||||||
price_font_size: (HAMKAR_PRICE_FONT_SIZE_OPTIONS.some((o) => o.value === font)
|
|
||||||
? font
|
|
||||||
: base.settings.price_font_size) as HamkarPriceFontSize,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Payload for API: empty color strings → null */
|
/** Payload for API: empty color strings → null; always keep defaultSnapshot if present */
|
||||||
export function hamkarUiToApiPayload(config: HamkarUiConfig) {
|
export function hamkarUiToApiPayload(config: HamkarUiConfig) {
|
||||||
const normalized = normalizeHamkarUiConfig(config)
|
const normalized = normalizeHamkarUiConfig(config)
|
||||||
const emptyToNull = (v: string) => (v.trim() ? v.trim() : null)
|
const emptyToNull = (v: string) => (v.trim() ? v.trim() : null)
|
||||||
|
const snap = normalized.defaultSnapshot
|
||||||
|
? {
|
||||||
|
registryMode: normalized.defaultSnapshot.registryMode,
|
||||||
|
brand: {
|
||||||
|
primaryColor: emptyToNull(normalized.defaultSnapshot.brand.primaryColor),
|
||||||
|
secondaryColor: emptyToNull(normalized.defaultSnapshot.brand.secondaryColor),
|
||||||
|
backgroundColor: emptyToNull(normalized.defaultSnapshot.brand.backgroundColor),
|
||||||
|
},
|
||||||
|
settings: { ...normalized.defaultSnapshot.settings },
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...normalized,
|
kind: 'hamkar' as const,
|
||||||
|
settingsManaged: normalized.settingsManaged,
|
||||||
|
registryMode: normalized.registryMode,
|
||||||
brand: {
|
brand: {
|
||||||
primaryColor: emptyToNull(normalized.brand.primaryColor),
|
primaryColor: emptyToNull(normalized.brand.primaryColor),
|
||||||
secondaryColor: emptyToNull(normalized.brand.secondaryColor),
|
secondaryColor: emptyToNull(normalized.brand.secondaryColor),
|
||||||
backgroundColor: emptyToNull(normalized.brand.backgroundColor),
|
backgroundColor: emptyToNull(normalized.brand.backgroundColor),
|
||||||
},
|
},
|
||||||
|
settings: { ...normalized.settings },
|
||||||
|
...(snap ? { defaultSnapshot: snap } : {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,3 +197,13 @@ export function hamkarUiToSettingsList(config: HamkarUiConfig) {
|
|||||||
{ key: 'orderUI', value: config.settings.orderUI },
|
{ key: 'orderUI', value: config.settings.orderUI },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolve original mode from tenant notes (`registryMode:X` or `mode:X`). */
|
||||||
|
export function registryModeFromNotes(notes?: string | null): string | null {
|
||||||
|
if (!notes) return null
|
||||||
|
return (
|
||||||
|
notes.match(/registryMode:([^\s|]+)/)?.[1] ||
|
||||||
|
notes.match(/(?:^|\|)\s*mode:([^\s|]+)/)?.[1] ||
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { message } from 'ant-design-vue'
|
import { Modal, message } from 'ant-design-vue'
|
||||||
import { ArrowRightOutlined } from '@ant-design/icons-vue'
|
import { ArrowRightOutlined } from '@ant-design/icons-vue'
|
||||||
import api from '@/api/http'
|
import api from '@/api/http'
|
||||||
import {
|
import {
|
||||||
|
cloneHamkarSnapshot,
|
||||||
createDefaultHamkarUiConfig,
|
createDefaultHamkarUiConfig,
|
||||||
HAMKAR_ORDER_UI_OPTIONS,
|
HAMKAR_ORDER_UI_OPTIONS,
|
||||||
HAMKAR_PRICE_FONT_SIZE_OPTIONS,
|
HAMKAR_PRICE_FONT_SIZE_OPTIONS,
|
||||||
HAMKAR_PRICING_BOX_THEME_OPTIONS,
|
HAMKAR_PRICING_BOX_THEME_OPTIONS,
|
||||||
hamkarUiToApiPayload,
|
hamkarUiToApiPayload,
|
||||||
normalizeHamkarUiConfig,
|
normalizeHamkarUiConfig,
|
||||||
|
registryModeFromNotes,
|
||||||
type HamkarUiConfig,
|
type HamkarUiConfig,
|
||||||
} from '@/config/defaultHamkarUiConfig'
|
} from '@/config/defaultHamkarUiConfig'
|
||||||
import { resolveHamkarEffectiveTheme } from '@/config/hamkarEffectiveTheme'
|
import { resolveHamkarEffectiveTheme } from '@/config/hamkarEffectiveTheme'
|
||||||
@@ -19,6 +21,7 @@ const route = useRoute()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
|
const resetting = ref(false)
|
||||||
const tenantName = ref('')
|
const tenantName = ref('')
|
||||||
const domain = ref('')
|
const domain = ref('')
|
||||||
|
|
||||||
@@ -40,6 +43,9 @@ const fontOptions = computed(() =>
|
|||||||
HAMKAR_PRICE_FONT_SIZE_OPTIONS.map((o) => ({ value: o.value, label: o.label })),
|
HAMKAR_PRICE_FONT_SIZE_OPTIONS.map((o) => ({ value: o.value, label: o.label })),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const hasDefaultSnapshot = computed(() => Boolean(config.defaultSnapshot))
|
||||||
|
const defaultModeLabel = computed(() => config.defaultSnapshot?.registryMode || '—')
|
||||||
|
|
||||||
const NAV_ITEMS = ['معامله', 'دارایی', 'سفارشات', 'گزارشدهی', 'مشتریان']
|
const NAV_ITEMS = ['معامله', 'دارایی', 'سفارشات', 'گزارشدهی', 'مشتریان']
|
||||||
|
|
||||||
function swatchHex(value: string, fallback = '#cbd5e1') {
|
function swatchHex(value: string, fallback = '#cbd5e1') {
|
||||||
@@ -62,7 +68,6 @@ function textOn(bg: string) {
|
|||||||
return (r * 299 + g * 587 + b * 114) / 1000 > 128 ? '#0f172a' : '#ffffff'
|
return (r * 299 + g * 587 + b * 114) / 1000 > 128 ? '#0f172a' : '#ffffff'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Live palette driving the preview (= what save will apply via dynamicMode) */
|
|
||||||
const palette = computed(() => ({
|
const palette = computed(() => ({
|
||||||
header: swatchHex(config.brand.primaryColor, '#ffffff'),
|
header: swatchHex(config.brand.primaryColor, '#ffffff'),
|
||||||
accent: swatchHex(config.brand.secondaryColor, '#2563EB'),
|
accent: swatchHex(config.brand.secondaryColor, '#2563EB'),
|
||||||
@@ -96,7 +101,6 @@ function seedEditableColors(normalized: HamkarUiConfig) {
|
|||||||
secondaryColor: normalized.brand.secondaryColor,
|
secondaryColor: normalized.brand.secondaryColor,
|
||||||
backgroundColor: normalized.brand.backgroundColor,
|
backgroundColor: normalized.brand.backgroundColor,
|
||||||
})
|
})
|
||||||
// Named CSS modes ignore registry colors — seed pickers with what the app actually paints
|
|
||||||
if (!effective.isDynamic) {
|
if (!effective.isDynamic) {
|
||||||
normalized.brand.primaryColor = effective.headerBg
|
normalized.brand.primaryColor = effective.headerBg
|
||||||
normalized.brand.secondaryColor = effective.accent
|
normalized.brand.secondaryColor = effective.accent
|
||||||
@@ -108,6 +112,47 @@ function seedEditableColors(normalized: HamkarUiConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureDefaultSnapshot(
|
||||||
|
normalized: HamkarUiConfig,
|
||||||
|
opts: {
|
||||||
|
notes?: string | null
|
||||||
|
primaryColor?: string | null
|
||||||
|
secondaryColor?: string | null
|
||||||
|
backgroundColor?: string | null
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
if (normalized.defaultSnapshot) return
|
||||||
|
|
||||||
|
const noteMode = registryModeFromNotes(opts.notes)
|
||||||
|
const currentMode = String(normalized.registryMode || '').trim()
|
||||||
|
const registryMode =
|
||||||
|
(noteMode && noteMode !== 'dynamicMode' ? noteMode : null) ||
|
||||||
|
(currentMode && currentMode !== 'dynamicMode' ? currentMode : null) ||
|
||||||
|
noteMode ||
|
||||||
|
currentMode ||
|
||||||
|
'dynamicMode'
|
||||||
|
|
||||||
|
normalized.defaultSnapshot = cloneHamkarSnapshot({
|
||||||
|
registryMode,
|
||||||
|
brand: {
|
||||||
|
primaryColor: String(opts.primaryColor || normalized.brand.primaryColor || ''),
|
||||||
|
secondaryColor: String(opts.secondaryColor || normalized.brand.secondaryColor || ''),
|
||||||
|
backgroundColor: String(opts.backgroundColor || normalized.brand.backgroundColor || ''),
|
||||||
|
},
|
||||||
|
settings: { ...normalized.settings },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function persist(payload: ReturnType<typeof hamkarUiToApiPayload>) {
|
||||||
|
await api.patch(`/tenants/${route.params.id}`, {
|
||||||
|
appUiConfig: payload,
|
||||||
|
primaryColor: payload.brand.primaryColor,
|
||||||
|
secondaryColor: payload.brand.secondaryColor,
|
||||||
|
backgroundColor: payload.brand.backgroundColor,
|
||||||
|
})
|
||||||
|
Object.assign(config, normalizeHamkarUiConfig(payload))
|
||||||
|
}
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -128,7 +173,7 @@ async function load() {
|
|||||||
settingsManaged: false,
|
settingsManaged: false,
|
||||||
registryMode:
|
registryMode:
|
||||||
data.appUiConfig?.registryMode ||
|
data.appUiConfig?.registryMode ||
|
||||||
data.notes?.match(/registryMode:([^\s|]+)/)?.[1] ||
|
registryModeFromNotes(data.notes) ||
|
||||||
'dynamicMode',
|
'dynamicMode',
|
||||||
brand: {
|
brand: {
|
||||||
primaryColor: data.primaryColor,
|
primaryColor: data.primaryColor,
|
||||||
@@ -148,8 +193,20 @@ async function load() {
|
|||||||
if (!normalized.brand.backgroundColor && data.backgroundColor) {
|
if (!normalized.brand.backgroundColor && data.backgroundColor) {
|
||||||
normalized.brand.backgroundColor = String(data.backgroundColor)
|
normalized.brand.backgroundColor = String(data.backgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensureDefaultSnapshot(normalized, {
|
||||||
|
notes: data.notes,
|
||||||
|
primaryColor: data.primaryColor,
|
||||||
|
secondaryColor: data.secondaryColor,
|
||||||
|
backgroundColor: data.backgroundColor,
|
||||||
|
})
|
||||||
|
|
||||||
seedEditableColors(normalized)
|
seedEditableColors(normalized)
|
||||||
Object.assign(config, normalized)
|
Object.assign(config, normalized)
|
||||||
|
|
||||||
|
if (!rawUi.defaultSnapshot && normalized.defaultSnapshot) {
|
||||||
|
await persist(hamkarUiToApiPayload(normalized))
|
||||||
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e?.response?.data?.message || 'خطا در بارگذاری ظاهر همکار')
|
message.error(e?.response?.data?.message || 'خطا در بارگذاری ظاهر همکار')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -160,16 +217,16 @@ async function load() {
|
|||||||
async function save() {
|
async function save() {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
try {
|
try {
|
||||||
// Colors only paint the app in dynamicMode (Hamkar apply-theme)
|
|
||||||
config.registryMode = 'dynamicMode'
|
config.registryMode = 'dynamicMode'
|
||||||
const payload = hamkarUiToApiPayload(config)
|
if (!config.defaultSnapshot) {
|
||||||
await api.patch(`/tenants/${route.params.id}`, {
|
ensureDefaultSnapshot(config, {
|
||||||
appUiConfig: payload,
|
notes: null,
|
||||||
primaryColor: payload.brand.primaryColor,
|
primaryColor: config.brand.primaryColor,
|
||||||
secondaryColor: payload.brand.secondaryColor,
|
secondaryColor: config.brand.secondaryColor,
|
||||||
backgroundColor: payload.brand.backgroundColor,
|
backgroundColor: config.brand.backgroundColor,
|
||||||
})
|
})
|
||||||
Object.assign(config, normalizeHamkarUiConfig(payload))
|
}
|
||||||
|
await persist(hamkarUiToApiPayload(config))
|
||||||
message.success('ظاهر همکار ذخیره شد')
|
message.success('ظاهر همکار ذخیره شد')
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
const msg = e?.response?.data?.message
|
const msg = e?.response?.data?.message
|
||||||
@@ -179,6 +236,40 @@ async function save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetToDefault() {
|
||||||
|
if (!config.defaultSnapshot) {
|
||||||
|
message.warning('ظاهر پیشفرض برای این نسخه ثبت نشده است')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'بازگشت به ظاهر اولیه؟',
|
||||||
|
content: `حالت پیشفرض «${config.defaultSnapshot.registryMode}» اعمال و ذخیره میشود.`,
|
||||||
|
okText: 'بازگشت به پیشفرض',
|
||||||
|
cancelText: 'انصراف',
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
async onOk() {
|
||||||
|
resetting.value = true
|
||||||
|
try {
|
||||||
|
const snap = config.defaultSnapshot!
|
||||||
|
config.registryMode = snap.registryMode
|
||||||
|
config.brand.primaryColor = snap.brand.primaryColor
|
||||||
|
config.brand.secondaryColor = snap.brand.secondaryColor
|
||||||
|
config.brand.backgroundColor = snap.brand.backgroundColor
|
||||||
|
config.settings = { ...snap.settings }
|
||||||
|
await persist(hamkarUiToApiPayload(config))
|
||||||
|
seedEditableColors(config)
|
||||||
|
message.success('ظاهر اولیه بازیابی شد')
|
||||||
|
} catch (e: any) {
|
||||||
|
const msg = e?.response?.data?.message
|
||||||
|
message.error(Array.isArray(msg) ? msg.join('، ') : msg || 'بازیابی ناموفق')
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
resetting.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(load)
|
onMounted(load)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -232,6 +323,9 @@ onMounted(load)
|
|||||||
|
|
||||||
<section class="form-section">
|
<section class="form-section">
|
||||||
<h3>رنگها</h3>
|
<h3>رنگها</h3>
|
||||||
|
<p v-if="hasDefaultSnapshot" class="hint">
|
||||||
|
ظاهر پیشفرض ثبتشده: <strong>{{ defaultModeLabel }}</strong>
|
||||||
|
</p>
|
||||||
<div class="palette-grid">
|
<div class="palette-grid">
|
||||||
<label class="palette-card">
|
<label class="palette-card">
|
||||||
<span class="palette-title">هدر</span>
|
<span class="palette-title">هدر</span>
|
||||||
@@ -325,6 +419,15 @@ onMounted(load)
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="sticky-actions">
|
<div class="sticky-actions">
|
||||||
|
<a-button
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
:loading="resetting"
|
||||||
|
:disabled="!hasDefaultSnapshot"
|
||||||
|
@click="resetToDefault"
|
||||||
|
>
|
||||||
|
بازگشت به ظاهر اولیه
|
||||||
|
</a-button>
|
||||||
<a-button type="primary" size="large" block :loading="saving" @click="save">
|
<a-button type="primary" size="large" block :loading="saving" @click="save">
|
||||||
ذخیره ظاهر همکار
|
ذخیره ظاهر همکار
|
||||||
</a-button>
|
</a-button>
|
||||||
@@ -340,6 +443,12 @@ onMounted(load)
|
|||||||
gap: 14px;
|
gap: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
@@ -466,4 +575,9 @@ onMounted(load)
|
|||||||
.palette-hex {
|
.palette-hex {
|
||||||
font-family: ui-monospace, monospace;
|
font-family: ui-monospace, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sticky-actions {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user