Commit Graph

2088 Commits

Author SHA1 Message Date
riyadhafraa 218d7dac58 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: 80fcbe81-ed83-4245-a43c-c4079f096dda
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 12:39:05 +00:00
Riyadh 991ddebe90 iOS keyboard fix v3 — visualViewport + touch-gate (#624)
Root causes the prior attempts missed:

1) Safari iOS/iPadOS ignores `interactive-widget=resizes-content`,
   so `100dvh` never shrinks when the soft keyboard opens. Every
   previous override that used `dvh` to size the dialog was sized
   to the full screen and ran behind the keyboard.

2) The CSS rule from #622 (`[role="dialog"]:has(:focus)`) was
   unconditional. On desktop, focusing any input inside a Radix
   DialogContent instantly stretched it to width:100vw + top:0,
   which collided with Radix's open-state slide-animation
   transforms and rendered the dialog as a broken narrow strip
   pinned in the top-left corner — visible in Tahani's laptop
   screenshot when opening "Add Meeting".

3) `AdminFormDialog` puts `role="dialog"` on a NON-fixed inner
   card, so the rule's `top/left/right` had no effect there
   anyway; the card stayed centred in a layout-viewport-sized
   wrapper which iOS does not shrink.

Fix:
- New `useVisualViewportVars` hook mounted in
  NotificationsSocketBridge. Writes `--vv-height` and
  `--vv-offset-top` on `<html>` from `window.visualViewport`,
  updated on resize / scroll / orientationchange. Falls back
  to `innerHeight`/0 if visualViewport is absent.
- Rewrote the index.css dialog rule:
  * Gated inside `@media (any-pointer: coarse)` so desktop is
    completely unaffected.
  * Uses `var(--vv-height, 100vh)` and `var(--vv-offset-top, 0px)`
    instead of `100dvh` / `0`, so dialogs actually track the
    visible band on iOS.
- New `.dialog-vv-wrapper` helper class that applies the same
  vv-sized top/height to bespoke full-screen overlay wrappers.
- Applied `.dialog-vv-wrapper` to AdminFormDialog and the admin
  ConfirmDialog wrappers; switched their `fixed inset-0` to
  `fixed inset-x-0 top-0 h-screen` and their inner cards from
  `max-h-[90vh]`/`max-h-[85vh]` to `max-h-full` so the card
  shrinks with the wrapper.

Kept `useScrollFocusedDialogField` unchanged — works correctly
once the dialog itself is sized to the visible band.

`tsc --noEmit` clean.
2026-05-21 12:04:12 +00:00
riyadhafraa 338d80cf0a iOS keyboard fix v3 — visualViewport + touch-gate (#624)
Root causes the prior attempts missed:

1) Safari iOS/iPadOS ignores `interactive-widget=resizes-content`,
   so `100dvh` never shrinks when the soft keyboard opens. Every
   previous override that used `dvh` to size the dialog was sized
   to the full screen and ran behind the keyboard.

2) The CSS rule from #622 (`[role="dialog"]:has(:focus)`) was
   unconditional. On desktop, focusing any input inside a Radix
   DialogContent instantly stretched it to width:100vw + top:0,
   which collided with Radix's open-state slide-animation
   transforms and rendered the dialog as a broken narrow strip
   pinned in the top-left corner — visible in Tahani's laptop
   screenshot when opening "Add Meeting".

3) `AdminFormDialog` puts `role="dialog"` on a NON-fixed inner
   card, so the rule's `top/left/right` had no effect there
   anyway; the card stayed centred in a layout-viewport-sized
   wrapper which iOS does not shrink.

Fix:
- New `useVisualViewportVars` hook mounted in
  NotificationsSocketBridge. Writes `--vv-height` and
  `--vv-offset-top` on `<html>` from `window.visualViewport`,
  updated on resize / scroll / orientationchange. Falls back
  to `innerHeight`/0 if visualViewport is absent.
