Skip to content

Commit 5cc080e

Browse files
committed
fix: remove collapsible behavior from ArchivedWorkspaces
1 parent b15a72e commit 5cc080e

File tree

1 file changed

+113
-124
lines changed

1 file changed

+113
-124
lines changed

src/browser/components/ArchivedWorkspaces.tsx

Lines changed: 113 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import React from "react";
22
import { cn } from "@/common/lib/utils";
33
import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
44
import { useWorkspaceContext } from "@/browser/contexts/WorkspaceContext";
5-
import { Trash2, ChevronDown, ChevronRight, Search } from "lucide-react";
5+
import { Trash2, Search } from "lucide-react";
66
import { ArchiveIcon, ArchiveRestoreIcon } from "./icons/ArchiveIcon";
77
import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip";
88
import { RuntimeBadge } from "./RuntimeBadge";
9-
import { usePersistedState } from "@/browser/hooks/usePersistedState";
109

1110
interface ArchivedWorkspacesProps {
1211
projectPath: string;
@@ -70,7 +69,6 @@ export const ArchivedWorkspaces: React.FC<ArchivedWorkspacesProps> = ({
7069
onWorkspacesChanged,
7170
}) => {
7271
const { unarchiveWorkspace, removeWorkspace, setSelectedWorkspace } = useWorkspaceContext();
73-
const [expanded, setExpanded] = usePersistedState("archivedWorkspacesExpanded", true);
7472
const [searchQuery, setSearchQuery] = React.useState("");
7573
const [processingIds, setProcessingIds] = React.useState<Set<string>>(new Set());
7674
const [deleteConfirmId, setDeleteConfirmId] = React.useState<string | null>(null);
@@ -136,140 +134,131 @@ export const ArchivedWorkspaces: React.FC<ArchivedWorkspacesProps> = ({
136134

137135
return (
138136
<div className="border-border rounded-lg border">
139-
<button
140-
onClick={() => setExpanded((prev) => !prev)}
141-
className="hover:bg-hover flex w-full items-center gap-2 rounded-t-lg px-4 py-3 text-left transition-colors"
142-
>
137+
{/* Header - not collapsible */}
138+
<div className="flex items-center gap-2 px-4 py-3">
143139
<ArchiveIcon className="text-muted h-4 w-4" />
144140
<span className="text-foreground flex-1 font-medium">
145141
Archived Workspaces ({workspaces.length})
146142
</span>
147-
{expanded ? (
148-
<ChevronDown className="text-muted h-4 w-4" />
149-
) : (
150-
<ChevronRight className="text-muted h-4 w-4" />
151-
)}
152-
</button>
143+
</div>
153144

154-
{expanded && (
155-
<div className="border-border border-t">
156-
{/* Search input */}
157-
{workspaces.length > 3 && (
158-
<div className="border-border border-b px-4 py-2">
159-
<div className="relative">
160-
<Search className="text-muted pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" />
161-
<input
162-
type="text"
163-
placeholder="Search archived workspaces..."
164-
value={searchQuery}
165-
onChange={(e) => setSearchQuery(e.target.value)}
166-
className="bg-bg-dark placeholder:text-muted text-foreground w-full rounded border border-transparent py-1.5 pl-8 pr-3 text-sm focus:border-border-light focus:outline-none"
167-
/>
168-
</div>
145+
<div className="border-border border-t">
146+
{/* Search input */}
147+
{workspaces.length > 3 && (
148+
<div className="border-border border-b px-4 py-2">
149+
<div className="relative">
150+
<Search className="text-muted pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2" />
151+
<input
152+
type="text"
153+
placeholder="Search archived workspaces..."
154+
value={searchQuery}
155+
onChange={(e) => setSearchQuery(e.target.value)}
156+
className="bg-bg-dark placeholder:text-muted text-foreground w-full rounded border border-transparent py-1.5 pl-8 pr-3 text-sm focus:border-border-light focus:outline-none"
157+
/>
169158
</div>
170-
)}
159+
</div>
160+
)}
171161

172-
{/* Timeline grouped list - no inner scroll, parent handles overflow */}
173-
<div>
174-
{filteredWorkspaces.length === 0 ? (
175-
<div className="text-muted px-4 py-6 text-center text-sm">
176-
No workspaces match "{searchQuery}"
177-
</div>
178-
) : (
179-
Array.from(groupedWorkspaces.entries()).map(([period, periodWorkspaces]) => (
180-
<div key={period}>
181-
{/* Period header */}
182-
<div className="bg-bg-dark text-muted px-4 py-1.5 text-xs font-medium">
183-
{period}
184-
</div>
185-
{/* Workspaces in this period */}
186-
{periodWorkspaces.map((workspace) => {
187-
const isProcessing = processingIds.has(workspace.id);
188-
const isDeleting = deleteConfirmId === workspace.id;
189-
const displayTitle = workspace.title ?? workspace.name;
162+
{/* Timeline grouped list */}
163+
<div>
164+
{filteredWorkspaces.length === 0 ? (
165+
<div className="text-muted px-4 py-6 text-center text-sm">
166+
No workspaces match "{searchQuery}"
167+
</div>
168+
) : (
169+
Array.from(groupedWorkspaces.entries()).map(([period, periodWorkspaces]) => (
170+
<div key={period}>
171+
{/* Period header */}
172+
<div className="bg-bg-dark text-muted px-4 py-1.5 text-xs font-medium">
173+
{period}
174+
</div>
175+
{/* Workspaces in this period */}
176+
{periodWorkspaces.map((workspace) => {
177+
const isProcessing = processingIds.has(workspace.id);
178+
const isDeleting = deleteConfirmId === workspace.id;
179+
const displayTitle = workspace.title ?? workspace.name;
190180

191-
return (
192-
<div
193-
key={workspace.id}
194-
className={cn(
195-
"border-border flex items-center gap-3 border-b px-4 py-2.5 last:border-b-0",
196-
isProcessing && "opacity-50"
197-
)}
198-
>
199-
<RuntimeBadge runtimeConfig={workspace.runtimeConfig} isWorking={false} />
200-
<div className="min-w-0 flex-1">
201-
<div className="text-foreground truncate text-sm font-medium">
202-
{displayTitle}
203-
</div>
204-
{workspace.archivedAt && (
205-
<div className="text-muted text-xs">
206-
{new Date(workspace.archivedAt).toLocaleString(undefined, {
207-
month: "short",
208-
day: "numeric",
209-
hour: "numeric",
210-
minute: "2-digit",
211-
})}
212-
</div>
213-
)}
181+
return (
182+
<div
183+
key={workspace.id}
184+
className={cn(
185+
"border-border flex items-center gap-3 border-b px-4 py-2.5 last:border-b-0",
186+
isProcessing && "opacity-50"
187+
)}
188+
>
189+
<RuntimeBadge runtimeConfig={workspace.runtimeConfig} isWorking={false} />
190+
<div className="min-w-0 flex-1">
191+
<div className="text-foreground truncate text-sm font-medium">
192+
{displayTitle}
214193
</div>
215-
216-
{isDeleting ? (
217-
<div className="flex items-center gap-2">
218-
<span className="text-muted text-xs">Delete?</span>
219-
<button
220-
onClick={() => void handleDelete(workspace.id)}
221-
disabled={isProcessing}
222-
className="rounded bg-red-600 px-2 py-1 text-xs text-white hover:bg-red-700 disabled:opacity-50"
223-
>
224-
Yes
225-
</button>
226-
<button
227-
onClick={() => setDeleteConfirmId(null)}
228-
disabled={isProcessing}
229-
className="text-muted hover:text-foreground text-xs disabled:opacity-50"
230-
>
231-
No
232-
</button>
233-
</div>
234-
) : (
235-
<div className="flex items-center gap-1">
236-
<Tooltip>
237-
<TooltipTrigger asChild>
238-
<button
239-
onClick={() => void handleUnarchive(workspace.id)}
240-
disabled={isProcessing}
241-
className="text-muted hover:text-foreground rounded p-1.5 transition-colors hover:bg-white/10 disabled:opacity-50"
242-
aria-label={`Restore workspace ${displayTitle}`}
243-
>
244-
<ArchiveRestoreIcon className="h-4 w-4" />
245-
</button>
246-
</TooltipTrigger>
247-
<TooltipContent>Restore to sidebar</TooltipContent>
248-
</Tooltip>
249-
<Tooltip>
250-
<TooltipTrigger asChild>
251-
<button
252-
onClick={() => setDeleteConfirmId(workspace.id)}
253-
disabled={isProcessing}
254-
className="text-muted rounded p-1.5 transition-colors hover:bg-white/10 hover:text-red-400 disabled:opacity-50"
255-
aria-label={`Delete workspace ${displayTitle}`}
256-
>
257-
<Trash2 className="h-4 w-4" />
258-
</button>
259-
</TooltipTrigger>
260-
<TooltipContent>Delete permanently</TooltipContent>
261-
</Tooltip>
194+
{workspace.archivedAt && (
195+
<div className="text-muted text-xs">
196+
{new Date(workspace.archivedAt).toLocaleString(undefined, {
197+
month: "short",
198+
day: "numeric",
199+
hour: "numeric",
200+
minute: "2-digit",
201+
})}
262202
</div>
263203
)}
264204
</div>
265-
);
266-
})}
267-
</div>
268-
))
269-
)}
270-
</div>
205+
206+
{isDeleting ? (
207+
<div className="flex items-center gap-2">
208+
<span className="text-muted text-xs">Delete?</span>
209+
<button
210+
onClick={() => void handleDelete(workspace.id)}
211+
disabled={isProcessing}
212+
className="rounded bg-red-600 px-2 py-1 text-xs text-white hover:bg-red-700 disabled:opacity-50"
213+
>
214+
Yes
215+
</button>
216+
<button
217+
onClick={() => setDeleteConfirmId(null)}
218+
disabled={isProcessing}
219+
className="text-muted hover:text-foreground text-xs disabled:opacity-50"
220+
>
221+
No
222+
</button>
223+
</div>
224+
) : (
225+
<div className="flex items-center gap-1">
226+
<Tooltip>
227+
<TooltipTrigger asChild>
228+
<button
229+
onClick={() => void handleUnarchive(workspace.id)}
230+
disabled={isProcessing}
231+
className="text-muted hover:text-foreground rounded p-1.5 transition-colors hover:bg-white/10 disabled:opacity-50"
232+
aria-label={`Restore workspace ${displayTitle}`}
233+
>
234+
<ArchiveRestoreIcon className="h-4 w-4" />
235+
</button>
236+
</TooltipTrigger>
237+
<TooltipContent>Restore to sidebar</TooltipContent>
238+
</Tooltip>
239+
<Tooltip>
240+
<TooltipTrigger asChild>
241+
<button
242+
onClick={() => setDeleteConfirmId(workspace.id)}
243+
disabled={isProcessing}
244+
className="text-muted rounded p-1.5 transition-colors hover:bg-white/10 hover:text-red-400 disabled:opacity-50"
245+
aria-label={`Delete workspace ${displayTitle}`}
246+
>
247+
<Trash2 className="h-4 w-4" />
248+
</button>
249+
</TooltipTrigger>
250+
<TooltipContent>Delete permanently</TooltipContent>
251+
</Tooltip>
252+
</div>
253+
)}
254+
</div>
255+
);
256+
})}
257+
</div>
258+
))
259+
)}
271260
</div>
272-
)}
261+
</div>
273262
</div>
274263
);
275264
};

0 commit comments

Comments
 (0)