112 lines
3.6 KiB
TypeScript
112 lines
3.6 KiB
TypeScript
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<T>(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<string, unknown>).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>): 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<ScreenDefinition>["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;
|
|
}
|