From 7ba8c986633f2ed8f6bfb880b8d716f04c4ec1f7 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jul 2025 13:58:38 -0400 Subject: [PATCH] feat: Implement expense creation modal and API integration - Added ExpenseCreateModal component for adding new expenses with form validation. - Integrated API endpoint for creating expenses, ensuring only authorized users can access it. - Updated dashboard to include functionality for adding expenses and refreshing the expense list after creation. - Enhanced UI with Vuetify components for better user experience and responsiveness. --- components/ExpenseCreateModal.vue | 322 +++++++++++++++++++ components/ExpenseList.vue | 468 +++++++++++++++++----------- pages/dashboard/expenses.vue | 493 ++++++++++++++++-------------- server/api/create-expense.ts | 79 +++++ 4 files changed, 957 insertions(+), 405 deletions(-) create mode 100644 components/ExpenseCreateModal.vue create mode 100644 server/api/create-expense.ts diff --git a/components/ExpenseCreateModal.vue b/components/ExpenseCreateModal.vue new file mode 100644 index 0000000..1f82ab7 --- /dev/null +++ b/components/ExpenseCreateModal.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/components/ExpenseList.vue b/components/ExpenseList.vue index a6325d9..35fb800 100644 --- a/components/ExpenseList.vue +++ b/components/ExpenseList.vue @@ -1,148 +1,187 @@ diff --git a/server/api/create-expense.ts b/server/api/create-expense.ts new file mode 100644 index 0000000..94c504e --- /dev/null +++ b/server/api/create-expense.ts @@ -0,0 +1,79 @@ +import { requireSalesOrAdmin } from '@/server/utils/auth'; +import { getNocoDbConfiguration } from '@/server/utils/nocodb'; +import type { Expense } from '@/utils/types'; + +export default defineEventHandler(async (event) => { + try { + // Ensure only sales/admin users can create expenses + await requireSalesOrAdmin(event); + + const body = await readBody(event); + console.log('[create-expense] Creating expense with data:', body); + + // Validate required fields + const requiredFields = ['Establishment Name', 'Price', 'Category', 'Payer', 'Time']; + for (const field of requiredFields) { + if (!body[field]) { + throw createError({ + statusCode: 400, + statusMessage: `Missing required field: ${field}` + }); + } + } + + // Get NocoDB configuration + const config = getNocoDbConfiguration(); + const expenseTableId = "mxfcefkk4dqs6uq"; // Expense table ID from nocodb.ts + + // Prepare expense data for NocoDB + const expenseData = { + "Establishment Name": body["Establishment Name"], + Price: body.Price, + Category: body.Category, + Payer: body.Payer, + Time: body.Time, + Contents: body.Contents || null, + "Payment Method": body["Payment Method"] || "Card", + currency: body.currency || "EUR", + Paid: body.Paid || false + }; + + console.log('[create-expense] Sending to NocoDB:', expenseData); + + // Create the expense in NocoDB + const response = await $fetch(`${config.url}/api/v2/tables/${expenseTableId}/records`, { + method: 'POST', + headers: { + 'xc-token': config.token, + 'Content-Type': 'application/json' + }, + body: expenseData + }); + + console.log('[create-expense] Expense created successfully:', response.Id); + + return response; + + } catch (error: any) { + console.error('[create-expense] Failed to create expense:', error); + + if (error.statusCode === 403) { + throw createError({ + statusCode: 403, + statusMessage: 'Access denied. This feature requires sales team or administrator privileges.' + }); + } + + if (error.statusCode === 400) { + throw createError({ + statusCode: 400, + statusMessage: error.statusMessage + }); + } + + throw createError({ + statusCode: 500, + statusMessage: 'Failed to create expense. Please try again later.' + }); + } +});