mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 18:15:34 +00:00
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:
1 parent
851cdb2391
commit
013a3c021b
5 files changed
+39
-22
No files matched your search
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user