mirror of
https://github.com/thegeneralist01/archivr
synced 2026-07-22 03:05:32 +02:00
feat: tag delete, rename, and per-user humanize-tags display setting (#16)
* feat: add tag delete and rename (backend + frontend)
- DELETE /api/archives/:id/tags/:tag_uid — deletes tag subtree via
recursive CTE; entry_tag_assignments cascade automatically
- PATCH /api/archives/:id/tags/:tag_uid { name } — renames a tag
segment (case-preserving slug), cascades full_path to all descendants
in one transaction using hierarchy CTE (not LIKE), returns updated Tag
- database: rename_tag + delete_tag with 7 unit tests covering subtree
cascade, collision detection, descendant path rewrite, slug stripping
- TagsView: inline rename (pencil icon / double-click → input), × delete
with confirmation dialog (warns about child tags)
- App.jsx: handleTagRenamed rewrites tagFilter for exact + descendant
paths; handleTagDeleted clears filter for deleted subtree
- api.js: renameTag (PATCH, returns Tag) + deleteTag (DELETE)
- ContextRail: tag pills show displayPath(full_path) (humanized) with
raw full_path in title tooltip; humanize-tags setting coming next
* feat: per-user humanize-tags display setting
- auth DB: idempotent migration adds users.humanize_slugs INTEGER DEFAULT 0
- GET /api/auth/me: returns humanize_slugs bool
- PATCH /api/auth/me: accepts { humanize_slugs: bool }, persisted via
database::update_user_humanize_slugs
- displayPath() helper moved to utils.js (was local to ContextRail)
- TagsView: node label shows tag.name vs tag.slug based on humanizeTags
- ContextRail: pill label applies displayPath conditionally
- App.jsx: derives humanizeTags from currentUser.humanize_slugs, passes
to TagsView + ContextRail; filter badge label humanized when on
- Settings > Profile: Display Preferences section with checkbox toggle,
updates backend and currentUser immediately via setCurrentUser
- api.js: patchMe(patch) for generic PATCH /api/auth/me
- Tests: auth_me default=false, PATCH persists=true (2 route tests)
This commit is contained in:
parent
d6b52ba06c
commit
55f85134df
13 changed files with 699 additions and 69 deletions
|
|
@ -58,6 +58,27 @@ export async function removeTag(archiveId, entryUid, tagUid) {
|
|||
if (!resp.ok) throw new Error(`Remove failed (${resp.status})`);
|
||||
}
|
||||
|
||||
export async function renameTag(archiveId, tagUid, name) {
|
||||
const res = await fetch(
|
||||
`/api/archives/${archiveId}/tags/${tagUid}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name }),
|
||||
}
|
||||
);
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
return res.json(); // returns the updated Tag: { tag_uid, name, slug, full_path }
|
||||
}
|
||||
|
||||
export async function deleteTag(archiveId, tagUid) {
|
||||
const res = await fetch(
|
||||
`/api/archives/${archiveId}/tags/${tagUid}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
}
|
||||
|
||||
export async function fetchRuns(archiveId) {
|
||||
return getJson(`/api/archives/${archiveId}/runs`);
|
||||
}
|
||||
|
|
@ -132,6 +153,15 @@ export async function updateProfile(displayName) {
|
|||
if (!res.ok) throw new Error(await res.text());
|
||||
}
|
||||
|
||||
export async function patchMe(patch) {
|
||||
const res = await fetch('/api/auth/me', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(patch),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
}
|
||||
|
||||
export async function changePassword(currentPassword, newPassword) {
|
||||
const res = await fetch('/api/auth/me', {
|
||||
method: 'PATCH',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue