Build Guide / 06
Folder structure
A single Vite project root, with the frontend under src/ and the
server split into its own files at the top level so it can be bundled
separately for deployment.
ebsofthms/
├── server.ts # Dev entry: Vite middleware + cron scheduler
├── server-app.ts # Express app: routes, Firebase Admin, Gemini proxy
├── vite.config.ts # Vite + Tailwind plugin, "@" → src alias
├── netlify.toml # Build command, redirects, security headers
├── netlify/
│ └── functions/
│ └── api.ts # Wraps server-app.ts for serverless execution
├── firestore.rules # Access control (see Database & Security)
├── storage.rules
├── test/
│ └── firestore.rules.test.ts
├── scripts/ # One-off / operational scripts (data generation,
│ # connection checks, reminder jobs)
├── public/ # Static assets served as-is
├── index.html # SPA shell
└── src/
├── main.tsx # React root
├── App.tsx # Route table — 231 routes
├── index.css # Tailwind entry + global styles
├── pages/ # One folder per hospital department (see
│ # Feature Modules) plus shared top-level pages
├── components/ # Shared UI: layout, dashboard widgets,
│ # per-domain components (billing/, opd/, lis/...),
│ # ui/ (shadcn primitives)
├── context/ # AuthContext, SettingsContext, Notification*,
│ # PatientPortalContext
├── hooks/ # Domain hooks: useIPDBilling, usePayroll,
│ # useAttendance, useLeave, useRoles...
├── lib/ # firebase.ts, autoNumbering, auditLogger,
│ # dateUtils, referralCommission, sanitizeHtml...
├── services/ # messageService, notificationService
├── constants/
├── data/ # Static reference data (e.g. medical_tests.json)
├── types/
└── utils/
Notable conventions
- Department-first, not layer-first.
src/pages/stores/holds every stores-related page together (inventory, suppliers, wastage, reports) rather than splitting inventory logic across a global "inventory" layer. - Domain components live near their domain.
src/components/billing/,opd/,radiology/etc. sit alongside the genericcomponents/ui/primitives. - The
@/import alias (configured invite.config.ts) always resolves tosrc/, so imports don't accumulate long relative paths as pages get nested deeper inside module folders.