Files
clienthub/scripts/import-services-notion.ts
simone 696a95950c chore: add Notion service import scripts
scripts/reset-and-import-services.ts — deletes 3 legacy test services,
imports 55 services from Notion "DB Offerta: Attività SC" CSV export
(Signature Offer / Entry Offer / Retainer Offer, with fase metadata).

scripts/import-services-notion.ts — standalone idempotent version.

Run via SSH tunnel with DATABASE_URL pointed at 127.0.0.1:54321.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:46:17 +02:00

142 lines
12 KiB
TypeScript

// Import 55 services from Notion "DB Offerta: Attività SC" CSV export.
// Idempotent: skips rows where a service with the same name already exists.
// Run via SSH tunnel (same pattern as push-12-offer-tier-schema.ts):
// DATABASE_URL=$(node --env-file=.env.local -e 'const u=new URL(process.env.DATABASE_URL); u.host="127.0.0.1:54321"; process.stdout.write(u.toString())') \
// npx tsx scripts/import-services-notion.ts
import postgres from "postgres";
import { customAlphabet } from "nanoid";
const nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz", 12);
type ServiceRow = {
name: string;
unit_price: number;
category: string;
fase: string | null;
};
// Source: offerta_servizi.csv exported from Notion.
// category = primary Offerta value (first when multi-value).
// fase = null for Retainer Offer rows (no Fase column value in source).
const SERVICES: ServiceRow[] = [
// ── Shared: Signature Offer + Entry Offer ──────────────────────────────────
{ name: "Raccolta e Mappatura Materiali", unit_price: 100, category: "Signature Offer", fase: "Fase 1 → Onboarding / Setup" },
{ name: "Audit iniziale (UX/UI, struttura, conversione)", unit_price: 500, category: "Signature Offer", fase: "Fase 1 → Onboarding / Setup" },
{ name: "Workshop (1° fase)", unit_price: 900, category: "Signature Offer", fase: "Fase 1 → Onboarding / Setup" },
{ name: "Analisi Competitor", unit_price: 400, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
// ── Entry Offer ────────────────────────────────────────────────────────────
{ name: "Documento di restituzione (problemi + lista ottimizzazioni)", unit_price: 500, category: "Entry Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Redesign visivo dell'above-the-fold / hero (il 'prima → dopo')", unit_price: 700, category: "Entry Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Call di restituzione", unit_price: 200, category: "Entry Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Call di presentazione Prima/Dopo", unit_price: 200, category: "Entry Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Roadmap / istruzioni operative + mini kit", unit_price: 600, category: "Entry Offer", fase: "Fase 5 → Offboarding / Consegna" },
// ── Signature Offer — Fase 2 ───────────────────────────────────────────────
{ name: "UX Research", unit_price: 1200, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
{ name: "Customer Journey", unit_price: 900, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
{ name: "Architettura (sitemap)", unit_price: 500, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
{ name: "Brand Identity - Visual identity", unit_price: 2000, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
{ name: "Brand Identity - Voice identity", unit_price: 800, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
{ name: "Direzione Creative (moodboard)", unit_price: 600, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
{ name: "Workshop (2° fase)", unit_price: 900, category: "Signature Offer", fase: "Fase 2 → Analisi / Strategia" },
// ── Signature Offer — Fase 3 ───────────────────────────────────────────────
{ name: "Settaggio CMS (Wordpress Webflow ecc)", unit_price: 500, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "UX - UI", unit_price: 1000, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Wireframe (Low-Mid Fidelity)", unit_price: 600, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Homepage (Figma + Dev)", unit_price: 1500, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Revisione #1", unit_price: 400, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- Chi siamo", unit_price: 600, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- Contatti", unit_price: 400, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- Blog (Archivio)", unit_price: 300, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- - Blog post (Template Singolo)", unit_price: 600, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- Case Study (Archivio)", unit_price: 300, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- - Case Study (Template Singolo)", unit_price: 800, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- Landing Page (Metodo o Differenziante)", unit_price: 1000, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- - - Thank You Page", unit_price: 300, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "- - - 404", unit_price: 150, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Responsive (inclusa nelle pagine?)", unit_price: 800, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "UX writing (Revisione testi)", unit_price: 400, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Seo Setup (basic on-page)", unit_price: 400, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Seo Avanzato (+ keyword + ricerca + blog post dentro retainer)", unit_price: 400, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
{ name: "Revisione #2 finale", unit_price: 400, category: "Signature Offer", fase: "Fase 3 → Esecuzione / Core" },
// ── Signature Offer — Fase 4 ───────────────────────────────────────────────
{ name: "Web Core Vitals (Optimization)", unit_price: 500, category: "Signature Offer", fase: "Fase 4 → Raffinamento / Extra" },
{ name: "QA - Test Cross Browser e responsive", unit_price: 400, category: "Signature Offer", fase: "Fase 4 → Raffinamento / Extra" },
{ name: "Setting GA4 - Tag Manager - Hotjar/Clarify", unit_price: 500, category: "Signature Offer", fase: "Fase 4 → Raffinamento / Extra" },
// ── Signature Offer — Fase 5 ───────────────────────────────────────────────
{ name: "Raccolta Feedback Post Lancio", unit_price: 200, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Go-live / messa online & accessi", unit_price: 300, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Audit uscita", unit_price: 500, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Real User Testing", unit_price: 1200, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Follow Up Handover", unit_price: 200, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Video Tutorial per micro modifiche in autonomia", unit_price: 400, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Revisioni Extra illimitate (sicuri illimitati???)", unit_price: 1200, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
{ name: "Brand Kit (youtube - linkedin - insta)", unit_price: 600, category: "Signature Offer", fase: "Fase 5 → Offboarding / Consegna" },
// ── Retainer Offer ─────────────────────────────────────────────────────────
{ name: "Mantenimento tecnico (sito sempre online, bello, funzionante)", unit_price: 200, category: "Retainer Offer", fase: null },
{ name: "Monitoraggio dati (GA4 / Hotjar-Clarity)", unit_price: 100, category: "Retainer Offer", fase: null },
{ name: "Report mensile", unit_price: 100, category: "Retainer Offer", fase: null },
{ name: "CRO - analisi UX (hotmap, punti di drop)", unit_price: 200, category: "Retainer Offer", fase: null },
{ name: "CRO - implementazione miglioramenti", unit_price: 300, category: "Retainer Offer", fase: null },
{ name: "Call di Mentorship/Consulenza/allineamento/revisione", unit_price: 800, category: "Retainer Offer", fase: null },
{ name: "Art direction su direzione da prendere (consulente strategico interno disponibile 24/7)", unit_price: 2000, category: "Retainer Offer", fase: null },
{ name: "Extra landing (prezzo singolo per pompare prezzo)", unit_price: 3000, category: "Retainer Offer", fase: null },
{ name: "SEO avanzato (Blog post)", unit_price: 400, category: "Retainer Offer", fase: null },
];
async function run() {
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
console.error("DATABASE_URL environment variable is required");
process.exit(1);
}
const client = postgres(databaseUrl);
try {
console.log(`Importing ${SERVICES.length} services from Notion CSV...`);
let inserted = 0;
let skipped = 0;
for (const svc of SERVICES) {
const existing = await client`
SELECT id FROM services WHERE name = ${svc.name} LIMIT 1
`;
if (existing.length > 0) {
console.log(` ↷ exists: ${svc.name}`);
skipped++;
continue;
}
const id = nanoid();
await client`
INSERT INTO services (id, name, unit_price, category, fase, active, migrated_from)
VALUES (
${id},
${svc.name},
${svc.unit_price},
${svc.category},
${svc.fase},
true,
'notion-csv-2026-06-18'
)
`;
console.log(` ✓ imported: ${svc.name}`);
inserted++;
}
console.log(`\n✓ Done — ${inserted} inserted, ${skipped} skipped (already existed)`);
process.exit(0);
} catch (err) {
if (err instanceof Error) {
console.error("Import failed:", err.message);
} else {
console.error("Import failed:", err);
}
process.exit(1);
} finally {
await client.end();
}
}
run();