import { computed, ref } from 'vue'; import { useNotification } from '~/composables/useNotification'; import { useYtpConfig } from '~/composables/useYtpConfig'; import { parse_api_error, parse_list_response, request } from '~/utils'; import type { Pagination } from '~/types/responses'; import type { StoreItem } from '~/types/store'; type HistoryLoadOptions = { order?: 'ASC' | 'DESC'; status?: string; perPage?: number; }; const items = ref([]); const pagination = ref({ page: 1, per_page: 50, total: 0, total_pages: 0, has_next: false, has_prev: false, }); const isLoading = ref(false); const isLoaded = ref(false); const lastError = ref(null); const throwInstead = ref(false); const readJson = async (response: Response): Promise => { try { const clone = response.clone(); return await clone.json(); } catch { return null; } }; const ensureSuccess = async (response: Response): Promise => { if (response.ok) { return; } const payload = await readJson(response); const message = await parse_api_error(payload); throw new Error(message); }; const handleError = (error: unknown): void => { const message = error instanceof Error ? error.message : 'Unexpected error occurred.'; lastError.value = message; useNotification().error(message); }; const pageSize = computed(() => { const config = useYtpConfig(); return Number(config.app.default_pagination || 50); }); const buildQuery = ( page: number, perPage: number, order: 'ASC' | 'DESC' = 'DESC', status?: string, ): string => { const params = new URLSearchParams({ type: 'done', page: String(page), per_page: String(perPage), order, }); if (status) { params.set('status', status); } return params.toString(); }; const loadHistory = async (page: number = 1, options: HistoryLoadOptions = {}): Promise => { const { order = 'DESC', status, perPage = pageSize.value } = options; isLoading.value = true; try { const response = await request(`/api/history?${buildQuery(page, perPage, order, status)}`); await ensureSuccess(response); const data = await response.json(); const { items: loadedItems, pagination: paginationData } = await parse_list_response(data); items.value = loadedItems; pagination.value = paginationData; isLoaded.value = true; lastError.value = null; } catch (error) { handleError(error); if (throwInstead.value) { throw error; } } finally { isLoading.value = false; } }; const reloadHistory = async (options: HistoryLoadOptions = {}): Promise => { const targetPage = isLoaded.value ? pagination.value.page : 1; await loadHistory(targetPage, options); }; const deleteHistoryItems = async ( options: { ids?: string[]; status?: string; removeFile?: boolean; } = {}, ): Promise => { const { ids, status, removeFile = true } = options; if (!ids && !status) { throw new Error('Either ids or status filter must be provided'); } try { const response = await request('/api/history', { method: 'DELETE', body: JSON.stringify({ type: 'done', ids, status, remove_file: removeFile, }), }); await ensureSuccess(response); const result = (await response.json()) as { deleted?: number; error?: string; message?: string; }; if (result.error || result.message) { throw new Error(result.error || result.message); } return Number(result.deleted ?? 0); } catch (error) { handleError(error); if (throwInstead.value) { throw error; } return 0; } }; const resetHistory = (): void => { items.value = []; pagination.value = { page: 1, per_page: pageSize.value, total: 0, total_pages: 0, has_next: false, has_prev: false, }; isLoaded.value = false; lastError.value = null; }; export const useHistoryState = () => { return { items, pagination, isLoading, isLoaded, lastError, pageSize, loadHistory, reloadHistory, deleteHistoryItems, resetHistory, }; };