108 lines
3.0 KiB
TypeScript
108 lines
3.0 KiB
TypeScript
export interface Transaction {
|
|
id: number
|
|
family_id: number
|
|
description: string | null
|
|
type: string
|
|
datetime: string
|
|
category: string
|
|
amount: number
|
|
created_at: string
|
|
created_by: number
|
|
receipt_id: number | null
|
|
}
|
|
|
|
interface TransactionsResponse {
|
|
items: Transaction[]
|
|
}
|
|
|
|
interface GetTransactionsOptions {
|
|
familyId?: number
|
|
limit?: number
|
|
offset?: number
|
|
}
|
|
|
|
export async function getTransactions(options: GetTransactionsOptions = {}): Promise<Transaction[]> {
|
|
const params = new URLSearchParams()
|
|
|
|
if (typeof options.familyId === 'number' && Number.isFinite(options.familyId) && options.familyId > 0) {
|
|
params.set('family_id', String(options.familyId))
|
|
}
|
|
|
|
params.set('limit', String(options.limit ?? 100))
|
|
params.set('offset', String(options.offset ?? 0))
|
|
|
|
const query = params.toString()
|
|
const response = await fetch(`/api/v1/transactions${query ? `?${query}` : ''}`)
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch transactions: ${response.status}`)
|
|
}
|
|
|
|
const payload = await response.json() as TransactionsResponse
|
|
return Array.isArray(payload.items) ? payload.items : []
|
|
}
|
|
|
|
export interface CreateTransactionData {
|
|
family_id: number
|
|
type?: string
|
|
category?: string
|
|
amount?: number
|
|
datetime?: string
|
|
description?: string
|
|
receipt_number?: string
|
|
receipt_date?: string
|
|
}
|
|
|
|
// TODO: Replace with the authenticated user id when frontend auth is implemented.
|
|
const TRANSACTION_CREATOR_ID = 1
|
|
|
|
export async function createTransaction(data: CreateTransactionData): Promise<Transaction> {
|
|
const response = await fetch('/api/v1/transactions', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
...data,
|
|
created_by: TRANSACTION_CREATOR_ID,
|
|
}),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ message: response.statusText }))
|
|
throw new Error(error.message || `Failed to create transaction: ${response.status}`)
|
|
}
|
|
|
|
return response.json() as Promise<Transaction>
|
|
}
|
|
|
|
export interface CreateTransactionPhotoData {
|
|
photo: File
|
|
family_id: number
|
|
type?: string
|
|
category?: string
|
|
description?: string
|
|
}
|
|
|
|
export async function createTransactionFromPhoto(data: CreateTransactionPhotoData): Promise<Transaction> {
|
|
const formData = new FormData()
|
|
formData.append('photo', data.photo)
|
|
formData.append('family_id', String(data.family_id))
|
|
formData.append('created_by', String(TRANSACTION_CREATOR_ID))
|
|
if (data.type) formData.append('type', data.type)
|
|
if (data.category) formData.append('category', data.category)
|
|
if (data.description) formData.append('description', data.description)
|
|
|
|
const response = await fetch('/api/v1/transactions', {
|
|
method: 'POST',
|
|
body: formData,
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ message: response.statusText }))
|
|
throw new Error(error.message || `Failed to create transaction from photo: ${response.status}`)
|
|
}
|
|
|
|
return response.json() as Promise<Transaction>
|
|
}
|