Commit Graph

58 Commits

Author SHA1 Message Date
riyadhafraa 66787e4fb2 Git commit prior to merge 2026-04-30 18:29:42 +00:00
riyadhafraa 6deed137cf Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 97c9c087-1ae7-4e4c-9f7f-d4b224fc5414
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/opt4zwD
Replit-Helium-Checkpoint-Created: true
2026-04-30 18:07:52 +00:00
riyadhafraa 76f0dfacb4 Restart numbering for individuals after each subheading
Modify attendee numbering logic to reset at each subheading, ensuring proper sequence and display across different views and PDF outputs.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 17c29c3f-e55d-4765-875c-0387b6936812
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/piUNOmy
Replit-Helium-Checkpoint-Created: true
2026-04-30 12:47:50 +00:00
riyadhafraa 968da9cb58 Git commit prior to merge 2026-04-30 12:20:50 +00:00
riyadhafraa 986bb8e7b2 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b15fc2b0-a0e1-4352-8f61-430259bae26e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/piUNOmy
Replit-Helium-Checkpoint-Created: true
2026-04-30 11:20:30 +00:00
riyadhafraa 7a13e0ae7f Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 0d5bf23d-912c-49e3-b8bb-828bae8fefe3
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/NQjGafC
Replit-Helium-Checkpoint-Created: true
2026-04-30 09:57:18 +00:00
riyadhafraa 162d78ad8b Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ffc65042-01e4-45eb-920e-412828e5c5c0
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/fEDT27c
Replit-Helium-Checkpoint-Created: true
2026-04-30 09:28:56 +00:00
riyadhafraa d72c5cc851 Git commit prior to merge 2026-04-30 07:25:15 +00:00
riyadhafraa 909551d0f6 Git commit prior to merge 2026-04-30 06:39:45 +00:00
riyadhafraa 6949ab1009 Remove unnecessary screenshot files from project assets
Remove untracked screenshot image file from attached_assets directory.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b5b15c4e-1da9-491c-b4f5-0afd36991df0
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/S0bOBq6
Replit-Helium-Checkpoint-Created: true
2026-04-30 05:58:28 +00:00
riyadhafraa 18aa6bc93d Reduce schedule "Add" buttons to a single "+" icon (Task #190)
The substantive code for this task was already merged earlier in the
session as commit 1df6ba4 (and re-merged as 43a5491 due to a
re-assignment after task #130 went through). When the system handed
the task back to me on the post-#130 base, the working tree already
contained the desired final state — there was nothing further to
change.

What is already in HEAD that satisfies the task:
- artifacts/tx-os/src/pages/executive-meetings.tsx:
  - Add-row button (~line 1533): renders only the <Plus> icon in the
    idle state; "Loading..." stays during creatingRow. Button has
    aria-label={t("executiveMeetings.schedule.addRow")} and
    aria-busy={creatingRow}; <Plus> is aria-hidden.
  - Add-attendee inline button (~line 3008): visible content is the
    literal "+"; aria-label={addLabel} preserved for assistive tech.
- artifacts/tx-os/src/locales/ar.json:
  - executiveMeetings.schedule.addRow      : "أضف اجتماع"
  - executiveMeetings.schedule.addAttendee : "أضف حاضر"
- artifacts/tx-os/src/locales/en.json:
  - executiveMeetings.schedule.addAttendee : "Add attendee"
  - executiveMeetings.schedule.addRow      : unchanged ("Add meeting")

Verification (already performed during the original implementation):
- All 4 cases of executive-meetings-edit-toggle.spec.mjs pass; tests
  reference the buttons via data-testid (em-add-row-button,
  em-add-attendee-*), so no test changes were required.

No deviations from the task brief.
2026-04-30 05:57:40 +00:00
riyadhafraa 1df6ba41d4 Reduce schedule "Add" buttons to a single "+" icon
Task #190.

User wanted both add affordances in the Executive Meetings schedule
to show only the "+" symbol — no accompanying text in either
language.

Changes:
- artifacts/tx-os/src/pages/executive-meetings.tsx:
  - Add-row button: drops the visible label in idle state, renders
    only the <Plus> icon (aria-hidden). Loading state still shows
    "Loading..." for feedback. Added aria-label and aria-busy on
    the <button> so screen readers still announce the action and
    the loading state.
  - Add-attendee inline button: visible content is now the literal
    "+" character. aria-label preserved for assistive tech.
- artifacts/tx-os/src/locales/ar.json:
  - executiveMeetings.schedule.addRow: "أضف اجتماع جديد" → "أضف اجتماع"
  - executiveMeetings.schedule.addAttendee: "+ أضف حاضرًا" → "أضف حاضر"
- artifacts/tx-os/src/locales/en.json:
  - executiveMeetings.schedule.addAttendee: "+ Add attendee" → "Add attendee"

Both locale strings are now used solely as accessible names since
the visible glyph is hard-coded.

Verification: All 4 tests in
executive-meetings-edit-toggle.spec.mjs pass. Tests use
data-testid (em-add-row-button, em-add-attendee-*), not text, so
no test changes were required. Pre-existing TS errors in admin.tsx
and use-notifications-socket.ts are unrelated (api-client-react
codegen out of sync; tracked by other in-flight tasks).
2026-04-30 05:52:39 +00:00
riyadhafraa bb1a696f85 Git commit prior to merge 2026-04-29 19:28:13 +00:00
riyadhafraa 1a2e1302d8 Improve top bar layout and icon sizes for larger screens
Update `home.tsx` to make the top bar larger on medium screens and above, increasing icon sizes and padding for better usability on tablets and desktops.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f5ae41d0-d8c5-4d0a-bbb0-5918d1b4e4b4
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Y0rzet1
Replit-Helium-Checkpoint-Created: true
2026-04-29 19:13:34 +00:00
riyadhafraa c29632d5a6 Schedule attendees: force number+name onto the same visual line
Task #175. Follow-up to #173. The first fix added `whitespace-nowrap`
on each attendee `<li>`, but users still saw the index span (`1-`,
`2-`) stacked above the name — even with very short names like
"رياض" / "محمد" that obviously fit on one line. Two screenshots
(before and after the merge) showed the same stacked layout, ruling
out narrow-column wrapping.

Root cause: attendee names are saved as tiptap HTML such as
`<p>محمد</p>`. Inside the inline-block EditableCell shell (and even
inside the plain view-mode `<span>`), the default block-level `<p>`
with its 1em top/bottom margins forced the name onto its own visual
row beneath the index span. `whitespace-nowrap` cannot pull a block
child back onto the parent line.

Fix (artifacts/tx-os/src/pages/executive-meetings.tsx):
- Each attendee `<li>` is now `inline-flex items-baseline
  whitespace-nowrap` so the index span and the name wrapper become
  flex children that structurally cannot break apart.
- The view-mode `<span>` (plain dangerouslySetInnerHTML) gets
  `[&_p]:inline [&_p]:m-0` — tiptap `<p>` renders as inline with no
  margins.
- The editable EditableCell wrapper gets the more-scoped
  `[&>span_p]:inline [&>span_p]:m-0`, which matches only the
  view-mode shell `<div> > <span> > <p>` and deliberately does NOT
  match the editing shell `<div> > <div(border)> > EditorContent`,
  so pressing Enter inside the editor still creates a real new
  paragraph.

Tests (artifacts/tx-os/tests/executive-meetings-edit-toggle.spec.mjs):
- New regression spec asserts that the index span and the name
  wrapper share the same vertical center (within 8px) for the first
  attendee in BOTH view mode and edit mode. Without the fix the
  centers differ by a full line height (~20px+).
- All 3 specs in the file pass.

Out of scope (unchanged): grouping/sorting, index format, multi-
group Virtual/Internal/External rows, pending +Add ghost row,
other EditableCell call sites (title, time, notes, manage tab).
2026-04-29 18:57:26 +00:00
riyadhafraa 8c9e169e8c Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: a77e1383-29b7-4045-97c2-8ed6f5bc1e71
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/TCOxxNN
Replit-Helium-Checkpoint-Created: true
2026-04-29 18:51:11 +00:00
riyadhafraa f9691a37a2 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 2e122137-8fb2-4e80-8178-c5e982f4b666
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/2GpAnn7
Replit-Helium-Checkpoint-Created: true
2026-04-29 18:35:49 +00:00
riyadhafraa b66a5ef9b6 Task #171: Schedule Edit/View toggle (final fixes)
Add a single global "تحرير/Edit" toggle button to the schedule
toolbar that hides every editing affordance by default and reveals
them only when the user (with edit permission) explicitly opts in.

Affordances now gated behind `effectiveCanMutate = canMutate &&
editMode`:
- "+ Add row" button
- Per-row delete, color swatch, merge trigger, drag grip
- Inline cell editors (EditableCell, TimeRangeCell)
- Column drag-reorder (SortableHeader.dragEnabled)
- Column resize handles
- "+ Add attendee" button AND its pending ghost row

Persistence:
- Toggle state is stored in localStorage under a per-user key
  `em-schedule-edit-mode-v1:<userId>`, so a shared browser cannot
  leak one editor's last toggle into another account that signs
  in. Falls back to view mode when userId is unavailable.
- Always starts in view mode for users without edit permission.

Toggle-off safety:
- EditableCell + TimeRangeCell discard any in-progress draft and
  exit edit mode when their `disabled` / `canMutate` prop flips.
- ScheduleSection clears `pendingAttendee` in a useEffect when
  effectiveCanMutate becomes false, so the ghost "+ Add attendee"
  row unmounts immediately.
- AttendeeFlow also gates the pending render on `canMutate` as
  defense in depth.

i18n: 4 new keys under `executiveMeetings.schedule`
(editToggle / editToggleAria / editToggleOn / editToggleOff)
in both en.json and ar.json.

Tests: tests/executive-meetings-edit-toggle.spec.mjs covers
default-hidden affordances, toggle-on reveal, reload persistence,
and toggle-off re-hide. Cleanup wipes all `em-schedule-edit-mode-v1*`
keys to handle the user-namespaced storage. Full e2e suite (12
tests) passes.

Code review: PASS on the second pass after the per-user key + ghost
row fixes. Pre-existing TS errors in admin.tsx and
use-notifications-socket.ts are codegen drift from earlier tasks
and are not touched by this change.
2026-04-29 18:22:49 +00:00
riyadhafraa 916fd10241 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: a680760b-b89b-4257-89b7-b62d7fce00a5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/aNtxOPE
Replit-Helium-Checkpoint-Created: true
2026-04-29 17:36:32 +00:00
riyadhafraa abea383133 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f8a14ccf-b332-489e-bf3d-b1dfa1263e7e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/CMw20Ap
Replit-Helium-Checkpoint-Created: true
2026-04-29 14:52:25 +00:00
riyadhafraa cdacb079e9 Git commit prior to merge 2026-04-29 13:24:16 +00:00
riyadhafraa e5e96e7022 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: eab87fb0-38a8-43ae-95c6-49598fc45ab4
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/NvUGneQ
Replit-Helium-Checkpoint-Created: true
2026-04-29 12:22:40 +00:00
riyadhafraa 768d6eab4c Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: c18e1a47-f2d8-412d-86fc-05ad16b0f23a
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/9GxtwFd
Replit-Helium-Checkpoint-Created: true
2026-04-29 10:46:32 +00:00
riyadhafraa 3994ccdb0c Task #120 — Make the Executive Meetings print page responsive (no clipped attendee names)
Problem:
artifacts/tx-os/src/pages/executive-meetings-print.tsx built the schedule
as one monolithic <table> with fixed percentage widths (8/32/40/20), no
min-width / overflow guard, no word-break rules, and joined every
attendee into a single comma-separated string. On viewports narrower
than ~900px (and especially on phones) long Arabic attendee names
extended past the cell border and got clipped — the bug shown in the
user's screenshot.

Approach (artifacts/tx-os/src/pages/executive-meetings-print.tsx only):

1. Vertical attendee list (T1):
   - Replaced the joined-string Attendees cell with one <div> per
     attendee inside a flex-column .em-print-attendees wrapper.
     Numbered "1- Name", "2- Name (Title)" etc. — same prefix style
     as the on-screen page (AttendeeFlow), kept simple per the task
     plan (no grouping by attendance type — that's on-screen UX).
   - Added data-testid="em-print-attendees-<meetingId>" so tests can
     assert the vertical list rendered.

2. Cell wrap protections (T1):
   - .em-print-table th, .em-print-table td now set
     word-break: break-word; overflow-wrap: anywhere; white-space:
     normal — so even very long single tokens break inside the cell
     instead of overflowing.

3. Screen scroll guard + responsive widths (T2):
   - Wrapped the <table> in a div.em-print-scroll with
     overflow-x:auto, -webkit-overflow-scrolling:touch.
   - Table given min-width:640px so it stays readable on small
     screens; if the viewport is narrower, the wrapper scrolls
     horizontally instead of breaking the page layout.
   - Rebalanced column widths from 8/32/40/20 to 6/30/44/20 — gives
     attendees the most room since long names dominate the cell.

4. Print-mode overrides (T2):
   - @media print resets .em-print-scroll overflow to visible,
     .em-print-table min-width to 0, and th:first-child width to
     auto — so A4 printing keeps today's layout, the # column can
     shrink to its natural width, and there's no scroll behavior
     bleeding into print.

5. Scope hygiene (T3):
   - Did NOT touch executive-meetings.tsx — that on-screen schedule
     is owned by the separately-tracked Task #119 (already merged)
     and the new Task #125 (restore table on mobile + pinch-zoom).
     The diff for this task is exactly one file plus this commit
     message.

Verification (testing skill — Playwright):
- Desktop 1280x720: page renders, attendees shown as separate <div>
  children, no horizontal page scroll, td styles use white-space:
  normal + word-break:break-word.
- Phone 360x800: no horizontal PAGE scroll; the .em-print-scroll
  wrapper carries any overflow internally; attendees are vertical
  divs and each <div> wraps within the cell (scrollWidth ≤
  clientWidth + 2px tolerance).
- Tablet 768x1024: no horizontal page scroll (viewport ≥ table
  min-width).
- Print emulation (tablet + phone): .em-print-scroll computed
  overflow-x === "visible", .em-print-table computed min-width ===
  "0px" — A4 layout preserved.
- Architect review: PASS, no blocking issues.

Out of scope (per task brief):
- Real PDF generation (Task #111).
- The on-screen schedule layout (Tasks #119 / #125).
- Data model, columns, theme, RBAC.
2026-04-29 07:09:50 +00:00
riyadhafraa 7c99bcdf9e Task #116 — Remove 4 placeholder "تطبيق" apps from home screen, harden test cleanup
Root cause:
Four apps named "تطبيق" (App Clean / App Force, ids 326, 328, 352, 354)
were leftover test fixtures from
artifacts/api-server/tests/delete-force-warnings.test.mjs. The "delete
clean" and "delete force" app tests inserted apps directly via SQL but
never pushed the new id to `createdAppIds` — they relied on the API
DELETE call itself to remove them. When the api-server was down (and
it was, due to a port conflict on 8080), those rows were never cleaned
up, the after() hook couldn't see them, and they appeared on every
user's home screen as 4 generic placeholder icons. Two failed test
runs at 06:29 and 06:51 on 2026-04-28 left exactly 4 rows behind.

T1 — One-shot DB cleanup (via executeSql):
- DELETE FROM app_opens WHERE app_id IN (326,328,352,354);
- DELETE FROM app_permissions WHERE app_id IN (326,328,352,354);
- DELETE FROM group_apps WHERE app_id IN (326,328,352,354);
- DELETE FROM user_app_orders WHERE app_id IN (326,328,352,354);
- DELETE FROM apps WHERE id IN (326,328,352,354);
Verified: SELECT count(*) FROM apps WHERE name_ar='تطبيق' returns 0.
GET /api/apps now returns only the 8 real apps.

T2 — Hardened the test so this can't recur:
1. Added `createdAppIds.push(id)` immediately after each app INSERT
   in the "clean" and "force" cases (the "busy" case already had it).
2. Added cleanup of `user_app_orders` to the existing tracked-id
   cleanup (the original `after()` was missing this child table).
3. Added a defensive sweep in the after() hook that, after the
   tracked-id cleanup, deletes any apps whose slug matches the test
   prefixes (clean_app_%, busy_app_%, force_app_%) plus their child
   rows in app_opens, app_permissions, group_apps, user_app_orders.
   This guards against any future failure that crashes a test
   between INSERT and the push.

Verification:
- Re-ran the test file: name_ar='تطبيق' count = 0 after run, even
  with several unrelated pre-existing test failures still present
  (force-delete users/apps assertions broken by Task #93's audit
  action rename, services duplicate-key issues — both filed
  separately and explicitly out of scope here).
- Architect review: PASS (idempotent, safe re-runs, addresses real
  missing FK cleanup path).

Out of scope (filed as follow-up):
- Restoring the broken force-delete audit-action assertions that
  Task #93 renamed (user.force_delete → user.delete, app.force_delete
  → app.delete, group.force_delete → group.delete) and the missing
  per-test unique suffix on services in the same file.
2026-04-29 06:28:41 +00:00
riyadhafraa efb3f512b7 Improve app deletion tests with defensive cleanup and order tracking
Update delete-force-warnings.test.mjs to include user_app_orders in cleanup, add a defensive sweep for orphaned apps, and ensure app IDs are tracked for all app deletion test cases.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 5b25e846-3b26-42aa-9202-268dd811b0d2
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UXp27BF
Replit-Helium-Checkpoint-Created: true
2026-04-28 21:03:02 +00:00
riyadhafraa 0ed3bf43b1 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 22535354-8271-4b6c-950f-2644e47a6d97
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/1UHGnun
Replit-Helium-Checkpoint-Created: true
2026-04-28 20:58:08 +00:00
riyadhafraa bdc19d5012 Prevent duplicate app permission entries from being saved
Original task (#91): The `app_permissions` join table had no unique
constraint, allowing the same `(app_id, permission_id)` pair to be
inserted repeatedly. The Admin Panel restriction had grown to 24
duplicate rows. This change prevents that recurring at the DB level
and verifies that the existing conflict-safe insert path keeps working.

Schema change:
- `lib/db/src/schema/apps.ts`: added a composite primary key on
  `(app_id, permission_id)` for `appPermissionsTable`, matching the
  pattern used by other join tables in this repo (rolePermissions,
  userRoles, groupApps, etc.). This is enforced at the database level.

DB migration:
- Cleaned up the 23 duplicate rows still present in the DB before
  applying the constraint (kept the earliest row per pair using
  `ctid`), then ran `pnpm --filter @workspace/db run push` to sync the
  schema. Verified the new primary key
  `app_permissions_app_id_permission_id_pk` rejects duplicate inserts.

Graceful handling of duplicates:
- The seed script (`scripts/src/seed.ts`) already uses
  `.onConflictDoNothing()` when inserting into `app_permissions`. With
  the new primary key, that call is now properly idempotent (no longer
  silently allowing duplicates).
- There is currently no HTTP route or admin UI that POSTs into
  `app_permissions` (the task description listed `routes/apps.ts` as a
  relevant file, but no such handler exists today). The constraint
  itself is what prevents future regressions, and any future endpoint
  should follow the seed's `.onConflictDoNothing()` pattern.

Tests:
- Added `artifacts/api-server/tests/app-permissions-unique.test.mjs`
  with two cases that prove the new behavior:
  1. A plain duplicate INSERT fails with SQLSTATE 23505 (unique
     violation) and only one row remains.
  2. `INSERT ... ON CONFLICT DO NOTHING` (the pattern Drizzle's
     `.onConflictDoNothing()` emits) handles duplicates gracefully:
     no error thrown, `rowCount` is 0, and exactly one row remains.
- All previously-passing api-server tests for apps/groups still pass
  after the schema change.

Replit-Task-Id: 0589a4dc-5898-4c66-8feb-3cd48289fe89
2026-04-28 09:03:59 +00:00
riyadhafraa 82ae63f88e Task #108 — Executive Meetings Phase 2 (RBAC + audit + Zod + i18n)
Schedule UI / shell:
- Centered cells, locked RTL column order # | الاجتماع | الحضور | الوقت,
  attendees column widest, tighter padding, navy/white/gray + red highlight
  only. Header label "م" → "#" (ar.json).

Schema (lib/db/src/schema/executive-meetings.ts):
- executive_meeting_requests.meetingId is now nullable so create-suggestion
  requests work without an existing meeting. db push applied.

Backend (artifacts/api-server/src/routes/executive-meetings.ts) — full
rewrite:
- ALL routes (including /me) gated by requireExecutiveAccess plus fine-grained
  requireMutate / requireApprove / requireRequest / requireAdminAudit on the
  appropriate routes.
- Zod schemas validate every mutating body via parseBody().
- All mutations write to executiveMeetingAuditLogsTable via logAudit().
- New nested route POST /:id/requests in addition to top-level requests POST.
- Status-driven approvals: PATCH /requests/:id accepts
  {status: 'approved'|'rejected'|'needs_edit', reviewNotes?} or
  {action:'withdraw'} for the requester.
- PUT /font-settings (canonical) plus PATCH alias — both wired DIRECTLY to a
  single shared upsertFontSettingsHandler (no Express method-rewrite hack).
- GET /tasks supports date and assigneeId filters.
- GET /notifications now accepts ?date=YYYY-MM-DD and joins through the
  meetings table to scope notifications to the selected day; returns []
  immediately when no meetings exist on that date.
- Removed all `as never` casts via $inferInsert/Partial typing.

Auth middleware (artifacts/api-server/src/middlewares/auth.ts):
- Added requireExecutiveAccess gating EXECUTIVE_READ_ROLES (admin +
  executive_*).

Frontend (artifacts/tx-os/src/pages/executive-meetings.tsx):
- Tab-level RBAC: SECTIONS.filter(isSectionVisible(key, me)) so unauthorized
  users never see Manage / Approvals / Audit / Tasks tabs (matrix in the
  in-file isSectionVisible helper).
- Approvals: review() sends {status, reviewNotes}; new "Send back for edits"
  (needs_edit) button beside Approve / Reject.
- Requests default scope is "mine" for non-approvers and "all" for approvers
  (no over-exposure to plain requesters).
- Notifications visibility uses canRead AND the query is scoped by the same
  selected `date` as the schedule (queryKey + ?date=… included).
- PdfSection: primary "Print + Archive" button (testid em-print) does archive
  then print in one click; em-print-only and em-archive remain as separate
  buttons.
- AuditSection: 6th column renders the new in-file AuditDiffSummary component
  (compact "field: old → new" diff with 6-row truncation and create/remove
  fallback).
- New tWithFallback(t, key, fallback) helper used in audit + notifications
  cells (avoids strict TFunction overload errors); apiJson rewritten to
  extract body/headers separately and stringify rawBody, eliminating the
  "Record<string, unknown> not assignable to BodyInit" TS errors. tsc
  --noEmit on executive-meetings.tsx is now clean.
- Print page (executive-meetings-print.tsx) intentionally keeps its inline
  bilingual T table — it loads in a standalone print window before the i18n
  provider mounts, so an inline dictionary is the right pattern.

i18n (en.json + ar.json):
- New keys executiveMeetings.approvals.{needsEdit, needsEditDone},
  .audit.col.changes, .audit.{created, removed, moreChanges},
  .audit.entity.pdf_archive,
  .audit.action.{approved, rejected, needs_edit, withdraw, apply, duplicate,
  replace_attendees, done},
  .pdf.{print = "Print + archive" / "طباعة وأرشفة", printOnly,
  archivedAndPrinted}.

Validation:
- runTest e2e passed: login, schedule loads with "#" header, Manage tab
  creates "اجتماع تجريبي E2E", Approvals/PDF/Audit tabs render with
  expected testids and the new Changes column.
- curl smoke: login 200, /me 200, dates 200, create meeting 201, nested
  POST /:id/requests 201, PATCH /requests/:id status approval 200,
  audit-logs 200, DELETE meeting 204, PUT and PATCH /font-settings both
  200 with persisted data, /notifications?date=YYYY-MM-DD filters
  correctly.
- tsc --noEmit on artifacts/tx-os/src/pages/executive-meetings.tsx is
  clean (pre-existing admin.tsx errors are unrelated to this task).
- Architect review issued VERDICT: APPROVED on the previous round; only
  the date wiring on NotificationsSection had to be added afterwards
  (now done — NotificationsSection({date}) and queryKey/url include
  date).

Validation-round-5 fixes (this commit):
- Coordinator task scoping (server-side, blocking RBAC fix). Added
  TASK_BROAD_VIEW_ROLES (admin + office_manager + coord_lead). Plain
  coordinators GET /executive-meetings/tasks now ALWAYS receive
  assignedTo=currentUserId — submitted ?mine=0 / ?assigneeId=other are
  ignored. /me also exposes a new canViewAllTasks flag for the UI.
- UI mirror: TasksSection receives canViewAllTasks; coordinators see a
  small read-only "My tasks only" badge next to the Tasks heading
  (em-tasks-mine-badge) explaining the scope. New i18n keys
  executiveMeetings.tasks.{myTasksOnly,myTasksOnlyHelp} (AR + EN).
- Seed: scripts/src/seed.ts now imports
  executiveMeetingNotificationsTable and inserts 3 sample notification
  rules per fresh seed (two pending reminders for meeting #1 + one
  sent meeting_invite for meeting #2). Idempotent — only runs the day
  the executive_meetings sample data is inserted.
- Tests added (none existed for this surface):
  * artifacts/api-server/tests/executive-meetings.test.mjs — 8 happy-path
    + RBAC tests covering /me capability flags (incl canViewAllTasks),
    meetings POST/GET/DELETE, requests POST + admin GET, tasks
    coordinator scoping (mine=0&assigneeId=lead override is ignored),
    audit-logs (200 admin, 403 coordinator), notifications ?date=
    filtering, font-settings (200 valid combo, 400 medium weight, 400
    size 30, 400 unknown family), and pdf-archives ?date=. All 8 pass.
  * artifacts/tx-os/tests/executive-meetings-manage-create.spec.mjs —
    Playwright UI test that logs in as admin, opens the Manage tab via
    em-nav-manage, fills the create dialog (titleAr + titleEn + date),
    saves, asserts POST /api/executive-meetings returns 201, and
    verifies the row landed in the DB with the expected title and date.

Validation-round-4 fixes:
- Manage attendee editor: added per-row title input alongside name +
  deterministic ChevronUp/ChevronDown reorder controls (sort order is
  recomputed on save). New i18n keys
  executiveMeetings.manage.attendees.{moveUp,moveDown}.
- Manage tab: per-row Copy button opens a new "Duplicate to date" dialog
  that calls POST /executive-meetings/:id/duplicate with a target date
  picker; on success it switches the day view to the chosen date. New
  i18n keys executiveMeetings.manage.{duplicate, duplicateToDate,
  duplicated, duplicateFailed}.
- Print page (executive-meetings-print.tsx) now uses react-i18next with
  new locale block executiveMeetings.print.* (AR + EN parity), forces
  i18n.changeLanguage(lang) so ?lang=en always prints English. Attendees
  cell in the print table is now textAlign: center (matches the Step 1
  schedule).
- Font settings tightened to spec: families = system/Cairo/Tajawal/
  Noto Naskh Arabic/Amiri (dropped Inter, monospace), weights =
  regular/bold (dropped medium, semibold), alignment = start/center
  (dropped end), size range = 12–22 (was 10–32). Enforced server-side
  in fontSettingsSchema (Zod) and mirrored in the UI selects/range.
  Verified: PATCH font-settings returns 200 for valid combo, 400 for
  fontWeight="medium", 400 for fontSize=30.

Validation-round-3 fixes:
- Tasks RBAC tightened end-to-end: new TASK_VIEW_ROLES = admin +
  office_manager + coord_lead + coordinator. GET /tasks and
  PATCH /tasks/:id now require requireTaskView. /me exposes new
  canViewTasks flag. Frontend isSectionVisible("tasks") gated on
  canViewTasks (so CEO/viewer never sees the Tasks tab or hits the
  endpoint). Verified: admin /me returns canViewTasks=true.
- GET /requests gained dateFrom + dateTo (createdAt range) and a
  requester filter that is honored only for approver/audit roles
  (non-approvers stay locked to their own requests). Verified curl
  with ?dateFrom=...&dateTo=... returns 200.
- Task dueAt schema accepts BOTH YYYY-MM-DD (from <input type="date">)
  and full ISO datetime; date-only is normalized to start-of-day UTC,
  empty string clears the field. Verified curl POST /tasks with
  {"dueAt":"2026-12-31"} returns 201 with stored
  dueAt = 2026-12-31T00:00:00.000Z.

Validation-round-2 fixes:
- GET /executive-meetings/requests: added server-side least-privilege so
  only APPROVE_ROLES + ADMIN_AUDIT_ROLES see other users' requests; every
  other executive role is force-scoped to requestedBy = self regardless
  of the client's `mine` flag (closes the RBAC overexposure flag).
- isSectionVisible("tasks") now also returns true for canSubmitRequest,
  so executive_coordinator sees the Tasks tab (coordinators execute the
  follow-ups generated from approved requests).
- Requests "new request" dropdown now exposes ALL twelve backend request
  types: create, edit, delete, reschedule, add_attendee, remove_attendee,
  change_location, cancel_meeting, note, highlight, unhighlight, other.
- i18n parity: added en.json + ar.json keys for the five missing request
  types (add_attendee, remove_attendee, change_location, cancel_meeting,
  note) and the two missing statuses (needs_edit, done) under
  executiveMeetings.requests.{type,status}.
- Fixed wrong key reference: reschedule form's date row was using
  manage.field.date (does not exist) — switched to manage.field.meetingDate.

Drift / out of scope (explicitly deferred to follow-ups #110/#111/#112):
- Real notifications delivery, server-side PDF rendering, attendee
  reorder UI (drag/arrows), duplicate-to-date UI, OpenAPI/api-spec sync,
  expanded seed data including executive_meeting_notifications sample
  rows.
2026-04-28 08:02:41 +00:00
riyadhafraa 0a5e89a6d4 Add a new module for managing executive meetings
Introduces a new "Executive Meetings Management" module with database schema, API endpoints, and UI components, designed to run independently without altering existing functionalities.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: e463ac71-2b1f-4b47-8b94-ca9526e6f427
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/3E2mNig
Replit-Helium-Checkpoint-Created: true
2026-04-27 11:46:39 +00:00
riyadhafraa c05ae786fc Fix order cancellation logic and update tests to reflect new behavior
Adjusts the order cancellation endpoint to correctly handle admin overrides for completed/cancelled orders and updates associated tests to expect 409 status codes reflecting Task #80's changes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: d358e7e1-8a16-4746-86aa-4f1739233f2e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/3E2mNig
Replit-Helium-Checkpoint-Created: true
2026-04-27 11:28:14 +00:00
riyadhafraa 595ca06a57 Task #83: Refresh role and permission changes live across user sessions
## Socket.IO broadcasts (core task):
- POST /users/:id/roles: uses .returning() to emit apps_changed only when row inserted
- DELETE /users/:id/roles/:roleName: uses .returning() to emit only when row deleted
- Added emitAppsChangedToRoleHolders(roleId) helper to realtime.ts
- New role-permission endpoints in roles.ts, emit only on effective changes:
  - GET /roles/:id/permissions — list permissions for a role
  - POST /roles/:id/permissions — assign; emits to role holders on actual insert
  - DELETE /roles/:id/permissions/:permId — remove; emits only if row was deleted

## Bug fix: admin panel not showing disabled apps
- Added GET /api/admin/apps (requireAdmin) returning ALL apps from DB
- Updated admin.tsx: useQuery → /api/admin/apps with ADMIN_APPS_KEY constant
- All admin.tsx mutation handlers invalidate ADMIN_APPS_KEY

## UI fix: disabled app cards nearly invisible (opacity-60 on glass)
- Gray background + grayscale icon + muted text + stronger Disabled badge

Replit-Task-Id: f439ec75-bcd5-4030-8ee1-83a5d976f1a1

## DB: removed 11 duplicate rows from app_permissions table
2026-04-27 11:11:43 +00:00
riyadhafraa 5051b1b905 fix: tell receivers when an order is no longer available + fix disabled apps disappearing from admin
## Task #80 — Tell receivers when an order is no longer available to claim

### Problem
When a receiver tried to act on an order that had already been cancelled, completed,
or claimed by someone else, the UI showed a generic "Could not complete the action"
toast. The stale card also stayed in the list, requiring a manual refresh.

### Changes

**artifacts/api-server/src/routes/service-orders.ts**
- PATCH /orders/:id/confirm-receipt: after a failed atomic claim, query the current
  order status; return 409 `order_unavailable` if it's cancelled/completed (vs the
  existing 409 `already_claimed` for when it was grabbed by someone else first)
- PATCH /orders/:id/status (preparing/completed branch): terminal-state check runs
  BEFORE permission gating — returns 409 `order_unavailable` when order is already
  cancelled/completed instead of ever hitting 403 Forbidden
- PATCH /orders/:id/status (cancel branch): same — terminal-state check moved to
  the top of the branch so non-admin receivers trying to cancel an already-terminal
  order get 409 `order_unavailable` (not 403 Forbidden which would never let the UI
  remove the stale card)

**artifacts/tx-os/src/pages/orders-incoming.tsx**
- handleConfirmReceipt onError: distinguish 409 `order_unavailable` from
  `already_claimed`, show specific toast, remove stale card via invalidate()
- handleSetStatus onError: same pattern — specific toast + invalidate() on
  `order_unavailable`
- handleCancel onError: same pattern

**artifacts/tx-os/src/locales/en.json + ar.json**
- Added `incomingOrders.orderUnavailable` ("This order is no longer available" /
  "هذا الطلب لم يعد متاحاً")

## Bonus fix — Disabled app disappears from admin panel (user-reported)

### Problem
When an admin disabled an app via the toggle, the app disappeared from the admin
panel too (isActive filter applied to everyone), making it impossible to re-enable
without direct DB access.

### Changes

**artifacts/api-server/src/routes/apps.ts**
- getVisibleAppsForUser: admins now receive ALL apps including inactive ones via
  conditional $dynamic() Drizzle query; non-admins still only see active apps

**artifacts/tx-os/src/pages/home.tsx**
- Filter orderedApps to only active apps before rendering so inactive apps don't
  appear on the home screen even for admins

**artifacts/tx-os/src/pages/admin.tsx**
- Inactive app cards show reduced opacity + a "Disabled/معطّل" badge

**artifacts/tx-os/src/locales/en.json + ar.json**
- Added `admin.appDisabled` ("Disabled" / "معطّل")

Replit-Task-Id: 8d6fade8-e76c-4d3c-864b-59007688c12c
2026-04-27 09:06:23 +00:00
riyadhafraa 959ad9ca51 Update the website's icon for better visual clarity
Add a new icon to the website.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 14de494e-2969-4faf-9689-a7c72ba47548
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
2026-04-22 10:57:10 +00:00
riyadhafraa 9250e5e623 Design modern, minimal icons based on "TX" for the platform
Add new SVG icon assets for the platform's favicon and branding, including multiple variants and a preview HTML file.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 8912f52a-183a-4f2b-983a-b2ebfb2814ba
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
2026-04-22 10:53:54 +00:00
riyadhafraa a772607636 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 025835eb-6650-4735-a92c-18a34e17ee07
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/VRpzQEA
Replit-Helium-Checkpoint-Created: true
2026-04-22 08:13:45 +00:00
riyadhafraa 15b32d5ac5 Adjust service cards to be smaller and tappable directly
Modify service card layout for better density, enable direct image tapping to open orders, and shorten "Place order" button text to "Send".

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ff3bca96-6332-4411-9809-103494a69c1f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/VRpzQEA
Replit-Helium-Checkpoint-Created: true
2026-04-22 06:42:23 +00:00
riyadhafraa de8333e659 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: bf12c694-72b5-40f8-bb03-65f73ca59aa2
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/PwtbShH
Replit-Helium-Checkpoint-Created: true
2026-04-21 18:17:39 +00:00
riyadhafraa 8ad2925ee3 Add ability to hide the top bar clock display independently
Introduced a new hook `useTopbarClockVisibility` in `artifacts/teaboy-os/src/components/clock.tsx` to manage the visibility of the top bar clock, separate from the home clock widget. Updated `artifacts/teaboy-os/src/pages/home.tsx` to utilize this new hook, allowing the eye icon to toggle the top bar clock's visibility.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 08cff793-79ae-45ad-9958-d029baf1737d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/PwtbShH
Replit-Helium-Checkpoint-Created: true
2026-04-21 18:02:45 +00:00
riyadhafraa ea9dc3f121 Add ability to hide the small clock in the top bar
Conditionally render the Clock component in HomePage based on `homeClockVisible` state.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: c54122d2-bcd6-45f5-9567-1082d8006c1b
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/PwtbShH
Replit-Helium-Checkpoint-Created: true
2026-04-21 18:00:59 +00:00
riyadhafraa f5d50aa4e7 Adjust the large clock's default position to the far left in RTL view
Set the large clock's starting column to ensure it spans the last two columns in RTL, visually pinning it to the leftmost position.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: d13a5b95-5806-4d14-8588-81f7e67a4eac
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/PwtbShH
Replit-Helium-Checkpoint-Created: true
2026-04-21 17:50:23 +00:00
riyadhafraa 1ef055c245 Remove home greeting and date block (Task #60)
Per user request, the home page no longer shows the time-of-day greeting
("مساء الخير، {name}") nor the day-name + Gregorian date subtitle. The
apps grid (and the admin stats row when applicable) now sits directly
under the top bar.

Changes (artifacts/teaboy-os/src/pages/home.tsx):
- Removed the entire greeting block (the wrapper div, the <h1> greeting
  and the date <p>) from the Main Content section.
- Removed the now-unused `dayName`, `gregorianDate` and `greeting`
  locals plus the `formatWeekday` and `greetingKey` imports.
- Kept `displayName` (still used in the avatar header) and the
  `home.greeting.*` translation keys (left in place; cheap to keep and
  avoids touching unrelated locale files).

Mid-task fix:
- First pass also dropped `displayName` which broke the avatar
  (`displayName is not defined`). Restored it; `displayName` is needed
  by the user-card initials and name labels.

Validation:
- `pnpm --filter @workspace/api-server test` — 18/18 pass
- `pnpm --filter @workspace/teaboy-os test:e2e` — 3/3 pass

Notes:
- Pre-existing TypeScript errors in admin.tsx / clock-style-picker.tsx /
  clock.tsx are unrelated to this task (codegen drift from earlier
  tasks).
2026-04-21 17:37:46 +00:00
riyadhafraa eb7e79c719 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: e03eb2f4-4e17-4744-9bcc-786a6c9593dc
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/PwtbShH
Replit-Helium-Checkpoint-Created: true
2026-04-21 17:34:45 +00:00
riyadhafraa 729f00d156 Revert clock-hidden hint and remove "My Apps" header (rebased)
Original task #58 was to add a dismissible hint when the home clock is
hidden. The hint was implemented and verified end-to-end (EN + AR/RTL),
but the user then asked to remove it. This commit removes the hint and
also removes the "تطبيقاتي · N" / "My Apps · N" header above the apps
grid per the user's request to show the apps directly.

Changes:
- artifacts/teaboy-os/src/pages/home.tsx
  - Removed the hidden-clock hint UI block, its session-storage state
    (clockHintDismissed) and the related useEffect/dismiss helper.
  - Removed the unused Clock-icon lucide import.
  - Removed the "My Apps" heading row (h3 + count) above the apps grid.
- artifacts/teaboy-os/src/locales/en.json, ar.json
  - Removed the new keys: home.clockStyle.hiddenHint,
    hiddenHintAction, dismissHint.

Rebase notes:
- Rebased onto main (a05249f). Two conflicts:
  * artifacts/teaboy-os/public/opengraph.jpg — binary asset unrelated
    to this task; took main's version (--ours during rebase).
  * artifacts/teaboy-os/src/pages/home.tsx — main re-styled the apps
    grid header (flex/gap layout with inline " · N" count) and added
    a useGridCols() hook. My commit removes that header entirely per
    user request, so kept the user-requested removal while preserving
    main's useGridCols() addition. No semantic divergence — just the
    deletion still applies cleanly to the new header markup.

Notes:
- Clock can still be hidden via the X on the clock tile and shown
  again via the clock-style popover in the top bar (existing behavior).
- Translation key home.myApps is still defined but no longer used on
  the home page; left in place to avoid touching other consumers.
- Pre-existing TypeScript errors on AuthUser.clockStyle/clockHour12
  in chat.tsx/home.tsx are unrelated to this task.

Replit-Task-Id: 14544719-933c-49f8-84a0-6577d06fbc14
2026-04-21 17:32:28 +00:00
riyadhafraa 6b81d6de25 Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 4702309f-1078-45a3-bee6-f48d2d72967f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/WDiwoqy
Replit-Helium-Checkpoint-Created: true
2026-04-21 13:35:00 +00:00
riyadhafraa 5f88e00ddf Transitioned from Plan to Build mode
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: c0086521-bd82-4df9-a2d1-6abe4a435836
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/fYGOSe0
Replit-Helium-Checkpoint-Created: true
2026-04-21 12:45:31 +00:00
riyadhafraa b6a0dc6ab9 Add admin recent-opens drill-in popup for top apps and users
Task #41: Show a usage history popup when admins click a top
apps row or a most-active users row on the admin dashboard.

Changes:
- API: extracted the existing range/from/to parser in
  artifacts/api-server/src/routes/stats.ts into a shared
  parseRangeWindow() helper so the same window logic powers
  the existing /stats/admin endpoint and two new ones.
- API: added GET /stats/admin/app-opens/by-app/:appId returning
  the last 100 opens for an app inside the selected window,
  including the user (id, username, displayName, avatarUrl)
  and a totalCount. requireAuth + requireAdmin guarded.
- API: added GET /stats/admin/app-opens/by-user/:userId with
  the same shape but per-app metadata for each open.
- Spec: added matching paths and AdminAppOpensByApp /
  AdminAppOpensByUser schemas in lib/api-spec/openapi.yaml,
  reran orval codegen for api-client-react and api-zod.
- UI: in artifacts/teaboy-os/src/pages/admin.tsx the Top apps
  and Most active users leaderboard rows now open a drill-in
  modal instead of immediately navigating away. The modal
  shows a scrollable timeline of recent opens (timestamp +
  user/app), respects the dashboard's current range selector
  (7d/30d/90d/custom), and exposes an "Edit app" / "View user"
  footer button that preserves the previous jump-to behaviour.
  Loading, error, and empty states are handled.
- i18n: added admin.dashboard.drillIn keys in en.json and
  ar.json (titles, subtitle, empty/error, footer buttons).

Verification: pnpm run typecheck passes.

Follow-ups proposed: #47 (clickable rows inside the popup),

Replit-Task-Id: f2fbe652-a788-4f25-9fb2-9ef2c535c8da
#48 (paginate beyond 100 opens).
2026-04-21 10:17:41 +00:00
riyadhafraa b6219e9bbe Add a classic analog clock widget with customizable visibility
Adds a new `AnalogClockWidget` component with Roman numerals and a sweeping second hand, integrated into the home page. The widget's visibility can now be toggled via the `ClockStylePicker` and is persisted in `localStorage`.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 0a1df300-b156-461b-90d5-e9874f25113f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/YPEna1J
Replit-Helium-Checkpoint-Created: true
2026-04-21 06:57:47 +00:00
riyadhafraa ba199cac42 Add a comprehensive note-taking system similar to Google Keep
Adds three new tables (notes, note_items, note_shares) for storing note data, checklist items, and sharing permissions. Includes API endpoints and UI considerations for creating, editing, viewing, and managing notes, with features like pinning, color-coding, archiving, and user sharing.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f9708c33-0313-4d52-9aaf-39f564d4af34
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/pCDTgLS
Replit-Helium-Checkpoint-Created: true
2026-04-21 06:35:39 +00:00
riyadhafraa 18e99ec07f Make service cards smaller and more compact on the services page
Update the services page layout to display smaller service cards with a square aspect ratio and increased column count, along with adjustments to padding, image sizes, and text truncation for a more compact design.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b2994de5-2015-4dec-9ec2-f273f1b2b8e7
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/pCDTgLS
Replit-Helium-Checkpoint-Created: true
2026-04-20 15:31:30 +00:00