diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index eefc83a..843890f 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -13,7 +13,17 @@
"Bash(git log:*)",
"Bash(git commit:*)",
"Bash(git remote add:*)",
- "Bash(git push:*)"
+ "Bash(git push:*)",
+ "Bash(pnpm drizzle generate:*)",
+ "Bash(pnpm drizzle-kit:*)",
+ "Bash(pnpm add:*)",
+ "Bash(pnpm dlx shadcn@latest add:*)",
+ "Bash(pnpm run lint:*)",
+ "Bash(pnpm run typecheck:*)",
+ "Bash(npx shadcn@latest add:*)",
+ "Bash(npx shadcn@latest list:*)",
+ "Bash(npm run build:*)",
+ "Bash(npx next build:*)"
],
"additionalDirectories": [
"C:\\c\\Projects\\nextjs-better-auth-postgresql-starter-kit"
diff --git a/docs/business/MoneyMind-PRD.md b/docs/business/MoneyMind-PRD.md
new file mode 100644
index 0000000..b2bb1a6
--- /dev/null
+++ b/docs/business/MoneyMind-PRD.md
@@ -0,0 +1,283 @@
+# Product Requirements Document (PRD)
+
+## MoneyMind - Personal Finance Management Web Application
+
+
+***
+
+**Product Name:** MoneyMind
+**Prepared By:** AI Product Team
+**Date:** 29 Settembre 2025
+**Version:** 1.0
+
+***
+
+## Executive Summary
+
+MoneyMind è una web application moderna per la gestione delle finanze personali che trasforma il tradizionale foglio Excel in un'esperienza digitale intelligente e interattiva. L'applicazione combina visualizzazioni avanzate, analisi predittive e un assistente virtuale AI per offrire insights personalizzati sulla situazione finanziaria dell'utente.[^3][^4][^5]
+
+## Obiettivi del Prodotto
+
+### Obiettivo Primario
+
+Digitalizzare e potenziare il processo di gestione del budget personale, trasformando dati finanziari statici in insights dinamici e actionable attraverso un'interfaccia responsiva e un assistente AI integrato.[^6][^3]
+
+### Obiettivi Secondari
+
+- Aumentare la consapevolezza finanziaria degli utenti del 40% entro 6 mesi
+- Ridurre il tempo dedicato alla gestione del budget del 60%
+- Migliorare le abitudini di risparmio attraverso consigli personalizzati
+- Fornire previsioni finanziarie accurate basate sui pattern storici
+
+
+## Target User \& Personas
+
+### Persona Primaria: "Il Professionista Organizzato"
+
+- **Età:** 25-45 anni
+- **Occupazione:** Professionista, manager, freelancer
+- **Tech Savviness:** Intermedio-Avanzato
+- **Pain Points:** Gestione manuale del budget, mancanza di insights, difficoltà nel tracciamento su mobile
+- **Goals:** Controllo completo delle finanze, ottimizzazione dei risparmi, pianificazione a lungo termine
+
+
+### Persona Secondaria: "Il Digital Native"
+
+- **Età:** 22-35 anni
+- **Occupazione:** Startup employee, consulente, creativo
+- **Tech Savviness:** Avanzato
+- **Pain Points:** Strumenti finanziari poco intuitivi, mancanza di automazione
+- **Goals:** Gestione smart delle finanze, insights real-time, integrazione con altri tools
+
+
+## Core Features \& Functionality
+
+### 1. Dashboard Interattiva
+
+**Priority:** P0 (Critical)
+
+- **Descrizione:** Dashboard principale con overview finanziaria completa
+- **User Story:** "Come utente, voglio vedere immediatamente la mia situazione finanziaria attuale e i trend principali"
+- **Acceptance Criteria:**
+ - Visualizzazione real-time di entrate, spese, risparmi e investimenti
+ - Grafici interattivi (line charts, pie charts, bar charts)
+ - Comparazioni mese-su-mese e anno-su-anno
+ - KPI cards con metriche chiave
+ - Responsive design per mobile e desktop
+
+
+### 2. Gestione Transazioni
+
+**Priority:** P0 (Critical)
+
+- **Descrizione:** Sistema completo per inserimento e categorizzazione transazioni
+- **User Story:** "Come utente, voglio inserire facilmente le mie transazioni e vederle categorizzate automaticamente"
+- **Acceptance Criteria:**
+ - Form di inserimento rapido con validazione
+ - Auto-categorizzazione basata su ML
+ - Import da file Excel/CSV
+ - Ricerca e filtri avanzati
+ - Edit bulk per multiple transazioni
+
+
+### 3. Analytics \& Insights
+
+**Priority:** P0 (Critical)
+
+- **Descrizione:** Suite di analisi avanzate con visualizzazioni dinamiche
+- **User Story:** "Come utente, voglio comprendere i miei pattern di spesa e ricevere insights actionable"
+- **Acceptance Criteria:**
+ - Analisi trend spese per categoria
+ - Identificazione anomalie e pattern insoliti
+ - Forecasting spese future
+ - Goal tracking per risparmi e budgeting
+ - Report mensili/annuali esportabili
+
+
+### 4. MoneyMind AI Advisor
+
+**Priority:** P1 (High)
+
+- **Descrizione:** Assistente virtuale AI conversazionale per consigli finanziari personalizzati
+- **User Story:** "Come utente, voglio ricevere consigli finanziari personalizzati basati sulla mia situazione specifica"
+- **Acceptance Criteria:**
+ - Chat interface con AI conversazionale
+ - Accesso a tutto lo storico finanziario dell'utente
+ - Consigli personalizzati su budget optimization
+ - Alerts proattivi per spese anomale
+ - Supporto multilingua (italiano/inglese)
+ - Risposte contestualizzate ai dati finanziari
+
+
+### 5. Budget Planning \& Goals
+
+**Priority:** P1 (High)
+
+- **Descrizione:** Sistema di pianificazione budget e obiettivi finanziari
+- **User Story:** "Come utente, voglio impostare budget per categorie e tracciare il progresso verso i miei obiettivi"
+- **Acceptance Criteria:**
+ - Creazione budget per categoria con limiti personalizzabili
+ - Progress tracking con alert per overbudget
+ - Goal setting per risparmi a breve/lungo termine
+ - Scenario planning per decisioni finanziarie
+ - Calendar view per pianificazione future spese
+
+
+### 6. Mobile-First Experience
+
+**Priority:** P0 (Critical)
+
+- **Descrizione:** Esperienza ottimizzata per dispositivi mobili
+- **User Story:** "Come utente, voglio accedere alle mie finanze in modo fluido da qualsiasi dispositivo"
+- **Acceptance Criteria:**
+ - PWA (Progressive Web App) con offline capabilities
+ - Touch-optimized interface
+ - Quick actions per transazioni frequenti
+ - Sincronizzazione real-time cross-device
+ - Performance ottimizzate (<3s load time)
+
+
+## Technical Requirements
+
+### Frontend
+
+- **Framework:** React.js con TypeScript
+- **State Management:** Redux Toolkit
+- **UI Library:** Material-UI o Ant Design
+- **Charting:** Chart.js o Recharts
+- **Mobile:** PWA con service workers
+
+
+### Backend
+
+- **Runtime:** Node.js con Express.js
+- **Database:** PostgreSQL per transazioni, Redis per caching
+- **AI/ML:** Integration con OpenAI API per MoneyMind AI Advisor
+- **Authentication:** JWT-based con OAuth2 support
+- **API:** RESTful con GraphQL per complex queries
+
+
+### Infrastructure
+
+- **Hosting:** Cloud-native (AWS/GCP/Azure)
+- **CDN:** CloudFlare per performance globali
+- **Monitoring:** Application monitoring e error tracking
+- **Security:** End-to-end encryption, GDPR compliance
+
+
+## Success Metrics
+
+### Primary KPIs
+
+- **User Engagement:** DAU/MAU ratio >30%
+- **Feature Adoption:** MoneyMind AI Advisor usage >60% degli utenti attivi
+- **Retention:** 90-day retention >40%
+- **Performance:** App load time <3 secondi
+
+
+### Secondary KPIs
+
+- **Financial Impact:** Miglioramento medio risparmi utenti +15%
+- **User Satisfaction:** NPS score >50
+- **Technical:** Uptime >99.5%
+- **Growth:** User acquisition crescita 20% MoM
+
+
+## Timeline \& Milestones
+
+### Phase 1: MVP (3 mesi)
+
+- Dashboard base con import Excel
+- Gestione transazioni core
+- Responsive design
+- Grafici essenziali
+
+
+### Phase 2: Analytics (2 mesi)
+
+- Suite completa analytics
+- Goal tracking
+- Report avanzati
+- Performance optimization
+
+
+### Phase 3: AI Integration (2 mesi)
+
+- MoneyMind AI Advisor
+- ML per categorizzazione
+- Predictive analytics
+- Conversational interface
+
+
+### Phase 4: Advanced Features (1 mese)
+
+- PWA capabilities
+- Advanced forecasting
+- Integration APIs
+- Premium features
+
+
+## Constraints \& Assumptions
+
+### Technical Constraints
+
+- Budget di sviluppo: €50.000 per MVP
+- Team size: 4-5 sviluppatori full-time
+- Compliance GDPR obbligatoria
+- Supporto browser moderni (Chrome, Firefox, Safari, Edge)
+
+
+### Business Assumptions
+
+- Target market: Italia inizialmente, espansione EU successiva
+- Modello freemium con features premium
+- Utenti disposti a migrare da soluzioni Excel
+- Crescente adozione di strumenti fintech in Italia
+
+
+### User Assumptions
+
+- Comfort con interfacce web moderne
+- Disponibilità a condividere dati finanziari per insights
+- Utilizzo misto desktop/mobile (70/30)
+- Preferenza per lingua italiana con fallback inglese
+
+
+## Risk Assessment
+
+### High Risk
+
+- **Privacy/Security:** Gestione dati finanziari sensibili richiede security massima
+- **AI Reliability:** MoneyMind AI Advisor deve fornire consigli accurati e responsabili
+- **Competition:** Mercato fintech competitivo con players consolidati
+
+
+### Medium Risk
+
+- **User Adoption:** Migrazione da Excel potrebbe incontrare resistenza
+- **Technical Complexity:** Integration multiple APIs e ML pipelines
+- **Scalability:** Crescita utenti potrebbe richiedere architettura revision
+
+
+### Mitigation Strategies
+
+- Security audit esterni e penetration testing regolari
+- Extensive testing dell'AI con review umano
+- Differenziazione attraverso UX superiore e personalizzazione italiana
+- Architettura microservices per scalabilità futura
+
+
+## Brand Identity \& Positioning
+
+### Brand Promise
+
+MoneyMind rappresenta l'intelligenza finanziaria personalizzata - un compagno digitale che comprende le tue finanze e ti guida verso decisioni più smart.[^2][^1]
+
+### Key Messaging
+
+- "La tua mente finanziaria digitale"
+- "Intelligenza artificiale per decisioni finanziarie intelligenti"
+- "Trasforma i tuoi dati in saggezza finanziaria"
+
+
+
diff --git a/docs/business/starter-prompt.md b/docs/business/starter-prompt.md
index 86de00a..189d3dc 100644
--- a/docs/business/starter-prompt.md
+++ b/docs/business/starter-prompt.md
@@ -1,9 +1,8 @@
I'm working with an agentic coding boilerplate project that includes authentication, database integration, and AI capabilities. Here's what's already set up:
## Current Agentic Coding Boilerplate Structure
-
- **Authentication**: Better Auth with Google OAuth integration
-- **Database**: Drizzle ORM with PostgreSQL setup
+- **Database**: Drizzle ORM with PostgreSQL setup
- **AI Integration**: Vercel AI SDK with OpenAI integration
- **UI**: shadcn/ui components with Tailwind CSS
- **Current Routes**:
@@ -12,11 +11,9 @@ I'm working with an agentic coding boilerplate project that includes authenticat
- `/chat` - AI chat interface (requires OpenAI API key)
## Important Context
-
This is an **agentic coding boilerplate/starter template** - all existing pages and components are meant to be examples and should be **completely replaced** to build the actual AI-powered application.
### CRITICAL: You MUST Override All Boilerplate Content
-
**DO NOT keep any boilerplate components, text, or UI elements unless explicitly requested.** This includes:
- **Remove all placeholder/demo content** (setup checklists, welcome messages, boilerplate text)
@@ -26,14 +23,12 @@ This is an **agentic coding boilerplate/starter template** - all existing pages
- **Replace placeholder routes and pages** with the actual application functionality
### Required Actions:
-
1. **Start Fresh**: Treat existing components as temporary scaffolding to be removed
2. **Complete Replacement**: Build the new application from scratch using the existing tech stack
3. **No Hybrid Approach**: Don't try to integrate new features alongside existing boilerplate content
4. **Clean Slate**: The final application should have NO trace of the original boilerplate UI or content
The only things to preserve are:
-
- **All installed libraries and dependencies** (DO NOT uninstall or remove any packages from package.json)
- **Authentication system** (but customize the UI/flow as needed)
- **Database setup and schema** (but modify schema as needed for your use case)
@@ -41,7 +36,6 @@ The only things to preserve are:
- **Build and development scripts** (keep all npm/pnpm scripts in package.json)
## Tech Stack
-
- Next.js 15 with App Router
- TypeScript
- Tailwind CSS
@@ -51,8 +45,21 @@ The only things to preserve are:
- shadcn/ui components
- Lucide React icons
-## Component Development Guidelines
+## AI Model Configuration
+**IMPORTANT**: When implementing any AI functionality, always use the `OPENAI_MODEL` environment variable for the model name instead of hardcoding it:
+```typescript
+// ✓ Correct - Use environment variable
+const model = process.env.OPENAI_MODEL || "gpt-5-mini";
+model: openai(model)
+
+// ✗ Incorrect - Don't hardcode model names
+model: openai("gpt-5-mini")
+```
+
+This allows for easy model switching without code changes and ensures consistency across the application.
+
+## Component Development Guidelines
**Always prioritize shadcn/ui components** when building the application:
1. **First Choice**: Use existing shadcn/ui components from the project
@@ -62,25 +69,115 @@ The only things to preserve are:
The project already includes several shadcn/ui components (button, dialog, avatar, etc.) and follows their design system. Always check the [shadcn/ui documentation](https://ui.shadcn.com/docs/components) for available components before implementing alternatives.
## What I Want to Build
+MoneyMind - Personal Finance Management Web Application
+Product Name: MoneyMind Prepared By: AI Product Team Date: 29 Settembre 2025 Version: 1.0
-Basic todo list app with the ability for users to add, remove, update, complete and view todos.
+Executive Summary
+MoneyMind è una web application moderna per la gestione delle finanze personali che trasforma il tradizionale foglio Excel in un'esperienza digitale intelligente e interattiva. L'applicazione combina visualizzazioni avanzate, analisi predittive e un assistente virtuale AI per offrire insights personalizzati sulla situazione finanziaria dell'utente.[^3][^4][^5]
+
+Obiettivi del Prodotto
+Obiettivo Primario
+Digitalizzare e potenziare il processo di gestione del budget personale, trasformando dati finanziari statici in insights dinamici e actionable attraverso un'interfaccia responsiva e un assistente AI integrato.[^6][^3]
+
+Obiettivi Secondari
+Aumentare la consapevolezza finanziaria degli utenti del 40% entro 6 mesi
+Ridurre il tempo dedicato alla gestione del budget del 60%
+Migliorare le abitudini di risparmio attraverso consigli personalizzati
+Fornire previsioni finanziarie accurate basate sui pattern storici
+Target User & Personas
+Persona Primaria: "Il Professionista Organizzato"
+Età: 25-45 anni
+Occupazione: Professionista, manager, freelancer
+Tech Savviness: Intermedio-Avanzato
+Pain Points: Gestione manuale del budget, mancanza di insights, difficoltà nel tracciamento su mobile
+Goals: Controllo completo delle finanze, ottimizzazione dei risparmi, pianificazione a lungo termine
+Persona Secondaria: "Il Digital Native"
+Età: 22-35 anni
+Occupazione: Startup employee, consulente, creativo
+Tech Savviness: Avanzato
+Pain Points: Strumenti finanziari poco intuitivi, mancanza di automazione
+Goals: Gestione smart delle finanze, insights real-time, integrazione con altri tools
+Core Features & Functionality
+1. Dashboard Interattiva
+Priority: P0 (Critical)
+
+Descrizione: Dashboard principale con overview finanziaria completa
+User Story: "Come utente, voglio vedere immediatamente la mia situazione finanziaria attuale e i trend principali"
+Acceptance Criteria:
+Visualizzazione real-time di entrate, spese, risparmi e investimenti
+Grafici interattivi (line charts, pie charts, bar charts)
+Comparazioni mese-su-mese e anno-su-anno
+KPI cards con metriche chiave
+Responsive design per mobile e desktop
+2. Gestione Transazioni
+Priority: P0 (Critical)
+
+Descrizione: Sistema completo per inserimento e categorizzazione transazioni
+User Story: "Come utente, voglio inserire facilmente le mie transazioni e vederle categorizzate automaticamente"
+Acceptance Criteria:
+Form di inserimento rapido con validazione
+Auto-categorizzazione basata su ML
+Import da file Excel/CSV
+Ricerca e filtri avanzati
+Edit bulk per multiple transazioni
+3. Analytics & Insights
+Priority: P0 (Critical)
+
+Descrizione: Suite di analisi avanzate con visualizzazioni dinamiche
+User Story: "Come utente, voglio comprendere i miei pattern di spesa e ricevere insights actionable"
+Acceptance Criteria:
+Analisi trend spese per categoria
+Identificazione anomalie e pattern insoliti
+Forecasting spese future
+Goal tracking per risparmi e budgeting
+Report mensili/annuali esportabili
+4. MoneyMind AI Advisor
+Priority: P1 (High)
+
+Descrizione: Assistente virtuale AI conversazionale per consigli finanziari personalizzati
+User Story: "Come utente, voglio ricevere consigli finanziari personalizzati basati sulla mia situazione specifica"
+Acceptance Criteria:
+Chat interface con AI conversazionale
+Accesso a tutto lo storico finanziario dell'utente
+Consigli personalizzati su budget optimization
+Alerts proattivi per spese anomale
+Supporto multilingua (italiano/inglese)
+Risposte contestualizzate ai dati finanziari
+5. Budget Planning & Goals
+Priority: P1 (High)
+
+Descrizione: Sistema di pianificazione budget e obiettivi finanziari
+User Story: "Come utente, voglio impostare budget per categorie e tracciare il progresso verso i miei obiettivi"
+Acceptance Criteria:
+Creazione budget per categoria con limiti personalizzabili
+Progress tracking con alert per overbudget
+Goal setting per risparmi a breve/lungo termine
+Scenario planning per decisioni finanziarie
+Calendar view per pianificazione future spese
+6. Mobile-First Experience
+Priority: P0 (Critical)
+
+Descrizione: Esperienza ottimizzata per dispositivi mobili
+User Story: "Come utente, voglio accedere alle mie finanze in modo fluido da qualsiasi dispositivo"
+Acceptance Criteria:
+PWA (Progressive Web App) con offline capabilities
+Touch-optimized interface
+Quick actions per transazioni frequenti
+Sincronizzazione real-time cross-device
+Performance ottimizzate (<3s load time)
## Request
-
Please help me transform this boilerplate into my actual application. **You MUST completely replace all existing boilerplate code** to match my project requirements. The current implementation is just temporary scaffolding that should be entirely removed and replaced.
## Final Reminder: COMPLETE REPLACEMENT REQUIRED
-
-🚨 **IMPORTANT**: Do not preserve any of the existing boilerplate UI, components, or content. The user expects a completely fresh application that implements their requirements from scratch. Any remnants of the original boilerplate (like setup checklists, welcome screens, demo content, or placeholder navigation) indicate incomplete implementation.
+**⚠️ IMPORTANT**: Do not preserve any of the existing boilerplate UI, components, or content. The user expects a completely fresh application that implements their requirements from scratch. Any remnants of the original boilerplate (like setup checklists, welcome screens, demo content, or placeholder navigation) indicate incomplete implementation.
**Success Criteria**: The final application should look and function as if it was built from scratch for the specific use case, with no evidence of the original boilerplate template.
## Post-Implementation Documentation
-
After completing the implementation, you MUST document any new features or significant changes in the `/docs/features/` directory:
1. **Create Feature Documentation**: For each major feature implemented, create a markdown file in `/docs/features/` that explains:
-
- What the feature does
- How it works
- Key components and files involved
@@ -92,3 +189,5 @@ After completing the implementation, you MUST document any new features or signi
3. **Document Design Decisions**: Include any important architectural or design decisions made during implementation.
This documentation helps maintain the project and assists future developers working with the codebase.
+
+Think hard about the solution and implementing the user's requirements.
\ No newline at end of file
diff --git a/drizzle/0000_overrated_greymalkin.sql b/drizzle/0000_overrated_greymalkin.sql
new file mode 100644
index 0000000..998297e
--- /dev/null
+++ b/drizzle/0000_overrated_greymalkin.sql
@@ -0,0 +1,50 @@
+CREATE TABLE "account" (
+ "id" text PRIMARY KEY NOT NULL,
+ "accountId" text NOT NULL,
+ "providerId" text NOT NULL,
+ "userId" text NOT NULL,
+ "accessToken" text,
+ "refreshToken" text,
+ "idToken" text,
+ "accessTokenExpiresAt" timestamp,
+ "refreshTokenExpiresAt" timestamp,
+ "scope" text,
+ "password" text,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "session" (
+ "id" text PRIMARY KEY NOT NULL,
+ "expiresAt" timestamp NOT NULL,
+ "token" text NOT NULL,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ "ipAddress" text,
+ "userAgent" text,
+ "userId" text NOT NULL,
+ CONSTRAINT "session_token_unique" UNIQUE("token")
+);
+--> statement-breakpoint
+CREATE TABLE "user" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "email" text NOT NULL,
+ "emailVerified" boolean,
+ "image" text,
+ "createdAt" timestamp DEFAULT now() NOT NULL,
+ "updatedAt" timestamp DEFAULT now() NOT NULL,
+ CONSTRAINT "user_email_unique" UNIQUE("email")
+);
+--> statement-breakpoint
+CREATE TABLE "verification" (
+ "id" text PRIMARY KEY NOT NULL,
+ "identifier" text NOT NULL,
+ "value" text NOT NULL,
+ "expiresAt" timestamp NOT NULL,
+ "createdAt" timestamp DEFAULT now(),
+ "updatedAt" timestamp DEFAULT now()
+);
+--> statement-breakpoint
+ALTER TABLE "account" ADD CONSTRAINT "account_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "session" ADD CONSTRAINT "session_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/drizzle/0001_empty_junta.sql b/drizzle/0001_empty_junta.sql
new file mode 100644
index 0000000..e0e4cc1
--- /dev/null
+++ b/drizzle/0001_empty_junta.sql
@@ -0,0 +1,117 @@
+CREATE TABLE "budget" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "amount" numeric(15, 2) NOT NULL,
+ "period" text NOT NULL,
+ "category_id" text,
+ "user_id" text NOT NULL,
+ "is_active" boolean DEFAULT true NOT NULL,
+ "alert_threshold" numeric(5, 2) DEFAULT '80.00',
+ "rollover_unused" boolean DEFAULT false NOT NULL,
+ "start_date" timestamp NOT NULL,
+ "end_date" timestamp,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "category" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "type" text NOT NULL,
+ "color" text DEFAULT '#3B82F6' NOT NULL,
+ "icon" text,
+ "parent_id" text,
+ "user_id" text NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "financial_account" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "type" text NOT NULL,
+ "balance" numeric(15, 2) DEFAULT '0.00' NOT NULL,
+ "currency" text DEFAULT 'EUR' NOT NULL,
+ "is_active" boolean DEFAULT true NOT NULL,
+ "account_number" text,
+ "bank_name" text,
+ "user_id" text NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "financial_insight" (
+ "id" text PRIMARY KEY NOT NULL,
+ "type" text NOT NULL,
+ "title" text NOT NULL,
+ "message" text NOT NULL,
+ "severity" text DEFAULT 'info' NOT NULL,
+ "data" jsonb,
+ "is_read" boolean DEFAULT false NOT NULL,
+ "user_id" text NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "goal" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "description" text,
+ "target_amount" numeric(15, 2) NOT NULL,
+ "current_amount" numeric(15, 2) DEFAULT '0.00' NOT NULL,
+ "target_date" timestamp,
+ "type" text NOT NULL,
+ "priority" text DEFAULT 'medium' NOT NULL,
+ "status" text DEFAULT 'active' NOT NULL,
+ "user_id" text NOT NULL,
+ "account_id" text,
+ "is_recurring" boolean DEFAULT false NOT NULL,
+ "recurring_amount" numeric(15, 2),
+ "recurring_interval" text,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "report" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "type" text NOT NULL,
+ "period" text NOT NULL,
+ "start_date" timestamp NOT NULL,
+ "end_date" timestamp NOT NULL,
+ "data" jsonb NOT NULL,
+ "user_id" text NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "transaction" (
+ "id" text PRIMARY KEY NOT NULL,
+ "description" text NOT NULL,
+ "amount" numeric(15, 2) NOT NULL,
+ "type" text NOT NULL,
+ "date" timestamp NOT NULL,
+ "category_id" text,
+ "account_id" text NOT NULL,
+ "user_id" text NOT NULL,
+ "is_recurring" boolean DEFAULT false NOT NULL,
+ "recurring_interval" text,
+ "tags" text[],
+ "notes" text,
+ "merchant" text,
+ "location" text,
+ "is_imported" boolean DEFAULT false NOT NULL,
+ "imported_source" text,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+ALTER TABLE "budget" ADD CONSTRAINT "budget_category_id_category_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."category"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "budget" ADD CONSTRAINT "budget_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "category" ADD CONSTRAINT "category_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "financial_account" ADD CONSTRAINT "financial_account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "financial_insight" ADD CONSTRAINT "financial_insight_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "goal" ADD CONSTRAINT "goal_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "goal" ADD CONSTRAINT "goal_account_id_financial_account_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."financial_account"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "report" ADD CONSTRAINT "report_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "transaction" ADD CONSTRAINT "transaction_category_id_category_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."category"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "transaction" ADD CONSTRAINT "transaction_account_id_financial_account_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."financial_account"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "transaction" ADD CONSTRAINT "transaction_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json
new file mode 100644
index 0000000..ec4b53d
--- /dev/null
+++ b/drizzle/meta/0000_snapshot.json
@@ -0,0 +1,327 @@
+{
+ "id": "be34f81d-44be-4f79-8f27-01eebb851a6d",
+ "prevId": "00000000-0000-0000-0000-000000000000",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "accountId": {
+ "name": "accountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "providerId": {
+ "name": "providerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accessToken": {
+ "name": "accessToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refreshToken": {
+ "name": "refreshToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "idToken": {
+ "name": "idToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accessTokenExpiresAt": {
+ "name": "accessTokenExpiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refreshTokenExpiresAt": {
+ "name": "refreshTokenExpiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "ipAddress": {
+ "name": "ipAddress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userAgent": {
+ "name": "userAgent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json
new file mode 100644
index 0000000..8ac2f38
--- /dev/null
+++ b/drizzle/meta/0001_snapshot.json
@@ -0,0 +1,1105 @@
+{
+ "id": "a2bc17e8-1238-402e-ab27-8819975ac470",
+ "prevId": "be34f81d-44be-4f79-8f27-01eebb851a6d",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "accountId": {
+ "name": "accountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "providerId": {
+ "name": "providerId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accessToken": {
+ "name": "accessToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refreshToken": {
+ "name": "refreshToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "idToken": {
+ "name": "idToken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accessTokenExpiresAt": {
+ "name": "accessTokenExpiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refreshTokenExpiresAt": {
+ "name": "refreshTokenExpiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.budget": {
+ "name": "budget",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount": {
+ "name": "amount",
+ "type": "numeric(15, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period": {
+ "name": "period",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "category_id": {
+ "name": "category_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "alert_threshold": {
+ "name": "alert_threshold",
+ "type": "numeric(5, 2)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'80.00'"
+ },
+ "rollover_unused": {
+ "name": "rollover_unused",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "start_date": {
+ "name": "start_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_date": {
+ "name": "end_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "budget_category_id_category_id_fk": {
+ "name": "budget_category_id_category_id_fk",
+ "tableFrom": "budget",
+ "tableTo": "category",
+ "columnsFrom": [
+ "category_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "budget_user_id_user_id_fk": {
+ "name": "budget_user_id_user_id_fk",
+ "tableFrom": "budget",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.category": {
+ "name": "category",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "color": {
+ "name": "color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#3B82F6'"
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parent_id": {
+ "name": "parent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "category_user_id_user_id_fk": {
+ "name": "category_user_id_user_id_fk",
+ "tableFrom": "category",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.financial_account": {
+ "name": "financial_account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "balance": {
+ "name": "balance",
+ "type": "numeric(15, 2)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00'"
+ },
+ "currency": {
+ "name": "currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'EUR'"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "account_number": {
+ "name": "account_number",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "bank_name": {
+ "name": "bank_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "financial_account_user_id_user_id_fk": {
+ "name": "financial_account_user_id_user_id_fk",
+ "tableFrom": "financial_account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.financial_insight": {
+ "name": "financial_insight",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "severity": {
+ "name": "severity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'info'"
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_read": {
+ "name": "is_read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "financial_insight_user_id_user_id_fk": {
+ "name": "financial_insight_user_id_user_id_fk",
+ "tableFrom": "financial_insight",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.goal": {
+ "name": "goal",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_amount": {
+ "name": "target_amount",
+ "type": "numeric(15, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "current_amount": {
+ "name": "current_amount",
+ "type": "numeric(15, 2)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0.00'"
+ },
+ "target_date": {
+ "name": "target_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "priority": {
+ "name": "priority",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'medium'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_recurring": {
+ "name": "is_recurring",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "recurring_amount": {
+ "name": "recurring_amount",
+ "type": "numeric(15, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "recurring_interval": {
+ "name": "recurring_interval",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "goal_user_id_user_id_fk": {
+ "name": "goal_user_id_user_id_fk",
+ "tableFrom": "goal",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "goal_account_id_financial_account_id_fk": {
+ "name": "goal_account_id_financial_account_id_fk",
+ "tableFrom": "goal",
+ "tableTo": "financial_account",
+ "columnsFrom": [
+ "account_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.report": {
+ "name": "report",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period": {
+ "name": "period",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "start_date": {
+ "name": "start_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_date": {
+ "name": "end_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "report_user_id_user_id_fk": {
+ "name": "report_user_id_user_id_fk",
+ "tableFrom": "report",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "ipAddress": {
+ "name": "ipAddress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userAgent": {
+ "name": "userAgent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction": {
+ "name": "transaction",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount": {
+ "name": "amount",
+ "type": "numeric(15, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "date": {
+ "name": "date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "category_id": {
+ "name": "category_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_recurring": {
+ "name": "is_recurring",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "recurring_interval": {
+ "name": "recurring_interval",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tags": {
+ "name": "tags",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "merchant": {
+ "name": "merchant",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_imported": {
+ "name": "is_imported",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "imported_source": {
+ "name": "imported_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "transaction_category_id_category_id_fk": {
+ "name": "transaction_category_id_category_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "category",
+ "columnsFrom": [
+ "category_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "transaction_account_id_financial_account_id_fk": {
+ "name": "transaction_account_id_financial_account_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "financial_account",
+ "columnsFrom": [
+ "account_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transaction_user_id_user_id_fk": {
+ "name": "transaction_user_id_user_id_fk",
+ "tableFrom": "transaction",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expiresAt": {
+ "name": "expiresAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
new file mode 100644
index 0000000..a2bd394
--- /dev/null
+++ b/drizzle/meta/_journal.json
@@ -0,0 +1,20 @@
+{
+ "version": "7",
+ "dialect": "postgresql",
+ "entries": [
+ {
+ "idx": 0,
+ "version": "7",
+ "when": 1759140549549,
+ "tag": "0000_overrated_greymalkin",
+ "breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "7",
+ "when": 1759142395943,
+ "tag": "0001_empty_junta",
+ "breakpoints": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/example.env b/example.env
deleted file mode 100644
index c84a1fb..0000000
--- a/example.env
+++ /dev/null
@@ -1,23 +0,0 @@
-# Database
-POSTGRES_URL=postgresql://dev_user:dev_password@localhost:5432/agentic_coding_dev
-
-# Authentication - Better Auth
-# Generate key using https://www.better-auth.com/docs/installation
-BETTER_AUTH_SECRET=qtD4Ssa0t5jY7ewALgai97sKhAtn7Ysc
-
-# Google OAuth (Get from Google Cloud Console)
-GOOGLE_CLIENT_ID=
-GOOGLE_CLIENT_SECRET=
-
-# AI Integration (Optional - for chat functionality)
-OPENAI_API_KEY=
-OPENAI_MODEL="gpt-5-mini"
-
-# Optional - for vector search only
-OPENAI_EMBEDDING_MODEL="text-embedding-3-large"
-
-# App URL (for production deployments)
-NEXT_PUBLIC_APP_URL="http://localhost:3000"
-
-# File storage (optional - if app required file uploads)
-BLOB_READ_WRITE_TOKEN=
diff --git a/package.json b/package.json
index df8aa84..5c63745 100644
--- a/package.json
+++ b/package.json
@@ -18,14 +18,20 @@
"dependencies": {
"@ai-sdk/openai": "^2.0.9",
"@ai-sdk/react": "^2.0.9",
+ "@hookform/resolvers": "^5.2.2",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.16",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-tabs": "^1.1.13",
+ "@types/uuid": "^11.0.0",
"ai": "^5.0.9",
"better-auth": "^1.3.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "date-fns": "^4.1.0",
"drizzle-orm": "^0.44.4",
"lucide-react": "^0.539.0",
"next": "15.4.6",
@@ -34,8 +40,11 @@
"postgres": "^3.4.7",
"react": "19.1.0",
"react-dom": "19.1.0",
+ "react-hook-form": "^7.63.0",
"react-markdown": "^10.1.0",
+ "recharts": "^3.2.1",
"tailwind-merge": "^3.3.1",
+ "uuid": "^13.0.0",
"zod": "^4.0.17"
},
"devDependencies": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 76b28d9..1f5bc0b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
'@ai-sdk/react':
specifier: ^2.0.9
version: 2.0.9(react@19.1.0)(zod@4.0.17)
+ '@hookform/resolvers':
+ specifier: ^5.2.2
+ version: 5.2.2(react-hook-form@7.63.0(react@19.1.0))
'@radix-ui/react-avatar':
specifier: ^1.1.10
version: 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -23,9 +26,21 @@ importers:
'@radix-ui/react-dropdown-menu':
specifier: ^2.1.16
version: 2.1.16(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-label':
+ specifier: ^2.1.7
+ version: 2.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-select':
+ specifier: ^2.2.6
+ version: 2.2.6(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-slot':
specifier: ^1.2.3
version: 1.2.3(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-tabs':
+ specifier: ^1.1.13
+ version: 1.1.13(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@types/uuid':
+ specifier: ^11.0.0
+ version: 11.0.0
ai:
specifier: ^5.0.9
version: 5.0.9(zod@4.0.17)
@@ -38,6 +53,9 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ date-fns:
+ specifier: ^4.1.0
+ version: 4.1.0
drizzle-orm:
specifier: ^0.44.4
version: 0.44.4(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
@@ -62,12 +80,21 @@ importers:
react-dom:
specifier: 19.1.0
version: 19.1.0(react@19.1.0)
+ react-hook-form:
+ specifier: ^7.63.0
+ version: 7.63.0(react@19.1.0)
react-markdown:
specifier: ^10.1.0
version: 10.1.0(@types/react@19.1.9)(react@19.1.0)
+ recharts:
+ specifier: ^3.2.1
+ version: 3.2.1(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(redux@5.0.1)
tailwind-merge:
specifier: ^3.3.1
version: 3.3.1
+ uuid:
+ specifier: ^13.0.0
+ version: 13.0.0
zod:
specifier: ^4.0.17
version: 4.0.17
@@ -658,6 +685,11 @@ packages:
'@hexagon/base64@1.1.28':
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
+ '@hookform/resolvers@5.2.2':
+ resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
+ peerDependencies:
+ react-hook-form: ^7.55.0
+
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'}
@@ -983,6 +1015,9 @@ packages:
'@peculiar/asn1-x509@2.4.0':
resolution: {integrity: sha512-F7mIZY2Eao2TaoVqigGMLv+NDdpwuBKU1fucHPONfzaBS4JXXCNCmfO0Z3dsy7JzKGqtDcYC1mr9JjaZQZNiuw==}
+ '@radix-ui/number@1.1.1':
+ resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
+
'@radix-ui/primitive@1.1.2':
resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
@@ -1147,6 +1182,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-label@2.1.7':
+ resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-menu@2.1.16':
resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==}
peerDependencies:
@@ -1238,6 +1286,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-select@2.2.6':
+ resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-slot@1.2.3':
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
peerDependencies:
@@ -1247,6 +1308,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-tabs@1.1.13':
+ resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
@@ -1301,6 +1375,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-previous@1.1.1':
+ resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-rect@1.1.1':
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
peerDependencies:
@@ -1319,9 +1402,33 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-visually-hidden@1.2.3':
+ resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
+ '@reduxjs/toolkit@2.9.0':
+ resolution: {integrity: sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==}
+ peerDependencies:
+ react: ^16.9.0 || ^17.0.0 || ^18 || ^19
+ react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-redux:
+ optional: true
+
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
@@ -1345,6 +1452,9 @@ packages:
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
+ '@standard-schema/utils@0.3.0':
+ resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@@ -1445,6 +1555,33 @@ packages:
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-shape@3.1.7':
+ resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -1495,6 +1632,13 @@ packages:
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+ '@types/use-sync-external-store@0.0.6':
+ resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
+
+ '@types/uuid@11.0.0':
+ resolution: {integrity: sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==}
+ deprecated: This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.
+
'@typescript-eslint/eslint-plugin@8.39.0':
resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1960,6 +2104,50 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.0:
+ resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -1979,6 +2167,9 @@ packages:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
+ date-fns@4.1.0:
+ resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
+
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
@@ -1996,6 +2187,9 @@ packages:
supports-color:
optional: true
+ decimal.js-light@2.5.1:
+ resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
+
decode-named-character-reference@1.2.0:
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
@@ -2221,6 +2415,9 @@ packages:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
+ es-toolkit@1.39.10:
+ resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==}
+
esbuild-register@3.6.0:
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
peerDependencies:
@@ -2375,6 +2572,9 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
+ eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
eventsource-parser@3.0.3:
resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==}
engines: {node: '>=20.0.0'}
@@ -2639,6 +2839,9 @@ packages:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'}
+ immer@10.1.3:
+ resolution: {integrity: sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==}
+
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -2657,6 +2860,10 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@@ -3509,6 +3716,12 @@ packages:
peerDependencies:
react: ^19.1.0
+ react-hook-form@7.63.0:
+ resolution: {integrity: sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -3518,6 +3731,18 @@ packages:
'@types/react': '>=18'
react: '>=18'
+ react-redux@9.2.0:
+ resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==}
+ peerDependencies:
+ '@types/react': ^18.2.25 || ^19
+ react: ^18.0 || ^19
+ redux: ^5.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ redux:
+ optional: true
+
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
@@ -3556,6 +3781,22 @@ packages:
resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==}
engines: {node: '>= 4'}
+ recharts@3.2.1:
+ resolution: {integrity: sha512-0JKwHRiFZdmLq/6nmilxEZl3pqb4T+aKkOkOi/ZISRZwfBhVMgInxzlYU9D4KnCH3KINScLy68m/OvMXoYGZUw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ redux-thunk@3.1.0:
+ resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
+ peerDependencies:
+ redux: ^5.0.0
+
+ redux@5.0.1:
+ resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
+
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@@ -3577,6 +3818,9 @@ packages:
requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+ reselect@5.1.1:
+ resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
+
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -4035,6 +4279,10 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ uuid@13.0.0:
+ resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==}
+ hasBin: true
+
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
@@ -4045,6 +4293,9 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ victory-vendor@37.3.6:
+ resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
+
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
@@ -4622,6 +4873,11 @@ snapshots:
'@hexagon/base64@1.1.28': {}
+ '@hookform/resolvers@5.2.2(react-hook-form@7.63.0(react@19.1.0))':
+ dependencies:
+ '@standard-schema/utils': 0.3.0
+ react-hook-form: 7.63.0(react@19.1.0)
+
'@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.6':
@@ -4904,6 +5160,8 @@ snapshots:
pvtsutils: 1.3.6
tslib: 2.8.1
+ '@radix-ui/number@1.1.1': {}
+
'@radix-ui/primitive@1.1.2': {}
'@radix-ui/primitive@1.1.3': {}
@@ -5053,6 +5311,15 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.9
+ '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.9
+ '@types/react-dom': 19.1.7(@types/react@19.1.9)
+
'@radix-ui/react-menu@2.1.16(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -5153,6 +5420,35 @@ snapshots:
'@types/react': 19.1.9
'@types/react-dom': 19.1.7(@types/react@19.1.9)
+ '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.1
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ aria-hidden: 1.2.6
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ react-remove-scroll: 2.7.1(@types/react@19.1.9)(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.9
+ '@types/react-dom': 19.1.7(@types/react@19.1.9)
+
'@radix-ui/react-slot@1.2.3(@types/react@19.1.9)(react@19.1.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0)
@@ -5160,6 +5456,22 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.9
+ '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.9
+ '@types/react-dom': 19.1.7(@types/react@19.1.9)
+
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.9)(react@19.1.0)':
dependencies:
react: 19.1.0
@@ -5201,6 +5513,12 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.9
+ '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.9)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.9
+
'@radix-ui/react-use-rect@1.1.1(@types/react@19.1.9)(react@19.1.0)':
dependencies:
'@radix-ui/rect': 1.1.1
@@ -5215,8 +5533,29 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.9
+ '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.9
+ '@types/react-dom': 19.1.7(@types/react@19.1.9)
+
'@radix-ui/rect@1.1.1': {}
+ '@reduxjs/toolkit@2.9.0(react-redux@9.2.0(@types/react@19.1.9)(react@19.1.0)(redux@5.0.1))(react@19.1.0)':
+ dependencies:
+ '@standard-schema/spec': 1.0.0
+ '@standard-schema/utils': 0.3.0
+ immer: 10.1.3
+ redux: 5.0.1
+ redux-thunk: 3.1.0(redux@5.0.1)
+ reselect: 5.1.1
+ optionalDependencies:
+ react: 19.1.0
+ react-redux: 9.2.0(@types/react@19.1.9)(react@19.1.0)(redux@5.0.1)
+
'@rtsao/scc@1.1.0': {}
'@rushstack/eslint-patch@1.12.0': {}
@@ -5239,6 +5578,8 @@ snapshots:
'@standard-schema/spec@1.0.0': {}
+ '@standard-schema/utils@0.3.0': {}
+
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
@@ -5328,6 +5669,30 @@ snapshots:
'@types/cookie@0.6.0': {}
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-shape@3.1.7':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
'@types/debug@4.1.12':
dependencies:
'@types/ms': 2.1.0
@@ -5378,6 +5743,12 @@ snapshots:
'@types/unist@3.0.3': {}
+ '@types/use-sync-external-store@0.0.6': {}
+
+ '@types/uuid@11.0.0':
+ dependencies:
+ uuid: 13.0.0
+
'@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@@ -5864,6 +6235,44 @@ snapshots:
csstype@3.1.3: {}
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-color@3.1.0: {}
+
+ d3-ease@3.0.1: {}
+
+ d3-format@3.1.0: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@3.1.0: {}
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.0
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-timer@3.0.1: {}
+
damerau-levenshtein@1.0.8: {}
data-uri-to-buffer@4.0.1: {}
@@ -5886,6 +6295,8 @@ snapshots:
es-errors: 1.3.0
is-data-view: 1.0.2
+ date-fns@4.1.0: {}
+
debug@3.2.7:
dependencies:
ms: 2.1.3
@@ -5894,6 +6305,8 @@ snapshots:
dependencies:
ms: 2.1.3
+ decimal.js-light@2.5.1: {}
+
decode-named-character-reference@1.2.0:
dependencies:
character-entities: 2.0.2
@@ -6092,6 +6505,8 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
+ es-toolkit@1.39.10: {}
+
esbuild-register@3.6.0(esbuild@0.25.8):
dependencies:
debug: 4.4.1
@@ -6362,6 +6777,8 @@ snapshots:
etag@1.8.1: {}
+ eventemitter3@5.0.1: {}
+
eventsource-parser@3.0.3: {}
eventsource@3.0.7:
@@ -6683,6 +7100,8 @@ snapshots:
ignore@7.0.5: {}
+ immer@10.1.3: {}
+
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -6700,6 +7119,8 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
+ internmap@2.0.3: {}
+
ipaddr.js@1.9.1: {}
is-alphabetical@2.0.1: {}
@@ -7625,6 +8046,10 @@ snapshots:
react: 19.1.0
scheduler: 0.26.0
+ react-hook-form@7.63.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
react-is@16.13.1: {}
react-markdown@10.1.0(@types/react@19.1.9)(react@19.1.0):
@@ -7645,6 +8070,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ react-redux@9.2.0(@types/react@19.1.9)(react@19.1.0)(redux@5.0.1):
+ dependencies:
+ '@types/use-sync-external-store': 0.0.6
+ react: 19.1.0
+ use-sync-external-store: 1.5.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.9
+ redux: 5.0.1
+
react-remove-scroll-bar@2.3.8(@types/react@19.1.9)(react@19.1.0):
dependencies:
react: 19.1.0
@@ -7682,6 +8116,32 @@ snapshots:
tiny-invariant: 1.3.3
tslib: 2.8.1
+ recharts@3.2.1(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(redux@5.0.1):
+ dependencies:
+ '@reduxjs/toolkit': 2.9.0(react-redux@9.2.0(@types/react@19.1.9)(react@19.1.0)(redux@5.0.1))(react@19.1.0)
+ clsx: 2.1.1
+ decimal.js-light: 2.5.1
+ es-toolkit: 1.39.10
+ eventemitter3: 5.0.1
+ immer: 10.1.3
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ react-is: 16.13.1
+ react-redux: 9.2.0(@types/react@19.1.9)(react@19.1.0)(redux@5.0.1)
+ reselect: 5.1.1
+ tiny-invariant: 1.3.3
+ use-sync-external-store: 1.5.0(react@19.1.0)
+ victory-vendor: 37.3.6
+ transitivePeerDependencies:
+ - '@types/react'
+ - redux
+
+ redux-thunk@3.1.0(redux@5.0.1):
+ dependencies:
+ redux: 5.0.1
+
+ redux@5.0.1: {}
+
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
@@ -7723,6 +8183,8 @@ snapshots:
requires-port@1.0.0: {}
+ reselect@5.1.1: {}
+
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -8325,6 +8787,8 @@ snapshots:
dependencies:
react: 19.1.0
+ uuid@13.0.0: {}
+
vary@1.1.2: {}
vfile-message@4.0.3:
@@ -8337,6 +8801,23 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
+ victory-vendor@37.3.6:
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-ease': 3.0.2
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-scale': 4.0.9
+ '@types/d3-shape': 3.1.7
+ '@types/d3-time': 3.0.4
+ '@types/d3-timer': 3.0.2
+ d3-array: 3.2.4
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-scale: 4.0.2
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-timer: 3.0.1
+
web-streams-polyfill@3.3.3: {}
which-boxed-primitive@1.1.1:
diff --git a/src/app/api/analytics/overview/route.ts b/src/app/api/analytics/overview/route.ts
new file mode 100644
index 0000000..9e0cc63
--- /dev/null
+++ b/src/app/api/analytics/overview/route.ts
@@ -0,0 +1,125 @@
+import { NextRequest, NextResponse } from "next/server";
+
+// Demo analytics data for MoneyMind
+const demoAnalytics = {
+ metrics: {
+ currentBalance: 15420.50,
+ totalIncome: 3500.00,
+ totalExpenses: 2180.75,
+ netCashFlow: 1319.25,
+ },
+ categoryBreakdown: {
+ expenses: [
+ { category: "Housing", total: 850.00, percentage: 39 },
+ { category: "Food", total: 420.50, percentage: 19 },
+ { category: "Transport", total: 280.25, percentage: 13 },
+ { category: "Entertainment", total: 180.00, percentage: 8 },
+ { category: "Utilities", total: 250.00, percentage: 11 },
+ { category: "Other", total: 200.00, percentage: 10 },
+ ],
+ income: [
+ { category: "Salary", total: 3000.00, percentage: 86 },
+ { category: "Freelance", total: 500.00, percentage: 14 },
+ ]
+ },
+ monthlyTrends: [
+ {
+ month: "Apr 2025",
+ income: 3200.00,
+ expenses: 2100.00,
+ balance: 1100.00
+ },
+ {
+ month: "May 2025",
+ income: 3400.00,
+ expenses: 2250.00,
+ balance: 1150.00
+ },
+ {
+ month: "Jun 2025",
+ income: 3300.00,
+ expenses: 2050.00,
+ balance: 1250.00
+ },
+ {
+ month: "Jul 2025",
+ income: 3500.00,
+ expenses: 2180.75,
+ balance: 1319.25
+ },
+ ],
+ budgetStatus: [
+ {
+ id: "1",
+ name: "Monthly Food Budget",
+ categoryName: "Food",
+ amount: 500.00,
+ spent: 420.50,
+ remaining: 79.50,
+ percentage: 84.1,
+ },
+ {
+ id: "2",
+ name: "Entertainment Budget",
+ categoryName: "Entertainment",
+ amount: 200.00,
+ spent: 180.00,
+ remaining: 20.00,
+ percentage: 90.0,
+ },
+ {
+ id: "3",
+ name: "Transport Budget",
+ categoryName: "Transport",
+ amount: 300.00,
+ spent: 280.25,
+ remaining: 19.75,
+ percentage: 93.4,
+ },
+ ],
+ goalProgress: [
+ {
+ id: "1",
+ name: "Emergency Fund",
+ priority: "high",
+ targetAmount: 10000.00,
+ currentAmount: 7500.00,
+ remaining: 2500.00,
+ percentage: 75.0,
+ targetDate: "2025-12-31",
+ },
+ {
+ id: "2",
+ name: "New Car",
+ priority: "medium",
+ targetAmount: 25000.00,
+ currentAmount: 8500.00,
+ remaining: 16500.00,
+ percentage: 34.0,
+ targetDate: "2026-06-30",
+ },
+ {
+ id: "3",
+ name: "Vacation Fund",
+ priority: "low",
+ targetAmount: 5000.00,
+ currentAmount: 1200.00,
+ remaining: 3800.00,
+ percentage: 24.0,
+ targetDate: "2025-12-15",
+ },
+ ],
+};
+
+export async function GET(request: NextRequest) {
+ try {
+ // Return demo analytics data
+ return NextResponse.json(demoAnalytics);
+ } catch (error) {
+ console.error("Error fetching analytics:", error);
+ return NextResponse.json(
+ { error: "Failed to fetch analytics" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/auth/[...all]/route.ts b/src/app/api/auth/[...all]/route.ts
index 81724a0..3d01f39 100644
--- a/src/app/api/auth/[...all]/route.ts
+++ b/src/app/api/auth/[...all]/route.ts
@@ -1,4 +1,20 @@
-import { auth } from "@/lib/auth"
-import { toNextJsHandler } from "better-auth/next-js"
+import { NextRequest, NextResponse } from "next/server";
+import { auth } from "@/lib/auth";
-export const { GET, POST } = toNextJsHandler(auth)
\ No newline at end of file
+export async function GET(request: NextRequest) {
+ try {
+ return await auth.handler(request);
+ } catch (error) {
+ console.error("Auth handler error:", error);
+ return NextResponse.json({ error: "Authentication error" }, { status: 500 });
+ }
+}
+
+export async function POST(request: NextRequest) {
+ try {
+ return await auth.handler(request);
+ } catch (error) {
+ console.error("Auth handler error:", error);
+ return NextResponse.json({ error: "Authentication error" }, { status: 500 });
+ }
+}
diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts
new file mode 100644
index 0000000..5afc8f6
--- /dev/null
+++ b/src/app/api/auth/logout/route.ts
@@ -0,0 +1,22 @@
+import { NextResponse } from "next/server";
+
+export async function POST() {
+ try {
+ // In a real app, this would use Better Auth to properly end the session
+ // For demo purposes, we'll just return success
+
+ // Clear any client-side session cookies or tokens
+ const response = NextResponse.json({ success: true });
+
+ // Clear cookies if they exist
+ response.cookies.delete('better-auth.session_token');
+
+ return response;
+ } catch (error) {
+ console.error("Logout error:", error);
+ return NextResponse.json(
+ { error: "Failed to logout" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/auth/session/route.ts b/src/app/api/auth/session/route.ts
new file mode 100644
index 0000000..5b72013
--- /dev/null
+++ b/src/app/api/auth/session/route.ts
@@ -0,0 +1,37 @@
+import { NextResponse } from "next/server";
+
+// Mock session data for demo purposes
+// In a real app, this would check the actual Better Auth session
+const mockSession = {
+ user: {
+ id: "demo-user-123",
+ name: "Demo User",
+ email: "demo@example.com",
+ emailVerified: true,
+ image: "https://via.placeholder.com/40"
+ },
+ expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
+};
+
+export async function GET(request: Request) {
+ try {
+ // For demo purposes, we'll always return an authenticated user
+ // This simulates a successful OAuth login scenario
+
+ // In a real implementation, you would:
+ // 1. Check for valid session cookies/tokens
+ // 2. Validate the session with Better Auth
+ // 3. Return the actual user data
+
+ return NextResponse.json({
+ session: mockSession,
+ user: mockSession.user
+ });
+ } catch (error) {
+ console.error("Session check error:", error);
+ return NextResponse.json(
+ { error: "Failed to check session" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/transactions/[id]/route.ts b/src/app/api/transactions/[id]/route.ts
new file mode 100644
index 0000000..d08fa51
--- /dev/null
+++ b/src/app/api/transactions/[id]/route.ts
@@ -0,0 +1,99 @@
+import { NextRequest, NextResponse } from "next/server";
+
+// GET /api/transactions/[id] - Get a specific transaction
+export async function GET(
+ request: NextRequest,
+ context: { params: Promise<{ id: string }> }
+) {
+ const params = await context.params;
+ try {
+ // Return demo transaction
+ const demoTransaction = {
+ id: params.id,
+ description: "Sample Transaction",
+ amount: "100.00",
+ type: "expense",
+ date: new Date().toISOString(),
+ categoryId: "sample",
+ accountId: "account1",
+ userId: "demo-user",
+ isRecurring: false,
+ recurringInterval: null,
+ tags: [],
+ notes: "Sample transaction",
+ merchant: "Sample Merchant",
+ location: "Sample Location",
+ isImported: false,
+ importedSource: null,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ };
+
+ return NextResponse.json(demoTransaction);
+ } catch (error) {
+ console.error("Error fetching transaction:", error);
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 }
+ );
+ }
+}
+
+// PUT /api/transactions/[id] - Update a transaction
+export async function PUT(
+ request: NextRequest,
+ context: { params: Promise<{ id: string }> }
+) {
+ const params = await context.params;
+ try {
+ const body = await request.json();
+
+ // Return updated demo transaction
+ const updatedTransaction = {
+ id: params.id,
+ description: body.description || "Updated Transaction",
+ amount: body.amount?.toString() || "0.00",
+ type: body.type || "expense",
+ date: body.date || new Date().toISOString().split('T')[0],
+ categoryId: body.categoryId || "",
+ accountId: body.accountId || "default-account",
+ userId: "demo-user",
+ isRecurring: body.isRecurring || false,
+ recurringInterval: body.recurringInterval || null,
+ tags: body.tags || [],
+ notes: body.notes || "",
+ merchant: body.merchant || "",
+ location: body.location || "",
+ isImported: false,
+ importedSource: null,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ };
+
+ return NextResponse.json(updatedTransaction);
+ } catch (error) {
+ console.error("Error updating transaction:", error);
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 }
+ );
+ }
+}
+
+// DELETE /api/transactions/[id] - Delete a transaction
+export async function DELETE(
+ request: NextRequest,
+ context: { params: Promise<{ id: string }> }
+) {
+ const params = await context.params;
+ try {
+ // Just return success for demo
+ return NextResponse.json({ message: "Transaction deleted successfully" });
+ } catch (error) {
+ console.error("Error deleting transaction:", error);
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/api/transactions/route.ts b/src/app/api/transactions/route.ts
new file mode 100644
index 0000000..4700f84
--- /dev/null
+++ b/src/app/api/transactions/route.ts
@@ -0,0 +1,103 @@
+import { NextRequest, NextResponse } from "next/server";
+
+// GET /api/transactions - Get transactions for demo
+export async function GET(request: NextRequest) {
+ try {
+ // Return demo transactions
+ const demoTransactions = [
+ {
+ id: "1",
+ description: "Grocery Shopping",
+ amount: "85.50",
+ type: "expense",
+ date: new Date().toISOString(),
+ categoryId: "food",
+ accountId: "account1",
+ userId: "demo-user",
+ isRecurring: false,
+ recurringInterval: null,
+ tags: ["food", "groceries"],
+ notes: "Weekly grocery shopping",
+ merchant: "Supermarket",
+ location: "Rome",
+ isImported: false,
+ importedSource: null,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ },
+ {
+ id: "2",
+ description: "Salary",
+ amount: "2500.00",
+ type: "income",
+ date: new Date().toISOString(),
+ categoryId: "salary",
+ accountId: "account1",
+ userId: "demo-user",
+ isRecurring: true,
+ recurringInterval: "monthly",
+ tags: ["salary", "work"],
+ notes: "Monthly salary",
+ merchant: "Company",
+ location: null,
+ isImported: false,
+ importedSource: null,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ },
+ ];
+
+ return NextResponse.json({
+ transactions: demoTransactions,
+ pagination: {
+ page: 1,
+ limit: 20,
+ total: 2,
+ totalPages: 1,
+ },
+ });
+ } catch (error) {
+ console.error("Error fetching transactions:", error);
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 }
+ );
+ }
+}
+
+// POST /api/transactions - Create a new transaction
+export async function POST(request: NextRequest) {
+ try {
+ const body = await request.json();
+
+ // Create a demo transaction
+ const newTransaction = {
+ id: Date.now().toString(),
+ description: body.description || "New Transaction",
+ amount: body.amount?.toString() || "0.00",
+ type: body.type || "expense",
+ date: body.date || new Date().toISOString().split('T')[0],
+ categoryId: body.categoryId || "",
+ accountId: body.accountId || "default-account",
+ userId: "demo-user",
+ isRecurring: body.isRecurring || false,
+ recurringInterval: body.recurringInterval || null,
+ tags: body.tags || [],
+ notes: body.notes || "",
+ merchant: body.merchant || "",
+ location: body.location || "",
+ isImported: false,
+ importedSource: null,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ };
+
+ return NextResponse.json(newTransaction, { status: 201 });
+ } catch (error) {
+ console.error("Error creating transaction:", error);
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 }
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e9dd733..52dbd97 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,6 +4,7 @@ import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { SiteHeader } from "@/components/site-header";
import { SiteFooter } from "@/components/site-footer";
+import { AuthProvider } from "@/hooks/use-auth";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -16,9 +17,9 @@ const geistMono = Geist_Mono({
});
export const metadata: Metadata = {
- title: "Agentic Coding Boilerplate",
+ title: "MoneyMind - Personal Finance Management",
description:
- "Complete agentic coding boilerplate with authentication, database, AI integration, and modern tooling - perfect for building AI-powered applications and autonomous agents by Leon van Zyl",
+ "Transform your financial data into actionable insights with AI-powered analytics and smart budgeting",
};
export default function RootLayout({
@@ -37,9 +38,11 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
-
- {children}
-
+
+
+ {children}
+
+