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
This commit is contained in:
67
k-photos-frontend/hooks/use-albums.ts
Normal file
67
k-photos-frontend/hooks/use-albums.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
"use client"
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
||||
import api from "@/lib/api"
|
||||
import type {
|
||||
AlbumResponse,
|
||||
CreateAlbumRequest,
|
||||
UpdateAlbumRequest,
|
||||
} from "@/lib/types"
|
||||
|
||||
export function useAlbums() {
|
||||
const qc = useQueryClient()
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ["albums"],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get<AlbumResponse[]>("/albums")
|
||||
return data
|
||||
},
|
||||
})
|
||||
|
||||
const create = useMutation({
|
||||
mutationFn: async (title: string) => {
|
||||
const body: CreateAlbumRequest = { title }
|
||||
const { data } = await api.post<AlbumResponse>("/albums", body)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["albums"] }),
|
||||
})
|
||||
|
||||
const update = useMutation({
|
||||
mutationFn: async ({ id, ...updates }: UpdateAlbumRequest & { id: string }) => {
|
||||
const { data } = await api.put<AlbumResponse>(`/albums/${id}`, updates)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["albums"] }),
|
||||
})
|
||||
|
||||
const addEntry = useMutation({
|
||||
mutationFn: async ({ albumId, assetId }: { albumId: string; assetId: string }) => {
|
||||
await api.post(`/albums/${albumId}/entries`, { asset_id: assetId })
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["albums"] })
|
||||
qc.invalidateQueries({ queryKey: ["album"] })
|
||||
},
|
||||
})
|
||||
|
||||
const removeEntry = useMutation({
|
||||
mutationFn: async ({ albumId, assetId }: { albumId: string; assetId: string }) => {
|
||||
await api.delete(`/albums/${albumId}/entries/${assetId}`)
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["albums"] })
|
||||
qc.invalidateQueries({ queryKey: ["album"] })
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
albums: query.data ?? [],
|
||||
isLoading: query.isLoading,
|
||||
createAlbum: create.mutateAsync,
|
||||
updateAlbum: update.mutateAsync,
|
||||
addEntry: addEntry.mutateAsync,
|
||||
removeEntry: removeEntry.mutateAsync,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user