import type { BuilderState } from "@/lib/admin/builder/context"; import type { BuilderScreen, BuilderFunnelState } from "@/lib/admin/builder/types"; import type { FunnelDefinition, ScreenDefinition, ListScreenDefinition, ScreenVariantDefinition, } from "@/lib/funnel/types"; function deepCloneValue(value: T): T { if (Array.isArray(value)) { return value.map((item) => deepCloneValue(item)) as unknown as T; } if (value && typeof value === "object") { const entries = Object.entries(value as Record).map(([key, entryValue]) => [ key, deepCloneValue(entryValue), ]); return Object.fromEntries(entries) as T; } return value; } function withPositions(screens: ScreenDefinition[]): BuilderScreen[] { return screens.map((screen, index) => ({ ...screen, position: { x: 120 + (index % 4) * 240, y: 120 + Math.floor(index / 4) * 200, }, })) as BuilderScreen[]; } export function deserializeFunnelDefinition(funnel: FunnelDefinition): BuilderState { const builderScreens = withPositions(funnel.screens); return { meta: funnel.meta, defaultTexts: funnel.defaultTexts, screens: builderScreens, selectedScreenId: builderScreens[0]?.id ?? null, isDirty: false, }; } export function serializeBuilderState(state: BuilderFunnelState): FunnelDefinition { const screens = state.screens; const meta: FunnelDefinition["meta"] = { ...state.meta, firstScreenId: state.meta.firstScreenId ?? state.screens[0]?.id, }; return { meta, defaultTexts: state.defaultTexts, screens, }; } export function cloneScreen(screen: BuilderScreen, overrides?: Partial): BuilderScreen { const copy = { ...screen, ...(screen.template === "list" && 'list' in screen ? { list: { ...(screen as ListScreenDefinition).list, options: (screen as ListScreenDefinition).list.options.map((option) => ({ ...option })), } } : {}), ...(Array.isArray(screen.variants) ? { variants: screen.variants.map((variant) => ({ conditions: variant.conditions.map((condition) => ({ screenId: condition.screenId, operator: condition.operator, conditionType: condition.conditionType, ...(condition.optionIds ? { optionIds: [...condition.optionIds] } : {}), ...(condition.values ? { values: [...condition.values] } : {}), })), ...(variant.overrides ? { overrides: deepCloneValue(variant.overrides) as ScreenVariantDefinition["overrides"] } : {}), })), } : {}), navigation: screen.navigation ? { defaultNextScreenId: screen.navigation.defaultNextScreenId, rules: screen.navigation.rules?.map((rule) => ({ nextScreenId: rule.nextScreenId, conditions: rule.conditions.map((condition) => ({ screenId: condition.screenId, operator: condition.operator, conditionType: condition.conditionType, ...(condition.optionIds ? { optionIds: [...condition.optionIds] } : {}), ...(condition.values ? { values: [...condition.values] } : {}), })), })), } : undefined, } as BuilderScreen; return overrides ? { ...copy, ...overrides } as BuilderScreen : copy; } export function toBuilderFunnelState(state: BuilderState): BuilderFunnelState { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { isDirty: _isDirty, selectedScreenId: _selectedScreenId, ...rest } = state; return rest; }