Add per-user clock style preference for the home status bar.

Original task #20: Let each user pick their own home-screen clock
style (analog/digital/minimal/etc.), persisted on the user record
and restored on login / other devices. Default = "full".

Changes:
- DB: added `clock_style varchar(30)` (nullable) to `users`
  (lib/db/src/schema/users.ts) and applied via direct ALTER TABLE
  (drizzle-kit push had unrelated interactive prompts about
  app_opens / user_sessions which were not safe to answer).
- OpenAPI: added `ClockStyle` enum (full/digital/digital-no-seconds
  /analog/minimal), `UpdateClockStyleBody`, exposed `clockStyle`
  on AuthUser and UserProfile, and added PATCH /auth/me/clock-style.
  Regenerated typed client and zod schemas.
- API: `buildAuthUser` and `buildUserProfile` include `clockStyle`;
  new authenticated endpoint updates the current user's style and
  returns the refreshed AuthUser.
- Frontend: new `Clock` component (artifacts/teaboy-os/src/
  components/clock.tsx) with five variants sharing a single `useNow`
  tick hook, plus an SVG analog clock; honors existing
  Latin-digit/locale formatting helpers. New `ClockStylePicker`
  (popover) shown in the status bar with a live preview for each
  option and optimistic update through the AuthUser query cache.
- home.tsx replaces the hard-coded clock block with `<Clock>` driven
  by `user.clockStyle`; trigger button placed next to the language
  toggle.
- i18n: added `home.clockStyle.label` and per-style option labels
  to en.json and ar.json.

Verified via e2e: register → default "full" rendered → switch to
analog → reload → analog persists → switch to minimal → time-only
renders. RTL layout + Latin digits both correct after language
toggle.

Replit-Task-Id: 475a7439-b357-400d-805f-5f2fda20ed24
This commit is contained in:
riyadhafraa
2026-04-20 16:40:49 +00:00
parent 4a481261a2
commit 284cb751ed
14 changed files with 591 additions and 23 deletions
+1
View File
@@ -10,6 +10,7 @@ export const usersTable = pgTable("users", {
displayNameAr: varchar("display_name_ar", { length: 200 }),
displayNameEn: varchar("display_name_en", { length: 200 }),
preferredLanguage: varchar("preferred_language", { length: 10 }).notNull().default("ar"),
clockStyle: varchar("clock_style", { length: 30 }),
avatarUrl: text("avatar_url"),
isActive: boolean("is_active").notNull().default(true),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),