webui: edit-category modal + dropdown dedupe
Add an "Edit category…" context-menu item and modal that renames a category and/or changes its save path (Uncategorized: path only). Filter empty-named categories out of the add/set-category dropdowns so the stored Uncategorized save-path entry doesn't double the option. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c3985fe742
commit
d55fc35c72
2 changed files with 34 additions and 2 deletions
|
|
@ -36,6 +36,8 @@ export const api = {
|
||||||
|
|
||||||
createCategory: (name, savePath) => jpost('/api/categories', { name, savePath }),
|
createCategory: (name, savePath) => jpost('/api/categories', { name, savePath }),
|
||||||
deleteCategory: (name) => jpost('/api/categories/delete', { name }),
|
deleteCategory: (name) => jpost('/api/categories/delete', { name }),
|
||||||
|
editCategory: (name, newName, savePath) =>
|
||||||
|
jpost('/api/categories/edit', { name, newName, savePath }),
|
||||||
createTag: (name) => jpost('/api/tags', { name }),
|
createTag: (name) => jpost('/api/tags', { name }),
|
||||||
deleteTag: (name) => jpost('/api/tags/delete', { name }),
|
deleteTag: (name) => jpost('/api/tags/delete', { name }),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,9 @@ function onSidebarContext(e, type, value, removable) {
|
||||||
const items = [
|
const items = [
|
||||||
{ label: type === 'category' ? 'New category…' : 'New tag…', act: () => (type === 'category' ? openCreateCategory() : openCreateTag()) },
|
{ label: type === 'category' ? 'New category…' : 'New tag…', act: () => (type === 'category' ? openCreateCategory() : openCreateTag()) },
|
||||||
];
|
];
|
||||||
|
if (type === 'category') {
|
||||||
|
items.push({ label: 'Edit category…', act: () => openEditCategory(value) });
|
||||||
|
}
|
||||||
if (removable) {
|
if (removable) {
|
||||||
items.push({ sep: true });
|
items.push({ sep: true });
|
||||||
items.push({ label: `Delete ${type}`, danger: true, act: () => confirmDeleteMeta(type, value) });
|
items.push({ label: `Delete ${type}`, danger: true, act: () => confirmDeleteMeta(type, value) });
|
||||||
|
|
@ -495,7 +498,7 @@ async function confirmDelete(hashes) {
|
||||||
/* ---------- add torrent modal ---------- */
|
/* ---------- add torrent modal ---------- */
|
||||||
function openAddModal() {
|
function openAddModal() {
|
||||||
const cats = '<option value="" selected>Uncategorized</option>' +
|
const cats = '<option value="" selected>Uncategorized</option>' +
|
||||||
state.meta.categories.map((c) => `<option value="${f.esc(c.name)}">${f.esc(c.name || 'Uncategorized')}</option>`).join('');
|
state.meta.categories.filter((c) => c.name).map((c) => `<option value="${f.esc(c.name)}">${f.esc(c.name)}</option>`).join('');
|
||||||
const tagOpts = state.meta.tags.map((t) =>
|
const tagOpts = state.meta.tags.map((t) =>
|
||||||
`<label class="cbdrop-item"><input type="checkbox" value="${f.esc(t)}"><span>${f.esc(t)}</span></label>`).join('');
|
`<label class="cbdrop-item"><input type="checkbox" value="${f.esc(t)}"><span>${f.esc(t)}</span></label>`).join('');
|
||||||
openModal('Add torrent', `
|
openModal('Add torrent', `
|
||||||
|
|
@ -765,7 +768,7 @@ function promptCategory() {
|
||||||
const cur = state.snapshot?.torrents?.find((t) => t.hash === [...state.selected][0])?.category || '';
|
const cur = state.snapshot?.torrents?.find((t) => t.hash === [...state.selected][0])?.category || '';
|
||||||
const sel = (v) => (v === cur ? ' selected' : '');
|
const sel = (v) => (v === cur ? ' selected' : '');
|
||||||
const opts = `<option value=""${sel('')}>Uncategorized</option>` +
|
const opts = `<option value=""${sel('')}>Uncategorized</option>` +
|
||||||
state.meta.categories.map((c) => `<option value="${f.esc(c.name)}"${sel(c.name)}>${f.esc(c.name || 'Uncategorized')}</option>`).join('');
|
state.meta.categories.filter((c) => c.name).map((c) => `<option value="${f.esc(c.name)}"${sel(c.name)}>${f.esc(c.name)}</option>`).join('');
|
||||||
openModal('Set category', `
|
openModal('Set category', `
|
||||||
<div class="field"><label>Category</label><select id="catSel">${opts}</select></div>
|
<div class="field"><label>Category</label><select id="catSel">${opts}</select></div>
|
||||||
<p class="dim" style="font-size:12px">Need a new one? Use <b>+</b> next to “Categories” in the sidebar.</p>`,
|
<p class="dim" style="font-size:12px">Need a new one? Use <b>+</b> next to “Categories” in the sidebar.</p>`,
|
||||||
|
|
@ -795,6 +798,33 @@ function openCreateCategory() {
|
||||||
setTimeout(() => document.getElementById('catName')?.focus(), 0);
|
setTimeout(() => document.getElementById('catName')?.focus(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openEditCategory(value) {
|
||||||
|
const isUncat = value === '';
|
||||||
|
const cur = state.meta.categories.find((c) => c.name === value);
|
||||||
|
const curPath = cur ? (cur.savePath || '') : '';
|
||||||
|
const nameField = isUncat
|
||||||
|
? `<div class="field"><label>Name</label><input type="text" value="Uncategorized" disabled /></div>`
|
||||||
|
: `<div class="field"><label>Name</label><input type="text" id="catName" value="${f.esc(value)}" /></div>`;
|
||||||
|
openModal('Edit category', `
|
||||||
|
${nameField}
|
||||||
|
<div class="field"><label>Save path</label>
|
||||||
|
<input type="text" id="catPath" value="${f.esc(curPath)}" placeholder="${f.esc(state.meta.preferences.save_path || '/data/downloads')}" /></div>`,
|
||||||
|
[{ label: 'Cancel', act: closeModal }, {
|
||||||
|
label: 'Save', primary: true, act: async () => {
|
||||||
|
const newName = isUncat ? '' : document.getElementById('catName').value.trim();
|
||||||
|
if (!isUncat && !newName) return closeModal();
|
||||||
|
const savePath = document.getElementById('catPath').value.trim();
|
||||||
|
await api.editCategory(value, newName, savePath);
|
||||||
|
await refreshMeta();
|
||||||
|
if (!isUncat && newName !== value && state.filter.type === 'category' && state.filter.value === value)
|
||||||
|
state.filter = { type: 'category', value: newName };
|
||||||
|
renderView();
|
||||||
|
closeModal();
|
||||||
|
},
|
||||||
|
}]);
|
||||||
|
setTimeout(() => document.getElementById('catName')?.focus(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
function openCreateTag() {
|
function openCreateTag() {
|
||||||
openModal('New tag', `<div class="field"><label>Name</label><input type="text" id="tagName" placeholder="e.g. seed-2weeks" /></div>`,
|
openModal('New tag', `<div class="field"><label>Name</label><input type="text" id="tagName" placeholder="e.g. seed-2weeks" /></div>`,
|
||||||
[{ label: 'Cancel', act: closeModal }, {
|
[{ label: 'Cancel', act: closeModal }, {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue