feat(06-01): add template API client functions and useTemplate hook

- Add ItemTier type and TemplateItem/TemplateDetail interfaces to api.ts
- Add item_tier field to BudgetItem interface
- Add template API object with get/updateName/addItem/updateItem/deleteItem/reorder
- Add generate function to budgets API object
- Create useTemplate hook with CRUD operations and reorder logic
This commit is contained in:
2026-03-12 13:03:23 +01:00
parent fd171edaf0
commit 0af9431435
2 changed files with 136 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
import { useState, useEffect } from 'react'
import {
template as templateApi,
categories as categoriesApi,
type TemplateDetail,
type TemplateItem,
type Category,
type ItemTier,
} from '@/lib/api'
export function useTemplate() {
const [templateDetail, setTemplateDetail] = useState<TemplateDetail | null>(null)
const [cats, setCats] = useState<Category[]>([])
const [loading, setLoading] = useState(true)
const fetchTemplate = async () => {
const data = await templateApi.get()
setTemplateDetail(data)
}
const fetchCategories = async () => {
const data = await categoriesApi.list()
setCats(data)
}
useEffect(() => {
const init = async () => {
try {
await Promise.all([fetchTemplate(), fetchCategories()])
} finally {
setLoading(false)
}
}
init()
}, [])
const addItem = async (data: {
category_id: string
item_tier: ItemTier
budgeted_amount?: number
}) => {
await templateApi.addItem(data)
await fetchTemplate()
}
const removeItem = async (itemId: string) => {
await templateApi.deleteItem(itemId)
await fetchTemplate()
}
const moveItem = async (itemId: string, direction: 'up' | 'down') => {
if (!templateDetail) return
const items = [...templateDetail.items].sort((a, b) => a.sort_order - b.sort_order)
const idx = items.findIndex((i) => i.id === itemId)
if (idx === -1) return
const swapIdx = direction === 'up' ? idx - 1 : idx + 1
if (swapIdx < 0 || swapIdx >= items.length) return
const current = items[idx]
const swap = items[swapIdx]
const reordered: { id: string; sort_order: number }[] = items.map((item) => {
if (item.id === current.id) return { id: item.id, sort_order: swap.sort_order }
if (item.id === swap.id) return { id: item.id, sort_order: current.sort_order }
return { id: item.id, sort_order: item.sort_order }
})
await templateApi.reorder(reordered)
await fetchTemplate()
}
const updateItem = async (
itemId: string,
data: { item_tier?: ItemTier; budgeted_amount?: number }
) => {
await templateApi.updateItem(itemId, data)
await fetchTemplate()
}
return {
template: templateDetail,
categories: cats,
loading,
addItem,
removeItem,
moveItem,
updateItem,
refetch: fetchTemplate,
} as {
template: TemplateDetail | null
categories: Category[]
loading: boolean
addItem: (data: { category_id: string; item_tier: ItemTier; budgeted_amount?: number }) => Promise<void>
removeItem: (itemId: string) => Promise<void>
moveItem: (itemId: string, direction: 'up' | 'down') => Promise<void>
updateItem: (itemId: string, data: { item_tier?: ItemTier; budgeted_amount?: number }) => Promise<void>
refetch: () => Promise<void>
}
}