export interface Publication { id: number title: string author: string | null osdb_pub_id: string image_url: string | null created_at: string updated_at: string } export interface PublicationWriteDTO { title: string author?: string | null osdb_pub_id: string } export interface PublicationFruit { fruit_id: number name: string osdb_number: string } export interface PublicationDescription { id: number publication_id: number fruit_id: number fruit_name: string publication_title: string url: string created_at: string } export interface PublicationFruitImage { id: number publication_id: number publication_title: string publication_author: string | null fruit_id: number filename: string | null image_type: string url: string created_at: string } export function pubImageUrl(pubId: number): string { return `/api/v1/publications/${pubId}/image` } export function pubDescriptionUrl(pubId: number, descId: number): string { return `/api/v1/publications/${pubId}/descriptions/${descId}` } export function pubFruitImageUrl(pubId: number, imgId: number): string { return `/api/v1/publications/${pubId}/fruit-images/${imgId}` } async function checkOk(res: Response): Promise { if (!res.ok) { let msg = `${res.status}` try { const body = await res.json() msg = body.error ?? body.errors?.join(', ') ?? msg } catch { // ignore } const err = new Error(msg) as Error & { status: number } err.status = res.status throw err } return res } export async function listPublications(): Promise { const res = await fetch('/api/v1/publications') return (await checkOk(res)).json() } export async function getPublication(id: number): Promise { const res = await fetch(`/api/v1/publications/${id}`) return (await checkOk(res)).json() } export async function createPublication(dto: PublicationWriteDTO): Promise { const res = await fetch('/api/v1/publications', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dto), }) return (await checkOk(res)).json() } export async function updatePublication(id: number, dto: PublicationWriteDTO): Promise { const res = await fetch(`/api/v1/publications/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dto), }) return (await checkOk(res)).json() } export async function deletePublication(id: number): Promise { const res = await fetch(`/api/v1/publications/${id}`, { method: 'DELETE' }) await checkOk(res) } export async function uploadCoverImage(pubId: number, file: File): Promise { const form = new FormData() form.append('image', file) const res = await fetch(`/api/v1/publications/${pubId}/image`, { method: 'POST', body: form }) await checkOk(res) } export async function deleteCoverImage(pubId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/image`, { method: 'DELETE' }) await checkOk(res) } export async function listPubFruits(pubId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/fruits`) return (await checkOk(res)).json() } export async function linkFruit(pubId: number, fruitId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/fruits`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fruit_id: fruitId }), }) await checkOk(res) } export async function unlinkFruit(pubId: number, fruitId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/fruits/${fruitId}`, { method: 'DELETE' }) await checkOk(res) } export async function listDescriptions(pubId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/descriptions`) return (await checkOk(res)).json() } export async function uploadDescription(pubId: number, fruitId: number, file: File): Promise { const form = new FormData() form.append('pdf', file) form.append('fruit_id', String(fruitId)) const res = await fetch(`/api/v1/publications/${pubId}/descriptions`, { method: 'POST', body: form }) return (await checkOk(res)).json() } export async function deleteDescription(pubId: number, descId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/descriptions/${descId}`, { method: 'DELETE' }) await checkOk(res) } export async function listPubFruitImages(pubId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/fruit-images`) return (await checkOk(res)).json() } export async function uploadPubFruitImage(pubId: number, fruitId: number, file: File): Promise { const form = new FormData() form.append('image', file) form.append('fruit_id', String(fruitId)) const res = await fetch(`/api/v1/publications/${pubId}/fruit-images`, { method: 'POST', body: form }) return (await checkOk(res)).json() } export async function deletePubFruitImage(pubId: number, imgId: number): Promise { const res = await fetch(`/api/v1/publications/${pubId}/fruit-images/${imgId}`, { method: 'DELETE' }) await checkOk(res) } export async function getFruitDescriptions(fruitId: number): Promise { const res = await fetch(`/api/v1/fruits/${fruitId}/descriptions`) return (await checkOk(res)).json() } export async function getFruitPublicationImages(fruitId: number): Promise { const res = await fetch(`/api/v1/fruits/${fruitId}/publication-images`) return (await checkOk(res)).json() }