Skip to content

Commit 237e5c2

Browse files
committed
feat(ui): enhance task detail dialog with collapsible data sections and improved layout (#39)
1 parent 1528e1c commit 237e5c2

File tree

1 file changed

+136
-71
lines changed
  • src/server/ui/app/session/[sessionId]/task

1 file changed

+136
-71
lines changed

src/server/ui/app/session/[sessionId]/task/page.tsx

Lines changed: 136 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
PaginationNext,
2929
PaginationPrevious,
3030
} from "@/components/ui/pagination";
31-
import { Loader2, RefreshCw, ArrowLeft } from "lucide-react";
31+
import { Loader2, RefreshCw, ArrowLeft, ChevronDown, ChevronUp } from "lucide-react";
3232
import { getTasks, getSessionConfigs } from "@/api/models/space";
3333
import { Task } from "@/types";
3434
import ReactCodeMirror from "@uiw/react-codemirror";
@@ -54,6 +54,7 @@ export default function TasksPage() {
5454

5555
const [detailDialogOpen, setDetailDialogOpen] = useState(false);
5656
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
57+
const [isDataExpanded, setIsDataExpanded] = useState(false);
5758

5859
const totalPages = Math.ceil(allTasks.length / PAGE_SIZE);
5960
const paginatedTasks = allTasks.slice(
@@ -351,19 +352,29 @@ export default function TasksPage() {
351352
</div>
352353

353354
{/* Task Detail Dialog */}
354-
<Dialog open={detailDialogOpen} onOpenChange={setDetailDialogOpen}>
355+
<Dialog
356+
open={detailDialogOpen}
357+
onOpenChange={(open) => {
358+
setDetailDialogOpen(open);
359+
if (!open) {
360+
setIsDataExpanded(false);
361+
}
362+
}}
363+
>
355364
<DialogContent className="max-w-4xl max-h-[90vh] flex flex-col">
356-
<DialogHeader>
357-
<DialogTitle>{t("taskDetail") || "Task Detail"}</DialogTitle>
365+
<DialogHeader className="flex-shrink-0">
366+
<DialogTitle>
367+
{t("taskDetail") || "Task Detail"}
368+
</DialogTitle>
358369
</DialogHeader>
359370
{selectedTask && (
360-
<div className="rounded-md border bg-card p-6 overflow-y-auto flex-1">
371+
<div className="flex-1 overflow-y-auto min-h-0">
361372
{/* Task header */}
362-
<div className="border-b pb-4">
363-
<h3 className="text-xl font-semibold mb-2 font-mono">
364-
{selectedTask.id}
365-
</h3>
366-
<div className="flex items-center gap-2">
373+
<div className="border-b pb-4 space-y-3 mb-4">
374+
<div className="flex items-center gap-2 flex-wrap">
375+
<span className="inline-flex items-center justify-center rounded-md bg-secondary border border-border px-2 py-1 text-xs font-medium">
376+
{t("order") || "Order"}: {selectedTask.order}
377+
</span>
367378
<span
368379
className={`inline-flex items-center rounded-md border px-2 py-1 text-xs font-medium ${getStatusColor(
369380
selectedTask.status
@@ -384,75 +395,129 @@ export default function TasksPage() {
384395
</div>
385396
</div>
386397

387-
{/* Task details in grid */}
388-
<div className="grid grid-cols-2 gap-4 mt-6">
389-
<div>
390-
<p className="text-sm font-medium text-muted-foreground mb-1">
391-
{t("sessionId") || "Session ID"}
392-
</p>
393-
<p className="text-sm bg-muted px-2 py-1 rounded font-mono">
394-
{selectedTask.session_id}
395-
</p>
396-
</div>
397-
<div>
398-
<p className="text-sm font-medium text-muted-foreground mb-1">
399-
{t("projectId") || "Project ID"}
400-
</p>
401-
<p className="text-sm bg-muted px-2 py-1 rounded font-mono">
402-
{selectedTask.project_id}
403-
</p>
404-
</div>
405-
<div>
406-
<p className="text-sm font-medium text-muted-foreground mb-1">
407-
{t("order") || "Order"}
408-
</p>
409-
<p className="text-sm bg-muted px-2 py-1 rounded">
410-
{selectedTask.order}
411-
</p>
412-
</div>
413-
<div>
414-
<p className="text-sm font-medium text-muted-foreground mb-1">
415-
{t("createdAt")}
416-
</p>
417-
<p className="text-sm bg-muted px-2 py-1 rounded">
418-
{new Date(selectedTask.created_at).toLocaleString()}
419-
</p>
420-
</div>
421-
<div>
422-
<p className="text-sm font-medium text-muted-foreground mb-1">
423-
{t("updatedAt")}
424-
</p>
425-
<p className="text-sm bg-muted px-2 py-1 rounded">
426-
{new Date(selectedTask.updated_at).toLocaleString()}
427-
</p>
398+
{/* Task details - reordered */}
399+
<div className="space-y-4">
400+
{/* 1. Task Description */}
401+
{selectedTask.data?.task_description != null && (
402+
<div>
403+
<p className="text-sm font-medium text-muted-foreground mb-1">
404+
Task Description
405+
</p>
406+
<p className="text-sm bg-muted px-2 py-1 rounded whitespace-pre-wrap">
407+
{typeof selectedTask.data.task_description === "string"
408+
? selectedTask.data.task_description
409+
: JSON.stringify(selectedTask.data.task_description, null, 2)}
410+
</p>
411+
</div>
412+
)}
413+
414+
{/* 2. Progresses */}
415+
{selectedTask.data?.progresses != null &&
416+
Array.isArray(selectedTask.data.progresses) &&
417+
selectedTask.data.progresses.length > 0 && (
418+
<div>
419+
<p className="text-sm font-medium text-muted-foreground mb-2">
420+
Progresses
421+
</p>
422+
<div className="space-y-1.5">
423+
{selectedTask.data.progresses.map(
424+
(progress: unknown, index: number) => (
425+
<div
426+
key={index}
427+
className="text-sm bg-muted px-3 py-1.5 rounded border-l-2 border-primary/30"
428+
>
429+
{typeof progress === "string" ? progress : String(progress)}
430+
</div>
431+
)
432+
)}
433+
</div>
434+
</div>
435+
)}
436+
437+
{/* 3. User Preferences */}
438+
{selectedTask.data?.user_preferences != null &&
439+
Array.isArray(selectedTask.data.user_preferences) &&
440+
selectedTask.data.user_preferences.length > 0 && (
441+
<div>
442+
<p className="text-sm font-medium text-muted-foreground mb-2">
443+
User Preferences
444+
</p>
445+
<div className="space-y-1">
446+
{selectedTask.data.user_preferences.map(
447+
(pref: unknown, index: number) => (
448+
<div
449+
key={index}
450+
className="text-sm bg-muted px-3 py-2 rounded border-l-2 border-primary/20"
451+
>
452+
{typeof pref === "string" ? pref : String(pref)}
453+
</div>
454+
)
455+
)}
456+
</div>
457+
</div>
458+
)}
459+
460+
{/* 4. Created At & Updated At */}
461+
<div className="grid grid-cols-2 gap-4">
462+
<div>
463+
<p className="text-sm font-medium text-muted-foreground mb-1">
464+
{t("createdAt")}
465+
</p>
466+
<p className="text-sm bg-muted px-2 py-1 rounded">
467+
{new Date(selectedTask.created_at).toLocaleString()}
468+
</p>
469+
</div>
470+
<div>
471+
<p className="text-sm font-medium text-muted-foreground mb-1">
472+
{t("updatedAt")}
473+
</p>
474+
<p className="text-sm bg-muted px-2 py-1 rounded">
475+
{new Date(selectedTask.updated_at).toLocaleString()}
476+
</p>
477+
</div>
428478
</div>
429-
</div>
430479

431-
{/* Task data */}
432-
<div className="border-t pt-6 mt-6">
433-
<p className="text-sm font-medium text-muted-foreground mb-3">
434-
{t("taskData") || "Task Data"}
435-
</p>
436-
<div className="border rounded-md overflow-hidden">
437-
<ReactCodeMirror
438-
value={JSON.stringify(selectedTask.data, null, 2)}
439-
height="400px"
440-
theme={resolvedTheme === "dark" ? okaidia : "light"}
441-
extensions={[json(), EditorView.lineWrapping]}
442-
editable={false}
443-
basicSetup={{
444-
lineNumbers: true,
445-
foldGutter: true,
446-
}}
447-
/>
480+
{/* 5. Task data - Collapsible */}
481+
<div className="border-t pt-4 mt-4">
482+
<button
483+
onClick={() => setIsDataExpanded(!isDataExpanded)}
484+
className="flex items-center gap-2 w-full text-left mb-3 hover:opacity-80 transition-opacity"
485+
>
486+
<p className="text-sm font-medium text-muted-foreground">
487+
{t("taskData") || "Task Data"}
488+
</p>
489+
{isDataExpanded ? (
490+
<ChevronUp className="h-4 w-4 text-muted-foreground" />
491+
) : (
492+
<ChevronDown className="h-4 w-4 text-muted-foreground" />
493+
)}
494+
</button>
495+
{isDataExpanded && (
496+
<div className="border rounded-md overflow-hidden">
497+
<ReactCodeMirror
498+
value={JSON.stringify(selectedTask.data, null, 2)}
499+
height="400px"
500+
theme={resolvedTheme === "dark" ? okaidia : "light"}
501+
extensions={[json(), EditorView.lineWrapping]}
502+
editable={false}
503+
basicSetup={{
504+
lineNumbers: true,
505+
foldGutter: true,
506+
}}
507+
/>
508+
</div>
509+
)}
448510
</div>
449511
</div>
450512
</div>
451513
)}
452-
<DialogFooter>
514+
<DialogFooter className="flex-shrink-0">
453515
<Button
454516
variant="outline"
455-
onClick={() => setDetailDialogOpen(false)}
517+
onClick={() => {
518+
setDetailDialogOpen(false);
519+
setIsDataExpanded(false);
520+
}}
456521
>
457522
{t("close")}
458523
</Button>

0 commit comments

Comments
 (0)