# Tx OS ## Overview **Tx OS** — a bilingual (Arabic/English, RTL/LTR) full-stack internal web platform styled as an OS-like interface with glassmorphism aesthetics. Built as a pnpm monorepo. ## Architecture ``` / ├── artifacts/ │ ├── api-server/ Express 5 backend (port 8080) │ └── tx-os/ React + Vite frontend (path: /, port dynamic) ├── lib/ │ ├── db/ Drizzle ORM + PostgreSQL schema │ ├── api-spec/ OpenAPI spec + Orval codegen │ ├── api-client-react/ Generated React Query hooks (from Orval) │ └── api-zod/ Generated Zod schemas (from Orval) └── scripts/ Seed script (pnpm run seed) ``` ## Stack - **Monorepo**: pnpm workspaces - **Node.js**: 24, TypeScript 5.9 - **Backend**: Express 5, express-session + connect-pg-simple (PostgreSQL sessions), bcryptjs, Socket.IO - **Database**: PostgreSQL + Drizzle ORM, Zod validation - **API codegen**: Orval (OpenAPI → React Query hooks + Zod schemas) - **Frontend**: React + Vite, Tailwind CSS v4, wouter (routing), i18next (i18n), react-i18next - **Real-time**: Socket.IO (path: /api/socket.io) - **Build**: esbuild (API), Vite (frontend) ## Features - **Arabic default** with full RTL layout; English toggle persisted in localStorage + user profile - **Glassmorphism OS UI**: animated gradient background, frosted glass panels - **OS Home Screen**: live clock status bar (per-user clock style: full / digital / digital-no-seconds / analog / minimal, picker in status bar), app grid, bottom dock - **خدماتي (My Services)**: service card grid with availability status - **Internal Chat**: real-time messages via Socket.IO, conversation list - **Notifications**: unread tracking, mark-all-read - **Admin Panel**: CRUD for apps, services, users (admin role required) - **Session-based Auth**: RBAC with roles (admin/user) - **Executive Meetings (Phase 2)**: bilingual full-stack module under `/executive-meetings` with 9 sections — Schedule (centered cells, attendees widest column, RTL-locked column order # / الاجتماع / الحضور / الوقت), Manage Meetings (CRUD with attendee replace, transactional), Change Requests (submit / withdraw, supports `meetingId=null` for create-suggestions), Approvals (approve/reject with review notes), Tasks (CRUD with assignee status updates — assignees and mutators can change status, only mutators can delete), Notifications (per-user feed), Audit Log (full action chain, admin role required), PDF (window.print export), Font Settings (per-user + global scope, family/size/weight/alignment with live preview). RBAC enforced via 5 role sets (READ/MUTATE/APPROVE/REQUEST/ADMIN_AUDIT) and a `makeRequireRoles` middleware factory; `/api/executive-meetings/me` returns `{userId, roles, canRead, canMutate, canApprove, canSubmitRequest, canViewAudit}`. Every mutation (meeting/request/task/font CRUD) wraps the DB write **and** the audit-log insert in the same `db.transaction(...)` so audit entries cannot drift from state. Routes use `router.param("id")` with `next("route")` to handle path-to-regexp 8 (no inline `:id(\\d+)` support). ## Key Commands - `pnpm run typecheck` — full typecheck across all packages - `pnpm run build` — build all packages - `pnpm --filter @workspace/api-spec run codegen` — regenerate API hooks from OpenAPI spec - `pnpm --filter @workspace/db run push` — push DB schema changes (dev) - `pnpm --filter @workspace/scripts run seed` — seed demo data ## Demo Accounts - **Admin**: `admin` / `admin123` (admin + user roles) - **User**: `ahmed` / `user123` (user role) ## Database Tables `users`, `roles`, `permissions`, `user_roles`, `role_permissions`, `apps`, `app_permissions`, `service_categories`, `services`, `conversations`, `conversation_participants`, `messages`, `message_reads`, `notifications`, `user_sessions`, `audit_logs`, `executive_meetings`, `executive_meeting_attendees`, `executive_meeting_requests`, `executive_meeting_tasks`, `executive_meeting_notifications`, `executive_meeting_audit_logs`, `executive_meeting_pdf_archives`, `executive_meeting_font_settings` ## Important Notes - Session table `user_sessions` is created manually (not auto-created) — needed in DB before first run - Vite dev server proxies `/api` → `localhost:8080` for cookie-based auth to work - All API calls use `credentials: "include"` via `lib/api-client-react/src/custom-fetch.ts` - Socket.IO server path: `/api/socket.io` - Frontend connects to Socket.IO via same-origin proxy (no separate URL needed) - i18n locale files: `artifacts/tx-os/src/locales/ar.json` and `en.json` - Default receivers group is named **Tx** (renamed from legacy "TeaBoy"); a one-time migration in the seed script renames any pre-existing legacy group on next run.