HandyManny Mission Control
Portal Online
--:--:--
Features Built
28 / 30
Tenants Live
2 / 2
API Endpoints
30
DB Models
10
1
Overview
CHAROS Live
Primary Tenant — Trucking Company
Red branding #d93324
U1ST Logistics Live
Second Tenant — Freight Forwarder
Blue branding #1e40af
Stack
Full-Stack TypeScript
Next.js 16 + React 19 + Prisma 6
PostgreSQL 16 + NextAuth v5
Tailwind v4 + TypeScript
Infrastructure
Docker + Traefik HTTPS
VPS: srv1139900 · 31.97.211.9
Auto-SSL via Let's Encrypt
GitHub: chavez684/handymanny
SourceC:\Users\chave\projects\handymanny\
VPS Path/docker/handymanny/ (CHAROS) · /docker/u1st-handymanny/ (U1ST)
Adminchavez684@gmail.com (password in .env)
Auth Rolessuper_admin, admin, dispatcher, customer_admin, customer_viewer
2
Features
28 Built
FeatureDetailsStatus
Multi-Tenant White-Labelbrand.ts config, CSS custom properties, parameterized Docker/seed/deployLive
U1ST Logistics Instanceu1st.handymanny.cloud, blue branding, independent DB, port 3004Live
CHAROS AI ChatbotClaude Haiku 4.5, 5 tools, image upload, rate-limited 20/minLive
Public Chat API (Manny)Public endpoint for handymanny.cloud, no auth, CORS, IP rate limit 10/min, Manny personaLive
Deploy Scripts./deploy.sh charos|u1st, deploy-all.shLive
AuthenticationNextAuth v5, 5 roles: super_admin, admin, dispatcher, customer_admin, customer_viewerLive
DashboardKPI cards, recent activity feed, role-based viewsLive
Shipments MapUnified truck+ocean, Leaflet/OSM, 3-tier geocoding, type-aware popupsLive
Shipment StatusFleet overview, status categories, position dataLive
Shipments CRUDCreate, list, detail pages with container editingLive
Shipment EditFull edit form at /shipments/[id]/edit, type tabs, containers CSVLive
Customer List + FilterServer-side company query, CustomerFilter dropdown on /shipmentsLive
Customer DetailContacts, invite buttons, notification prefs, shipments table, notification logLive
Contact CRUDInline create/edit/delete on customer detail, role-basedLive
Public TrackingToken-based /track/[code], no login requiredLive
Position Tracking5 DB fields (lat/lng/city/state/timestamp), auto-extract on trackLive
Auto-Track (n8n)Hourly 8am-7pm CST Mon-Sat, workflow XEYwLqrwEJwCz39gLive
Archive PanelSlide-in for delivered shipments >2 days old, auto-excluded from listLive
Notification PrefsChannel (email/whatsapp/both), frequency, quiet hoursLive
Settings > UsersFull CRUD, company dropdown, super_admin only with self-protectionLive
Settings > NotificationsAdmin: templates + global log; customer_admin: own prefs + logLive
Platform Invitationsnanoid(32) token, 7-day expiry, public /invite/[token] registrationLive
WhatsApp ClientMeta Cloud API: sendWhatsAppTemplate + sendWhatsAppTextLive
Email Client (Resend)Verified domain handymanny.cloud, DKIM/SPF/DMARC, noreply@handymanny.cloudLive
Notify Endpoint/api/shipments/[id]/notify — WhatsApp + email, respects quiet hoursLive
InboxInternal message system with parsed dataLive
Webhook ReceiversWhatsApp + n8n webhook endpointsLive
Docker DeployTraefik, PostgreSQL, healthcheck, fix-pw.js baked inLive
GitHub RepoPrivate, security-hardened: no creds, IPs, or deploy scripts committedLive
FeatureNotesStatus
n8n-clientDirect n8n API integration from portalStub
Track Now UILive carrier query button in shipment detail (API works, UI stub)Stub
3
Multi-Tenant Architecture
2 Tenants
Single codebase, branding via env vars (NEXT_PUBLIC_BRAND_*) + CSS custom properties (--brand, --brand-light, --brand-dark).
brand.ts NEXT_PUBLIC_* env CSS vars on <html> Themed UI
TenantURLVPS PathPortColorStatus
CHAROScharos.handymanny.cloud/docker/handymanny/3002 #d93324Live
U1ST Logisticsu1st.handymanny.cloud/docker/u1st-handymanny/3004 #1e40afLive
Key Implementation Details
Brand configsrc/lib/brand.ts — reads NEXT_PUBLIC_BRAND_* with CHAROS defaults
CSS injectionInline style on <html> in src/app/layout.tsx
Build-timeNEXT_PUBLIC_* inlined at npm run build via Docker ARG + ENV
DockerSeparate containers per tenant (independent PostgreSQL, independent app)
SeedParameterized with SEED_COMPANY_* env vars (CHAROS defaults)
Deploy./deploy.sh charos or ./deploy.sh u1st — sets VPS_PATH, CONTAINER, COMPOSE_CMD
Important: NEXT_PUBLIC_* are build-time only. Changing brand env vars requires Docker rebuild, not just restart. Values with spaces MUST be quoted in .env.
4
CHAROS AI Chatbot
Live
ModelClaude Haiku 4.5 (claude-haiku-4-5-20251001)
API routePOST /api/chat — tool loop, rate limit 20/min, image support
Widgetsrc/components/chat/chat-widget.tsx — floating red button, 400x500 panel
Toolssrc/lib/chat-tools.ts — 5 tools with Prisma executor, company-scoped
ScopeCustomers: read-only · Admin/dispatcher: write access
5 AI Tools
ToolPurposeAccess
list_shipmentsList all shipments with filters (type, status, carrier)All roles
get_shipmentGet detailed shipment info by ID or MBLAll roles
search_shipmentsFull-text search across shipments and containersAll roles
create_shipmentCreate new shipment with containersAdmin, Dispatcher
update_shipmentUpdate shipment fields and statusAdmin, Dispatcher
Features
Image Upload
Drag-and-drop document images for AI analysis (e.g., arrival notices, tracking screenshots)
Session Persistence
Chat history saved in sessionStorage, survives page navigation
Multi-Tenant
Deployed to both CHAROS + U1ST with separate Anthropic API keys
5
Shipments Map & Position Tracking
11/14 Mapped
Unified Shipments Map
Leaflet + OpenStreetMap
Truck markers (circle) + Ocean markers (rounded square)
Type filters: Todos / Terrestres / Marítimos
Status filters, type-aware popups
3-Tier Geocoding Pipeline
Phase 1: Hardcoded lookup (80+ ports/cities, longest-match)
Phase 2: Nominatim API (sequential, 1 req/sec)
Phase 3: AI batch via n8n Claude Haiku
Phase 4: Fire-and-forget DB persist
Position Tracking
DB fieldslatitude, longitude, positionCity, positionState, positionAt
Geocodingsrc/lib/geocoding.ts — 80+ hardcoded locations + state abbreviation map + Nominatim fallback
Position extractorsrc/lib/position-extractor.ts — extracts from truck (JSON) and ocean (events) data
Auto-trackn8n cron 0 14-23,0-1 * * 1-6 (8am-7pm CST Mon-Sat), Bearer token auth
AI Geocodingn8n workflow J76GltqEdd731XGY — Claude Haiku, MX/US specialization
Map Components
FilePurpose
fleet-map.tsxMap page shell — type filters, status filters, legend, dynamic import
fleet-map-inner.tsxLeaflet map component (client-only, no SSR, type-aware popups)
map-marker-icon.tsColor-coded DivIcon markers (truck=circle, ocean=rounded square)
mock-fleet-data.tsExports MapTruck interface with type/carrier/mbl/vessel
6
API Routes
31 Endpoints
MethodRoutePurposeAuth
GET/api/shipmentsList shipments (filters: type, status, carrier, companyId, archived)All roles
POST/api/shipmentsCreate shipment with containersAdmin+
GET/api/shipments/[id]Shipment detail with containers & company linksAll roles
PATCH/api/shipments/[id]Update shipment fields, containers, company linkAdmin+
DELETE/api/shipments/[id]Delete shipmentsuper_admin, admin
POST/api/shipments/[id]/trackManual trigger tracking (ocean or truck)Admin+
POST/api/shipments/[id]/notifySend WhatsApp + email to contactsAdmin+
POST/api/shipments/auto-trackBatch auto-track (Bearer token auth)API Key
MethodRoutePurposeAuth
GET/api/customersList customers (paginated, searchable)Admin+
POST/api/customersCreate customerAdmin+
GET/api/customers/[id]Customer detail with contacts & shipmentsAll roles
PATCH/api/customers/[id]Update customerAdmin+
DELETE/api/customers/[id]Soft delete (sets active=false)Admin+
GET/api/customers/[id]/portal-summaryDashboard stats (totals, by category/type, recent)All roles
POST/api/contactsCreate contact for companyAdmin+
PATCH/DEL/api/contacts/[id]Update or delete contactAdmin+
MethodRoutePurposeAuth
GET/POST/api/auth/[...nextauth]NextAuth handlersPublic
GET/api/usersList users (filterable by role)super_admin, admin
POST/api/usersCreate usersuper_admin, admin
PATCH/api/users/[id]Update user (name, role, company, active, password)super_admin, admin
DELETE/api/users/[id]Delete user (self-protection)super_admin
POST/api/invitationsCreate platform invitationAdmin+
GET/DEL/api/invitations/[id]List or revoke invitationsAdmin+
MethodRoutePurposeAuth
GET/api/track/[code]Public tracking (sanitized shipment + containers)Public
GET/api/messagesList messages (pagination, processed filter)Admin+
GET/PUT/api/notificationsList logs / Update preferences (upsert)Varies
GET/api/notifications/preferencesGet notification prefs for companyAll roles
GET/POST/api/webhooks/whatsappWhatsApp verification + incoming messagesWebhook
POST/api/webhooks/n8nn8n tracking update webhookWebhook
POST/api/chatAI chatbot with 5 tools (rate limited 20/min)All roles
POST/api/chat/publicPublic Manny chatbot (no tools, CORS, 10/min IP limit)Public
7
Database Schema
10 Models · 8 Enums
User
id, email, passwordHash, name, role (UserRole), companyId?, active, createdAt, updatedAt
Company
id, name, taxId?, address?, phone?, email?, active, createdAt, updatedAt → users[], contacts[], companyShipments[], notificationPreference?, notificationLogs[], invitations[]
Contact
id, companyId, name, email?, phone?, whatsapp?, roleTitle?, isPrimary, createdAt, updatedAt
Shipment
id, type (ShipmentType), mbl?, ssl?, vessel?, voyage?, pol?, pod?, truckerName?, unitNumber?, trackingUrl?, cuentaEspejoUrl?, platform?, eta?, status?, statusCategory, trackingCode (unique), trackingData (JSON), lastTrackedAt?, latitude?, longitude?, positionCity?, positionState?, positionAt?, createdAt, updatedAt
ShipmentContainer
id, shipmentId, containerNumber, size?, type?, seal?, createdAt
CompanyShipment
id, companyId, shipmentId, role (CompanyShipmentRole), createdAt — @@unique([companyId, shipmentId])
NotificationPreference
id, companyId (unique), channel (NotificationChannel), frequency (NotificationFrequency), quietHoursStart?, quietHoursEnd?, whatsappGroupId?, createdAt, updatedAt
NotificationLog
id, companyId, shipmentId?, channel, template?, status (NotificationStatus), sentAt, createdAt
Message
id, source (MessageSource), sender, subject?, rawContent, parsedData (JSON), shipmentId?, processed, createdAt, updatedAt
PlatformInvitation
id, companyId, contactId?, email, name, role (UserRole), token (unique), status (InvitationStatus), expiresAt, acceptedAt?, invitedBy, createdAt

