From bdabcb2ea434e98b0fe11f7ccc45f9fcf2886341 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Thu, 16 Apr 2026 23:05:32 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20consistent=20UI=20across=20all=20list?= =?UTF-8?q?=20pages=20=E2=80=94=20PhoneActionCell,=20custom=20pills,=20eye?= =?UTF-8?q?=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PhoneActionCell: kebab always visible (SMS + WhatsApp), Call removed from menu, phone number always brand-colored regardless of telephony state - LeadTable: replaced actions kebab column with eye icon (first column) for view activity, phone column now uses PhoneActionCell - Worklist: React Aria Tabs replaced with custom pill buttons matching All Leads pattern (bg-brand-solid on selected), search lifted to call-desk.tsx header - Appointments: underline tabs replaced with custom pills, phone in patient cell uses PhoneActionCell, group/row added to rows - Patients: removed redundant HamburgerMenu column, group/row on rows - Call Desk: search input in header row, cleaned up duplicate imports Co-Authored-By: Claude Opus 4.6 (1M context) --- .../call-desk/phone-action-cell.tsx | 24 +++----- src/components/call-desk/worklist-panel.tsx | 49 ++++++++-------- src/components/leads/lead-table.tsx | 49 +++++++--------- src/pages/appointments-v2.tsx | 26 ++++++--- src/pages/call-desk.tsx | 24 ++++++-- src/pages/patients.tsx | 58 ++----------------- 6 files changed, 94 insertions(+), 136 deletions(-) diff --git a/src/components/call-desk/phone-action-cell.tsx b/src/components/call-desk/phone-action-cell.tsx index 5642e17..e5177e3 100644 --- a/src/components/call-desk/phone-action-cell.tsx +++ b/src/components/call-desk/phone-action-cell.tsx @@ -74,43 +74,33 @@ export const PhoneActionCell = ({ phoneNumber, displayNumber, leadId: _leadId, o {/* Clickable phone number — calls directly */} - {/* Kebab menu trigger — desktop */} + {/* Kebab menu trigger — SMS + WhatsApp */} - {/* Context menu */} + {/* Context menu — SMS + WhatsApp only (dial is the primary click) */} {menuOpen && (
- + ))}
{/* Missed-call sub-tabs removed per QA feedback — the Missed tab diff --git a/src/components/leads/lead-table.tsx b/src/components/leads/lead-table.tsx index 00f2b0b..5610790 100644 --- a/src/components/leads/lead-table.tsx +++ b/src/components/leads/lead-table.tsx @@ -1,17 +1,14 @@ -import type { FC } from 'react'; import { useMemo, useState } from 'react'; import { TableBody as AriaTableBody } from 'react-aria-components'; import type { SortDescriptor, Selection } from 'react-aria-components'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faEllipsisVertical } from '@fortawesome/pro-duotone-svg-icons'; - -const DotsVertical: FC<{ className?: string }> = ({ className }) => ; +import { faEye } from '@fortawesome/pro-duotone-svg-icons'; import { Badge } from '@/components/base/badges/badges'; -import { Button } from '@/components/base/buttons/button'; import { Table } from '@/components/application/table/table'; import { LeadStatusBadge } from '@/components/shared/status-badge'; import { SourceTag } from '@/components/shared/source-tag'; import { AgeIndicator } from '@/components/shared/age-indicator'; +import { PhoneActionCell } from '@/components/call-desk/phone-action-cell'; import { formatPhone, formatShortDate } from '@/lib/format'; import { cx } from '@/utils/cx'; import type { Lead } from '@/types/entities'; @@ -97,6 +94,7 @@ export const LeadTable = ({ }, [leads, expandedDupId]); const allColumns = [ + { id: 'view', label: '', allowsSorting: false, defaultWidth: 40 }, { id: 'phone', label: 'Phone', allowsSorting: true, defaultWidth: 150 }, { id: 'name', label: 'Name', allowsSorting: true, defaultWidth: 160 }, { id: 'email', label: 'Email', allowsSorting: false, defaultWidth: 180 }, @@ -109,11 +107,10 @@ export const LeadTable = ({ { id: 'createdAt', label: 'Age', allowsSorting: true, defaultWidth: 80 }, { id: 'spamScore', label: 'Spam', allowsSorting: true, defaultWidth: 70 }, { id: 'dups', label: 'Dups', allowsSorting: false, defaultWidth: 60 }, - { id: 'actions', label: '', allowsSorting: false, defaultWidth: 50 }, ]; const columns = visibleColumns - ? allColumns.filter(c => visibleColumns.has(c.id) || c.id === 'actions') + ? allColumns.filter(c => visibleColumns.has(c.id) || c.id === 'view') : allColumns; return ( @@ -145,6 +142,7 @@ export const LeadTable = ({ const firstName = lead.contactName?.firstName ?? ''; const lastName = lead.contactName?.lastName ?? ''; const name = `${firstName} ${lastName}`.trim() || '\u2014'; + const phoneRaw = lead.contactPhone?.[0]?.number ?? ''; const phone = lead.contactPhone?.[0] ? formatPhone(lead.contactPhone[0]) : '\u2014'; @@ -158,6 +156,7 @@ export const LeadTable = ({ id={row.id} className="bg-warning-primary" > + {phone} @@ -191,17 +190,6 @@ export const LeadTable = ({ - - -
- - -
-
); } @@ -219,12 +207,26 @@ export const LeadTable = ({ key={row.id} id={row.id} className={cx( + 'group/row', isSpamRow && !isSelected && 'bg-warning-primary', isSelected && 'bg-brand-primary', )} > + + + {isCol('phone') && - {phone} + {phoneRaw ? ( + + ) : ( + {'\u2014'} + )} } {isCol('name') && {name} @@ -308,15 +310,6 @@ export const LeadTable = ({ 0 )} } - - + ))} + } /> @@ -611,7 +621,7 @@ export const AppointmentsPageV2 = () => { return ( {/* Eye icon — first column */} @@ -628,7 +638,7 @@ export const AppointmentsPageV2 = () => {

{name}

- {phone &&

{formatPhone({ number: phone, callingCode: '+91' })}

} + {phone && }
diff --git a/src/pages/call-desk.tsx b/src/pages/call-desk.tsx index 90af3ec..6519848 100644 --- a/src/pages/call-desk.tsx +++ b/src/pages/call-desk.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faSidebarFlip, faSidebar, faPhone, faXmark, faDeleteLeft, faFlask } from '@fortawesome/pro-duotone-svg-icons'; +import { faSidebarFlip, faSidebar, faPhone, faXmark, faDeleteLeft, faFlask, faMagnifyingGlass, faCircleInfo } from '@fortawesome/pro-duotone-svg-icons'; import { useSetAtom } from 'jotai'; import { sipCallStateAtom, sipCallerNumberAtom, sipCallUcidAtom, sipCallDurationAtom } from '@/state/sip-state'; import { useAuth } from '@/providers/auth-provider'; @@ -12,12 +12,14 @@ import type { WorklistSelection } from '@/components/call-desk/worklist-panel'; import { ContextPanel } from '@/components/call-desk/context-panel'; import type { ContextPanelSubject } from '@/components/call-desk/context-panel'; import { ActiveCallCard } from '@/components/call-desk/active-call-card'; +import { Input } from '@/components/base/input/input'; +import { faIcon } from '@/lib/icon-wrapper'; import { apiClient } from '@/lib/api-client'; import { notify } from '@/lib/toast'; import { cx } from '@/utils/cx'; -import { FontAwesomeIcon as FAIcon } from '@fortawesome/react-fontawesome'; -import { faCircleInfo } from '@fortawesome/pro-duotone-svg-icons'; + +const SearchLg = faIcon(faMagnifyingGlass); export const CallDeskPage = () => { const { user } = useAuth(); @@ -32,6 +34,7 @@ export const CallDeskPage = () => { const [diallerOpen, setDiallerOpen] = useState(false); const [dialNumber, setDialNumber] = useState(''); const [dialling, setDialling] = useState(false); + const [search, setSearch] = useState(''); // DEV: simulate incoming call const setSimCallState = useSetAtom(sipCallStateAtom); @@ -174,11 +177,23 @@ export const CallDeskPage = () => {

Call Desk

{user.name} - +
+ {!isInCall && ( +
+ +
+ )} {import.meta.env.DEV && (!isInCall ? (
diff --git a/src/pages/patients.tsx b/src/pages/patients.tsx index bcf16b1..8b38140 100644 --- a/src/pages/patients.tsx +++ b/src/pages/patients.tsx @@ -1,7 +1,7 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; +import { useMemo, useState } from 'react'; // useNavigate removed — row click opens profile panel import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faUser, faMagnifyingGlass, faCommentDots, faMessageDots, faEllipsisVertical, faSidebarFlip, faSidebar } from '@fortawesome/pro-duotone-svg-icons'; +import { faUser, faMagnifyingGlass, faSidebarFlip, faSidebar } from '@fortawesome/pro-duotone-svg-icons'; import { faIcon } from '@/lib/icon-wrapper'; const SearchLg = faIcon(faMagnifyingGlass); @@ -55,49 +55,6 @@ const getPatientEmail = (patient: Patient): string => { return patient.emails?.primaryEmail ?? ''; }; -const HamburgerMenu = ({ phone }: { phone: string }) => { - const [open, setOpen] = useState(false); - const ref = useRef(null); - - useEffect(() => { - if (!open) return; - const handler = (e: MouseEvent) => { - if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); - }; - document.addEventListener('mousedown', handler); - return () => document.removeEventListener('mousedown', handler); - }, [open]); - - return ( -
- - {open && ( -
- - -
- )} -
- ); -}; export const PatientsPage = () => { const { patients, loading } = useData(); @@ -178,7 +135,6 @@ export const PatientsPage = () => { - {(patient) => { @@ -192,10 +148,10 @@ export const PatientsPage = () => { : '?'; return ( - { @@ -255,12 +211,6 @@ export const PatientsPage = () => {
- {/* Hamburger — SMS + WhatsApp */} - - {phone ? ( - - ) : null} -
); }}