Unify sidebar fields: all editable fields use full-width clickable blocks
- Status and Severity now match CTI style (full-width button, hover bg, no chevron) - Remove 'Change routing' hint text from CTI block - Replace Assignee dropdown with clickable block that opens a modal picker with avatars - Add Assignee modal consistent with Status/Severity modal pattern Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,7 @@ export default function TicketDetail() {
|
||||
const [pendingCTI, setPendingCTI] = useState({ categoryId: '', typeId: '', itemId: '' })
|
||||
const [editingStatus, setEditingStatus] = useState(false)
|
||||
const [editingSeverity, setEditingSeverity] = useState(false)
|
||||
const [editingAssignee, setEditingAssignee] = useState(false)
|
||||
|
||||
const isAdmin = authUser?.role === 'ADMIN'
|
||||
|
||||
@@ -492,33 +493,28 @@ export default function TicketDetail() {
|
||||
<p className="text-xs font-semibold text-gray-500 uppercase tracking-wide">Ticket Summary</p>
|
||||
</div>
|
||||
|
||||
{/* Status + Severity */}
|
||||
<div className="px-4 py-3 space-y-3">
|
||||
<SidebarField label="Status">
|
||||
{/* Status */}
|
||||
<button
|
||||
onClick={() => setEditingStatus(true)}
|
||||
className="flex items-center gap-1.5 hover:opacity-75 transition-opacity"
|
||||
className="w-full px-4 py-3 text-left hover:bg-gray-800/50 transition-colors"
|
||||
>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1.5">Status</p>
|
||||
<StatusBadge status={ticket.status} />
|
||||
<ChevronDown size={12} className="text-gray-500" />
|
||||
</button>
|
||||
</SidebarField>
|
||||
|
||||
<SidebarField label="Severity">
|
||||
{/* Severity */}
|
||||
<button
|
||||
onClick={() => setEditingSeverity(true)}
|
||||
className="flex items-center gap-1.5 hover:opacity-75 transition-opacity"
|
||||
className="w-full px-4 py-3 text-left hover:bg-gray-800/50 transition-colors"
|
||||
>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1.5">Severity</p>
|
||||
<SeverityBadge severity={ticket.severity} />
|
||||
<ChevronDown size={12} className="text-gray-500" />
|
||||
</button>
|
||||
</SidebarField>
|
||||
</div>
|
||||
|
||||
{/* CTI — one clickable unit */}
|
||||
<button
|
||||
onClick={startReroute}
|
||||
className="w-full px-4 py-3 text-left space-y-3 hover:bg-gray-800/50 transition-colors group"
|
||||
className="w-full px-4 py-3 text-left space-y-3 hover:bg-gray-800/50 transition-colors"
|
||||
>
|
||||
<div>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1">Category</p>
|
||||
@@ -532,7 +528,6 @@ export default function TicketDetail() {
|
||||
<p className="text-xs font-medium text-gray-500 mb-1">Issue</p>
|
||||
<p className="text-sm text-gray-300">{ticket.item.name}</p>
|
||||
</div>
|
||||
<p className="text-xs text-blue-500 group-hover:text-blue-400 transition-colors">Change routing</p>
|
||||
</button>
|
||||
|
||||
{/* Dates */}
|
||||
@@ -550,33 +545,29 @@ export default function TicketDetail() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* People */}
|
||||
<div className="px-4 py-3 space-y-3">
|
||||
<SidebarField label="Assignee">
|
||||
<select
|
||||
value={ticket.assigneeId ?? ''}
|
||||
onChange={(e) => patch({ assigneeId: e.target.value || null })}
|
||||
className={selectClass}
|
||||
{/* Assignee */}
|
||||
<button
|
||||
onClick={() => setEditingAssignee(true)}
|
||||
className="w-full px-4 py-3 text-left hover:bg-gray-800/50 transition-colors"
|
||||
>
|
||||
<option value="">Unassigned</option>
|
||||
{agentUsers.map((u) => (
|
||||
<option key={u.id} value={u.id}>{u.displayName}</option>
|
||||
))}
|
||||
</select>
|
||||
{ticket.assignee && (
|
||||
<div className="flex items-center gap-1.5 mt-1.5">
|
||||
<p className="text-xs font-medium text-gray-500 mb-1.5">Assignee</p>
|
||||
{ticket.assignee ? (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Avatar name={ticket.assignee.displayName} size="sm" />
|
||||
<span className="text-xs text-gray-400">{ticket.assignee.displayName}</span>
|
||||
<span className="text-sm text-gray-300">{ticket.assignee.displayName}</span>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500">Unassigned</p>
|
||||
)}
|
||||
</SidebarField>
|
||||
</button>
|
||||
|
||||
<SidebarField label="Requester">
|
||||
{/* Requester */}
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-xs font-medium text-gray-500 mb-1.5">Requester</p>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Avatar name={ticket.createdBy.displayName} size="sm" />
|
||||
<span className="text-xs text-gray-300">{ticket.createdBy.displayName}</span>
|
||||
<span className="text-sm text-gray-300">{ticket.createdBy.displayName}</span>
|
||||
</div>
|
||||
</SidebarField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -653,6 +644,45 @@ export default function TicketDetail() {
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
{editingAssignee && (
|
||||
<Modal title="Change Assignee" onClose={() => setEditingAssignee(false)}>
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
onClick={async () => {
|
||||
await patch({ assigneeId: null })
|
||||
setEditingAssignee(false)
|
||||
}}
|
||||
className={`w-full flex items-center gap-3 px-4 py-3 rounded-lg border transition-colors ${
|
||||
!ticket.assigneeId
|
||||
? 'border-blue-500/50 bg-blue-500/10'
|
||||
: 'border-gray-700 hover:bg-gray-800'
|
||||
}`}
|
||||
>
|
||||
<span className="text-sm text-gray-400">Unassigned</span>
|
||||
{!ticket.assigneeId && <Check size={14} className="ml-auto text-blue-400" />}
|
||||
</button>
|
||||
{agentUsers.map((u) => (
|
||||
<button
|
||||
key={u.id}
|
||||
onClick={async () => {
|
||||
await patch({ assigneeId: u.id })
|
||||
setEditingAssignee(false)
|
||||
}}
|
||||
className={`w-full flex items-center gap-3 px-4 py-3 rounded-lg border transition-colors ${
|
||||
ticket.assigneeId === u.id
|
||||
? 'border-blue-500/50 bg-blue-500/10'
|
||||
: 'border-gray-700 hover:bg-gray-800'
|
||||
}`}
|
||||
>
|
||||
<Avatar name={u.displayName} size="sm" />
|
||||
<span className="text-sm text-gray-300">{u.displayName}</span>
|
||||
{ticket.assigneeId === u.id && <Check size={14} className="ml-auto text-blue-400" />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
{reroutingCTI && (
|
||||
<Modal title="Change Routing" onClose={() => setReroutingCTI(false)} size="lg">
|
||||
<div className="space-y-5">
|
||||
|
||||
Reference in New Issue
Block a user