Refactor: Implement DashboardLayout, fix mobile nav, and resolve scroll issues
This commit is contained in:
39
lib/otp.ts
Normal file
39
lib/otp.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const OTP_FILE = path.join(process.cwd(), 'auth-otp.json');
|
||||
|
||||
interface OTPData {
|
||||
code: string;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
export function generateOTP(): string {
|
||||
return Math.floor(100000 + Math.random() * 900000).toString();
|
||||
}
|
||||
|
||||
export function saveOTP(code: string) {
|
||||
const data: OTPData = {
|
||||
code,
|
||||
expiresAt: Date.now() + 5 * 60 * 1000 // 5 minutes
|
||||
};
|
||||
try {
|
||||
fs.writeFileSync(OTP_FILE, JSON.stringify(data));
|
||||
} catch (err) {
|
||||
console.error("Error saving OTP:", err);
|
||||
}
|
||||
}
|
||||
|
||||
export function verifyOTP(code: string): boolean {
|
||||
if (!fs.existsSync(OTP_FILE)) return false;
|
||||
|
||||
try {
|
||||
const data: OTPData = JSON.parse(fs.readFileSync(OTP_FILE, 'utf8'));
|
||||
if (Date.now() > data.expiresAt) return false;
|
||||
// Simple check
|
||||
return String(data.code).trim() === String(code).trim();
|
||||
} catch (e) {
|
||||
console.error("Error verifying OTP:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
39
lib/server-db.ts
Normal file
39
lib/server-db.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { AppState } from './types';
|
||||
|
||||
const DB_FILE = path.join(process.cwd(), 'data', 'db.json');
|
||||
|
||||
const defaultState: AppState = {
|
||||
fixedDebts: [],
|
||||
variableDebts: [],
|
||||
creditCards: [],
|
||||
cardPayments: [],
|
||||
incomes: [],
|
||||
monthlyBudgets: [],
|
||||
serviceBills: [],
|
||||
alerts: [],
|
||||
currentMonth: new Date().getMonth(),
|
||||
currentYear: new Date().getFullYear(),
|
||||
};
|
||||
|
||||
export function getDatabase(): AppState {
|
||||
if (!fs.existsSync(DB_FILE)) {
|
||||
saveDatabase(defaultState);
|
||||
return defaultState;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(DB_FILE, 'utf8'));
|
||||
} catch (error) {
|
||||
console.error('Database read error:', error);
|
||||
return defaultState;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveDatabase(data: AppState) {
|
||||
try {
|
||||
fs.writeFileSync(DB_FILE, JSON.stringify(data, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Database write error:', error);
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ import { createDebtsSlice, DebtsSlice } from './store/slices/debtsSlice'
|
||||
import { createCardsSlice, CardsSlice } from './store/slices/cardsSlice'
|
||||
import { createBudgetSlice, BudgetSlice } from './store/slices/budgetSlice'
|
||||
import { createAlertsSlice, AlertsSlice } from './store/slices/alertsSlice'
|
||||
import { createIncomesSlice, IncomesSlice } from './store/slices/incomesSlice'
|
||||
|
||||
import { createServicesSlice, ServicesSlice } from './store/slices/servicesSlice'
|
||||
|
||||
// Combined State Interface
|
||||
// Note: We extend the individual slices to create the full store interface
|
||||
export interface FinanzasState extends DebtsSlice, CardsSlice, BudgetSlice, AlertsSlice, ServicesSlice { }
|
||||
export interface FinanzasState extends DebtsSlice, CardsSlice, BudgetSlice, AlertsSlice, ServicesSlice, IncomesSlice { }
|
||||
|
||||
export const useFinanzasStore = create<FinanzasState>()(
|
||||
persist(
|
||||
@@ -20,6 +21,7 @@ export const useFinanzasStore = create<FinanzasState>()(
|
||||
...createBudgetSlice(...a),
|
||||
...createAlertsSlice(...a),
|
||||
...createServicesSlice(...a),
|
||||
...createIncomesSlice(...a),
|
||||
}),
|
||||
{
|
||||
name: 'finanzas-storage',
|
||||
|
||||
33
lib/store/slices/incomesSlice.ts
Normal file
33
lib/store/slices/incomesSlice.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { StateCreator } from 'zustand'
|
||||
import { Income, AppState } from '@/lib/types'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export interface IncomesSlice {
|
||||
incomes: Income[]
|
||||
addIncome: (income: Omit<Income, 'id'>) => void
|
||||
removeIncome: (id: string) => void
|
||||
updateIncome: (id: string, income: Partial<Income>) => void
|
||||
}
|
||||
|
||||
export const createIncomesSlice: StateCreator<
|
||||
AppState & IncomesSlice,
|
||||
[],
|
||||
[],
|
||||
IncomesSlice
|
||||
> = (set) => ({
|
||||
incomes: [],
|
||||
addIncome: (income) =>
|
||||
set((state) => ({
|
||||
incomes: [...(state.incomes || []), { ...income, id: uuidv4() }],
|
||||
})),
|
||||
removeIncome: (id) =>
|
||||
set((state) => ({
|
||||
incomes: state.incomes.filter((i) => i.id !== id),
|
||||
})),
|
||||
updateIncome: (id, updatedIncome) =>
|
||||
set((state) => ({
|
||||
incomes: state.incomes.map((i) =>
|
||||
i.id === id ? { ...i, ...updatedIncome } : i
|
||||
),
|
||||
})),
|
||||
})
|
||||
@@ -74,11 +74,20 @@ export interface ServiceBill {
|
||||
notes?: string
|
||||
}
|
||||
|
||||
export interface Income {
|
||||
id: string
|
||||
amount: number
|
||||
description: string
|
||||
date: string
|
||||
category: 'salary' | 'freelance' | 'business' | 'gift' | 'other'
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
fixedDebts: FixedDebt[]
|
||||
variableDebts: VariableDebt[]
|
||||
creditCards: CreditCard[]
|
||||
cardPayments: CardPayment[]
|
||||
incomes: Income[] // Added incomes
|
||||
monthlyBudgets: MonthlyBudget[]
|
||||
serviceBills: ServiceBill[]
|
||||
alerts: Alert[]
|
||||
|
||||
Reference in New Issue
Block a user