Skip to content

Commit 2a9430e

Browse files
committed
Fix series dropdown on book edit page
When manually entering a series name, the dropdown now shows a "Create new series" option when no matching series exists.
1 parent 8135c14 commit 2a9430e

3 files changed

Lines changed: 66 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.9.15] - 2026-06-14
11+
12+
### Fixed
13+
- **Series Dropdown on Edit Page** - Fixed "create new series" option not appearing when typing a new series name on book edit page
14+
1015
## [2.9.14] - 2026-06-14
1116

1217
### Fixed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bookshelf",
3-
"version": "2.9.14",
3+
"version": "2.9.15",
44
"description": "Personal book library management application",
55
"private": true,
66
"type": "module",

src/routes/books/[id]/edit/+page.svelte

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,18 @@
139139
let seriesSearch = $state('');
140140
let seriesBookNum = $state('');
141141
let showSeriesDropdown = $state(false);
142+
let creatingSeries = $state(false);
143+
let availableSeries = $state(options.series);
142144
let filteredSeries = $derived(
143-
options.series.filter((s: any) =>
145+
availableSeries.filter((s: any) =>
144146
s.title.toLowerCase().includes(seriesSearch.toLowerCase()) &&
145147
!selectedSeries.some(ss => ss.id === s.id)
146148
).slice(0, 10)
147149
);
150+
let canCreateSeries = $derived(
151+
seriesSearch.trim().length > 0 &&
152+
!availableSeries.some((s: any) => s.title.toLowerCase() === seriesSearch.trim().toLowerCase())
153+
);
148154
149155
const tabs = [
150156
{ id: 'basic', label: 'Basic', icon: Info },
@@ -176,6 +182,30 @@
176182
showSeriesDropdown = false;
177183
}
178184
185+
async function createNewSeries() {
186+
const title = seriesSearch.trim();
187+
if (!title) return;
188+
189+
creatingSeries = true;
190+
try {
191+
const res = await fetch('/api/series', {
192+
method: 'POST',
193+
headers: { 'Content-Type': 'application/json' },
194+
body: JSON.stringify({ title })
195+
});
196+
197+
if (res.ok) {
198+
const newSeries = await res.json();
199+
availableSeries = [...availableSeries, { id: newSeries.id, title: newSeries.title }];
200+
addSeries({ id: newSeries.id, title: newSeries.title });
201+
}
202+
} catch (e) {
203+
console.error('Failed to create series:', e);
204+
} finally {
205+
creatingSeries = false;
206+
}
207+
}
208+
179209
function removeSeries(id: number) {
180210
selectedSeries = selectedSeries.filter(s => s.id !== id);
181211
}
@@ -1013,10 +1043,11 @@
10131043
placeholder="Search series..."
10141044
bind:value={seriesSearch}
10151045
onfocus={() => showSeriesDropdown = true}
1046+
oninput={() => showSeriesDropdown = true}
10161047
class="w-full px-3 py-2 rounded-md text-sm"
10171048
style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary);"
10181049
/>
1019-
{#if showSeriesDropdown && filteredSeries.length > 0}
1050+
{#if showSeriesDropdown}
10201051
<div
10211052
class="absolute z-20 w-full mt-1 rounded-lg shadow-xl max-h-40 overflow-y-auto"
10221053
style="background-color: var(--bg-secondary); border: 1px solid var(--border-color);"
@@ -1034,6 +1065,33 @@
10341065
{s.title}
10351066
</button>
10361067
{/each}
1068+
{#if canCreateSeries}
1069+
<button
1070+
type="button"
1071+
class="w-full px-3 py-2 text-left text-sm transition-colors flex items-center gap-2 font-medium"
1072+
style="color: var(--success); border-top: 1px solid var(--border-color);"
1073+
onmouseenter={(e) => e.currentTarget.style.backgroundColor = 'var(--bg-hover)'}
1074+
onmouseleave={(e) => e.currentTarget.style.backgroundColor = 'transparent'}
1075+
onclick={createNewSeries}
1076+
disabled={creatingSeries}
1077+
>
1078+
{#if creatingSeries}
1079+
<Loader2 class="w-3.5 h-3.5 animate-spin" />
1080+
Creating...
1081+
{:else}
1082+
<Plus class="w-3.5 h-3.5" />
1083+
Create "{seriesSearch.trim()}"
1084+
{/if}
1085+
</button>
1086+
{:else if filteredSeries.length === 0 && seriesSearch.trim().length === 0}
1087+
<div class="px-3 py-2 text-sm" style="color: var(--text-muted);">
1088+
Type to search or create a new series
1089+
</div>
1090+
{:else if filteredSeries.length === 0}
1091+
<div class="px-3 py-2 text-sm" style="color: var(--text-muted);">
1092+
No matching series found
1093+
</div>
1094+
{/if}
10371095
</div>
10381096
{/if}
10391097
</div>

0 commit comments

Comments
 (0)