Commit Graph

78 Commits

Author SHA1 Message Date
riyadhafraa f7b93aa4e7 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: d8980aeb-a819-4c14-97a6-7384215c51d1
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/GbTNwz8
Replit-Helium-Checkpoint-Created: true
2026-05-02 09:34:57 +00:00
riyadhafraa 4e8beaaa76 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: d9167fa7-ae0a-40cd-8655-0f20384142fe
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/HJW50TH
Replit-Helium-Checkpoint-Created: true
2026-05-02 09:03:03 +00:00
riyadhafraa e4aa4fa280 Improve time input functionality and fix related test cases
Replace native time input with a custom text input and parser, enabling support for various time formats and fixing associated end-to-end tests.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 4ef80303-e96f-4ad0-ba0f-8fce49b7b340
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/HJW50TH
Replit-Helium-Checkpoint-Created: true
2026-05-02 08:39:00 +00:00
riyadhafraa aa0de3c99b 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: 7471c94c-e508-444f-a1f0-ecf0659e56b6
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/CO2oJgb
Replit-Helium-Checkpoint-Created: true
2026-05-01 22:03:02 +00:00
riyadhafraa 75fa714b25 #302 cascade-shift later meetings on postpone/reschedule (review fixes)
Backend (artifacts/api-server/src/routes/executive-meetings.ts):
- New helpers computeCascadeShift + applyCascadeShift; new schema
  cascadePreviewSchema; cascadeFollowing flag on postpone-minutes
  and reschedule.
- POST /executive-meetings/:id/cascade-preview returns followers +
  blockedBy.
- Writer paths now SELECT followers FOR UPDATE inside the txn so
  concurrent mutations cannot lose updates and audit oldStart/oldEnd
  always reflect the locked-current values (architect feedback).
- Reschedule only cascades when same date AND newStart > oldStart.
- Midnight rejection rolls back the primary too; named offender is
  surfaced verbatim to the UI.
- One meeting_cascade_shift audit per follower (trigger meeting +
  delta in the row for replay).

Frontend (artifacts/tx-os/src/components/executive-meetings/
upcoming-meeting-alert.tsx):
- CascadePromptBlock with loading / blocked / normal variants and
  data-testids cascade-prompt[-blocked|-loading], cascade-keep-times,
  cascade-shift-following, cascade-back, cascade-follower-{id}.
- runCascadePreview helper with in-flight + busy guard against
  duplicate submissions; falls through to single-meeting submit when
  no followers and not blocked.
- rescheduleSubmit extracted; cascade_crosses_midnight from server
  is caught and re-rendered as the blocked variant.

i18n: cascade* keys under executiveMeetings.alert in en.json + ar.json
(Arabic plural zero/one/two/few/many/other for header + shift action).
ALSO added `executiveMeetings.audit.action.meeting_cascade_shift` in
both locales so the History view renders a localized label instead of
falling back to the raw action code (review feedback).

Tests:
- 8 cascade backend tests in executive-meetings.test.mjs (preview
  shape, atomic shift, skip cancelled/completed, midnight rollback,
  reschedule delta + no-op cases) all pass.
- New e2e "Reschedule cascade — opting in shifts all later same-day
  meetings" (API-driven backend contract).
- NEW UI dialog test "Cascade prompt UI: Shift sends
  cascadeFollowing=true; Keep sends false" — opens the postpone
  confirm, mocks cascade-preview to a deterministic single-follower
  response, then verifies (via request interception of
  /postpone-minutes) that Shift sends cascadeFollowing=true and Keep
  sends cascadeFollowing=false (review feedback).
- NEW UI test "Cascade prompt UI: no followers => prompt skipped,
  direct submit with cascadeFollowing=false" — locks in the no-
  followers branch from the second review pass: mocks cascade-
  preview to {followers:[], blockedBy:null} and asserts the prompt
  never renders and the postpone-minutes payload carries
  cascadeFollowing=false. Both UI tests use wildcard regex routes so
  same-day pollution from prior tests cannot misroute requests.
- Made two existing tests (Postpone by 10 minutes, conflict warning)
  pollution-tolerant by polling for either the cascade prompt or the
  meeting-postponed audit row.

Pre-existing flaky tests (notifications fan-out, postpone race, row
color realtime, reorder, status transitions, alert-done/close
realtime, reschedule-different-day visibility, cancel/renumber strict
mode) are not related to this work and were not modified.

