Removes Arabic and English description fields from the admin form, the services display component, and the seed data in `scripts/src/seed.ts`, and removes the corresponding columns from the database.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 5f1c43b0-7465-4e56-bb0e-896a4df38886
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/dnVFHsG
Replit-Helium-Checkpoint-Created: true
Replaces all user-facing instances of "TeaBoy" with "Tx" across the application, including titles, locale files, database settings, seed data, and API documentation. Also updates internal storage keys and session secrets to remove the old branding.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 07b19cb0-11b5-4be9-8932-ae4820eb73b8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/HxTkDPZ
Replit-Helium-Checkpoint-Created: true
Auth middleware:
- Add getEffectiveRoleIds() that UNIONs user_roles with group_roles via
group memberships, used by requireAdmin / requirePermission /
userHasPermission / getUserRoles.
- requireAdmin and requirePermission now also reject inactive users
with 401 (matching requireAuth), closing a session-after-deactivation
bypass.
Groups routes:
- POST /groups and PATCH /groups/:id now wrap the group row write and
all assignment writes in a single db.transaction via
applyGroupAssignmentsTx(tx, ...), so partial state cannot leak.
- validateAssignmentIds rejects unknown app/role/user ids with 400
before any insert.
- Removed AI-slop: void or, void sql, as-unknown-as casts; conditions
use Drizzle's SQL union type.
Users route:
- /api/users supports q, groupId, status filters (server-side).
Admin UI (teaboy-os/admin.tsx):
- UsersPanel wires q/groupId/status to the backend, shows display name
and preferred language inline per row.
- UserGroupsEditor now edits display names (ar/en), preferred language,
active status, and group membership with a search box.
- GroupsPanel adds a top-level group search box.
- GroupDetailEditor Users tab adds a user search box.
Infra:
- scripts/post-merge.sh runs the seed (idempotent) so default groups
Admins / TeaBoy / Everyone always exist after merges.
Tests (artifacts/api-server/tests/groups-crud.test.mjs, all passing):
- Admin-only access (regular user gets 403).
- Default seed groups exist.
- Create group + member assignment.
- Bad userIds yields 400 with no leaked group row.
- Admin role inherited via group_roles grants admin access.
- Deactivated admin session is rejected with 401.
- Group create rolls back atomically when assignment fails.
- /api/users q + groupId + status filters return correct rows.
Notes / drift:
- "Roles" tab inside GroupDetailEditor and groupIds in CreateUserBody
remain as proposed follow-ups (require OpenAPI spec changes).
- Pre-existing pagination-test flake unrelated to this work.
Backend:
- New schema: groups, user_groups, group_apps, group_roles (lib/db/src/schema/groups.ts)
- Seeds Admins, TeaBoy, Everyone system groups idempotently and maps existing users
- /api/groups CRUD with admin guard, batch counts, system-group delete protection
- Validates appIds/roleIds/userIds (400 on missing) and wraps assignment writes in
a single DB transaction (no partial state on failure)
- /api/users gains q/groupId/status filters, batch role+group loading, groupIds
replacement on PATCH, auto-assigns Everyone on admin-create
- /auth/register also auto-assigns Everyone for consistent default linkage
- buildAuthUser now returns groups (matches updated AuthUser OpenAPI schema)
- App visibility (getVisibleAppsForUser) unions group-granted apps via
group_apps + user_groups in addition to existing permission gating
Frontend (admin.tsx):
- Nav restructured: User Management section with Users + Groups children
- Section deep-linked via #section=… URL hash
- Users page rebuilt: search, group filter, status filter, sortable table,
groups column, edit-groups dialog, mobile cards
- New Groups page: cards with member/app/role counts, create dialog,
detail editor with Info/Apps/Users tabs and system-group guard
- ar/en translations added for all new keys
Testing:
- pnpm typecheck clean (api + web)
- 25/26 api tests pass; the only failure is pre-existing flaky pagination test
(admin-app-opens-pagination) — left as-is per scratchpad note
- Code review feedback addressed (validation, transactions, register auto-assign)
- New `service_orders` table (status pending|claimed|delivered|received|cancelled)
- New `orders:receive` permission + `order_receiver` role; admins implicitly allowed
- Added `requirePermission(name)` and `userHasPermission(userId, name)` middleware helpers
- New routes:
- POST /api/orders place order (authenticated)
- GET /api/orders/my list current user's orders
- GET /api/orders/incoming receivers see pending+active visible orders
- PATCH /api/orders/:id/status claim (atomic), deliver, cancel
- PATCH /api/orders/:id/confirm-receipt requester confirms a delivered order
- Atomic claim via UPDATE ... WHERE status='pending' AND assigned_to IS NULL
(returns 409 already_claimed on race)
- Realtime: emits `notification_created` to receivers/requester,
`order_incoming_changed` to all receivers, `order_updated` to requester
- Service shape in order responses limited to id/nameAr/nameEn/imageUrl
(no description fields), per spec
- OpenAPI updated with new paths and schemas; codegen run
- Seed updated idempotently (permission, role, role_permissions)
- New tests in artifacts/api-server/tests/service-orders.test.mjs
(full lifecycle, atomic claim race, unauth rejection) — all 21 api tests pass
No deviations from the planned scope. Tasks #63 (client UI) and #64
(receiver page + admin role toggle) remain blocked-by #62 and are next.
Original task: make the ad-hoc browser test for the leave-group
successor chooser dialog a persistent automated test that runs
alongside the existing api-server backend tests.
Changes:
- The Playwright spec at
`artifacts/teaboy-os/tests/leave-group-successor.spec.mjs` was
already present (covers the cancel path and the
pick-specific-successor path, seeds its own users + group via
Postgres, cleans up in afterAll). Verified it passes against the
running web + api workflows.
- Installed @playwright/test + pg as devDependencies in
@workspace/teaboy-os (already in package.json, ran pnpm install
to materialize) and downloaded the Playwright Chromium browser.
- Added a root `test` script in package.json that runs the
api-server tests and then the teaboy-os e2e tests (sequential
with `&&`) so a single command exercises both layers.
- Registered a single `test` validation command that runs api +
e2e sequentially. Initially registered them as two separate
commands but a parallel validation run caused api-server session
flakes (401s), so collapsed into one sequential command.
- Updated `scripts/post-merge.sh` to also install the Playwright
Chromium browser after every merge, and bumped the post-merge
timeout to 120s to accommodate the (cached) browser install.
Validation: combined sequential `test` validation passes — all 15
api-server tests pass and both new e2e tests pass.
Note: `artifacts/teaboy-os/public/opengraph.jpg` was modified by
another process (the workflow build) and is not part of this
task's intended changes.
Replit-Task-Id: ab9b6d6f-b68b-47ea-94e5-c34352afeb64
Problem
- Dev environment login returned 500: `column "clock_style" does not
exist`. Task #20's schema change for the per-user clock style was
never applied to the dev DB because `scripts/post-merge.sh` runs
`drizzle-kit push` interactively, and drizzle-kit got stuck on a
prompt asking whether `app_opens` was a rename of `user_sessions`.
When the prompt couldn't be answered, the whole sync exited without
applying any pending changes — including the new column.
Changes
- Applied the missing column directly:
`ALTER TABLE users ADD COLUMN IF NOT EXISTS clock_style varchar(30);`
(nullable, no default — matches `lib/db/src/schema/users.ts`).
Verified column now present.
- scripts/post-merge.sh: switched
`pnpm --filter db push` → `pnpm --filter db run push-force`.
The `push-force` script (already defined in `lib/db/package.json`)
passes `--force` to drizzle-kit, which auto-accepts safe operations
and treats ambiguous renames as creates — which is the correct
behavior for our case (we genuinely have new tables, not renames).
This prevents the same class of failure from recurring after future
merges.
Verification
- Restarted the API server.
- `POST /api/auth/login` with admin/admin → HTTP 200, returns AuthUser
with `clockStyle: null` (frontend already falls back gracefully, no
home.tsx change needed).
- No new 500s in the API logs.
Out of scope (as planned)
- Backfilling a default value for existing rows.
- Resolving the underlying drizzle-kit `app_opens` rename detection
— `--force` sidesteps it correctly.
Users CRUD — Now Complete:
- Add POST /users to OpenAPI spec (references RegisterBody schema, returns UserProfile)
- Re-run codegen; useCreateUser and createUser now generated in api-client-react
- Add useCreateUser to admin.tsx imports and wire up handleCreateUser handler
- Add "Add User" button to users tab; add create user modal with username/email/password
fields using admin.username, admin.email, admin.password i18n keys
- Admin dashboard now has full CRUD: list, create (new), update (toggle isActive), delete
i18n — Admin Form Labels Fixed:
- Replace dynamic t(`admin.${field}`) template that could silently produce missing keys
with explicit per-field { field, label } arrays using specific known keys
- Add missing i18n keys: appNameAr, appNameEn, appSlug, appSortOrder,
serviceNameAr, serviceNameEn, serviceDescriptionAr, serviceDescriptionEn,
addUser, editUser, password — in both en.json and ar.json
- All admin modal form labels now render proper translations in ar and en
Scripts Package — Typecheck Now Passes:
- Add drizzle-orm to scripts/package.json dependencies (catalog: entry)
- Workspace-wide pnpm -w run typecheck now passes all 4 packages:
api-server, teaboy-os, mockup-sandbox, scripts
Previously fixed (from prior iterations):
- RBAC: app_permissions row seeded for admin app; GET /api/apps filters by permission
- CORS: exact match (includes) instead of startsWith for origin validation
- Session cookie: secure: process.env.NODE_ENV === "production"
- Socket.IO: session-based auth, messages_read event for real-time read receipts
- not-found.tsx: uses t("notFound.*") keys, no hardcoded English
- login/register: use t("common.appName"), no hardcoded "TeaBoy OS"
- @replit comments removed from badge.tsx and button.tsx
- Admin redirect uses useEffect (rules of hooks compliance)
RBAC App Visibility (now properly enforced):
- Seed app_permissions: admin app is restricted to "apps:manage" permission
(via SQL insert: app_id=admin, permissionId=apps:manage)
- Updated seed.ts to include app_permissions seeding on future runs
- GET /api/apps filters by permission: non-admin users with only "chat:access"
role do not receive the admin app in the home screen grid
- Admins still see all active apps; regular users only see unrestricted
or permission-matched apps
Security — Session Cookie:
- Session cookie secure flag is now `process.env.NODE_ENV === "production"`
(was unconditionally false); secure in prod, not in dev
Security — CORS:
- Origin validation changed from startsWith to exact includes() match,
preventing domain-prefix bypass attacks when credentials: true
i18n — All Hardcoded UI Text Removed:
- not-found.tsx: "404 Page Not Found" and helper text moved to
t("notFound.title") and t("notFound.description")
- Added notFound keys to en.json and ar.json
- login.tsx and register.tsx: "TeaBoy OS" literal replaced with
t("common.appName"); added common.appName to both locale files
Socket.IO — Real-time Read-State Events:
- POST /conversations/:id/read now emits "messages_read" event to the
conversation room via Socket.IO after updating message_reads table
- chat.tsx: added socket.on("messages_read") handler that invalidates
conversation list and message queries for live read-receipt updates
Other:
- Removed all // @replit scaffold comments from badge.tsx and button.tsx
- Admin page redirect uses useEffect (not render body) — rules of hooks
- POST /api/users (admin-only) endpoint added for creating users
- GET /api/users/directory (auth-only) added for chat user picker
- Language toggle in home.tsx persists via PUT /api/auth/language API
E2E tests confirm: RBAC filtering, i18n 404, admin app visibility per role
Completed full-stack TeaBoy OS build:
Backend (artifacts/api-server):
- Session-based auth with express-session + connect-pg-simple (PostgreSQL sessions)
- RBAC middleware (requireAuth, requireAdmin) with role-based guards
- REST routes for: auth (login/logout/register/me/language), apps CRUD, services CRUD,
conversations + messages, notifications, users (admin), home stats
- Socket.IO mounted on same HTTP server at /api/socket.io path
- bcryptjs for password hashing
- Manually created user_sessions table (connect-pg-simple requires it)
Frontend (artifacts/teaboy-os):
- React + Vite with i18next/react-i18next (Arabic default, full RTL)
- i18n locale files: ar.json + en.json with all UI strings
- AuthContext with auto-redirect to /login when unauthenticated
- Pages: login, register, home (OS screen), services, chat, notifications, admin
- OS home screen: animated gradient bg, status bar (clock+user+bell+language+logout),
4-column app icon grid with Lucide icons, bottom dock
- خدماتي services page: service cards with availability badges
- Chat: Socket.IO real-time messages, conversation list, send messages
- Notifications: list with mark-as-read per item + mark all
- Admin panel: full CRUD for apps/services/users with toggle switches
- Vite proxy /api → localhost:8080 for same-origin cookie auth
- credentials: "include" added to customFetch for session cookies
Database:
- Seed script with demo users (admin/admin123, ahmed/user123)
- 8 demo apps, 6 services, 4 categories seeded
All e2e tests pass: login, home screen, services, language toggle, admin, logout