From 04f2e59c92522b34e87f5220ac41207e3f830cf7 Mon Sep 17 00:00:00 2001 From: Developer Date: Wed, 17 Jun 2026 16:18:49 +0000 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20slice=206b=20=E2=80=94=20User?= =?UTF-8?q?s=20compose=20dialog=20+=209=20icons=20(finish=20Users)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Web UI rework. Completes the Users migration (6a directory + 6b compose): - Compose dialog off @mui: shadcn Dialog family + Input (subject) + Textarea (html body) + Separator + Label; IconButton -> Button ghost - 9 @mui/icons-material -> lucide-react: Close->X, AttachFile->Paperclip, FormatBold->Bold, FormatItalic->Italic, Link->Link, FormatListBulleted->List, MailOutlined->Mail, Send->Send, DeleteOutlined->Trash2 - Rich-text compose parity: markup insertion, attachments (FormData), queue-status polling, send via useSendUserMessage - UsersPage.impl.tsx now has ZERO @mui imports Gate: build + lint + test green (20 files / 46 tests). --- frontend/src/pages/UsersPage.impl.tsx | 207 ++++++++++-------- .../src/pages/__tests__/UsersPage.test.tsx | 44 ++++ 2 files changed, 158 insertions(+), 93 deletions(-) diff --git a/frontend/src/pages/UsersPage.impl.tsx b/frontend/src/pages/UsersPage.impl.tsx index 31e8f4c..cc962cd 100644 --- a/frontend/src/pages/UsersPage.impl.tsx +++ b/frontend/src/pages/UsersPage.impl.tsx @@ -1,30 +1,32 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useSearchParams } from "react-router-dom"; import type { ChangeEvent } from "react"; -// Slice 6b owns the compose-dialog STRUCTURE + these icons: Dialog family, -// TextField, Divider, IconButton (below). Slice 6a migrated the directory -// surface + drawer AND the compose content's shared leaf components. -import CloseIcon from "@mui/icons-material/Close"; -import AttachFileIcon from "@mui/icons-material/AttachFile"; -import FormatBoldIcon from "@mui/icons-material/FormatBold"; -import FormatItalicIcon from "@mui/icons-material/FormatItalic"; -import LinkIcon from "@mui/icons-material/Link"; -import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted"; -import MailOutlinedIcon from "@mui/icons-material/MailOutlined"; -import SendIcon from "@mui/icons-material/Send"; -import DeleteOutlinedIcon from "@mui/icons-material/DeleteOutlined"; +// Slice 6b: compose dialog (shadcn Dialog family) + lucide icons. The file is +// now fully @mui-free (6a migrated the directory surface, drawer, and the +// compose content's shared leaf components). +import { + X, + Paperclip, + Bold, + Italic, + Link, + List, + Mail, + Send, + Trash2, +} from "lucide-react"; import { - // 6b-owned compose-dialog components only (the contract's narrow @mui set). - // Everything else — Alert/Box/Button/Chip/Paper/Stack/Typography/Tooltip/ - // LinearProgress/useMediaQuery — was migrated to shadcn/Tailwind in 6a. Dialog, - DialogActions, DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, DialogTitle, - Divider, - IconButton, - TextField, -} from "@mui/material"; +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Separator } from "@/components/ui/separator"; +import { Label } from "@/components/ui/label"; // Slice 6a directory surface + drawer primitives. import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; @@ -456,7 +458,7 @@ export function UsersPage() { disabled={!selectedDeliverableRows.length} onClick={openCompose} > - + Message selected Clear selection - setSearch(event.target.value)} - sx={{ minWidth: { xs: "100%", sm: 320 } }} + className="w-full sm:w-80" /> @@ -671,7 +672,7 @@ export function UsersPage() { aria-label="Close user details" onClick={() => setSearchParams({})} > - + Close @@ -743,7 +744,7 @@ export function UsersPage() { - +

This panel is read-only for now. Communication actions will be @@ -756,26 +757,29 @@ export function UsersPage() {

{ + if (!open) { + closeCompose(); + } + }} > - - Message selected users - - - - - {sendUserMessage.isPending ? ( - - ) : null} - -
+ + + Message selected users + + Compose a message to the selected deliverable users. + + + {sendUserMessage.isPending ? ( + + ) : null} +
{sendUserMessage.isError ? ( @@ -832,67 +836,84 @@ export function UsersPage() { ))}
- setSubject(event.target.value)} - /> +
+ + setSubject(event.target.value)} + /> +
- insertMarkup("", "")} aria-label="Bold" > - - + + Bold - insertMarkup("", "")} aria-label="Italic" > - - + + Italic - - - + + + Link - insertMarkup("
  • ", "
")} aria-label="Bullet list" > - -
+ +
Bullet list
- setHtmlBody(event.target.value)} - helperText="Formatting is sent as HTML; a plain-text fallback is generated automatically." - /> +
+ +