- Rewrote the index.css dialog rule:
  * Gated inside `@media (any-pointer: coarse)` so desktop is
    completely unaffected.
  * Uses `var(--vv-height, 100vh)` and `var(--vv-offset-top, 0px)`
    instead of `100dvh` / `0`, so dialogs actually track the
    visible band on iOS.
- New `.dialog-vv-wrapper` helper class that applies the same
  vv-sized top/height to bespoke full-screen overlay wrappers.
- Applied `.dialog-vv-wrapper` to AdminFormDialog and the admin
  ConfirmDialog wrappers; switched their `fixed inset-0` to
  `fixed inset-x-0 top-0 h-screen` and their inner cards from
  `max-h-[90vh]`/`max-h-[85vh]` to `max-h-full` so the card
  shrinks with the wrapper.

Kept `useScrollFocusedDialogField` unchanged — works correctly
once the dialog itself is sized to the visible band.

`tsc --noEmit` clean.
2026-05-21 12:04:12 +00:00
Riyadh 6d60994078 iPad keyboard scroll fix v2 (#623)
#622's `useScrollFocusedDialogField` still failed on Tahani's iPad
for two reasons:

1) Touch-gate too narrow: `pointer: coarse` is *primary* pointer.
   An iPad connected to a Magic Keyboard / trackpad reports primary
   as `fine`, so the hook returned early and never armed. Broadened
   to `(pointer: coarse) OR (any-pointer: coarse)` so any device
   with any touch input qualifies.

2) `scrollIntoView` doesn't work inside `position: fixed` on iOS
   Safari/PWA — the scroll silently no-ops. Replaced it with a
   manual walk: from the focused field, ascend to the nearest
   ancestor whose computed `overflow-y` is auto/scroll AND that
   actually has overflow, stopping at the dialog root. Compute the
   target's offset within that container's content and set
   `container.scrollTop` so the target sits centred in the visible
   band, clamped to [0, scrollHeight-clientHeight].

Also tightened cleanup per #622's reviewer note — timer IDs are
now removed from the tracking set inside the timeout callback as
well as on blur.

No CSS or dialog component changes. `tsc --noEmit` clean.
2026-05-21 11:21:58 +00:00
riyadhafraa 0a1c6b41de iPad keyboard scroll fix v2 (#623)
#622's `useScrollFocusedDialogField` still failed on Tahani's iPad
for two reasons:

1) Touch-gate too narrow: `pointer: coarse` is *primary* pointer.
   An iPad connected to a Magic Keyboard / trackpad reports primary
   as `fine`, so the hook returned early and never armed. Broadened
   to `(pointer: coarse) OR (any-pointer: coarse)` so any device
   with any touch input qualifies.

2) `scrollIntoView` doesn't work inside `position: fixed` on iOS
   Safari/PWA — the scroll silently no-ops. Replaced it with a
   manual walk: from the focused field, ascend to the nearest
   ancestor whose computed `overflow-y` is auto/scroll AND that
   actually has overflow, stopping at the dialog root. Compute the
   target's offset within that container's content and set
   `container.scrollTop` so the target sits centred in the visible
   band, clamped to [0, scrollHeight-clientHeight].

Also tightened cleanup per #622's reviewer note — timer IDs are
now removed from the tracking set inside the timeout callback as
well as on blur.

