@@ -13,6 +13,13 @@ import {
1313 DialogHeader ,
1414 DialogTitle ,
1515} from '@uipath/apollo-wind/components/ui/dialog'
16+ import {
17+ Sheet ,
18+ SheetContent ,
19+ SheetFooter ,
20+ SheetHeader ,
21+ SheetTitle ,
22+ } from '@uipath/apollo-wind/components/ui/sheet'
1623import { Input } from '@uipath/apollo-wind/components/ui/input'
1724import { Label } from '@uipath/apollo-wind/components/ui/label'
1825import {
@@ -36,19 +43,17 @@ import {
3643interface Props {
3744 taskId : number
3845 folderId : number
46+ /** Opened from Manage Tasks — gates the Assign/Reassign actions. */
47+ isManage : boolean
3948 onClose : ( ) => void
4049 /** Called after a mutation so the parent list can refresh. */
4150 onChanged : ( ) => void
4251}
4352
4453type Sub = 'assign' | 'reassign' | 'complete' | null
4554
46- /**
47- * Detail modal for one task (via `Tasks.getById`) plus the lifecycle actions.
48- * Assign / reassign / complete open small inline forms; unassign runs inline.
49- * All mutations use the task-attached methods on the fetched task.
50- */
51- export function TaskDetail ( { taskId, folderId, onClose, onChanged } : Props ) {
55+ /** Task detail panel (Tasks.getById) with lifecycle actions via task-attached methods. */
56+ export function TaskDetail ( { taskId, folderId, isManage, onClose, onChanged } : Props ) {
5257 const { task, loading, error, reload } = useTask ( taskId , folderId )
5358 const [ sub , setSub ] = useState < Sub > ( null )
5459 const [ busy , setBusy ] = useState ( false )
@@ -79,12 +84,16 @@ export function TaskDetail({ taskId, folderId, onClose, onChanged }: Props) {
7984
8085 return (
8186 < >
82- < Dialog open onOpenChange = { ( open ) => ! open && onClose ( ) } >
83- < DialogContent className = "sm:max-w-lg" >
84- < DialogHeader >
85- < DialogTitle className = "truncate" > { task ?. title ?? `Task #${ taskId } ` } </ DialogTitle >
86- </ DialogHeader >
87+ < Sheet open onOpenChange = { ( open ) => ! open && onClose ( ) } >
88+ < SheetContent side = "right" className = "flex w-full flex-col sm:max-w-lg" >
89+ < SheetHeader >
90+ < SheetTitle className = "min-w-0 break-words pr-8" >
91+ { task ?. title ?? `Task #${ taskId } ` }
92+ </ SheetTitle >
93+ </ SheetHeader >
8794
95+ { /* Scrollable body so the footer stays pinned regardless of content height. */ }
96+ < div className = "-mx-6 min-h-0 flex-1 overflow-y-auto px-6" >
8897 { loading ? (
8998 < div className = "flex justify-center py-6" >
9099 < Spinner label = "Loading task…" showLabel />
@@ -94,8 +103,8 @@ export function TaskDetail({ taskId, folderId, onClose, onChanged }: Props) {
94103 < AlertDescription > { error } </ AlertDescription >
95104 </ Alert >
96105 ) : task ? (
97- < div className = "space-y-5 text-sm" >
98- < div className = "flex flex-wrap items-center gap-2 " >
106+ < div className = "space-y-4 text-sm" >
107+ < div className = "flex flex-wrap items-center gap-1.5 " >
99108 < Badge variant = { statusBadge ( task . status ) . variant } >
100109 { statusBadge ( task . status ) . label }
101110 </ Badge >
@@ -115,27 +124,23 @@ export function TaskDetail({ taskId, folderId, onClose, onChanged }: Props) {
115124 label = "Assignee"
116125 value = { task . assignedToUser ?. displayName ?? 'Unassigned' }
117126 />
127+ { task . taskSource ?. sourceName && (
128+ < Field label = "Source" value = { task . taskSource . sourceName } />
129+ ) }
118130 < Field label = "Created" value = { formatDateTime ( task . createdTime ) } />
119131 { task . completedTime && (
120132 < Field label = "Completed" value = { formatDateTime ( task . completedTime ) } />
121133 ) }
134+ { task . completedByUser ?. displayName && (
135+ < Field label = "Completed by" value = { task . completedByUser . displayName } />
136+ ) }
137+ { ( task . actionLabel || task . action ) && (
138+ < Field label = "Action" value = { task . actionLabel ?? task . action ?? '' } />
139+ ) }
122140 { task . externalTag && < Field label = "External tag" value = { task . externalTag } mono /> }
123141 < Field label = "Key" value = { task . key } mono />
124142 </ dl >
125143
126- { task . tags && task . tags . length > 0 && (
127- < Section title = "Tags" >
128- < div className = "flex flex-wrap gap-1.5" >
129- { task . tags . map ( ( t ) => (
130- < Badge key = { t . name } variant = "secondary" >
131- { t . displayName || t . name }
132- { t . displayValue ? `: ${ t . displayValue } ` : '' }
133- </ Badge >
134- ) ) }
135- </ div >
136- </ Section >
137- ) }
138-
139144 { task . data && Object . keys ( task . data ) . length > 0 && (
140145 < Section title = "Data" >
141146 < dl className = "grid grid-cols-[10rem_1fr] gap-x-4 gap-y-1 rounded-lg border bg-muted/40 p-3" >
@@ -176,11 +181,15 @@ export function TaskDetail({ taskId, folderId, onClose, onChanged }: Props) {
176181 ) }
177182 </ div >
178183 ) : null }
184+ </ div >
179185
180186 { task && ! isCompleted && (
181- < DialogFooter className = "flex-wrap" >
182- { ! hasAssignee && < Button onClick = { ( ) => setSub ( 'assign' ) } > Assign</ Button > }
183- { hasAssignee && (
187+ < SheetFooter className = "flex-wrap gap-2" >
188+ { /* Assign/Reassign to others is a Manage Tasks action only. */ }
189+ { isManage && ! hasAssignee && (
190+ < Button onClick = { ( ) => setSub ( 'assign' ) } > Assign</ Button >
191+ ) }
192+ { isManage && hasAssignee && (
184193 < Button variant = "outline" onClick = { ( ) => setSub ( 'reassign' ) } >
185194 Reassign
186195 </ Button >
@@ -193,10 +202,10 @@ export function TaskDetail({ taskId, folderId, onClose, onChanged }: Props) {
193202 < Button variant = "outline" onClick = { ( ) => setSub ( 'complete' ) } >
194203 Complete
195204 </ Button >
196- </ DialogFooter >
205+ </ SheetFooter >
197206 ) }
198- </ DialogContent >
199- </ Dialog >
207+ </ SheetContent >
208+ </ Sheet >
200209
201210 { task && ( sub === 'assign' || sub === 'reassign' ) && (
202211 < AssignForm
@@ -269,9 +278,8 @@ function AssignForm({
269278 const id = Number ( userId )
270279 if ( ! Number . isFinite ( id ) || id <= 0 ) return
271280 const selected = users . find ( ( u ) => u . id === id )
272- // Assigning to a directory group needs an assignment criteria so the
273- // backend knows how to distribute the task across the group's members; a
274- // direct (single-user) assignment omits it.
281+ // Directory-group assignment needs a criteria to distribute across members;
282+ // single-user assignment omits it.
275283 const opts : TaskAssignOptions =
276284 selected ?. type === TaskUserType . DirectoryGroup
277285 ? { userId : id , assignmentCriteria : TaskAssignmentCriteria . AllUsers }
0 commit comments