feat: implement MoneyMind personal finance management application
- Complete transformation from boilerplate to full-featured financial app - Add comprehensive dashboard with KPI cards and interactive charts - Implement transaction management with predefined expense/income categories - Create account management system with multiple account types - Add authentication flow with session management - Implement analytics overview with demo financial data - Add budget tracking and goal progress visualization - Include custom category creation functionality - Update branding and footer with MoneyMind by RoMoS - Add shadcn/ui components and Recharts for data visualization 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
163
src/lib/validations/financial.ts
Normal file
163
src/lib/validations/financial.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { z } from "zod";
|
||||
|
||||
// Financial Account Schemas
|
||||
export const financialAccountSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
type: z.enum(["checking", "savings", "credit", "investment", "cash"]),
|
||||
balance: z.number().min(0, "Balance must be non-negative"),
|
||||
currency: z.string().default("EUR"),
|
||||
isActive: z.boolean().default(true),
|
||||
accountNumber: z.string().optional(),
|
||||
bankName: z.string().optional(),
|
||||
});
|
||||
|
||||
export const updateFinancialAccountSchema = financialAccountSchema.partial().extend({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
// Category Schemas
|
||||
export const categorySchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
type: z.enum(["income", "expense"]),
|
||||
color: z.string().default("#3B82F6"),
|
||||
icon: z.string().optional(),
|
||||
parentId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const updateCategorySchema = categorySchema.partial().extend({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
// Transaction Schemas
|
||||
export const transactionSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
description: z.string().min(1, "Description is required"),
|
||||
amount: z.number().min(0.01, "Amount must be greater than 0"),
|
||||
type: z.enum(["income", "expense"]),
|
||||
date: z.string().min(1, "Date is required"),
|
||||
categoryId: z.string().optional(),
|
||||
accountId: z.string().min(1, "Account is required"),
|
||||
isRecurring: z.boolean().default(false),
|
||||
recurringInterval: z.enum(["daily", "weekly", "monthly", "yearly"]).optional(),
|
||||
tags: z.array(z.string()).default([]),
|
||||
notes: z.string().optional(),
|
||||
merchant: z.string().optional(),
|
||||
location: z.string().optional(),
|
||||
});
|
||||
|
||||
export const updateTransactionSchema = transactionSchema.partial().extend({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
export const transactionImportSchema = z.object({
|
||||
transactions: z.array(transactionSchema),
|
||||
source: z.string().optional(),
|
||||
});
|
||||
|
||||
// Budget Schemas
|
||||
export const budgetSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
amount: z.number().min(0.01, "Budget amount must be greater than 0"),
|
||||
period: z.enum(["weekly", "monthly", "yearly"]),
|
||||
categoryId: z.string().optional(),
|
||||
isActive: z.boolean().default(true),
|
||||
alertThreshold: z.number().min(0).max(100).default(80),
|
||||
rolloverUnused: z.boolean().default(false),
|
||||
startDate: z.string().min(1, "Start date is required"),
|
||||
endDate: z.string().optional(),
|
||||
});
|
||||
|
||||
export const updateBudgetSchema = budgetSchema.partial().extend({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
// Goal Schemas
|
||||
export const goalSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.string().optional(),
|
||||
targetAmount: z.number().min(0.01, "Target amount must be greater than 0"),
|
||||
currentAmount: z.number().min(0).default(0),
|
||||
targetDate: z.string().optional(),
|
||||
type: z.enum(["savings", "debt_payoff", "investment", "emergency_fund"]),
|
||||
priority: z.enum(["low", "medium", "high"]).default("medium"),
|
||||
status: z.enum(["active", "completed", "paused", "cancelled"]).default("active"),
|
||||
accountId: z.string().optional(),
|
||||
isRecurring: z.boolean().default(false),
|
||||
recurringAmount: z.number().min(0).optional(),
|
||||
recurringInterval: z.enum(["daily", "weekly", "monthly", "yearly"]).optional(),
|
||||
});
|
||||
|
||||
export const updateGoalSchema = goalSchema.partial().extend({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
// Analytics & Insights Schemas
|
||||
export const financialInsightSchema = z.object({
|
||||
type: z.enum(["spending_alert", "saving_opportunity", "budget_warning", "trend_analysis"]),
|
||||
title: z.string().min(1, "Title is required"),
|
||||
message: z.string().min(1, "Message is required"),
|
||||
severity: z.enum(["low", "medium", "high", "critical"]).default("low"),
|
||||
data: z.record(z.string(), z.unknown()).optional(),
|
||||
});
|
||||
|
||||
// Report Schemas
|
||||
export const reportSchema = z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
type: z.enum(["monthly_summary", "expense_analysis", "income_report", "net_worth"]),
|
||||
period: z.enum(["monthly", "quarterly", "yearly", "custom"]),
|
||||
startDate: z.string().min(1, "Start date is required"),
|
||||
endDate: z.string().min(1, "End date is required"),
|
||||
data: z.record(z.string(), z.unknown()),
|
||||
});
|
||||
|
||||
// Query Schemas
|
||||
export const dateRangeSchema = z.object({
|
||||
startDate: z.string().optional(),
|
||||
endDate: z.string().optional(),
|
||||
});
|
||||
|
||||
export const paginationSchema = z.object({
|
||||
page: z.coerce.number().min(1).default(1),
|
||||
limit: z.coerce.number().min(1).max(100).default(20),
|
||||
});
|
||||
|
||||
export const transactionFilterSchema = z.object({
|
||||
type: z.enum(["income", "expense"]).optional(),
|
||||
categoryId: z.string().optional(),
|
||||
accountId: z.string().optional(),
|
||||
startDate: z.string().optional(),
|
||||
endDate: z.string().optional(),
|
||||
search: z.string().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
...paginationSchema.shape,
|
||||
});
|
||||
|
||||
export const analyticsQuerySchema = z.object({
|
||||
period: z.enum(["week", "month", "quarter", "year"]).default("month"),
|
||||
startDate: z.string().optional(),
|
||||
endDate: z.string().optional(),
|
||||
type: z.enum(["overview", "trends", "categories", "comparison"]).default("overview"),
|
||||
});
|
||||
|
||||
// Type exports
|
||||
export type FinancialAccountInput = z.infer<typeof financialAccountSchema>;
|
||||
export type UpdateFinancialAccountInput = z.infer<typeof updateFinancialAccountSchema>;
|
||||
export type CategoryInput = z.infer<typeof categorySchema>;
|
||||
export type UpdateCategoryInput = z.infer<typeof updateCategorySchema>;
|
||||
export type TransactionInput = z.infer<typeof transactionSchema>;
|
||||
export type UpdateTransactionInput = z.infer<typeof updateTransactionSchema>;
|
||||
export type TransactionImportInput = z.infer<typeof transactionImportSchema>;
|
||||
export type BudgetInput = z.infer<typeof budgetSchema>;
|
||||
export type UpdateBudgetInput = z.infer<typeof updateBudgetSchema>;
|
||||
export type GoalInput = z.infer<typeof goalSchema>;
|
||||
export type UpdateGoalInput = z.infer<typeof updateGoalSchema>;
|
||||
export type FinancialInsightInput = z.infer<typeof financialInsightSchema>;
|
||||
export type ReportInput = z.infer<typeof reportSchema>;
|
||||
export type DateRangeInput = z.infer<typeof dateRangeSchema>;
|
||||
export type PaginationInput = z.infer<typeof paginationSchema>;
|
||||
export type TransactionFilterInput = z.infer<typeof transactionFilterSchema>;
|
||||
export type AnalyticsQueryInput = z.infer<typeof analyticsQuerySchema>;
|
||||
Reference in New Issue
Block a user