From 1f23e65c0b73a444fe856a0e0d1d8570b9e563e7 Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Wed, 22 Apr 2026 10:26:53 +0000 Subject: [PATCH] Update system name and references from TeaBoy to Tx 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 --- artifacts/api-server/src/app.ts | 2 +- .../api-server/tests/groups-crud.test.mjs | 4 +-- artifacts/teaboy-os/index.html | 2 +- artifacts/teaboy-os/src/components/clock.tsx | 20 +++++------ .../teaboy-os/src/hooks/use-app-settings.ts | 2 +- artifacts/teaboy-os/src/i18n.ts | 4 +-- artifacts/teaboy-os/src/locales/ar.json | 2 +- artifacts/teaboy-os/src/locales/en.json | 2 +- artifacts/teaboy-os/src/pages/chat.tsx | 2 +- .../tests/home-clock-persistence.spec.mjs | 6 ++-- .../src/generated/api.schemas.ts | 2 +- lib/api-client-react/src/generated/api.ts | 2 +- lib/api-spec/openapi.yaml | 2 +- lib/api-zod/src/generated/api.ts | 2 +- lib/db/src/schema/settings.ts | 4 +-- replit.md | 6 ++-- scripts/post-merge.sh | 2 +- scripts/src/seed.ts | 34 +++++++++++++------ 18 files changed, 57 insertions(+), 43 deletions(-) diff --git a/artifacts/api-server/src/app.ts b/artifacts/api-server/src/app.ts index 585bdb50..e3b44841 100644 --- a/artifacts/api-server/src/app.ts +++ b/artifacts/api-server/src/app.ts @@ -64,7 +64,7 @@ export const sessionMiddleware = session({ if (process.env.NODE_ENV === "production") { throw new Error("SESSION_SECRET must be set in production"); } - return "teaboy-dev-secret-change-before-deploy"; + return "tx-dev-secret-change-before-deploy"; })(), resave: false, saveUninitialized: false, diff --git a/artifacts/api-server/tests/groups-crud.test.mjs b/artifacts/api-server/tests/groups-crud.test.mjs index 468cbeff..72981892 100644 --- a/artifacts/api-server/tests/groups-crud.test.mjs +++ b/artifacts/api-server/tests/groups-crud.test.mjs @@ -114,7 +114,7 @@ test("GET /api/groups requires admin (regular user gets 403)", async () => { assert.equal(res.status, 403); }); -test("admin lists default seed groups including Admins, TeaBoy, Everyone", async () => { +test("admin lists default seed groups including Admins, Tx, Everyone", async () => { const res = await fetch(`${API_BASE}/api/groups`, { headers: { Cookie: adminCookie }, }); @@ -122,7 +122,7 @@ test("admin lists default seed groups including Admins, TeaBoy, Everyone", async const list = await res.json(); const names = new Set(list.map((g) => g.name)); assert.ok(names.has("Admins")); - assert.ok(names.has("TeaBoy")); + assert.ok(names.has("Tx")); assert.ok(names.has("Everyone")); }); diff --git a/artifacts/teaboy-os/index.html b/artifacts/teaboy-os/index.html index 9ccce42f..9d9953a7 100644 --- a/artifacts/teaboy-os/index.html +++ b/artifacts/teaboy-os/index.html @@ -3,7 +3,7 @@ - TeaBoy OS + Tx OS diff --git a/artifacts/teaboy-os/src/components/clock.tsx b/artifacts/teaboy-os/src/components/clock.tsx index 02f0729e..fc6a09ae 100644 --- a/artifacts/teaboy-os/src/components/clock.tsx +++ b/artifacts/teaboy-os/src/components/clock.tsx @@ -339,8 +339,8 @@ export function AnalogClockWidget({ ); } -const HOME_CLOCK_STORAGE_KEY = "teaboy:home-clock-visible"; -const HOME_CLOCK_EVENT = "teaboy:home-clock-changed"; +const HOME_CLOCK_STORAGE_KEY = "tx:home-clock-visible"; +const HOME_CLOCK_EVENT = "tx:home-clock-changed"; function readHomeClockVisible(): boolean { if (typeof window === "undefined") return true; @@ -384,8 +384,8 @@ export function useHomeClockVisibility(): [boolean, (next: boolean) => void] { return [visible, setVisible]; } -const TOPBAR_CLOCK_STORAGE_KEY = "teaboy:topbar-clock-visible"; -const TOPBAR_CLOCK_EVENT = "teaboy:topbar-clock-changed"; +const TOPBAR_CLOCK_STORAGE_KEY = "tx:topbar-clock-visible"; +const TOPBAR_CLOCK_EVENT = "tx:topbar-clock-changed"; function readTopbarClockVisible(): boolean { if (typeof window === "undefined") return true; @@ -428,13 +428,13 @@ export function useTopbarClockVisibility(): [boolean, (next: boolean) => void] { return [visible, setVisible]; } -const HOME_CLOCK_SIZE_EVENT = "teaboy:home-clock-size-changed"; +const HOME_CLOCK_SIZE_EVENT = "tx:home-clock-size-changed"; export type HomeClockSize = "compact" | "large"; function homeClockSizeKey(userId: number | string | null | undefined): string { return userId == null - ? "teaboy:home-clock-size" - : `teaboy:home-clock-size:${userId}`; + ? "tx:home-clock-size" + : `tx:home-clock-size:${userId}`; } function readHomeClockSize(userId: number | string | null | undefined): HomeClockSize { @@ -473,12 +473,12 @@ export function useHomeClockSize( return [size, setSize]; } -const HOME_CLOCK_POS_EVENT = "teaboy:home-clock-pos-changed"; +const HOME_CLOCK_POS_EVENT = "tx:home-clock-pos-changed"; function homeClockPosKey(userId: number | string | null | undefined): string { return userId == null - ? "teaboy:home-clock-position" - : `teaboy:home-clock-position:${userId}`; + ? "tx:home-clock-position" + : `tx:home-clock-position:${userId}`; } function readHomeClockPosition(userId: number | string | null | undefined): number { diff --git a/artifacts/teaboy-os/src/hooks/use-app-settings.ts b/artifacts/teaboy-os/src/hooks/use-app-settings.ts index 970c39cd..23d66243 100644 --- a/artifacts/teaboy-os/src/hooks/use-app-settings.ts +++ b/artifacts/teaboy-os/src/hooks/use-app-settings.ts @@ -11,7 +11,7 @@ export function useAppName() { const { i18n } = useTranslation(); const { data } = useAppSettings(); const isAr = i18n.language === "ar"; - const fallback = isAr ? "نظام TeaBoy" : "TeaBoy OS"; + const fallback = isAr ? "نظام Tx" : "Tx OS"; const name = data ? (isAr ? data.siteNameAr : data.siteNameEn) : fallback; useEffect(() => { if (typeof document !== "undefined" && data) { diff --git a/artifacts/teaboy-os/src/i18n.ts b/artifacts/teaboy-os/src/i18n.ts index 2e1459c3..812cef6b 100644 --- a/artifacts/teaboy-os/src/i18n.ts +++ b/artifacts/teaboy-os/src/i18n.ts @@ -4,7 +4,7 @@ import ar from './locales/ar.json'; import en from './locales/en.json'; // Initialize i18next -const savedLanguage = localStorage.getItem('teaboy-lang') || 'ar'; +const savedLanguage = localStorage.getItem('tx-lang') || 'ar'; i18n .use(initReactI18next) @@ -26,7 +26,7 @@ document.documentElement.lang = savedLanguage; i18n.on('languageChanged', (lng) => { document.documentElement.dir = lng === 'ar' ? 'rtl' : 'ltr'; document.documentElement.lang = lng; - localStorage.setItem('teaboy-lang', lng); + localStorage.setItem('tx-lang', lng); }); export default i18n; diff --git a/artifacts/teaboy-os/src/locales/ar.json b/artifacts/teaboy-os/src/locales/ar.json index 3aa96719..d071bac3 100644 --- a/artifacts/teaboy-os/src/locales/ar.json +++ b/artifacts/teaboy-os/src/locales/ar.json @@ -488,7 +488,7 @@ "error": "حدث خطأ", "success": "تمت العملية بنجاح", "language": "English", - "appName": "نظام TeaBoy" + "appName": "نظام Tx" }, "notes": { "title": "الملاحظات", diff --git a/artifacts/teaboy-os/src/locales/en.json b/artifacts/teaboy-os/src/locales/en.json index 30472b68..57789296 100644 --- a/artifacts/teaboy-os/src/locales/en.json +++ b/artifacts/teaboy-os/src/locales/en.json @@ -485,7 +485,7 @@ "error": "An error occurred", "success": "Success", "language": "العربية", - "appName": "TeaBoy OS" + "appName": "Tx OS" }, "notes": { "title": "Notes", diff --git a/artifacts/teaboy-os/src/pages/chat.tsx b/artifacts/teaboy-os/src/pages/chat.tsx index ee570e9d..6c12d097 100644 --- a/artifacts/teaboy-os/src/pages/chat.tsx +++ b/artifacts/teaboy-os/src/pages/chat.tsx @@ -253,7 +253,7 @@ export default function ChatPage() { // arrive in the conversation before the user next opens chat. useEffect(() => { if (!user || !conversations) return; - const ackKey = `teaboy:admin-known:${user.id}`; + const ackKey = `tx:admin-known:${user.id}`; let ack: number[] = []; try { ack = JSON.parse(localStorage.getItem(ackKey) ?? "[]"); diff --git a/artifacts/teaboy-os/tests/home-clock-persistence.spec.mjs b/artifacts/teaboy-os/tests/home-clock-persistence.spec.mjs index f0941023..44cace59 100644 --- a/artifacts/teaboy-os/tests/home-clock-persistence.spec.mjs +++ b/artifacts/teaboy-os/tests/home-clock-persistence.spec.mjs @@ -1,7 +1,7 @@ // UI test verifying that the home-page clock widget keeps its grid position // and compact/large size after a full page reload, and that the changes are // persisted to localStorage under the documented keys -// `teaboy:home-clock-position` / `teaboy:home-clock-size` (or their per-user +// `tx:home-clock-position` / `tx:home-clock-size` (or their per-user // variants once a user is logged in). // // This is the recurring automated counterpart to the manual e2e check @@ -95,9 +95,9 @@ async function readClockStorage(page, userId) { return page.evaluate((uid) => { return { position: window.localStorage.getItem( - `teaboy:home-clock-position:${uid}`, + `tx:home-clock-position:${uid}`, ), - size: window.localStorage.getItem(`teaboy:home-clock-size:${uid}`), + size: window.localStorage.getItem(`tx:home-clock-size:${uid}`), }; }, userId); } diff --git a/lib/api-client-react/src/generated/api.schemas.ts b/lib/api-client-react/src/generated/api.schemas.ts index 5947c93b..a524f7fb 100644 --- a/lib/api-client-react/src/generated/api.schemas.ts +++ b/lib/api-client-react/src/generated/api.schemas.ts @@ -2,7 +2,7 @@ * Generated by orval v8.5.3 🍺 * Do not edit manually. * Api - * TeaBoy OS API specification + * Tx OS API specification * OpenAPI spec version: 0.1.0 */ export interface HealthStatus { diff --git a/lib/api-client-react/src/generated/api.ts b/lib/api-client-react/src/generated/api.ts index 67af3f1a..8e4e2d68 100644 --- a/lib/api-client-react/src/generated/api.ts +++ b/lib/api-client-react/src/generated/api.ts @@ -2,7 +2,7 @@ * Generated by orval v8.5.3 🍺 * Do not edit manually. * Api - * TeaBoy OS API specification + * Tx OS API specification * OpenAPI spec version: 0.1.0 */ import { useMutation, useQuery } from "@tanstack/react-query"; diff --git a/lib/api-spec/openapi.yaml b/lib/api-spec/openapi.yaml index cc290e02..9f92faec 100644 --- a/lib/api-spec/openapi.yaml +++ b/lib/api-spec/openapi.yaml @@ -3,7 +3,7 @@ info: # Do not change the title, if the title changes, the import paths will be broken title: Api version: 0.1.0 - description: TeaBoy OS API specification + description: Tx OS API specification servers: - url: /api description: Base API path diff --git a/lib/api-zod/src/generated/api.ts b/lib/api-zod/src/generated/api.ts index 959932c4..2f23f30d 100644 --- a/lib/api-zod/src/generated/api.ts +++ b/lib/api-zod/src/generated/api.ts @@ -2,7 +2,7 @@ * Generated by orval v8.5.3 🍺 * Do not edit manually. * Api - * TeaBoy OS API specification + * Tx OS API specification * OpenAPI spec version: 0.1.0 */ import * as zod from "zod"; diff --git a/lib/db/src/schema/settings.ts b/lib/db/src/schema/settings.ts index df51a5f7..e7b43562 100644 --- a/lib/db/src/schema/settings.ts +++ b/lib/db/src/schema/settings.ts @@ -4,8 +4,8 @@ import { z } from "zod/v4"; export const appSettingsTable = pgTable("app_settings", { id: integer("id").primaryKey().default(1), - siteNameAr: varchar("site_name_ar", { length: 200 }).notNull().default("نظام TeaBoy"), - siteNameEn: varchar("site_name_en", { length: 200 }).notNull().default("TeaBoy OS"), + siteNameAr: varchar("site_name_ar", { length: 200 }).notNull().default("نظام Tx"), + siteNameEn: varchar("site_name_en", { length: 200 }).notNull().default("Tx OS"), registrationOpen: boolean("registration_open").notNull().default(true), footerTextAr: varchar("footer_text_ar", { length: 300 }).notNull().default("جميع الحقوق محفوظة."), footerTextEn: varchar("footer_text_en", { length: 300 }).notNull().default("All rights reserved."), diff --git a/replit.md b/replit.md index 8f8b4653..7ed2343d 100644 --- a/replit.md +++ b/replit.md @@ -1,8 +1,8 @@ -# TeaBoy OS +# Tx OS ## Overview -**TeaBoy 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. +**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 @@ -11,6 +11,7 @@ ├── artifacts/ │ ├── api-server/ Express 5 backend (port 8080) │ └── teaboy-os/ React + Vite frontend (path: /, port dynamic) +│ (legacy folder name; product brand is "Tx OS") ├── lib/ │ ├── db/ Drizzle ORM + PostgreSQL schema │ ├── api-spec/ OpenAPI spec + Orval codegen @@ -66,3 +67,4 @@ - Socket.IO server path: `/api/socket.io` - Frontend connects to Socket.IO via same-origin proxy (no separate URL needed) - i18n locale files: `artifacts/teaboy-os/src/locales/ar.json` and `en.json` +- Default receivers group is named **Tx** (was renamed from a legacy "TeaBoy"); a one-time migration in the seed script renames any pre-existing legacy group on next run. diff --git a/scripts/post-merge.sh b/scripts/post-merge.sh index be2dbc93..0a97617f 100644 --- a/scripts/post-merge.sh +++ b/scripts/post-merge.sh @@ -3,6 +3,6 @@ set -e pnpm install --frozen-lockfile pnpm --filter db run push-force # Idempotent: ensure the default groups exist and existing users are mapped -# (Admins, TeaBoy, Everyone). Safe to run repeatedly. +# (Admins, Tx, Everyone). Safe to run repeatedly. pnpm --filter scripts run seed pnpm --filter @workspace/teaboy-os exec playwright install chromium diff --git a/scripts/src/seed.ts b/scripts/src/seed.ts index 66ad0704..a332b56f 100644 --- a/scripts/src/seed.ts +++ b/scripts/src/seed.ts @@ -102,7 +102,7 @@ async function main() { .insert(usersTable) .values({ username: "admin", - email: "admin@teaboy.local", + email: "admin@tx.local", passwordHash: adminHash, displayNameAr: "مدير النظام", displayNameEn: "System Admin", @@ -118,7 +118,7 @@ async function main() { .insert(usersTable) .values({ username: "ahmed", - email: "ahmed@teaboy.local", + email: "ahmed@tx.local", passwordHash: userHash, displayNameAr: "أحمد محمد", displayNameEn: "Ahmed Mohammed", @@ -369,7 +369,7 @@ async function main() { isSystem: 1, }, { - name: "TeaBoy", + name: "Tx", descriptionAr: "فريق إعداد وتقديم الطلبات", descriptionEn: "Team that prepares and delivers orders", isSystem: 1, @@ -383,9 +383,21 @@ async function main() { ]) .onConflictDoNothing(); + // One-time migration: rename legacy "TeaBoy" group to "Tx" if it still exists + // and the new "Tx" group hasn't been created yet. + const existingGroups = await db.select().from(groupsTable); + const legacy = existingGroups.find((g) => g.name === "TeaBoy"); + const newOne = existingGroups.find((g) => g.name === "Tx"); + if (legacy && !newOne) { + await db + .update(groupsTable) + .set({ name: "Tx" }) + .where(eq(groupsTable.id, legacy.id)); + } + const allGroups = await db.select().from(groupsTable); const adminsGroup = allGroups.find((g) => g.name === "Admins"); - const teaboyGroup = allGroups.find((g) => g.name === "TeaBoy"); + const txGroup = allGroups.find((g) => g.name === "Tx"); const everyoneGroup = allGroups.find((g) => g.name === "Everyone"); const allRolesNow = await db.select().from(rolesTable); @@ -399,10 +411,10 @@ async function main() { .values({ groupId: adminsGroup.id, roleId: adminRoleNow.id }) .onConflictDoNothing(); } - if (teaboyGroup && orderReceiverNow) { + if (txGroup && orderReceiverNow) { await db .insert(groupRolesTable) - .values({ groupId: teaboyGroup.id, roleId: orderReceiverNow.id }) + .values({ groupId: txGroup.id, roleId: orderReceiverNow.id }) .onConflictDoNothing(); } @@ -414,13 +426,13 @@ async function main() { .values(allAppsNow.map((a) => ({ groupId: adminsGroup.id, appId: a.id }))) .onConflictDoNothing(); } - // TeaBoy gets the services app (which is where receivers see incoming orders) - if (teaboyGroup) { + // The Tx receivers group gets the services app (where receivers see incoming orders) + if (txGroup) { const servicesApp = allAppsNow.find((a) => a.slug === "services"); if (servicesApp) { await db .insert(groupAppsTable) - .values({ groupId: teaboyGroup.id, appId: servicesApp.id }) + .values({ groupId: txGroup.id, appId: servicesApp.id }) .onConflictDoNothing(); } } @@ -447,7 +459,7 @@ async function main() { } } - if (teaboyGroup && orderReceiverNow) { + if (txGroup && orderReceiverNow) { const receiverRows = await db .select({ userId: userRolesTable.userId }) .from(userRolesTable) @@ -455,7 +467,7 @@ async function main() { if (receiverRows.length > 0) { await db .insert(userGroupsTable) - .values(receiverRows.map((r) => ({ userId: r.userId, groupId: teaboyGroup.id }))) + .values(receiverRows.map((r) => ({ userId: r.userId, groupId: txGroup.id }))) .onConflictDoNothing(); } }