diff --git a/app/components/purchases/PurchaseExcelUpload.vue b/app/components/purchases/PurchaseExcelUpload.vue new file mode 100644 index 0000000..bcac469 --- /dev/null +++ b/app/components/purchases/PurchaseExcelUpload.vue @@ -0,0 +1,381 @@ + + + + + + + + + + + + {{ file ? file.name : '파일을 클릭하거나 드래그하여 업로드' }} + + .xlsx, .xls 파일 지원 + + + + + + + 처음 사용하시나요? + + + + + + + + + + + + + 총 {{ rows.length }}행 + + + 유효 {{ validRows.length }}행 + + + 오류 {{ errorRows.length }}행 + + + + + + + + {{ row.original._index }} + + + + {{ row.original.status === 'valid' ? '유효' : '오류' }} + + + + {{ row.original.data.name }} + + + {{ row.original.data.category }} + + + {{ formatPrice(row.original.data.price) }} + + + {{ row.original.data.purchase_date }} + + + + + {{ err }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/components/purchases/PurchaseModal.vue b/app/components/purchases/PurchaseModal.vue index bed62ee..b2c3243 100644 --- a/app/components/purchases/PurchaseModal.vue +++ b/app/components/purchases/PurchaseModal.vue @@ -14,8 +14,8 @@ const emit = defineEmits<{ const title = computed(() => props.initial ? '장비 수정' : '장비 추가') function handleSubmit(data: PurchaseInsert) { + // 부모의 비동기 핸들러가 완료된 후 모달을 닫으므로 여기서 닫지 않음 emit('submit', data) - emit('update:open', false) } diff --git a/app/components/purchases/SellFromPurchaseModal.vue b/app/components/purchases/SellFromPurchaseModal.vue new file mode 100644 index 0000000..c2be3e3 --- /dev/null +++ b/app/components/purchases/SellFromPurchaseModal.vue @@ -0,0 +1,104 @@ + + + + + + + + + + 구매 장비 {{ purchase.name }} 와 연결됩니다 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/composables/usePurchases.ts b/app/composables/usePurchases.ts index ef439c0..0e0a3d4 100644 --- a/app/composables/usePurchases.ts +++ b/app/composables/usePurchases.ts @@ -28,6 +28,7 @@ export function usePurchases() { 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[] @@ -59,6 +60,7 @@ export function usePurchases() { } async function updatePurchase(id: string, payload: Partial) { + if (!user.value) return loading.value = true error.value = null try { @@ -66,6 +68,7 @@ export function usePurchases() { .from('purchases') .update(payload) .eq('id', id) + .eq('user_id', user.value.id) .select() .single() if (err) throw err @@ -80,6 +83,7 @@ export function usePurchases() { } async function deletePurchase(id: string) { + if (!user.value) return loading.value = true error.value = null try { @@ -87,6 +91,7 @@ export function usePurchases() { .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) { @@ -97,11 +102,13 @@ export function usePurchases() { } async function getPurchase(id: string): Promise { + 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 @@ -110,6 +117,24 @@ export function usePurchases() { } } + async function bulkCreatePurchases(payloads: PurchaseInsert[]): Promise { + 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), @@ -120,6 +145,7 @@ export function usePurchases() { createPurchase, updatePurchase, deletePurchase, - getPurchase + getPurchase, + bulkCreatePurchases } } diff --git a/app/pages/purchases/index.vue b/app/pages/purchases/index.vue index 3585e6f..4d95ff6 100644 --- a/app/pages/purchases/index.vue +++ b/app/pages/purchases/index.vue @@ -1,46 +1,76 @@
+ {{ file ? file.name : '파일을 클릭하거나 드래그하여 업로드' }} +
.xlsx, .xls 파일 지원