Files
nuxt-claude/app/composables/usePurchases.ts
hyeonggil 6784786262 feat: 구매 관리에 엑셀 업로드 및 중고 판매 등록 기능 추가
- usePurchases: user_id 필터링으로 타 사용자 데이터 접근 차단
- usePurchases: bulkCreatePurchases() 일괄 저장 메서드 추가
- PurchaseModal: submit 시 중복 닫힘 방지 (부모에서 제어)
- purchases/index: 엑셀 업로드 버튼 및 모달 연동
- purchases/index: 중고 판매 등록(태그 아이콘) 버튼 및 모달 연동
- purchases/index: 판매 상태 뱃지를 장비명 옆에 표시
- PurchaseExcelUpload: xlsx 파일 파싱 후 일괄 저장 컴포넌트 추가
- SellFromPurchaseModal: 구매 장비에서 중고 판매 등록 모달 추가
- xlsx 패키지 추가
2026-03-08 21:25:29 +09:00

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
}
}