MolinoPro

tally-integration-startert_akill

Master Codebase Guidebook
Markdown + HTML Dev-Docs Renderer - Frontend Client Module

Default Index
Open README.md
Root: README.mdintegrations
Milestones

Perfect — thanks for pasting the article. This actually confirms (and strengthens) the architectural decisions we already sketched, and it lets us make a few very concrete, high-leverage choices for your platform.

Below is a clean synthesis + execution plan, written so you can: • copy sections as .txt • implement one file at a time • reuse your existing documents2 + trips + assistant stack • avoid dead ends or duplicated systems

I’ll be explicit about what stays, what is deprecated, and what is added.

🧠 KEY REALISATION (VERY IMPORTANT)

You now have three generations of the same idea: 1. Apps Script era Sheets → Docs → deterministic population 2. documents2 era TripBuilder → LineItems → Document sections 3. Tally + Next App Router era External structured input → internal mutation

👉 They are not competing systems. 👉 They are different input adapters into the same core pipeline.

Your mistake would be trying to merge them. Your strength is to normalize them.

🧩 FINAL CANONICAL PIPELINE (LOCK THIS IN)

[Tally | In-app Form | AI Assist | Sheet Import] ↓ Normalized Draft (TripDraftData) ↓ Pricing Engine ↓ LineItems ↓ documents2 (TripOffer template) ↓ Optional AI refinement

📌 Everything feeds TripDraftData 📌 Everything ends in documents2

This matches exactly how your Apps Script worked — just cleaner.

1️⃣ DECISION: TALLY INTEGRATION MODE (FINAL)

Based on the article and your needs, the correct mode for you is:

❌ NOT webhooks ❌ NOT cron jobs ✅ Client-side Tally.openPopup + onSubmit

Why this is perfect for your app: • You already have auth context • You already have user, project, space • You can attach entityId, projectId, spaceId • You can choose which document or trip to mutate

This is strictly superior to the webhook-only approach.

2️⃣ WHAT TALLY IS IN YOUR SYSTEM

Let’s name it clearly:

Tally = External Draft Input Adapter

It does not: • generate documents • apply pricing • create offers directly

It only: • collects structured answers • submits them into your app context • creates or updates a draft entity

3️⃣ NEW MODULE: tally/ (VERY SMALL)

You only need 3 files to start.

📁 app/lib/tally/types.ts

export type TallyFieldAnswer = { id: string; title: string; type: string; answer: { value?: string; values?: string[]; } | null; };

export type TallyPayload = { fields: TallyFieldAnswer[]; submissionId: string; };

📁 app/lib/tally/mapTallyToTripDraft.ts

This is the equivalent of your Apps Script _getRutaData().

import type { TripDraftData } from "@/app/(pages)/documents2/types/DocTripSection.types"; import type { TallyPayload } from "./types";

export function mapTallyToTripDraft(payload: TallyPayload): TripDraftData { const get = (label: string) => payload.fields.find(f => f.title === label)?.answer?.value ?? "";

return { id: crypto.randomUUID(), title: get("Trip title"), pax: Number(get("Participants") || 1), startDate: get("Start date"), endDate: get("End date"), style: get("Trip style") as any, cities: [], // populated later or via follow-up }; }

📌 This mirrors your Sheet-cell extraction logic.

📁 app/api/tally/route.ts

This becomes your single intake point.

import { NextResponse } from "next/server"; import { getCurrentUser } from "@/lib/auth"; import { mapTallyToTripDraft } from "@/app/lib/tally/mapTallyToTripDraft"; import prisma from "lib/prisma";

export async function POST(req: Request) { const payload = await req.json(); const user = await getCurrentUser();

if (!user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); }

const tripDraft = mapTallyToTripDraft(payload);

const doc = await prisma.document.create({ data: { type: "trip-offer-v1", ownerId: user.id, data: { tripSection: { kind: "trip", tripData: tripDraft, }, }, }, });

return NextResponse.json({ ok: true, documentId: doc.id, }); }

📌 This is your Sheets → Doc replacement.

4️⃣ CLIENT-SIDE TALLY TRIGGER (GLOBAL OR CONTEXTUAL)

This matches the article but with entity awareness.

📁 components/tally/TallyTripPopup.tsx

"use client";

export default function TallyTripPopup({ projectId, spaceId, }: { projectId?: number; spaceId?: number; }) { const open = () => { // @ts-ignore window.Tally.openPopup("YOUR_FORM_ID", { layout: "modal", width: 600, overlay: true, hiddenFields: { projectId, spaceId, }, onSubmit: async (payload: any) => { await fetch("/api/tally", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); }, }); };

return ( <button onClick={open} className="btn"> 🧾 Create Trip from Form </button> ); }

📌 This is exactly where Tally shines for your users.

5️⃣ HOW THIS CONNECTS TO documents2 (CRITICAL)

When the document opens: • tripSection.tripData already exists • TripBuilder loads immediately • Pricing can be computed • LineItems inserted • Offer letter generated

This replaces 100% of: • Apps Script doc clearing • Marker logic • Manual copying

6️⃣ WHERE AI FITS (AND WHERE IT DOES NOT)

❌ AI should NOT: • parse Tally payloads • decide pricing • build line items

✅ AI SHOULD: • write the intro letter • rewrite itinerary paragraphs • explain options • generate variants

You already have everything needed for this via: • runDevLayerAction • personas • contexts • document sections

7️⃣ OPTIONAL: AI-ASSISTED FORM FILLING (FUTURE)

Later, you can add: • “✨ Fill this form for me” • AI pre-fills Tally hidden fields • Or fills in-app forms

But this is after your deterministic pipeline is stable.

8️⃣ YOUR MIGRATION IS 100% CONSISTENT

Let me say this clearly:

Your Apps Script logic was excellent. You are not abandoning it — you are porting it.

The mental model is unchanged: • structured input • deterministic generation • optional AI polish

You are now doing it: • with Prisma • with versioned documents • with reusable sections • with real product potential

🧾 WHAT YOU SHOULD SAVE AS .txt NOW 1. This entire answer 2. mapTallyToTripDraft.ts 3. trip-offer-v1 template 4. populateTripOfferFromTrip.ts 5. TallyTripPopup.tsx

That’s a 2–3 week roadmap, clearly scoped.

When you’re ready, next good prompts would be: • “Generate full trip-offer-v1 template + registry entry” • “Generate AI-in-document assistant section” • “Generate Space builder cloned from Project”