feat(web): update frontend TypeScript modules and API types

This commit updates the web frontend TypeScript modules:

Core modules:
- admin.ts: Admin panel functionality and user management
- analytics.ts: Analytics dashboard and data visualization
- api-explorer.ts: Interactive API documentation explorer
- api.ts: Core API client with request/response handling
- collections.ts: Book collection management UI
- conflicts.ts: Sync conflict resolution interface
- custom-section-builder.ts: Dynamic section builder for UI
- docs.ts: Documentation viewer and navigation
- dom.ts: DOM manipulation utilities and helpers
- header.ts: Application header with navigation
- library.ts: Library view and book grid management
- linking.ts: Device-book linking interface
- password_validation.ts: Client-side password strength validation
- queue.ts: Device sync queue management UI
- search.ts: Full-text search with Lunr integration
- storage.ts: Local storage and cache management
- theme.ts: Theme management and CSS variable updates
- themeDropdown.ts: Theme selector dropdown component
- toast.ts: Toast notification system
- woodPaneling.ts: Visual theme effects
- woodPanelingInit.ts: Visual effects initialization

Type definitions:
- api.d.ts: Updated TypeScript definitions for API responses

These updates enhance the frontend with improved functionality
for book management, device synchronization, and user experience.
This commit is contained in:
2026-02-27 17:06:48 -05:00
parent 4d321528b2
commit ea5ad7a41b
22 changed files with 3133 additions and 2737 deletions
+200 -185
View File
@@ -11,75 +11,75 @@
// Note: internal/handlers/search.go has an unused MediaItemSummary - ignore it
// Used in: search.ts
interface MediaItemSummary {
id: string;
library_id: string;
title: string;
author?: string;
isbn?: string;
description?: string;
file_path: string;
file_size?: number;
mime_type?: string;
cover_image_path?: string;
series?: string;
series_number?: number;
tags?: string[];
asin?: string;
date_published?: string;
publisher?: string;
contributors?: string[];
language?: string;
edition?: string;
page_count?: number;
genre?: string;
copyright_year?: number;
goodreads_id?: string;
openlibrary_id?: string;
google_books_id?: string;
added_by_admin_id?: string;
created_at: string;
updated_at: string;
format_group: string;
format_mimetype?: string;
is_reflowable?: boolean;
has_fixed_layout?: boolean;
total_characters?: number;
chapter_count?: number;
entitlement_id?: string;
revision_number?: number;
kobo_content_id?: string;
kobo_metadata?: string;
tags_search?: string[];
contributors_search?: string[];
file_sha256?: string;
opf_identifier?: string;
opf_uuid?: string;
hash_confidence?: string;
library_name: string;
library_type_name: string;
id: string;
library_id: string;
title: string;
author?: string;
isbn?: string;
description?: string;
file_path: string;
file_size?: number;
mime_type?: string;
cover_image_path?: string;
series?: string;
series_number?: number;
tags?: string[];
asin?: string;
date_published?: string;
publisher?: string;
contributors?: string[];
language?: string;
edition?: string;
page_count?: number;
genre?: string;
copyright_year?: number;
goodreads_id?: string;
openlibrary_id?: string;
google_books_id?: string;
added_by_admin_id?: string;
created_at: string;
updated_at: string;
format_group: string;
format_mimetype?: string;
is_reflowable?: boolean;
has_fixed_layout?: boolean;
total_characters?: number;
chapter_count?: number;
entitlement_id?: string;
revision_number?: number;
kobo_content_id?: string;
kobo_metadata?: string;
tags_search?: string[];
contributors_search?: string[];
file_sha256?: string;
opf_identifier?: string;
opf_uuid?: string;
hash_confidence?: string;
library_name: string;
library_type_name: string;
}
// Matches handlers.CollectionData / CollectionResponse JSON response
// Source: internal/handlers/collections.go:123-131 CollectionResponse
// Used in: collections.ts
interface CollectionData {
id: string;
name: string;
description: string;
color: string;
icon: string;
auto_assign_rules?: unknown;
created_at: string;
id: string;
name: string;
description: string;
color: string;
icon: string;
auto_assign_rules?: unknown;
created_at: string;
}
// Matches handlers.BookInfo JSON response (internal/handlers/collections.go:66-71)
// JSON tags: media_item_id, title, author, cover_image_path
// Used in: collections.templ (server-rendered), collections.ts
interface BookInfo {
media_item_id: string;
title: string;
author: string;
cover_image_path: string;
media_item_id: string;
title: string;
author: string;
cover_image_path: string;
}
// Dashboard type definitions
@@ -87,237 +87,252 @@ interface BookInfo {
// Source: handlers.SectionData in collections.go (lines 73-81)
// Used in: dashboard API responses, TypeScript dashboard components
interface SectionData {
id: string;
is_system: boolean;
title: string;
description: string;
icon: string;
items: BookInfo[];
view_all_url: string;
priority: number;
id: string;
is_system: boolean;
title: string;
description: string;
icon: string;
items: BookInfo[];
view_all_url: string;
priority: number;
}
// Matches database.UserDashboardPreferences and dashboard preferences API
// Source: internal/database/models.go:381-390
// Used in: dashboard preferences API
interface DashboardPreferences {
library_id: string;
hidden_collections: string[];
collection_order: string[];
items_per_section: number;
library_id: string;
hidden_collections: string[];
collection_order: string[];
items_per_section: number;
}
// Matches handlers.UnlinkedBookData JSON response
// Used in: unlinked_books.ts, unlinked_books.templ
interface UnlinkedBookData {
progress_id: string;
device_id: string;
device_name: string;
device_type: 'koreader' | 'kobo' | 'web';
title_from_device: string;
file_path: string;
sha256: string;
last_sync_time: string;
confidence_score: number;
potential_matches: PotentialMatchData[];
progress_id: string;
device_id: string;
device_name: string;
device_type: "koreader" | "kobo" | "web";
title_from_device: string;
file_path: string;
sha256: string;
last_sync_time: string;
confidence_score: number;
potential_matches: PotentialMatchData[];
}
interface PotentialMatchData {
media_item_id: string;
title: string;
author: string;
confidence: number;
cover_image_path?: string;
media_item_id: string;
title: string;
author: string;
confidence: number;
cover_image_path?: string;
}
// Matches collection rule objects
// Used in: collection_rules.ts
interface CollectionRule {
id: string;
field: 'genre' | 'series' | 'author' | 'language' | 'publisher' | 'copyright_year' | 'tags';
operator: 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'greater_than' | 'less_than';
value: string;
enabled: boolean;
priority: number;
id: string;
field:
| "genre"
| "series"
| "author"
| "language"
| "publisher"
| "copyright_year"
| "tags";
operator:
| "equals"
| "not_equals"
| "contains"
| "not_contains"
| "starts_with"
| "ends_with"
| "greater_than"
| "less_than";
value: string;
enabled: boolean;
priority: number;
}
// Matches API test rule responses
// Used in: collection_rules.ts (test results)
interface TestRuleMatch {
title: string;
author: string;
cover_image_path?: string;
title: string;
author: string;
cover_image_path?: string;
}
// Matches handlers.SearchResponse (internal/handlers/search.go)
interface SearchResponse {
results: SearchBookResponse[];
total: number;
results: SearchBookResponse[];
total: number;
}
interface SearchBookResponse {
id: string;
title: string;
authors: SearchAuthor[];
id: string;
title: string;
authors: SearchAuthor[];
}
interface SearchAuthor {
first_name: string;
last_name: string;
first_name: string;
last_name: string;
}
// Matches AuthResponse (internal/handlers/auth.go:59-65)
interface AuthResponse {
access_token: string;
refresh_token?: string;
token_type: string;
expires_in: number;
user: UserProfile;
access_token: string;
refresh_token?: string;
token_type: string;
expires_in: number;
user: UserProfile;
}
interface UserProfile {
id: string;
email: string;
username: string;
first_name?: string;
last_name?: string;
role: string;
theme?: string;
id: string;
email: string;
username: string;
first_name?: string;
last_name?: string;
role: string;
theme?: string;
}
// Matches handlers.ReadingStatsResponse (internal/handlers/analytics.go:26-35)
// Used in: analytics.ts
interface ReadingStatsResponse {
total_books_read: number;
total_pages_read: number;
total_reading_time_minutes: number;
average_session_time_minutes: number;
longest_session_minutes: number;
most_active_day_of_week: string;
completion_rate: number;
daily_reading_minutes: DailyReading[];
total_books_read: number;
total_pages_read: number;
total_reading_time_minutes: number;
average_session_time_minutes: number;
longest_session_minutes: number;
most_active_day_of_week: string;
completion_rate: number;
daily_reading_minutes: DailyReading[];
}
interface DailyReading {
date: string;
minutes: number;
pages: number;
date: string;
minutes: number;
pages: number;
}
// Matches handlers.DeviceUsageResponse (internal/handlers/analytics.go:43-45)
// Note: Response is wrapped: { devices: DeviceUsage[] }
// Used in: analytics.ts
interface DeviceUsageResponse {
devices: DeviceUsage[];
devices: DeviceUsage[];
}
interface DeviceUsage {
device_id: string;
device_name: string;
device_type: string;
sync_count: number;
last_sync: string;
total_time_seconds: number;
total_time_minutes: number;
device_id: string;
device_name: string;
device_type: string;
sync_count: number;
last_sync: string;
total_time_seconds: number;
total_time_minutes: number;
}
// Matches handlers.PopularBooksResponse (internal/handlers/analytics.go:57-59)
// Note: Response is wrapped: { books: PopularBook[] }
// Used in: analytics.ts
interface PopularBooksResponse {
books: PopularBook[];
books: PopularBook[];
}
interface PopularBook {
media_item_id: string;
title: string;
author: string;
read_count: number;
avg_completion: number;
last_read: string;
media_item_id: string;
title: string;
author: string;
read_count: number;
avg_completion: number;
last_read: string;
}
// Matches handlers.QueueItemResponse (internal/handlers/queue.go:35-51)
// Used in: queue.ts
interface QueueItemResponse {
id: string;
device_id: string;
device_name: string;
device_type: string;
media_item_id?: string;
media_title?: string;
user_email: string;
sync_type: string;
priority: number;
attempts: number;
max_attempts: number;
status: string;
error_message?: string;
created_at: string;
processed_at?: string;
id: string;
device_id: string;
device_name: string;
device_type: string;
media_item_id?: string;
media_title?: string;
user_email: string;
sync_type: string;
priority: number;
attempts: number;
max_attempts: number;
status: string;
error_message?: string;
created_at: string;
processed_at?: string;
}
// Matches handlers.QueueStatsResponse (internal/handlers/queue.go:27-33)
// Used in: queue.ts
interface QueueStatsResponse {
pending_count: number;
processing_count: number;
failed_count: number;
completed_count: number;
total_count: number;
pending_count: number;
processing_count: number;
failed_count: number;
completed_count: number;
total_count: number;
}
// Matches handlers.ConflictDetailResponse (internal/handlers/conflicts.go:42-53)
// Used in: conflicts.ts
interface ConflictDetailResponse {
id: string;
media_item_id: string;
media_item_title: string;
conflict_type: string;
conflict_data: Record<string, ConflictSourceData>;
resolution_status: string;
resolution_data?: Record<string, unknown>;
resolved_by?: string;
resolved_at?: string;
created_at: string;
id: string;
media_item_id: string;
media_item_title: string;
conflict_type: string;
conflict_data: Record<string, ConflictSourceData>;
resolution_status: string;
resolution_data?: Record<string, unknown>;
resolved_by?: string;
resolved_at?: string;
created_at: string;
}
// Matches handlers.ConflictSourceData (internal/handlers/conflicts.go:36-40)
interface ConflictSourceData {
source: string;
timestamp: string;
data: Record<string, unknown>;
source: string;
timestamp: string;
data: Record<string, unknown>;
}
// Matches handlers.ConflictListResponse (internal/handlers/conflicts.go:55-59)
// Used in: conflicts.ts
interface ConflictListResponse {
conflicts: ConflictDetailResponse[];
total: number;
unresolved: number;
conflicts: ConflictDetailResponse[];
total: number;
unresolved: number;
}
// Matches handlers.ConflictResolveResponse (internal/handlers/conflicts.go:61-65)
// Used in: conflicts.ts
interface ConflictResolveResponse {
conflict_resolved: boolean;
applied_to: Record<string, boolean>;
devices_synced: string[];
conflict_resolved: boolean;
applied_to: Record<string, boolean>;
devices_synced: string[];
}
// Matches handlers.BulkResolveResponse (internal/handlers/conflicts.go:424-429)
// Used in: conflicts.ts
interface BulkResolveResponse {
results: ConflictResult[];
total: number;
success: number;
failed: number;
results: ConflictResult[];
total: number;
success: number;
failed: number;
}
// Matches handlers.ConflictResult (internal/handlers/conflicts.go:431-436)
interface ConflictResult {
conflict_id: string;
status: string;
error?: string;
winner?: string;
conflict_id: string;
status: string;
error?: string;
winner?: string;
}