Skip to content

Latest commit

 

History

History
249 lines (179 loc) · 6.43 KB

File metadata and controls

249 lines (179 loc) · 6.43 KB

Developer Guide — GoNow Logistics Planner

Detailed setup, workflow, conventions, and deployment reference.


Prerequisites

Tool Version Notes
Node.js v22 See .nvmrc
Firebase CLI Latest npm install -g firebase-tools
Git Any recent

Setup

1. Clone and install

git clone https://github.com/scriptbuzz/gonow-app.git
cd gonow-app
npm install

2. Configure environment

cp .env.example .env

Fill in the values from:

  • Firebase Console → Project Settings → Your Apps → Web App config
  • Google Cloud Console → APIs & Services → Credentials (Maps API key)

3. Start the dev server

npm run dev        # http://localhost:3000

Daily Workflow

git pull origin main   # Always pull first
npm install            # Install any new dependencies
npm run dev            # Start dev server

Or use the sync script (checks Node version, pulls, installs):

npm run dev-sync

Project Structure

gonow-app/
├── components/         React components (PascalCase)
│   ├── App/            AppHeader, Sidebar
│   ├── RouteEditor/    Route editor sub-components
│   ├── UI/             Reusable UI primitives
│   ├── reports/        Report sub-components
│   └── app-status/     System health sub-components
├── contexts/           React Context providers
├── hooks/              Custom React hooks
├── services/           Firebase data access layer
├── functions/          Cloud Functions (Node 22, TypeScript)
│   └── src/            Function source files
├── scripts/            Dev automation (deploy, seed, test runner)
├── tests/              Vitest unit/component tests
├── cypress/            Cypress E2E tests
├── locales/            i18n JSON files (en.json, ar.json)
├── docs/               Architecture, PRD, data model
├── types.ts            All TypeScript types and enums
├── constants.ts        App-wide constants
├── App.tsx             Root component and tab router
└── index.tsx           React entry point

Code Conventions

TypeScript

  • Strict mode enabled — no any unless genuinely necessary
  • All domain types defined in types.ts
  • Use enums from types.ts instead of magic strings (e.g., Role.MANAGER, not 'MANAGER')

Components

  • File names: PascalCase.tsx
  • One component per file
  • Props interfaces defined inline or at top of file

Hooks & Services

  • File names: camelCase.ts
  • Hooks prefix: use (e.g., useAppState)
  • Services are plain exported objects or functions — no class instances unless required

i18n

  • All user-visible strings go through useTranslation() — no hardcoded English strings in JSX
  • Add keys to both locales/en.json and locales/ar.json together

Firestore

  • Use the service layer (services/) — do not call db directly from components
  • All list queries must use cursor-based pagination (see CustomerService.getPaginated)
  • Search fields must use the searchKey pattern (lowercase name stored alongside)

Search

Use the shared SearchInput component for all list filtering:

import { SearchInput } from './SearchInput';

<SearchInput
    value={searchTerm}
    onChange={setSearchTerm}
    placeholder={t('module.searchPlaceholder')}
    className="flex-1 min-w-[200px]"
/>

Backend search uses a searchKey field (stored as name.toLowerCase()) with a Firestore range query:

const q = query(
    collection(db, 'users'),
    where('searchKey', '>=', term.toLowerCase().trim()),
    where('searchKey', '<=', term.toLowerCase().trim() + '\uf8ff')
);
  • Always debounce search inputs (500ms) to limit read operations
  • Always .trim() input to handle copy-paste whitespace
  • If the search term is empty, revert to the default paginated view
  • When creating or updating a document with a name field, always write searchKey: name.toLowerCase() alongside it

Testing

Unit & Component Tests

npm test                          # Run once
npm test -- --watch               # Watch mode
npx vitest run --coverage         # With coverage report

Tests live in tests/. Component tests use @testing-library/react and jsdom.

E2E Tests (Cypress)

npm run test:e2e          # Headless
npm run test:e2e:headed   # With browser visible

Ensure TEST_USER_PASSWORD is set in .env and the test user exists:

tsx scripts/ensure-test-user.ts

Seeding Demo Data

  1. Log in as Administrator
  2. Navigate to Settings → Maintenance → Seed Data
  3. Configure: city, industry, customer count, history window
  4. Click Seed — triggers the reseed Cloud Function

Or seed users only via script:

tsx scripts/seedUsers.ts

Deployment

See DEPLOYMENT.md for the full deployment guide — Firebase project setup, environment config, first deploy, and troubleshooting.

Quick reference for ongoing deployments:

npm run deploy                                  # Full deploy (version bump + build + Firebase)
npx firebase deploy --only hosting             # Frontend only
npx firebase deploy --only functions           # Cloud Functions only
npx firebase deploy --only firestore:rules     # Firestore rules only
npx firebase deploy --only storage             # Storage rules only

Test rules locally with the emulator:

npx firebase emulators:start

Emulator ports: Auth 9099 · Firestore 8080 · Functions 5001 · Hosting 5000


Adding a New Module

  1. Create components/MyModule.tsx
  2. Add a tab entry in components/App/Sidebar.tsx with a role gate
  3. Add the tab case in App.tsx render logic
  4. Add Firestore queries in a new service file under services/
  5. Add types to types.ts
  6. Add i18n keys to both locales/en.json and locales/ar.json
  7. Write tests in tests/MyModule.test.tsx

Version Management

Version is stored in package.json and shown in the About section. Incremented automatically by npm run deploy. To bump manually:

npm run increment-version

Cloud Functions Development

# Build functions only
npm --prefix functions run build

# Deploy functions only
npx firebase deploy --only functions

# Local emulator
npx firebase emulators:start --only functions,firestore,auth

Function source: functions/src/ Compiled output: functions/lib/ (gitignored)