Files
k-photos/k-photos-frontend/hooks/use-jobs.ts
Gabriel Kaszewski 957737ac9b feat: frontend MVP — auth, timeline, upload, albums, admin, image viewer
Backend:
- user roles (DB + JWT + first-user-is-admin)
- volume-aware file resolver (multi-volume asset serving)
- directory scanner uses volume URI directly
- date-summary endpoint (capture date from EXIF)
- timeline ordered by capture date
- list endpoints: volumes, plugins, pipelines, library paths
- delete endpoints: volumes, library paths
- configurable upload body limit (MAX_UPLOAD_BYTES)

Frontend:
- auth: login/register, token refresh, role-based admin gate
- timeline: date-grouped grid, infinite scroll, date scrubber
- image viewer: fullscreen zoom/pan/pinch, metadata sidebar
- upload: drag-drop, sequential upload, progress tracking
- albums: create, add/remove photos, asset picker dialog
- admin: storage (import library), jobs (pagination, error details),
  plugins (list + toggle), pipelines, sidecars, duplicates
- multi-select mode with add-to-album action
- TanStack Query for all data fetching
2026-06-01 01:35:43 +02:00

90 lines
2.3 KiB
TypeScript

"use client"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "@/lib/api"
import type { JobListResponse, BatchProgressResponse } from "@/lib/types"
const PAGE_SIZE = 25
export function useJobs(status?: string, offset = 0) {
return useQuery({
queryKey: ["admin", "jobs", status, offset],
queryFn: async () => {
const { data } = await api.get<JobListResponse>("/jobs", {
params: { status, limit: PAGE_SIZE, offset },
})
return data
},
refetchInterval: 5000,
})
}
export { PAGE_SIZE as JOBS_PAGE_SIZE }
export function useEnqueueJob() {
const qc = useQueryClient()
return useMutation({
mutationFn: async (body: {
job_type: string
priority?: number
payload?: Record<string, unknown>
target_asset_id?: string
batch_id?: string
}) => {
const { data } = await api.post("/jobs", body)
return data
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["admin", "jobs"] }),
})
}
export function useStartJob() {
const qc = useQueryClient()
return useMutation({
mutationFn: async (jobId: string) => {
await api.post(`/jobs/${jobId}/start`)
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["admin", "jobs"] }),
})
}
export function useCompleteJob() {
const qc = useQueryClient()
return useMutation({
mutationFn: async ({
jobId,
result,
}: {
jobId: string
result: Record<string, unknown>
}) => {
await api.post(`/jobs/${jobId}/complete`, { result })
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["admin", "jobs"] }),
})
}
export function useFailJob() {
const qc = useQueryClient()
return useMutation({
mutationFn: async ({ jobId, error }: { jobId: string; error: string }) => {
await api.post(`/jobs/${jobId}/fail`, { error })
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["admin", "jobs"] }),
})
}
export function useBatchProgress(batchId: string) {
return useQuery({
queryKey: ["admin", "batch", batchId],
queryFn: async () => {
const { data } = await api.get<BatchProgressResponse>(
`/jobs/batches/${batchId}`,
)
return data
},
enabled: !!batchId,
refetchInterval: 3000,
})
}