/// import { type Cell, cell, Default, handler, lift, recipe, str, } from "commontools"; interface QuoteOptionSeed { id: string; label: string; price: number; defaultSelected: boolean; } interface QuoteOptionInput extends Partial {} interface QuoteOption { id: string; label: string; price: number; selected: boolean; } interface ToggleOptionEvent { id?: string; } interface ConfigureOptionEvent { id?: string; price?: number; selected?: boolean; label?: string; } interface ConfigurePricingEvent { basePrice?: number; discountRate?: number; } interface QuoteConfigurationArgs { basePrice: Default; discountRate: Default; options: Default; } interface PricingDetails { base: number; optionsTotal: number; subtotal: number; discountAmount: number; total: number; } const defaultBasePrice = 1800; const defaultOptionSeeds: QuoteOptionSeed[] = [ { id: "support", label: "Priority support", price: 250, defaultSelected: true, }, { id: "training", label: "Team onboarding workshop", price: 450, defaultSelected: false, }, { id: "analytics", label: "Advanced analytics suite", price: 600, defaultSelected: false, }, { id: "compliance", label: "Regulatory compliance review", price: 320, defaultSelected: false, }, ]; const maxDiscountRate = 0.5; const currencyPrecision = 100; const clampCurrency = (value: number): number => { if (!Number.isFinite(value)) return 0; return Math.round(Math.max(value, 0) * currencyPrecision) / currencyPrecision; }; const toIdentifier = (value: string): string => { return value .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, "") .slice(0, 48); }; const sanitizeLabel = ( maybeLabel: unknown, fallback: string, index: number, ): string => { if (typeof maybeLabel === "string") { const trimmed = maybeLabel.trim(); if (trimmed.length > 0) { return trimmed.slice(0, 64); } } if (fallback.length > 0) { return fallback.slice(0, 64); } return `Option ${index + 1}`; }; const sanitizeId = ( maybeId: unknown, label: string, fallback: string, index: number, used: Set, ): string => { const base = typeof maybeId === "string" && maybeId.trim().length > 0 ? toIdentifier(maybeId) : toIdentifier(label.length > 0 ? label : fallback); let candidate = base.length > 0 ? base : `option-${index + 1}`; if (!used.has(candidate)) { return candidate; } let attempt = 1; while (used.has(candidate)) { attempt += 1; const suffix = `-${attempt}`; const sliceLength = Math.max(16, 48 - suffix.length); candidate = `${candidate.slice(0, sliceLength)}${suffix}`; } return candidate; }; const sanitizePrice = (maybePrice: unknown, fallback: number): number => { if (typeof maybePrice === "number" && Number.isFinite(maybePrice)) { return clampCurrency(maybePrice); } return clampCurrency(fallback); }; const sanitizeSelected = ( maybeSelected: unknown, fallback: boolean, ): boolean => { if (typeof maybeSelected === "boolean") { return maybeSelected; } return fallback; }; const sanitizeOptionList = ( value: readonly QuoteOptionInput[] | undefined, ): QuoteOption[] => { const source = Array.isArray(value) && value.length > 0 ? value : defaultOptionSeeds; const sanitized: QuoteOption[] = []; const usedIds = new Set(); for (let index = 0; index < source.length; index += 1) { const entry = source[index]; const fallback = defaultOptionSeeds[index] ?? defaultOptionSeeds[0]; const label = sanitizeLabel( entry?.label, fallback?.label ?? `Option ${index + 1}`, index, ); const id = sanitizeId( entry?.id, label, fallback?.id ?? label, index, usedIds, ); const price = sanitizePrice(entry?.price, fallback?.price ?? 0); const selected = sanitizeSelected( entry?.defaultSelected, fallback?.defaultSelected ?? false, ); sanitized.push({ id, label, price, selected }); usedIds.add(id); } if (sanitized.length === 0) { const fallback: QuoteOption[] = []; for (let index = 0; index < defaultOptionSeeds.length; index += 1) { const seed = defaultOptionSeeds[index]; const id = sanitizeId(seed.id, seed.label, seed.id, index, usedIds); usedIds.add(id); fallback.push({ id, label: seed.label, price: clampCurrency(seed.price), selected: seed.defaultSelected, }); } return fallback; } return sanitized; }; const normalizeOptionId = (value: unknown): string | null => { if (value == null) return null; const raw = typeof value === "string" ? value : String(value); const normalized = toIdentifier(raw); return normalized.length > 0 ? normalized : null; }; const toRawOptionId = (value: unknown): string | null => { if (value == null) return null; const raw = typeof value === "string" ? value : String(value); const trimmed = raw.trim(); return trimmed.length > 0 ? trimmed.slice(0, 64) : null; }; const optionMatchesId = ( option: QuoteOption, normalized: string | null, raw: string | null, ): boolean => { if (normalized && option.id === normalized) return true; if (raw && option.id === raw) return true; if (raw) { const lowered = raw.toLowerCase(); if (option.label.toLowerCase() === lowered) return true; const slugged = toIdentifier(raw); if (slugged.length > 0 && option.id === slugged) return true; } return false; }; const fingerprintOptions = ( entries: readonly QuoteOption[] | undefined, ): string => { if (!entries || entries.length === 0) return ""; return entries.map((option) => { const price = option.price.toFixed(2); const flag = option.selected ? "1" : "0"; return `${option.id}:${option.label}:${price}:${flag}`; }).join("|"); }; const sanitizeBasePrice = (value: number | undefined): number => { if (typeof value !== "number" || !Number.isFinite(value)) { return clampCurrency(defaultBasePrice); } return clampCurrency(Math.max(value, 0)); }; const sanitizeDiscountRate = (value: number | undefined): number => { if (typeof value !== "number" || !Number.isFinite(value)) { return 0; } if (value <= 0) return 0; if (value >= maxDiscountRate) return maxDiscountRate; return Math.round(value * 1000) / 1000; }; const toggleOptionSelection = handler( ( event: ToggleOptionEvent | undefined, context: { options: Cell }, ) => { const normalizedId = normalizeOptionId(event?.id); const rawId = toRawOptionId(event?.id); if (!normalizedId && !rawId) return; const current = context.options.get(); if (!Array.isArray(current) || current.length === 0) { return; } let changed = false; for (let index = 0; index < current.length; index += 1) { const option = current[index]; if (!optionMatchesId(option, normalizedId, rawId)) { continue; } const selectedCell = context.options.key(index).key("selected"); selectedCell.set(!option.selected); changed = true; } if (!changed) { return; } }, ); const configureOption = handler( ( event: ConfigureOptionEvent | undefined, context: { options: Cell }, ) => { const normalizedId = normalizeOptionId(event?.id); const rawId = toRawOptionId(event?.id); if (!normalizedId && !rawId) return; const current = context.options.get(); if (!Array.isArray(current) || current.length === 0) { return; } let _changed = false; for (let index = 0; index < current.length; index += 1) { const option = current[index]; if (!optionMatchesId(option, normalizedId, rawId)) { continue; } const price = typeof event?.price === "number" && Number.isFinite(event.price) ? clampCurrency(Math.max(event.price, 0)) : option.price; const selected = typeof event?.selected === "boolean" ? event.selected : option.selected; const label = typeof event?.label === "string" && event.label.trim() ? event.label.trim().slice(0, 64) : option.label; if (price !== option.price) { context.options.key(index).key("price").set(price); _changed = true; } if (selected !== option.selected) { context.options.key(index).key("selected").set(selected); _changed = true; } if (label !== option.label) { context.options.key(index).key("label").set(label); _changed = true; } } }, ); const configurePricing = handler( ( event: ConfigurePricingEvent | undefined, context: { basePrice: Cell; discountRate: Cell }, ) => { if ( typeof event?.basePrice === "number" && Number.isFinite(event.basePrice) ) { context.basePrice.set(sanitizeBasePrice(event.basePrice)); } if ( typeof event?.discountRate === "number" && Number.isFinite(event.discountRate) ) { context.discountRate.set(sanitizeDiscountRate(event.discountRate)); } }, ); export const quoteConfiguration = recipe( "Quote Configuration Pattern", ({ basePrice, discountRate, options }) => { const initialOptions = sanitizeOptionList(undefined); const optionState = cell(initialOptions); let lastOptionSignature = fingerprintOptions(initialOptions); const syncOptions = lift((raw: QuoteOptionInput[] | undefined) => { const normalized = sanitizeOptionList(raw); const signature = fingerprintOptions(normalized); if (signature === lastOptionSignature) { return normalized; } lastOptionSignature = signature; optionState.set(normalized); return normalized; })(options); const safeBasePrice = lift(sanitizeBasePrice)(basePrice); const safeDiscountRate = lift(sanitizeDiscountRate)(discountRate); const optionsView = lift((list: QuoteOption[] | undefined) => { if (!Array.isArray(list)) { return [] as QuoteOption[]; } return list.map((option) => ({ ...option })); })(optionState); const selectedOptionIds = lift((list: QuoteOption[]) => { return list.filter((option) => option.selected).map((option) => option.id ); })(optionsView); const pricing = lift((input: { base: number; addOns: QuoteOption[]; discount: number; }): PricingDetails => { const addOnTotal = input.addOns.reduce((total, option) => { return option.selected ? total + option.price : total; }, 0); const normalizedBase = clampCurrency(input.base); const normalizedAddOns = clampCurrency(addOnTotal); const subtotal = clampCurrency(normalizedBase + normalizedAddOns); const discountAmount = clampCurrency(subtotal * input.discount); const total = clampCurrency(subtotal - discountAmount); return { base: normalizedBase, optionsTotal: normalizedAddOns, subtotal, discountAmount, total, }; })({ base: safeBasePrice, addOns: optionsView, discount: safeDiscountRate, }); const subtotal = lift((details: PricingDetails) => details.subtotal)( pricing, ); const discountAmount = lift((details: PricingDetails) => details.discountAmount )( pricing, ); const total = lift((details: PricingDetails) => details.total)(pricing); const optionsTotal = lift((details: PricingDetails) => details.optionsTotal )( pricing, ); const formattedTotal = lift((value: number) => `$${value.toFixed(2)}`)( total, ); const formattedDiscount = lift((value: number) => `$${value.toFixed(2)}`)( discountAmount, ); const summary = str`Quote total ${formattedTotal} (discount ${formattedDiscount})`; return { basePrice: safeBasePrice, discountRate: safeDiscountRate, options: optionsView, selectedOptionIds, optionsTotal, subtotal, discountAmount, total, summary, pricing, toggleOption: toggleOptionSelection({ options: optionState }), configureOption: configureOption({ options: optionState }), configurePricing: configurePricing({ basePrice, discountRate, }), effects: { syncOptions }, }; }, );