// One-off deploy step for the Client Detail redesign: writes the live onboarding-template // settings with the composed quotation-led pipeline (shared front + per-module tail), then runs // the tick-preserving migration so existing SMS/RTGS/MobileApp projects adopt the new steps. // Idempotent ONLY as long as the templates below haven't been owner-edited since: each setting // is written only if it is still ABSENT, so a plain re-run never clobbers an owner's edit made // in the Modules onboarding-template editor. Pass --force to unconditionally overwrite every // template with the settings below — this DOES clobber owner edits, use with care. // Run from repo root: // npx tsx apps/hq/scripts/apply-onboarding-pipeline.ts [--force] import { openDb } from '../src/db' import { openPgDb } from '../src/db-pg' import { migrateOnboardingTemplates } from '../src/migrate-onboarding-templates' import { getSetting, setSetting } from '../src/repos-reminders' const FRONT = [ { key: 'enquiry', label: 'Enquiry received' }, { key: 'visit_meeting', label: 'Visit / meeting scheduled' }, { key: 'quotation_sent', label: 'Quotation sent' }, { key: 'quotation_approved', label: 'Quotation approved' }, ] const TAILS: Record = { SMS: [ { key: 'document_collection', label: 'Document collection' }, { key: 'dlt_registration', label: 'DLT registration' }, { key: 'account_creation', label: 'Account creation & registration' }, { key: 'installation', label: 'Installation' }, { key: 'testing', label: 'Testing' }, { key: 'training', label: 'Training' }, { key: 'go_live', label: 'Go-live' }, { key: 'payment_received', label: 'Payment received' }, ], RTGS: [ { key: 'document_collection', label: 'Document collection' }, { key: 'server_checkup', label: 'Server checkup' }, { key: 'setup_configuration', label: 'Setup & configuration' }, { key: 'installation', label: 'Installation' }, { key: 'testing', label: 'Testing' }, { key: 'training', label: 'Training' }, { key: 'go_live', label: 'Go-live' }, { key: 'payment_received', label: 'Payment received' }, ], MOBILEAPP: [ { key: 'requirement_setup', label: 'Requirement & setup' }, { key: 'configuration', label: 'Configuration' }, { key: 'data_import', label: 'Data import' }, { key: 'installation', label: 'Installation' }, { key: 'testing', label: 'Testing' }, { key: 'training', label: 'Training' }, { key: 'go_live', label: 'Go-live' }, { key: 'payment_received', label: 'Payment received' }, ], } async function main(): Promise { const pgUrl = process.env['DATABASE_URL'] ?? '' const db = pgUrl !== '' ? await openPgDb(pgUrl) : openDb(process.env['HQ_DATA_DIR']) const force = process.argv.includes('--force') // Audited via setSetting (append-only-audit rule) and, unless --force, skips any key // that's already set — so an owner's edit in the Modules onboarding-template editor is // left alone on a re-run instead of being silently clobbered. const upsert = async (key: string, list: unknown): Promise => { if (!force && (await getSetting(db, key)) !== null) { // eslint-disable-next-line no-console console.log(`Skipped ${key} — already set (pass --force to overwrite an owner edit).`) return } await setSetting(db, 'system', key, JSON.stringify(list)) } await upsert('project.milestone_template.front', FRONT) for (const [code, list] of Object.entries(TAILS)) await upsert(`project.milestone_template:${code}`, list) // eslint-disable-next-line no-console console.log('Template settings step done: front + SMS/RTGS/MOBILEAPP tails.') const res = await migrateOnboardingTemplates(db) // eslint-disable-next-line no-console console.log(`Onboarding migration: ${res.projects} project(s) rebuilt, ${res.carried} tick(s) carried, ${res.dropped} dropped, ${res.preserved} custom step(s) preserved.`) } main().catch((e) => { console.error(e); process.exit(1) })