Introduce a centralized Zustand store and refactor app state out of App.tsx into src/store/appStore.ts. Modularize API surface by moving media/cast/settings/converters/types into src/lib/api/* and re-exporting from src/api.ts for backward compatibility. Replace inline route helpers with dedicated route components (MediaDetailRoute, CastDetailRoute, CategoryBrowseRoute) and wire CATEGORY_PATHS/PATH_TO_CATEGORY constants. Update AddMediaView UI (icons, layout) and adjust settings/category handling to use DEFAULT_SETTINGS and the store. Add zustand to package.json/package-lock.json and include a new React SKILL.md. Overall changes improve state management, API organization, and route/component separation for better maintainability and code-splitting.
106 lines
3.1 KiB
TypeScript
106 lines
3.1 KiB
TypeScript
import { Media } from '../../types';
|
|
import { ApiResponse, PaginatedResponse, ApiMediaItem, CreateMediaInput, UpdateMediaInput } from './types';
|
|
import { convertApiToMedia } from './converters';
|
|
|
|
const BASE_URL = import.meta.env.VITE_API_URL;
|
|
|
|
export async function fetchAllMedia(page: number = 1, limit: number = 10000): Promise<Media[]> {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/media?page=${page}&limit=${limit}`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data: ApiResponse<PaginatedResponse<ApiMediaItem>> = await response.json();
|
|
|
|
if (data.success && data.data.items) {
|
|
return data.data.items.map(convertApiToMedia);
|
|
}
|
|
return [];
|
|
} catch (error) {
|
|
console.error('Error fetching media from API:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function fetchMediaById(id: number | string): Promise<Media | null> {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/media/${id}`);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data: ApiResponse<ApiMediaItem> = await response.json();
|
|
|
|
if (data.success && data.data) {
|
|
return convertApiToMedia(data.data);
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error fetching media by ID:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function createMedia(media: CreateMediaInput): Promise<Media | null> {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/media`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(media),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data: ApiResponse<ApiMediaItem> = await response.json();
|
|
|
|
if (data.success && data.data) {
|
|
return convertApiToMedia(data.data);
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error creating media:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function updateMedia(id: number | string, media: UpdateMediaInput): Promise<Media | null> {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/media/${id}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(media),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data: ApiResponse<ApiMediaItem> = await response.json();
|
|
|
|
if (data.success && data.data) {
|
|
return convertApiToMedia(data.data);
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error updating media:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function deleteMedia(id: number | string): Promise<boolean> {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/media/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data: ApiResponse<{ message: string }> = await response.json();
|
|
return data.success;
|
|
} catch (error) {
|
|
console.error('Error deleting media:', error);
|
|
return false;
|
|
}
|
|
}
|