Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 57 additions & 58 deletions frontend/src/pages/Album/AlbumDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,75 +230,74 @@ export const AlbumDetail = () => {

return (
<div className="flex h-full flex-col">
{/* Header */}
<div className="mb-6">
<div className="mb-4 flex items-center gap-3">
<Button variant="ghost" size="icon" onClick={handleBack}>
<ArrowLeft className="h-5 w-5" />
</Button>
<div className="flex-1">
<h1 className="text-2xl font-bold">{album.name}</h1>
{album.description && (
<p className="text-muted-foreground text-sm">
{album.description}
</p>
)}
</div>
</div>
{/* Header — same shape as the AI Tagging collection page */}
<div className="my-6 flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<Button
variant="outline"
onClick={handleBack}
className="flex cursor-pointer items-center gap-2"
>
<ArrowLeft className="h-4 w-4" />
Back
</Button>

<div className="flex items-center justify-between">
<p className="text-muted-foreground text-sm">
<div className="flex flex-wrap items-center justify-end gap-2 self-end sm:self-auto">
<p className="text-muted-foreground mr-1 text-sm">
{images.length} {images.length === 1 ? 'photo' : 'photos'}
{selectedImages.size > 0 && ` • ${selectedImages.size} selected`}
</p>

<div className="flex items-center gap-2">
{isSelectionMode ? (
<>
{isSelectionMode ? (
<>
<Button
variant="outline"
onClick={() => {
setIsSelectionMode(false);
setSelectedImages(new Set());
}}
className="cursor-pointer"
>
Cancel
</Button>
<Button
variant="destructive"
onClick={handleRemoveSelected}
disabled={selectedImages.size === 0}
className="cursor-pointer"
>
<Trash2 className="mr-2 h-4 w-4" />
Remove Selected
</Button>
</>
) : (
<>
{images.length > 0 && (
<Button
variant="outline"
size="sm"
onClick={() => {
setIsSelectionMode(false);
setSelectedImages(new Set());
}}
>
Cancel
</Button>
<Button
variant="destructive"
size="sm"
onClick={handleRemoveSelected}
disabled={selectedImages.size === 0}
onClick={() => setIsSelectionMode(true)}
className="cursor-pointer"
>
<Trash2 className="mr-2 h-4 w-4" />
Remove Selected
Select Images
</Button>
</>
) : (
<>
{images.length > 0 && (
<Button
variant="outline"
size="sm"
onClick={() => setIsSelectionMode(true)}
>
Select Images
</Button>
)}
<Button
size="sm"
onClick={() => setIsAddImagesDialogOpen(true)}
>
<Plus className="mr-2 h-4 w-4" />
Add Images
</Button>
</>
)}
</div>
)}
<Button
onClick={() => setIsAddImagesDialogOpen(true)}
className="cursor-pointer"
>
<Plus className="mr-2 h-4 w-4" />
Add Images
</Button>
</>
)}
</div>
</div>

<div className="mb-6">
<h1 className="text-2xl font-bold">{album.name}</h1>
{album.description && (
<p className="text-muted-foreground text-sm">{album.description}</p>
)}
</div>

{/* Images Grid */}
<div className="flex-1 overflow-y-auto pt-2">
{isLoadingContent ? (
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/pages/__tests__/AlbumDetail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ describe('AlbumDetail', () => {
expect(store.getState().loader.loading).toBe(false);
}, 30000);

// The header mirrors the AI Tagging collection page: a labelled Back button
// on its own row, with the album name as the heading underneath.
test('renders a labelled back button that returns to the albums list', async () => {
const user = userEvent.setup();
renderDetail();

expect(
await screen.findByRole('heading', { name: 'Trip' }),
).toBeInTheDocument();

await user.click(screen.getByRole('button', { name: 'Back' }));

expect(await screen.findByText('Albums list')).toBeInTheDocument();
}, 30000);

// The cover is whatever image comes first in the album, so there is nothing
// to pick and no per-image menu left to pick it from.
test('offers no per-image cover menu', async () => {
Expand Down