Drift note: prior commit picked up an incidental opengraph.jpg byte
diff (25906 -> 25929 bytes) from the tx-os web workflow restart — no
content change visible to this task; left in place as removing it
requires a destructive git operation.
2026-05-01 21:30:16 +00:00
riyadhafraa 471c480824 Task #303: DIN Next LT Arabic site default + working font picker
User report: "the dropdown for changing the site font does not work."
Root cause: FontSettingsSection listed Cairo / Tajawal / Noto Naskh
Arabic / Amiri, but only Tajawal had been registered with @font-face.
Picking the others was a no-op. Site default body font was also not
DIN Next LT Arabic as designed.

Changes:
- artifacts/tx-os/src/index.css: --app-font-sans now starts with
  "DIN Next LT Arabic"; dropped IBM Plex Mono.
- artifacts/tx-os/index.html: removed Google Fonts <link> + preconnect.
- artifacts/tx-os/src/pages/executive-meetings.tsx: FontSettingsSection
  dropdown replaced with the 5 actually-bundled families + system.
- artifacts/api-server/src/routes/executive-meetings.ts: FONT_FAMILIES
  Zod allowlist updated to match.
- artifacts/api-server/src/lib/sanitize.ts: FONT_NAME_PART regex now
  allows the new families (kept IBM Plex Sans Arabic for backward
  compat). Fixes a latent bug from #301 where the rich-text sanitizer
  silently stripped attendee font picks on save.
- artifacts/api-server/src/lib/pdf-renderer.ts: FAMILY_MAP and
  ARABIC_FONT_NAMES updated. Sans-style families map to the bundled
  NotoSansArabic; Naskh-style families to NotoNaskhArabic.
- artifacts/api-server/tests/executive-meetings.test.mjs: PDF
  sans-vs-naskh assertion updated from Cairo/Noto Naskh Arabic to
  DIN Next LT Arabic/Majalla, preserving its semantics. Other
  Cairo/Noto Naskh Arabic occurrences swapped to the new names.
- replit.md: documented site default font + lockstep allowlist rule.

Deviations from plan: kept DEFAULT_FONT.fontFamily = "system" on the
frontend (and the backend Zod default) instead of changing it to
"DIN Next LT Arabic". "system" semantically means "no override → use
the CSS default", which is now DIN Next LT Arabic — same end result
without forcing a migration on existing rows. The sanitizer change
was not in the original plan but was required to make the picker
actually persist.

Verification:
- Frontend tsc clean.
- Backend tsc shows the same pre-existing unrelated errors as before
  (isHighlighted boolean/number; Drizzle scope typing).
- E2E browser test verified all 7 assertions: site default font,
  no Google Fonts requests, exactly 6 dropdown options, font picker
  applies + reverts correctly, attendee font picks persist after save.
- Code review verdict: PASS.
2026-05-01 19:55:39 +00:00
riyadhafraa a79e1e125a 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: 09224b88-5f3a-4c5a-bec0-93eaaf66ca08
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/iRjGQNR
Replit-Helium-Checkpoint-Created: true
2026-05-01 18:57:48 +00:00
riyadhafraa 4d290cb10d 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: 7eff3391-bf57-4f34-b8c2-410f9fa489ca
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/iRjGQNR
Replit-Helium-Checkpoint-Created: true
2026-05-01 18:46:35 +00:00
riyadhafraa 1ac81b7a35 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: 70d86683-5d4a-4f99-9ac0-eb156e5defe4
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/GMnW1ol
Replit-Helium-Checkpoint-Created: true
2026-05-01 18:37:16 +00:00
riyadhafraa b60935ed00 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: 5b2a9a59-f8a7-4e97-bd4f-ac8cc8a6bd23
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/GMnW1ol
Replit-Helium-Checkpoint-Created: true
2026-05-01 18:23:29 +00:00
riyadhafraa d044496d5e Remove AM/PM indicators from meeting time displays
Remove day period indicators (AM/PM, ص/م) from the 12-hour time display in executive meeting schedules and PDF exports.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: d895acbd-d69d-4d3e-8ded-4cb2be114779
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/0VZWDfs
Replit-Helium-Checkpoint-Created: true
2026-05-01 18:00:00 +00:00
riyadhafraa ab5ec2e2e2 Add shared row highlighting to executive meeting scheduler
Implement shared row highlighting for executive meetings by adding a `rowColor` field to the database schema and API, and migrating existing per-device colors to the new shared field.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 273accfc-a301-41b9-bd20-c121cb4e79c7
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/g7BgHDL
Replit-Helium-Checkpoint-Created: true
2026-05-01 15:24:59 +00:00
riyadhafraa 6c6f9c1848 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: 5670060a-d0e8-4cba-bce2-918e04416bb4
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/8gwn7Xm
Replit-Helium-Checkpoint-Created: true
2026-05-01 15:03:18 +00:00
riyadhafraa bb340de455 Git commit prior to merge 2026-05-01 14:58:31 +00:00
riyadhafraa 50627b5b74 Add real-time updates for meeting alerts across user devices
Introduce real-time event listeners and emitters to synchronize meeting alert status changes, such as "Done" or "Dismissed", across a user's multiple open tabs and devices. This update refactors the `executive-meetings.ts` route to trigger a per-user socket event upon state transitions, which is then handled by `use-notifications-socket.ts` to invalidate the alert state query, ensuring near-instantaneous UI updates. New tests in `executive-meetings-upcoming-alert.spec.mjs` verify the cross-tab synchronization for both "Done" and "Dismiss" actions, confirming that only the acting user's alerts are affected.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b81561d1-3ba4-46fc-96c0-77d50130c061
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/8gwn7Xm
Replit-Helium-Checkpoint-Created: true
2026-05-01 14:23:01 +00:00
riyadhafraa cbda85fc73 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: a686e421-ab75-4435-aa36-9b1065d2a1ed
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/8gwn7Xm
Replit-Helium-Checkpoint-Created: true
2026-05-01 13:12:00 +00:00
riyadhafraa bbf494f2a6 Git commit prior to merge 2026-05-01 11:18:40 +00:00
riyadhafraa 307552317e 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: 8cdf91f8-56af-4ced-99ad-62723365085e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/zG2WcPi
Replit-Helium-Checkpoint-Created: true
2026-05-01 09:01:21 +00:00
riyadhafraa 21c935064d #262: remove Requests / Approvals / Tasks tabs from Executive Meetings
Full-stack removal of the three retired sections — UI, locales, realtime
invalidations, backend routes, role lists, capability flags, schema
tables, notify lib, and tests.

