- 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.
- 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.
- 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.
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
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.
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.
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.
Native window.confirm exposes the Repl hostname and ignores the app's
RTL/branding. The user (Arabic-speaking) flagged it as visually jarring
inside Executive Meetings.
Changes (artifacts/tx-os/src/pages/executive-meetings.tsx):
- Add a small ConfirmProvider + useConfirm() backed by the existing
shadcn AlertDialog (`@/components/ui/alert-dialog`). The hook returns
Promise<boolean> so call sites stay one-liners:
if (!(await confirm({ message, destructive: true }))) return;
- Single-flight guard: if a second confirm() arrives while the first is
still pending, the previous Promise resolves to false (no orphaned
resolvers).
- Mount the provider once around the page (split the default export
into a thin wrapper + ExecutiveMeetingsPageInner so the inner tree
can call useConfirm()).
- Centered title + description, destructive-styled confirm button,
mobile-safe sizing (max-w-md, w-[calc(100%-2rem)], max-h-[90vh],
overflow-y-auto). Explicit `dir` on AlertDialogContent so the
portal-rendered dialog always inherits the page's RTL/LTR.
- Footer renders Action first then Cancel: in LTR Confirm sits on the
left and Cancel on the right; RTL flips automatically (Confirm right,
Cancel left). Mobile stacks Cancel on top, destructive at the bottom.
- Replace all 5 window.confirm sites:
1. Schedule single-row delete (deleteMeeting)
2. Schedule bulk delete
3. Manage bulk delete
4. Manage single delete (confirmDelete)
5. MeetingFormDialog "remove all attendees" (function turned async)
- Add `confirm` to the relevant useCallback deps (deleteMeeting,
bulk delete) and inject useConfirm() inside ScheduleSection,
ManageSection, MeetingFormDialog (all rendered inside the provider).
- New i18n keys: executiveMeetings.common.confirm and .confirmTitle in
both ar.json and en.json (used as default title / non-destructive
confirm label).
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated to this UI change
(also failing before the task started).
Native window.confirm exposes the Repl hostname and ignores the app's
RTL/branding. The user (Arabic-speaking) flagged it as visually jarring
inside Executive Meetings.
Changes (artifacts/tx-os/src/pages/executive-meetings.tsx):
- Add a small ConfirmProvider + useConfirm() backed by the existing
shadcn AlertDialog (`@/components/ui/alert-dialog`). The hook returns
Promise<boolean> so call sites stay one-liners:
if (!(await confirm({ message, destructive: true }))) return;
- Mount the provider once around the page (split the default export
into a thin wrapper + ExecutiveMeetingsPageInner so the inner tree
can call useConfirm()).
- Centered title + description, destructive-styled confirm button,
mobile-safe sizing (max-w-md, w-[calc(100%-2rem)], max-h-[90vh],
overflow-y-auto), centered footer that stacks on small screens via
the existing AlertDialogFooter classes.
- Replace all 5 window.confirm sites:
1. Schedule single-row delete (deleteMeeting)
2. Schedule bulk delete
3. Manage bulk delete
4. Manage single delete (confirmDelete)
5. MeetingFormDialog "remove all attendees" (function turned async)
- Add `confirm` to the relevant useCallback deps (deleteMeeting,
bulk delete) and inject useConfirm() inside ScheduleSection,
ManageSection, MeetingFormDialog (all rendered inside the provider).
Reuses existing translation keys (deleteRowConfirm, bulkDeleteConfirm,
manage.deleteConfirm, attendees.removeAllConfirm). Title/labels fall
back via tWithFallback to Arabic strings if the i18n keys are missing.
Verified `pnpm -C artifacts/tx-os exec tsc --noEmit` is clean.
Pre-existing api-server / test workflow failures are unrelated to this
UI change (also failing before the task started).
User feedback (RTL screenshot): the "إجراءات" header and its three
icon buttons were stuck against the far edge of the column, away
from the new vertical divider added in #337; the "الوقت" header
also sat against the start edge while the short "1:55 — 2:05"
value floated separately, so they didn't visually line up.
- Switches both the Time and Actions header cells from
`text-start` / `text-end` to `text-center`.
- Adds `text-center` to the Time data cell so the
fixed-width "HH:MM — HH:MM" string sits centered under its
header.
- Switches the Actions data cell from `text-end` to
`text-center`; the inline-flex button group (edit /
duplicate / delete) inherits the centering.
- Loading and "no meetings" rows already render the message
via `colSpan={4}` and a separate empty Actions cell; no
text alignment change is needed there for visual parity.
Other columns (select-checkbox, #, Meeting) keep their
existing `text-start` so long titles stay flush with their
header label. The vertical divider (#337) and column widths
(w-28 / w-32) remain untouched. No change to data flow,
testids, or other tabs.
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated.
User feedback: the small grey subtitle under each meeting title in
the Manage tab table read e.g. "4 · none" — the raw `platform`
enum value leaked an English token next to Arabic content for
every meeting that has no virtual platform.
- Drops the ` · {m.platform}` segment from the subtitle in
`ManageSection`'s row rendering inside
`artifacts/tx-os/src/pages/executive-meetings.tsx`. The
attendee count (filtered on `kind === "person"`) stays
exactly as before, with the same `text-xs text-gray-500`
styling so layout is unchanged.
- Adds a short comment explaining why so future readers don't
re-introduce the platform token here. The platform is still
shown elsewhere when meaningful (the "Virtual attendance —
{platformLabel}" group header in AttendeeFlow at L4304/L4402,
and the platform `<Select>` in the edit dialog) — those were
intentionally not touched.
- No change to data model, save/load logic, or testids.
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated to this UI
change.
User feedback: in the Manage tab table the action icons looked
glued to the time value because there was no visible boundary
between the Time column and the Actions column.
- Adds `border-s border-gray-200` to both the Actions header
cell (`<th>`) and the per-row Actions data cell (`<td>`) in
the Manage tab table inside `executive-meetings.tsx`.
- Uses the logical `border-s` (inline-start) so the divider
lands on the correct edge of the Actions column in both LTR
and RTL — visually it always sits between Time and Actions.
- Color matches the existing table border (`gray-200`) and the
border extends the full row height, in line with the
surrounding row separators.
- Loading / empty states use a `colSpan={5}` row so the divider
doesn't show there, which is the desired behavior (no
inconsistent half-divider over centered text).
- No change to other columns, no change to Schedule/Audit/
Notifications tables, and no change to colors/widths/testids.
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated to this UI
change.
User feedback: in the Manage tab table the action icons looked
glued to the time value because there was no visible boundary
between the Time column and the Actions column.
- Adds `border-s border-gray-200` to both the Actions header
cell (`<th>`) and the per-row Actions data cell (`<td>`) in
the Manage tab table inside `executive-meetings.tsx`.
- Uses the logical `border-s` (inline-start) so the divider
lands on the correct edge of the Actions column in both LTR
and RTL — visually it always sits between Time and Actions.
- Color matches the existing table border (`gray-200`) and the
border extends the full row height, in line with the
surrounding row separators.
- Loading / empty states use a `colSpan={5}` row so the divider
doesn't show there, which is the desired behavior (no
inconsistent half-divider over centered text).
- No change to other columns, no change to Schedule/Audit/
Notifications tables, and no change to colors/widths/testids.
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated to this UI
change.
User feedback: "سو فريز هنا لأني نزلت تحت وما شفتها" — the
Manage tab's bulk-action toolbar (added in #332) was not
visible after scrolling down through the meetings list.
- Adds a `headingRef` + `ResizeObserver` effect in
`ManageSection` that publishes the live height of
`em-manage-heading-bar` as a `--em-manage-heading-h` CSS
var on the `[data-em-root]` element. Mirrors the Schedule
tab's existing pattern at L2155 for `--em-heading-h`.
Distinct var name avoids collision when switching tabs;
cleared on unmount so the Schedule tab doesn't inherit a
stale offset.
- Updates `em-manage-bulk-toolbar` to `position: sticky`
with `top: calc(var(--em-header-h) + var(--em-manage-heading-h))`,
matching horizontal bleed (`-mx-3 sm:-mx-6 px-3 sm:px-6`)
and `#f4f6fb` page-bg of the heading bar so the two visually
stack as a single header unit. Drops the rounded card style,
uses a bottom-border + drop-shadow for the seam.
- Adds `print:hidden` for parity with the Schedule
counterpart so the toolbar doesn't leak into PDF exports.
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated to this
change. No behavior change to the Schedule tab.
User feedback: "سو فريز هنا لأني نزلت تحت وما شفتها" — the
Manage tab's bulk-action toolbar (added in #332) was not
visible after scrolling down through the meetings list.
- Adds a `headingRef` + `ResizeObserver` effect in
`ManageSection` that publishes the live height of
`em-manage-heading-bar` as a `--em-manage-heading-h` CSS
var on the `[data-em-root]` element. Mirrors the Schedule
tab's existing pattern at L2155 for `--em-heading-h`.
Distinct var name avoids collision when switching tabs;
cleared on unmount so the Schedule tab doesn't inherit a
stale offset.
- Updates `em-manage-bulk-toolbar` to `position: sticky`
with `top: calc(var(--em-header-h) + var(--em-manage-heading-h))`,
matching horizontal bleed (`-mx-3 sm:-mx-6 px-3 sm:px-6`)
and `#f4f6fb` page-bg of the heading bar so the two visually
stack as a single header unit. Drops the rounded card style,
uses a bottom-border + drop-shadow for the seam.
- Adds `print:hidden` for parity with the Schedule
counterpart so the toolbar doesn't leak into PDF exports.
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Pre-existing
api-server / test workflow failures are unrelated to this
change. No behavior change to the Schedule tab.
Brings the Schedule tab's in-day search (#329) and bulk
delete/duplicate-to-date (#330) into the Manage tab of
executive-meetings.tsx (`ManageSection`).
- Adds `searchQuery` state + search input in the manage heading
bar; reuses module-level `normalizeForSearch` /
`meetingMatchesSearch` helpers and the same "reset on date
change" semantics as the Schedule tab.
- Adds `selectedMeetingIds` Set + tri-state "select all
visible" header checkbox + per-row checkbox column. Action
handlers always intersect the selection set with the visible
(filtered) rows, matching Schedule's safety pattern.
- Adds bulk-delete (with confirm + aggregated toast via
`Promise.allSettled`) and bulk-duplicate-to-date (separate
Dialog mirroring the Schedule one).
- Bumps loading/empty `colSpan` from 4 → 5 for the new
checkbox column.
- Reuses existing `executiveMeetings.schedule.*` locale keys
(already present in both ar.json + en.json from #329/#330);
no new strings were needed.
- New testids: `em-manage-search`, `em-manage-search-clear`,
`em-manage-select-all`, `em-manage-row-select-{id}`,
`em-manage-bulk-toolbar`, `em-manage-bulk-selection-count`,
`em-manage-bulk-clear-selection`, `em-manage-bulk-duplicate`,
`em-manage-bulk-duplicate-{date,confirm}`,
`em-manage-bulk-delete`.
Out of scope per plan (deferred): undo for bulk-delete on
Manage, cross-date "select all on this date".
`pnpm -C artifacts/tx-os exec tsc --noEmit` clean. Architect
review passed. Pre-existing api-server / test workflow
failures are unrelated to this change.
Lets users find a meeting in the day's schedule by typing part of its
title, an attendee name, or other text — instead of scrolling.
artifacts/tx-os/src/pages/executive-meetings.tsx
- Added two top-level helpers near translateSaveError:
- normalizeForSearch(input): strips HTML tags + entities, drops
Arabic diacritics + tatweel, folds common letter variants
(آأإٱ→ا, ى→ي, ة→ه, ؤ→و, ئ→ي), lowercases.
- meetingMatchesSearch(meeting, needle): tests title (AR/EN),
attendee names + titles, location, notes, and merge text via
normalized substring containment.
- ScheduleSection: added searchQuery state, useEffect that resets it
whenever the active `date` changes, normalizedSearch memo, and a
filteredMeetings memo derived from orderedMeetings.
- Switched the row render path to filteredMeetings: SortableContext
items, the .map render, and the select-all overlay's "anything to
select" check now read from the filtered list. The
setAllMeetingsSelected and allSelectedState helpers also operate
on filteredMeetings so the header checkbox + select-all reflect
what's actually visible.
- Added a new "no matches" empty-state row that appears only when
the day has meetings but the active search filters them all out
(the original "no meetings today" row is preserved for empty days).
- New search Input + clear-X button placed in the schedule toolbar,
next to the existing date picker. Hidden in print so PDFs export
the full schedule.
- MeetingRow gained an optional `displayNumber` prop. The "#" cell
now renders displayNumber when provided, falling back to the
server's dailyNumber. The schedule passes idx+1, so visible rows
always show 1, 2, 3… without gaps when the list is filtered.
artifacts/tx-os/src/locales/ar.json + en.json
- New keys under executiveMeetings.schedule: searchPlaceholder,
searchAria, searchClear, searchNoMatches.
- Bulk delete now intersects the selection with filteredMeetings at
action time. The selection set is intentionally NOT auto-pruned
when the search narrows (so clearing the search restores the
user's earlier picks), but destructive actions only ever target
rows the user can currently see.
- Print/PDF export ignores the active search. An `isPrinting` flag
driven by beforeprint/afterprint + a `print` media-query listener
swaps the table to the unfiltered orderedMeetings while printing,
so generated PDFs always contain the full day's schedule even
when the user has typed into the search box.
Out of scope: cross-day search, prev/next-day chevron buttons,
search history, fuzzy matching.
Verified: `tsc --noEmit` clean.
Lets users find a meeting in the day's schedule by typing part of its
title, an attendee name, or other text — instead of scrolling.
artifacts/tx-os/src/pages/executive-meetings.tsx
- Added two top-level helpers near translateSaveError:
- normalizeForSearch(input): strips HTML tags + entities, drops
Arabic diacritics + tatweel, folds common letter variants
(آأإٱ→ا, ى→ي, ة→ه, ؤ→و, ئ→ي), lowercases.
- meetingMatchesSearch(meeting, needle): tests title (AR/EN),
attendee names + titles, location, notes, and merge text via
normalized substring containment.
- ScheduleSection: added searchQuery state, useEffect that resets it
whenever the active `date` changes, normalizedSearch memo, and a
filteredMeetings memo derived from orderedMeetings.
- Switched the row render path to filteredMeetings: SortableContext
items, the .map render, and the select-all overlay's "anything to
select" check now read from the filtered list. The
setAllMeetingsSelected and allSelectedState helpers also operate
on filteredMeetings so the header checkbox + select-all reflect
what's actually visible.
- Added a new "no matches" empty-state row that appears only when
the day has meetings but the active search filters them all out
(the original "no meetings today" row is preserved for empty days).
- New search Input + clear-X button placed in the schedule toolbar,
next to the existing date picker. Hidden in print so PDFs export
the full schedule.
- MeetingRow gained an optional `displayNumber` prop. The "#" cell
now renders displayNumber when provided, falling back to the
server's dailyNumber. The schedule passes idx+1, so visible rows
always show 1, 2, 3… without gaps when the list is filtered.
artifacts/tx-os/src/locales/ar.json + en.json
- New keys under executiveMeetings.schedule: searchPlaceholder,
searchAria, searchClear, searchNoMatches.
- Bulk delete now intersects the selection with filteredMeetings at
action time. The selection set is intentionally NOT auto-pruned
when the search narrows (so clearing the search restores the
user's earlier picks), but destructive actions only ever target
rows the user can currently see.
Out of scope: cross-day search, prev/next-day chevron buttons,
search history, fuzzy matching.
Verified: `tsc --noEmit` clean.
Two small UX fixes for the Add/Edit Meeting save toast.
artifacts/tx-os/src/pages/executive-meetings.tsx
- Added a `translateSaveError(rawMsg, t)` helper near the other
top-level helpers (~line 425). It substring-matches the API's
Zod-generated message "startTime must be <= endTime" and returns
the translated copy from
`executiveMeetings.manage.errors.endTimeBeforeStart`. Anything
unknown falls through to the raw server message so unexpected
errors aren't silently hidden.
- The save catch block now passes the message through this helper
before showing the toast. Title still uses
`errors.saveFailed`.
artifacts/tx-os/src/locales/ar.json + en.json
- New translation key `executiveMeetings.manage.errors.endTimeBeforeStart`:
- ar: "وقت الانتهاء يجب أن يكون بعد وقت البدء"
- en: "End time must be after start time"
artifacts/tx-os/src/components/ui/toast.tsx
- ToastViewport: replaced the mobile-top + desktop-bottom-right
positioning with a single bottom-center placement on every
viewport (`fixed bottom-0 left-1/2 -translate-x-1/2`). Width
is still capped at `md:max-w-[420px]`. The flex direction is
`flex-col` (no longer `flex-col-reverse`) since there is now
only one slide-in direction.
- toastVariants: dropped `data-[state=open]:slide-in-from-top-full`
so toasts slide in from the bottom on every breakpoint, matching
the new viewport position.
Out of scope (untouched): API validation, other dialogs/pages,
preventive client-side validation for invalid time ranges.
After #322 trimmed the dialog, the Save/Cancel footer slid below
the visible 90vh sheet on phones because the whole DialogContent
was a single overflow-y-auto block.
Changes in artifacts/tx-os/src/pages/executive-meetings.tsx:
MeetingFormDialog
- Rebuilt DialogContent as a flex column: shrink-0 header,
flex-1 min-h-0 overflow-y-auto middle (the only scroller),
shrink-0 footer. Save/Cancel now stay pinned at the bottom
even after many attendees are added.
- Reduced mobile padding/gaps (`p-4 sm:p-6`,
`gap-3 sm:gap-4`, grid `gap-2 sm:gap-3`). Desktop spacing
unchanged.
- Switched the field grid from `grid-cols-1 sm:grid-cols-2`
to a flat `grid-cols-2` so the two short fields (التاريخ,
الرقم اليومي) share a row even on mobile. Title spans both
columns; time pickers span both on mobile and one each on
sm+ via the new mobileFull variant.
- Inner scroll wrapper uses negative margins + matching padding
so scroll content can extend edge-to-edge without losing the
dialog's gutter.
FormRow helper
- Broadened `full` from `sm:col-span-2` to `col-span-2`
(works in both 1-col and 2-col grids; only callers using
`full` are in this file).
- Added `mobileFull` prop → `col-span-2 sm:col-span-1` for
the time pickers, so AM/PM controls don't get squeezed on
phones.
Out of scope (untouched): validation, save logic, attendee
model, removed-field defaults from #322, duplicate dialog,
audit, schedule view, translations.
Refactor executive meetings form layout in `executive-meetings.tsx` to use a responsive grid, ensuring the Arabic title spans full width and other fields stack appropriately on smaller screens. Update Arabic and English locale files (`ar.json`, `en.json`) to change the Arabic title field label from "العنوان (عربي)" to "العنوان".
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 57375b7d-57d9-4fe3-a745-00a2cad59df8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/2VzZgdn
Replit-Helium-Checkpoint-Created: true
Trim the إضافة اجتماع / تعديل اجتماع dialog and attendee rows in
artifacts/tx-os/src/pages/executive-meetings.tsx down to the fields
the user actually fills in.
MeetingFormDialog
- Removed FormRows: titleEn, location, meetingUrl, platform, status,
isHighlighted, notes.
- Kept titleAr, meetingDate, dailyNumber, startTime, endTime, and
the attendees section.
- Save validation now only requires titleAr.
- titleEn is always mirrored from titleAr on save. The English input
is gone, so there is no way to keep an independent English title;
always syncing avoids leaving a stale server-side titleEn after the
Arabic title is edited. The API still receives a non-empty titleEn
so POST validation (min length 1) is satisfied.
- MeetingFormState, emptyMeetingForm, and openEdit are unchanged so
hidden fields (location, notes, platform, status, isHighlighted,
meetingUrl) round-trip through the save body and existing data is
preserved on edit.
SortableAttendeeRow
- Removed the parenthesized title Input and the
internal/virtual/external Select.
- Dropped the now-unused onChangeTitle / onChangeAttendanceType
props from the component signature and the call site.
- Existing attendees keep their stored title and attendanceType;
new attendees added via addAttendee continue to default to
title=null and attendanceType="internal" so the schedule's
grouping keeps working.
Cleanup
- Removed the now-unused Textarea import (only the notes field
used it). Switch is still imported because it is used elsewhere
in the file.
Code review found two issues that were fixed before completion:
- Mirroring made deterministic (always sync) instead of
blank-only fallback.
- Orphaned Textarea import removed.
No DB schema, API, schedule view, Manage list, audit, notifications,
or translation changes.
Trim the إضافة اجتماع / تعديل اجتماع dialog and attendee rows in
artifacts/tx-os/src/pages/executive-meetings.tsx down to the fields
the user actually fills in.
MeetingFormDialog
- Removed FormRows: titleEn, location, meetingUrl, platform, status,
isHighlighted, notes.
- Kept titleAr, meetingDate, dailyNumber, startTime, endTime, and
the attendees section.
- Save validation now only requires titleAr.
- titleEn is always mirrored from titleAr on save. The English input
is gone, so there is no way to keep an independent English title;
always syncing avoids leaving a stale server-side titleEn after the
Arabic title is edited. The API still receives a non-empty titleEn
so POST validation (min length 1) is satisfied.
- MeetingFormState, emptyMeetingForm, and openEdit are unchanged so
hidden fields (location, notes, platform, status, isHighlighted,
meetingUrl) round-trip through the save body and existing data is
preserved on edit.
SortableAttendeeRow
- Removed the parenthesized title Input and the
internal/virtual/external Select.
- Dropped the now-unused onChangeTitle / onChangeAttendanceType
props from the component signature and the call site.
- Existing attendees keep their stored title and attendanceType;
new attendees added via addAttendee continue to default to
title=null and attendanceType="internal" so the schedule's
grouping keeps working.
Cleanup
- Removed the now-unused Textarea import (only the notes field
used it). Switch is still imported because it is used elsewhere
in the file.
Code review found two issues that were fixed before completion:
- Mirroring made deterministic (always sync) instead of
blank-only fallback.
- Orphaned Textarea import removed.
No DB schema, API, schedule view, Manage list, audit, notifications,
or translation changes.