No CSS or dialog component changes. `tsc --noEmit` clean.
2026-05-21 11:21:58 +00:00
Riyadh 0413fec3f7 Transitioned from Plan to Build mode 2026-05-21 11:21:10 +00:00
riyadhafraa 6896304981 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: b591d01c-8c2c-4e81-81d9-b67736dfaea9
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 11:21:10 +00:00
Riyadh b1589fe4e7 Scroll focused dialog field above iOS keyboard (#622)
The CSS `[role="dialog"]:has(:focus)` rule from #621 promotes any
focused dialog to a full-width sheet sized to `100dvh`, but Safari
does not auto-scroll the focused input into view inside the
dialog's own overflow container — it only tries to scroll the
document, which our position:fixed dialog ignores. The field stays
where it was, often behind the keyboard.

Added a global `useScrollFocusedDialogField` hook (in
artifacts/tx-os/src/hooks/) that listens to `document` focusin. If
the target is an input/textarea/select/contenteditable inside a
`closest('[role="dialog"]')`, it calls
`target.scrollIntoView({block:'center', behavior:'smooth'})` twice
(300ms and 600ms) to cover the iPad keyboard animation window and
the dialog's `max-height:100dvh` recalc. Timers are cleared on blur
and on unmount.

Mounted the hook once in `NotificationsSocketBridge` inside App.tsx
so it covers every page and every dialog kind we use (Radix
DialogContent, the bespoke AdminFormDialog, ad-hoc role="dialog"
wrappers) without touching their JSX.

No CSS or dialog component changes. `tsc --noEmit` clean.
2026-05-21 09:05:29 +00:00
riyadhafraa c98bf56706 Scroll focused dialog field above iOS keyboard (#622)
The CSS `[role="dialog"]:has(:focus)` rule from #621 promotes any
focused dialog to a full-width sheet sized to `100dvh`, but Safari
does not auto-scroll the focused input into view inside the
dialog's own overflow container — it only tries to scroll the
document, which our position:fixed dialog ignores. The field stays
where it was, often behind the keyboard.

Added a global `useScrollFocusedDialogField` hook (in
artifacts/tx-os/src/hooks/) that listens to `document` focusin. If
the target is an input/textarea/select/contenteditable inside a
`closest('[role="dialog"]')`, it calls
`target.scrollIntoView({block:'center', behavior:'smooth'})` twice
(300ms and 600ms) to cover the iPad keyboard animation window and
the dialog's `max-height:100dvh` recalc. Timers are cleared on blur
and on unmount.

Mounted the hook once in `NotificationsSocketBridge` inside App.tsx
so it covers every page and every dialog kind we use (Radix
DialogContent, the bespoke AdminFormDialog, ad-hoc role="dialog"
wrappers) without touching their JSX.

No CSS or dialog component changes. `tsc --noEmit` clean.
2026-05-21 09:05:29 +00:00
Riyadh 9370051ecf Scroll focused dialog field above iOS keyboard (#622)
The CSS `[role="dialog"]:has(:focus)` rule from #621 promotes any
focused dialog to a full-width sheet sized to `100dvh`, but Safari
does not auto-scroll the focused input into view inside the
dialog's own overflow container — it only tries to scroll the
document, which our position:fixed dialog ignores. The field stays
where it was, often behind the keyboard.

Added a global `useScrollFocusedDialogField` hook (in
artifacts/tx-os/src/hooks/) that listens to `document` focusin. If
the target is an input/textarea/select/contenteditable inside a
`closest('[role="dialog"]')`, it calls
`target.scrollIntoView({block:'center', behavior:'smooth'})` twice
(300ms and 600ms) to cover the iPad keyboard animation window and
the dialog's `max-height:100dvh` recalc. Timers are cleared on blur
and on unmount.

Mounted the hook once in `NotificationsSocketBridge` inside App.tsx
so it covers every page and every dialog kind we use (Radix
DialogContent, the bespoke AdminFormDialog, ad-hoc role="dialog"
wrappers) without touching their JSX.

No CSS or dialog component changes. `tsc --noEmit` clean.
2026-05-21 09:04:41 +00:00
riyadhafraa f352aa574d Scroll focused dialog field above iOS keyboard (#622)
The CSS `[role="dialog"]:has(:focus)` rule from #621 promotes any
focused dialog to a full-width sheet sized to `100dvh`, but Safari
does not auto-scroll the focused input into view inside the
dialog's own overflow container — it only tries to scroll the
document, which our position:fixed dialog ignores. The field stays
where it was, often behind the keyboard.

Added a global `useScrollFocusedDialogField` hook (in
artifacts/tx-os/src/hooks/) that listens to `document` focusin. If
the target is an input/textarea/select/contenteditable inside a
`closest('[role="dialog"]')`, it calls
`target.scrollIntoView({block:'center', behavior:'smooth'})` twice
(300ms and 600ms) to cover the iPad keyboard animation window and
the dialog's `max-height:100dvh` recalc. Timers are cleared on blur
and on unmount.

Mounted the hook once in `NotificationsSocketBridge` inside App.tsx
so it covers every page and every dialog kind we use (Radix
DialogContent, the bespoke AdminFormDialog, ad-hoc role="dialog"
wrappers) without touching their JSX.

No CSS or dialog component changes. `tsc --noEmit` clean.
2026-05-21 09:04:41 +00:00
Riyadh 09aa9096cf Transitioned from Plan to Build mode 2026-05-21 09:03:51 +00:00
riyadhafraa 958c40b5fb 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: fb89a8f2-a9db-43ed-943f-b147e6519687
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 09:03:51 +00:00
Riyadh d8fcdc4662 Unify edit-role dialog with AdminFormDialog (#621)
The "edit role" dialog in admin.tsx was hand-rolled as a bespoke
<div role-less wrapper> with its own close button, sticky footer and
full-width split buttons. That made it look different from every
other admin dialog (add role, add/edit group, add user), and on
iPad the action buttons floated mid-dialog because the content
exceeded max-h-[92vh]. The custom wrapper also lacked role="dialog",
so the new `:has(:focus)` CSS keyboard rule (index.css:492) never
applied to it.

Replaced the wrapper with AdminFormDialog (maxWidth="lg", KeyRound
icon, same title key). All inner content — name/desc fields, rename
warning, permissions list, removal impact, RolePermissionHistory,
RecentActivityForTarget — moved verbatim as children. Removed the
custom sticky footer; AdminFormDialog provides the unified footer
with cancel/save buttons. Preserved testIds `edit-role-dialog` and
`edit-role-submit`. Disabled logic translated 1:1 into
submitDisabled + isPending props (note: `impactError` coerced to
boolean via `!!` to satisfy the prop type).

No behavior change: same handlers (closeEditDialog, handleEdit),
same loading spinner, same disabled conditions. Bonus: the keyboard
override CSS now applies because AdminFormDialog already sets
role="dialog".

`tsc --noEmit` clean.
2026-05-21 08:57:08 +00:00
riyadhafraa 9e590b959e Unify edit-role dialog with AdminFormDialog (#621)
The "edit role" dialog in admin.tsx was hand-rolled as a bespoke
<div role-less wrapper> with its own close button, sticky footer and
full-width split buttons. That made it look different from every
other admin dialog (add role, add/edit group, add user), and on
iPad the action buttons floated mid-dialog because the content
exceeded max-h-[92vh]. The custom wrapper also lacked role="dialog",
so the new `:has(:focus)` CSS keyboard rule (index.css:492) never
applied to it.

Replaced the wrapper with AdminFormDialog (maxWidth="lg", KeyRound
icon, same title key). All inner content — name/desc fields, rename
warning, permissions list, removal impact, RolePermissionHistory,
RecentActivityForTarget — moved verbatim as children. Removed the
custom sticky footer; AdminFormDialog provides the unified footer
with cancel/save buttons. Preserved testIds `edit-role-dialog` and
`edit-role-submit`. Disabled logic translated 1:1 into
submitDisabled + isPending props (note: `impactError` coerced to
boolean via `!!` to satisfy the prop type).

No behavior change: same handlers (closeEditDialog, handleEdit),
same loading spinner, same disabled conditions. Bonus: the keyboard
override CSS now applies because AdminFormDialog already sets
role="dialog".

`tsc --noEmit` clean.
2026-05-21 08:57:08 +00:00
Riyadh 53be7431cb Transitioned from Plan to Build mode 2026-05-21 08:56:06 +00:00
riyadhafraa 6a151393f5 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: 57ab0c63-40b4-44e7-9bf3-7d71a7442473
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 08:56:06 +00:00
Riyadh 65c5335eb2 Improve dialog behavior when virtual keyboard is open
Update dialog component to use CSS :has(:focus) for keyboard detection, removing reliance on visual viewport for better cross-platform compatibility.
2026-05-21 08:45:43 +00:00
riyadhafraa 1e8f4435cd Improve dialog behavior when virtual keyboard is open
Update dialog component to use CSS :has(:focus) for keyboard detection, removing reliance on visual viewport for better cross-platform compatibility.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 144d5d9b-0d4f-4783-a2d4-8af91a180dd6
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 08:45:43 +00:00
Riyadh 210afc7d44 Add a temporary diagnostic display for keyboard inset calculations
Add a temporary debug overlay to the DialogContent component in `dialog.tsx` to display `keyboardInset`, `height`, and `offsetTop` values, and the keyboard open state.
2026-05-21 08:30:27 +00:00
riyadhafraa 27dea3f39e Add a temporary diagnostic display for keyboard inset calculations
Add a temporary debug overlay to the DialogContent component in `dialog.tsx` to display `keyboardInset`, `height`, and `offsetTop` values, and the keyboard open state.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: e4cbec37-342b-4d6e-9215-50c182eb1ed0
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 08:30:27 +00:00
Riyadh 686dda1e54 Improve dialog behavior and appearance when the keyboard is open
Refactor dialog styling to use global CSS with !important rules for keyboard open state, overriding inline styles and Tailwind classes.
2026-05-21 08:10:48 +00:00
riyadhafraa 0f7be86504 Improve dialog behavior and appearance when the keyboard is open
Refactor dialog styling to use global CSS with !important rules for keyboard open state, overriding inline styles and Tailwind classes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 93a2c072-c3ed-47c7-99d1-75c48f36c85e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 08:10:48 +00:00
Riyadh 8980eedae2 Improve dialog scrolling behavior when the keyboard is open
Adjust dialog content to enable scrolling and center focused input fields, addressing issues on iOS and Android where content was hidden by the on-screen keyboard.
2026-05-21 07:40:44 +00:00
riyadhafraa 7f61ded586 Improve dialog scrolling behavior when the keyboard is open
Adjust dialog content to enable scrolling and center focused input fields, addressing issues on iOS and Android where content was hidden by the on-screen keyboard.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 6290c891-00dd-4660-a47c-75d5e9999b12
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 07:40:44 +00:00
Riyadh 92953cfb4a Improve drag and drop interactions by adjusting activation constraints
Adjusts drag activation constraints for pointer and touch sensors, enabling instant drag initiation in edit mode with a lower activation distance and no delay, while maintaining a higher threshold and delay outside of edit mode.
2026-05-21 07:07:31 +00:00
riyadhafraa bc47a08559 Improve drag and drop interactions by adjusting activation constraints
Adjusts drag activation constraints for pointer and touch sensors, enabling instant drag initiation in edit mode with a lower activation distance and no delay, while maintaining a higher threshold and delay outside of edit mode.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 1b66c87b-bec3-4f9e-b161-b4789c48a6fa
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 07:07:31 +00:00
Riyadh c184b7bc9c Ensure timely delivery of notifications to iOS devices
Add `urgency: "high"` to the push notification payload in `push.ts` to prioritize delivery for iOS PWA users.
2026-05-21 07:01:16 +00:00
riyadhafraa 26f8e45db6 Ensure timely delivery of notifications to iOS devices
Add `urgency: "high"` to the push notification payload in `push.ts` to prioritize delivery for iOS PWA users.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: fc796524-4846-43f8-869a-fe4775e6b171
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-21 07:01:16 +00:00
Riyadh 965e8eba29 Allow reordering services even when not all are provided
Modify the service reorder endpoint to handle cases where the client sends a partial list of service IDs, ensuring that unprovided services are appended to the end of the list without causing validation errors.
2026-05-20 18:47:58 +00:00
riyadhafraa ea8ee1af51 Allow reordering services even when not all are provided
Modify the service reorder endpoint to handle cases where the client sends a partial list of service IDs, ensuring that unprovided services are appended to the end of the list without causing validation errors.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 4a518c00-92a6-443c-95de-44f6faa44d35
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-20 18:47:58 +00:00
Riyadh 3f4b3119fd Adjust dialog positioning to better handle keyboard interactions on mobile
Modify dialog component to use window.innerWidth for width and position fixed, ensuring consistent display across iOS PWA environments by anchoring to the layout viewport.
2026-05-20 18:29:39 +00:00
riyadhafraa 674924f974 Adjust dialog positioning to better handle keyboard interactions on mobile
Modify dialog component to use window.innerWidth for width and position fixed, ensuring consistent display across iOS PWA environments by anchoring to the layout viewport.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b5415cc4-9462-49ea-bda1-5d40d587d3d1
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-20 18:29:39 +00:00
Riyadh 18e98a8828 Improve layout alignment for form fields on smaller screens
Add `min-w-0` to `FormRow` component to prevent input fields from overlapping in RTL layouts and ensure proper column spanning on various screen sizes.
2026-05-20 14:57:31 +00:00
riyadhafraa d31dfe5962 Improve layout alignment for form fields on smaller screens
Add `min-w-0` to `FormRow` component to prevent input fields from overlapping in RTL layouts and ensure proper column spanning on various screen sizes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 5dabc2a6-6905-4d77-ba0d-f4edc783cb06
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-20 14:57:31 +00:00
Riyadh c34959ed0e Make dialogs appear correctly on iOS when the keyboard is open
Adjust dialog positioning logic to use visual viewport dimensions for precise placement and sizing on iOS, resolving display issues caused by layout viewport discrepancies with the soft keyboard.
2026-05-20 14:30:09 +00:00
riyadhafraa f0d59d2d9e Make dialogs appear correctly on iOS when the keyboard is open
Adjust dialog positioning logic to use visual viewport dimensions for precise placement and sizing on iOS, resolving display issues caused by layout viewport discrepancies with the soft keyboard.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f92aad5a-65d6-41c4-a2d9-0806c97f1464
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-20 14:30:09 +00:00
Riyadh 6e7e6091bb Fix dialog display issues when the on-screen keyboard is active
Adjust dialog positioning on iOS Safari to render as a full-width sheet when the keyboard is open, preventing layout shifts and ensuring content remains visible.
2026-05-20 14:10:41 +00:00
riyadhafraa 01adae003e Fix dialog display issues when the on-screen keyboard is active
Adjust dialog positioning on iOS Safari to render as a full-width sheet when the keyboard is open, preventing layout shifts and ensuring content remains visible.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b9f9a662-72a9-4295-a75e-826dc00f7f90
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-20 14:10:41 +00:00
Riyadh d2b83ad1e3 Improve dialog positioning to better handle mobile keyboards
Adjust dialog positioning on mobile to anchor to the top of the visual viewport when the keyboard is open, ensuring content remains accessible and preventing overlap.
2026-05-20 12:04:35 +00:00
riyadhafraa 98a07daa35 Improve dialog positioning to better handle mobile keyboards
Adjust dialog positioning on mobile to anchor to the top of the visual viewport when the keyboard is open, ensuring content remains accessible and preventing overlap.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 8ed39e54-3a41-4a5b-a3e7-13162e8f9590
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/qrzw3bH
Replit-Helium-Checkpoint-Created: true
2026-05-20 12:04:35 +00:00
Riyadh 87e8d46b1e Drag-to-reorder service cards (iOS jiggle mode)
Long-press a service tile (~500 ms) on the Services page → grid enters
edit mode: every card jiggles, a floating "تم / Done" pill appears, and
cards become draggable via @dnd-kit. Drop on another tile to reorder.
Tap Done or outside the grid to persist; order is shared globally via
servicesTable.sortOrder.

Per the task default: admin-only. Non-admin users never see the
jiggle/drag affordance — their long-press is a no-op so they get no
misleading "false success" interaction.

Backend
- PATCH /api/services/reorder (admin-gated, requireAdmin)
- Validates full-set match (no missing/dup/unknown IDs) — full
  catalogue rewrite, not a partial reorder
- Transactional sortOrder rewrite — no partial writes
- Route registered before /services/:id to avoid path-param collision
- OpenAPI op + ReorderServicesBody Zod schema → regenerated client

Frontend
- artifacts/tx-os/src/pages/services.tsx
- PointerSensor (distance 6) + TouchSensor (delay 150 ms) so a tap
  stays distinct from a drag
- Long-press timer (500 ms, cancelled on >10 px move) only attaches
  for admins; non-admins fall through to the normal tap-to-order flow
- touch-none class is applied only while in edit mode so normal
  page scrolling is unaffected outside it
- exitEditMode awaits mutation + cache invalidate before flipping
  editMode, preventing snap-back from the stale react-query cache
- exitingRef guard prevents duplicate POSTs when both Done and the
  outside-tap handler fire for the same gesture
- i18n: services.editMode.{done,hint,saveFailed} in ar.json + en.json
2026-05-20 11:53:47 +00:00
riyadhafraa d369447917 Drag-to-reorder service cards (iOS jiggle mode)
Long-press a service tile (~500 ms) on the Services page → grid enters
edit mode: every card jiggles, a floating "تم / Done" pill appears, and
cards become draggable via @dnd-kit. Drop on another tile to reorder.
Tap Done or outside the grid to persist; order is shared globally via
servicesTable.sortOrder.

Per the task default: admin-only. Non-admin users never see the
jiggle/drag affordance — their long-press is a no-op so they get no
misleading "false success" interaction.

Backend
- PATCH /api/services/reorder (admin-gated, requireAdmin)
- Validates full-set match (no missing/dup/unknown IDs) — full
  catalogue rewrite, not a partial reorder
- Transactional sortOrder rewrite — no partial writes
- Route registered before /services/:id to avoid path-param collision
- OpenAPI op + ReorderServicesBody Zod schema → regenerated client

Frontend
- artifacts/tx-os/src/pages/services.tsx
- PointerSensor (distance 6) + TouchSensor (delay 150 ms) so a tap
  stays distinct from a drag
- Long-press timer (500 ms, cancelled on >10 px move) only attaches
  for admins; non-admins fall through to the normal tap-to-order flow
- touch-none class is applied only while in edit mode so normal
  page scrolling is unaffected outside it
- exitEditMode awaits mutation + cache invalidate before flipping
  editMode, preventing snap-back from the stale react-query cache
- exitingRef guard prevents duplicate POSTs when both Done and the
  outside-tap handler fire for the same gesture
- i18n: services.editMode.{done,hint,saveFailed} in ar.json + en.json
2026-05-20 11:53:47 +00:00
Riyadh b69a2d1728 Drag-to-reorder service cards (iOS jiggle mode)
Long-press a service tile (~500 ms) → grid enters edit mode: every
card jiggles, a floating "تم / Done" pill appears, and cards become
draggable via @dnd-kit. Drop on another tile to reorder. Tap Done or
outside the grid to persist; the new order is shared globally via
servicesTable.sortOrder.

Backend
- POST /api/services/reorder (admin-gated, requireAdmin)
- Validates full-set match (no missing/dup/unknown IDs)
- Transactional sortOrder rewrite — no partial writes
- Route registered before /services/:id to avoid path-param collision
- OpenAPI op + ReorderServicesBody Zod schema → regenerated client

Frontend
- artifacts/tx-os/src/pages/services.tsx rewritten
- PointerSensor (distance 6) + TouchSensor (delay 150 ms) — long-press
  and tap-to-open stay distinct
- touch-none only applied while in edit mode so normal scrolling is
  unaffected outside it
- exitEditMode awaits mutation + invalidate before flipping editMode,
  preventing snap-back from stale react-query cache
- exitingRef guard against duplicate exit calls (Done button +
  outside-tap handler firing for the same gesture)
- Non-admins can enter edit mode (haptic affordance) but local reorder
  is silently reverted on exit
- i18n: services.editMode.{done,hint,saveFailed} added to ar.json/en.json

Pre-existing unrelated typecheck errors in push.ts and
executive-meeting-font-settings.ts are not touched by this change.
2026-05-20 11:51:28 +00:00
riyadhafraa 376918f983 Drag-to-reorder service cards (iOS jiggle mode)
Long-press a service tile (~500 ms) → grid enters edit mode: every
card jiggles, a floating "تم / Done" pill appears, and cards become
draggable via @dnd-kit. Drop on another tile to reorder. Tap Done or
outside the grid to persist; the new order is shared globally via
servicesTable.sortOrder.

Backend
- POST /api/services/reorder (admin-gated, requireAdmin)
- Validates full-set match (no missing/dup/unknown IDs)
- Transactional sortOrder rewrite — no partial writes
- Route registered before /services/:id to avoid path-param collision
- OpenAPI op + ReorderServicesBody Zod schema → regenerated client

Frontend
- artifacts/tx-os/src/pages/services.tsx rewritten
- PointerSensor (distance 6) + TouchSensor (delay 150 ms) — long-press
  and tap-to-open stay distinct
- touch-none only applied while in edit mode so normal scrolling is
  unaffected outside it
- exitEditMode awaits mutation + invalidate before flipping editMode,
  preventing snap-back from stale react-query cache
- exitingRef guard against duplicate exit calls (Done button +
  outside-tap handler firing for the same gesture)
- Non-admins can enter edit mode (haptic affordance) but local reorder
  is silently reverted on exit
- i18n: services.editMode.{done,hint,saveFailed} added to ar.json/en.json

Pre-existing unrelated typecheck errors in push.ts and
executive-meeting-font-settings.ts are not touched by this change.
2026-05-20 11:51:28 +00:00
Riyadh 50806bbe39 Transitioned from Plan to Build mode 2026-05-20 11:42:19 +00:00
riyadhafraa f02305fe9f 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: af14f8d6-5fb9-40dd-b404-a1cc57afe3b2
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/IO8TMSC
Replit-Helium-Checkpoint-Created: true
2026-05-20 11:42:19 +00:00
Riyadh 3edeee1512 Remove HTML from meeting notification subjects
Strip HTML tags from meeting subjects using a helper function in `executive-meeting-scheduler.ts` to ensure plain text display in push notifications.
2026-05-20 11:16:31 +00:00
riyadhafraa 47f31730f7 Remove HTML from meeting notification subjects
Strip HTML tags from meeting subjects using a helper function in `executive-meeting-scheduler.ts` to ensure plain text display in push notifications.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ec660c7b-c476-4440-8578-eef124542d3b
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/IO8TMSC
Replit-Helium-Checkpoint-Created: true
2026-05-20 11:16:31 +00:00
Riyadh d4a24a47fb Adjust note sending dialog to accommodate iPad keyboard
Add logic to `send-note-dialog.tsx` to dynamically adjust the recipients list's max-height based on the visual viewport, ensuring visibility when the iPad keyboard is active.
2026-05-20 10:07:52 +00:00