Backend (artifacts/api-server)
- routes/executive-meetings.ts: deleted /requests* + /tasks* handler
  block, REQUEST_ROLES / TASK_VIEW_ROLES / TASK_BROAD_VIEW_ROLES,
  canSubmitRequest / canViewTasks / canViewAllTasks from /me, retired
  table imports, and dead schemas (detailsByType, requestPayloadSchemas,
  request*Schema, taskCreateSchema, taskPatchSchema, dueAtSchema,
  dateOnly, timeHm). canApprove kept (still used by FontSettings).
- lib/executive-meeting-notify.ts: EXECUTIVE_MEETING_NOTIFICATION_TYPES
  collapsed to ['meeting_created'].

Frontend (artifacts/tx-os)
- pages/executive-meetings.tsx: deleted RequestsSection /
  ApprovalsSection / TasksSection / RequestListRow, pruned SECTIONS,
  MeCapabilities / MeRoles types, isSectionVisible cases, icon imports.
- hooks/use-notifications-socket.ts: dropped the two retired query
  invalidations.
- locales/{ar,en}.json: removed nav.{requests,approvals,tasks},
  executiveMeetings.{requests,approvals,tasks} subtrees, and the 6
  retired notification.type entries.

Schema + DB
- lib/db/src/schema/executive-meetings.ts: tables + relations + types
  for requests/tasks removed.
- artifacts/api-server/scripts/cleanup-em-requests-tasks.sql:
  idempotent BEGIN/COMMIT — deletes orphan prefs / notifications /
  audit rows, then DROP TABLE … CASCADE for both retired tables.
  Applied to dev DB and `db push` re-synced.

Tests
- executive-meetings.test.mjs: deleted 9 retired blocks + 2 covered
  prefs duplicates, rewrote /me capability test to assert flags absent,
  rewrote DELETE-wipe test to use meeting_created via POST
  /api/executive-meetings, removed /requests + /tasks router.param
  entries.
- executive-meetings-notifications.test.mjs: deleted 7 blocks
  (request_*, task_*, cross-event-mute), updated before/after
  cleanup to skip dropped tables, kept setPref/clearPref helpers
  (still used by surviving meeting_created opt-out tests).

Drift / pre-existing
- 3 test failures observed under the parallel `node --test` workflow
  (meeting_created fan-out count, pref opt-out daily-number conflict,
  service-orders JSON-vs-HTML) are pre-existing parallel-file
  pollution between executive-meetings.test.mjs and
  executive-meetings-notifications.test.mjs. Verified by running
  `node --test --test-concurrency=1 'tests/**/*.test.mjs'` →
  226/226 pass. Out of scope for #262.
- Pre-existing tsc warnings at routes/executive-meetings.ts L509/625
  (boolean/number on isHighlighted) and L1594 (font-settings scope
  query) untouched.
2026-05-01 08:18:29 +00:00
riyadhafraa f7ab5e8b08 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: c723ef1f-32f7-4eb8-baaa-d3d4fa9eab8f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/OwMOjxo
Replit-Helium-Checkpoint-Created: true
2026-05-01 07:43:38 +00:00
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