Skip to content

devrodri-com/lem-box-sistema-v2

Repository files navigation

📦 LEM-BOX System V2

Logistics & shipping management system built with Next.js 15 + Firebase
Enables end‑to‑end management of packages, boxes, and shipments in the Miami warehouse, with access for Admin/Staff, Clients, and Partners (multi‑client view).

Tests Firebase Next.js License

✨ Highlights

  • Full admin + client portal (Next.js App Router)
  • New Partner area (/partner/*) with multi-client visibility (trackings/boxes/shipments/clients) scoped to assigned clients.
  • Role-based security (Firestore Rules tested with Emulator)
  • Vitest suite with integration, unit & rule tests
  • 6×4 label generation (jsPDF) + dual weight handling (lb/kg)
  • Mobile-first, accessible, bilingual-ready

🚀 Core technologies

  • Next.js 15 (App Router, TypeScript, TailwindCSS)
  • Firebase
    • Authentication (Email/Password)
    • Firestore Database
    • Storage (package and document images)
  • React Hook Form + Zod for forms
  • ZXing for scanning tracking barcodes

🧭 Architecture (high level)

  • Next.js (App Router) as frontend + server (routes /admin/*, client portal under /mi/*, and partner area under /partner/*).
  • Firebase Auth manages the session (email/password).
  • Firestore stores entities (users, clients, inboundPackages, boxes, shipments, trackingAlerts).
  • Storage stores photos (packages/documents), accessed via URL.
  • jsPDF (CDN) generates 6×4 PDFs for labels.
  • Tailwind defines color tokens and utility components.

Flow (summary)

Received → Consolidated (box) → Shipped → In transit → At destination.

  • Admin/Staff: enters packages, builds boxes, creates shipments, and changes statuses.
  • Partner: manages their assigned clients (create/edit/activate/deactivate) and sees trackings/boxes/shipments for all assigned clients.
  • Client: sees their own trackings/boxes/shipments and edits their data.

🎨 Branding

Official palette:

  • Primary green: #005f40
  • Secondary orange: #eb6619
  • Dark orange (shadow): #cf6934
  • White for contrast and backgrounds.

Official logo available in /public. Use green as primary and orange for CTAs.

📁 Folder structure (summary)

src/
  app/
    admin/
      ingreso/
      preparado/
      estado-envios/
      historial-tracking/
      clientes/
      usuarios/
    mi/
      layout.tsx
      page.tsx        (redirects to /mi/historial)
      historial/
        page.tsx
      cajas/
        page.tsx
      envios/
        page.tsx
      cuenta/
        page.tsx
    partner/
      layout.tsx
      page.tsx
      historial/
        page.tsx
      cajas/
        page.tsx
      envios/
        page.tsx
      clientes/
        page.tsx
        [id]/
          page.tsx
    acceder/
    registro/
  components/
    RequireAuth.tsx
    AdminNav.tsx
    PartnerNav.tsx
    ConditionalNav.tsx
    PartnerContext.tsx
    clients/
      ClientsManager.tsx
      ClientProfile.tsx
    boxes/
      BoxDetailModal.tsx
      useBoxDetailModal.ts
    ui/
      StatusBadge.tsx
      BrandSelect.tsx
      icons.tsx
  lib/
    firebase.ts
    printBoxLabel.ts
    weight.ts
    utils.ts   (chunk helper)

⚙️ Features

Admin panel

  • Package intake: tracking (hardware scanner or manual), client selection, weight lb↔kg with automatic conversion, photo (camera or file) with compression; same‑day listing.
  • Load preparation: search by client, build boxes (1 box = 1 client), CSV export; table with sticky header, zebra, accessible focus; dual weight X lb / Y kg.
  • Shipments: create shipment (saves clientIds), change status (Open → In transit → At destination → Closed), expand boxes, print 6×4 label.
  • Tracking history: filters; BOX: # modal with Type + Apply, Reference + Print label; items with dual weight and total weight.
  • Clients: CRUD with 20‑column layout: Code (read‑only), Name, DocType/DocNumber, Country/State/City, Address/Postal code, Phone/Email/Extra email.

Partner area (/partner)

  • Historial (multi-client): received trackings for all assigned clients (read-only).
  • Cajas (multi-client): boxes for all assigned clients + detail modal.
  • Envíos (multi-client): shipments derived from assigned clients’ boxes.
  • Clientes: uses the same management UI as admin but scoped and with restricted actions.
    • Can create/edit/activate/deactivate clients.
    • Cannot delete clients.
    • Cannot reset password or change managerUid.
  • Navigation keeps Partner navbar across sections.

Client portal (/mi)

  • History: their trackings (date, tracking, carrier, weight lb/kg, status, photo).
  • Boxes: their boxes and detail (items with dual weight).
  • Shipments: their shipments (visible if their clientIdshipment.clientIds).
  • Account: edit Name, Phone, Country/State/City, Address, Postal code, Extra email, DocType/DocNumber. Code and Email are read‑only.
  • Report tracking: creates a document in trackingAlerts for admin to handle.
  • Account linking: /mi requires users/{uid}.clientId to be present. If the user is not linked yet, the portal shows a "not linked" message and blocks access until the account is linked by staff.
  • Bulk bootstrap (migration): legacy clients imported into Firestore can be linked to Firebase Auth using the superadmin tools (see Data maintenance below).

Internally, the client portal is split into nested routes: /mi/historial, /mi/cajas, /mi/envios, and /mi/cuenta, all sharing a common layout that handles authentication, header, and tabs.

6×4 label printing (horizontal)

  • 6×4 PDF generated with jsPDF (CDN) in src/lib/printBoxLabel.ts.
  • Layout: #REFERENCE at top (large auto‑fit text), two columns below #CLIENT and #BOX. No weight.

🔒 Security & access

  • RequireAuth with requireAdmin protects all /admin/* routes.
  • Navigation: AdminNav (admin/staff), PartnerNav (partner), and a ConditionalNav wrapper at the root layout to ensure partners never see /admin/* links.
  • Firestore rules (effective summary):
    • users: self or staff.
    • clients: client reads/updates basic fields of their own client; staff full. code/email read‑only for client.
    • inboundPackages/boxes: client only those with their clientId.
    • shipments: readable if clientIdshipment.clientIds.
    • trackingAlerts: client create, staff read/manage.
  • Post-login routing is role-based: partner_admin → /partner, client → /mi, staff → /admin/ingreso (with Firestore role reconciliation to handle stale claims).
  • Partner scoping: data is filtered to the partner’s assigned clients using users/{uid}.managedClientIds and/or clients.managerUid == uid (fallback where needed).
Firestore rules (suggested)
rules_version = '2';
service cloud.firestore {
  match /databases/{db}/documents {
    function hasAuth() { return request.auth != null; }
    function userDoc() { return hasAuth() ? get(/databases/$(db)/documents/users/$(request.auth.uid)) : null; }
    function role() { return hasAuth() ? (userDoc().data.role != null ? userDoc().data.role : (request.auth.token.role != null ? request.auth.token.role : null)) : null; }
    function clientId() { return hasAuth() ? (userDoc().data.clientId != null ? userDoc().data.clientId : (request.auth.token.clientId != null ? request.auth.token.clientId : null)) : null; }
    function isSuperAdmin() { return role() == 'superadmin' || request.auth.token.superadmin == true; }
    function isAdmin() { return role() == 'admin' || request.auth.token.admin == true; }
    function isStaff() { return isSuperAdmin() || isAdmin(); }
    function isOwner(cid) { return clientId() != null && clientId() == cid; }

    match /users/{uid} {
      allow read:   if isStaff() || (hasAuth() && (uid == request.auth.uid || resource.data.uid == request.auth.uid));
      allow create: if hasAuth() && (uid == request.auth.uid || request.resource.data.uid == request.auth.uid);
      allow update: if isStaff() || (hasAuth() && (uid == request.auth.uid || resource.data.uid == request.auth.uid));
      allow delete: if isSuperAdmin();
    }
    match /clients/{id} {
      allow read: if isStaff() || isOwner(id) || (hasAuth() && resource.data.email == request.auth.token.email);
      allow update: if isStaff() || ( isOwner(id) && resource.data.diff(request.resource.data).changedKeys().hasOnly(['name','phone','country','state','city','address','emailAlt','postalCode','docType','docNumber']) );
      allow create, delete: if isStaff();
    }
    match /inboundPackages/{inbId} {
      allow read: if isStaff() || isOwner(resource.data.clientId);
      allow create, update, delete: if isStaff();
    }
    match /boxes/{boxId} {
      allow read: if isStaff() || isOwner(resource.data.clientId);
      allow create, update, delete: if isStaff();
    }
    match /shipments/{id} {
      allow read: if isStaff() || (clientId() != null && clientId() in resource.data.clientIds);
      allow write: if isStaff();
    }
    match /trackingAlerts/{id} {
      allow create: if hasAuth() && request.resource.data.uid == request.auth.uid;
      allow read, update, delete: if isStaff();
    }
    match /{document=**} { allow read, write: if false; }
  }
}

🧪 Testing & QA Automation

LEM‑BOX V2 includes a complete automated testing suite to ensure functional accuracy, data integrity, and rule enforcement across the system.

Testing stack

  • Vitest for unit, integration, and UI component tests.
  • Firebase Emulator Suite for Firestore Rules validation.
  • Playwright for end‑to‑end (E2E) browser automation.

Coverage

  • Unit & integration: services (userService, utilities like formatDate, weight).
  • UI: visual and DOM interaction tests (ContactButton, smoke tests).
  • Firestore Rules: verified with Emulator (users, clients, boxes, inboundPackages, shipments).
  • E2E: login, admin panel access, and client portal flow.

All automated tests currently pass successfully (pnpm test:all ✅).

Test scripts

pnpm test         # Unit / integration / UI
pnpm test:rules   # Firestore rules (Emulator)
pnpm test:all     # Full suite (with Emulator)
pnpm e2e          # Playwright E2E

🧩 UI conventions

  • CTAs: orange #eb6619; secondary with border and green focus #005f40.
  • Status: StatusBadge (Received/Consolidated; Open/In transit/At destination/Closed).
  • Tables: sticky header, subtle zebra, tabular-nums, clear hover.
  • Weights: always X lb / Y kg (util fmtWeightPairFromLb).
  • Accessibility: visible focus, role="tablist/tab", aria-current in steppers.
  • Large lists: history pages use pagination (e.g., 25 per page) and token-based search to avoid loading all documents at once.

🧱 Firestore indexes

  • inboundPackages: composite clientId ASC, receivedAt DESC (for where(clientId) + orderBy(receivedAt)).
  • inboundPackages: (token search) composite indexes may be required for:
    • managerUid ASC, trackingTokens ARRAY_CONTAINS_ANY, receivedAt DESC
    • managerUid ASC, clientTokens ARRAY_CONTAINS, receivedAt DESC (create the exact composite suggested by Firestore when prompted).
  • boxes: single index by clientId.
  • (Optional) shipments: by status/country/type per admin listing needs.

🗃️ Collections (summary)

  • users/{uid}: uid, email, displayName, clientId, managedClientIds:string[], termsAcceptedAt, lang:"es", role:"client"|"admin"|"superadmin"|"partner_admin".
  • clients/{id}: code, name, email, phone, country, state, city, address, emailAlt?, postalCode?, docType?, docNumber?, activo, createdAt, managerUid?.
  • inboundPackages/{id}: tracking, carrier('UPS'|'FedEx'|'USPS'|'DHL'|'Amazon'|'Other'), clientId, weightLb:number, photoUrl?, status('received'|'boxed'|'void'), receivedAt.
  • boxes/{id}: code, clientId, type('COMERCIAL'|'FRANQUICIA'), country, itemIds:string[], weightLb:number, status('open'|'closed'), shipmentId?:string|null, createdAt?.
  • shipments/{id}: code, country, type('COMERCIAL'|'FRANQUICIA'), status('open'|'shipped'|'arrived'|'closed'), boxIds:string[], clientIds:string[], openedAt?, arrivedAt?, closedAt?.
  • trackingAlerts/{id}: uid, clientId, tracking, note?, createdAt.

🔑 Roles

  • SuperAdmin: full access, user/partner management, can delete.
  • Admin: full operational access.
  • Operador: intake + box building (staff).
  • Partner (partner_admin): multi-client view + client management for assigned clients; restricted from staff-only modules.
  • Client: single-client portal under /mi.

▶️ Local development

Prerequisites

  • pnpm is recommended (the repo includes pnpm-lock.yaml).
  • Node.js 18.17+ (or Node 20+) to match Next.js 15 requirements and typical Vercel defaults.
  1. Clone the repo and enter the folder:

    cd /Users/lolo/PROYECTOS/lem-box-sistema-v2
  2. Install dependencies:

    pnpm install
  3. Create .env.local with Firebase credentials:

    # Client SDK (required)
    NEXT_PUBLIC_FIREBASE_API_KEY=xxx
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=xxx
    NEXT_PUBLIC_FIREBASE_PROJECT_ID=lem-box-sistema-v2
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=xxx
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=xxx
    NEXT_PUBLIC_FIREBASE_APP_ID=xxx
    
    # Firebase Admin SDK (required for /api/admin/*)
    FIREBASE_PROJECT_ID=lem-box-sistema-v2
    FIREBASE_CLIENT_EMAIL=xxx@xxx.iam.gserviceaccount.com
    FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

    Notes:

    • FIREBASE_PRIVATE_KEY must preserve newlines (\n).
    • Without the Admin SDK vars, /api/admin/* will fail in deploy.
  4. Start the dev server:

    pnpm dev
  5. Open http://localhost:3000.

🧪 Useful scripts

  • pnpm dev - development mode
  • pnpm build - production build
  • pnpm start - start local build
  • pnpm lint - linter
  • pnpm format - code formatting

📦 Deploy

The project will be deployed on Vercel, connected to the main repository.
Backend services managed with Firebase (Firestore, Auth, Storage).


📝 Roadmap

  • Login with Firebase Auth.
  • Package intake (tracking, weight, photo).
  • Box building (Box Builder) + CSV export.
  • 6×4 PDF labels (jsPDF, CDN).
  • Client portal (MVP: History, Boxes, Shipments, Account, Report tracking).
  • Role‑based security (RequireAuth + effective Firestore rules).
  • Rates and reports.
  • Hybrid scanner (BarcodeDetector + ZXing) with haptics/sounds.
  • Sub‑clients (managedClientIds) with view selector.
  • Usage telemetry/analytics.
  • Offline‑first for intake.

🧵 Work streams

  • A) Admin panel + Client portal: consolidation, shipments, 6×4 labels, consistent UI/UX, dual weight.
  • B) Data maintenance: backfill of shipments.clientIds (legacy shipments) + indexes.
  • C) Future: rates/reports, hybrid scanner, sub‑clients, analytics.

✅ QA checklist (quick)

  • Intake: scan tracking, take/upload photo, lb↔kg conversion.
  • Preparation: create box, add packages, CSV export, 6×4 label.
  • Shipments: create, add boxes, change status, expand boxes.
  • History: open box modal, edit reference, print label.
  • Client portal: tabs History/Boxes/Shipments/Account, edit data, report tracking.
  • Access: admin does not fall into /mi; client cannot access /admin/*.

🖨️ 6×4 printing - notes

  • Thermal printers: horizontal orientation, None margins, 100% scale.
  • If the PDF opens blank: reload jsPDF (CDN) or disable blockers.
  • Long references: text size auto‑adjusts.

♿ Accessibility (checklist)

  • Visible focus on all controls.
  • aria-current="step" in steppers; role="tablist/tab" in tabs.
  • Touch targets ≥ 44px on buttons and interactive cells.

🧰 Code conventions

  • TypeScript with a core-strict lint policy: no-explicit-any is error in src/components/** and src/app/partner/**, and warn in legacy areas (admin/mi/api/tests/lib).
  • Pure components, no side‑effects on render.
  • Commit style: Conventional Commits (feat:, fix:, chore:…).

🧯 Operational notes

  • If Next.js build/dev shows missing .next artifacts, clear cache: rm -rf .next node_modules/.cache.
  • Partner does not require label printing; label printing is for staff workflows.

🧯 Data maintenance (admin tools)

  • Bootstrap legacy clients: /api/admin/bootstrap-all-clients links Firestore clients to Firebase Auth users and creates/updates users/{uid} docs. Intended as a one-time migration step.
  • Duplicate client codes:
    • Detect: /api/admin/detect-duplicate-codes
    • Fix (dry-run + apply): /api/admin/fix-duplicate-codes After fixing, all new client creation goes through server endpoints that guarantee unique codes.
  • Reindex search tokens: admin utilities exist to backfill trackingTokens / clientTokens for legacy inboundPackages so global search works without loading all rows at once.

🚀 Release checklist

  • Firestore rules published.
  • shipments.clientIds populated (legacy shipments).
  • Indexes created (see Firestore indexes section).
  • Full smoke test of admin and client flows.

🌐 Portfolio

Project: portal.lem-box.com
Repository: github.com/devrodri-com/lem-box-sistema-v2

LEM-BOX V2 is a modern logistics platform built with performance, accessibility, and data security in mind.


📤 Data migration (final phase)

  • Source: Current system MySQL database (tracking.users).
  • Status: Migration deferred until the end of the development sprint.
  • Safe procedure:
    • Create a snapshot of the Droplet in DigitalOcean.
    • Connect to the database in read‑only mode.
    • Export users table to CSV (/root/users.csv).
    • Download and then import into Firestore via script.
  • Policy: No production changes until the new system is validated.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages