Tag Enhancement + bugfixes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { AuthResponse, Story, Author, Tag, Series, SearchResult, PagedResult, Collection, CollectionSearchResult, StoryWithCollectionContext, CollectionStatistics } from '../types/api';
|
||||
import { AuthResponse, Story, Author, Tag, TagAlias, Series, SearchResult, PagedResult, Collection, CollectionSearchResult, StoryWithCollectionContext, CollectionStatistics } from '../types/api';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || '/api';
|
||||
|
||||
@@ -303,6 +303,33 @@ export const tagApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getTag: async (id: string): Promise<Tag> => {
|
||||
const response = await api.get(`/tags/${id}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
createTag: async (tagData: {
|
||||
name: string;
|
||||
color?: string;
|
||||
description?: string;
|
||||
}): Promise<Tag> => {
|
||||
const response = await api.post('/tags', tagData);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
updateTag: async (id: string, tagData: {
|
||||
name?: string;
|
||||
color?: string;
|
||||
description?: string;
|
||||
}): Promise<Tag> => {
|
||||
const response = await api.put(`/tags/${id}`, tagData);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
deleteTag: async (id: string): Promise<void> => {
|
||||
await api.delete(`/tags/${id}`);
|
||||
},
|
||||
|
||||
getTagAutocomplete: async (query: string): Promise<string[]> => {
|
||||
const response = await api.get('/tags/autocomplete', { params: { query } });
|
||||
// Backend returns TagDto[], extract just the names
|
||||
@@ -313,6 +340,76 @@ export const tagApi = {
|
||||
const response = await api.get('/tags/collections');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Alias operations
|
||||
addAlias: async (tagId: string, aliasName: string): Promise<TagAlias> => {
|
||||
const response = await api.post(`/tags/${tagId}/aliases`, { aliasName });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
removeAlias: async (tagId: string, aliasId: string): Promise<void> => {
|
||||
await api.delete(`/tags/${tagId}/aliases/${aliasId}`);
|
||||
},
|
||||
|
||||
resolveTag: async (name: string): Promise<Tag | null> => {
|
||||
try {
|
||||
const response = await api.get(`/tags/resolve/${encodeURIComponent(name)}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
if (error.response?.status === 404) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Batch resolve multiple tag names to their canonical forms
|
||||
resolveTags: async (names: string[]): Promise<string[]> => {
|
||||
const resolved = await Promise.all(
|
||||
names.map(async (name) => {
|
||||
const tag = await tagApi.resolveTag(name);
|
||||
return tag ? tag.name : name; // Return canonical name or original if not found
|
||||
})
|
||||
);
|
||||
return resolved;
|
||||
},
|
||||
|
||||
// Merge operations
|
||||
previewMerge: async (sourceTagIds: string[], targetTagId: string): Promise<{
|
||||
targetTagName: string;
|
||||
targetStoryCount: number;
|
||||
totalResultStoryCount: number;
|
||||
aliasesToCreate: string[];
|
||||
}> => {
|
||||
const response = await api.post('/tags/merge/preview', {
|
||||
sourceTagIds,
|
||||
targetTagId
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
mergeTags: async (sourceTagIds: string[], targetTagId: string): Promise<Tag> => {
|
||||
const response = await api.post('/tags/merge', {
|
||||
sourceTagIds,
|
||||
targetTagId
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Tag suggestions
|
||||
suggestTags: async (title: string, content?: string, summary?: string, limit?: number): Promise<{
|
||||
tagName: string;
|
||||
confidence: number;
|
||||
reason: string;
|
||||
}[]> => {
|
||||
const response = await api.post('/tags/suggest', {
|
||||
title,
|
||||
content,
|
||||
summary,
|
||||
limit: limit || 10
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
// Series endpoints
|
||||
@@ -347,6 +444,18 @@ export const searchApi = {
|
||||
sortDir?: string;
|
||||
facetBy?: string[];
|
||||
}): Promise<SearchResult> => {
|
||||
// Resolve tag aliases to canonical names for expanded search
|
||||
let resolvedTags = params.tags;
|
||||
if (params.tags && params.tags.length > 0) {
|
||||
try {
|
||||
resolvedTags = await tagApi.resolveTags(params.tags);
|
||||
} catch (error) {
|
||||
console.warn('Failed to resolve tag aliases during search:', error);
|
||||
// Fall back to original tags if resolution fails
|
||||
resolvedTags = params.tags;
|
||||
}
|
||||
}
|
||||
|
||||
// Create URLSearchParams to properly handle array parameters
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
@@ -363,8 +472,8 @@ export const searchApi = {
|
||||
if (params.authors && params.authors.length > 0) {
|
||||
params.authors.forEach(author => searchParams.append('authors', author));
|
||||
}
|
||||
if (params.tags && params.tags.length > 0) {
|
||||
params.tags.forEach(tag => searchParams.append('tags', tag));
|
||||
if (resolvedTags && resolvedTags.length > 0) {
|
||||
resolvedTags.forEach(tag => searchParams.append('tags', tag));
|
||||
}
|
||||
if (params.facetBy && params.facetBy.length > 0) {
|
||||
params.facetBy.forEach(facet => searchParams.append('facetBy', facet));
|
||||
|
||||
Reference in New Issue
Block a user