- usePurchases: user_id 필터링으로 타 사용자 데이터 접근 차단 - usePurchases: bulkCreatePurchases() 일괄 저장 메서드 추가 - PurchaseModal: submit 시 중복 닫힘 방지 (부모에서 제어) - purchases/index: 엑셀 업로드 버튼 및 모달 연동 - purchases/index: 중고 판매 등록(태그 아이콘) 버튼 및 모달 연동 - purchases/index: 판매 상태 뱃지를 장비명 옆에 표시 - PurchaseExcelUpload: xlsx 파일 파싱 후 일괄 저장 컴포넌트 추가 - SellFromPurchaseModal: 구매 장비에서 중고 판매 등록 모달 추가 - xlsx 패키지 추가
152 lines
4.2 KiB
TypeScript
152 lines
4.2 KiB
TypeScript
import type { Purchase, PurchaseInsert, EquipmentCategory } from '~/types/purchase'
|
|
|
|
export function usePurchases() {
|
|
const client = useSupabaseClient()
|
|
const user = useSupabaseUser()
|
|
|
|
const purchases = ref<Purchase[]>([])
|
|
const loading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
const totalSpent = computed(() =>
|
|
purchases.value.reduce((sum, p) => sum + p.price, 0)
|
|
)
|
|
|
|
const categoryBreakdown = computed(() => {
|
|
const breakdown: Record<string, number> = {}
|
|
for (const p of purchases.value) {
|
|
breakdown[p.category] = (breakdown[p.category] ?? 0) + p.price
|
|
}
|
|
return breakdown
|
|
})
|
|
|
|
async function fetchPurchases() {
|
|
if (!user.value) return
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const { data, error: err } = await client
|
|
.from('purchases')
|
|
.select('*')
|
|
.eq('user_id', user.value.id)
|
|
.order('purchase_date', { ascending: false })
|
|
if (err) throw err
|
|
purchases.value = data as Purchase[]
|
|
} catch (e: unknown) {
|
|
error.value = e instanceof Error ? e.message : '오류가 발생했습니다'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function createPurchase(payload: PurchaseInsert) {
|
|
if (!user.value) return
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const { data, error: err } = await client
|
|
.from('purchases')
|
|
.insert({ ...payload, user_id: user.value.id })
|
|
.select()
|
|
.single()
|
|
if (err) throw err
|
|
purchases.value.unshift(data as Purchase)
|
|
return data as Purchase
|
|
} catch (e: unknown) {
|
|
error.value = e instanceof Error ? e.message : '저장에 실패했습니다'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function updatePurchase(id: string, payload: Partial<PurchaseInsert>) {
|
|
if (!user.value) return
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const { data, error: err } = await client
|
|
.from('purchases')
|
|
.update(payload)
|
|
.eq('id', id)
|
|
.eq('user_id', user.value.id)
|
|
.select()
|
|
.single()
|
|
if (err) throw err
|
|
const idx = purchases.value.findIndex(p => p.id === id)
|
|
if (idx !== -1) purchases.value[idx] = data as Purchase
|
|
return data as Purchase
|
|
} catch (e: unknown) {
|
|
error.value = e instanceof Error ? e.message : '수정에 실패했습니다'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function deletePurchase(id: string) {
|
|
if (!user.value) return
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const { error: err } = await client
|
|
.from('purchases')
|
|
.delete()
|
|
.eq('id', id)
|
|
.eq('user_id', user.value.id)
|
|
if (err) throw err
|
|
purchases.value = purchases.value.filter(p => p.id !== id)
|
|
} catch (e: unknown) {
|
|
error.value = e instanceof Error ? e.message : '삭제에 실패했습니다'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function getPurchase(id: string): Promise<Purchase | null> {
|
|
if (!user.value) return null
|
|
try {
|
|
const { data, error: err } = await client
|
|
.from('purchases')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.eq('user_id', user.value.id)
|
|
.single()
|
|
if (err) throw err
|
|
return data as Purchase
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function bulkCreatePurchases(payloads: PurchaseInsert[]): Promise<number> {
|
|
if (!user.value || payloads.length === 0) return 0
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const rows = payloads.map(p => ({ ...p, user_id: user.value!.id }))
|
|
const { data, error: err } = await client.from('purchases').insert(rows).select()
|
|
if (err) throw err
|
|
purchases.value.unshift(...(data as Purchase[]))
|
|
return data.length
|
|
} catch (e: unknown) {
|
|
error.value = e instanceof Error ? e.message : '일괄 저장에 실패했습니다'
|
|
return 0
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
purchases: readonly(purchases),
|
|
loading: readonly(loading),
|
|
error: readonly(error),
|
|
totalSpent,
|
|
categoryBreakdown,
|
|
fetchPurchases,
|
|
createPurchase,
|
|
updatePurchase,
|
|
deletePurchase,
|
|
getPurchase,
|
|
bulkCreatePurchases
|
|
}
|
|
}
|