Various Fixes and QoL enhancements.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { searchApi, tagApi } from '../../lib/api';
|
||||
import { searchApi } from '../../lib/api';
|
||||
import { Story, Tag } from '../../types/api';
|
||||
import AppLayout from '../../components/layout/AppLayout';
|
||||
import { Input } from '../../components/ui/Input';
|
||||
@@ -11,7 +11,7 @@ import TagFilter from '../../components/stories/TagFilter';
|
||||
import LoadingSpinner from '../../components/ui/LoadingSpinner';
|
||||
|
||||
type ViewMode = 'grid' | 'list';
|
||||
type SortOption = 'createdAt' | 'title' | 'authorName' | 'rating';
|
||||
type SortOption = 'createdAt' | 'title' | 'authorName' | 'rating' | 'wordCount';
|
||||
|
||||
export default function LibraryPage() {
|
||||
const [stories, setStories] = useState<Story[]>([]);
|
||||
@@ -28,19 +28,27 @@ export default function LibraryPage() {
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||
|
||||
|
||||
// Load tags for filtering
|
||||
useEffect(() => {
|
||||
const loadTags = async () => {
|
||||
try {
|
||||
const tagsResult = await tagApi.getTags({ page: 0, size: 1000 });
|
||||
setTags(tagsResult?.content || []);
|
||||
} catch (error) {
|
||||
console.error('Failed to load tags:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Extract tags from current search results with counts
|
||||
const extractTagsFromResults = (stories: Story[]): Tag[] => {
|
||||
const tagCounts: { [key: string]: number } = {};
|
||||
|
||||
loadTags();
|
||||
}, []);
|
||||
stories.forEach(story => {
|
||||
story.tagNames?.forEach(tagName => {
|
||||
if (tagCounts[tagName]) {
|
||||
tagCounts[tagName]++;
|
||||
} else {
|
||||
tagCounts[tagName] = 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Object.entries(tagCounts).map(([tagName, count]) => ({
|
||||
id: tagName, // Use tag name as ID since we don't have actual IDs from search results
|
||||
name: tagName,
|
||||
storyCount: count
|
||||
}));
|
||||
};
|
||||
|
||||
// Debounce search to avoid too many API calls
|
||||
useEffect(() => {
|
||||
@@ -59,9 +67,14 @@ export default function LibraryPage() {
|
||||
sortDir: sortDirection,
|
||||
});
|
||||
|
||||
setStories(result?.results || []);
|
||||
const currentStories = result?.results || [];
|
||||
setStories(currentStories);
|
||||
setTotalPages(Math.ceil((result?.totalHits || 0) / 20));
|
||||
setTotalElements(result?.totalHits || 0);
|
||||
|
||||
// Always update tags based on current search results (including initial wildcard search)
|
||||
const resultTags = extractTagsFromResults(currentStories);
|
||||
setTags(resultTags);
|
||||
} catch (error) {
|
||||
console.error('Failed to load stories:', error);
|
||||
setStories([]);
|
||||
@@ -99,16 +112,21 @@ export default function LibraryPage() {
|
||||
};
|
||||
|
||||
const handleSortChange = (newSortOption: SortOption) => {
|
||||
if (newSortOption === sortOption) {
|
||||
// Toggle direction if same option
|
||||
setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc');
|
||||
setSortOption(newSortOption);
|
||||
// Set appropriate default direction for the sort option
|
||||
if (newSortOption === 'title' || newSortOption === 'authorName') {
|
||||
setSortDirection('asc'); // Alphabetical fields default to ascending
|
||||
} else {
|
||||
setSortOption(newSortOption);
|
||||
setSortDirection('desc'); // Default to desc for new sort option
|
||||
setSortDirection('desc'); // Numeric/date fields default to descending
|
||||
}
|
||||
resetPage();
|
||||
};
|
||||
|
||||
const toggleSortDirection = () => {
|
||||
setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc');
|
||||
resetPage();
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedTags([]);
|
||||
@@ -203,7 +221,18 @@ export default function LibraryPage() {
|
||||
<option value="title">Title</option>
|
||||
<option value="authorName">Author</option>
|
||||
<option value="rating">Rating</option>
|
||||
<option value="wordCount">Word Count</option>
|
||||
</select>
|
||||
|
||||
{/* Sort Direction Toggle */}
|
||||
<button
|
||||
onClick={toggleSortDirection}
|
||||
className="p-2 rounded-lg theme-card theme-text hover:bg-opacity-80 transition-colors border theme-border"
|
||||
title={`Sort ${sortDirection === 'asc' ? 'Ascending' : 'Descending'}`}
|
||||
aria-label={`Toggle sort direction - currently ${sortDirection === 'asc' ? 'ascending' : 'descending'}`}
|
||||
>
|
||||
{sortDirection === 'asc' ? '↑' : '↓'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Clear Filters */}
|
||||
|
||||
Reference in New Issue
Block a user