fix(ui): team detail layout fixes, thin scrollbar, and refresh buttons

- Remove card wrapper from team tabs to fix scroll-Y inside tabs
- Add overflow-y-hidden to TabsList to prevent vertical scrollbar leak
- Widen task detail dialog to match trace detail (95vw/max-w-4xl)
- Add max-h-[85vh] + overflow-y-auto to task detail dialog
- Add global thin scrollbar styling (scrollbar-width: thin)
- Add refresh buttons to Tasks and Delegations tabs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
viettranxandClaude Opus 4.6 committed 2026-03-05 12:49:04 +07:00
1 parent 851cdb2391
commit 013a3c021b
5 files changed
+39 -22

No files matched your search

+2
View File
@@ -128,6 +128,8 @@
@layer base {
* {
@apply border-border;
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
body {
@apply bg-background text-foreground;
@@ -17,7 +17,7 @@ interface TaskDetailDialogProps {
export function TaskDetailDialog({ task, onClose }: TaskDetailDialogProps) {
return (
<Dialog open onOpenChange={() => onClose()}>
<DialogContent className="sm:max-w-lg">
<DialogContent className="max-h-[85vh] w-[95vw] overflow-y-auto sm:max-w-4xl">
<DialogHeader>
<DialogTitle>Task Detail</DialogTitle>
</DialogHeader>
@@ -1,6 +1,7 @@
import { useState } from "react";
import { ArrowRightLeft } from "lucide-react";
import { ArrowRightLeft, RefreshCw } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { EmptyState } from "@/components/shared/empty-state";
import { DeferredSpinner } from "@/components/shared/loading-skeleton";
import { formatDate, formatDuration } from "@/lib/format";
@@ -14,12 +15,12 @@ interface TeamDelegationsTabProps {
}
export function TeamDelegationsTab({ teamId }: TeamDelegationsTabProps) {
const { records, loading } = useTeamDelegations(teamId);
const { records, loading, refresh } = useTeamDelegations(teamId);
const { getDelegation } = useDelegations();
const { getTrace } = useTraces();
const [selectedId, setSelectedId] = useState<string | null>(null);
if (loading) return <DeferredSpinner />;
if (loading && records.length === 0) return <DeferredSpinner />;
if (records.length === 0) {
return (
@@ -33,6 +34,12 @@ export function TeamDelegationsTab({ teamId }: TeamDelegationsTabProps) {
return (
<>
<div className="mb-4 flex justify-end">
<Button variant="ghost" size="sm" onClick={refresh} disabled={loading} className="gap-1.5">
<RefreshCw className={`h-3.5 w-3.5 ${loading ? "animate-spin" : ""}`} />
Refresh
</Button>
</div>
<div className="rounded-md border overflow-x-auto">
<table className="w-full min-w-[600px] text-sm">
<thead>
+2 -2
View File
@@ -105,9 +105,9 @@ export function TeamDetailPage({ teamId, onBack }: TeamDetailPageProps) {
</div>
{/* Tabs */}
<div className="max-w-4xl rounded-xl border bg-card p-3 shadow-sm sm:p-4">
<div className="max-w-4xl">
<Tabs defaultValue="members">
<TabsList className="w-full justify-start overflow-x-auto">
<TabsList className="w-full justify-start overflow-x-auto overflow-y-hidden">
<TabsTrigger value="members">Members</TabsTrigger>
<TabsTrigger value="tasks">Tasks</TabsTrigger>
<TabsTrigger value="delegations">Delegations</TabsTrigger>
+24 -16
View File
@@ -1,4 +1,6 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { Button } from "@/components/ui/button";
import { RefreshCw } from "lucide-react";
import type { TeamTaskData } from "@/types/team";
import { TaskList } from "./task-sections";
@@ -11,24 +13,30 @@ export function TeamTasksTab({ teamId, getTeamTasks }: TeamTasksTabProps) {
const [tasks, setTasks] = useState<TeamTaskData[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
(async () => {
setLoading(true);
try {
const res = await getTeamTasks(teamId);
if (!cancelled) setTasks(res.tasks ?? []);
} catch {
// ignore
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => { cancelled = true; };
const load = useCallback(async () => {
setLoading(true);
try {
const res = await getTeamTasks(teamId);
setTasks(res.tasks ?? []);
} catch {
// ignore
} finally {
setLoading(false);
}
}, [teamId, getTeamTasks]);
useEffect(() => {
load();
}, [load]);
return (
<div className="space-y-6">
<div className="space-y-4">
<div className="flex justify-end">
<Button variant="ghost" size="sm" onClick={load} disabled={loading} className="gap-1.5">
<RefreshCw className={`h-3.5 w-3.5 ${loading ? "animate-spin" : ""}`} />
Refresh
</Button>
</div>
<TaskList tasks={tasks} loading={loading} />
</div>
);