fix(ui): collapse session lists and scope selection controls to panels
This commit is contained in:
@@ -256,7 +256,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
const contextLocked = interactionBusy || creatingSession || administrationBusy
|
||||
|| preprocessingQuery.data?.state === "running";
|
||||
const [adminNavigationValue, setAdminNavigationValue] = useState<string[]>([]);
|
||||
const [activeOpen, setActiveOpen] = useState(true);
|
||||
const [activeOpen, setActiveOpen] = useState(false);
|
||||
const [archiveOpen, setArchiveOpen] = useState(false);
|
||||
const [renameTarget, setRenameTarget] = useState<SessionSummary | null>(null);
|
||||
const [deleteTargets, setDeleteTargets] = useState<SessionSummary[]>([]);
|
||||
@@ -326,8 +326,39 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
});
|
||||
}
|
||||
|
||||
function toggleAllSessions(selected: boolean) {
|
||||
setSelectedSessionIds(selected ? new Set(sessions.map((session) => session.id)) : new Set());
|
||||
function sessionSelectionControls(list: SessionSummary[]) {
|
||||
if (list.length === 0) return null;
|
||||
const selected = list.filter((session) => selectedSessionIds.has(session.id));
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2 px-2 pb-2">
|
||||
<label className="flex cursor-pointer items-center gap-2 text-xs font-medium text-muted-foreground hover:text-foreground">
|
||||
<Checkbox
|
||||
checked={selected.length === list.length}
|
||||
indeterminate={selected.length > 0 && selected.length < list.length}
|
||||
onCheckedChange={(checked) => setSelectedSessionIds((current) => {
|
||||
const next = new Set(current);
|
||||
for (const session of list) {
|
||||
if (checked) next.add(session.id);
|
||||
else next.delete(session.id);
|
||||
}
|
||||
return next;
|
||||
})}
|
||||
/>
|
||||
<span>{t("Select all")}</span>
|
||||
</label>
|
||||
{selected.length > 0 && (
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="xs"
|
||||
aria-label={t("Delete {count} selected sessions", { count: selected.length })}
|
||||
onClick={() => requestDelete(selected)}
|
||||
>
|
||||
<Trash2 />
|
||||
{t("Delete ({count})", { count: selected.length })}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function handleSessionScopeTabKeyDown(
|
||||
@@ -1124,42 +1155,11 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
className="flex min-h-0 flex-1 flex-col"
|
||||
>
|
||||
{canReadAllSessions && showingAllSessions && (
|
||||
<p className="mx-4 mb-2 mt-2 text-xs font-medium text-muted-foreground">
|
||||
<p className="sr-only">
|
||||
{t("Administrator view: all sessions")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* L1 — rail title */}
|
||||
<div className="px-4 pb-1.5 pt-1">
|
||||
<span className="text-sm font-semibold text-foreground">
|
||||
{t("Sessions")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between px-4 pb-2">
|
||||
<label className="flex cursor-pointer items-center gap-2 text-xs font-medium text-muted-foreground hover:text-foreground">
|
||||
<Checkbox
|
||||
checked={allSessionsSelected}
|
||||
aria-label={t("Select all sessions")}
|
||||
disabled={sessions.length === 0}
|
||||
onCheckedChange={(selected) => toggleAllSessions(selected === true)}
|
||||
/>
|
||||
<span>{t("Select all")}</span>
|
||||
</label>
|
||||
{selectedSessions.length > 0 && (
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="xs"
|
||||
aria-label={t("Delete {count} selected sessions", { count: selectedSessions.length })}
|
||||
onClick={() => {
|
||||
requestDelete(selectedSessions);
|
||||
}}
|
||||
>
|
||||
<Trash2 />
|
||||
{t("Delete ({count})", { count: selectedSessions.length })}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<Accordion.Root multiple
|
||||
value={[...(activeOpen ? ["active"] : []), ...(archiveOpen ? ["archive"] : [])]}
|
||||
onValueChange={value => { setActiveOpen(value.includes("active")); setArchiveOpen(value.includes("archive")); }}
|
||||
@@ -1172,6 +1172,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
</Accordion.Trigger>
|
||||
</Accordion.Header>
|
||||
<Accordion.Panel className="thot-session-accordion__panel" tabIndex={0}>
|
||||
{sessionSelectionControls(activeList)}
|
||||
<div className="flex flex-col gap-3 pb-2">
|
||||
{groups.map((g) => (
|
||||
<div key={g}>
|
||||
@@ -1233,6 +1234,7 @@ export function AppShell({ canLogout }: AppShellProps) {
|
||||
</Accordion.Trigger>
|
||||
</Accordion.Header>
|
||||
<Accordion.Panel className="thot-session-accordion__panel" tabIndex={0}>
|
||||
{sessionSelectionControls(archivedList)}
|
||||
<NavSessions
|
||||
sessions={archivedList}
|
||||
activeSessionId={activeSessionId}
|
||||
|
||||
Reference in New Issue
Block a user