Enums (8)
UserRole
super_admin, admin, dispatcher, customer_admin, customer_viewer
ShipmentType
ocean, truck
StatusCategory
on_time, delayed, stopped, delivered, pending, expired, not_found
NotificationChannel
whatsapp, email, both
NotificationFrequency
realtime, milestone, daily, exception_only
NotificationStatus
sent, delivered, failed, read
CompanyShipmentRole
consignee, shipper, notify_party
InvitationStatus
pending, accepted, expired, revoked
8
Connected n8n Workflows
Auto-Track Hourly
XEYwLqrwEJwCz39g
Cron 8am-7pm CST Mon-Sat · POST /api/shipments/auto-track
Geocode AI
J76GltqEdd731XGY
Claude Haiku · POST /webhook/geocode-ai
Ocean Orchestrator
AzACxa8tKeisIYss
19 nodes · POST /ocean-track
Truck Tracker
2GF72l5GeWcUYohm
Single truck via headless browser
Shipment Notice Emailer
dH1nNvwPxR3eyGU8
11 nodes · POST /shipment-notice
Batch Tracker
fCVbPtxhdMcroH4P
8 nodes · POST /charos-batch-track
Email Report
ry7zDIJTBXzV1dez
Needs SMTP · POST /charos-email-report
9
Integrations
Resend (Email) Active
Verified domain handymanny.cloud. DKIM/SPF/DMARC. From: noreply@handymanny.cloud. Templates: tracking update + invitation.
WhatsApp (Meta Cloud) Active
sendWhatsAppTemplate + sendWhatsAppText. Quiet hours respected (America/Mexico_City). Needs env vars configured per tenant.
Anthropic API Active
Claude Haiku 4.5 for CHAROS AI chatbot. Separate API keys per tenant. Rate limited 20 req/min.
Ocean Tracker Active
11 carriers via n8n orchestrator + headless browser. 6 verified (ONE, MSC, Yang Ming, Evergreen, HMM, COSCO). 3 blocked.
NextAuth v5 Active
Credentials provider, 5 roles, middleware route protection. Session via HttpOnly cookies.
Evolution API QR Needed
WhatsApp API v2.3.7 on srv1139900 port 8080. Instances need QR re-scan after migration.
10
Planned Modules
Planned
Comercializadora Planned
Mexican Customs 19-Stage Workflow
Booking Pedimento Classification Duties Payment Dispatch Inspection Release Delivery
Requires logistics domain expert before development.
Certificado de Molino Planned
EN 10204 Steel Mill Certificates
Standard: EN 10204 Type 3.1
Covers: Chemical composition, mechanical properties, dimensions, heat treatment
Features: Heat number traceability, PDF generation, quality conformance
11
Key Files Reference
FilePurpose
src/lib/brand.tsBrand config (reads NEXT_PUBLIC_BRAND_* env vars, CHAROS defaults)
prisma/schema.prisma10 models, 8 enums — core data layer
prisma/seed.tsParameterized with SEED_COMPANY_* env vars
src/middleware.tsRoute protection, role-based access control
src/components/layout/sidebar.tsxNav items, brand.* imports for name/logo/footer
src/lib/chat-tools.tsCHAROS AI tool definitions (5 tools) + Prisma executor
src/app/api/chat/route.tsChat API — Claude Haiku, tool loop, rate limit, image
src/app/api/chat/public/route.tsPublic chat API — Manny persona, no auth, CORS, IP rate limit
src/components/chat/chat-widget.tsxFloating chat widget (red button, 400x500 panel, drag-and-drop)
src/lib/geocoding.ts80+ hardcoded ports/cities, state abbrev map, Nominatim fallback, AI batch
src/lib/position-extractor.tsExtracts position from truck (JSON) and ocean (events) data
src/lib/email-templates.tsUses brand.color directly (CSS vars don't work in emails)
src/lib/email-client.tsDynamic from using brand.name + brand.tagline
src/components/fleet/fleet-map.tsxMap page shell — type filters, status filters, legend
src/components/fleet/fleet-map-inner.tsxLeaflet map (client-only, no SSR, type-aware popups)
src/components/shipments/archive-panel.tsxSlide-in archive for delivered shipments >2 days
fix-pw.jsPassword fix script (admin roles only), runs after rebuild
deploy.shMulti-tenant VPS deploy (./deploy.sh charos|u1st)
deploy-all.shDeploys both tenants sequentially
12
Roadmap
Phase 1 — Core Portal Done
Auth (5 roles), Dashboard, Shipments CRUD
Customer list + detail + contacts CRUD
Public tracking, webhooks, inbox
Docker deploy + Traefik HTTPS
Position tracking + auto-track hourly workflow
Unified Shipments Map (truck + ocean)
Phase 2 — Multi-Tenant & Comms Done
Multi-tenant white-label architecture (brand.ts)
U1ST Logistics tenant deployment
CHAROS AI chatbot (Claude Haiku, 5 tools, image upload)
WhatsApp + Email client (Resend verified)
Platform invitations + Settings
Archive panel, customer filter, shipment edit
Phase 3 — Domain Modules Next
Comercializadora module (19-stage Mexican customs)
Certificado de Molino (EN 10204)
Track Now live carrier query UI
n8n-client direct integration
WhatsApp notifications via Evolution API
Phase 4 — Mobile & Scale Future
Flutter mobile app (customer tracking companion)
Push notifications
Offline mode
Additional tenants onboarding
13
Known Issues & Fixes
IssueImpactMitigationStatus
Password hash corruption on Docker rebuildLogin fails after deployfix-pw.js baked into Docker image, runs in deploy.shFixed
NEXT_PUBLIC_* build-timeBrand changes need full rebuildDocker ARG + ENV in builder stageBy Design
.env quotingValues with spaces break bash sourceAlways quote: SEED_COMPANY_NAME="U1ST Logistics"Documented
3 ocean carriers blockedOOCL/Wan Hai/ZIM unusableAPI aggregator ($350-720/mo) or scraping specialistOpen
Seed trucks expired4 seed trucks show as expiredstatus_category=expired, auto-track skips themAccepted
Batch Tracker inactiveNo bulk truck trackingNeeds activation + SMTP for email reportPending
Shipment Notice MSC bugMSC items dropped in mixed batchesAdded Merge node (append, 3 inputs) before NormalizeFixed
14
Decision Log
DateDecisionRationaleAlternatives
2026-02Next.js 16 + Prisma 6Latest stable, TypeScript-first, excellent DXExpress+Sequelize, Remix
2026-02Multi-tenant via env varsSingle codebase, separate Docker containers per tenantShared DB with org_id, microservices
2026-02Resend for transactional emailBetter deliverability, verified domain (DKIM/SPF)Gmail SMTP, SendGrid
2026-02Meta Cloud API for WhatsAppDirect API, no middleware dependencyEvolution API only
2026-02Claude Haiku for chatbotLow cost, fast, good enough for shipment queriesGPT-4o-mini, local LLM
2026-02Leaflet/OSM for mapsFree, open-source, no API key requiredGoogle Maps ($), Mapbox
2026-02Hourly auto-track via n8n cronPassive position updates, no manual tracking neededWebhook-triggered only
2026-023-tier geocodingHardcoded for speed, Nominatim fallback, AI for edge casesGoogle Geocoding API ($), single source
2026-02Decouple MC from Next.jsStatic HTML = simpler, faster, independent deployKeep as Next.js route