6e49010940d63466d4c51319a1a60dfbf44c31a4
646 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e49010940 |
notes: user-defined folders with drag-drop (task #413)
DB - New `note_folders` table (per-user unique name); `notes.folder_id` nullable with `ON DELETE SET NULL` so deleting a folder preserves its notes. API - /note-folders CRUD (GET, POST, PATCH, DELETE), all user-scoped. - /notes POST/PATCH validate folder ownership before assignment (no cross-user folder bleed). - Folder noteCount is tab-aware (active vs archived) via ?archived=. - OpenAPI spec extended with /note-folders endpoints, NoteFolder schema, and folderId on SentNote/ReceivedNote. Codegen regenerated for api-client-react and api-zod (`pnpm --filter @workspace/api-spec run codegen`). Client - NoteFolder type and useNoteFolders / useCreateFolder / useUpdateFolder / useDeleteFolder / useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches; rollback on error). UI (FoldersRail next to My Notes + Archive tabs only) - Desktop: sidebar with All / Unfiled / user folders + counts. - Mobile: horizontal chip row (flex + overflow-x-auto) above the notes grid. - Per-folder kebab dropdown with Rename + Delete. - Newly-created folder is auto-selected. - HTML5 draggable note cards on desktop; touch long-press fallback (350ms) for iPad/mobile via shared drop pipeline (registerFolderDropHandler + hit-tested `[data-folder-drop]`). - Dev-only `window.__notesTouchTest` helper (gated to non-production builds) exposes the touch pipeline so e2e tests can drive it deterministically without synthesizing native TouchEvents. Bilingual - notes.folders.* keys added to en.json and ar.json (RTL inherited from existing dir prop wired from i18n.language). Tests (artifacts/tx-os/tests/notes-folders.spec.mjs) - create folder + auto-select - drag note → folder, refresh-and-still-in-folder persistence - filter by folder / Unfiled / All - drag between folders (count math) - rename via kebab - delete a non-empty folder via kebab → notes return to Unfiled (FK SET NULL) - cross-user folder rejection (400) - Arabic + RTL render - separate touch test (414×800, hasTouch+isMobile) covers chip-row layout and exercises the touch drop pipeline through window helper. All API node tests + tx-os Playwright suite + tsc clean. |
||
|
|
b976c408a6 |
notes: user-defined folders with drag-drop (task #413)
DB - New `note_folders` table (per-user unique name); `notes.folder_id` nullable with `ON DELETE SET NULL` so deleting a folder preserves its notes. API - /note-folders CRUD (GET, POST, PATCH, DELETE), all user-scoped. - /notes POST/PATCH validate folder ownership before assignment (no cross-user folder bleed). - Folder noteCount is tab-aware (active vs archived) via ?archived=. - OpenAPI spec extended with /note-folders endpoints, NoteFolder schema, and folderId on SentNote/ReceivedNote. Codegen regenerated for api-client-react and api-zod (`pnpm --filter @workspace/api-spec run codegen`). Client - NoteFolder type and useNoteFolders / useCreateFolder / useUpdateFolder / useDeleteFolder / useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches; rollback on error). UI (FoldersRail next to My Notes + Archive tabs only) - Desktop: sidebar with All / Unfiled / user folders + counts. - Mobile: horizontal chip row (flex + overflow-x-auto) above the notes grid. - Per-folder kebab dropdown with Rename + Delete. - Newly-created folder is auto-selected. - HTML5 draggable note cards on desktop; touch long-press fallback (350ms) for iPad/mobile via shared drop pipeline (registerFolderDropHandler + hit-tested `[data-folder-drop]`). - Dev-only `window.__notesTouchTest` helper (gated to non-production builds) exposes the touch pipeline so e2e tests can drive it deterministically without synthesizing native TouchEvents. Bilingual - notes.folders.* keys added to en.json and ar.json (RTL inherited from existing dir prop wired from i18n.language). Tests (artifacts/tx-os/tests/notes-folders.spec.mjs) - create folder + auto-select - drag note → folder, refresh-and-still-in-folder persistence - filter by folder / Unfiled / All - drag between folders (count math) - rename via kebab - delete a non-empty folder via kebab → notes return to Unfiled (FK SET NULL) - cross-user folder rejection (400) - Arabic + RTL render - separate touch test (414×800, hasTouch+isMobile) covers chip-row layout and exercises the touch drop pipeline through window helper. All API node tests + tx-os Playwright suite + tsc clean. |
||
|
|
f562749214 |
notes: user-defined folders with drag-drop (task #413)
DB - New `note_folders` table (per-user unique name); `notes.folder_id` nullable with `ON DELETE SET NULL` so deleting a folder preserves its notes. API - /note-folders CRUD (GET, POST, PATCH, DELETE), all user-scoped. - /notes POST/PATCH validate folder ownership before assignment (no cross-user folder bleed). - Folder noteCount is tab-aware (active vs archived) via ?archived=. - OpenAPI spec extended with /note-folders endpoints, NoteFolder schema, and folderId on SentNote/ReceivedNote. Codegen regenerated for api-client-react and api-zod (`pnpm --filter @workspace/api-spec run codegen`). Client - NoteFolder type and useNoteFolders / useCreateFolder / useUpdateFolder / useDeleteFolder / useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches; rollback on error). UI (FoldersRail next to My Notes + Archive tabs only) - Desktop: sidebar with All / Unfiled / user folders + counts. - Mobile: horizontal chip row (flex + overflow-x-auto) above the notes grid. - Per-folder kebab dropdown with Rename + Delete. - Newly-created folder is auto-selected. - HTML5 draggable note cards on desktop; touch long-press fallback (350ms) for iPad/mobile via shared drop pipeline (registerFolderDropHandler + hit-tested `[data-folder-drop]`). - Dev-only `window.__notesTouchTest` helper (gated to non-production builds) exposes the touch pipeline so e2e tests can drive it deterministically without synthesizing native TouchEvents. Bilingual - notes.folders.* keys added to en.json and ar.json (RTL inherited from existing dir prop wired from i18n.language). Tests (artifacts/tx-os/tests/notes-folders.spec.mjs) - create folder + auto-select - drag note → folder, refresh-and-still-in-folder persistence - filter by folder / Unfiled / All - drag between folders (count math) - rename via kebab - delete a non-empty folder via kebab → notes return to Unfiled (FK SET NULL) - cross-user folder rejection (400) - Arabic + RTL render - separate touch test (414×800, hasTouch+isMobile) covers chip-row layout and exercises the touch drop pipeline through window helper. All API node tests + tx-os Playwright suite + tsc clean. |
||
|
|
1889407565 |
notes: user-defined folders with drag-drop (task #413)
DB - New `note_folders` table (per-user unique name); `notes.folder_id` nullable with `ON DELETE SET NULL` so deleting a folder preserves its notes. API - /note-folders CRUD (GET, POST, PATCH, DELETE), all user-scoped. - /notes POST/PATCH validate folder ownership before assignment (no cross-user folder bleed). - Folder noteCount is tab-aware (active vs archived) via ?archived=. - OpenAPI spec extended with /note-folders endpoints, NoteFolder schema, and folderId on SentNote/ReceivedNote. Codegen regenerated for api-client-react and api-zod (`pnpm --filter @workspace/api-spec run codegen`). Client - NoteFolder type and useNoteFolders / useCreateFolder / useUpdateFolder / useDeleteFolder / useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches; rollback on error). UI (FoldersRail next to My Notes + Archive tabs only) - Desktop: sidebar with All / Unfiled / user folders + counts. - Mobile: horizontal chip row (flex + overflow-x-auto) above the notes grid. - Per-folder kebab dropdown with Rename + Delete. - Newly-created folder is auto-selected. - HTML5 draggable note cards on desktop; touch long-press fallback (350ms) for iPad/mobile via shared drop pipeline (registerFolderDropHandler + hit-tested `[data-folder-drop]`). - Dev-only `window.__notesTouchTest` helper (gated to non-production builds) exposes the touch pipeline so e2e tests can drive it deterministically without synthesizing native TouchEvents. Bilingual - notes.folders.* keys added to en.json and ar.json (RTL inherited from existing dir prop wired from i18n.language). Tests (artifacts/tx-os/tests/notes-folders.spec.mjs) - create folder + auto-select - drag note → folder, refresh-and-still-in-folder persistence - filter by folder / Unfiled / All - drag between folders (count math) - rename via kebab - delete a non-empty folder via kebab → notes return to Unfiled (FK SET NULL) - cross-user folder rejection (400) - Arabic + RTL render - separate touch test (414×800, hasTouch+isMobile) covers chip-row layout and exercises the touch drop pipeline through window helper. All API node tests + tx-os Playwright suite + tsc clean. |
||
|
|
9fd4671327 |
notes: user-defined folders with drag-drop (task #413)
DB - New `note_folders` table (per-user unique name); `notes.folder_id` nullable with `ON DELETE SET NULL` so deleting a folder preserves its notes. API - /note-folders CRUD (GET, POST, PATCH, DELETE), all user-scoped. - /notes POST/PATCH validate folder ownership before assignment (no cross-user folder bleed). - Folder noteCount is tab-aware (active vs archived) via ?archived=. - OpenAPI spec extended with /note-folders endpoints, NoteFolder schema, and folderId on SentNote/ReceivedNote. Codegen regenerated for api-client-react and api-zod (`pnpm --filter @workspace/api-spec run codegen`). Client - NoteFolder type and useNoteFolders / useCreateFolder / useUpdateFolder / useDeleteFolder / useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches; rollback on error). UI (FoldersRail next to My Notes + Archive tabs only) - Desktop: sidebar with All / Unfiled / user folders + counts. - Mobile: horizontal chip row (flex + overflow-x-auto) above the notes grid. - Per-folder kebab dropdown with Rename + Delete. - Newly-created folder is auto-selected. - HTML5 draggable note cards on desktop; touch long-press fallback (350ms) for iPad/mobile via shared drop pipeline (registerFolderDropHandler + hit-tested `[data-folder-drop]`). - Dev-only `window.__notesTouchTest` helper (gated to non-production builds) exposes the touch pipeline so e2e tests can drive it deterministically without synthesizing native TouchEvents. Bilingual - notes.folders.* keys added to en.json and ar.json (RTL inherited from existing dir prop wired from i18n.language). Tests (artifacts/tx-os/tests/notes-folders.spec.mjs) - create folder + auto-select - drag note → folder, refresh-and-still-in-folder persistence - filter by folder / Unfiled / All - drag between folders (count math) - rename via kebab - delete a non-empty folder via kebab → notes return to Unfiled (FK SET NULL) - cross-user folder rejection (400) - Arabic + RTL render - separate touch test (414×800, hasTouch+isMobile) covers chip-row layout and exercises the touch drop pipeline through window helper. All API node tests + tx-os Playwright suite + tsc clean. |
||
|
|
6826b475b2 |
notes: user-defined folders with drag-drop (task #413)
DB - New `note_folders` table (per-user unique name); `notes.folder_id` nullable with `ON DELETE SET NULL` so deleting a folder preserves its notes. API - /note-folders CRUD (GET, POST, PATCH, DELETE), all user-scoped. - /notes POST/PATCH validate folder ownership before assignment (no cross-user folder bleed). - Folder noteCount is tab-aware (active vs archived) via ?archived=. - OpenAPI spec extended with /note-folders endpoints, NoteFolder schema, and folderId on SentNote/ReceivedNote. Codegen regenerated for api-client-react and api-zod (`pnpm --filter @workspace/api-spec run codegen`). Client - NoteFolder type and useNoteFolders / useCreateFolder / useUpdateFolder / useDeleteFolder / useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches; rollback on error). UI (FoldersRail next to My Notes + Archive tabs only) - Desktop: sidebar with All / Unfiled / user folders + counts. - Mobile: horizontal chip row (flex + overflow-x-auto) above the notes grid. - Per-folder kebab dropdown with Rename + Delete. - Newly-created folder is auto-selected. - HTML5 draggable note cards on desktop; touch long-press fallback (350ms) for iPad/mobile via shared drop pipeline (registerFolderDropHandler + hit-tested `[data-folder-drop]`). - Dev-only `window.__notesTouchTest` helper (gated to non-production builds) exposes the touch pipeline so e2e tests can drive it deterministically without synthesizing native TouchEvents. Bilingual - notes.folders.* keys added to en.json and ar.json (RTL inherited from existing dir prop wired from i18n.language). Tests (artifacts/tx-os/tests/notes-folders.spec.mjs) - create folder + auto-select - drag note → folder, refresh-and-still-in-folder persistence - filter by folder / Unfiled / All - drag between folders (count math) - rename via kebab - delete a non-empty folder via kebab → notes return to Unfiled (FK SET NULL) - cross-user folder rejection (400) - Arabic + RTL render - separate touch test (414×800, hasTouch+isMobile) covers chip-row layout and exercises the touch drop pipeline through window helper. All API node tests + tx-os Playwright suite + tsc clean. |
||
|
|
0bcfb9017d |
notes: user-defined folders with drag-drop (task #413)
- DB: new note_folders table (per-user unique name) + nullable folder_id on notes with ON DELETE SET NULL. - API: full /note-folders CRUD with user scoping; /notes POST/PATCH validate folder ownership; folder noteCount is tab-aware (active vs archived) via ?archived= query. - Client: NoteFolder type, useNoteFolders/useCreateFolder/useUpdateFolder/ useDeleteFolder/useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches). - UI: new FoldersRail rendered next to My Notes + Archive tabs; HTML5 draggable note cards on desktop, touch long-press fallback for iPad/mobile, responsive layout (horizontal chip row on small viewports, sidebar on md+); newly-created folder is auto-selected. - Bilingual: notes.folders.* keys added to en.json and ar.json (RTL works via existing dir prop wired from i18n.language). - Test: tests/notes-folders.spec.mjs covers create folder + auto-select, drag note to folder, refresh-and-still-in-folder persistence, filter by folder/Unfiled, drag between folders, rename, delete a non-empty folder (FK SET NULL), cross-user folder rejection (400), Arabic+RTL render, plus a separate touch test (iPhone-sized viewport, hasTouch+isMobile) that validates the chip-row layout (display:flex + overflow-x:auto) and exercises the touch drop pipeline (registered handler + dispatchTouchDropAt hit-test). Drift from plan: none. Code-review pass round 2 added: touch fallback, responsive mobile rail, auto-select-on-create, refresh-persistence test, removed defensive try/catch around dataTransfer. |
||
|
|
2b01cc1c24 |
notes: user-defined folders with drag-drop (task #413)
- DB: new note_folders table (per-user unique name) + nullable folder_id on notes with ON DELETE SET NULL. - API: full /note-folders CRUD with user scoping; /notes POST/PATCH validate folder ownership; folder noteCount is tab-aware (active vs archived) via ?archived= query. - Client: NoteFolder type, useNoteFolders/useCreateFolder/useUpdateFolder/ useDeleteFolder/useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches). - UI: new FoldersRail rendered next to My Notes + Archive tabs; HTML5 draggable note cards on desktop, touch long-press fallback for iPad/mobile, responsive layout (horizontal chip row on small viewports, sidebar on md+); newly-created folder is auto-selected. - Bilingual: notes.folders.* keys added to en.json and ar.json (RTL works via existing dir prop wired from i18n.language). - Test: tests/notes-folders.spec.mjs covers create folder + auto-select, drag note to folder, refresh-and-still-in-folder persistence, filter by folder/Unfiled, drag between folders, rename, delete a non-empty folder (FK SET NULL), cross-user folder rejection (400), Arabic+RTL render, plus a separate touch test (iPhone-sized viewport, hasTouch+isMobile) that validates the chip-row layout (display:flex + overflow-x:auto) and exercises the touch drop pipeline (registered handler + dispatchTouchDropAt hit-test). Drift from plan: none. Code-review pass round 2 added: touch fallback, responsive mobile rail, auto-select-on-create, refresh-persistence test, removed defensive try/catch around dataTransfer. |
||
|
|
4d720fdeed |
notes: user-defined folders with drag-drop (task #413)
- DB: new note_folders table (per-user unique name) + nullable folder_id on notes with ON DELETE SET NULL. - API: full /note-folders CRUD with user scoping; /notes POST/PATCH validate folder ownership; folder noteCount is tab-aware (active vs archived) via ?archived= query. - Client: NoteFolder type, useNoteFolders/useCreateFolder/useUpdateFolder/ useDeleteFolder/useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches). - UI: new FoldersRail rendered next to My Notes + Archive tabs; HTML5 draggable note cards on desktop, touch long-press fallback for iPad/mobile, responsive layout (horizontal chip row on small viewports, sidebar on md+); newly-created folder is auto-selected. - Bilingual: notes.folders.* keys added to en.json and ar.json (RTL works via existing dir prop wired from i18n.language). - Test: tests/notes-folders.spec.mjs covers create folder + auto-select, drag note to folder, refresh-and-still-in-folder persistence, filter by folder/Unfiled, drag between folders, rename, delete a non-empty folder (FK SET NULL), cross-user folder rejection (400), Arabic+RTL render, plus a separate touch test (iPhone-sized viewport, hasTouch+isMobile) that validates the chip-row layout (display:flex + overflow-x:auto) and exercises the touch drop pipeline (registered handler + dispatchTouchDropAt hit-test). Drift from plan: none. Code-review pass round 2 added: touch fallback, responsive mobile rail, auto-select-on-create, refresh-persistence test, removed defensive try/catch around dataTransfer. |
||
|
|
fd194c5ed1 |
notes: user-defined folders with drag-drop (task #413)
- DB: new note_folders table (per-user unique name) + nullable folder_id on notes with ON DELETE SET NULL. - API: full /note-folders CRUD with user scoping; /notes POST/PATCH validate folder ownership; folder noteCount is tab-aware (active vs archived) via ?archived= query. - Client: NoteFolder type, useNoteFolders/useCreateFolder/useUpdateFolder/ useDeleteFolder/useMoveNoteToFolder hooks (optimistic across all ["notes", ...] caches). - UI: new FoldersRail rendered next to My Notes + Archive tabs; HTML5 draggable note cards on desktop, touch long-press fallback for iPad/mobile, responsive layout (horizontal chip row on small viewports, sidebar on md+); newly-created folder is auto-selected. - Bilingual: notes.folders.* keys added to en.json and ar.json (RTL works via existing dir prop wired from i18n.language). - Test: tests/notes-folders.spec.mjs covers create folder + auto-select, drag note to folder, refresh-and-still-in-folder persistence, filter by folder/Unfiled, drag between folders, rename, delete a non-empty folder (FK SET NULL), cross-user folder rejection (400), Arabic+RTL render, plus a separate touch test (iPhone-sized viewport, hasTouch+isMobile) that validates the chip-row layout (display:flex + overflow-x:auto) and exercises the touch drop pipeline (registered handler + dispatchTouchDropAt hit-test). Drift from plan: none. Code-review pass round 2 added: touch fallback, responsive mobile rail, auto-select-on-create, refresh-persistence test, removed defensive try/catch around dataTransfer. |
||
|
|
ba0da3d26c |
notes: user-defined folders with drag-drop (task #413)
- DB: new note_folders table (per-user unique name) + nullable folder_id on notes with ON DELETE SET NULL; pushed via @workspace/db. - API: full /note-folders CRUD with user scoping; /notes POST/PATCH validate folder ownership; folder noteCount is tab-aware (active vs archived) via ?archived= query param computed by GROUP BY (drizzle correlated subquery was returning 0 — replaced with two queries merged in JS). - Client: NoteFolder type, useNoteFolders/useCreateFolder/useUpdateFolder/ useDeleteFolder/useMoveNoteToFolder hooks (optimistic update for moves across all ["notes", ...] caches). - UI: new FoldersRail (artifacts/tx-os/src/components/notes/folders-rail.tsx) rendered next to My Notes + Archive tabs; HTML5 draggable note cards with module-scoped DRAGGING_NOTE_ID fallback; visible drop highlight; rename + delete + create inline; folder-scoped + Unfiled filtering in notes.tsx. - Bilingual: notes.folders.* keys added to en.json and ar.json (RTL works via existing dir prop wired from i18n.language). - Test: artifacts/tx-os/tests/notes-folders.spec.mjs covers create folder, drag note to folder, filter by folder/Unfiled, drag between folders, rename, delete a non-empty folder (verifies FK SET NULL + Unfiled count), cross-user folder rejection (400), and Arabic+RTL rendering. Drift from plan: none — all originally scoped work shipped. Strengthened e2e + cross-user check + tab-aware counts added per code review feedback. |
||
|
|
6d3e38ec26 |
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: bda408e0-d5cf-4fb7-b04e-7a48c77fe40c Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/uqawMO0 Replit-Helium-Checkpoint-Created: true |
||
|
|
4258aa092c |
Task #410: floating draggable note popup + reply alert at sender
Convert the incoming-note popup from a centered AlertDialog modal into
a floating, draggable, animated card (no backdrop) and surface the
same card variant at the original sender when the recipient replies.
UI / popup:
- Rewrite IncomingNotePopup as a fixed-positioned card with custom
pointer-event drag handler, viewport clamping, sessionStorage-
persisted position keyed per user, RTL-aware default anchor, ESC
dismissal, scale-in/fade enter animation, and click-through layer
(pointer-events-none wrapper). Initial framer-motion impl crashed
in vite (useRef-of-null / Invalid hook call); rewrote without
framer-motion using plain CSS transitions for stability.
- isDragging tracked in state so the transform transition is reliably
disabled during drag (per architect review).
Reply variant:
- Generalize incoming-note-queue with PopupPayload discriminated union
(note | reply); reply dedupe by replyId, note dedupe by noteId;
applyDismiss accepts number | {replyId}.
- Floating card switches heading + actions for reply variant: shows
replier name, reply text, and openThread / replyBack actions; the
recipient-only mark-read action is suppressed (owner can't mark own
outbound note read).
Sound + socket:
- New note_replied client handler enqueues the reply payload and plays
notificationSoundNote + vibrationEnabledNote (gated by
notificationsMuted + notifyNotesEnabled — no new prefs), deduped
via playedReplyIdsRef.
- Server emit at /notes/:id/reply enriched with replyContent (≤280
chars), replier (UserSummary), noteTitle, color so the client can
render without an extra round-trip.
i18n + tests:
- Add en/ar keys: replyHeading, replyHeadingNoSender, replyBack,
openThread, dragHint.
- Extend notes-popup-on-receive.spec.mjs: first test asserts
no [role=alertdialog], drag-handle visible, data-popup-kind="note";
new reply test asserts data-popup-kind="reply", reply text +
replier name visible, mark-read action absent, chime fires once
for sender on reply.
Drift from plan:
- Used custom pointer-event drag instead of framer-motion drag — the
framer-motion impl crashed in vite (React-instance null in dev).
Same UX (drag from header only, click-through behind card).
- E2E: api-server unit test failures (executive-meetings, pre-existing
and unrelated) abort the test workflow before playwright runs;
could not get a clean green run during this session. Tx-os
typecheck passes; browser console clean; popup interface (testIds,
data attrs, text) is identical to the previously-working
framer-motion run, so no functional regression expected.
|
||
|
|
71cfa14bf6 |
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: 65c1a634-bf1c-4ec0-a1bb-0835f701dd68 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/G3ZvHke Replit-Helium-Checkpoint-Created: true |
||
|
|
d8cb2c8dea |
feat(notes): make incoming-note popup attention-grabbing
Task #409: incoming notes now mirror UpcomingMeetingAlert's attention model — sound + vibration + visual pulse + persistent queue indicator — so recipients can't miss them. Changes: - DB: add notification_sound_note (default "knock"), notify_notes_enabled, vibration_enabled_note to users schema; pushed via drizzle. - API spec: add 3 new fields to AuthUser + UpdateNotificationPreferencesBody; regenerated codegen. - Auth route: include new fields in buildAuthUser + PATCH allowlist. - Socket hook (use-notifications-socket): play sound + vibrate on note_received, gated by mute/notifyNotesEnabled/socket warmup, with playedNoteIdsRef dedupe (mirrors upcoming-meeting-alert playedRef). - IncomingNotePopupContext: enqueue() returns boolean acceptance so the socket hook suppresses sound on own-note/duplicate. - Popup visual (incoming-note-popup): z-[110], ring-4 amber, shadow-2xl, animate-in zoom, avatar animate-ping pulse. - Persistent indicator on Notes app tile (home.tsx): badge = max(unread, queueLength), animate-ping ring while queueLength > 0, via useIncomingNotePopup().queueLength + new pulse prop on AppIconContent. - Settings UI: refactored to SLOT_KEYS map, added 3rd "Notes" slot (grid-cols-3) with enabled/vibration toggles + sound picker. - Locales en/ar: added notifSettings.slot.note, notesEnabled, vibrationNote. - Test instrumentation: NotificationPlayer exposes window.__txosNotifPlayCount and __txosNotifLastSound for E2E observability. - Playwright spec (notes-popup-on-receive): asserts chime fires once on recipient with sound="knock", sender plays nothing, pulse visible while queued, pulse gone after dismiss. Pre-existing typecheck errors in executive-meetings.ts (font settings scope) are unrelated to this task. |
||
|
|
d2b6e7f98c |
feat(notes): make incoming-note popup attention-grabbing
Task #409: incoming notes now mirror UpcomingMeetingAlert's attention model — sound + vibration + visual pulse + persistent queue indicator — so recipients can't miss them. Changes: - DB: add notification_sound_note (default "knock"), notify_notes_enabled, vibration_enabled_note to users schema; pushed via drizzle. - API spec: add 3 new fields to AuthUser + UpdateNotificationPreferencesBody; regenerated codegen. - Auth route: include new fields in buildAuthUser + PATCH allowlist. - Socket hook (use-notifications-socket): play sound + vibrate on note_received, gated by mute/notifyNotesEnabled/socket warmup, with playedNoteIdsRef dedupe (mirrors upcoming-meeting-alert playedRef). - IncomingNotePopupContext: enqueue() returns boolean acceptance so the socket hook suppresses sound on own-note/duplicate. - Popup visual (incoming-note-popup): z-[110], ring-4 amber, shadow-2xl, animate-in zoom, avatar animate-ping pulse. - Persistent indicator on Notes app tile (home.tsx): badge = max(unread, queueLength), animate-ping ring while queueLength > 0, via useIncomingNotePopup().queueLength + new pulse prop on AppIconContent. - Settings UI: refactored to SLOT_KEYS map, added 3rd "Notes" slot (grid-cols-3) with enabled/vibration toggles + sound picker. - Locales en/ar: added notifSettings.slot.note, notesEnabled, vibrationNote. - Test instrumentation: NotificationPlayer exposes window.__txosNotifPlayCount and __txosNotifLastSound for E2E observability. - Playwright spec (notes-popup-on-receive): asserts chime fires once on recipient with sound="knock", sender plays nothing, pulse visible while queued, pulse gone after dismiss. Pre-existing typecheck errors in executive-meetings.ts (font settings scope) are unrelated to this task. |
||
|
|
dcf90e1c2d |
feat(notes): make incoming-note popup attention-grabbing
Task #409: incoming notes now mirror UpcomingMeetingAlert's attention model — sound + vibration + visual pulse + persistent queue indicator — so recipients can't miss them. Changes: - DB: add notification_sound_note (default "knock"), notify_notes_enabled, vibration_enabled_note to users schema; pushed via drizzle. - API spec: add 3 new fields to AuthUser + UpdateNotificationPreferencesBody; regenerated codegen. - Auth route: include new fields in buildAuthUser + PATCH allowlist. - Socket hook (use-notifications-socket): play sound + vibrate on note_received, gated by mute/notifyNotesEnabled/socket warmup, with playedNoteIdsRef dedupe (mirrors upcoming-meeting-alert playedRef). - IncomingNotePopupContext: enqueue() returns boolean acceptance so the socket hook suppresses sound on own-note/duplicate. - Popup visual (incoming-note-popup): z-[110], ring-4 amber, shadow-2xl, animate-in zoom, avatar animate-ping pulse. - Persistent indicator on Notes app tile (home.tsx): badge = max(unread, queueLength), animate-ping ring while queueLength > 0, via useIncomingNotePopup().queueLength + new pulse prop on AppIconContent. - Settings UI: refactored to SLOT_KEYS map, added 3rd "Notes" slot (grid-cols-3) with enabled/vibration toggles + sound picker. - Locales en/ar: added notifSettings.slot.note, notesEnabled, vibrationNote. - Test instrumentation: NotificationPlayer exposes window.__txosNotifPlayCount and __txosNotifLastSound for E2E observability. - Playwright spec (notes-popup-on-receive): asserts chime fires once on recipient with sound="knock", sender plays nothing, pulse visible while queued, pulse gone after dismiss. Pre-existing typecheck errors in executive-meetings.ts (font settings scope) are unrelated to this task. |
||
|
|
8ca324a557 |
feat(notes): make incoming-note popup attention-grabbing
Task #409: incoming notes now mirror UpcomingMeetingAlert's attention model — sound + vibration + visual pulse + persistent queue indicator — so recipients can't miss them. Changes: - DB: add notification_sound_note (default "knock"), notify_notes_enabled, vibration_enabled_note to users schema; pushed via drizzle. - API spec: add 3 new fields to AuthUser + UpdateNotificationPreferencesBody; regenerated codegen. - Auth route: include new fields in buildAuthUser + PATCH allowlist. - Socket hook (use-notifications-socket): play sound + vibrate on note_received, gated by mute/notifyNotesEnabled/socket warmup, with playedNoteIdsRef dedupe (mirrors upcoming-meeting-alert playedRef). - IncomingNotePopupContext: enqueue() returns boolean acceptance so the socket hook suppresses sound on own-note/duplicate. - Popup visual (incoming-note-popup): z-[110], ring-4 amber, shadow-2xl, animate-in zoom, avatar animate-ping pulse. - Persistent indicator on Notes app tile (home.tsx): badge = max(unread, queueLength), animate-ping ring while queueLength > 0, via useIncomingNotePopup().queueLength + new pulse prop on AppIconContent. - Settings UI: refactored to SLOT_KEYS map, added 3rd "Notes" slot (grid-cols-3) with enabled/vibration toggles + sound picker. - Locales en/ar: added notifSettings.slot.note, notesEnabled, vibrationNote. - Test instrumentation: NotificationPlayer exposes window.__txosNotifPlayCount and __txosNotifLastSound for E2E observability. - Playwright spec (notes-popup-on-receive): asserts chime fires once on recipient with sound="knock", sender plays nothing, pulse visible while queued, pulse gone after dismiss. Pre-existing typecheck errors in executive-meetings.ts (font settings scope) are unrelated to this task. |
||
|
|
aaadd9b520 |
feat(notes): make incoming-note popup attention-grabbing
Task #409: incoming notes now mirror UpcomingMeetingAlert's attention model so recipients can't miss them. Changes: - DB: add `notification_sound_note` (default "knock"), `notify_notes_enabled`, `vibration_enabled_note` to users schema; pushed via drizzle. - API spec: add 3 new fields to AuthUser + UpdateNotificationPreferencesBody; regenerated codegen. - Auth route: include new fields in buildAuthUser + PATCH allowlist. - Socket hook: play notification sound + vibrate on `note_received`, gated by mute/notifyNotesEnabled/socket warmup, with playedNoteIdsRef dedupe (mirrors upcoming-meeting-alert playedRef). - IncomingNotePopupContext: enqueue() now returns boolean acceptance so the socket hook can suppress sound on own-note/duplicate. - Popup visual: z-[110], ring-4 amber, shadow-2xl, animate-in zoom, avatar animate-ping pulse. - Settings UI: refactored to SLOT_KEYS map, added 3rd "Notes" slot tab (grid-cols-3) with enabled/vibration toggles + sound picker. - Locales en/ar: added notifSettings.slot.note, notesEnabled, vibrationNote. Pre-existing typecheck errors in executive-meetings.ts (font settings scope) are unrelated to this task. |
||
|
|
33038106b4 |
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: 07143651-1eb7-4f19-9d54-e529fe42f635 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/G3ZvHke Replit-Helium-Checkpoint-Created: true |
||
|
|
45609c81d6 |
Task #406: Pop the note onto the recipient's screen on arrival
- Backend: extend `note_received` socket payload with note snapshot (title, content, color, sentAt), recipientRowId, senderUserId, and sender summary so the recipient can render the popup without an extra fetch. - Add IncomingNotePopupContext: FIFO queue, dedupe by note id, suppress events for the user's own sends, auto-clear on logout. - Add IncomingNotePopup AlertDialog that shows the note (title/content/ color) with a sender chip and Reply / Mark read / Open in Notes / Dismiss buttons. Acknowledge actions call /notes/:id/read. - Wire the popup globally in App.tsx (alongside the socket bridge). - Notes page accepts a `?thread=ID` deep link so the popup's Open and Reply buttons land on the inbox tab + thread dialog. - Added bilingual `notes.popup.*` strings in EN + AR (RTL respected). - New Playwright e2e (`notes-popup-on-receive.spec.mjs`) verifies the popup appears in a second browser context within seconds, sender doesn't see their own popup, and dismiss closes it. |
||
|
|
39d645ae4e |
Task #406: Pop the note onto the recipient's screen on arrival
- Backend: extend `note_received` socket payload with note snapshot (title, content, color, sentAt), recipientRowId, senderUserId, and sender summary so the recipient can render the popup without an extra fetch. - Add IncomingNotePopupContext: FIFO queue, dedupe by note id, suppress events for the user's own sends, auto-clear on logout. - Add IncomingNotePopup AlertDialog that shows the note (title/content/ color) with a sender chip and Reply / Mark read / Open in Notes / Dismiss buttons. Acknowledge actions call /notes/:id/read. - Wire the popup globally in App.tsx (alongside the socket bridge). - Notes page accepts a `?thread=ID` deep link so the popup's Open and Reply buttons land on the inbox tab + thread dialog. - Added bilingual `notes.popup.*` strings in EN + AR (RTL respected). - New Playwright e2e (`notes-popup-on-receive.spec.mjs`) verifies the popup appears in a second browser context within seconds, sender doesn't see their own popup, and dismiss closes it. |
||
|
|
1e358ab32e |
Task #406: Pop the note onto the recipient's screen on arrival
- Backend: extend `note_received` socket payload with note snapshot (title, content, color, sentAt), recipientRowId, senderUserId, and sender summary so the recipient can render the popup without an extra fetch. - Add IncomingNotePopupContext: FIFO queue, dedupe by note id, suppress events for the user's own sends, auto-clear on logout. - Add IncomingNotePopup AlertDialog that shows the note (title/content/ color) with a sender chip and Reply / Mark read / Open in Notes / Dismiss buttons. Acknowledge actions call /notes/:id/read. - Wire the popup globally in App.tsx (alongside the socket bridge). - Notes page accepts a `?thread=ID` deep link so the popup's Open and Reply buttons land on the inbox tab + thread dialog. - Added bilingual `notes.popup.*` strings in EN + AR (RTL respected). - New Playwright e2e (`notes-popup-on-receive.spec.mjs`) verifies the popup appears in a second browser context within seconds, sender doesn't see their own popup, and dismiss closes it. |
||
|
|
3d54270fdd |
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: d6173571-0cc3-4cd8-ac96-63f3c8723474 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/G3ZvHke Replit-Helium-Checkpoint-Created: true |
||
|
|
eade4643e5 |
Add ability to view personal notes and update note schemas
Adds a new endpoint for listing user-specific notes and refines OpenAPI schemas for better data structure definition. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 3565b59b-6890-4cf5-90a7-0c8041e15041 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/G3ZvHke Replit-Helium-Checkpoint-Created: true |
||
|
|
5b13fe16f5 |
Task #402: Convert Notes into in-app messaging
Original task: turn personal Notes into in-app messaging — sender composes a note (title/content/color), picks recipient(s), Send. Recipients get an Inbox with sender name, color, Read/Unread badge, and inline reply. Sender sees Sent Notes with per-recipient status. Sender's and recipient's copies must be INDEPENDENT, with backend access checks (admin sees everything), realtime updates, toasts, an unread badge on the Notes app tile, and full i18n + RTL. Four review rounds were addressed in this commit: Round 1 (independence): - Snapshot columns (title/content/color) on note_recipients; FKs dropped on note_recipients.note_id and note_replies.note_id so recipient threads survive the sender deleting their note. - /notes/received and /notes/:id/thread render the recipient snapshot. Round 2: - POST /notes/:id/reply: owner is now allowed to reply too. - Added GET /notes/:id as alias of /notes/:id/thread. - Added OpenAPI ops for the Notes routes; ran orval codegen. - use-notifications-socket.ts shows bilingual toasts for note_received / note_replied (suppressed during socket warmup). - home.tsx renders an unread badge on the Notes app tile. Round 3: - Archived tab now shows BOTH "My archived notes" and "Archived inbox". - Sender thread view groups replies by recipient with per-conversation header and per-reply author + localized timestamp. - Send dialog requires explicit AlertDialog confirmation + success toast. - Realtime toast strings moved off hardcoded EN/AR to i18n keys. - handleNoteDetail returns 403 (not 404) for non-participants of an existing conversation. - useReplyToNote accepts recipientUserId; ThreadDialog shows a recipient picker for owner replies on multi-recipient notes. Round 6 (this round): - OpenAPI: replaced all `additionalProperties: true` schemas in /notes paths with concrete component refs — NoteRecipientStatus (enum), NoteUserSummary, NoteRecipientSummary, SentNote, ReceivedNote, NoteReply, NoteThread, SendNoteResult. Re-ran orval; api-zod and api-client-react regenerated cleanly. - Trimmed narrative comments in artifacts/api-server/src/routes/notes.ts (handleNoteDetail, /sent, /received, /send, /read, /reply). - Schema FK + enum policy: kept noteId as plain integer (no FK) on note_recipients/note_replies — this is required so recipient snapshots survive sender deletion. Status remains a varchar but is constrained at the API boundary by the new NoteRecipientStatus enum schema and TS string-literal union on both server and client. - Migrations: this monorepo uses `drizzle-kit push` exclusively (lib/db has no migrations directory; drizzle.config.ts is push-only; package.json defines only push/push-force scripts). No migration files committed by design. Round 5: admin authorization fix - handleNoteDetail: admin bypass is now evaluated BEFORE the "non-participant 403" branch. When the sender has deleted the live note row but recipient snapshots remain, an admin GET /notes/:id now returns 200 with thread payload assembled from a fallback recipient snapshot (instead of 403). - New regression test: "admin can read a note whose sender deleted their copy (recipient snapshot remains)". Round 4: - Added GET /notes/my as an explicit alias of GET /notes (shared handleListMyNotes handler). - Removed `as any[]` cast in /notes/sent — replaced with a precise local SentNoteOut type derived from the query result. - Added auth coverage tests: - stranger forbidden from /read, /archive, /reply, GET /notes/:id - recipient cannot PATCH the sender's note body - admin can read any note via GET /notes/:id and sees full thread - GET /notes/my matches GET /notes createUser test helper now accepts an optional role. Note on schema migrations: this monorepo uses `drizzle-kit push` (no migration files); `pnpm --filter @workspace/db push` was already run when the new columns/tables landed. Tests: 11 backend tests in notes-share.test.mjs + 1 e2e (notes-inbox) all pass. tx-os typecheck is clean. Architect re-review: PASS. Pre-existing executive-meetings TS errors and the failing top-level `test` workflow are unrelated to this task. |
||
|
|
73c9953675 |
Task #402: Convert Notes into in-app messaging
Original task: turn personal Notes into in-app messaging — sender composes a note (title/content/color), picks recipient(s), Send. Recipients get an Inbox with sender name, color, Read/Unread badge, and inline reply. Sender sees Sent Notes with per-recipient status. Sender's and recipient's copies must be INDEPENDENT, with backend access checks (admin sees everything), realtime updates, toasts, an unread badge on the Notes app tile, and full i18n + RTL. Four review rounds were addressed in this commit: Round 1 (independence): - Snapshot columns (title/content/color) on note_recipients; FKs dropped on note_recipients.note_id and note_replies.note_id so recipient threads survive the sender deleting their note. - /notes/received and /notes/:id/thread render the recipient snapshot. Round 2: - POST /notes/:id/reply: owner is now allowed to reply too. - Added GET /notes/:id as alias of /notes/:id/thread. - Added OpenAPI ops for the Notes routes; ran orval codegen. - use-notifications-socket.ts shows bilingual toasts for note_received / note_replied (suppressed during socket warmup). - home.tsx renders an unread badge on the Notes app tile. Round 3: - Archived tab now shows BOTH "My archived notes" and "Archived inbox". - Sender thread view groups replies by recipient with per-conversation header and per-reply author + localized timestamp. - Send dialog requires explicit AlertDialog confirmation + success toast. - Realtime toast strings moved off hardcoded EN/AR to i18n keys. - handleNoteDetail returns 403 (not 404) for non-participants of an existing conversation. - useReplyToNote accepts recipientUserId; ThreadDialog shows a recipient picker for owner replies on multi-recipient notes. Round 5 (this round): admin authorization fix - handleNoteDetail: admin bypass is now evaluated BEFORE the "non-participant 403" branch. When the sender has deleted the live note row but recipient snapshots remain, an admin GET /notes/:id now returns 200 with thread payload assembled from a fallback recipient snapshot (instead of 403). - New regression test: "admin can read a note whose sender deleted their copy (recipient snapshot remains)". Round 4: - Added GET /notes/my as an explicit alias of GET /notes (shared handleListMyNotes handler). - Removed `as any[]` cast in /notes/sent — replaced with a precise local SentNoteOut type derived from the query result. - Added auth coverage tests: - stranger forbidden from /read, /archive, /reply, GET /notes/:id - recipient cannot PATCH the sender's note body - admin can read any note via GET /notes/:id and sees full thread - GET /notes/my matches GET /notes createUser test helper now accepts an optional role. Note on schema migrations: this monorepo uses `drizzle-kit push` (no migration files); `pnpm --filter @workspace/db push` was already run when the new columns/tables landed. Tests: 11 backend tests in notes-share.test.mjs + 1 e2e (notes-inbox) all pass. tx-os typecheck is clean. Architect re-review: PASS. Pre-existing executive-meetings TS errors and the failing top-level `test` workflow are unrelated to this task. |
||
|
|
672cb6280c |
Task #402: Convert Notes into in-app messaging
Original task: turn personal Notes into in-app messaging — sender composes a note (title/content/color), picks recipient(s), Send. Recipients get an Inbox with sender name, color, Read/Unread badge, and inline reply. Sender sees Sent Notes with per-recipient status. Sender's and recipient's copies must be INDEPENDENT, with backend access checks (admin sees everything), realtime updates, toasts, an unread badge on the Notes app tile, and full i18n + RTL. Four review rounds were addressed in this commit: Round 1 (independence): - Snapshot columns (title/content/color) on note_recipients; FKs dropped on note_recipients.note_id and note_replies.note_id so recipient threads survive the sender deleting their note. - /notes/received and /notes/:id/thread render the recipient snapshot. Round 2: - POST /notes/:id/reply: owner is now allowed to reply too. - Added GET /notes/:id as alias of /notes/:id/thread. - Added OpenAPI ops for the Notes routes; ran orval codegen. - use-notifications-socket.ts shows bilingual toasts for note_received / note_replied (suppressed during socket warmup). - home.tsx renders an unread badge on the Notes app tile. Round 3: - Archived tab now shows BOTH "My archived notes" and "Archived inbox". - Sender thread view groups replies by recipient with per-conversation header and per-reply author + localized timestamp. - Send dialog requires explicit AlertDialog confirmation + success toast. - Realtime toast strings moved off hardcoded EN/AR to i18n keys. - handleNoteDetail returns 403 (not 404) for non-participants of an existing conversation. - useReplyToNote accepts recipientUserId; ThreadDialog shows a recipient picker for owner replies on multi-recipient notes. Round 4 (this round): - Added GET /notes/my as an explicit alias of GET /notes (shared handleListMyNotes handler). - Removed `as any[]` cast in /notes/sent — replaced with a precise local SentNoteOut type derived from the query result. - Added auth coverage tests: - stranger forbidden from /read, /archive, /reply, GET /notes/:id - recipient cannot PATCH the sender's note body - admin can read any note via GET /notes/:id and sees full thread - GET /notes/my matches GET /notes createUser test helper now accepts an optional role. Note on schema migrations: this monorepo uses `drizzle-kit push` (no migration files); `pnpm --filter @workspace/db push` was already run when the new columns/tables landed. Tests: 11 backend tests in notes-share.test.mjs + 1 e2e (notes-inbox) all pass. tx-os typecheck is clean. Architect re-review: PASS. Pre-existing executive-meetings TS errors and the failing top-level `test` workflow are unrelated to this task. |
||
|
|
d1b4bb228c |
Task #402: Convert Notes into in-app messaging
Original task: turn personal Notes into in-app messaging — sender composes a note (title/content/color), picks recipient(s), Send. Recipients get an Inbox with sender name, color, Read/Unread badge, and inline reply. Sender sees Sent Notes with per-recipient status. Sender's and recipient's copies must be INDEPENDENT, with backend access checks (admin sees everything), realtime updates, toasts, an unread badge on the Notes app tile, and full i18n + RTL. Three review rounds were addressed in this commit: Round 1 (independence): - Snapshot columns (title/content/color) on note_recipients; FKs dropped on note_recipients.note_id and note_replies.note_id so recipient threads survive the sender deleting their note. - /notes/received and /notes/:id/thread render the recipient snapshot for recipients (sender/admin still see the live note). Round 2: - POST /notes/:id/reply: owner is now allowed to reply too. Owner replies do not change recipient status; recipient replies still flip to "replied" and clear archivedAt. - Added GET /notes/:id as an alias of /notes/:id/thread. - Added OpenAPI ops for the Notes routes and ran orval codegen. - use-notifications-socket.ts shows bilingual toasts for note_received / note_replied (suppressed during socket warmup). - home.tsx renders an unread badge on the Notes app tile. Round 3 (this round): - Archived tab now shows BOTH "My archived notes" and "Archived inbox" via the new ArchivedView component. - Sender thread view groups replies by recipient (GroupedReplies) with per-conversation header and per-reply author + localized timestamp. Recipient view stays flat. - Send dialog now requires explicit confirmation (AlertDialog) and shows a success / failure toast. - Realtime toast strings moved off hardcoded EN/AR to i18n keys (notes.toast.received|replied.*) added in en.json and ar.json. - handleNoteDetail returns 403 (not 404) when the caller is a non-participant of an existing conversation; 404 only when no trace of the note exists. - useReplyToNote accepts recipientUserId; ThreadDialog auto-targets the sole recipient on owner replies and shows a recipient picker when the owner has more than one recipient. Tests: 7 backend tests in notes-share.test.mjs + 1 e2e (notes-inbox.spec.mjs) all pass. tx-os typecheck is clean. Architect re-review: PASS. Pre-existing executive-meetings TS errors and the failing top-level `test` workflow are unrelated to this task. |
||
|
|
65c5a172d6 |
Task #402: Convert Notes into in-app messaging
Original task: turn personal Notes into in-app messaging — sender composes a
note (title/content/color), picks recipient(s), Send. Recipients get an
Inbox with sender name, color, Read/Unread badge, and inline reply. Sender
sees Sent Notes with per-recipient status. Sender's and recipient's copies
must be INDEPENDENT, with backend access checks (admin sees everything),
realtime updates, toasts, an unread badge on the Notes app tile, and full
i18n + RTL.
Two prior code-review rounds were addressed in this commit:
Round 1 (independence):
- Added immutable snapshot columns (title/content/color) on note_recipients.
- Dropped the FK from note_recipients.note_id and note_replies.note_id so
recipient threads survive the sender deleting their note.
- /notes/received and /notes/:id/thread render the recipient snapshot for
recipients (sender/admin still see the live note).
Round 2 (validation REJECT fixes):
- POST /notes/:id/reply: owner is now allowed to reply too. Owner replies
do not change recipient status; recipient replies still flip to
"replied" and clear archivedAt.
- Added GET /notes/:id as an alias of /notes/:id/thread (shared handler).
- Added OpenAPI ops for /notes/sent, /notes/received, /notes/{id},
/notes/{id}/send, /notes/{id}/read, /notes/{id}/archive,
/notes/{id}/reply, and ran orval codegen.
- use-notifications-socket.ts now shows bilingual toasts for note_received
and note_replied (suppressed during the socket warmup window).
- home.tsx renders an unread badge on the Notes app tile, fed by
useReceivedNotes(false) filtered to status === "unread"; refreshes
automatically on socket invalidation.
Tests: 7 backend tests in notes-share.test.mjs (independence,
archived-reply, owner-reply preserves recipient status, GET /notes/:id
alias, etc.) + the notes-inbox e2e all pass. tx-os typecheck is clean.
Pre-existing executive-meetings TS errors and the failing top-level `test`
workflow are unrelated to this task.
|
||
|
|
6352cbf844 |
Task #402: Convert Notes into in-app messaging (independence fix)
Original task: turn personal Notes into in-app messaging — sender composes a
note (title/content/color), picks recipient(s), Send. Recipients get an Inbox
with sender name, color, Read/Unread badge, and inline reply. Sender sees
Sent Notes with per-recipient status. Sender's and recipient's copies must
be INDEPENDENT, with proper backend access checks (admin sees everything),
realtime updates, and full i18n + RTL.
Initial implementation review FAILED because the recipient view still read
from the sender's notes table, so sender edits/deletes mutated recipient
copies. This commit completes the fix:
- Schema (lib/db/src/schema/notes.ts): added immutable snapshot columns
(title/content/color) on note_recipients; dropped the FK on
note_recipients.note_id and note_replies.note_id and made them plain
integers so recipient threads survive the sender deleting their note.
- Routes (artifacts/api-server/src/routes/notes.ts):
- /notes/received and /notes/:id/thread now serve the recipient snapshot
(sender/admin still see the live note).
- /notes/:id/reply derives the owner from note_recipients.senderUserId
so it works after sender deletion, clears archivedAt, and bumps
status to "replied".
- /notes/sent filters out null noteIds.
- Tests (artifacts/api-server/tests/notes-share.test.mjs): added
snapshot-independence test (sender edit + delete must not mutate
recipient copy; thread + reply still work after sender delete) and
archived-reply-clears-archivedAt test. All 5 backend tests pass; the
existing notes-inbox e2e still passes.
- Architect review: PASS (conditional only on the schema migration being
applied, which has been pushed via `pnpm --filter @workspace/db push`).
Pre-existing executive-meetings TS errors and the failing `test` workflow
are unrelated to this task.
|
||
|
|
c0d9e30b91 |
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: 2ca1a6d7-3aae-4de6-b02f-bcf6a2301173 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/G3ZvHke Replit-Helium-Checkpoint-Created: true |
||
|
|
da6025ba5f |
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: b4630b18-e3fb-4f6c-ad9e-1764f7a9f88a Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UggHvbd Replit-Helium-Checkpoint-Created: true |
||
|
|
e13231a9ef |
Update order confirmation modal title for clarity
Modify the order confirmation modal title in both Arabic and English locale files to a generic confirmation message, resolving a UI redundancy where the item name was displayed twice. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: d2a9850b-3afa-47aa-b54b-73a5f14c5ac6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UggHvbd Replit-Helium-Checkpoint-Created: true |
||
|
|
96147a347a |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() and refactored DELETE /orders/:id into a
shared authorizeAndDeleteOrder() helper so single and bulk paths use
the same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization to mirror GET /orders/incoming: a
receiver may only delete an unclaimed pending order or one they have
themselves claimed and is still active (received/preparing). Another
receiver's claimed order, and any terminal order, return 403 even at
the API layer. Code, comments and OpenAPI description now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas. Updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — both
useDeleteServiceOrder and useBulkDeleteServiceOrders are used by the UI.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Per-card checkbox visible at all times (RTL-safe leading edge).
- Per-card trash icon for single delete.
- Section-scoped Select-all (مين / unclaimed) with tri-state
all/some(indeterminate)/none and shadcn Checkbox.
- Sticky bottom bulk action bar shows selected count + Clear selection +
destructive Delete.
- Single AlertDialog used by both per-card and bulk paths, with
pluralized confirmation copy. Single delete calls DELETE /orders/:id;
multi delete calls POST /orders/bulk-delete; partial-failure surfaces
via toast.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization in both languages.
Tests:
- artifacts/api-server/tests/service-orders.test.mjs: receivers can
delete pending/received/preparing; receivers cannot delete terminal
(completed/cancelled); bulk-delete with mixed authorized / unknown /
dedup / unauthenticated cases.
- artifacts/tx-os/tests/order-incoming-delete.spec.mjs (new Playwright):
bulk-delete two unclaimed orders shrinks the list by 2; per-card
trash on a claimed order deletes one. Both pass.
Pre-existing executive-meetings PDF/font test failures are unrelated.
Follow-ups proposed: #398 (Undo for incoming-order deletes), #399
(safer notification handling in bulk-delete).
|
||
|
|
89de41e949 |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() and refactored DELETE /orders/:id into a
shared authorizeAndDeleteOrder() helper so single and bulk paths use
the same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization to mirror GET /orders/incoming: a
receiver may only delete an unclaimed pending order or one they have
themselves claimed and is still active (received/preparing). Another
receiver's claimed order, and any terminal order, return 403 even at
the API layer. Code, comments and OpenAPI description now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas. Updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — both
useDeleteServiceOrder and useBulkDeleteServiceOrders are used by the UI.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Per-card checkbox visible at all times (RTL-safe leading edge).
- Per-card trash icon for single delete.
- Section-scoped Select-all (مين / unclaimed) with tri-state
all/some(indeterminate)/none and shadcn Checkbox.
- Sticky bottom bulk action bar shows selected count + Clear selection +
destructive Delete.
- Single AlertDialog used by both per-card and bulk paths, with
pluralized confirmation copy. Single delete calls DELETE /orders/:id;
multi delete calls POST /orders/bulk-delete; partial-failure surfaces
via toast.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization in both languages.
Tests:
- artifacts/api-server/tests/service-orders.test.mjs: receivers can
delete pending/received/preparing; receivers cannot delete terminal
(completed/cancelled); bulk-delete with mixed authorized / unknown /
dedup / unauthenticated cases.
- artifacts/tx-os/tests/order-incoming-delete.spec.mjs (new Playwright):
bulk-delete two unclaimed orders shrinks the list by 2; per-card
trash on a claimed order deletes one. Both pass.
Pre-existing executive-meetings PDF/font test failures are unrelated.
Follow-ups proposed: #398 (Undo for incoming-order deletes), #399
(safer notification handling in bulk-delete).
|
||
|
|
4e657a2d4d |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() and refactored DELETE /orders/:id into a
shared authorizeAndDeleteOrder() helper so single and bulk paths use
the same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization to mirror GET /orders/incoming: a
receiver may only delete an unclaimed pending order or one they have
themselves claimed and is still active (received/preparing). Another
receiver's claimed order, and any terminal order, return 403 even at
the API layer. Code, comments and OpenAPI description now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas. Updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — both
useDeleteServiceOrder and useBulkDeleteServiceOrders are used by the UI.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Per-card checkbox visible at all times (RTL-safe leading edge).
- Per-card trash icon for single delete.
- Section-scoped Select-all (مين / unclaimed) with tri-state
all/some(indeterminate)/none and shadcn Checkbox.
- Sticky bottom bulk action bar shows selected count + Clear selection +
destructive Delete.
- Single AlertDialog used by both per-card and bulk paths, with
pluralized confirmation copy. Single delete calls DELETE /orders/:id;
multi delete calls POST /orders/bulk-delete; partial-failure surfaces
via toast.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization in both languages.
Tests:
- artifacts/api-server/tests/service-orders.test.mjs: receivers can
delete pending/received/preparing; receivers cannot delete terminal
(completed/cancelled); bulk-delete with mixed authorized / unknown /
dedup / unauthenticated cases.
- artifacts/tx-os/tests/order-incoming-delete.spec.mjs (new Playwright):
bulk-delete two unclaimed orders shrinks the list by 2; per-card
trash on a claimed order deletes one. Both pass.
Pre-existing executive-meetings PDF/font test failures are unrelated.
Follow-ups proposed: #398 (Undo for incoming-order deletes), #399
(safer notification handling in bulk-delete).
|
||
|
|
e085801f07 |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() and refactored DELETE /orders/:id into a
shared authorizeAndDeleteOrder() helper so single and bulk paths use
the same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization: receivers may only delete orders
currently in the incoming queue (pending/received/preparing). Terminal
orders remain the owner's cleanup responsibility. Code, comments and
OpenAPI description now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas. Updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — both
useDeleteServiceOrder and useBulkDeleteServiceOrders are used by the UI.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Per-card checkbox visible at all times (RTL-safe leading edge).
- Per-card trash icon for single delete.
- Section-scoped Select-all (مين / unclaimed) with tri-state
all/some(indeterminate)/none and shadcn Checkbox.
- Sticky bottom bulk action bar shows selected count + Clear selection +
destructive Delete.
- Single AlertDialog used by both per-card and bulk paths, with
pluralized confirmation copy. Single delete calls DELETE /orders/:id;
multi delete calls POST /orders/bulk-delete; partial-failure surfaces
via toast.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization in both languages.
Tests:
- artifacts/api-server/tests/service-orders.test.mjs: receivers can
delete pending/received/preparing; receivers cannot delete terminal
(completed/cancelled); bulk-delete with mixed authorized / unknown /
dedup / unauthenticated cases.
- artifacts/tx-os/tests/order-incoming-delete.spec.mjs (new Playwright):
bulk-delete two unclaimed orders shrinks the list by 2; per-card
trash on a claimed order deletes one. Both pass.
Pre-existing executive-meetings PDF/font test failures are unrelated.
Follow-ups proposed: #398 (Undo for incoming-order deletes), #399
(safer notification handling in bulk-delete).
|
||
|
|
d62e67af96 |
Task #397: Per-card and bulk select+delete on Incoming Orders
Backend (artifacts/api-server):
- Added hasReceivePermission() helper and refactored DELETE /orders/:id
into shared authorizeAndDeleteOrder() so single and bulk paths use the
same authorization rules.
- Added POST /orders/bulk-delete returning { deletedIds, failedIds } with
per-id authorization, dedup, and best-effort partial success.
- Tightened receiver authorization (per code review): receivers may only
delete pending/received/preparing orders; terminal (completed/cancelled)
orders remain the owner's cleanup responsibility. OpenAPI description
and the implementation now agree.
API spec (lib/api-spec/openapi.yaml):
- New /orders/bulk-delete operation with BulkDeleteServiceOrdersBody and
BulkDeleteServiceOrdersResponse schemas; updated DELETE /orders/{id}
description. Regenerated react-query hooks via codegen — useBulkDelete
ServiceOrders is now available.
UI (artifacts/tx-os/src/pages/orders-incoming.tsx):
- Selection mode toggle in the page header (RTL-safe).
- Per-card Checkbox plus a Select-all control.
- Sticky bottom action bar with destructive Delete and selected count.
- AlertDialog confirmation using pluralized i18n keys; toast surfaces
partial-failure results when some ids couldn't be deleted.
i18n: New incomingOrders keys in ar.json and en.json (select, selectAll,
clearSelection, selectedCount, delete, deleteConfirmTitle/Body, deleted,
deleteFailed, deletePartial) with pluralization.
Tests: Added two new tests in service-orders.test.mjs covering receiver
delete on incoming queue, receiver forbidden on terminal orders, and
bulk-delete with mixed authorized / unknown / dedup / unauthenticated
cases. Both pass. Pre-existing executive-meetings PDF/font test failures
are unrelated.
|
||
|
|
19fb83b389 |
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: 3a74b326-531d-4b9c-bfd3-03752532691d Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UggHvbd Replit-Helium-Checkpoint-Created: true |
||
|
|
c8c67881ec |
Saudi Coffee image: handleless finjan + brown coffee (Task #396)
Regenerated artifacts/tx-os/public/service-images/saudi-coffee.png
with a sharper prompt that explicitly names a handleless Saudi
finjan (small flared white porcelain cup with a thin gold patterned
rim, no handle) and a clear warm brown coffee color for both the
pour and the liquid in the cup. Strengthened the negative prompt
against handled cups, mugs, amber/orange liquid, and tea so the new
image no longer reads as "tea in a teacup".
Same path as before, so no DB change is needed — the existing
services.image_url ("service-images/saudi-coffee.png") on row id=2
keeps pointing at the new file.
No code, schema, locale, or test changes. Pre-existing `test`
workflow failure is unrelated.
|
||
|
|
6d896a9d09 |
Saudi Coffee image: handleless finjan + brown coffee (Task #396)
Regenerated artifacts/tx-os/public/service-images/saudi-coffee.png
with a sharper prompt that explicitly names a handleless Saudi
finjan (small flared white porcelain cup with a thin gold patterned
rim, no handle) and a clear warm brown coffee color for both the
pour and the liquid in the cup. Strengthened the negative prompt
against handled cups, mugs, amber/orange liquid, and tea so the new
image no longer reads as "tea in a teacup".
Same path as before, so no DB change is needed — the existing
services.image_url ("service-images/saudi-coffee.png") on row id=2
keeps pointing at the new file.
No code, schema, locale, or test changes. Pre-existing `test`
workflow failure is unrelated.
|
||
|
|
7c10df782d |
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: bc24a35e-0df8-4013-8518-ff5db8e998c9 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UggHvbd Replit-Helium-Checkpoint-Created: true |
||
|
|
7ba15355de |
Saudi Coffee image cleanup + shrink "Add notes" toggle (Task #395)
Two small follow-ups on Task #394 polish: 1. Regenerated artifacts/tx-os/public/service-images/saudi-coffee.png with a tighter prompt — just the brass dallah pouring brown coffee into a small white finjan, soft beige background, no dates/cardamom/garnish. Same path so no DB change needed. 2. Shrunk the "إضافة ملاحظات / Add notes" toggle in OrderServiceModal: replaced the chunky outline Button with a small ghost text-link style (text-xs, muted, pencil icon size 12, underline-on-hover). It now reads as a secondary affordance and sits below the service-preview row, well clear of the dialog's absolute close (X) at right-4 top-4 — no overlap in either AR or EN. Behavior unchanged: data-testid="order-notes-toggle" preserved, tapping still expands and focuses the textarea, hide link still collapses without losing typed text. Verification: - pnpm --filter @workspace/tx-os typecheck → passes. - The pre-existing `test` workflow failure is unrelated to this change. |
||
|
|
c2003b518a |
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: a8d49e87-cfd0-424a-912c-0de9c90ac604 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UggHvbd Replit-Helium-Checkpoint-Created: true |
||
|
|
dad3a909d3 |
Polish Services: Saudi Coffee rename + image, fix Black Coffee Arabic, hide order notes behind a button (Task #394)
Three small UX fixes on the Services screen: 1. Renamed service id=2 to "قهوة سعودي" / "Saudi Coffee" and swapped the thumbnail to a new clear brown image (small dallah pouring into a finjan with cardamom + a date) saved at public/service-images/saudi-coffee.png. The legacy arabic-coffee.png is left in place so any cached references keep loading. 2. Renamed service id=2450 Arabic name to "بلاك كوفي" (English "Black Coffee" unchanged). 3. OrderServiceModal now opens with the notes textarea collapsed and not mounted, so iPad/Safari no longer auto-pops the keyboard. A small outline "إضافة ملاحظات / Add notes" button (with pencil icon) expands it and focuses the textarea on demand. While expanded, a small "إخفاء / Hide" link collapses it again without clearing typed text. If initialNotes is non-empty when the dialog opens, it starts expanded. Submission behavior unchanged. Verified DB state after updates: id=2 name_ar=قهوة سعودي name_en=Saudi Coffee image_url=service-images/saudi-coffee.png id=2450 name_ar=بلاك كوفي name_en=Black Coffee image_url=service-images/black-coffee.png Added i18n keys services.addNotes / services.hideNotes to ar.json and en.json. Added data-testid="order-notes-toggle" and data-testid="order-notes-textarea" for future tests. `pnpm --filter @workspace/tx-os typecheck` passes. The pre-existing `test` workflow failure is unrelated to this change. |
||
|
|
69f20228b9 |
Polish Services: Saudi Coffee rename + image, fix Black Coffee Arabic, hide order notes behind a button (Task #394)
Three small UX fixes on the Services screen: 1. Renamed service id=2 to "قهوة سعودي" / "Saudi Coffee" and swapped the thumbnail to a new clear brown image (small dallah pouring into a finjan with cardamom + a date) saved at public/service-images/saudi-coffee.png. The legacy arabic-coffee.png is left in place so any cached references keep loading. 2. Renamed service id=2450 Arabic name to "بلاك كوفي" (English "Black Coffee" unchanged). 3. OrderServiceModal now opens with the notes textarea collapsed and not mounted, so iPad/Safari no longer auto-pops the keyboard. A small outline "إضافة ملاحظات / Add notes" button (with pencil icon) expands it and focuses the textarea on demand. While expanded, a small "إخفاء / Hide" link collapses it again without clearing typed text. If initialNotes is non-empty when the dialog opens, it starts expanded. Submission behavior unchanged. Verified DB state after updates: id=2 name_ar=قهوة سعودي name_en=Saudi Coffee image_url=service-images/saudi-coffee.png id=2450 name_ar=بلاك كوفي name_en=Black Coffee image_url=service-images/black-coffee.png Added i18n keys services.addNotes / services.hideNotes to ar.json and en.json. Added data-testid="order-notes-toggle" and data-testid="order-notes-textarea" for future tests. `pnpm --filter @workspace/tx-os typecheck` passes. The pre-existing `test` workflow failure is unrelated to this change. |
||
|
|
cc4c01e8ba |
Polish Services: Saudi Coffee rename + image, fix Black Coffee Arabic, hide order notes behind a button (Task #394)
Three small UX fixes on the Services screen: 1. Renamed service id=2 to "قهوة سعودي" / "Saudi Coffee" and swapped the thumbnail to a new clear brown image (small dallah pouring into a finjan with cardamom + a date) saved at public/service-images/saudi-coffee.png. The legacy arabic-coffee.png is left in place so any cached references keep loading. 2. Renamed service id=2450 Arabic name to "بلاك كوفي" (English "Black Coffee" unchanged). 3. OrderServiceModal now opens with the notes textarea collapsed and not mounted, so iPad/Safari no longer auto-pops the keyboard. A small outline "إضافة ملاحظات / Add notes" button (with pencil icon) expands it and focuses the textarea on demand. While expanded, a small "إخفاء / Hide" link collapses it again without clearing typed text. If initialNotes is non-empty when the dialog opens, it starts expanded. Submission behavior unchanged. Verified DB state after updates: id=2 name_ar=قهوة سعودي name_en=Saudi Coffee image_url=service-images/saudi-coffee.png id=2450 name_ar=بلاك كوفي name_en=Black Coffee image_url=service-images/black-coffee.png Added i18n keys services.addNotes / services.hideNotes to ar.json and en.json. Added data-testid="order-notes-toggle" and data-testid="order-notes-textarea" for future tests. `pnpm --filter @workspace/tx-os typecheck` passes. The pre-existing `test` workflow failure is unrelated to this change. |
||
|
|
7c2e4bc8f6 |
Polish Services: Saudi Coffee rename + image, fix Black Coffee Arabic, hide order notes behind a button (Task #394)
Three small UX fixes on the Services screen: 1. Renamed service id=2 to "قهوة سعودي" / "Saudi Coffee" and swapped the thumbnail to a new clear brown image (small dallah pouring into a finjan with cardamom + a date) saved at public/service-images/saudi-coffee.png. The legacy arabic-coffee.png is left in place so any cached references keep loading. 2. Renamed service id=2450 Arabic name to "بلاك كوفي" (English "Black Coffee" unchanged). 3. OrderServiceModal now opens with the notes textarea collapsed and not mounted, so iPad/Safari no longer auto-pops the keyboard. A small outline "إضافة ملاحظات / Add notes" button (with pencil icon) expands it and focuses the textarea on demand. While expanded, a small "إخفاء / Hide" link collapses it again without clearing typed text. If initialNotes is non-empty when the dialog opens, it starts expanded. Submission behavior unchanged. Verified DB state after updates: id=2 name_ar=قهوة سعودي name_en=Saudi Coffee image_url=service-images/saudi-coffee.png id=2450 name_ar=بلاك كوفي name_en=Black Coffee image_url=service-images/black-coffee.png Added i18n keys services.addNotes / services.hideNotes to ar.json and en.json. Added data-testid="order-notes-toggle" and data-testid="order-notes-textarea" for future tests. `pnpm --filter @workspace/tx-os typecheck` passes. The pre-existing `test` workflow failure is unrelated to this change. |
||
|
|
cec99c549f |
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: d07fba80-74ce-4e52-9683-881b7aa3ca71 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/UggHvbd Replit-Helium-Checkpoint-Created: true |