Commit Graph

675 Commits

Author SHA1 Message Date
riyadhafraa 7a71befb62 PDF: enlarge header logo and center attendees column (Task #364)
- Increased logoBoxSize multiplier from 1.8 to 2.8 so the header logo
  renders noticeably larger in the PDF output.
- Changed attendees column text alignment from direction-based (right for
  RTL) to "center" so attendee names are centered in their column.
- Both AR and EN PDFs verified: valid PDF output, logo embedded, correct
  alignment. Code review PASSED, e2e tests PASSED.
2026-05-04 08:12:02 +00:00
riyadhafraa e6a43b85bf 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: c50a00a7-7b14-4d88-b65c-7ca92171a73e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/bEwHL7p
Replit-Helium-Checkpoint-Created: true
2026-05-04 08:09:03 +00:00
riyadhafraa af9d214e37 Fix PDF formatting: subheading groups, vertical centering, "#" header (Task #361)
- Changed attendee subheading formatting from inline join to grouped with
  newline separators between subheading sections. Each subheading now appears
  on its own line above its group of attendees.
- Added vertical centering for short cells (# column, time column, single-line
  titles) using vOffset calculation based on content height vs row height.
- "#" column header label already applied in pdf-labels.ts (from earlier edit).
- Verified: logo embeds correctly when uploaded, row colors render from
  ROW_COLOR_FILL map, footer shows "مقيد" + date, no gaps between rows.
- Code review PASSED, e2e tests PASSED (both AR and EN PDF generation).
2026-05-04 07:57:47 +00:00
riyadhafraa 57096e69a5 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: 6a7f1a74-369f-4e38-9f13-96369912cc6c
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/TFW8qXL
Replit-Helium-Checkpoint-Created: true
2026-05-04 07:50:43 +00:00
riyadhafraa e802a93b14 Compact PDF table layout to match reference design (Task #359)
Changes to artifacts/api-server/src/lib/pdf-renderer.ts:

- Reduced cellPadX from 6 to 4 (horizontal cell padding)
- Reduced cellPadY from 3 to 2 (vertical cell padding)
- Tightened lineHeight multiplier from 1.25 to 1.2
- Removed +2 padding buffer from heightOfString measurement loop
- Removed +2 from drawWrappingLine return value
- Tightened drawMixedLine return value (1.25→1.2)
- Consolidated header spacing from two moveDown(0.2+0.4) to one moveDown(0.3)
- Tightened title Y offset multiplier from 1.25 to 1.2
- Fixed row height probe to detect script per-line (matching draw path)
  instead of per-cell, preventing measurement/draw mismatch in
  mixed Arabic/Latin content now that the +2 cushion is removed

Result: Table rows are compact with no visible gaps between them,
matching the user's reference PDF (attached_assets/rrr1_1777879598338.pdf).
Row colors, cell borders, and footer positioning are unchanged.
Both AR and EN PDFs generate successfully (200 status).
2026-05-04 07:35:52 +00:00
riyadhafraa 10679127e6 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: 6f5061c3-9885-4173-9988-00e335acadc8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/HhIDQsV
Replit-Helium-Checkpoint-Created: true
2026-05-04 07:29:50 +00:00
riyadhafraa ec7bbb229d Task #357: Show attendee groups correctly in the upcoming meeting alert
The upcoming meeting alert's details panel was grouping attendees by
the `attendanceType` field, but in practice every attendee is stored
as type=internal regardless of their actual group. Users define groups
via subheading rows in the schedule editor (e.g. "الحضور الخارجي",
"الحضور الداخلي"), but the alert stripped subheadings and lumped
everyone under "داخلي".

Fix: Rewrote the grouping logic in DetailsPanel to walk through the
attendees array in order, using subheading rows as natural group
separators. Each subheading becomes a bold, clearly visible group
header (text-xs font-bold) instead of the old tiny 10px/60% opacity
labels. Person rows are listed under their preceding subheading.

Edge cases handled:
- Persons before any subheading: shown without a group header
- No subheadings at all: flat list without misleading "داخلي" label
- Empty subheading groups (heading with no persons): skipped
- Person count still counts only persons, not subheadings

Changed file:
- artifacts/tx-os/src/components/executive-meetings/upcoming-meeting-alert.tsx
  (DetailsPanel component, lines ~1744-1885)
2026-05-04 07:17:51 +00:00
riyadhafraa 17f1771659 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: 7d330ba0-531c-4dc4-a3bd-3ac086027533
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/HhIDQsV
Replit-Helium-Checkpoint-Created: true
2026-05-04 07:15:12 +00:00
riyadhafraa bcebd7c83e Task #355: Highlight all overlapping current meetings
Previously, `computeCurrentMeetingId` returned the first meeting whose
time range contained the current minute, so only one row ever got the
green highlight. When two meetings overlapped (e.g., 9:30–10:00 and
9:45–10:15), only the first was highlighted.

Changed `computeCurrentMeetingId` → `computeCurrentMeetingIds` to
return a `ReadonlySet<number>` of ALL matching meeting ids. Updated the
`useMemo` and the `isCurrent` prop on each row from
`currentMeetingId === m.id` to `currentMeetingIds.has(m.id)`.

A module-level `EMPTY_MEETING_SET` constant avoids allocating a new
empty Set on every tick when no meetings are in progress.

Changed file:
- artifacts/tx-os/src/pages/executive-meetings.tsx
  - Lines 369-396: new computeCurrentMeetingIds function
  - Line 1092: useMemo now uses computeCurrentMeetingIds
  - Line 2782: isCurrent uses .has() instead of ===
2026-05-04 07:05:40 +00:00
riyadhafraa acd1199d61 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: f68e6e2d-59e7-427a-8220-1cead24caf3b
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/HhIDQsV
Replit-Helium-Checkpoint-Created: true
2026-05-04 07:03:37 +00:00
riyadhafraa d310c6b8cb Task #353: Fix current-meeting highlight sticking after device sleep
The green highlight on the currently-in-progress meeting row would get
stuck when the user locked their iPad, switched apps, or the device
slept. The 60-second setInterval timer that drives `nowTick` is frozen
by the browser during background/sleep states. When the user returned,
the timer wouldn't fire immediately, leaving the highlight on the
old meeting.

Fix: Added a `visibilitychange` event listener alongside the existing
interval timer. When the document becomes visible again, it immediately
calls `setNowTick(Date.now())`, which triggers `computeCurrentMeetingId`
to re-evaluate via the existing useMemo dependency. The listener is
properly cleaned up in the effect's return function.

Changed file:
- artifacts/tx-os/src/pages/executive-meetings.tsx (lines 1065-1084)
2026-05-04 06:51:50 +00:00
riyadhafraa 7473e6f2ea Task #352: Make logo upload always visible for admin users
Previously, the logo upload field in Font Settings was hidden behind two
conditions: canEditGlobal AND scope === "global". Admin users had to
first switch the scope dropdown to "Global" before the logo upload
appeared, making it very hard to discover.

Changes:
- Frontend (executive-meetings.tsx): Removed `scope === "global"`
  condition from logo upload rendering — now shows whenever
  `canEditGlobal` is true (admin/executive_office_manager).
- Frontend: Save function now always sends `logoObjectPath` when
  `canEditGlobal` is true, regardless of selected scope.
- Backend (executive-meetings.ts): When an admin saves with user scope
  and includes logoObjectPath, the logo is written to the global row
  (upsert) in the same transaction. This preserves the global-only
  semantics while allowing admins to update the logo from any scope.
- When no global row exists, new row uses safe defaults (system font,
  14px, regular, start, #000000) instead of copying the user's prefs.
- Removed the 400 rejection for logoObjectPath on user-scope saves.
- Authorization preserved: only EM_ADMIN_ROLES can trigger logo writes.

Tested:
- API: PATCH with scope=user + logoObjectPath correctly updates global row
- E2E: Logo upload field visible for admin in user scope (confirmed)
- Code review: APPROVED (addressed comment about safe defaults)
- Pre-existing test failures unchanged (PDF font assertion, notification tests)
2026-05-04 06:44:02 +00:00
riyadhafraa bd939e6f6e Task #352: Make logo upload always visible for admin users
Previously, the logo upload field in Font Settings was hidden behind two
conditions: canEditGlobal AND scope === "global". Admin users had to
first switch the scope dropdown to "Global" before the logo upload
appeared, making it very hard to discover.

Changes:
- Frontend (executive-meetings.tsx): Removed `scope === "global"`
  condition from logo upload rendering — now shows whenever
  `canEditGlobal` is true (admin/executive_office_manager).
- Frontend: Save function now always sends `logoObjectPath` when
  `canEditGlobal` is true, regardless of selected scope.
- Backend (executive-meetings.ts): When an admin saves with user scope
  and includes logoObjectPath, the logo is written to the global row
  (upsert) in the same transaction. This preserves the global-only
  semantics while allowing admins to update the logo from any scope.
- When no global row exists, new row uses safe defaults (system font,
  14px, regular, start, #000000) instead of copying the user's prefs.
- Removed the 400 rejection for logoObjectPath on user-scope saves.
- Authorization preserved: only EM_ADMIN_ROLES can trigger logo writes.

Tested:
- API: PATCH with scope=user + logoObjectPath correctly updates global row
- E2E: Logo upload field visible for admin in user scope (confirmed)
- Code review: APPROVED (addressed comment about safe defaults)
- Pre-existing test failures unchanged (PDF font assertion, notification tests)
2026-05-04 06:42:38 +00:00
riyadhafraa fcdc1b8e8e Task #352: Make logo upload always visible for admin users
Previously, the logo upload field in Font Settings was hidden behind two
conditions: canEditGlobal AND scope === "global". Admin users had to
first switch the scope dropdown to "Global" before the logo upload
appeared, making it very hard to discover.

Changes:
- Frontend (executive-meetings.tsx): Removed `scope === "global"`
  condition from logo upload rendering — now shows whenever
  `canEditGlobal` is true (admin/executive_office_manager).
- Frontend: Save function now always sends `logoObjectPath` when
  `canEditGlobal` is true, regardless of selected scope.
- Backend (executive-meetings.ts): When an admin saves with user scope
  and includes logoObjectPath, the logo is written to the global row
  (upsert) in the same transaction. This preserves the global-only
  semantics while allowing admins to update the logo from any scope.
- Removed the 400 rejection for logoObjectPath on user-scope saves.
- Authorization preserved: only EM_ADMIN_ROLES can trigger logo writes.

Tested:
- API: PATCH with scope=user + logoObjectPath correctly updates global row
- E2E: Logo upload field visible for admin in user scope (confirmed)
- Pre-existing test failures unchanged (PDF font assertion, notification tests)
2026-05-04 06:40:35 +00:00
riyadhafraa 6a964ed9d9 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: c6f14bf2-1beb-4d63-b916-cc82cbea11ff
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/hqxYGHc
Replit-Helium-Checkpoint-Created: true
2026-05-04 06:34:58 +00:00
riyadhafraa f9d05421f6 Use actual font files in PDF generation and fix footer display
Update pdf-renderer.ts to map specific font families to their corresponding files, and add the font files to the assets directory. Adjust footer height calculation to ensure proper display.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 32f0ec38-20de-4de2-916d-1b5b97281170
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-04 06:19:52 +00:00
riyadhafraa d308b0a5ac Add custom Arabic font files for PDF generation
Uploads DIN Next LT Arabic font files (Regular and Bold) to the attached assets directory for use in PDF generation, replacing system fallbacks.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 93e41a01-e925-4b9c-8659-8917bbcf8455
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-04 06:13:12 +00:00
riyadhafraa 751d81b1f6 Update image file for font display clarification
Update attached image to illustrate font rendering and fallback mechanisms.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ff6817a2-f5cf-42bf-b743-cec3bf708ab5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 19:38:33 +00:00
riyadhafraa 78a1c5072d Task #351: Match Executive Meetings PDF to user's reference layout
User compared the rendered PDF against their reference (rrr1) and flagged
three concrete differences. This change addresses all three:

1. Time format — formatTimeRange now joins start/end with U+2011
   NON-BREAKING HYPHEN (no surrounding spaces): "9:40‑9:50" instead of
   "9:40 – 9:50". The non-breaking hyphen looks identical to ASCII "-"
   and prevents PDFKit from wrapping the range across two lines in the
   narrow Time column.

2. Date placement — removed the ISO date that was printed under the
   header title. Added a new bottom-aligned footer block on the leading
   edge (right for AR, left for EN) that prints:
     • "مقيد" / "Recorded by" (bold)
     • Long-form date "3 May 2026" (always Latin, English month name
       even on the AR PDF, mirroring the reference)
   The date row is forced to baseDirection="ltr" so RTL bidi doesn't
   visually reorder the runs into "May 2026 3" on the AR PDF.
   formatLongDate parses the ISO route param in UTC to avoid host-tz
   day drift. The footer is extracted into a drawFooter() helper and
   called in BOTH the empty-day and populated-day code paths so it
   always renders.

3. Title weight — title was already calling drawMixedLine with
   weight:"bold" but added explicit confirmation; column proportions
   tweaked (6/36/39/19) so the wider Time column doesn't force range
   wrapping while keeping enough room for Meeting/Attendees content.

Other touches:
- Added `recordedBy` to PdfLabels type + RenderPdfInput.labels type
  and to ar.json/en.json executiveMeetings.pdf blocks.
- Page-break check reserves footer height so the last row never
  overlaps the bottom footer.

Verified: PDF Arabic shaping regression test passes; live AR/EN PDFs
match the reference; one-page-fit still holds for typical days.
Pre-existing tsc errors in executive-meetings.ts and unrelated test
suite failures are not caused by this work.
2026-05-03 19:28:11 +00:00
riyadhafraa 0ef7d91cbe Task #351: Match Executive Meetings PDF to user's reference layout
User compared the rendered PDF against their reference (rrr1) and flagged
three concrete differences. This change addresses all three:

1. Time format — formatTimeRange now joins start/end with U+2011
   NON-BREAKING HYPHEN (no surrounding spaces): "9:40‑9:50" instead of
   "9:40 – 9:50". The non-breaking hyphen looks identical to ASCII "-"
   and prevents PDFKit from wrapping the range across two lines in the
   narrow Time column.

2. Date placement — removed the ISO date that was printed under the
   header title. Added a new bottom-aligned footer block on the leading
   edge (right for AR, left for EN) that prints:
     • "مقيد" / "Recorded by" (bold)
     • Long-form date "3 May 2026" (always Latin, English month name
       even on the AR PDF, mirroring the reference)
   The date row is forced to baseDirection="ltr" so RTL bidi doesn't
   visually reorder the runs into "May 2026 3" on the AR PDF.
   formatLongDate parses the ISO route param in UTC to avoid host-tz
   day drift. The footer is extracted into a drawFooter() helper and
   called in BOTH the empty-day and populated-day code paths so it
   always renders.

3. Title weight — title was already calling drawMixedLine with
   weight:"bold" but added explicit confirmation; column proportions
   tweaked (6/36/39/19) so the wider Time column doesn't force range
   wrapping while keeping enough room for Meeting/Attendees content.

Other touches:
- Added `recordedBy` to PdfLabels type + RenderPdfInput.labels type
  and to ar.json/en.json executiveMeetings.pdf blocks.
- Page-break check reserves footer height so the last row never
  overlaps the bottom footer.

Verified: PDF Arabic shaping regression test passes; live AR/EN PDFs
match the reference; one-page-fit still holds for typical days.
Pre-existing tsc errors in executive-meetings.ts and unrelated test
suite failures are not caused by this work.
2026-05-03 19:27:11 +00:00
riyadhafraa fa9b395be5 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: 06c7a404-fec6-4834-9c06-327a20dc608d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 19:17:44 +00:00
riyadhafraa 26a869c0eb PDF: inline attendees, tighter layout — fit a typical day on one page
Follow-up to the Arabic shaping fix. The downloaded schedule PDF was
spilling onto a second page even for modest days because (a) attendees
were stacked one-per-line and (b) the page header + cell padding +
column proportions reserved more vertical space than necessary.

Changes (artifacts/api-server/src/lib/pdf-renderer.ts):

- Attendees now render as a single inline paragraph per cell (joined
  with two spaces) and wrap naturally — matches the user's reference
  layout where attendees flow as one paragraph.
- Re-balanced column widths from 6/30/44/20 → 6/38/40/16. Meeting
  titles get more room (no more 6-line wraps for typical titles) and
  attendees get less (they're inline now).
- Page margin tightened 36pt → 24pt.
- Title size cap tightened (28 → 22) so the header band doesn't eat a
  table row.
- moveDown gaps trimmed (0.4→0.2 between title and date, 0.8→0.4
  before the table).
- Cell vertical padding trimmed (5pt → 3pt).
- drawWrappingLine now passes lineGap: -1 to pdfkit's text() so wrapped
  paragraphs get tighter inter-line spacing; the row-height probe
  passes the same option so probe and draw stay symmetric.
- Row-height probe now uses heightOfString() output directly
  (Math.max(lineHeight, measured) + 2) instead of rounding up to whole
  lineHeight units, removing the half-line of empty space that was
  visible below short cells.

Verified:
- EN: all 10 meetings on one A4 page.
- AR: 9 of 10 meetings on one A4 page; the 10th row contains
  multi-line internal/external subheadings which legitimately need a
  taller cell — typical days fit on a single page.
- PDF Arabic shaping regression test still passes.
- No row overlaps anywhere.

Logo: handler already loads the brand-settings logo (logoObjectPath)
and embeds it in the header — no code change needed. If the logo isn't
visible, none has been uploaded in brand settings.
2026-05-03 19:11:49 +00:00
riyadhafraa e0824bc6a0 Fix overlapping text and improve Arabic rendering in PDFs
Update PDF rendering logic to use fontkit for Arabic shaping and RTL reordering, eliminating pre-shaping and resolving text overlap issues.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 32b8a89e-ae1f-4a09-ae50-3a53ae3f3477
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 19:01:50 +00:00
riyadhafraa 6cbd5968e5 Ensure Arabic text displays correctly when printing documents
Update font configurations to exclusively use DIN Next LT Arabic, preload necessary font files, and implement eager font loading to prevent display issues during printing.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 814df7a0-9300-4efe-a4b5-b5e474e8c99f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 18:37:48 +00:00
riyadhafraa a4e58b2263 Improve dialog box alignment and spacing for better user experience
Update `DialogHeader` to use `sm:text-start` for RTL alignment and `DialogFooter` to use `gap-2` instead of `sm:space-x-2` for improved spacing in dialog components.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f335791d-bd37-4801-bf2e-18c339d6fdc5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 17:43:07 +00:00
riyadhafraa abaf54f254 Improve Arabic text rendering for printouts by preloading fonts
Preload DIN Next LT Arabic and Tajawal fonts in index.html and adjust the print CSS font stack to ensure correct Arabic character shaping in print mode.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 820da5a0-fe82-4670-9807-63a51f533e44
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 16:02:10 +00:00
riyadhafraa 03e92f9e8c Improve Arabic text display when printing the schedule
Add print-specific CSS to force Arabic web font and enable ligatures for proper shaping in print mode.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 93b31a62-139b-4870-b40e-2ffc4ec05a8d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 15:55:29 +00:00
riyadhafraa 627a8fe7b1 Fix Arabic text rendering in generated PDFs
Add Arabic text shaping functionality to convert base characters to their contextual presentation forms before bidi reordering and PDF rendering. Includes a new regression test to verify correct shaping by asserting the presence of presentation form codepoints in the PDF's embedded ToUnicode CMap.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 18d38e3a-cfe8-4b69-a02c-380283320b78
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 15:26:38 +00:00
riyadhafraa 62ee62e6fa Task #349 follow-up: brand-logo embed test + label module + user-scope guard
Original task: Executive Meetings PDF improvements — respect user
font prefs, render saved per-meeting rowColor, drop legacy
isHighlighted baking, brand logo on left of PDF header with title
"قائمة بأسماء حضور الاجتماعات", and a logo upload + font color
picker in the font-settings page.

Prior mark_task_complete was rejected for three issues. This commit
closes all of them:

- Extract bilingual PDF labels into
  artifacts/api-server/src/lib/pdf-labels.ts and import it from the
  PDF route. Mirror the same keys under executiveMeetings.pdf.* in
  the tx-os ar.json / en.json locales so the frontend stays in
  sync.
- Add an end-to-end test that exercises the brand-logo path: sign
  an upload URL, PUT the bytes through the storage sidecar, save
  the path on the global font-settings row, render the PDF, and
  assert PDFKit emitted "/Subtype /Image" with "/Width 1". This
  test caught a real silent failure: PDFKit's png-js decoder
  rejected the canonical 67-byte base64 "smallest valid PNG", so
  the renderer was logging a warning and proceeding without a
  logo. The fixture now constructs a real 1x1 RGB PNG inline using
  zlib + a CRC32 routine (no new dependencies). Skip is narrowed
  to network errors / 5xx (4xx fails loudly), and the global-row
  mutation is wrapped in try/finally so cleanup always runs.
- Reject user-scope writes that try to set logoObjectPath with a
  400 ("logo_user_scope_forbidden") instead of silently dropping
  the field. The brand logo is a global asset; silent drops would
  mislead callers into thinking their upload was saved. Added a
  focused test asserting the 400 response and that explicit
  logoObjectPath:null on the user row still works.

Also removed 12 stray backup/snapshot files at the repo root
(*.old, *-base.{ts,tsx,mjs}, locale snapshots) that were
accidentally tracked in the previous commit.

Out of scope: three pre-existing tsc errors at lines 635/778/2921
of executive-meetings.ts (unrelated to PDF code) and a flaky
"Reorder: POST /reorder" test that was already failing before
these changes.
2026-05-03 15:10:34 +00:00
riyadhafraa 64fcbb7a20 Task #349 follow-up: brand-logo embed test + label module + user-scope guard
Original task: Executive Meetings PDF improvements — respect user
font prefs, render saved per-meeting rowColor, drop legacy
isHighlighted baking, brand logo on left of PDF header with title
"قائمة بأسماء حضور الاجتماعات", and a logo upload + font color
picker in the font-settings page.

Prior mark_task_complete was rejected for three issues. This commit
closes all of them:

- Extract bilingual PDF labels into
  artifacts/api-server/src/lib/pdf-labels.ts and import it from the
  PDF route. Mirror the same keys under executiveMeetings.pdf.* in
  the tx-os ar.json / en.json locales so the frontend stays in
  sync.
- Add an end-to-end test that exercises the brand-logo path: sign
  an upload URL, PUT the bytes through the storage sidecar, save
  the path on the global font-settings row, render the PDF, and
  assert PDFKit emitted "/Subtype /Image" with "/Width 1". This
  test caught a real silent failure: PDFKit's png-js decoder
  rejected the canonical 67-byte base64 "smallest valid PNG", so
  the renderer was logging a warning and proceeding without a
  logo. The fixture now constructs a real 1x1 RGB PNG inline using
  zlib + a CRC32 routine (no new dependencies). Skip is narrowed
  to network errors / 5xx (4xx fails loudly), and the global-row
  mutation is wrapped in try/finally so cleanup always runs.
- Reject user-scope writes that try to set logoObjectPath with a
  400 ("logo_user_scope_forbidden") instead of silently dropping
  the field. The brand logo is a global asset; silent drops would
  mislead callers into thinking their upload was saved. Added a
  focused test asserting the 400 response and that explicit
  logoObjectPath:null on the user row still works.

Also removed 12 stray backup/snapshot files at the repo root
(*.old, *-base.{ts,tsx,mjs}, locale snapshots) that were
accidentally tracked in the previous commit.

Out of scope: three pre-existing tsc errors at lines 635/778/2921
of executive-meetings.ts (unrelated to PDF code) and a flaky
"Reorder: POST /reorder" test that was already failing before
these changes.
2026-05-03 15:06:14 +00:00
riyadhafraa 4640bda260 Task #349 follow-up: brand-logo embed test + label module + user-scope guard
Original task: Executive Meetings PDF improvements — respect user
font prefs, render saved per-meeting rowColor, drop legacy
isHighlighted baking, brand logo on left of PDF header with title
"قائمة بأسماء حضور الاجتماعات", and a logo upload + font color
picker in the font-settings page.

Prior mark_task_complete was rejected for three issues. This commit
closes all of them:

- Extract bilingual PDF labels into
  artifacts/api-server/src/lib/pdf-labels.ts and import it from the
  PDF route. Mirror the same keys under executiveMeetings.pdf.* in
  the tx-os ar.json / en.json locales so the frontend stays in
  sync.
- Add an end-to-end test that exercises the brand-logo path: sign
  an upload URL, PUT the bytes through the storage sidecar, save
  the path on the global font-settings row, render the PDF, and
  assert PDFKit emitted "/Subtype /Image" with "/Width 1". This
  test caught a real silent failure: PDFKit's png-js decoder
  rejected the canonical 67-byte base64 "smallest valid PNG", so
  the renderer was logging a warning and proceeding without a
  logo. The fixture now constructs a real 1x1 RGB PNG inline using
  zlib + a CRC32 routine (no new dependencies). Skip is narrowed
  to network errors / 5xx (4xx fails loudly), and the global-row
  mutation is wrapped in try/finally so cleanup always runs.
- Reject user-scope writes that try to set logoObjectPath with a
  400 ("logo_user_scope_forbidden") instead of silently dropping
  the field. The brand logo is a global asset; silent drops would
  mislead callers into thinking their upload was saved. Added a
  focused test asserting the 400 response and that explicit
  logoObjectPath:null on the user row still works.

Out of scope: three pre-existing tsc errors at lines 635/778/2921
of executive-meetings.ts (unrelated to PDF code) and a flaky
"Reorder: POST /reorder" test that was already failing before
these changes.
2026-05-03 15:03:54 +00:00
riyadhafraa f810afe0cd Task #349 follow-up: brand-logo embed test + PDF label module
Original task: Executive Meetings PDF improvements — respect user
font prefs, render saved per-meeting rowColor, drop legacy
isHighlighted baking, brand logo on left of PDF header with title
"قائمة بأسماء حضور الاجتماعات", and a logo upload + font color
picker in the font-settings page.

Prior mark_task_complete was rejected for three issues. The first
(stray backup files) was a false positive. This commit closes the
remaining two:

- Extract bilingual PDF labels into
  artifacts/api-server/src/lib/pdf-labels.ts and import it from the
  PDF route so the strings are no longer inlined inside the route
  handler. Mirror the same keys under executiveMeetings.pdf.* in
  the tx-os ar.json / en.json locales so the frontend stays in
  sync.
- Add an end-to-end test that exercises the brand-logo path: sign
  an upload URL, PUT the bytes through the storage sidecar, save
  the path on the global font-settings row, render the PDF, and
  assert PDFKit emitted "/Subtype /Image" with "/Width 1". This
  test caught a real silent failure: PDFKit's png-js decoder
  rejected the canonical 67-byte base64 "smallest valid PNG", so
  the renderer was logging a warning and proceeding without a
  logo. The fixture now constructs a real 1x1 RGB PNG inline using
  zlib + a CRC32 routine (no new dependencies) and the assertion
  passes.

Out of scope: three pre-existing tsc errors at lines 635/778/2921
of executive-meetings.ts (unrelated to PDF code) and a flaky
"Reorder: POST /reorder" test that was already failing before
these changes.
2026-05-03 14:59:23 +00:00
riyadhafraa 1c7c91e8f2 Task #349: Executive Meetings PDF improvements
- Schema: add `font_color` (hex, default #000000) and `logo_object_path`
  to `executive_meeting_font_settings`. Pushed via drizzle-kit.

- PDF renderer:
  - Add `fontColor` to PdfFontPrefs (body cells only; header chrome
    and logo intentionally ignore it to preserve branding).
  - Add `rowColor` to PdfMeeting and a ROW_COLOR_FILL palette kept in
    lockstep with the on-screen swatches; deliberately stop painting
    the legacy `isHighlighted` overlay so the archived PDF reflects
    editorial state instead of the viewer's transient cursor.
  - Add optional `logo: Buffer`. Header now reserves a left-anchored
    logo box and centers the title in the remaining width; bad image
    bytes log + fall back to the no-logo layout instead of crashing.

- API route:
  - Extend fontSettingsSchema with strict #RRGGBB regex and
    /^/objects/<id>$/ regex for logoObjectPath.
  - resolveFontPrefsForUser now returns { font, logoObjectPath }.
  - loadLogoBytes downloads the brand asset via ObjectStorageService.
  - Logo only writable on the global-scope row.
  - PDF labels switched to "قائمة بأسماء حضور الاجتماعات" /
    "Meeting Attendance List" per the user's printed sample.

- Frontend (executive-meetings.tsx):
  - FontPrefs gains fontColor; FontSettingsResponse.global gains
    logoObjectPath; DEFAULT_FONT and effectiveFont updated.
  - buildFontStyle applies fontColor to on-screen rows.
  - FontSettingsSection: native color picker + hex text input;
    logo upload (PNG/JPEG) via @workspace/object-storage-web's
    useUpload, visible only at the global scope for admins.

- Locales: AR/EN keys for fontColor + logo.{label,upload,replace,
  remove,uploading,uploadFailed,globalOnly}.

- Tests: existing font-settings roundtrip extended with fontColor;
  new test rejects malformed fontColor and non-/objects logo paths.
  Added "PDF content" test that inflates PDFKit FlateDecode streams
  and asserts (a) /Title carries the new Arabic label, (b) amber
  rowColor paints #fef3c7, (c) #fecaca isHighlighted overlay is
  gone, (d) user fontColor reaches body text fills.

- Frontend follow-up: logo preview in FontSettingsSection now uses
  resolveServiceImageUrl so /objects/<id> -> /api/storage/objects/<id>
  (the URL the API actually serves), matching chat-avatar plumbing.

Type-check clean for api-server and tx-os; new font-settings + PDF
content tests pass. Other test failures in the suite pre-date this
change.
2026-05-03 14:43:22 +00:00
riyadhafraa 57e8297464 Task #349: Executive Meetings PDF improvements
- Schema: add `font_color` (hex, default #000000) and `logo_object_path`
  to `executive_meeting_font_settings`. Pushed via drizzle-kit.

- PDF renderer:
  - Add `fontColor` to PdfFontPrefs (body cells only; header chrome
    and logo intentionally ignore it to preserve branding).
  - Add `rowColor` to PdfMeeting and a ROW_COLOR_FILL palette kept in
    lockstep with the on-screen swatches; deliberately stop painting
    the legacy `isHighlighted` overlay so the archived PDF reflects
    editorial state instead of the viewer's transient cursor.
  - Add optional `logo: Buffer`. Header now reserves a left-anchored
    logo box and centers the title in the remaining width; bad image
    bytes log + fall back to the no-logo layout instead of crashing.

- API route:
  - Extend fontSettingsSchema with strict #RRGGBB regex and
    /^/objects/<id>$/ regex for logoObjectPath.
  - resolveFontPrefsForUser now returns { font, logoObjectPath }.
  - loadLogoBytes downloads the brand asset via ObjectStorageService.
  - Logo only writable on the global-scope row.
  - PDF labels switched to "قائمة بأسماء حضور الاجتماعات" /
    "Meeting Attendance List" per the user's printed sample.

- Frontend (executive-meetings.tsx):
  - FontPrefs gains fontColor; FontSettingsResponse.global gains
    logoObjectPath; DEFAULT_FONT and effectiveFont updated.
  - buildFontStyle applies fontColor to on-screen rows.
  - FontSettingsSection: native color picker + hex text input;
    logo upload (PNG/JPEG) via @workspace/object-storage-web's
    useUpload, visible only at the global scope for admins.

- Locales: AR/EN keys for fontColor + logo.{label,upload,replace,
  remove,uploading,uploadFailed,globalOnly}.

- Tests: existing font-settings roundtrip extended with fontColor;
  new test rejects malformed fontColor and non-/objects logo paths.

Type-check clean for api-server and tx-os; new font-settings tests
pass. Other test failures in the suite pre-date this change.
2026-05-03 14:34:29 +00:00
riyadhafraa 9fdabc4794 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: 6174e31e-97f1-4520-b730-a4c0e860c044
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 14:22:10 +00:00
riyadhafraa 6de67b0ecb Update audit log table with reorganized columns and localized content
Reorganize audit log table columns for RTL rendering, remove entityId, and update localized keys and enum values.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 37907bed-f6b6-47d9-bffb-5a50bc660b83
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 14:19:06 +00:00
riyadhafraa b864b92dc6 Task #348: Arabic-friendly Executive Meetings audit log
- Reorder audit table columns to RTL order: المستخدم، الوقت، الإجراء،
  النوع، التغييرات. Drop the entityId ("المعرف") column entirely. Update
  loading/empty colSpan from 6 to 5 to match the new column count.
- Rename audit.col.actor copy: "المنفّذ" → "المستخدم" (ar.json) and
  "Actor" → "User" (en.json). Underlying data field name unchanged.
- Localize the Changes cell in AuditDiffSummary:
  - Field-name labels (titleAr, titleEn, meetingDate, dailyNumber,
    startTime, endTime, location, meetingUrl, platform, status,
    isHighlighted, notes, assignedTo, taskType, dueAt, reviewDecision,
    reviewNotes) now resolve via executiveMeetings.audit.field.* with a
    raw-key fallback so unknown future fields don't crash the row.
  - Enum values render via existing label namespaces:
    `platform` → executiveMeetings.platform.*, `status` →
    executiveMeetings.status.*, `isHighlighted` → executiveMeetings.common.yes/no.
  - Both the diff view and the single-side created/removed view use the
    same labelForKey/fmtValue helpers.
- Added matching audit.field.* keys to ar.json and en.json with sensible
  Arabic and English labels.
- No test changes needed: no existing test asserts on the old column
  count, the "Actor"/"المنفّذ" header, the entityId column, or raw
  English field names in Changes.
- tsc passes for @workspace/tx-os.
2026-05-03 13:58:26 +00:00
riyadhafraa 3d2d80c250 Task #348: Arabic-friendly Executive Meetings audit log
- Reorder audit table columns to RTL order: المستخدم، الوقت، الإجراء،
  النوع، التغييرات. Drop the entityId ("المعرف") column entirely. Update
  loading/empty colSpan from 6 to 5 to match the new column count.
- Rename audit.col.actor copy: "المنفّذ" → "المستخدم" (ar.json) and
  "Actor" → "User" (en.json). Underlying data field name unchanged.
- Localize the Changes cell in AuditDiffSummary:
  - Field-name labels (titleAr, titleEn, meetingDate, dailyNumber,
    startTime, endTime, location, meetingUrl, platform, status,
    isHighlighted, notes, assignedTo, taskType, dueAt, reviewDecision,
    reviewNotes) now resolve via executiveMeetings.audit.field.* with a
    raw-key fallback so unknown future fields don't crash the row.
  - Enum values render via existing label namespaces:
    `platform` → executiveMeetings.platform.*, `status` →
    executiveMeetings.status.*, `isHighlighted` → executiveMeetings.common.yes/no.
  - Both the diff view and the single-side created/removed view use the
    same labelForKey/fmtValue helpers.
- Added matching audit.field.* keys to ar.json and en.json with sensible
  Arabic and English labels.
- No test changes needed: no existing test asserts on the old column
  count, the "Actor"/"المنفّذ" header, the entityId column, or raw
  English field names in Changes.
- tsc passes for @workspace/tx-os.
2026-05-03 13:57:22 +00:00
riyadhafraa 0a18cf2abe Task start baseline checkpoint for code review
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ac46926c-da15-4b39-a44e-d8b743720927
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 13:54:24 +00:00
riyadhafraa 46dee9cf13 Update button order and clean up HTML content
Reorder UI buttons and sanitize HTML content in title and attendee fields.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 7ff9197c-e52a-49a8-b981-b98e20de8d21
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:59:13 +00:00
riyadhafraa 81f4d7cb55 Task #347: EM edit dialog — strip HTML in inputs + reorder/rename buttons
- safe-html.ts: add htmlToPlainText (DOMParser, br/block→\n, decode, collapse)
  and wrapAsParagraph (HTML-escape + <p>…</p>) helpers.
- executive-meetings.tsx:
  - openEdit() runs htmlToPlainText on titleAr, titleEn, and each attendee.name
    so the dialog's plain <Input> shows clean text instead of raw markup.
  - save() wraps titleAr, titleEn, and each attendee.name with wrapAsParagraph
    so the on-disk format stays byte-compatible with what the inline Tiptap
    EditableCell produces. Empty-title validation still fires off the plain
    string before wrapping.
  - Swapped MeetingFormDialog buttons: addSubheading now renders before the
    attendee-add button.
- ar.json: executiveMeetings.manage.attendees.add "إضافة حاضر" → "إضافة عضو".
- en.json: same key "Add attendee" → "Add member".
- Inline schedule cell chips (executiveMeetings.schedule.*) untouched.
2026-05-03 12:58:11 +00:00
riyadhafraa e695f5b4cb Improve attendee management by refining text handling and button labels
Introduce new utility functions for HTML to plain text conversion and paragraph wrapping in `safe-html.ts`. Update locale files (`ar.json`, `en.json`) to change "attendee" to "member" in the add button label. Modify `executive-meetings.tsx` to swap the functionality and labels of the "Add attendee" and "Add subheading" buttons.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: edd35722-74e8-4f8d-a3cc-bac6c5dccc51
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:54:50 +00:00
riyadhafraa 2278cb5567 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: a70f5e65-6c2c-46f9-a392-62d97ca24df2
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:53:08 +00:00
riyadhafraa 4845b00229 Update Arabic meeting alerts to use "next" and "starts after" phrasing
Update Arabic localization strings in `ar.json` and adjust conditional logic in `upcoming-meeting-alert.tsx` to implement the requested text changes for meeting alerts.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 1fa1a054-545f-4815-84a7-7b8664041413
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:49:59 +00:00
riyadhafraa 41cf24533e EM upcoming alert: Arabic copy tweaks (#346)
Two small Arabic-only string changes in the Upcoming Meeting alert
popup, requested off a screenshot:

1. Header title: "اجتماع يبدأ قريبًا" → "الاجتماع التالي".
2. Countdown line: replaced "خلال" with "بعد" so the alert reads
   "يبدأ بعد 5 دقائق" instead of "يبدأ خلال 5 دقائق". Applied to all
   plural forms (one/two/few/many/other). minutesAway_zero ("يبدأ
   الآن") and the separate startsNow key were left unchanged.

Files:
- artifacts/tx-os/src/locales/ar.json
  - executiveMeetings.alert.title
  - executiveMeetings.alert.minutesAway_{one,two,few,many,other}

Out of scope (untouched on purpose):
- en.json — user only asked about the Arabic copy.
- upcoming-meeting-alert.tsx — no logic/layout change needed; it
  already reads these keys via i18next.
- All other behavior (blink, drag, buttons, etc.) carried over from
  #344.

Verification:
- ar.json still valid JSON; key set unchanged.
- Pre-existing red workflows (api-server, combined `test`) are
  unrelated upstream failures and not caused by this change.
2026-05-03 12:49:15 +00:00
riyadhafraa d725a63db3 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: 75a790b7-ee08-416a-aec5-88fe90a5d590
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:48:03 +00:00
riyadhafraa de9b5954ce EM Notifications tab: remove header + intro + per-user prefs card (#345)
The user pointed out that the "بريد إلكتروني" channel they could see in
the per-user "تفضيلات التنبيهات الخاصة بي" card is not part of the
product (no email delivery wired up), so exposing the toggle was
misleading. They sent a screenshot and asked to delete everything in
it: the page heading "التنبيهات", the intro paragraph, and the entire
prefs card (event row + in-app/email switches + Save / Reset / Restore
defaults buttons).

Changes:
- artifacts/tx-os/src/pages/executive-meetings.tsx
  - Removed the <h2> heading, the <p> intro, and the
    <NotificationPrefsCard /> render call from the notifications page
    component. The audit-log table below stays untouched.
  - Deleted the NotificationPrefsCard component definition and the
    NotificationPref / NotificationPrefsResponse types (only consumers
    were inside the deleted card).
- artifacts/tx-os/src/locales/{ar,en}.json
  - Removed executiveMeetings.notificationsPage.heading,
    executiveMeetings.notificationsPage.intro, and the entire
    executiveMeetings.notificationsPage.prefs.* subtree.
  - Kept notificationsPage.col.*, type.*, status.*, empty — still used
    by the audit-log table.

Out of scope (intentionally not touched):
- Backend /api/executive-meetings/notification-prefs routes and the
  email channel in fan-out helpers. The backend defaults are left
  intact so notification fan-out keeps working; only the UI surface
  is removed.
- Tests: ripgrep showed no references to the removed test-ids
  (em-notification-prefs, em-pref-inapp-*, em-pref-email-*,
  em-pref-save, em-pref-reset, em-pref-restore-defaults) in the
  Playwright/unit suites, so nothing to update.

Verification:
- pnpm -C artifacts/tx-os exec tsc --noEmit clean.
- Switch import retained: still used by row-color popover and another
  call site in the same file.
- The api-server workflow and combined `test` workflow are red with
  pre-existing upstream failures unrelated to this UI-only change.
2026-05-03 12:43:02 +00:00
riyadhafraa d0038a0bd9 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: c6c7f89f-2076-4655-8246-744d340fea59
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:39:01 +00:00
riyadhafraa ff2304c4c2 tx-os(#344): polish UpcomingMeetingAlert per user feedback
Four UI changes requested by an Arabic-speaking user on the floating
"اجتماع يبدأ قريباً" popup:

1. Time window LTR — wrap the header `alert-time-window` span in
   `dir="ltr"` so the start time always appears on the left and the
   end on the right, even inside the Arabic RTL UI. Prevents the
   "م 3:20 – م 3:10" reversal seen in the user's screenshot.
2. Drop the duplicate "Time" row inside DetailsPanel — the same
   start/end window is already shown in the header strip. Removed
   the `if (timeWindow) rows.push({ key: "time", ... })` block; the
   `timeWindow` local was deleted along with it. Header is now the
   single source of truth.
3. Remove the labelled "تجاهل التنبيه" footer button. The X close
   button in the header still calls `handleDismiss`, so users can
   still dismiss; the action bar is now just Done / Postpone /
   Details. `handleDismiss`, `dismissToast`, and the i18n keys are
   left intact for the X button.
4. Make the "يبدأ خلال N دقائق" / "يبدأ الآن" countdown blink red.
   Added a dedicated `@keyframes em-blink` (1s, opacity 1 → 0.15 →
   1) and `.em-blink` class in `src/index.css`, with a
   `prefers-reduced-motion` override that pins it at full opacity.
   Countdown span uses `text-red-600 em-blink font-semibold`. The
   previously-used `accentText` local had no remaining consumers,
   so it was removed (replaced with an explanatory comment).

Files:
- artifacts/tx-os/src/components/executive-meetings/upcoming-meeting-alert.tsx
- artifacts/tx-os/src/index.css

Verification: `pnpm -C artifacts/tx-os exec tsc --noEmit` clean.
Architect review APPROVED (PASS). No tests referenced the removed
`alert-dismiss` / `alert-details-time` testids.

Pre-existing failures (unrelated): api-server workflow and the
combined `test` workflow continue to fail with the same upstream
issues observed in #342/#343 trajectory; this change does not
touch the API server or e2e test infrastructure.
2026-05-03 12:26:38 +00:00
riyadhafraa b2e6512d0c 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: 46b7b7a7-6013-4b4f-93b1-5aebc91376e6
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/Ow4s0aa
Replit-Helium-Checkpoint-Created: true
2026-05-03 12:24:09 +00:00