feat: add edit button to collection detail page header (#113)

* feat: add edit button to collection detail page header

Wire up the existing EditCollectionModal to the collection detail page
with a pencil icon button in the header actions bar. Owner-only,
responsive (icon-only on mobile), refreshes page data after saves.

Closes IDEA-494

* fix: navigate to new slug after collection rename in edit modal

When a collection is renamed, the backend regenerates the slug. The
onupdated callback now receives the updated Collection object, so the
collection page can detect a slug change and navigate to the new URL
instead of 404ing on the old slug.

Addresses PR review feedback from #113.

* fix: refresh collectionStore after edit modal update

Ensures the sidebar reflects updated collection name/slug/icon
immediately after editing, matching the settings page behavior.

Addresses P2 review feedback from #113.
This commit is contained in:
xarmian
2026-04-14 14:37:45 -04:00
committed by GitHub
parent aeda627320
commit b620b7e5cc
2 changed files with 59 additions and 3 deletions
@@ -10,7 +10,7 @@
open: boolean;
collection: Collection;
wsSlug: string;
onupdated: () => void;
onupdated: (updated?: Collection) => void;
onclose: () => void;
}
@@ -336,9 +336,9 @@
data.migrations = migrations;
}
await api.collections.update(wsSlug, collection.slug, data);
const updated = await api.collections.update(wsSlug, collection.slug, data);
toastStore.show(`Updated ${name.trim()}`, 'success');
onupdated();
onupdated(updated);
} catch (err) {
error = err instanceof Error ? err.message : 'Failed to update collection';
} finally {
@@ -14,7 +14,9 @@
import { syncService } from '$lib/services/sync.svelte';
import { toastStore } from '$lib/stores/toast.svelte';
import ShareDialog from '$lib/components/ShareDialog.svelte';
import EditCollectionModal from '$lib/components/collections/EditCollectionModal.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { collectionStore } from '$lib/stores/collections.svelte';
type ViewMode = 'list' | 'board' | 'table';
@@ -38,6 +40,7 @@
let saveViewInput = $state<HTMLInputElement>();
let shareDialogOpen = $state(false);
let editCollectionOpen = $state(false);
let workspaceMembers = $state<{ user_id: string; role: string }[]>([]);
let wsSlug = $derived(page.params.workspace ?? '');
@@ -803,6 +806,17 @@
<QuickActionsMenu actions={quickActions} {collection} scope="collection" />
{/if}
{#if isOwner}
<button
class="edit-collection-btn"
onclick={() => { editCollectionOpen = true; }}
title="Edit collection"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
<span class="edit-collection-label">Edit</span>
</button>
{/if}
{#if isOwner}
<button
class="share-btn-header"
@@ -966,6 +980,23 @@
/>
{/if}
{#if isOwner && collection}
<EditCollectionModal
bind:open={editCollectionOpen}
{collection}
{wsSlug}
onupdated={(updated) => {
collectionStore.loadCollections(wsSlug);
if (updated && updated.slug !== collSlug) {
goto(`/${username}/${wsSlug}/${updated.slug}`);
} else {
loadCollection(wsSlug, collSlug, showArchived);
}
}}
onclose={() => { editCollectionOpen = false; }}
/>
{/if}
<style>
.collection-page {
max-width: var(--content-max-width);
@@ -1374,6 +1405,10 @@
.share-btn-label {
display: none;
}
.edit-collection-label {
display: none;
}
}
/* Share button */
@@ -1400,4 +1435,25 @@
.share-icon {
flex-shrink: 0;
}
/* Edit collection button */
.edit-collection-btn {
display: flex;
align-items: center;
gap: var(--space-1);
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--space-1) var(--space-3);
cursor: pointer;
font-size: 0.82em;
color: var(--text-secondary);
white-space: nowrap;
transition: border-color 0.15s, color 0.15s;
}
.edit-collection-btn:hover {
color: var(--text-primary);
border-color: var(--text-muted);
}
</style>