TIP TOP - System Architecture & Technical Overview
Version: 2026-05-19
Purpose: Reference document for safe, accurate, and fast future development
Status: Production system — read this before touching anything
1. System Purpose
What the Platform Does
TIP TOP is an event management platform for an Israeli event management company ("TIP TOP - ניהול אירועים"). It manages the full lifecycle of an event: from first client inquiry → quotation → signing → payment → event preparation → supplier management → post-event receipts and client portal access.
Main Business Goals
Allow staff to create and manage quotations, events, clients, ushers, and receiptsGive clients a secure self-service portal to sign contracts, complete tasks, view documents, and track their eventManage deputy managers (field staff) who handle event files and collect supplier signaturesAutomate event-related reminders, calendar invites, and SMS notificationsMain User Types
| Role | Access | Key Pages |
|---|
| Admin | Full system access | Dashboard, Events, Quotations, Clients, Receipts, SystemSettings, UserManagement |
| Staff | Role-based page permissions | Dashboard, Events, Quotations (limited) |
| Client | Client portal only | ClientDashboard, ViewQuotation, DepositPayment |
| Deputy Manager | Field-only page | DeputyManager (event file packages) |
| Supplier | No login required | Direct link to sign supplementary quotations via /SignSupplementaryQuotation |
Authentication Architecture
NOT base44 native auth. Uses a custom localStorage-based auth system.Login via /AccessLogin — phone + password for staff, phone-based OTP for clients.userRole, staffMember, clientPhone, clientName, quotationId stored in localStorage.layout.jsx checks currentPageName === 'AccessLogin' to suppress the inactivity logout component.InactivityLogout component auto-logs out inactive staff sessions.
2. Main Modules
2.1 Client Portal (/ClientDashboard)
Purpose: Self-service portal for clients to track their event, sign quotations, complete tasks, view receipts and documents.
Main Files:
pages/ClientDashboard.jsx — monolithic, ~1000 lines — DO NOT REBUILDcomponents/client/MoreServicesTab.jsxcomponents/client/SupplierReceiptsTab.jsxcomponents/client/TermsReacceptanceDialog.jsxcomponents/ProgressStepper.jsxEntities Involved: Quotation, ClientTask, ClientFile, ClientActivity, Receipt, SupplementaryQuotation, Event, Client, CompanySettings, Service
Critical Flows:
Client authenticates via SMS OTP → clientPhone in localStorageQuotations fetched by client_phoneIf no approved quotation → shows unsigned quotation with "sign" CTAIf approved quotation → full dashboard with tabsIf event has passed → shows post-event view (thank you, event summary, receipts, review link)Real-time subscriptions on Quotation, ClientTask, Receipt, Event, DeletedEventAuto-kicks client from portal if quotation or event is deleted (redirects to login)Risks if Modified:
Any change to subscription logic or clientPhone normalization can break real-time updatesThe phone normalization .replace(/[-\s]/g, '') is used throughout — must be consistentThe needsTermsReacceptance = false is **intentionally hardcoded** — do not restore the logic without testingThe isEventPassed flag gates many tabs — breaking it breaks the whole post-event flow
2.2 Quotation System
Purpose: Create, send, and manage price quotations for clients. Clients sign them; admin approves them.
Main Files:
pages/Quotations.jsxcomponents/quotations/QuotationForm.jsxcomponents/quotations/QuotationCard.jsxcomponents/quotations/QuotationPDF.jsxpages/ViewQuotation.jsx — client-facing signing pagepages/AdminViewQuotation.jsx — admin viewEntities: Quotation, Service, Client, Event, CompanySettings
Critical Fields on Quotation:
approved (boolean) — set by admin; gates full client dashboard accesssigned_date — set when client signs; triggers notifyAdminQuotationSignedcalendar_invite_sent + google_calendar_event_id — deduplication for Google Calendarlegacy_terms (boolean) — marks old contracts; shows special legacy message in TermsTabaccess_code — one-time code for client to access their quotationFlow:
Admin creates quotation → sets access_code → sends link to clientClient opens /ViewQuotation?id=... → views terms → signs → signed_date setAutomation triggers notifyAdminQuotationSigned → SMS to adminAdmin approves in Dashboard → approved = trueAutomation triggers createQuotationCalendarInvite → Google Calendar event createdClient now has full dashboard accessRisks if Modified:
approved field is the master gate for the client portal — changing its name/type will break everythingcalendar_invite_sent is checked BEFORE creating a Calendar event to prevent duplicatesChanging quotation signing flow will affect ClientActivity logging
2.3 Receipts Module
Purpose: Issue numbered receipts for payments (deposit, balance, supplementary, credit invoice).
Main Files:
pages/Receipts.jsxcomponents/receipts/ReceiptForm.jsxcomponents/receipts/ReceiptPDF.jsx — generates downloadable PDF client-side using html2canvas + jsPDFcomponents/receipts/SplitReceiptDialog.jsxcomponents/receipts/CreditInvoicePDF.jsxfunctions/reindexReceipts.js — corrects sequential numberingfunctions/splitReceipt.jsCritical Logic:
Receipt numbering uses CompanySettings.last_receipt_number — auto-incremented on each receipt creationreceipt_start_number in CompanySettings defines starting offsetis_credit_invoice field distinguishes credit invoices from regular receiptssplit_from_receipt_id tracks which receipt was splitauto_generated marks system-generated receiptsRisks if Modified:
last_receipt_number in CompanySettings must NEVER be manually edited — always use the reindex functionReceiptPDF depends on the DOM structure of the receipt — if layout changes, PDF output breaksDo not rename receipt_number field — used in display, PDF, and client portal
2.4 Supplier Receipts & Deputy Manager
Purpose: Field staff (deputy managers) collect payment confirmations from suppliers AT THE EVENT by capturing their signature on a tablet.
Flow:
Admin prepares event file package (EventFilePackage) and uploads files (EventFile) for the eventDeputy manager opens /DeputyManager → sees complete packages (is_complete: true)At event: deputy manager opens event file viewer → can add supplier signaturesSupplier signs on-screen → signature saved as private file → SupplierPaymentReceipt createdAdmin publishes receipts → publishReceiptsToClientPortal creates PublishedEventDocument recordsClient sees these in their portal under "Supplier Receipts" tab (only visible after event)Risks if Modified:
SupplierPaymentReceipt.supplier_signature_url stores a **private file URI** (not a public URL)publishReceiptsToClientPortal uses supplier_signature_url as the file_url for PublishedEventDocumentgetEventFileSignedUrl creates temporary signed URLs for private files
2.5 SMS System
Architecture:
Business Event → calls sendSystemSms(trigger_key, dynamic_data) → looks up SmsTemplate by trigger_key → replaces {{placeholders}} with dynamic_data → sends via Active Trail API → logs to SmsLog entity
Key Functions:
functions/sendSms.js — raw SMS sender (phone + message → Active Trail API)functions/sendSystemSms.js — template-based SMS systemfunctions/requestSmsVerificationCode.js — client OTP loginfunctions/verifySmsCode.js — verifies OTP codeProvider: Active Trail (webapi.mymarketing.co.il) using ACTIVE_TRAIL_TOKEN secret.
Sender Name: TIPTOP (hardcoded in both sendSms.js and sendSystemSms.js)
SMS Trigger Keys:
admin_quotation_signed — when client signs quotationadmin_quotation_viewed — when client clicks "view quotation" buttonadmin_task_completed — when client completes a taskevent_prep_72_hour_notification — 72h before event with external managerevent_prep_24_hour_notification — 24h before event with external managerclient_sms_verification — OTP code for client loginRate Limiting on requestSmsVerificationCode:
60-second cooldown between requests10-minute code expiry5 resend maximum before lockoutLockout stored in Client.verification_locked_until
2.6 Automations
| Automation Name | Type | Trigger | Function | Notes |
|---|
| בדיקת תאריך אירוע | Entity | Event create/update | checkEventDateAndNotify | Only fires for events with מנהל אירוע חיצוני service |
| התראה 24 שעות | Scheduled | Daily 03:00 | create24HourEventPrepNotification | Currently FAILING |
| התראה 72 שעות | Scheduled | Daily 03:00 | create72HourEventPrepNotification | Currently FAILING |
| מחיקת משימות לקוח | Entity | Client delete | cleanupClientTasks | Deletes all ClientTask by phone |
| התראה לאדמין - לקוח צפה בהצעה | Entity | ClientActivity create | notifyAdminQuotationViewed | Condition: action_type=view_quotation |
| Event Deleted - Delete Google Calendar | Entity | Event delete | syncEventCalendarInvite | Removes event from Google Calendar |
| Event Date Changed - Sync Calendar | Entity | Event update | syncEventCalendarInvite | Condition: changed_fields contains event_date |
| Quotation Approved - Send Calendar Invite | Entity | Quotation update | createQuotationCalendarInvite | Condition: approved=true AND calendar_invite_sent != true |
| Quotation Signed - Notify Admin | Entity | Quotation update | notifyAdminQuotationSigned | Condition: signed_date was just set |
2.7 Calendar Integration
Provider: Google Calendar via authorized shared connector.
Functions:
createQuotationCalendarInvite — creates event when quotation is approvedsyncEventCalendarInvite — updates/deletes event when event date changes or event is deletedaddEventToStaffCalendars — adds to staff members' calendarssendInterviewCalendarInvite — sends calendar invite for client interviewDeduplication:
Quotation.calendar_invite_sent (boolean) — checked before creatingQuotation.google_calendar_event_id (string) — stored after creation; used for updates/deletes
3. Entity Architecture
Critical Entities
Client
client_phone — primary identifier used everywhere. Format varies: must always use .replace(/[-\s]/g, '') for comparison.verified_email / is_email_verified — email verification stateverified_phone / is_phone_verified — SMS verification stateverification_locked_until — lockout field; set to "" (empty string) to unlockaccepted_general_terms_version — ISO datetime; compared against CompanySettings.general_terms_versionaccepted_service_terms — array of {service_name, accepted_version} objectsNEVER DELETE without running cleanupClientTasks first.Event
contract_id — links to the parent Quotation.id — MASTER JOIN FIELDclient_phone — must match quotation's phone (normalized)service_type — array of service names; drives many conditional featuresassigned_ushers — array of usher IDsevent_summary — nested object {actual_guest_count, summary_notes, completed_at, completed_by}Relationships: Event.contract_id → Quotation.id; Event.id → EventFile.event_idQuotation
approved (boolean) — MASTER GATE for client portal accesssigned_date — triggers admin SMS notificationcalendar_invite_sent + google_calendar_event_id — deduplication for Google Calendaraccess_code — client login keylegacy_terms — marks pre-system contractsdeposit_status — not_paid / paidCompanySettings
general_terms — displayed to all clients in TermsTabgeneral_terms_version — ISO datetime; auto-updated every time general_terms is savedlast_receipt_number — auto-incremented counter; NEVER manually editrenovation_mode — if true, redirects all client portal users to /RenovationModeadmin_notification_phone — phone number for admin SMS alertsCRITICAL: There is always exactly ONE record. Always access via .list()[0]Service
terms_and_limitations — shown to clients in TermsTabterms_version — ISO datetime; auto-updated when terms_and_limitations changesis_active — gates display in MoreServicesTab and TermsTabpredefined_items — default line items for quotation creationReceipt
receipt_number — sequential; driven by CompanySettings.last_receipt_numberis_credit_invoice — boolean; separate PDF template if truesplit_from_receipt_id — tracks split originauto_generated — system-created flagSupplierPaymentReceipt
supplier_signature_url — PRIVATE file URI (not a public URL) — used to generate signed download URLsevent_id — links to Event
4. Financial Logic
Receipt Numbering
CompanySettings.receipt_start_number = N (one-time setup)
CompanySettings.last_receipt_number = increments with each new receipt
New receipt number = receipt_start_number + last_receipt_number offset
Payment Flow
Client signs quotation (deposit amount set on quotation)Admin creates Receipt for deposit → payment_for = 'מקדמה'Admin creates Receipt for balance → payment_for = 'יתרה'Supplementary services → payment_for = 'שירותים נוספים'Credit invoices → is_credit_invoice = true, original_receipt_number set
5. Dangerous Areas / Do-Not-Break List
| Area | Risk | Files |
|---|
| Quotation.approved logic | Gates entire client portal | ClientDashboard, ViewQuotation, Dashboard |
| Receipt numbering (last_receipt_number) | Financial integrity | CompanySettings, Receipts.jsx |
| Phone normalization | Auth, portal, all lookups | AccessLogin, requestSmsVerificationCode, ClientDashboard |
| clientPhone in localStorage | All client portal data | ClientDashboard (every query key) |
| Google Calendar deduplication | Double-booking prevention | createQuotationCalendarInvite, syncEventCalendarInvite |
| Real-time subscriptions + verificationInProgress guard | Portal stability, rate limits | ClientDashboard |
| SupplierPaymentReceipt.supplier_signature_url | Private URI, not public URL | saveSupplierPaymentReceipt, getClientEventDocuments |
| needsTermsReacceptance = false | Intentionally disabled | ClientDashboard |
| CompanySettings single-record assumption | All settings queries | Everywhere that calls .list()[0] |
6. Current Known Issues & Technical Debt
Intentionally Disabled
Terms re-acceptance dialog — needsTermsReacceptance = false hardcoded in ClientDashboard. Disabled to avoid forcing all production clients to re-sign terms.Scheduled Automation Failures
create24HourEventPrepNotification and create72HourEventPrepNotification are FAILING (as of 2026-05-19). Both scheduled at 03:00 daily.Email Largely Replaced by SMS
The sendSystemEmail / EmailTemplate system exists but most notifications have been migrated to SMS.SEND_REAL_EMAILS_ENABLED secret controls email sending — check this before any email testing.ClientDashboard Sensitivity
ClientDashboard.jsx is ~1000 lines with many interconnected states, queries, subscriptions, and conditional rendering. It is EXTREMELY fragile.Rule: minimal, surgical fixes only. Never refactor the whole file at once.Always use find_replace for changes here, never write_file.
7. Recommended Development Rules
Before Any Change
Read the relevant file first — never assume contentCheck if an automation watches the entity being modifiedCheck if a subscription in ClientDashboard depends on this entityAsk: will this change trigger any SMS/email/notification?Making Changes
Use find_replace for existing files — never use write_file on large existing filesOne thing at a time — change one logical thing per sessionTest backend functions with test_backend_function before wiring to UIPhone numbers — always normalize with .replace(/[-\s]/g, '') when comparingFinancial Data
Never manually edit CompanySettings.last_receipt_numberNever modify ReceiptPDF structure without testing the download outputCredit invoices — always check is_credit_invoice flag before processingProduction Safety
No notification/SMS testing in production without disabling the relevant SmsTemplate (is_active = false)No bulk entity operations without confirming no automations are watching that entityTerms changes — currently SAFE (no automated notifications)Quotation approval — TRIGGERS Google Calendar invite (one-time, deduplicated)Quotation signing — TRIGGERS admin SMS notificationArchitecture Rules
Client portal authentication is localStorage-based — not base44 authCompanySettings — always one record, always access via .list()[0]Event-Quotation link is Event.contract_id = Quotation.id — this is the master joinPrivate files (supplier signatures, event files) use signed URLs — never embed raw private URIs in client-facing HTML
*Document generated: 2026-05-19. Update whenever significant architectural changes are made.*