From 9ff4c84d253ef80231a07c03303255df8349fdbf Mon Sep 17 00:00:00 2001 From: Rishabh Mishra Date: Mon, 9 Feb 2026 22:33:07 +0530 Subject: [PATCH] Dockerize MERN Stack Environment --- .dockerignore | 7 + analytics-dashboard.js | 1370 ----- analytics.html | 1117 ----- 2fa-manage.css => client/2fa-manage.css | 0 2fa-manage.html => client/2fa-manage.html | 0 2fa-manage.js => client/2fa-manage.js | 0 2fa-setup.css => client/2fa-setup.css | 0 2fa-setup.html => client/2fa-setup.html | 0 2fa-setup.js => client/2fa-setup.js | 0 {public => client}/AboutUs.html | 0 {public => client}/Community.html | 0 {public => client}/CurrencyConverter.html | 0 client/Dockerfile | 17 + {public => client}/ExpenseSplitcalc.css | 0 {public => client}/ExpenseSplitcalc.html | 0 {public => client}/ExpenseSplitcalc.js | 0 {public => client}/Help-Center.html | 0 {public => client}/Monthlysummary.css | 0 {public => client}/Monthlysummary.html | 0 {public => client}/Monthlysummary.js | 0 {public => client}/PrivacyPolicy.html | 0 {public => client}/REPORTS_ACCESSIBILITY.md | 0 {public => client}/aboutus.css | 0 {public => client}/accounts-dashboard.css | 0 {public => client}/accounts-dashboard.js | 0 {public => client}/accounts.html | 0 {public => client}/analytics-dashboard.js | 0 .../analytics-local.js | 0 {public => client}/analytics.css | 0 {public => client}/analytics.html | 0 {public => client}/analytics.js | 0 .../api-integration.js | 0 {public => client}/approval-dashboard.html | 0 {public => client}/asset-inventory.html | 0 .../auth-integration.js | 0 {public => client}/auth.js | 0 budget-goals.js => client/budget-goals.js | 0 {public => client}/budget-planner.html | 0 {public => client}/budget.css | 0 {public => client}/budget.html | 0 {public => client}/budget.js | 0 {public => client}/challenges.html | 0 {public => client}/chat-assistant.js | 0 {public => client}/community.css | 0 {public => client}/community.js | 0 {public => client}/compliance-center.html | 0 {public => client}/contact.css | 0 {public => client}/contact.html | 0 {public => client}/contact.js | 0 {public => client}/contribute.css | 0 {public => client}/contribute.html | 0 {public => client}/contributor.html | 0 {public => client}/cookiepolicy.html | 0 {public => client}/css/folders.css | 0 {public => client}/currency-settings.html | 0 {public => client}/currencyConverter.css | 0 {public => client}/currencyConverter.js | 0 {public => client}/dashboard-clean.html | 0 {public => client}/dashboard.css | 0 {public => client}/dashboard.html | 0 {public => client}/dashboard.js | 0 {public => client}/db-manager.js | 0 {public => client}/debt-dashboard.html | 0 debug-auth.html => client/debug-auth.html | 0 {public => client}/expensetracker.css | 0 export-feature.js => client/export-feature.js | 0 {public => client}/faq.css | 0 {public => client}/faq.html | 0 {public => client}/faq.js | 0 {public => client}/feedback.css | 0 {public => client}/feedback.html | 0 {public => client}/feedback.js | 0 {public => client}/finance-tips.css | 0 {public => client}/finance-tips.html | 0 {public => client}/finance-tips.js | 0 {public => client}/forecasting.html | 0 {public => client}/gamification.js | 0 {public => client}/goal-feature.js | 0 {public => client}/goals.css | 0 {public => client}/goals.html | 0 {public => client}/goals.js | 0 groups.html => client/groups.html | 0 groups.js => client/groups.js | 0 {public => client}/helpcenter.css | 0 {public => client}/helpcenter.js | 0 {public => client}/i18n.js | 0 {public => client}/index.css | 0 {public => client}/index.css.bak | Bin {public => client}/index.html | 0 {public => client}/index.js | 0 {public => client}/inventory-hub.html | 0 {public => client}/investment-portfolio.js | 0 .../join-workspace.html | 0 {public => client}/js/approval-controller.js | 0 {public => client}/js/asset-controller.js | 0 {public => client}/js/budget-controller.js | 0 .../js/compliance-controller.js | 0 .../js/currency-intelligence.js | 0 {public => client}/js/debt-logic.js | 0 {public => client}/js/folders.js | 0 {public => client}/js/forecast-ctrl.js | 0 {public => client}/js/inventory-controller.js | 0 {public => client}/js/payroll-controller.js | 0 .../js/project-billing-controller.js | 0 {public => client}/js/tag-controller.js | 0 {public => client}/js/team-controller.js | 0 {public => client}/js/treasury-controller.js | 0 {public => client}/loanCalculator.css | 0 {public => client}/loanCalculator.html | 0 {public => client}/loanCalculator.js | 0 login-signup.html => client/login-signup.html | 608 +-- {public => client}/login.css | 0 {public => client}/login.html | 0 {public => client}/login.js | 0 {public => client}/manifest.json | 0 modal-test.html => client/modal-test.html | 0 client/nginx.conf | 29 + .../notification-center.js | 0 {public => client}/payroll-management.html | 0 {public => client}/performance-min.css | 0 {public => client}/performance.html | 0 {public => client}/performance.js | 0 {public => client}/profile.css | 0 {public => client}/profile.html | 0 {public => client}/profile.js | 0 {public => client}/project-billing.html | 0 {public => client}/protect.js | 0 {public => client}/pwa-features.html | 0 {public => client}/pwa-features.js | 0 {public => client}/pwa-min.css | 0 realtime-sync.js => client/realtime-sync.js | 0 {public => client}/receipt-ocr.js | 0 receipt-upload.js => client/receipt-upload.js | 0 .../recurring-expenses.js | 0 {public => client}/report-generator.js | 0 {public => client}/reports-critical.js | 0 {public => client}/reports-min.css | Bin {public => client}/reports-optimized.css | 0 {public => client}/reports-performance.html | 0 {public => client}/reports-simple.html | 0 {public => client}/reports-sw.js | 0 {public => client}/reports.css | 0 {public => client}/reports.html | 0 {public => client}/reports.js | 0 {public => client}/schemes.html | 0 {public => client}/security-dashboard.html | 0 {public => client}/security-dashboard.js | 0 {public => client}/settings.css | 0 {public => client}/settings.html | 0 {public => client}/settings.js | 0 {public => client}/signup.html | 0 {public => client}/smart-triggers.js | 0 {public => client}/subscription-manager.js | 0 {public => client}/sw-notifications.js | 0 {public => client}/sw.js | 0 {public => client}/tag-manager.html | 0 {public => client}/tax-reports.html | 0 {public => client}/tax-reports.js | 0 {public => client}/team-settings.html | 0 {public => client}/terms_service.html | 0 .../test-analytics.html | 0 test-login.html => client/test-login.html | 0 test-modal.html => client/test-modal.html | 0 theme.js => client/theme.js | 0 {public => client}/trackerscript.js | 0 {public => client}/transactions.css | 0 {public => client}/transactions.html | 0 {public => client}/transactions.js | 0 {public => client}/treasury-dashboard.html | 0 .../working-modal-demo.html | 0 {public => client}/workspace-feature.js | 0 docker-compose.yml | 51 +- expensetracker.css | 4389 ----------------- goals.html | 791 --- index.html | 1638 ------ manifest.json | 16 - .env.example => server/.env.example | 0 server/Dockerfile | 20 + {chatbot => server/chatbot}/README.md | 0 {chatbot => server/chatbot}/chatbot.config.js | 0 {chatbot => server/chatbot}/chatbot.css | 0 {chatbot => server/chatbot}/chatbot.data.js | 0 {chatbot => server/chatbot}/chatbot.html | 0 {chatbot => server/chatbot}/chatbot.js | 0 .../middleware}/analyticsValidator.js | 0 .../middleware}/auditMiddleware.js | 0 {middleware => server/middleware}/auth.js | 0 .../middleware}/authMiddleware.js | 0 .../middleware}/bankingValidator.js | 0 .../middleware}/categorizationValidator.js | 0 .../middleware}/clerkAuth.js | 0 .../middleware}/deviceFingerprint.js | 0 .../middleware}/errorMiddleware.js | 0 .../middleware}/forecastValidator.js | 0 .../middleware}/gamificationValidator.js | 0 .../middleware}/inputValidator.js | 0 .../middleware}/insightValidator.js | 0 .../middleware}/insightsValidator.js | 0 .../middleware}/investmentValidator.js | 0 .../middleware}/policyGuard.js | 0 .../middleware}/rateLimit.js | 0 .../middleware}/rateLimiter.js | 0 {middleware => server/middleware}/rbac.js | 0 .../middleware}/recurringValidator.js | 0 .../middleware}/roleCheck.js | 0 .../middleware}/sanitization.js | 0 .../middleware}/sanitizer.js | 0 .../middleware}/sharedSpaceValidator.js | 0 .../middleware}/socketAuth.js | 0 .../middleware}/spaceAuth.js | 0 .../middleware}/splitValidator.js | 0 .../middleware}/subscriptionValidator.js | 0 .../middleware}/taxValidator.js | 0 .../middleware}/twoFactorAuthMiddleware.js | 0 .../middleware}/uploadMiddleware.js | 0 {models => server/models}/AIPrediction.js | 0 {models => server/models}/AITrainingData.js | 0 {models => server/models}/Account.js | 0 .../models}/AccountingConnection.js | 0 {models => server/models}/Achievement.js | 0 .../models}/AmortizationSchedule.js | 0 {models => server/models}/AnalyticsCache.js | 0 {models => server/models}/AnomalyEvent.js | 0 {models => server/models}/AnomalyRule.js | 0 {models => server/models}/ApprovalRequest.js | 0 {models => server/models}/ApprovalWorkflow.js | 0 {models => server/models}/Asset.js | 0 .../models}/AssetDepreciation.js | 0 {models => server/models}/AssetTransaction.js | 0 {models => server/models}/AuditLog.js | 0 {models => server/models}/BackOrder.js | 0 {models => server/models}/BalanceHistory.js | 0 {models => server/models}/BankConnection.js | 0 {models => server/models}/BankInstitution.js | 0 {models => server/models}/BankLink.js | 0 {models => server/models}/Bill.js | 0 {models => server/models}/BillPayment.js | 0 {models => server/models}/BlockedEntity.js | 0 {models => server/models}/Budget.js | 0 {models => server/models}/BudgetForecast.js | 0 {models => server/models}/BudgetSnapshot.js | 0 {models => server/models}/CalendarEvent.js | 0 {models => server/models}/CashFlowForecast.js | 0 {models => server/models}/CategoryPattern.js | 0 {models => server/models}/CategoryRule.js | 0 {models => server/models}/CategoryTraining.js | 0 {models => server/models}/Challenge.js | 0 .../models}/ChallengeParticipant.js | 0 {models => server/models}/Chat.js | 0 {models => server/models}/Client.js | 0 .../models}/ComplianceFramework.js | 0 {models => server/models}/ComplianceRule.js | 0 .../models}/ComplianceViolation.js | 0 {models => server/models}/Currency.js | 0 {models => server/models}/CurrencyRate.js | 0 {models => server/models}/CustomDashboard.js | 0 {models => server/models}/DataWarehouse.js | 0 {models => server/models}/DebtAccount.js | 0 {models => server/models}/Deduction.js | 0 .../models}/DeviceFingerprint.js | 0 {models => server/models}/DocumentFolder.js | 0 {models => server/models}/EmployeePerk.js | 0 {models => server/models}/ExchangeHedge.js | 0 {models => server/models}/Expense.js | 0 {models => server/models}/ExpenseSplit.js | 0 .../models}/ExpenseSubmission.js | 0 {models => server/models}/FinancialAlert.js | 0 .../models}/FinancialHealthScore.js | 0 {models => server/models}/FinancialInsight.js | 0 {models => server/models}/FinancialReport.js | 0 {models => server/models}/FixedAsset.js | 0 {models => server/models}/Forecast.js | 0 {models => server/models}/ForecastScenario.js | 0 {models => server/models}/ForecastSnapshot.js | 0 {models => server/models}/FraudDetection.js | 0 {models => server/models}/Goal.js | 0 {models => server/models}/Group.js | 0 {models => server/models}/GroupInvite.js | 0 .../models}/ImmutableAuditLog.js | 0 .../models}/ImportedTransaction.js | 0 {models => server/models}/IncomeSource.js | 0 {models => server/models}/Insight.js | 0 {models => server/models}/Integration.js | 0 {models => server/models}/Investment.js | 0 {models => server/models}/Invoice.js | 0 {models => server/models}/LinkedAccount.js | 0 .../models}/LiquidityThreshold.js | 0 {models => server/models}/MerchantDatabase.js | 0 .../models}/MultiCurrencyWallet.js | 0 {models => server/models}/NetWorthSnapshot.js | 0 {models => server/models}/Notification.js | 0 {models => server/models}/Pattern.js | 0 {models => server/models}/Payment.js | 0 {models => server/models}/PayrollRun.js | 0 {models => server/models}/Policy.js | 0 {models => server/models}/Portfolio.js | 0 {models => server/models}/PriceHistory.js | 0 {models => server/models}/ProcurementOrder.js | 0 {models => server/models}/Project.js | 0 {models => server/models}/ProjectInvoice.js | 0 {models => server/models}/Receipt.js | 0 {models => server/models}/ReceiptDocument.js | 0 .../models}/ReconciliationRule.js | 0 {models => server/models}/RecurringExpense.js | 0 {models => server/models}/ReminderSchedule.js | 0 {models => server/models}/RiskScore.js | 0 {models => server/models}/Rule.js | 0 {models => server/models}/SalaryStructure.js | 0 {models => server/models}/SeasonalPattern.js | 0 {models => server/models}/SecurityEvent.js | 0 {models => server/models}/Session.js | 0 {models => server/models}/Settlement.js | 0 {models => server/models}/SharedGoal.js | 0 {models => server/models}/SharedSpace.js | 0 {models => server/models}/SpaceActivity.js | 0 {models => server/models}/SpendingAnomaly.js | 0 {models => server/models}/SpendingPattern.js | 0 {models => server/models}/SplitExpense.js | 0 {models => server/models}/SplitGroup.js | 0 {models => server/models}/StockItem.js | 0 {models => server/models}/Subscription.js | 0 {models => server/models}/SyncLog.js | 0 {models => server/models}/SyncQueue.js | 0 {models => server/models}/Tag.js | 0 {models => server/models}/TaxAuditPack.js | 0 {models => server/models}/TaxCategory.js | 0 {models => server/models}/TaxConfig.js | 0 {models => server/models}/TaxDocument.js | 0 {models => server/models}/TaxEstimate.js | 0 {models => server/models}/TaxProfile.js | 0 {models => server/models}/TaxRule.js | 0 {models => server/models}/Team.js | 0 {models => server/models}/TimeEntry.js | 0 {models => server/models}/Transaction.js | 0 {models => server/models}/Transfer.js | 0 {models => server/models}/TreasuryVault.js | 0 {models => server/models}/TrustedDevice.js | 0 {models => server/models}/TwoFactorAuth.js | 0 {models => server/models}/User.js | 0 {models => server/models}/UserAchievement.js | 0 .../models}/UserBehaviorProfile.js | 0 {models => server/models}/UserGamification.js | 0 {models => server/models}/Vendor.js | 0 {models => server/models}/Warehouse.js | 0 {models => server/models}/Webhook.js | 0 {models => server/models}/Workspace.js | 0 {models => server/models}/WorkspaceInvite.js | 0 {netlify => server/netlify}/functions/api.js | 0 package-lock.json => server/package-lock.json | 0 package.json => server/package.json | 0 .../repositories}/baseRepository.js | 0 .../repositories}/budgetRepository.js | 0 .../repositories}/expenseRepository.js | 0 .../repositories}/goalRepository.js | 0 .../repositories}/userRepository.js | 0 {routes => server/routes}/accounting.js | 0 {routes => server/routes}/accounts.js | 0 {routes => server/routes}/ai.js | 0 {routes => server/routes}/analytics.js | 0 {routes => server/routes}/approvals.js | 0 {routes => server/routes}/audit.js | 0 {routes => server/routes}/auditCompliance.js | 0 {routes => server/routes}/auth.js | 0 {routes => server/routes}/backups.js | 0 {routes => server/routes}/bills.js | 0 {routes => server/routes}/budgets.js | 0 {routes => server/routes}/calendar.js | 0 {routes => server/routes}/categorization.js | 0 {routes => server/routes}/chat.js | 0 {routes => server/routes}/clients.js | 0 {routes => server/routes}/collaboration.js | 0 {routes => server/routes}/compliance.js | 0 {routes => server/routes}/contact.js | 0 {routes => server/routes}/currency.js | 0 {routes => server/routes}/debt.js | 0 {routes => server/routes}/expenses.js | 0 {routes => server/routes}/export.js | 0 {routes => server/routes}/folders.js | 0 {routes => server/routes}/forecast.js | 0 {routes => server/routes}/forecasting.js | 0 {routes => server/routes}/fraudDetection.js | 0 {routes => server/routes}/gamification.js | 0 {routes => server/routes}/goals.js | 0 {routes => server/routes}/groups.js | 0 {routes => server/routes}/insights.js | 0 {routes => server/routes}/integrations.js | 0 {routes => server/routes}/inventory.js | 0 {routes => server/routes}/investments.js | 0 {routes => server/routes}/invoices.js | 0 {routes => server/routes}/multicurrency.js | 0 {routes => server/routes}/notifications.js | 0 {routes => server/routes}/openBanking.js | 0 {routes => server/routes}/payments.js | 0 {routes => server/routes}/payroll.js | 0 {routes => server/routes}/portfolios.js | 0 {routes => server/routes}/procurement.js | 0 {routes => server/routes}/project-billing.js | 0 {routes => server/routes}/receipts.js | 0 {routes => server/routes}/recurring.js | 0 {routes => server/routes}/reminders.js | 0 {routes => server/routes}/reports.js | 0 {routes => server/routes}/rules.js | 0 {routes => server/routes}/security.js | 0 {routes => server/routes}/sharedSpaces.js | 0 {routes => server/routes}/splits.js | 0 {routes => server/routes}/subscriptions.js | 0 {routes => server/routes}/sync.js | 0 {routes => server/routes}/tags.js | 0 {routes => server/routes}/tax.js | 0 {routes => server/routes}/teams.js | 0 {routes => server/routes}/transactions.js | 0 {routes => server/routes}/treasury.js | 0 {routes => server/routes}/twoFactorAuth.js | 0 {routes => server/routes}/user.js | 0 {routes => server/routes}/workspaces.js | 0 server.js => server/server.js | 12 +- .../accountTakeoverAlertingService.js | 0 .../services}/accountingService.js | 0 .../services}/advancedAnalyticsService.js | 0 .../services}/aiInsightsService.js | 0 {services => server/services}/aiService.js | 0 {services => server/services}/alertService.js | 0 .../services}/analysisEngine.js | 0 .../services}/analyticsService.js | 0 .../services}/anomalyDetectionService.js | 0 .../services}/approvalService.js | 0 {services => server/services}/assetService.js | 0 .../services}/auditComplianceService.js | 0 {services => server/services}/auditService.js | 0 .../services}/backupRecoveryService.js | 0 .../services}/backupService.js | 0 .../services}/billReminderService.js | 0 {services => server/services}/billService.js | 0 .../services}/budgetAlertService.js | 0 .../services}/budgetForecastingService.js | 0 .../services}/budgetIntelligenceService.js | 0 .../services}/budgetService.js | 0 .../services}/calendarService.js | 0 .../services}/cashFlowForecastService.js | 0 .../services}/categorizationEngine.js | 0 .../services}/categorizationService.js | 0 .../services}/categoryService.js | 0 {services => server/services}/chatService.js | 0 .../services}/collaborationService.js | 0 .../services}/complianceEngine.js | 0 {services => server/services}/cronJobs.js | 0 .../services}/currencyService.js | 0 {services => server/services}/debtService.js | 0 .../services}/deductionEngine.js | 0 .../services}/discoveryService.js | 0 {services => server/services}/emailService.js | 0 .../services}/expenseService.js | 0 .../services}/exportService.js | 0 .../services}/fileUploadService.js | 0 .../services}/forecastService.js | 0 .../services}/forecastingEngine.js | 0 .../services}/forecastingService.js | 0 .../services}/forensicAuditService.js | 0 {services => server/services}/forexService.js | 0 .../services}/fraudDetectionService.js | 0 .../services}/gamificationService.js | 0 {services => server/services}/goalService.js | 0 {services => server/services}/groupService.js | 0 .../services}/insightService.js | 0 .../services}/integrationService.js | 0 .../services}/intelligenceService.js | 0 .../services}/internationalizationService.js | 0 .../services}/inventoryService.js | 0 .../services}/investmentService.js | 0 .../services}/invitationService.js | 0 .../services}/inviteService.js | 0 .../services}/invoiceService.js | 0 .../services}/invoiceSyncService.js | 0 .../services}/merchantLearningService.js | 0 .../services}/notificationService.js | 0 {services => server/services}/ocrService.js | 0 .../services}/openBankingService.js | 0 .../services}/parsingService.js | 0 .../services}/paymentService.js | 0 .../services}/payrollService.js | 0 {services => server/services}/pdfService.js | 0 .../services}/portfolioService.js | 0 .../services}/priceUpdateService.js | 0 .../services}/procurementService.js | 0 .../services}/projectRevenueService.js | 0 .../services}/recurringService.js | 0 .../services}/reminderService.js | 0 .../services}/replenishmentService.js | 0 .../services}/reportService.js | 0 .../services}/revaluationService.js | 0 {services => server/services}/ruleEngine.js | 0 .../services}/runwayForecaster.js | 0 {services => server/services}/scoreService.js | 0 .../services}/securityMonitor.js | 0 .../services}/securityService.js | 0 .../services}/settlementService.js | 0 .../services}/sharedSpaceService.js | 0 {services => server/services}/splitService.js | 0 .../services}/subscriptionDetector.js | 0 .../services}/subscriptionService.js | 0 .../suspiciousLoginDetectionService.js | 0 .../services}/tagManagementService.js | 0 .../services}/taxOptimizationService.js | 0 {services => server/services}/taxService.js | 0 .../services}/teamManagementService.js | 0 .../services}/transactionImportService.js | 0 .../services}/transactionService.js | 0 .../services}/treasuryService.js | 0 .../services}/twoFactorAuthService.js | 0 .../services}/webhookService.js | 0 .../services}/wellnessService.js | 0 .../services}/workspaceService.js | 0 {socket => server/socket}/collabHandler.js | 0 {tests => server/tests}/backupService.test.js | 0 .../tests}/output/test-report.csv | 0 .../tests}/output/test-report.pdf | Bin .../tests}/output/test-report.xlsx | Bin {utils => server/utils}/AppError.js | 0 {utils => server/utils}/ResponseFactory.js | 0 {utils => server/utils}/financialModels.js | 0 {utils => server/utils}/patternMatcher.js | 0 {utils => server/utils}/stockMath.js | 0 {utils => server/utils}/taxCalculators.js | 0 .../verify-backup-system.js | 0 workspace-feature.js | 908 ---- 525 files changed, 429 insertions(+), 10544 deletions(-) create mode 100644 .dockerignore delete mode 100644 analytics-dashboard.js delete mode 100644 analytics.html rename 2fa-manage.css => client/2fa-manage.css (100%) rename 2fa-manage.html => client/2fa-manage.html (100%) rename 2fa-manage.js => client/2fa-manage.js (100%) rename 2fa-setup.css => client/2fa-setup.css (100%) rename 2fa-setup.html => client/2fa-setup.html (100%) rename 2fa-setup.js => client/2fa-setup.js (100%) rename {public => client}/AboutUs.html (100%) rename {public => client}/Community.html (100%) rename {public => client}/CurrencyConverter.html (100%) create mode 100644 client/Dockerfile rename {public => client}/ExpenseSplitcalc.css (100%) rename {public => client}/ExpenseSplitcalc.html (100%) rename {public => client}/ExpenseSplitcalc.js (100%) rename {public => client}/Help-Center.html (100%) rename {public => client}/Monthlysummary.css (100%) rename {public => client}/Monthlysummary.html (100%) rename {public => client}/Monthlysummary.js (100%) rename {public => client}/PrivacyPolicy.html (100%) rename {public => client}/REPORTS_ACCESSIBILITY.md (100%) rename {public => client}/aboutus.css (100%) rename {public => client}/accounts-dashboard.css (100%) rename {public => client}/accounts-dashboard.js (100%) rename {public => client}/accounts.html (100%) rename {public => client}/analytics-dashboard.js (100%) rename analytics-local.js => client/analytics-local.js (100%) rename {public => client}/analytics.css (100%) rename {public => client}/analytics.html (100%) rename {public => client}/analytics.js (100%) rename api-integration.js => client/api-integration.js (100%) rename {public => client}/approval-dashboard.html (100%) rename {public => client}/asset-inventory.html (100%) rename auth-integration.js => client/auth-integration.js (100%) rename {public => client}/auth.js (100%) rename budget-goals.js => client/budget-goals.js (100%) rename {public => client}/budget-planner.html (100%) rename {public => client}/budget.css (100%) rename {public => client}/budget.html (100%) rename {public => client}/budget.js (100%) rename {public => client}/challenges.html (100%) rename {public => client}/chat-assistant.js (100%) rename {public => client}/community.css (100%) rename {public => client}/community.js (100%) rename {public => client}/compliance-center.html (100%) rename {public => client}/contact.css (100%) rename {public => client}/contact.html (100%) rename {public => client}/contact.js (100%) rename {public => client}/contribute.css (100%) rename {public => client}/contribute.html (100%) rename {public => client}/contributor.html (100%) rename {public => client}/cookiepolicy.html (100%) rename {public => client}/css/folders.css (100%) rename {public => client}/currency-settings.html (100%) rename {public => client}/currencyConverter.css (100%) rename {public => client}/currencyConverter.js (100%) rename {public => client}/dashboard-clean.html (100%) rename {public => client}/dashboard.css (100%) rename {public => client}/dashboard.html (100%) rename {public => client}/dashboard.js (100%) rename {public => client}/db-manager.js (100%) rename {public => client}/debt-dashboard.html (100%) rename debug-auth.html => client/debug-auth.html (100%) rename {public => client}/expensetracker.css (100%) rename export-feature.js => client/export-feature.js (100%) rename {public => client}/faq.css (100%) rename {public => client}/faq.html (100%) rename {public => client}/faq.js (100%) rename {public => client}/feedback.css (100%) rename {public => client}/feedback.html (100%) rename {public => client}/feedback.js (100%) rename {public => client}/finance-tips.css (100%) rename {public => client}/finance-tips.html (100%) rename {public => client}/finance-tips.js (100%) rename {public => client}/forecasting.html (100%) rename {public => client}/gamification.js (100%) rename {public => client}/goal-feature.js (100%) rename {public => client}/goals.css (100%) rename {public => client}/goals.html (100%) rename {public => client}/goals.js (100%) rename groups.html => client/groups.html (100%) rename groups.js => client/groups.js (100%) rename {public => client}/helpcenter.css (100%) rename {public => client}/helpcenter.js (100%) rename {public => client}/i18n.js (100%) rename {public => client}/index.css (100%) rename {public => client}/index.css.bak (100%) rename {public => client}/index.html (100%) rename {public => client}/index.js (100%) rename {public => client}/inventory-hub.html (100%) rename {public => client}/investment-portfolio.js (100%) rename join-workspace.html => client/join-workspace.html (100%) rename {public => client}/js/approval-controller.js (100%) rename {public => client}/js/asset-controller.js (100%) rename {public => client}/js/budget-controller.js (100%) rename {public => client}/js/compliance-controller.js (100%) rename {public => client}/js/currency-intelligence.js (100%) rename {public => client}/js/debt-logic.js (100%) rename {public => client}/js/folders.js (100%) rename {public => client}/js/forecast-ctrl.js (100%) rename {public => client}/js/inventory-controller.js (100%) rename {public => client}/js/payroll-controller.js (100%) rename {public => client}/js/project-billing-controller.js (100%) rename {public => client}/js/tag-controller.js (100%) rename {public => client}/js/team-controller.js (100%) rename {public => client}/js/treasury-controller.js (100%) rename {public => client}/loanCalculator.css (100%) rename {public => client}/loanCalculator.html (100%) rename {public => client}/loanCalculator.js (100%) rename login-signup.html => client/login-signup.html (96%) rename {public => client}/login.css (100%) rename {public => client}/login.html (100%) rename {public => client}/login.js (100%) rename {public => client}/manifest.json (100%) rename modal-test.html => client/modal-test.html (100%) create mode 100644 client/nginx.conf rename notification-center.js => client/notification-center.js (100%) rename {public => client}/payroll-management.html (100%) rename {public => client}/performance-min.css (100%) rename {public => client}/performance.html (100%) rename {public => client}/performance.js (100%) rename {public => client}/profile.css (100%) rename {public => client}/profile.html (100%) rename {public => client}/profile.js (100%) rename {public => client}/project-billing.html (100%) rename {public => client}/protect.js (100%) rename {public => client}/pwa-features.html (100%) rename {public => client}/pwa-features.js (100%) rename {public => client}/pwa-min.css (100%) rename realtime-sync.js => client/realtime-sync.js (100%) rename {public => client}/receipt-ocr.js (100%) rename receipt-upload.js => client/receipt-upload.js (100%) rename recurring-expenses.js => client/recurring-expenses.js (100%) rename {public => client}/report-generator.js (100%) rename {public => client}/reports-critical.js (100%) rename {public => client}/reports-min.css (100%) rename {public => client}/reports-optimized.css (100%) rename {public => client}/reports-performance.html (100%) rename {public => client}/reports-simple.html (100%) rename {public => client}/reports-sw.js (100%) rename {public => client}/reports.css (100%) rename {public => client}/reports.html (100%) rename {public => client}/reports.js (100%) rename {public => client}/schemes.html (100%) rename {public => client}/security-dashboard.html (100%) rename {public => client}/security-dashboard.js (100%) rename {public => client}/settings.css (100%) rename {public => client}/settings.html (100%) rename {public => client}/settings.js (100%) rename {public => client}/signup.html (100%) rename {public => client}/smart-triggers.js (100%) rename {public => client}/subscription-manager.js (100%) rename {public => client}/sw-notifications.js (100%) rename {public => client}/sw.js (100%) rename {public => client}/tag-manager.html (100%) rename {public => client}/tax-reports.html (100%) rename {public => client}/tax-reports.js (100%) rename {public => client}/team-settings.html (100%) rename {public => client}/terms_service.html (100%) rename test-analytics.html => client/test-analytics.html (100%) rename test-login.html => client/test-login.html (100%) rename test-modal.html => client/test-modal.html (100%) rename theme.js => client/theme.js (100%) rename {public => client}/trackerscript.js (100%) rename {public => client}/transactions.css (100%) rename {public => client}/transactions.html (100%) rename {public => client}/transactions.js (100%) rename {public => client}/treasury-dashboard.html (100%) rename working-modal-demo.html => client/working-modal-demo.html (100%) rename {public => client}/workspace-feature.js (100%) delete mode 100644 expensetracker.css delete mode 100644 goals.html delete mode 100644 index.html delete mode 100644 manifest.json rename .env.example => server/.env.example (100%) create mode 100644 server/Dockerfile rename {chatbot => server/chatbot}/README.md (100%) rename {chatbot => server/chatbot}/chatbot.config.js (100%) rename {chatbot => server/chatbot}/chatbot.css (100%) rename {chatbot => server/chatbot}/chatbot.data.js (100%) rename {chatbot => server/chatbot}/chatbot.html (100%) rename {chatbot => server/chatbot}/chatbot.js (100%) rename {middleware => server/middleware}/analyticsValidator.js (100%) rename {middleware => server/middleware}/auditMiddleware.js (100%) rename {middleware => server/middleware}/auth.js (100%) rename {middleware => server/middleware}/authMiddleware.js (100%) rename {middleware => server/middleware}/bankingValidator.js (100%) rename {middleware => server/middleware}/categorizationValidator.js (100%) rename {middleware => server/middleware}/clerkAuth.js (100%) rename {middleware => server/middleware}/deviceFingerprint.js (100%) rename {middleware => server/middleware}/errorMiddleware.js (100%) rename {middleware => server/middleware}/forecastValidator.js (100%) rename {middleware => server/middleware}/gamificationValidator.js (100%) rename {middleware => server/middleware}/inputValidator.js (100%) rename {middleware => server/middleware}/insightValidator.js (100%) rename {middleware => server/middleware}/insightsValidator.js (100%) rename {middleware => server/middleware}/investmentValidator.js (100%) rename {middleware => server/middleware}/policyGuard.js (100%) rename {middleware => server/middleware}/rateLimit.js (100%) rename {middleware => server/middleware}/rateLimiter.js (100%) rename {middleware => server/middleware}/rbac.js (100%) rename {middleware => server/middleware}/recurringValidator.js (100%) rename {middleware => server/middleware}/roleCheck.js (100%) rename {middleware => server/middleware}/sanitization.js (100%) rename {middleware => server/middleware}/sanitizer.js (100%) rename {middleware => server/middleware}/sharedSpaceValidator.js (100%) rename {middleware => server/middleware}/socketAuth.js (100%) rename {middleware => server/middleware}/spaceAuth.js (100%) rename {middleware => server/middleware}/splitValidator.js (100%) rename {middleware => server/middleware}/subscriptionValidator.js (100%) rename {middleware => server/middleware}/taxValidator.js (100%) rename {middleware => server/middleware}/twoFactorAuthMiddleware.js (100%) rename {middleware => server/middleware}/uploadMiddleware.js (100%) rename {models => server/models}/AIPrediction.js (100%) rename {models => server/models}/AITrainingData.js (100%) rename {models => server/models}/Account.js (100%) rename {models => server/models}/AccountingConnection.js (100%) rename {models => server/models}/Achievement.js (100%) rename {models => server/models}/AmortizationSchedule.js (100%) rename {models => server/models}/AnalyticsCache.js (100%) rename {models => server/models}/AnomalyEvent.js (100%) rename {models => server/models}/AnomalyRule.js (100%) rename {models => server/models}/ApprovalRequest.js (100%) rename {models => server/models}/ApprovalWorkflow.js (100%) rename {models => server/models}/Asset.js (100%) rename {models => server/models}/AssetDepreciation.js (100%) rename {models => server/models}/AssetTransaction.js (100%) rename {models => server/models}/AuditLog.js (100%) rename {models => server/models}/BackOrder.js (100%) rename {models => server/models}/BalanceHistory.js (100%) rename {models => server/models}/BankConnection.js (100%) rename {models => server/models}/BankInstitution.js (100%) rename {models => server/models}/BankLink.js (100%) rename {models => server/models}/Bill.js (100%) rename {models => server/models}/BillPayment.js (100%) rename {models => server/models}/BlockedEntity.js (100%) rename {models => server/models}/Budget.js (100%) rename {models => server/models}/BudgetForecast.js (100%) rename {models => server/models}/BudgetSnapshot.js (100%) rename {models => server/models}/CalendarEvent.js (100%) rename {models => server/models}/CashFlowForecast.js (100%) rename {models => server/models}/CategoryPattern.js (100%) rename {models => server/models}/CategoryRule.js (100%) rename {models => server/models}/CategoryTraining.js (100%) rename {models => server/models}/Challenge.js (100%) rename {models => server/models}/ChallengeParticipant.js (100%) rename {models => server/models}/Chat.js (100%) rename {models => server/models}/Client.js (100%) rename {models => server/models}/ComplianceFramework.js (100%) rename {models => server/models}/ComplianceRule.js (100%) rename {models => server/models}/ComplianceViolation.js (100%) rename {models => server/models}/Currency.js (100%) rename {models => server/models}/CurrencyRate.js (100%) rename {models => server/models}/CustomDashboard.js (100%) rename {models => server/models}/DataWarehouse.js (100%) rename {models => server/models}/DebtAccount.js (100%) rename {models => server/models}/Deduction.js (100%) rename {models => server/models}/DeviceFingerprint.js (100%) rename {models => server/models}/DocumentFolder.js (100%) rename {models => server/models}/EmployeePerk.js (100%) rename {models => server/models}/ExchangeHedge.js (100%) rename {models => server/models}/Expense.js (100%) rename {models => server/models}/ExpenseSplit.js (100%) rename {models => server/models}/ExpenseSubmission.js (100%) rename {models => server/models}/FinancialAlert.js (100%) rename {models => server/models}/FinancialHealthScore.js (100%) rename {models => server/models}/FinancialInsight.js (100%) rename {models => server/models}/FinancialReport.js (100%) rename {models => server/models}/FixedAsset.js (100%) rename {models => server/models}/Forecast.js (100%) rename {models => server/models}/ForecastScenario.js (100%) rename {models => server/models}/ForecastSnapshot.js (100%) rename {models => server/models}/FraudDetection.js (100%) rename {models => server/models}/Goal.js (100%) rename {models => server/models}/Group.js (100%) rename {models => server/models}/GroupInvite.js (100%) rename {models => server/models}/ImmutableAuditLog.js (100%) rename {models => server/models}/ImportedTransaction.js (100%) rename {models => server/models}/IncomeSource.js (100%) rename {models => server/models}/Insight.js (100%) rename {models => server/models}/Integration.js (100%) rename {models => server/models}/Investment.js (100%) rename {models => server/models}/Invoice.js (100%) rename {models => server/models}/LinkedAccount.js (100%) rename {models => server/models}/LiquidityThreshold.js (100%) rename {models => server/models}/MerchantDatabase.js (100%) rename {models => server/models}/MultiCurrencyWallet.js (100%) rename {models => server/models}/NetWorthSnapshot.js (100%) rename {models => server/models}/Notification.js (100%) rename {models => server/models}/Pattern.js (100%) rename {models => server/models}/Payment.js (100%) rename {models => server/models}/PayrollRun.js (100%) rename {models => server/models}/Policy.js (100%) rename {models => server/models}/Portfolio.js (100%) rename {models => server/models}/PriceHistory.js (100%) rename {models => server/models}/ProcurementOrder.js (100%) rename {models => server/models}/Project.js (100%) rename {models => server/models}/ProjectInvoice.js (100%) rename {models => server/models}/Receipt.js (100%) rename {models => server/models}/ReceiptDocument.js (100%) rename {models => server/models}/ReconciliationRule.js (100%) rename {models => server/models}/RecurringExpense.js (100%) rename {models => server/models}/ReminderSchedule.js (100%) rename {models => server/models}/RiskScore.js (100%) rename {models => server/models}/Rule.js (100%) rename {models => server/models}/SalaryStructure.js (100%) rename {models => server/models}/SeasonalPattern.js (100%) rename {models => server/models}/SecurityEvent.js (100%) rename {models => server/models}/Session.js (100%) rename {models => server/models}/Settlement.js (100%) rename {models => server/models}/SharedGoal.js (100%) rename {models => server/models}/SharedSpace.js (100%) rename {models => server/models}/SpaceActivity.js (100%) rename {models => server/models}/SpendingAnomaly.js (100%) rename {models => server/models}/SpendingPattern.js (100%) rename {models => server/models}/SplitExpense.js (100%) rename {models => server/models}/SplitGroup.js (100%) rename {models => server/models}/StockItem.js (100%) rename {models => server/models}/Subscription.js (100%) rename {models => server/models}/SyncLog.js (100%) rename {models => server/models}/SyncQueue.js (100%) rename {models => server/models}/Tag.js (100%) rename {models => server/models}/TaxAuditPack.js (100%) rename {models => server/models}/TaxCategory.js (100%) rename {models => server/models}/TaxConfig.js (100%) rename {models => server/models}/TaxDocument.js (100%) rename {models => server/models}/TaxEstimate.js (100%) rename {models => server/models}/TaxProfile.js (100%) rename {models => server/models}/TaxRule.js (100%) rename {models => server/models}/Team.js (100%) rename {models => server/models}/TimeEntry.js (100%) rename {models => server/models}/Transaction.js (100%) rename {models => server/models}/Transfer.js (100%) rename {models => server/models}/TreasuryVault.js (100%) rename {models => server/models}/TrustedDevice.js (100%) rename {models => server/models}/TwoFactorAuth.js (100%) rename {models => server/models}/User.js (100%) rename {models => server/models}/UserAchievement.js (100%) rename {models => server/models}/UserBehaviorProfile.js (100%) rename {models => server/models}/UserGamification.js (100%) rename {models => server/models}/Vendor.js (100%) rename {models => server/models}/Warehouse.js (100%) rename {models => server/models}/Webhook.js (100%) rename {models => server/models}/Workspace.js (100%) rename {models => server/models}/WorkspaceInvite.js (100%) rename {netlify => server/netlify}/functions/api.js (100%) rename package-lock.json => server/package-lock.json (100%) rename package.json => server/package.json (100%) rename {repositories => server/repositories}/baseRepository.js (100%) rename {repositories => server/repositories}/budgetRepository.js (100%) rename {repositories => server/repositories}/expenseRepository.js (100%) rename {repositories => server/repositories}/goalRepository.js (100%) rename {repositories => server/repositories}/userRepository.js (100%) rename {routes => server/routes}/accounting.js (100%) rename {routes => server/routes}/accounts.js (100%) rename {routes => server/routes}/ai.js (100%) rename {routes => server/routes}/analytics.js (100%) rename {routes => server/routes}/approvals.js (100%) rename {routes => server/routes}/audit.js (100%) rename {routes => server/routes}/auditCompliance.js (100%) rename {routes => server/routes}/auth.js (100%) rename {routes => server/routes}/backups.js (100%) rename {routes => server/routes}/bills.js (100%) rename {routes => server/routes}/budgets.js (100%) rename {routes => server/routes}/calendar.js (100%) rename {routes => server/routes}/categorization.js (100%) rename {routes => server/routes}/chat.js (100%) rename {routes => server/routes}/clients.js (100%) rename {routes => server/routes}/collaboration.js (100%) rename {routes => server/routes}/compliance.js (100%) rename {routes => server/routes}/contact.js (100%) rename {routes => server/routes}/currency.js (100%) rename {routes => server/routes}/debt.js (100%) rename {routes => server/routes}/expenses.js (100%) rename {routes => server/routes}/export.js (100%) rename {routes => server/routes}/folders.js (100%) rename {routes => server/routes}/forecast.js (100%) rename {routes => server/routes}/forecasting.js (100%) rename {routes => server/routes}/fraudDetection.js (100%) rename {routes => server/routes}/gamification.js (100%) rename {routes => server/routes}/goals.js (100%) rename {routes => server/routes}/groups.js (100%) rename {routes => server/routes}/insights.js (100%) rename {routes => server/routes}/integrations.js (100%) rename {routes => server/routes}/inventory.js (100%) rename {routes => server/routes}/investments.js (100%) rename {routes => server/routes}/invoices.js (100%) rename {routes => server/routes}/multicurrency.js (100%) rename {routes => server/routes}/notifications.js (100%) rename {routes => server/routes}/openBanking.js (100%) rename {routes => server/routes}/payments.js (100%) rename {routes => server/routes}/payroll.js (100%) rename {routes => server/routes}/portfolios.js (100%) rename {routes => server/routes}/procurement.js (100%) rename {routes => server/routes}/project-billing.js (100%) rename {routes => server/routes}/receipts.js (100%) rename {routes => server/routes}/recurring.js (100%) rename {routes => server/routes}/reminders.js (100%) rename {routes => server/routes}/reports.js (100%) rename {routes => server/routes}/rules.js (100%) rename {routes => server/routes}/security.js (100%) rename {routes => server/routes}/sharedSpaces.js (100%) rename {routes => server/routes}/splits.js (100%) rename {routes => server/routes}/subscriptions.js (100%) rename {routes => server/routes}/sync.js (100%) rename {routes => server/routes}/tags.js (100%) rename {routes => server/routes}/tax.js (100%) rename {routes => server/routes}/teams.js (100%) rename {routes => server/routes}/transactions.js (100%) rename {routes => server/routes}/treasury.js (100%) rename {routes => server/routes}/twoFactorAuth.js (100%) rename {routes => server/routes}/user.js (100%) rename {routes => server/routes}/workspaces.js (100%) rename server.js => server/server.js (97%) rename {services => server/services}/accountTakeoverAlertingService.js (100%) rename {services => server/services}/accountingService.js (100%) rename {services => server/services}/advancedAnalyticsService.js (100%) rename {services => server/services}/aiInsightsService.js (100%) rename {services => server/services}/aiService.js (100%) rename {services => server/services}/alertService.js (100%) rename {services => server/services}/analysisEngine.js (100%) rename {services => server/services}/analyticsService.js (100%) rename {services => server/services}/anomalyDetectionService.js (100%) rename {services => server/services}/approvalService.js (100%) rename {services => server/services}/assetService.js (100%) rename {services => server/services}/auditComplianceService.js (100%) rename {services => server/services}/auditService.js (100%) rename {services => server/services}/backupRecoveryService.js (100%) rename {services => server/services}/backupService.js (100%) rename {services => server/services}/billReminderService.js (100%) rename {services => server/services}/billService.js (100%) rename {services => server/services}/budgetAlertService.js (100%) rename {services => server/services}/budgetForecastingService.js (100%) rename {services => server/services}/budgetIntelligenceService.js (100%) rename {services => server/services}/budgetService.js (100%) rename {services => server/services}/calendarService.js (100%) rename {services => server/services}/cashFlowForecastService.js (100%) rename {services => server/services}/categorizationEngine.js (100%) rename {services => server/services}/categorizationService.js (100%) rename {services => server/services}/categoryService.js (100%) rename {services => server/services}/chatService.js (100%) rename {services => server/services}/collaborationService.js (100%) rename {services => server/services}/complianceEngine.js (100%) rename {services => server/services}/cronJobs.js (100%) rename {services => server/services}/currencyService.js (100%) rename {services => server/services}/debtService.js (100%) rename {services => server/services}/deductionEngine.js (100%) rename {services => server/services}/discoveryService.js (100%) rename {services => server/services}/emailService.js (100%) rename {services => server/services}/expenseService.js (100%) rename {services => server/services}/exportService.js (100%) rename {services => server/services}/fileUploadService.js (100%) rename {services => server/services}/forecastService.js (100%) rename {services => server/services}/forecastingEngine.js (100%) rename {services => server/services}/forecastingService.js (100%) rename {services => server/services}/forensicAuditService.js (100%) rename {services => server/services}/forexService.js (100%) rename {services => server/services}/fraudDetectionService.js (100%) rename {services => server/services}/gamificationService.js (100%) rename {services => server/services}/goalService.js (100%) rename {services => server/services}/groupService.js (100%) rename {services => server/services}/insightService.js (100%) rename {services => server/services}/integrationService.js (100%) rename {services => server/services}/intelligenceService.js (100%) rename {services => server/services}/internationalizationService.js (100%) rename {services => server/services}/inventoryService.js (100%) rename {services => server/services}/investmentService.js (100%) rename {services => server/services}/invitationService.js (100%) rename {services => server/services}/inviteService.js (100%) rename {services => server/services}/invoiceService.js (100%) rename {services => server/services}/invoiceSyncService.js (100%) rename {services => server/services}/merchantLearningService.js (100%) rename {services => server/services}/notificationService.js (100%) rename {services => server/services}/ocrService.js (100%) rename {services => server/services}/openBankingService.js (100%) rename {services => server/services}/parsingService.js (100%) rename {services => server/services}/paymentService.js (100%) rename {services => server/services}/payrollService.js (100%) rename {services => server/services}/pdfService.js (100%) rename {services => server/services}/portfolioService.js (100%) rename {services => server/services}/priceUpdateService.js (100%) rename {services => server/services}/procurementService.js (100%) rename {services => server/services}/projectRevenueService.js (100%) rename {services => server/services}/recurringService.js (100%) rename {services => server/services}/reminderService.js (100%) rename {services => server/services}/replenishmentService.js (100%) rename {services => server/services}/reportService.js (100%) rename {services => server/services}/revaluationService.js (100%) rename {services => server/services}/ruleEngine.js (100%) rename {services => server/services}/runwayForecaster.js (100%) rename {services => server/services}/scoreService.js (100%) rename {services => server/services}/securityMonitor.js (100%) rename {services => server/services}/securityService.js (100%) rename {services => server/services}/settlementService.js (100%) rename {services => server/services}/sharedSpaceService.js (100%) rename {services => server/services}/splitService.js (100%) rename {services => server/services}/subscriptionDetector.js (100%) rename {services => server/services}/subscriptionService.js (100%) rename {services => server/services}/suspiciousLoginDetectionService.js (100%) rename {services => server/services}/tagManagementService.js (100%) rename {services => server/services}/taxOptimizationService.js (100%) rename {services => server/services}/taxService.js (100%) rename {services => server/services}/teamManagementService.js (100%) rename {services => server/services}/transactionImportService.js (100%) rename {services => server/services}/transactionService.js (100%) rename {services => server/services}/treasuryService.js (100%) rename {services => server/services}/twoFactorAuthService.js (100%) rename {services => server/services}/webhookService.js (100%) rename {services => server/services}/wellnessService.js (100%) rename {services => server/services}/workspaceService.js (100%) rename {socket => server/socket}/collabHandler.js (100%) rename {tests => server/tests}/backupService.test.js (100%) rename {tests => server/tests}/output/test-report.csv (100%) rename {tests => server/tests}/output/test-report.pdf (100%) rename {tests => server/tests}/output/test-report.xlsx (100%) rename {utils => server/utils}/AppError.js (100%) rename {utils => server/utils}/ResponseFactory.js (100%) rename {utils => server/utils}/financialModels.js (100%) rename {utils => server/utils}/patternMatcher.js (100%) rename {utils => server/utils}/stockMath.js (100%) rename {utils => server/utils}/taxCalculators.js (100%) rename verify-backup-system.js => server/verify-backup-system.js (100%) delete mode 100644 workspace-feature.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..fab3f3a7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +npm-debug.log +.git +.gitignore +.env +dist +build diff --git a/analytics-dashboard.js b/analytics-dashboard.js deleted file mode 100644 index d05c24b1..00000000 --- a/analytics-dashboard.js +++ /dev/null @@ -1,1370 +0,0 @@ -// Analytics Dashboard Feature for ExpenseFlow -var ANALYTICS_API_URL = '/api/analytics'; - -// State management -let analyticsData = { - trends: null, - categoryBreakdown: null, - insights: null, - predictions: null, - velocity: null, - forecast: null -}; - -const getAnalyticsLocale = () => (window.i18n?.getLocale?.() && window.i18n.getLocale()) || 'en-US'; -const getAnalyticsCurrency = () => (window.i18n?.getCurrency?.() && window.i18n.getCurrency()) || 'INR'; - -function formatAnalyticsCurrency(value, options = {}) { - const currency = options.currency || getAnalyticsCurrency(); - if (window.i18n?.formatCurrency) { - return window.i18n.formatCurrency(value, { - currency, - locale: getAnalyticsLocale(), - minimumFractionDigits: options.minimumFractionDigits ?? 0, - maximumFractionDigits: options.maximumFractionDigits ?? 0 - }); - } - - const amount = Number(value || 0); - return `${currency} ${amount.toLocaleString(undefined, { maximumFractionDigits: 2 })}`; -} - -// ======================== -// API Functions -// ======================== - -async function getAuthHeaders() { - // Accept either 'token' or legacy 'authToken' key used by auth integration - const token = localStorage.getItem('token') || localStorage.getItem('authToken'); - const headers = { 'Content-Type': 'application/json' }; - if (token) headers['Authorization'] = `Bearer ${token}`; - return headers; -} - -/** - * Fetch spending trends - */ -async function fetchSpendingTrends(period = 'monthly', months = 6) { - try { - const token = localStorage.getItem('token'); - if (!token) return { data: [] }; - - const response = await fetch( - `${ANALYTICS_API_URL}/spending-trends?period=${period}&months=${months}`, - { headers: await getAuthHeaders() } - ); - if (!response.ok) throw new Error('Failed to fetch trends'); - const data = await response.json(); - analyticsData.trends = data.data; - return data.data; - } catch (error) { - console.error('Error fetching spending trends:', error); - throw error; - } -} - -/** - * Fetch category breakdown - */ -async function fetchCategoryBreakdown(type = 'expense', startDate = null, endDate = null) { - try { - let url = `${ANALYTICS_API_URL}/category-breakdown?type=${type}`; - if (startDate) url += `&startDate=${startDate}`; - if (endDate) url += `&endDate=${endDate}`; - - const response = await fetch(url, { headers: await getAuthHeaders() }); - if (!response.ok) throw new Error('Failed to fetch category breakdown'); - const data = await response.json(); - analyticsData.categoryBreakdown = data.data; - return data.data; - } catch (error) { - console.error('Error fetching category breakdown:', error); - throw error; - } -} - -/** - * Fetch insights - */ -async function fetchInsights() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/insights`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch insights'); - const data = await response.json(); - analyticsData.insights = data.data; - return data.data; - } catch (error) { - console.error('Error fetching insights:', error); - throw error; - } -} - -/** - * Fetch predictions - */ -async function fetchPredictions() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/predictions`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch predictions'); - const data = await response.json(); - analyticsData.predictions = data.data; - return data.data; - } catch (error) { - console.error('Error fetching predictions:', error); - throw error; - } -} - -/** - * Fetch spending velocity - */ -async function fetchSpendingVelocity() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/velocity`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch velocity'); - const data = await response.json(); - analyticsData.velocity = data.data; - return data.data; - } catch (error) { - console.error('Error fetching velocity:', error); - throw error; - } -} - -/** - * Fetch financial forecast - */ -async function fetchForecast() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/forecast`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch forecast'); - const data = await response.json(); - analyticsData.forecast = data.data; - return data.data; - } catch (error) { - console.error('Error fetching forecast:', error); - throw error; - } -} - -/** - * Fetch month-over-month comparison - */ -async function fetchComparison(months = 3) { - try { - const response = await fetch(`${ANALYTICS_API_URL}/comparison?months=${months}`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch comparison'); - const data = await response.json(); - return data.data; - } catch (error) { - console.error('Error fetching comparison:', error); - throw error; - } -} - -/** - * Fetch complete analytics summary - */ -async function fetchAnalyticsSummary() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/summary`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch summary'); - const data = await response.json(); - return data.data; - } catch (error) { - console.error('Error fetching summary:', error); - throw error; - } -} - -// ======================== -// UI Rendering Functions -// ======================== - -/** - * Render spending velocity widget - */ -function renderVelocityWidget(velocity) { - const container = document.getElementById('velocity-widget'); - if (!container) return; - - const progressPercent = Math.min(100, (velocity.dayOfMonth / 30) * 100); - - container.innerHTML = ` -
-

Spending Velocity

- Day ${velocity.dayOfMonth} of month -
-
-
- ${formatAnalyticsCurrency(velocity.currentSpent)} - Spent this month -
-
- ${formatAnalyticsCurrency(velocity.dailyAverage)} - Daily average -
-
- ${formatAnalyticsCurrency(velocity.projectedMonthEnd)} - Projected month end -
-
-
-
-
-
- ${velocity.daysRemaining} days remaining -
- `; -} - -/** - * Render category breakdown chart - */ -function renderCategoryChart(breakdown) { - const container = document.getElementById('category-chart'); - if (!container) return; - - if (!breakdown || breakdown.categories.length === 0) { - container.innerHTML = '
No expense data available
'; - return; - } - - // Clear previous chart - container.innerHTML = ''; - - const categoryColors = { - food: '#FF6B6B', - transport: '#4ECDC4', - entertainment: '#96CEB4', - utilities: '#FECA57', - healthcare: '#FF9FF3', - shopping: '#45B7D1', - other: '#A55EEA' - }; - - const categoryIcons = { - food: '🍽️', - transport: '🚗', - entertainment: '🎬', - utilities: '💡', - healthcare: '🏥', - shopping: '🛒', - other: '📋' - }; - - // Create chart header - const header = document.createElement('div'); - header.className = 'category-chart-header'; - header.innerHTML = ` -

Category Breakdown

- Total: ₹${breakdown.grandTotal.toLocaleString()} - `; - container.appendChild(header); - - // Create canvas for Chart.js - const canvas = document.createElement('canvas'); - canvas.id = 'category-pie-chart'; - canvas.style.maxWidth = '100%'; - canvas.style.height = '300px'; - container.appendChild(canvas); - - // Prepare data for Chart.js - const chartData = { - labels: breakdown.categories.map(cat => `${categoryIcons[cat.category] || '📋'} ${capitalizeFirst(cat.category)}`), - datasets: [{ - data: breakdown.categories.map(cat => cat.total), - backgroundColor: breakdown.categories.map(cat => categoryColors[cat.category] || '#999'), - borderColor: breakdown.categories.map(cat => categoryColors[cat.category] || '#999'), - borderWidth: 2, - hoverOffset: 10 - }] - }; - - // Create pie chart - new Chart(canvas, { - type: 'pie', - data: chartData, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'bottom', - labels: { - padding: 20, - usePointStyle: true, - font: { - size: 12, - family: 'Inter, sans-serif' - }, - color: '#ffffff' - } - }, - tooltip: { - callbacks: { - label: function(context) { - const value = context.raw; - const percentage = breakdown.categories[context.dataIndex].percentage; - return `₹${value.toLocaleString()} (${percentage}%)`; - } - }, - backgroundColor: 'rgba(15, 15, 35, 0.9)', - titleColor: '#64ffda', - bodyColor: '#ffffff', - borderColor: 'rgba(100, 255, 218, 0.3)', - borderWidth: 1 - } - } - } - }); -} - -/** - * Render spending trends chart - */ -function renderTrendsChart(trends) { - const container = document.getElementById('trends-chart'); - if (!container) return; - - if (!trends || trends.data.length === 0) { - container.innerHTML = '
Not enough data for trends
'; - return; - } - - // Clear previous chart - container.innerHTML = ''; - - // Create chart header - const header = document.createElement('div'); - header.className = 'trends-header'; - header.innerHTML = ` -

Spending Trends

- `; - container.appendChild(header); - - // Create canvas for Chart.js - const canvas = document.createElement('canvas'); - canvas.id = 'trends-line-chart'; - canvas.style.maxWidth = '100%'; - canvas.style.height = '300px'; - container.appendChild(canvas); - - // Prepare data for Chart.js - const chartData = { - labels: trends.data.map(item => formatPeriodLabel(item.period)), - datasets: [ - { - label: 'Income', - data: trends.data.map(item => item.income), - borderColor: '#00e676', - backgroundColor: 'rgba(0, 230, 118, 0.1)', - borderWidth: 3, - fill: false, - tension: 0.4, - pointBackgroundColor: '#00e676', - pointBorderColor: '#ffffff', - pointBorderWidth: 2, - pointRadius: 6, - pointHoverRadius: 8 - }, - { - label: 'Expense', - data: trends.data.map(item => item.expense), - borderColor: '#ff5722', - backgroundColor: 'rgba(255, 87, 34, 0.1)', - borderWidth: 3, - fill: false, - tension: 0.4, - pointBackgroundColor: '#ff5722', - pointBorderColor: '#ffffff', - pointBorderWidth: 2, - pointRadius: 6, - pointHoverRadius: 8 - } - ] - }; - - // Create line chart - new Chart(canvas, { - type: 'line', - data: chartData, - options: { - responsive: true, - maintainAspectRatio: false, - interaction: { - mode: 'index', - intersect: false - }, - plugins: { - legend: { - position: 'top', - labels: { - padding: 20, - usePointStyle: true, - font: { - size: 12, - family: 'Inter, sans-serif' - }, - color: '#ffffff' - } - }, - tooltip: { - callbacks: { - label: function(context) { - return `${context.dataset.label}: ₹${context.raw.toLocaleString()}`; - } - }, - backgroundColor: 'rgba(15, 15, 35, 0.9)', - titleColor: '#64ffda', - bodyColor: '#ffffff', - borderColor: 'rgba(100, 255, 218, 0.3)', - borderWidth: 1 - } - }, - scales: { - x: { - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: '#b4b4b4', - font: { - size: 11 - } - } - }, - y: { - beginAtZero: true, - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: '#b4b4b4', - font: { - size: 11 - }, - callback: function(value) { - return '₹' + value.toLocaleString(); - } - } - } - } - } - }); - - // Add summary section - if (trends.summary) { - const summary = document.createElement('div'); - summary.className = 'trends-summary'; - summary.innerHTML = ` -
- Avg Monthly Expense - ₹${trends.summary.avgMonthlyExpense.toLocaleString()} -
-
- Savings Rate - ${trends.summary.avgSavingsRate}% -
-
- Trend - - ${trends.summary.spendingTrend === 'decreasing' ? '↓' : '↑'} ${capitalizeFirst(trends.summary.spendingTrend)} - -
- `; - container.appendChild(summary); - } -} - -/** - * Render insights cards - */ -function renderInsights(insights) { - const container = document.getElementById('insights-container'); - if (!container) return; - - if (!insights || insights.insights.length === 0) { - container.innerHTML = '
No insights available yet
'; - return; - } - - const insightIcons = { - savings: 'piggy-bank', - category: 'tags', - trend: 'chart-line', - anomaly: 'exclamation-triangle', - info: 'info-circle' - }; - - const statusClasses = { - good: 'success', - moderate: 'warning', - warning: 'warning', - critical: 'danger' - }; - - container.innerHTML = ` -
-

Smart Insights

-
-
- ${insights.insights.map(insight => ` -
-
- -
-
-
${insight.title || capitalizeFirst(insight.type)}
-

${insight.message}

- ${insight.suggestion ? `${insight.suggestion}` : ''} -
-
- `).join('')} -
- `; -} - -/** - * Render predictions widget - */ -function renderPredictions(predictions) { - const container = document.getElementById('predictions-widget'); - if (!container) return; - - if (!predictions || !predictions.nextMonthPrediction) { - container.innerHTML = '
Need more data for predictions
'; - return; - } - - const trendIcon = predictions.trend === 'increasing' ? 'arrow-up' : - predictions.trend === 'decreasing' ? 'arrow-down' : 'minus'; - const trendClass = predictions.trend === 'decreasing' ? 'positive' : 'negative'; - - container.innerHTML = ` -
-

Spending Predictions

- Confidence: ${predictions.confidence}% -
-
- Next Month Forecast - ${formatAnalyticsCurrency(predictions.nextMonthPrediction)} - - - ${capitalizeFirst(predictions.trend)} - -
-
-
- Historical Avg - ${formatAnalyticsCurrency(predictions.historicalAverage)} -
-
- Moving Avg - ${formatAnalyticsCurrency(predictions.movingAverage)} -
-
- Based on - ${predictions.basedOnMonths} months -
-
- `; -} - -/** - * Render forecast widget (Safe-to-Spend) - */ -function renderForecastWidget(forecast) { - const container = document.getElementById('forecast-widget'); - if (!container) return; - - const sts = forecast.safe_to_spend || forecast.safeToSpend; - - container.innerHTML = ` -
-

Safe-to-Spend

- ${sts.remainingDays} days left in month -
-
-
- Daily Limit - ₹${sts.daily.toLocaleString()} -
-
- Total Available - ₹${sts.total.toLocaleString()} -
-
-
-
- Recurring Bills - ₹${sts.commitments.recurring.toLocaleString()} -
-
- Goal Targets - ₹${sts.commitments.goals.toLocaleString()} -
-
-
- ${forecast.anomalies && forecast.anomalies.length > 0 ? ` -
- - ${forecast.anomalies[0].message} -
- ` : ` -
- - No spending anomalies detected this week -
- `} -
- `; -} - -// ======================== -// Helper Functions -// ======================== - -function capitalizeFirst(str) { - if (!str) return ''; - return str.charAt(0).toUpperCase() + str.slice(1); -} - -function formatPeriodLabel(period) { - // Format: 2024-01 to Jan - if (period.includes('-W')) { - return `W${period.split('-W')[1]}`; - } - const parts = period.split('-'); - if (parts.length === 2) { - const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - return months[parseInt(parts[1]) - 1] || period; - } - return period; -} - -function showAnalyticsNotification(message, type = 'info') { - if (typeof showNotification === 'function') { - showNotification(message, type); - return; - } - console.log(`[${type}] ${message}`); -} - -// ======================== -// Dashboard Loading -// ======================== - -async function loadAnalyticsDashboard() { - const dashboardContainer = document.getElementById('analytics-dashboard'); - if (!dashboardContainer) return; - - const token = localStorage.getItem('token'); - if (!token) return; - - try { - // Show loading state - dashboardContainer.classList.add('loading'); - - // Fetch all analytics data in parallel - const [velocity, breakdown, trends, insights, predictions, forecast] = await Promise.all([ - fetchSpendingVelocity().catch(() => null), - fetchCategoryBreakdown().catch(() => null), - fetchSpendingTrends().catch(() => null), - fetchInsights().catch(() => null), - fetchPredictions().catch(() => null), - fetchForecast().catch(() => null) - ]); - - // Render all widgets - if (velocity) renderVelocityWidget(velocity); - if (breakdown) renderCategoryChart(breakdown); - if (trends) renderTrendsChart(trends); - if (insights) renderInsights(insights); - if (predictions) renderPredictions(predictions); - if (forecast) renderForecastWidget(forecast); - - dashboardContainer.classList.remove('loading'); - } catch (error) { - console.error('Error loading analytics dashboard:', error); - showAnalyticsNotification('Failed to load analytics', 'error'); - dashboardContainer.classList.remove('loading'); - } -} - -// ======================== -// Initialization -// ======================== - -function initAnalyticsDashboard() { - const dashboardContainer = document.getElementById('analytics-dashboard'); - if (!dashboardContainer) return; - - // Refresh button - const refreshBtn = document.getElementById('refresh-analytics'); - if (refreshBtn) { - refreshBtn.addEventListener('click', loadAnalyticsDashboard); - } - - // Period selector for trends - const periodSelect = document.getElementById('trends-period'); - if (periodSelect) { - periodSelect.addEventListener('change', async (e) => { - const trends = await fetchSpendingTrends(e.target.value); - renderTrendsChart(trends); - }); - } - - // Load initial data - loadAnalyticsDashboard(); -} - -// Initialize when DOM is ready -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initAnalyticsDashboard); -} else { - initAnalyticsDashboard(); -} - -// ======================== -// Health Score & Gamification UI (Issue #421) -// ======================== - -let gamificationData = { - healthScore: null, - profile: null, - badges: null, - leaderboard: null -}; - -/** - * Fetch complete health score data - */ -async function fetchHealthScore() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/gamification/health-score`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch health score'); - const data = await response.json(); - gamificationData.healthScore = data.data; - return data.data; - } catch (error) { - console.error('Error fetching health score:', error); - throw error; - } -} - -/** - * Fetch gamification profile - */ -async function fetchGamificationProfile() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/gamification/profile`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch profile'); - const data = await response.json(); - gamificationData.profile = data.data; - return data.data; - } catch (error) { - console.error('Error fetching gamification profile:', error); - throw error; - } -} - -/** - * Fetch all badges - */ -async function fetchBadges() { - try { - const response = await fetch(`${ANALYTICS_API_URL}/gamification/badges`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch badges'); - const data = await response.json(); - gamificationData.badges = data.data; - return data.data; - } catch (error) { - console.error('Error fetching badges:', error); - throw error; - } -} - -/** - * Fetch leaderboard - */ -async function fetchLeaderboard(type = 'health') { - try { - const response = await fetch(`${ANALYTICS_API_URL}/gamification/leaderboard?type=${type}&limit=10`, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch leaderboard'); - const data = await response.json(); - gamificationData.leaderboard = data.data; - return data.data; - } catch (error) { - console.error('Error fetching leaderboard:', error); - throw error; - } -} - -/** - * Update financial profile - */ -async function updateFinancialProfile(profileData) { - try { - const response = await fetch(`${ANALYTICS_API_URL}/gamification/financial-profile`, { - method: 'PUT', - headers: await getAuthHeaders(), - body: JSON.stringify(profileData) - }); - if (!response.ok) throw new Error('Failed to update profile'); - const data = await response.json(); - showAnalyticsNotification('Financial profile updated!', 'success'); - return data.data; - } catch (error) { - console.error('Error updating financial profile:', error); - showAnalyticsNotification('Failed to update profile', 'error'); - throw error; - } -} - -/** - * Render Health Score Dashboard - */ -function renderHealthScoreDashboard(healthData) { - const container = document.getElementById('health-score-dashboard'); - if (!container) return; - - const { score, grade, components, communityComparison, insights } = healthData; - - // Get score color based on grade - const scoreColors = { - 'A+': '#00e676', 'A': '#00c853', 'B+': '#4caf50', 'B': '#8bc34a', - 'C+': '#ffc107', 'C': '#ff9800', 'D': '#ff5722', 'F': '#f44336' - }; - const scoreColor = scoreColors[grade] || '#ffc107'; - - container.innerHTML = ` -
- -
-
- - - - -
- ${score} - ${grade} -
-
-

Financial Health Score

-

- - ${communityComparison.rank} of users -

-
- - -
-

Score Breakdown

- ${renderScoreComponent('Savings Rate', components.savingsRate, '💰', 20)} - ${renderScoreComponent('Budget Discipline', components.budgetDiscipline, '📊', 25)} - ${renderScoreComponent('Debt-to-Income', components.debtToIncome, '💳', 20)} - ${renderScoreComponent('Emergency Fund', components.emergencyFund, '🛡️', 15)} - ${renderScoreComponent('Investment', components.investmentConsistency, '📈', 20)} -
-
- - -
-

Community Comparison

-
- ${communityComparison.comparisons.map(comp => ` -
- ${comp.component} - ${comp.message} -
- `).join('')} - ${communityComparison.comparisons.length === 0 ? ` -
- Keep improving to beat the average! -
- ` : ''} -
-
- - -
-

Personalized Insights

- - ${insights.strengths.length > 0 ? ` -
-
💪 Your Strengths
- ${insights.strengths.map(s => ` -
- ${s.icon} - ${s.message} -
- `).join('')} -
- ` : ''} - - ${insights.improvements.length > 0 ? ` -
-
📈 Areas to Improve
- ${insights.improvements.map(i => ` -
- ${i.icon} -
- ${i.message} - ${i.priority} -
-
- `).join('')} -
- ` : ''} -
- - -
-

Score History

- -
- `; - - // Render history chart if data exists - if (healthData.history && healthData.history.length > 0) { - renderHealthHistoryChart(healthData.history); - } -} - -/** - * Render individual score component bar - */ -function renderScoreComponent(name, data, icon, weight) { - const score = data.score; - const barColor = score >= 70 ? '#00e676' : score >= 50 ? '#ffc107' : '#ff5722'; - - return ` -
-
- ${icon} - ${name} - ${weight}% -
-
-
-
- -
- `; -} - -/** - * Render health history chart - */ -function renderHealthHistoryChart(history) { - const canvas = document.getElementById('health-history-chart'); - if (!canvas || !history.length) return; - - const labels = history.map(h => { - const d = new Date(h.date); - return `${d.toLocaleString('default', { month: 'short' })}`; - }); - - new Chart(canvas, { - type: 'line', - data: { - labels, - datasets: [{ - label: 'Health Score', - data: history.map(h => h.score), - borderColor: '#64ffda', - backgroundColor: 'rgba(100, 255, 218, 0.1)', - borderWidth: 3, - fill: true, - tension: 0.4, - pointBackgroundColor: '#64ffda', - pointBorderColor: '#fff', - pointRadius: 6 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { display: false } - }, - scales: { - y: { - min: 0, - max: 100, - grid: { color: 'rgba(255,255,255,0.1)' }, - ticks: { color: '#b4b4b4' } - }, - x: { - grid: { color: 'rgba(255,255,255,0.1)' }, - ticks: { color: '#b4b4b4' } - } - } - } - }); -} - -/** - * Render Gamification Profile (Level, XP, Badges) - */ -function renderGamificationProfile(profile) { - const container = document.getElementById('gamification-profile'); - if (!container) return; - - const xpProgress = profile.xpToNextLevel > 0 - ? (profile.currentLevelXp / profile.xpToNextLevel) * 100 - : 0; - - container.innerHTML = ` -
-
-
- Lv.${profile.level} -
-
-

${profile.levelName}

-
-
-
- ${profile.currentLevelXp} / ${profile.xpToNextLevel} XP -
-
-
-
- ${profile.totalPoints.toLocaleString()} - Total XP -
-
- ${profile.streakDays} - Day Streak 🔥 -
-
- ${profile.badgeCount} - Badges -
-
-
- `; -} - -/** - * Render Badges Grid - */ -function renderBadgesGrid(badges) { - const container = document.getElementById('badges-grid'); - if (!container) return; - - // Separate earned and unearned - const earned = badges.filter(b => b.earned); - const unearned = badges.filter(b => !b.earned); - - const tierColors = { - bronze: '#CD7F32', - silver: '#C0C0C0', - gold: '#FFD700', - platinum: '#E5E4E2', - diamond: '#B9F2FF' - }; - - container.innerHTML = ` -
-

Earned Badges (${earned.length})

-
- ${earned.map(badge => ` -
- ${badge.icon} - ${badge.name} - ${badge.tier} - ${new Date(badge.earnedAt).toLocaleDateString()} -
- `).join('')} - ${earned.length === 0 ? '

Complete challenges to earn badges!

' : ''} -
-
- -
-

Locked Badges (${unearned.length})

-
- ${unearned.map(badge => ` -
- 🔒 - ${badge.name} - ${badge.tier} -
- `).join('')} -
-
- `; -} - -/** - * Render Leaderboard - */ -function renderLeaderboard(leaderboard) { - const container = document.getElementById('leaderboard'); - if (!container) return; - - container.innerHTML = ` -
-

Community Leaderboard

-
- - -
-
-
- ${leaderboard.map((user, idx) => ` -
- ${idx === 0 ? '🥇' : idx === 1 ? '🥈' : idx === 2 ? '🥉' : '#' + (idx + 1)} - ${user.name} - ${user.healthScore} - ${user.healthGrade} -
- `).join('')} -
- `; -} - -/** - * Switch leaderboard type - */ -async function switchLeaderboard(type) { - const tabs = document.querySelectorAll('.lb-tab'); - tabs.forEach(t => t.classList.remove('active')); - event.target.classList.add('active'); - - const leaderboard = await fetchLeaderboard(type); - renderLeaderboard(leaderboard); -} - -/** - * Render Financial Profile Form - */ -function renderFinancialProfileForm(currentProfile = {}) { - const container = document.getElementById('financial-profile-form'); - if (!container) return; - - container.innerHTML = ` -

Your Financial Profile

-

Update these values for accurate health score calculation

-
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- -
- `; - - // Handle form submission - container.querySelector('#profile-update-form').addEventListener('submit', async (e) => { - e.preventDefault(); - const formData = new FormData(e.target); - const data = {}; - for (const [key, value] of formData.entries()) { - if (value) data[key] = parseFloat(value); - } - await updateFinancialProfile(data); - await loadHealthDashboard(); // Refresh scores - }); -} - -/** - * Load complete Health & Achievements dashboard - */ -async function loadHealthDashboard() { - const container = document.getElementById('health-achievements-section'); - if (!container) return; - - const token = localStorage.getItem('authToken'); - if (!token) return; - - try { - container.classList.add('loading'); - - // Fetch all gamification data in parallel - const [healthScore, profile, badges, leaderboard] = await Promise.all([ - fetchHealthScore().catch(() => null), - fetchGamificationProfile().catch(() => null), - fetchBadges().catch(() => null), - fetchLeaderboard().catch(() => null) - ]); - - // Render all components - if (healthScore) renderHealthScoreDashboard(healthScore); - if (profile) renderGamificationProfile(profile); - if (badges) renderBadgesGrid(badges); - if (leaderboard) renderLeaderboard(leaderboard); - - // Render financial profile form with current values - renderFinancialProfileForm(healthScore?.components?.debtToIncome?.details || {}); - - container.classList.remove('loading'); - } catch (error) { - console.error('Error loading health dashboard:', error); - showAnalyticsNotification('Failed to load health dashboard', 'error'); - container.classList.remove('loading'); - } -} - -/** - * Generate social share preview - */ -function generateSharePreview(healthData, profile) { - return { - title: `My Financial Health Score: ${healthData.score} (${healthData.grade})`, - description: `I'm Level ${profile.level} (${profile.levelName}) with ${profile.badgeCount} badges! Check your financial health on ExpenseFlow.`, - image: null, // Could generate a canvas image - url: window.location.origin - }; -} - -/** - * Share health score - */ -async function shareHealthScore() { - if (!gamificationData.healthScore || !gamificationData.profile) { - showAnalyticsNotification('Calculate your health score first!', 'warning'); - return; - } - - const shareData = generateSharePreview(gamificationData.healthScore, gamificationData.profile); - - if (navigator.share) { - try { - await navigator.share({ - title: shareData.title, - text: shareData.description, - url: shareData.url - }); - } catch (err) { - console.log('Share cancelled'); - } - } else { - // Fallback: copy to clipboard - const text = `${shareData.title}\n${shareData.description}\n${shareData.url}`; - navigator.clipboard.writeText(text); - showAnalyticsNotification('Copied to clipboard!', 'success'); - } -} - -/** - * Initialize Health & Achievements tab - */ -function initHealthAchievements() { - const healthTab = document.getElementById('health-tab'); - if (healthTab) { - healthTab.addEventListener('click', (e) => { - e.preventDefault(); - showHealthSection(); - loadHealthDashboard(); - }); - } - - // Share button - const shareBtn = document.getElementById('share-health-btn'); - if (shareBtn) { - shareBtn.addEventListener('click', shareHealthScore); - } - - // Refresh button - const refreshBtn = document.getElementById('refresh-health-btn'); - if (refreshBtn) { - refreshBtn.addEventListener('click', loadHealthDashboard); - } -} - -/** - * Show health section and hide others - */ -function showHealthSection() { - // Hide all main sections - const sections = ['dashboard', 'analytics', 'goals', 'settings', 'health']; - sections.forEach(id => { - const section = document.getElementById(id); - if (section) { - section.style.display = id === 'health' ? 'block' : 'none'; - } - }); - - // Update active nav link - document.querySelectorAll('.nav-link').forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === '#health') { - link.classList.add('active'); - } - }); -} - -// Initialize health achievements when DOM is ready -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initHealthAchievements); -} else { - initHealthAchievements(); -} - -// Export for use in other modules -if (typeof module !== 'undefined' && module.exports) { - module.exports = { - fetchSpendingTrends, - fetchCategoryBreakdown, - fetchInsights, - fetchPredictions, - fetchSpendingVelocity, - fetchComparison, - fetchAnalyticsSummary, - loadAnalyticsDashboard, - // Gamification exports - fetchHealthScore, - fetchGamificationProfile, - fetchBadges, - fetchLeaderboard, - loadHealthDashboard, - shareHealthScore - }; -} diff --git a/analytics.html b/analytics.html deleted file mode 100644 index 31328d94..00000000 --- a/analytics.html +++ /dev/null @@ -1,1117 +0,0 @@ - - - - - - - Analytics - ExpenseFlow - - - - - - - - - - - - -
- -
- - -
-
- - - -
-
-
- - -
- - -
- - -
- -
-
- - -
-
-
- -

Total Expenses

-
-
₹0
-
- - 0% vs last period -
-
- -
-
- -

Average Daily

-
-
₹0
-
- - 5% vs last period -
-
- -
-
- -

Transactions

-
-
0
-
- 0 this period -
-
- -
-
- -

Top Category

-
-
-
-
- ₹0 spent -
-
-
- - -
-
-
- -

Spending Trend

-
-
- -
-
- -
-
- -

Category Breakdown

-
-
- -
-
-
- -
-
-
- -

Monthly Comparison

-
-
- -
-
- -
-
- -

Day of Week Analysis

-
-
- -
-
-
- - -
-
- -

Top Spending Categories

-
-
- -
-
- - -
-
- -

Smart Insights

-
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/2fa-manage.css b/client/2fa-manage.css similarity index 100% rename from 2fa-manage.css rename to client/2fa-manage.css diff --git a/2fa-manage.html b/client/2fa-manage.html similarity index 100% rename from 2fa-manage.html rename to client/2fa-manage.html diff --git a/2fa-manage.js b/client/2fa-manage.js similarity index 100% rename from 2fa-manage.js rename to client/2fa-manage.js diff --git a/2fa-setup.css b/client/2fa-setup.css similarity index 100% rename from 2fa-setup.css rename to client/2fa-setup.css diff --git a/2fa-setup.html b/client/2fa-setup.html similarity index 100% rename from 2fa-setup.html rename to client/2fa-setup.html diff --git a/2fa-setup.js b/client/2fa-setup.js similarity index 100% rename from 2fa-setup.js rename to client/2fa-setup.js diff --git a/public/AboutUs.html b/client/AboutUs.html similarity index 100% rename from public/AboutUs.html rename to client/AboutUs.html diff --git a/public/Community.html b/client/Community.html similarity index 100% rename from public/Community.html rename to client/Community.html diff --git a/public/CurrencyConverter.html b/client/CurrencyConverter.html similarity index 100% rename from public/CurrencyConverter.html rename to client/CurrencyConverter.html diff --git a/client/Dockerfile b/client/Dockerfile new file mode 100644 index 00000000..d2a291fd --- /dev/null +++ b/client/Dockerfile @@ -0,0 +1,17 @@ +# Use Nginx as the base image +FROM nginx:alpine + +# Remove default nginx static assets +RUN rm -rf /usr/share/nginx/html/* + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy static assets to nginx html directory +COPY . /usr/share/nginx/html/ + +# Expose port 80 +EXPOSE 80 + +# Start Nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/public/ExpenseSplitcalc.css b/client/ExpenseSplitcalc.css similarity index 100% rename from public/ExpenseSplitcalc.css rename to client/ExpenseSplitcalc.css diff --git a/public/ExpenseSplitcalc.html b/client/ExpenseSplitcalc.html similarity index 100% rename from public/ExpenseSplitcalc.html rename to client/ExpenseSplitcalc.html diff --git a/public/ExpenseSplitcalc.js b/client/ExpenseSplitcalc.js similarity index 100% rename from public/ExpenseSplitcalc.js rename to client/ExpenseSplitcalc.js diff --git a/public/Help-Center.html b/client/Help-Center.html similarity index 100% rename from public/Help-Center.html rename to client/Help-Center.html diff --git a/public/Monthlysummary.css b/client/Monthlysummary.css similarity index 100% rename from public/Monthlysummary.css rename to client/Monthlysummary.css diff --git a/public/Monthlysummary.html b/client/Monthlysummary.html similarity index 100% rename from public/Monthlysummary.html rename to client/Monthlysummary.html diff --git a/public/Monthlysummary.js b/client/Monthlysummary.js similarity index 100% rename from public/Monthlysummary.js rename to client/Monthlysummary.js diff --git a/public/PrivacyPolicy.html b/client/PrivacyPolicy.html similarity index 100% rename from public/PrivacyPolicy.html rename to client/PrivacyPolicy.html diff --git a/public/REPORTS_ACCESSIBILITY.md b/client/REPORTS_ACCESSIBILITY.md similarity index 100% rename from public/REPORTS_ACCESSIBILITY.md rename to client/REPORTS_ACCESSIBILITY.md diff --git a/public/aboutus.css b/client/aboutus.css similarity index 100% rename from public/aboutus.css rename to client/aboutus.css diff --git a/public/accounts-dashboard.css b/client/accounts-dashboard.css similarity index 100% rename from public/accounts-dashboard.css rename to client/accounts-dashboard.css diff --git a/public/accounts-dashboard.js b/client/accounts-dashboard.js similarity index 100% rename from public/accounts-dashboard.js rename to client/accounts-dashboard.js diff --git a/public/accounts.html b/client/accounts.html similarity index 100% rename from public/accounts.html rename to client/accounts.html diff --git a/public/analytics-dashboard.js b/client/analytics-dashboard.js similarity index 100% rename from public/analytics-dashboard.js rename to client/analytics-dashboard.js diff --git a/analytics-local.js b/client/analytics-local.js similarity index 100% rename from analytics-local.js rename to client/analytics-local.js diff --git a/public/analytics.css b/client/analytics.css similarity index 100% rename from public/analytics.css rename to client/analytics.css diff --git a/public/analytics.html b/client/analytics.html similarity index 100% rename from public/analytics.html rename to client/analytics.html diff --git a/public/analytics.js b/client/analytics.js similarity index 100% rename from public/analytics.js rename to client/analytics.js diff --git a/api-integration.js b/client/api-integration.js similarity index 100% rename from api-integration.js rename to client/api-integration.js diff --git a/public/approval-dashboard.html b/client/approval-dashboard.html similarity index 100% rename from public/approval-dashboard.html rename to client/approval-dashboard.html diff --git a/public/asset-inventory.html b/client/asset-inventory.html similarity index 100% rename from public/asset-inventory.html rename to client/asset-inventory.html diff --git a/auth-integration.js b/client/auth-integration.js similarity index 100% rename from auth-integration.js rename to client/auth-integration.js diff --git a/public/auth.js b/client/auth.js similarity index 100% rename from public/auth.js rename to client/auth.js diff --git a/budget-goals.js b/client/budget-goals.js similarity index 100% rename from budget-goals.js rename to client/budget-goals.js diff --git a/public/budget-planner.html b/client/budget-planner.html similarity index 100% rename from public/budget-planner.html rename to client/budget-planner.html diff --git a/public/budget.css b/client/budget.css similarity index 100% rename from public/budget.css rename to client/budget.css diff --git a/public/budget.html b/client/budget.html similarity index 100% rename from public/budget.html rename to client/budget.html diff --git a/public/budget.js b/client/budget.js similarity index 100% rename from public/budget.js rename to client/budget.js diff --git a/public/challenges.html b/client/challenges.html similarity index 100% rename from public/challenges.html rename to client/challenges.html diff --git a/public/chat-assistant.js b/client/chat-assistant.js similarity index 100% rename from public/chat-assistant.js rename to client/chat-assistant.js diff --git a/public/community.css b/client/community.css similarity index 100% rename from public/community.css rename to client/community.css diff --git a/public/community.js b/client/community.js similarity index 100% rename from public/community.js rename to client/community.js diff --git a/public/compliance-center.html b/client/compliance-center.html similarity index 100% rename from public/compliance-center.html rename to client/compliance-center.html diff --git a/public/contact.css b/client/contact.css similarity index 100% rename from public/contact.css rename to client/contact.css diff --git a/public/contact.html b/client/contact.html similarity index 100% rename from public/contact.html rename to client/contact.html diff --git a/public/contact.js b/client/contact.js similarity index 100% rename from public/contact.js rename to client/contact.js diff --git a/public/contribute.css b/client/contribute.css similarity index 100% rename from public/contribute.css rename to client/contribute.css diff --git a/public/contribute.html b/client/contribute.html similarity index 100% rename from public/contribute.html rename to client/contribute.html diff --git a/public/contributor.html b/client/contributor.html similarity index 100% rename from public/contributor.html rename to client/contributor.html diff --git a/public/cookiepolicy.html b/client/cookiepolicy.html similarity index 100% rename from public/cookiepolicy.html rename to client/cookiepolicy.html diff --git a/public/css/folders.css b/client/css/folders.css similarity index 100% rename from public/css/folders.css rename to client/css/folders.css diff --git a/public/currency-settings.html b/client/currency-settings.html similarity index 100% rename from public/currency-settings.html rename to client/currency-settings.html diff --git a/public/currencyConverter.css b/client/currencyConverter.css similarity index 100% rename from public/currencyConverter.css rename to client/currencyConverter.css diff --git a/public/currencyConverter.js b/client/currencyConverter.js similarity index 100% rename from public/currencyConverter.js rename to client/currencyConverter.js diff --git a/public/dashboard-clean.html b/client/dashboard-clean.html similarity index 100% rename from public/dashboard-clean.html rename to client/dashboard-clean.html diff --git a/public/dashboard.css b/client/dashboard.css similarity index 100% rename from public/dashboard.css rename to client/dashboard.css diff --git a/public/dashboard.html b/client/dashboard.html similarity index 100% rename from public/dashboard.html rename to client/dashboard.html diff --git a/public/dashboard.js b/client/dashboard.js similarity index 100% rename from public/dashboard.js rename to client/dashboard.js diff --git a/public/db-manager.js b/client/db-manager.js similarity index 100% rename from public/db-manager.js rename to client/db-manager.js diff --git a/public/debt-dashboard.html b/client/debt-dashboard.html similarity index 100% rename from public/debt-dashboard.html rename to client/debt-dashboard.html diff --git a/debug-auth.html b/client/debug-auth.html similarity index 100% rename from debug-auth.html rename to client/debug-auth.html diff --git a/public/expensetracker.css b/client/expensetracker.css similarity index 100% rename from public/expensetracker.css rename to client/expensetracker.css diff --git a/export-feature.js b/client/export-feature.js similarity index 100% rename from export-feature.js rename to client/export-feature.js diff --git a/public/faq.css b/client/faq.css similarity index 100% rename from public/faq.css rename to client/faq.css diff --git a/public/faq.html b/client/faq.html similarity index 100% rename from public/faq.html rename to client/faq.html diff --git a/public/faq.js b/client/faq.js similarity index 100% rename from public/faq.js rename to client/faq.js diff --git a/public/feedback.css b/client/feedback.css similarity index 100% rename from public/feedback.css rename to client/feedback.css diff --git a/public/feedback.html b/client/feedback.html similarity index 100% rename from public/feedback.html rename to client/feedback.html diff --git a/public/feedback.js b/client/feedback.js similarity index 100% rename from public/feedback.js rename to client/feedback.js diff --git a/public/finance-tips.css b/client/finance-tips.css similarity index 100% rename from public/finance-tips.css rename to client/finance-tips.css diff --git a/public/finance-tips.html b/client/finance-tips.html similarity index 100% rename from public/finance-tips.html rename to client/finance-tips.html diff --git a/public/finance-tips.js b/client/finance-tips.js similarity index 100% rename from public/finance-tips.js rename to client/finance-tips.js diff --git a/public/forecasting.html b/client/forecasting.html similarity index 100% rename from public/forecasting.html rename to client/forecasting.html diff --git a/public/gamification.js b/client/gamification.js similarity index 100% rename from public/gamification.js rename to client/gamification.js diff --git a/public/goal-feature.js b/client/goal-feature.js similarity index 100% rename from public/goal-feature.js rename to client/goal-feature.js diff --git a/public/goals.css b/client/goals.css similarity index 100% rename from public/goals.css rename to client/goals.css diff --git a/public/goals.html b/client/goals.html similarity index 100% rename from public/goals.html rename to client/goals.html diff --git a/public/goals.js b/client/goals.js similarity index 100% rename from public/goals.js rename to client/goals.js diff --git a/groups.html b/client/groups.html similarity index 100% rename from groups.html rename to client/groups.html diff --git a/groups.js b/client/groups.js similarity index 100% rename from groups.js rename to client/groups.js diff --git a/public/helpcenter.css b/client/helpcenter.css similarity index 100% rename from public/helpcenter.css rename to client/helpcenter.css diff --git a/public/helpcenter.js b/client/helpcenter.js similarity index 100% rename from public/helpcenter.js rename to client/helpcenter.js diff --git a/public/i18n.js b/client/i18n.js similarity index 100% rename from public/i18n.js rename to client/i18n.js diff --git a/public/index.css b/client/index.css similarity index 100% rename from public/index.css rename to client/index.css diff --git a/public/index.css.bak b/client/index.css.bak similarity index 100% rename from public/index.css.bak rename to client/index.css.bak diff --git a/public/index.html b/client/index.html similarity index 100% rename from public/index.html rename to client/index.html diff --git a/public/index.js b/client/index.js similarity index 100% rename from public/index.js rename to client/index.js diff --git a/public/inventory-hub.html b/client/inventory-hub.html similarity index 100% rename from public/inventory-hub.html rename to client/inventory-hub.html diff --git a/public/investment-portfolio.js b/client/investment-portfolio.js similarity index 100% rename from public/investment-portfolio.js rename to client/investment-portfolio.js diff --git a/join-workspace.html b/client/join-workspace.html similarity index 100% rename from join-workspace.html rename to client/join-workspace.html diff --git a/public/js/approval-controller.js b/client/js/approval-controller.js similarity index 100% rename from public/js/approval-controller.js rename to client/js/approval-controller.js diff --git a/public/js/asset-controller.js b/client/js/asset-controller.js similarity index 100% rename from public/js/asset-controller.js rename to client/js/asset-controller.js diff --git a/public/js/budget-controller.js b/client/js/budget-controller.js similarity index 100% rename from public/js/budget-controller.js rename to client/js/budget-controller.js diff --git a/public/js/compliance-controller.js b/client/js/compliance-controller.js similarity index 100% rename from public/js/compliance-controller.js rename to client/js/compliance-controller.js diff --git a/public/js/currency-intelligence.js b/client/js/currency-intelligence.js similarity index 100% rename from public/js/currency-intelligence.js rename to client/js/currency-intelligence.js diff --git a/public/js/debt-logic.js b/client/js/debt-logic.js similarity index 100% rename from public/js/debt-logic.js rename to client/js/debt-logic.js diff --git a/public/js/folders.js b/client/js/folders.js similarity index 100% rename from public/js/folders.js rename to client/js/folders.js diff --git a/public/js/forecast-ctrl.js b/client/js/forecast-ctrl.js similarity index 100% rename from public/js/forecast-ctrl.js rename to client/js/forecast-ctrl.js diff --git a/public/js/inventory-controller.js b/client/js/inventory-controller.js similarity index 100% rename from public/js/inventory-controller.js rename to client/js/inventory-controller.js diff --git a/public/js/payroll-controller.js b/client/js/payroll-controller.js similarity index 100% rename from public/js/payroll-controller.js rename to client/js/payroll-controller.js diff --git a/public/js/project-billing-controller.js b/client/js/project-billing-controller.js similarity index 100% rename from public/js/project-billing-controller.js rename to client/js/project-billing-controller.js diff --git a/public/js/tag-controller.js b/client/js/tag-controller.js similarity index 100% rename from public/js/tag-controller.js rename to client/js/tag-controller.js diff --git a/public/js/team-controller.js b/client/js/team-controller.js similarity index 100% rename from public/js/team-controller.js rename to client/js/team-controller.js diff --git a/public/js/treasury-controller.js b/client/js/treasury-controller.js similarity index 100% rename from public/js/treasury-controller.js rename to client/js/treasury-controller.js diff --git a/public/loanCalculator.css b/client/loanCalculator.css similarity index 100% rename from public/loanCalculator.css rename to client/loanCalculator.css diff --git a/public/loanCalculator.html b/client/loanCalculator.html similarity index 100% rename from public/loanCalculator.html rename to client/loanCalculator.html diff --git a/public/loanCalculator.js b/client/loanCalculator.js similarity index 100% rename from public/loanCalculator.js rename to client/loanCalculator.js diff --git a/login-signup.html b/client/login-signup.html similarity index 96% rename from login-signup.html rename to client/login-signup.html index c9cb55de..98700d3b 100644 --- a/login-signup.html +++ b/client/login-signup.html @@ -1,305 +1,305 @@ - - - - - - Login / Signup – All in One - - - - - - -
-

Login

- - - -
- -
- -
- -
- - - -
- Don't have an account? Sign Up -
-
- - -
- - - - - + + + + + + Login / Signup – All in One + + + + + + +
+

Login

+ + + +
+ +
+ +
+ +
+ + + +
+ Don't have an account? Sign Up +
+
+ + +
+ + + + + \ No newline at end of file diff --git a/public/login.css b/client/login.css similarity index 100% rename from public/login.css rename to client/login.css diff --git a/public/login.html b/client/login.html similarity index 100% rename from public/login.html rename to client/login.html diff --git a/public/login.js b/client/login.js similarity index 100% rename from public/login.js rename to client/login.js diff --git a/public/manifest.json b/client/manifest.json similarity index 100% rename from public/manifest.json rename to client/manifest.json diff --git a/modal-test.html b/client/modal-test.html similarity index 100% rename from modal-test.html rename to client/modal-test.html diff --git a/client/nginx.conf b/client/nginx.conf new file mode 100644 index 00000000..07594845 --- /dev/null +++ b/client/nginx.conf @@ -0,0 +1,29 @@ +server { + listen 80; + + # Serve static files + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ =404; + } + + # Proxy API requests to backend + location /api { + proxy_pass http://server:5000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # Proxy Socket.IO requests + location /socket.io/ { + proxy_pass http://server:5000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + } +} diff --git a/notification-center.js b/client/notification-center.js similarity index 100% rename from notification-center.js rename to client/notification-center.js diff --git a/public/payroll-management.html b/client/payroll-management.html similarity index 100% rename from public/payroll-management.html rename to client/payroll-management.html diff --git a/public/performance-min.css b/client/performance-min.css similarity index 100% rename from public/performance-min.css rename to client/performance-min.css diff --git a/public/performance.html b/client/performance.html similarity index 100% rename from public/performance.html rename to client/performance.html diff --git a/public/performance.js b/client/performance.js similarity index 100% rename from public/performance.js rename to client/performance.js diff --git a/public/profile.css b/client/profile.css similarity index 100% rename from public/profile.css rename to client/profile.css diff --git a/public/profile.html b/client/profile.html similarity index 100% rename from public/profile.html rename to client/profile.html diff --git a/public/profile.js b/client/profile.js similarity index 100% rename from public/profile.js rename to client/profile.js diff --git a/public/project-billing.html b/client/project-billing.html similarity index 100% rename from public/project-billing.html rename to client/project-billing.html diff --git a/public/protect.js b/client/protect.js similarity index 100% rename from public/protect.js rename to client/protect.js diff --git a/public/pwa-features.html b/client/pwa-features.html similarity index 100% rename from public/pwa-features.html rename to client/pwa-features.html diff --git a/public/pwa-features.js b/client/pwa-features.js similarity index 100% rename from public/pwa-features.js rename to client/pwa-features.js diff --git a/public/pwa-min.css b/client/pwa-min.css similarity index 100% rename from public/pwa-min.css rename to client/pwa-min.css diff --git a/realtime-sync.js b/client/realtime-sync.js similarity index 100% rename from realtime-sync.js rename to client/realtime-sync.js diff --git a/public/receipt-ocr.js b/client/receipt-ocr.js similarity index 100% rename from public/receipt-ocr.js rename to client/receipt-ocr.js diff --git a/receipt-upload.js b/client/receipt-upload.js similarity index 100% rename from receipt-upload.js rename to client/receipt-upload.js diff --git a/recurring-expenses.js b/client/recurring-expenses.js similarity index 100% rename from recurring-expenses.js rename to client/recurring-expenses.js diff --git a/public/report-generator.js b/client/report-generator.js similarity index 100% rename from public/report-generator.js rename to client/report-generator.js diff --git a/public/reports-critical.js b/client/reports-critical.js similarity index 100% rename from public/reports-critical.js rename to client/reports-critical.js diff --git a/public/reports-min.css b/client/reports-min.css similarity index 100% rename from public/reports-min.css rename to client/reports-min.css diff --git a/public/reports-optimized.css b/client/reports-optimized.css similarity index 100% rename from public/reports-optimized.css rename to client/reports-optimized.css diff --git a/public/reports-performance.html b/client/reports-performance.html similarity index 100% rename from public/reports-performance.html rename to client/reports-performance.html diff --git a/public/reports-simple.html b/client/reports-simple.html similarity index 100% rename from public/reports-simple.html rename to client/reports-simple.html diff --git a/public/reports-sw.js b/client/reports-sw.js similarity index 100% rename from public/reports-sw.js rename to client/reports-sw.js diff --git a/public/reports.css b/client/reports.css similarity index 100% rename from public/reports.css rename to client/reports.css diff --git a/public/reports.html b/client/reports.html similarity index 100% rename from public/reports.html rename to client/reports.html diff --git a/public/reports.js b/client/reports.js similarity index 100% rename from public/reports.js rename to client/reports.js diff --git a/public/schemes.html b/client/schemes.html similarity index 100% rename from public/schemes.html rename to client/schemes.html diff --git a/public/security-dashboard.html b/client/security-dashboard.html similarity index 100% rename from public/security-dashboard.html rename to client/security-dashboard.html diff --git a/public/security-dashboard.js b/client/security-dashboard.js similarity index 100% rename from public/security-dashboard.js rename to client/security-dashboard.js diff --git a/public/settings.css b/client/settings.css similarity index 100% rename from public/settings.css rename to client/settings.css diff --git a/public/settings.html b/client/settings.html similarity index 100% rename from public/settings.html rename to client/settings.html diff --git a/public/settings.js b/client/settings.js similarity index 100% rename from public/settings.js rename to client/settings.js diff --git a/public/signup.html b/client/signup.html similarity index 100% rename from public/signup.html rename to client/signup.html diff --git a/public/smart-triggers.js b/client/smart-triggers.js similarity index 100% rename from public/smart-triggers.js rename to client/smart-triggers.js diff --git a/public/subscription-manager.js b/client/subscription-manager.js similarity index 100% rename from public/subscription-manager.js rename to client/subscription-manager.js diff --git a/public/sw-notifications.js b/client/sw-notifications.js similarity index 100% rename from public/sw-notifications.js rename to client/sw-notifications.js diff --git a/public/sw.js b/client/sw.js similarity index 100% rename from public/sw.js rename to client/sw.js diff --git a/public/tag-manager.html b/client/tag-manager.html similarity index 100% rename from public/tag-manager.html rename to client/tag-manager.html diff --git a/public/tax-reports.html b/client/tax-reports.html similarity index 100% rename from public/tax-reports.html rename to client/tax-reports.html diff --git a/public/tax-reports.js b/client/tax-reports.js similarity index 100% rename from public/tax-reports.js rename to client/tax-reports.js diff --git a/public/team-settings.html b/client/team-settings.html similarity index 100% rename from public/team-settings.html rename to client/team-settings.html diff --git a/public/terms_service.html b/client/terms_service.html similarity index 100% rename from public/terms_service.html rename to client/terms_service.html diff --git a/test-analytics.html b/client/test-analytics.html similarity index 100% rename from test-analytics.html rename to client/test-analytics.html diff --git a/test-login.html b/client/test-login.html similarity index 100% rename from test-login.html rename to client/test-login.html diff --git a/test-modal.html b/client/test-modal.html similarity index 100% rename from test-modal.html rename to client/test-modal.html diff --git a/theme.js b/client/theme.js similarity index 100% rename from theme.js rename to client/theme.js diff --git a/public/trackerscript.js b/client/trackerscript.js similarity index 100% rename from public/trackerscript.js rename to client/trackerscript.js diff --git a/public/transactions.css b/client/transactions.css similarity index 100% rename from public/transactions.css rename to client/transactions.css diff --git a/public/transactions.html b/client/transactions.html similarity index 100% rename from public/transactions.html rename to client/transactions.html diff --git a/public/transactions.js b/client/transactions.js similarity index 100% rename from public/transactions.js rename to client/transactions.js diff --git a/public/treasury-dashboard.html b/client/treasury-dashboard.html similarity index 100% rename from public/treasury-dashboard.html rename to client/treasury-dashboard.html diff --git a/working-modal-demo.html b/client/working-modal-demo.html similarity index 100% rename from working-modal-demo.html rename to client/working-modal-demo.html diff --git a/public/workspace-feature.js b/client/workspace-feature.js similarity index 100% rename from public/workspace-feature.js rename to client/workspace-feature.js diff --git a/docker-compose.yml b/docker-compose.yml index 7ea6444e..bd1e0c12 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,48 @@ -version: "3.9" +version: '3.8' services: - expenseflow: - build: . - image: expenseflow:latest + client: + build: ./client ports: - - "8080:80" - restart: unless-stopped + - "3000:80" + depends_on: + - server + networks: + - mern-network + + server: + build: ./server + ports: + - "5000:5000" + environment: + - PORT=5000 + - MONGODB_URI=mongodb://db:27017/expenseflow + - FRONTEND_URL=http://localhost:3000 + # Use the .env file for other variables, but override DB URI for docker network + env_file: + - ./server/.env.example # Fallback if .env doesn't exist, user should copy .env.example to .env + depends_on: + - db + volumes: + - server-data:/app/data + - server-backups:/app/backups + networks: + - mern-network + + db: + image: mongo:latest + ports: + - "27017:27017" # Optional: Expose 27017 to host if you want to connect with Compass + volumes: + - mongo-data:/data/db + networks: + - mern-network + +networks: + mern-network: + driver: bridge + +volumes: + mongo-data: + server-data: + server-backups: diff --git a/expensetracker.css b/expensetracker.css deleted file mode 100644 index 414944f8..00000000 --- a/expensetracker.css +++ /dev/null @@ -1,4389 +0,0 @@ - :root { - /* Color Palette */ - --primary-gradient: linear-gradient(135deg, #309b81 0%, #31319a 100%); - --secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); - --success-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - --warning-gradient: linear-gradient(135deg, #fa709a 0%, #fee140 100%); - --dark-gradient: linear-gradient(135deg, #232526 0%, #414345 100%); - - /* Background Colors */ - --bg-primary: #0f0f23; - --bg-secondary: #1a1a2e; - --bg-tertiary: #16213e; - --bg-glass: rgba(255, 255, 255, 0.1); - - /* Text Colors */ - --text-primary: #f5f4f4; - --text-secondary: #a3a0a0; - --text-accent: #40fcd0; - - /* Accent Colors */ - --accent-primary: #40fcd0; - --accent-secondary: #ff6b9d; - --success: #00b75e; - --error: #ff855e; - --warning: #ffc107; - - /* Shadows */ - --shadow-glass: 0 8px 32px 0 rgba(31, 38, 135, 0.37); - --shadow-hover: 0 15px 35px rgba(0, 0, 0, 0.1); - --shadow-card: 0 8px 16px rgba(0, 0, 0, 0.3); -} - -/* Light Mode Theme */ -[data-theme="light"] { - /* Color Palette */ - --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - --secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); - --success-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - --warning-gradient: linear-gradient(135deg, #fa709a 0%, #fee140 100%); - --dark-gradient: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%); - - /* Background Colors */ - --bg-primary: #ffffff; - --bg-secondary: #f5f5f5; - --bg-tertiary: #efefef; - --bg-glass: rgba(0, 0, 0, 0.05); - - /* Text Colors */ - --text-primary: #1a1a1a; - --text-secondary: #666666; - --text-accent: #667eea; - - /* Accent Colors */ - --accent-primary: #667eea; - --accent-secondary: #764ba2; - --success: #00c853; - --error: #d32f2f; - --warning: #ffa000; - - /* Shadows */ - --shadow-glass: 0 8px 32px 0 rgba(0, 0, 0, 0.1); - --shadow-hover: 0 15px 35px rgba(0, 0, 0, 0.15); - --shadow-card: 0 8px 16px rgba(0, 0, 0, 0.1); -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html, body { - width: 100%; - max-width: 100%; - overflow-x: hidden; -} - -html { - scroll-behavior: smooth; -} - -body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - background: var(--bg-primary); - background-image: - radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%), - radial-gradient(circle at 80% 20%, rgba(255, 107, 157, 0.3) 0%, transparent 50%), - radial-gradient(circle at 40% 40%, rgba(100, 255, 218, 0.2) 0%, transparent 50%); - min-height: 100vh; - color: var(--text-primary); - line-height: 1.6; - overflow-x: hidden; -} - -/* Header Styles */ -.header { - position: fixed; - top: 0; - width: 100%; - z-index: 1000; - backdrop-filter: blur(20px); - background: rgba(15, 15, 35, 0.8); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); -} - -.navbar { - padding: 1rem 0; -} - -.nav-container { - max-width: 1200px; - margin: 0 auto; - padding: 0 2rem; - display: flex; - justify-content: space-between; - align-items: center; -} - -.nav-logo { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 1.5rem; - font-weight: 700; - color: var(--accent-primary); -} - -.nav-logo i { - font-size: 2rem; - background: var(--primary-gradient); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.nav-menu { - display: flex; - gap: 2rem; - list-style: none; -} - -.nav-link { - color: var(--text-secondary); - text-decoration: none; - font-weight: 500; - padding: 0.5rem 1rem; - border-radius: 0.5rem; - transition: all 0.3s ease; - position: relative; -} - -.nav-link:hover, -.nav-link.active { - color: var(--accent-primary); - background: rgba(100, 255, 218, 0.1); -} - -.user-profile { - display: flex; - align-items: center; - gap: 0.75rem; -} - -.theme-toggle-btn { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(100, 255, 218, 0.3); - color: var(--accent-primary); - width: 40px; - height: 40px; - border-radius: 50%; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.2rem; - transition: all 0.3s ease; - margin-right: 1rem; -} - -.theme-toggle-btn:hover { - background: rgba(100, 255, 218, 0.2); - border-color: var(--accent-primary); - transform: scale(1.1); -} - -.theme-toggle-btn.light-mode { - color: #ffd700; -} - -.profile-img { - width: 40px; - height: 40px; - border-radius: 50%; - border: 2px solid var(--accent-primary); -} - -.username { - font-weight: 500; - color: var(--text-primary); -} - -.nav-toggle { - display: none; - flex-direction: column; - cursor: pointer; -} - -.bar { - width: 25px; - height: 3px; - background: var(--accent-primary); - margin: 3px 0; - transition: 0.3s; - border-radius: 5px; -} - -/* Main Content */ -.main-content { - padding-top: 100px; - min-height: 100vh; -} - -.hero-section { - text-align: center; - padding: 3rem 2rem; - max-width: 800px; - margin: 0 auto; -} - -.hero-title { - font-size: 3.5rem; - font-weight: 700; - background: var(--primary-gradient); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - margin-bottom: 1rem; - animation: fadeInUp 1s ease-out; -} - -.hero-subtitle { - font-size: 1.2rem; - color: var(--text-secondary); - margin-bottom: 2rem; - animation: fadeInUp 1s ease-out 0.2s both; -} - -.container { - width: 100%; - max-width: 1100px; - margin: 0 auto; -} - -/* Balance Card */ -.balance-card { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(20px); - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 2rem; - text-align: center; - box-shadow: var(--shadow-glass); - transition: transform 0.3s ease, box-shadow 0.3s ease; - animation: fadeInUp 1s ease-out 0.4s both; -} - -.balance-card:hover { - transform: translateY(-5px); - box-shadow: var(--shadow-hover); -} - -.balance-header { - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - margin-bottom: 1rem; -} - -.balance-header i { - color: var(--accent-primary); - font-size: 1.5rem; -} - -.balance-header h4 { - color: var(--text-secondary); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 1px; - font-size: 0.9rem; -} - -.balance-amount { - font-size: 3rem; - font-weight: 700; - color: var(--text-primary); - margin: 1rem 0; - text-shadow: 0 0 20px rgba(100, 255, 218, 0.3); -} - -.balance-trend { - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; -} - -.trend-indicator { - font-size: 1.5rem; - font-weight: bold; -} - -.trend-indicator.positive { - color: var(--success); -} - -.trend-text { - color: var(--text-secondary); - font-size: 0.9rem; -} - -/* Income & Expense Cards */ -.inc-exp-container { - display: flex; - width: 100%; - max-width: 100%; - gap: 1rem; -} - -.income-card, -.expense-card { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(20px); - border-radius: 15px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 1.5rem; - display: flex; - align-items: center; - gap: 1rem; - box-shadow: var(--shadow-glass); - transition: all 0.3s ease; -} - -.income-card:hover, -.expense-card:hover { - transform: translateY(-3px); - box-shadow: var(--shadow-hover); -} - -.card-icon { - width: 60px; - height: 60px; - border-radius: 15px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - color: white; -} - -.income-icon { - background: var(--success-gradient); -} - -.expense-icon { - background: var(--warning-gradient); -} - -.card-content h4 { - color: var(--text-secondary); - font-size: 0.9rem; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.5rem; -} - -.money-plus { - color: var(--success); - font-size: 1.5rem; - font-weight: 600; -} - -.money-minus { - color: var(--error); - font-size: 1.5rem; - font-weight: 600; -} - -/* History Section */ -.history-section { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(20px); - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 2rem; - box-shadow: var(--shadow-glass); - animation: fadeInUp 1s ease-out 0.8s both; -} - -.section-header { - display: flex; - flex-direction: column; - margin-bottom: 1.5rem; - gap: 1rem; -} - -.section-header h3 { - color: var(--text-primary); - font-size: 1.3rem; - font-weight: 600; - display: flex; - align-items: center; - gap: 0.5rem; - border-bottom: none; - padding-bottom: 0; - margin: 0; -} - -.section-header h3 i { - color: var(--accent-primary); -} - -.filter-controls { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.search-container { - position: relative; - max-width: 300px; -} - -.search-input { - width: 100%; - padding: 0.75rem 1rem 0.75rem 2.5rem; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 25px; - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - color: var(--text-primary); - font-size: 0.9rem; - transition: all 0.3s ease; -} - -.search-input:focus { - outline: none; - border-color: var(--accent-primary); - box-shadow: 0 0 0 3px rgba(100, 255, 218, 0.1); -} - -.search-input::placeholder { - color: var(--text-secondary); -} - -.search-icon { - position: absolute; - left: 0.75rem; - top: 50%; - transform: translateY(-50%); - color: var(--text-secondary); - font-size: 0.9rem; -} - -.filter-buttons { - display: flex; - gap: 0.5rem; - align-items: center; - flex-wrap: wrap; -} - -.advanced-filters { - display: flex; - gap: 1rem; - align-items: center; - flex-wrap: wrap; - padding: 1rem; - background: rgba(255, 255, 255, 0.03); - border-radius: 15px; - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.category-select, -.date-input, -.amount-input { - padding: 0.5rem 1rem; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 8px; - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - color: var(--text-primary); - font-size: 0.9rem; - cursor: pointer; - transition: all 0.3s ease; - min-width: 120px; -} - -.category-select:focus, -.date-input:focus, -.amount-input:focus { - outline: none; - border-color: var(--accent-primary); - background: rgba(100, 255, 218, 0.1); -} - -.category-select option { - background: var(--bg-secondary); - color: var(--text-primary); -} - -.date-filters, -.amount-filters { - display: flex; - gap: 0.5rem; - align-items: center; -} - -.date-input, -.amount-input { - cursor: text; -} - -.amount-input::placeholder, -.date-input::placeholder { - color: var(--text-secondary); -} - -.clear-filters-btn { - padding: 0.5rem 1rem; - background: rgba(255, 87, 34, 0.2); - color: var(--error); - border: 1px solid rgba(255, 87, 34, 0.3); - border-radius: 8px; - cursor: pointer; - transition: all 0.3s ease; - font-size: 0.9rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.clear-filters-btn:hover { - background: rgba(255, 87, 34, 0.3); - border-color: var(--error); -} - -.filter-btn { - padding: 0.5rem 1rem; - border: 1px solid rgba(255, 255, 255, 0.2); - background: transparent; - color: var(--text-secondary); - border-radius: 25px; - cursor: pointer; - transition: all 0.3s ease; - font-size: 0.9rem; - font-weight: 500; -} - -.filter-btn:hover, -.filter-btn.active { - background: var(--accent-primary); - color: var(--bg-primary); - border-color: var(--accent-primary); -} - -.list { - list-style: none; - padding: 0; - max-height: 400px; - overflow-y: auto; - scrollbar-width: thin; - scrollbar-color: var(--accent-primary) transparent; -} - -.list::-webkit-scrollbar { - width: 8px; -} - -.list::-webkit-scrollbar-track { - background: rgba(255, 255, 255, 0.1); - border-radius: 4px; -} - -.list::-webkit-scrollbar-thumb { - background: var(--accent-primary); - border-radius: 4px; -} - -.list li { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 1rem; - margin-bottom: 0.5rem; - display: flex; - justify-content: space-between; - align-items: center; - position: relative; - transition: all 0.3s ease; - animation: slideInLeft 0.3s ease-out; - max-width: 100%; - word-break: break-word; -} - -.list li:hover { - transform: translateX(5px); - background: rgba(255, 255, 255, 0.1); -} - -.list li.plus { - border-left: 4px solid var(--success); -} - -.list li.minus { - border-left: 4px solid var(--error); -} - -.delete-btn { - position: absolute; - left: -40px; - top: 50%; - transform: translateY(-50%); - background: var(--error); - color: white; - border: none; - width: 30px; - height: 30px; - border-radius: 50%; - cursor: pointer; - opacity: 0; - transition: all 0.3s ease; - font-size: 0.9rem; - display: flex; - align-items: center; - justify-content: center; -} - -.list li:hover .delete-btn { - opacity: 1; -} - -.delete-btn:hover { - background: #d32f2f; - transform: translateY(-50%) scale(1.1); -} - -/* Data Management Section */ -.data-management-section { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(20px); - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 2rem; - box-shadow: var(--shadow-glass); - animation: fadeInUp 1s ease-out 0.9s both; -} - -.data-management-section h3 { - color: var(--text-primary); - font-size: 1.3rem; - font-weight: 600; - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1.5rem; -} - -.data-management-section h3 i { - color: var(--accent-primary); -} - -.data-actions { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 2rem; -} - -.export-section, -.import-section { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.export-section h4, -.import-section h4 { - color: var(--text-secondary); - font-size: 1rem; - font-weight: 500; - margin-bottom: 0.5rem; -} - -.export-buttons { - display: flex; - gap: 1rem; -} - -.export-btn { - padding: 0.75rem 1.5rem; - background: var(--success-gradient); - color: white; - border: none; - border-radius: 10px; - font-size: 0.9rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.export-btn:hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(76, 175, 80, 0.3); -} - -.import-controls { - display: flex; - gap: 1rem; - align-items: center; -} - -.file-input { - display: none; -} - -.file-label { - padding: 0.75rem 1.5rem; - background: rgba(255, 255, 255, 0.1); - color: var(--text-primary); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 10px; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.9rem; - font-weight: 500; -} - -.file-label:hover { - background: rgba(255, 255, 255, 0.15); - border-color: var(--accent-primary); -} - -.import-btn { - padding: 0.75rem 1.5rem; - background: var(--primary-gradient); - color: white; - border: none; - border-radius: 10px; - font-size: 0.9rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.import-btn:disabled { - background: rgba(255, 255, 255, 0.1); - color: var(--text-secondary); - cursor: not-allowed; -} - -.import-btn:not(:disabled):hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); -} - -.import-options { - margin-top: 1rem; -} - -.checkbox-label { - display: flex; - align-items: center; - gap: 0.75rem; - color: var(--text-secondary); - font-size: 0.9rem; - cursor: pointer; -} - -.checkbox-label input[type="checkbox"] { - width: 18px; - height: 18px; - accent-color: var(--accent-primary); -} - -/* Form Section */ -.form-section { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(20px); - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 2rem; - box-shadow: var(--shadow-glass); - animation: fadeInUp 1s ease-out 1s both; -} - -.form-section h3 { - color: var(--text-primary); - font-size: 1.3rem; - font-weight: 600; - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1.5rem; - border-bottom: none; - padding-bottom: 0; -} - -.form-section h3 i { - color: var(--accent-primary); -} - -.transaction-form { - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.form-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; -} - -.form-control { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.form-control label { - color: var(--text-secondary); - font-weight: 500; - font-size: 0.9rem; - text-transform: uppercase; - letter-spacing: 1px; -} - -.form-control input { - padding: 1rem; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 10px; - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - color: var(--text-primary); - font-size: 1rem; - transition: all 0.3s ease; -} - -.form-control input:focus { - outline: none; - border-color: var(--accent-primary); - box-shadow: 0 0 0 3px rgba(100, 255, 218, 0.1); -} - -.form-control input::placeholder { - color: var(--text-secondary); -} - -.form-control select { - padding: 1rem; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 10px; - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - color: var(--text-primary); - font-size: 1rem; - transition: all 0.3s ease; - cursor: pointer; -} - -.form-control select:focus { - outline: none; - border-color: var(--accent-primary); - box-shadow: 0 0 0 3px rgba(100, 255, 218, 0.1); -} - -.form-control select option { - background: var(--bg-secondary); - color: var(--text-primary); - padding: 0.5rem; -} - -.form-control small { - color: var(--text-secondary); - font-size: 0.8rem; - font-style: italic; -} - -.btn-submit { - padding: 1rem 2rem; - background: var(--primary-gradient); - color: white; - border: none; - border-radius: 10px; - font-size: 1rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - text-transform: uppercase; - letter-spacing: 1px; -} - -.btn-submit:hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); -} - -.btn-submit:active { - transform: translateY(0); -} - -/* Footer */ -.footer { - background: rgba(15, 15, 35, 0.9); - backdrop-filter: blur(20px); - border-top: 1px solid rgba(255, 255, 255, 0.1); - padding: 3rem 0 1rem; - margin-top: 4rem; -} - -.footer-container { - max-width: 1200px; - margin: 0 auto; - padding: 0 2rem; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 2rem; -} - -.footer-section { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.footer-logo { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 1.5rem; - font-weight: 700; - color: var(--accent-primary); - margin-bottom: 0.5rem; -} - -.footer-logo i { - font-size: 2rem; - background: var(--primary-gradient); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.footer-section p { - color: var(--text-secondary); - line-height: 1.6; -} - -.footer-section h4 { - color: var(--text-primary); - font-weight: 600; - margin-bottom: 0.5rem; -} - -.footer-links { - list-style: none; - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.footer-links a { - color: var(--text-secondary); - text-decoration: none; - transition: color 0.3s ease; -} - -.footer-links a:hover { - color: var(--accent-primary); -} - -.social-links { - display: flex; - gap: 1rem; -} - -.social-link { - display: flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - background: rgba(255, 255, 255, 0.1); - border-radius: 50%; - color: var(--text-secondary); - text-decoration: none; - transition: all 0.3s ease; -} - -.social-link:hover { - background: var(--accent-primary); - color: var(--bg-primary); - transform: translateY(-2px); -} - -.footer-bottom { - border-top: 1px solid rgba(255, 255, 255, 0.1); - margin-top: 2rem; - padding-top: 1rem; -} - -.footer-bottom .footer-container { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 1rem; - grid-template-columns: 1fr; -} - -.footer-bottom p { - color: var(--text-secondary); - font-size: 0.9rem; -} - -.footer-stats { - display: flex; - gap: 1rem; - flex-wrap: wrap; -} - -.footer-stats span { - font-size: 0.8rem; - color: var(--text-secondary); - background: rgba(255, 255, 255, 0.05); - padding: 0.25rem 0.75rem; - border-radius: 15px; -} - -/* Animations */ -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes slideInLeft { - from { - opacity: 0; - transform: translateX(-20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes pulse { - 0%, 100% { - transform: scale(1); - } - 50% { - transform: scale(1.05); - } -} - -/* Transaction Display Improvements */ -.transaction-content { - display: flex; - flex-direction: column; - gap: 0.5rem; - flex: 1; -} - -.transaction-main { - display: flex; - justify-content: space-between; - align-items: center; -} - -.transaction-text { - font-weight: 500; - color: var(--text-primary); -} - -.transaction-amount { - font-weight: 600; - font-size: 1.1rem; -} - -.transaction-date { - font-size: 0.8rem; - color: var(--text-secondary); - opacity: 0.7; -} - -.transaction-category { - font-size: 0.8rem; - color: var(--accent-primary); - background: rgba(100, 255, 218, 0.1); - padding: 0.2rem 0.6rem; - border-radius: 15px; - margin-top: 0.25rem; - display: inline-block; -} - -/* Balance Card States */ -.balance-card.positive .balance-amount { - color: var(--success); - text-shadow: 0 0 20px rgba(0, 230, 118, 0.3); -} - -.balance-card.negative .balance-amount { - color: var(--error); - text-shadow: 0 0 20px rgba(255, 87, 34, 0.3); -} - -.balance-card.neutral .balance-amount { - color: var(--text-secondary); - text-shadow: none; -} - -/* Mobile Navigation */ -@media (max-width: 768px) { - .nav-menu { - position: fixed; - top: 80px; - left: -100%; - width: 100%; - height: calc(100vh - 80px); - background: rgba(15, 15, 35, 0.95); - backdrop-filter: blur(20px); - flex-direction: column; - justify-content: flex-start; - align-items: center; - padding: 2rem 0; - transition: left 0.3s ease; - } - - .nav-menu.active { - left: 0; - display: flex; - } - - .nav-link { - font-size: 1.2rem; - padding: 1rem 2rem; - width: 80%; - text-align: center; - margin: 0.5rem 0; - } - - .nav-toggle { - display: flex; - } - - .nav-toggle.active .bar:nth-child(2) { - opacity: 0; - } - - .nav-toggle.active .bar:nth-child(1) { - transform: translateY(8px) rotate(45deg); - } - - .nav-toggle.active .bar:nth-child(3) { - transform: translateY(-8px) rotate(-45deg); - } - - .user-profile { - display: none; - } -} - -/* Responsive Design */ -@media (max-width: 768px) { - .hero-title { - font-size: 2.5rem; - } - - .container { - padding: 0 1rem; - } - - .inc-exp-container { - grid-template-columns: 1fr; - } - - .form-row { - grid-template-columns: 1fr; - } - - .section-header { - flex-direction: column; - align-items: stretch; - } - - .footer-bottom .footer-container { - flex-direction: column; - text-align: center; - } - - .footer-stats { - justify-content: center; - } -} - -@media (max-width: 480px) { - .hero-title { - font-size: 2rem; - } - - .balance-amount { - font-size: 2rem; - } - - .income-card, - .expense-card { - flex-direction: column; - text-align: center; - } - - .card-icon { - width: 50px; - height: 50px; - } - - .advanced-filters { - flex-direction: column; - align-items: stretch; - } - - .date-filters, - .amount-filters { - justify-content: stretch; - } - - .date-input, - .amount-input, - .category-select { - flex: 1; - } - - .data-actions { - grid-template-columns: 1fr; - } -} - -/* PWA Install Prompt */ -.install-prompt { - position: fixed; - bottom: 20px; - left: 20px; - right: 20px; - z-index: 10000; - animation: slideInUp 0.3s ease-out; -} - -.install-prompt-content { - background: rgba(15, 15, 35, 0.95); - backdrop-filter: blur(20px); - border-radius: 15px; - border: 1px solid rgba(255, 255, 255, 0.2); - padding: 1.5rem; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); -} - -.install-prompt-header { - display: flex; - align-items: center; - gap: 0.75rem; - margin-bottom: 0.75rem; -} - -.install-prompt-header i { - color: var(--accent-primary); - font-size: 1.5rem; -} - -.install-prompt-header h3 { - color: var(--text-primary); - font-size: 1.1rem; - font-weight: 600; - margin: 0; -} - -.install-prompt p { - color: var(--text-secondary); - margin-bottom: 1rem; - font-size: 0.9rem; -} - -.install-prompt-actions { - display: flex; - gap: 1rem; -} - -.install-btn { - padding: 0.75rem 1.5rem; - background: var(--primary-gradient); - color: white; - border: none; - border-radius: 8px; - font-size: 0.9rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; - flex: 1; - justify-content: center; -} - -.install-btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); -} - -.dismiss-btn { - padding: 0.75rem 1.5rem; - background: transparent; - color: var(--text-secondary); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 8px; - font-size: 0.9rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; - justify-content: center; -} - -.dismiss-btn:hover { - background: rgba(255, 255, 255, 0.1); - color: var(--text-primary); -} - -/* Offline Indicator */ -.offline-indicator { - position: fixed; - top: 80px; - left: 50%; - transform: translateX(-50%); - background: rgba(255, 152, 0, 0.9); - color: white; - padding: 0.75rem 1.5rem; - border-radius: 25px; - font-size: 0.9rem; - font-weight: 500; - display: flex; - align-items: center; - gap: 0.5rem; - z-index: 9999; - backdrop-filter: blur(10px); - box-shadow: 0 4px 20px rgba(255, 152, 0, 0.3); - animation: slideInDown 0.3s ease-out; -} - -/* Update Notification */ -.update-notification { - position: fixed; - top: 20px; - right: 20px; - z-index: 10000; - animation: slideInRight 0.3s ease-out; -} - -.update-content { - background: rgba(76, 175, 80, 0.9); - color: white; - padding: 1rem 1.5rem; - border-radius: 10px; - display: flex; - align-items: center; - gap: 1rem; - backdrop-filter: blur(10px); - box-shadow: 0 4px 20px rgba(76, 175, 80, 0.3); -} - -.update-btn { - background: white; - color: #4CAF50; - border: none; - padding: 0.5rem 1rem; - border-radius: 5px; - font-size: 0.9rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; -} - -.update-btn:hover { - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); -} - -/* Touch and gesture improvements */ -@media (hover: none) and (pointer: coarse) { - /* Touch device specific styles */ - .filter-btn, - .export-btn, - .import-btn, - .btn-submit { - min-height: 44px; - padding: 0.75rem 1.5rem; - } - - .delete-btn { - min-width: 44px; - min-height: 44px; - } - - .nav-link { - padding: 1rem 1.5rem; - } - - /* Larger tap targets for mobile */ - .category-select, - .date-input, - .amount-input, - .search-input { - min-height: 48px; - font-size: 16px; /* Prevents zoom on iOS */ - } - - .form-control input, - .form-control select { - min-height: 48px; - font-size: 16px; - } -} - -/* Animations */ -@keyframes slideInUp { - from { - transform: translateY(100%); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes slideInDown { - from { - transform: translateX(-50%) translateY(-100%); - opacity: 0; - } - to { - transform: translateX(-50%) translateY(0); - opacity: 1; - } -} - -@keyframes slideInRight { - from { - transform: translateX(100%); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } -} - -/* Safe area insets for devices with notches */ -@supports (padding: max(0px)) { - .header { - padding-left: max(0px, env(safe-area-inset-left)); - padding-right: max(0px, env(safe-area-inset-right)); - } - - .main-content { - padding-left: max(2rem, env(safe-area-inset-left) + 2rem); - padding-right: max(2rem, env(safe-area-inset-right) + 2rem); - padding-bottom: max(2rem, env(safe-area-inset-bottom) + 2rem); - } - - .install-prompt { - bottom: max(20px, env(safe-area-inset-bottom) + 20px); - left: max(20px, env(safe-area-inset-left) + 20px); - right: max(20px, env(safe-area-inset-right) + 20px); - } -} - -@media (max-width: 768px) { - - body { - overflow-x: hidden; - } - - .nav-toggle { - display: block; - } - - .nav-menu { - position: absolute; - top: 64px; - left: 0; - width: 100%; - background: #0f0f23; - flex-direction: column; - display: none; - } - - .nav-menu.active { - display: flex; - } - - .user-profile { - display: none; - } - - .inc-exp-container { - flex-direction: column; - align-items: center; - } - - .income-card, - .expense-card { - width: 100%; - max-width: 100%; - } - - form { - display: flex; - flex-direction: column; - gap: 1rem; - } -} - -/* ========================= - HAMBURGER MENU FIX - ========================= */ - -/* Default: hide toggle */ -.nav-toggle { - display: none; -} - -/* Hamburger bars */ -.nav-toggle .bar { - width: 25px; - height: 3px; - background-color: #64ffda; - margin: 5px 0; - display: block; - transition: 0.3s ease; -} - -/* Show hamburger on tablets & phones */ -@media (max-width: 1024px) { - .nav-toggle { - display: block; - cursor: pointer; - z-index: 1001; - } - - .nav-menu { - position: absolute; - top: 64px; - left: 0; - width: 100%; - background: #0f0f23; - flex-direction: column; - align-items: center; - display: none; - z-index: 1000; - } - - .nav-menu.active { - display: flex; - } - - .nav-link { - padding: 1rem; - width: 100%; - text-align: center; - } - - .user-profile { - display: none; - } -} - -/* Extra-small devices (<400px) */ -@media (max-width: 400px) { - .hero-title { - font-size: 1.5rem; - } - - .hero-subtitle { - font-size: 0.9rem; - } -} - -/* ULTRA SMALL DEVICES (320px)*/ -@media (max-width: 360px) { - - /* Global */ - html, body { - overflow-x: hidden; - } - - body { - font-size: 14px; - } - - .container { - padding: 0.75rem; - margin-left: auto; - margin-right: auto; - } - - /* Navbar */ - .nav-logo span { - font-size: 0.9rem; - } - - .nav-toggle .bar { - width: 22px; - height: 2.5px; - } - - /* Hero */ - .hero-title { - font-size: 1.4rem; - line-height: 1.3; - } - - .hero-subtitle { - font-size: 0.85rem; - } - - /* Balance */ - .balance-card { - padding: 1rem; - } - - .balance-amount { - font-size: 1.5rem; - } - - /* Income / Expense cards */ - .income-card, - .expense-card { - padding: 0.75rem; - } - - /* Filters */ - .search-input, - .category-select, - .date-input, - .amount-input, - .filter-btn, - .clear-filters-btn { - width: 100%; - min-width: 0; - font-size: 0.85rem; - } - - .filter-buttons { - flex-wrap: wrap; - gap: 0.5rem; - } - - /* Transaction list */ - .list li { - flex-direction: column; - align-items: flex-start; - gap: 0.25rem; - } - - /* Form */ - .transaction-form { - padding: 1rem; - } - - .transaction-form input, - .transaction-form select, - .btn-submit { - font-size: 0.9rem; - } - - /* Footer */ - .footer-section h4 { - font-size: 1rem; - } - - .footer-links a { - font-size: 0.85rem; - } - .nav-container { - padding: 0 0.75rem; - } - - .nav-logo span { - font-size: 0.85rem; - } -} - -#scrollToTopBtn svg { - display: block; -} -input, -select, -button { - max-width: 100%; - min-width: 0; -} - -/* ============================================ - Security Dashboard Styles (Issue #338) - ============================================ */ - -/* Security Overview */ -.security-overview { - display: flex; - gap: 2rem; - margin-bottom: 2rem; - padding: 1.5rem; - background: var(--bg-glass); - border-radius: 12px; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.security-score-card { - text-align: center; - padding: 1rem; -} - -.score-circle { - width: 100px; - height: 100px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 0.5rem; - font-size: 2rem; - font-weight: bold; -} - -.score-circle.excellent { background: linear-gradient(135deg, #00c853 0%, #00f5a0 100%); color: white; } -.score-circle.good { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; } -.score-circle.fair { background: linear-gradient(135deg, #ffa726 0%, #ffcc02 100%); color: #333; } -.score-circle.poor { background: linear-gradient(135deg, #ff5252 0%, #f48fb1 100%); color: white; } - -.score-label { - font-size: 0.875rem; - color: var(--text-secondary); -} - -.security-stats { - display: flex; - gap: 2rem; - align-items: center; - flex-wrap: wrap; -} - -.stat-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.25rem; - padding: 1rem; - min-width: 100px; -} - -.stat-item i { - font-size: 1.5rem; - color: var(--accent-primary); -} - -.stat-item i.enabled { color: var(--success); } -.stat-item i.disabled { color: var(--error); } - -.stat-label { - font-size: 0.75rem; - color: var(--text-secondary); - text-transform: uppercase; -} - -.stat-value { - font-size: 1.125rem; - font-weight: 600; - color: var(--text-primary); -} - -/* Security Sections */ -.security-sections { - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.security-section { - background: var(--bg-glass); - border-radius: 12px; - padding: 1.5rem; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.security-section h3 { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1rem; - color: var(--text-primary); - font-size: 1.125rem; -} - -.security-section h3 i { - color: var(--accent-primary); -} - -.security-section h3 .btn-link { - margin-left: auto; - font-size: 0.875rem; - background: none; - border: none; - color: var(--accent-secondary); - cursor: pointer; - text-decoration: underline; -} - -/* 2FA Section */ -.twofa-enabled, .twofa-disabled { - padding: 1rem; -} - -.status-badge { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 1rem; - border-radius: 20px; - font-size: 0.875rem; - margin-bottom: 1rem; -} - -.status-badge.success { - background: rgba(0, 200, 83, 0.2); - color: var(--success); -} - -.status-badge.warning { - background: rgba(255, 167, 38, 0.2); - color: var(--warning); -} - -.twofa-actions { - display: flex; - gap: 1rem; - margin-top: 1rem; - flex-wrap: wrap; -} - -/* Sessions List */ -.sessions-list { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.session-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem; - background: var(--bg-secondary); - border-radius: 8px; - border: 1px solid transparent; - transition: border-color 0.2s; -} - -.session-item.current { - border-color: var(--accent-primary); -} - -.session-device { - display: flex; - align-items: center; - gap: 1rem; -} - -.session-device i { - font-size: 1.5rem; - color: var(--text-secondary); -} - -.device-info { - display: flex; - flex-direction: column; -} - -.device-info strong { - color: var(--text-primary); -} - -.session-location { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.session-meta { - display: flex; - align-items: center; - gap: 0.75rem; -} - -.session-time { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.badge-2fa { - background: var(--success); - color: white; - padding: 0.25rem 0.5rem; - border-radius: 4px; - font-size: 0.625rem; -} - -.badge-current { - background: var(--accent-primary); - color: var(--bg-primary); - padding: 0.25rem 0.5rem; - border-radius: 4px; - font-size: 0.75rem; - font-weight: 600; -} - -.btn-revoke { - background: transparent; - border: 1px solid var(--error); - color: var(--error); - padding: 0.25rem 0.5rem; - border-radius: 4px; - cursor: pointer; - transition: all 0.2s; -} - -.btn-revoke:hover { - background: var(--error); - color: white; -} - -/* Audit Trail */ -.audit-list { - display: flex; - flex-direction: column; - gap: 0.5rem; - max-height: 400px; - overflow-y: auto; -} - -.audit-item { - display: flex; - align-items: center; - gap: 1rem; - padding: 0.75rem; - background: var(--bg-secondary); - border-radius: 8px; - border-left: 3px solid transparent; -} - -.audit-item.failed { - border-left-color: var(--error); - background: rgba(255, 82, 82, 0.1); -} - -.audit-icon { - width: 32px; - height: 32px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.audit-icon.success { background: rgba(0, 200, 83, 0.2); color: var(--success); } -.audit-icon.danger { background: rgba(255, 82, 82, 0.2); color: var(--error); } -.audit-icon.warning { background: rgba(255, 167, 38, 0.2); color: var(--warning); } -.audit-icon.info { background: rgba(79, 172, 254, 0.2); color: var(--accent-primary); } - -.audit-content { - flex: 1; - min-width: 0; -} - -.audit-content strong { - display: block; - color: var(--text-primary); - font-size: 0.875rem; -} - -.audit-meta { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.audit-status { - width: 24px; - height: 24px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.audit-status.success { background: var(--success); color: white; } -.audit-status.failure { background: var(--error); color: white; } - -/* Password Form */ -.password-form { - max-width: 400px; -} - -.password-form .form-group { - margin-bottom: 1rem; -} - -.password-form label { - display: block; - margin-bottom: 0.25rem; - font-size: 0.875rem; - color: var(--text-secondary); -} - -.password-form input { - width: 100%; - padding: 0.75rem; - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 6px; - background: var(--bg-secondary); - color: var(--text-primary); - font-size: 1rem; -} - -.password-form input:focus { - outline: none; - border-color: var(--accent-primary); -} - -/* Buttons */ -.btn-primary { - background: var(--primary-gradient); - color: white; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 8px; - font-size: 0.875rem; - font-weight: 600; - cursor: pointer; - transition: transform 0.2s, box-shadow 0.2s; -} - -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); -} - -.btn-secondary { - background: transparent; - color: var(--text-primary); - border: 1px solid rgba(255, 255, 255, 0.3); - padding: 0.75rem 1.5rem; - border-radius: 8px; - font-size: 0.875rem; - cursor: pointer; - transition: all 0.2s; -} - -.btn-secondary:hover { - background: rgba(255, 255, 255, 0.1); - border-color: var(--accent-primary); -} - -.btn-danger { - background: var(--error); - color: white; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 8px; - font-size: 0.875rem; - cursor: pointer; - transition: transform 0.2s, opacity 0.2s; -} - -.btn-danger:hover { - opacity: 0.9; - transform: translateY(-2px); -} - -/* Security Modal */ -.security-modal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 10000; - display: flex; - align-items: center; - justify-content: center; -} - -.modal-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(4px); -} - -.security-modal .modal-content { - position: relative; - background: var(--bg-secondary); - border-radius: 16px; - padding: 2rem; - max-width: 500px; - width: 90%; - max-height: 90vh; - overflow-y: auto; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.modal-close { - position: absolute; - top: 1rem; - right: 1rem; - background: transparent; - border: none; - color: var(--text-secondary); - font-size: 1.5rem; - cursor: pointer; - transition: color 0.2s; -} - -.modal-close:hover { - color: var(--text-primary); -} - -/* 2FA Setup */ -.setup-2fa-content h2 { - margin-bottom: 1.5rem; - color: var(--text-primary); -} - -.setup-steps .step { - margin-bottom: 1.5rem; - padding-bottom: 1.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); -} - -.setup-steps .step:last-child { - border-bottom: none; -} - -.setup-steps h3 { - font-size: 1rem; - color: var(--accent-primary); - margin-bottom: 0.5rem; -} - -.qr-code { - display: flex; - justify-content: center; - margin: 1rem 0; - padding: 1rem; - background: white; - border-radius: 8px; - width: fit-content; - margin: 1rem auto; -} - -.qr-code img { - width: 200px; - height: 200px; -} - -.secret-key { - display: block; - padding: 0.75rem; - background: var(--bg-tertiary); - border-radius: 6px; - font-family: 'Courier New', monospace; - font-size: 1rem; - letter-spacing: 0.1em; - word-break: break-all; - text-align: center; -} - -.totp-input { - width: 100%; - padding: 1rem; - font-size: 1.5rem; - text-align: center; - letter-spacing: 0.5em; - font-weight: bold; - border: 2px solid rgba(255, 255, 255, 0.2); - border-radius: 8px; - background: var(--bg-secondary); - color: var(--text-primary); - margin-bottom: 1rem; -} - -.totp-input:focus { - outline: none; - border-color: var(--accent-primary); -} - -/* Backup Codes */ -.backup-codes-content h2 { - margin-bottom: 0.5rem; - color: var(--text-primary); -} - -.backup-codes-content .warning { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 1rem; - background: rgba(255, 167, 38, 0.2); - border-radius: 8px; - margin-bottom: 1.5rem; - font-size: 0.875rem; - color: var(--warning); -} - -.backup-codes-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 0.5rem; - margin-bottom: 1.5rem; -} - -.backup-code { - padding: 0.75rem; - background: var(--bg-tertiary); - border-radius: 6px; - font-family: 'Courier New', monospace; - font-size: 0.875rem; - text-align: center; -} - -.backup-actions { - display: flex; - gap: 1rem; - margin-bottom: 1rem; -} - -.modal-actions { - display: flex; - gap: 1rem; - margin-top: 1.5rem; -} - -/* Empty State */ -.empty-state { - text-align: center; - padding: 2rem; - color: var(--text-secondary); - font-style: italic; -} - -/* User Controls */ -.user-controls { - display: flex; - align-items: center; - gap: 0.75rem; -} - -.user-controls .security-btn { - background: transparent; - border: 1px solid var(--accent-primary); - color: var(--accent-primary); - padding: 0.5rem; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s; -} - -.user-controls .security-btn:hover { - background: var(--accent-primary); - color: var(--bg-primary); -} - -.user-controls .user-name { - font-size: 0.875rem; - color: var(--text-secondary); -} - -.user-controls .logout-btn { - background: var(--error); - color: white; - border: none; - padding: 0.5rem 1rem; - border-radius: 6px; - cursor: pointer; - font-size: 0.875rem; - transition: opacity 0.2s; -} - -.user-controls .logout-btn:hover { - opacity: 0.9; -} - -/* Responsive Security Dashboard */ -@media (max-width: 768px) { - .security-overview { - flex-direction: column; - gap: 1.5rem; - } - - .security-stats { - justify-content: center; - } - - .session-item { - flex-direction: column; - align-items: flex-start; - gap: 0.75rem; - } - - .session-meta { - width: 100%; - justify-content: space-between; - } - - .twofa-actions { - flex-direction: column; - } - - .backup-codes-grid { - grid-template-columns: 1fr; - } - - .backup-actions { - flex-direction: column; - } - - .modal-actions { - flex-direction: column; - } -} - - -/* ============================================ - AI-DRIVEN BUDGET INTELLIGENCE DASHBOARD - Z-Score Anomaly Detection & Self-Healing - Issue #339 - ============================================ */ - -.intelligence-section { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(20px); - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 2rem; - box-shadow: var(--shadow-glass); - margin-bottom: 2rem; -} - -.intelligence-section .section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1.5rem; -} - -.intelligence-section h3 { - color: var(--text-primary); - font-size: 1.3rem; - font-weight: 600; - display: flex; - align-items: center; - gap: 0.5rem; - margin: 0; -} - -.intelligence-section h3 i { - color: var(--accent-primary); -} - -.intelligence-controls .refresh-btn { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - color: var(--text-primary); - padding: 0.5rem 1rem; - border-radius: 8px; - cursor: pointer; - transition: all 0.3s ease; -} - -.intelligence-controls .refresh-btn:hover { - background: var(--accent-primary); - color: var(--bg-primary); -} - -/* Intelligence Loader */ -.intelligence-loader { - display: none; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 2rem; - gap: 1rem; -} - -.intelligence-loader.active { - display: flex; -} - -.loader-spinner { - width: 40px; - height: 40px; - border: 3px solid rgba(255, 255, 255, 0.1); - border-top-color: var(--accent-primary); - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -/* Intelligence Cards */ -.intelligence-cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 1rem; - margin-bottom: 1.5rem; -} - -.intel-card { - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 15px; - padding: 1.25rem; - display: flex; - align-items: center; - gap: 1rem; - transition: all 0.3s ease; -} - -.intel-card:hover { - transform: translateY(-3px); - box-shadow: var(--shadow-hover); -} - -.intel-card-icon { - width: 50px; - height: 50px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.25rem; -} - -.intel-card-icon.anomaly { - background: linear-gradient(135deg, #ff6b6b, #ee5a5a); - color: white; -} - -.intel-card-icon.volatility { - background: linear-gradient(135deg, #feca57, #ff9f43); - color: white; -} - -.intel-card-icon.volatility.high { - background: linear-gradient(135deg, #ff6b6b, #ee5a5a); -} - -.intel-card-icon.volatility.medium { - background: linear-gradient(135deg, #feca57, #ff9f43); -} - -.intel-card-icon.volatility.low { - background: linear-gradient(135deg, #1dd1a1, #10ac84); -} - -.intel-card-icon.reallocation { - background: linear-gradient(135deg, #54a0ff, #5f27cd); - color: white; -} - -.intel-card-icon.health { - background: linear-gradient(135deg, #1dd1a1, #10ac84); - color: white; -} - -.intel-card-icon.health.excellent { - background: linear-gradient(135deg, #1dd1a1, #10ac84); -} - -.intel-card-icon.health.good { - background: linear-gradient(135deg, #54a0ff, #2e86de); -} - -.intel-card-icon.health.fair { - background: linear-gradient(135deg, #feca57, #ff9f43); -} - -.intel-card-icon.health.poor { - background: linear-gradient(135deg, #ff6b6b, #ee5a5a); -} - -.intel-card-content { - display: flex; - flex-direction: column; -} - -.intel-card-value { - font-size: 1.5rem; - font-weight: 700; - color: var(--text-primary); -} - -.intel-card-label { - font-size: 0.85rem; - color: var(--text-secondary); -} - -/* Intelligence Tabs */ -.intelligence-tabs { - display: flex; - gap: 0.5rem; - margin-bottom: 1.5rem; - flex-wrap: wrap; -} - -.intelligence-tab { - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - color: var(--text-secondary); - padding: 0.75rem 1.25rem; - border-radius: 10px; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.9rem; -} - -.intelligence-tab:hover { - background: rgba(255, 255, 255, 0.1); - color: var(--text-primary); -} - -.intelligence-tab.active { - background: var(--accent-primary); - color: var(--bg-primary); - border-color: var(--accent-primary); -} - -/* Intelligence Panels */ -.intelligence-panels { - margin-bottom: 1.5rem; -} - -.intelligence-panel { - display: none; - background: rgba(255, 255, 255, 0.03); - border-radius: 15px; - padding: 1.5rem; - border: 1px solid rgba(255, 255, 255, 0.05); -} - -.intelligence-panel.active { - display: block; -} - -.intelligence-panel h4 { - color: var(--text-primary); - margin-bottom: 0.5rem; - font-size: 1.1rem; -} - -.panel-description { - color: var(--text-secondary); - font-size: 0.85rem; - margin-bottom: 1rem; -} - -/* Empty State */ -.empty-state { - text-align: center; - padding: 2rem; - color: var(--text-secondary); -} - -.empty-state i { - font-size: 3rem; - margin-bottom: 1rem; - opacity: 0.5; -} - -.empty-state p { - margin-bottom: 0.5rem; -} - -.empty-state small { - font-size: 0.8rem; - opacity: 0.7; -} - -/* Anomaly Timeline */ -.anomaly-list { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.anomaly-item { - display: flex; - gap: 1rem; - padding: 1rem; - background: rgba(255, 255, 255, 0.03); - border-radius: 10px; - border-left: 4px solid var(--warning); - transition: all 0.3s ease; -} - -.anomaly-item:hover { - background: rgba(255, 255, 255, 0.05); -} - -.anomaly-item.critical { - border-left-color: #ff6b6b; -} - -.anomaly-item.high { - border-left-color: #ff9f43; -} - -.anomaly-item.medium { - border-left-color: #feca57; -} - -.anomaly-indicator { - width: 12px; - height: 12px; - border-radius: 50%; - background: currentColor; - margin-top: 0.25rem; -} - -.anomaly-item.critical .anomaly-indicator { - background: #ff6b6b; - box-shadow: 0 0 10px rgba(255, 107, 107, 0.5); -} - -.anomaly-item.high .anomaly-indicator { - background: #ff9f43; - box-shadow: 0 0 10px rgba(255, 159, 67, 0.5); -} - -.anomaly-item.medium .anomaly-indicator { - background: #feca57; -} - -.anomaly-content { - flex: 1; -} - -.anomaly-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5rem; -} - -.anomaly-category { - font-weight: 600; - color: var(--text-primary); -} - -.anomaly-zscore { - font-size: 0.8rem; - background: rgba(255, 107, 107, 0.2); - color: #ff6b6b; - padding: 0.25rem 0.5rem; - border-radius: 5px; -} - -.anomaly-details { - display: flex; - gap: 1rem; - margin-bottom: 0.5rem; -} - -.anomaly-amount { - font-weight: 600; - color: var(--error); -} - -.anomaly-deviation { - font-size: 0.85rem; - color: var(--text-secondary); -} - -.anomaly-meta { - display: flex; - gap: 1rem; - font-size: 0.8rem; - color: var(--text-secondary); -} - -/* Volatility Chart */ -.volatility-bars { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.volatility-bar-item { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.volatility-bar-label { - display: flex; - justify-content: space-between; - align-items: center; -} - -.category-name { - font-weight: 500; - color: var(--text-primary); -} - -.risk-badge { - font-size: 0.7rem; - padding: 0.2rem 0.5rem; - border-radius: 10px; - text-transform: uppercase; - font-weight: 600; -} - -.risk-badge.low { - background: rgba(29, 209, 161, 0.2); - color: #1dd1a1; -} - -.risk-badge.medium { - background: rgba(254, 202, 87, 0.2); - color: #feca57; -} - -.risk-badge.high { - background: rgba(255, 107, 107, 0.2); - color: #ff6b6b; -} - -.volatility-bar-container { - position: relative; - height: 8px; - background: rgba(255, 255, 255, 0.1); - border-radius: 4px; - overflow: hidden; -} - -.volatility-bar { - height: 100%; - border-radius: 4px; - transition: width 0.5s ease; -} - -.volatility-value { - position: absolute; - right: -45px; - top: 50%; - transform: translateY(-50%); - font-size: 0.8rem; - color: var(--text-secondary); -} - -.volatility-stats { - display: flex; - gap: 1.5rem; - font-size: 0.8rem; - color: var(--text-secondary); -} - -/* Reallocation Suggestions */ -.reallocation-list { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.reallocation-item { - background: rgba(255, 255, 255, 0.03); - border-radius: 12px; - padding: 1.25rem; - border: 1px solid rgba(255, 255, 255, 0.05); -} - -.reallocation-flow { - display: flex; - align-items: center; - gap: 1rem; - margin-bottom: 1rem; - flex-wrap: wrap; -} - -.reallocation-from, -.reallocation-to { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.category-badge { - padding: 0.5rem 1rem; - border-radius: 8px; - font-weight: 500; -} - -.category-badge.surplus { - background: rgba(29, 209, 161, 0.2); - color: #1dd1a1; -} - -.category-badge.deficit { - background: rgba(255, 107, 107, 0.2); - color: #ff6b6b; -} - -.surplus-amount { - font-size: 0.85rem; - color: #1dd1a1; -} - -.reallocation-arrow { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.25rem; - color: var(--accent-primary); -} - -.transfer-amount { - font-size: 0.9rem; - font-weight: 600; -} - -.reallocation-reason { - font-size: 0.85rem; - color: var(--text-secondary); - margin-bottom: 1rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.reallocation-actions { - display: flex; - gap: 0.75rem; -} - -.reallocation-actions .btn { - padding: 0.5rem 1rem; - border-radius: 8px; - font-size: 0.85rem; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.reallocation-actions .btn-primary { - background: var(--accent-primary); - color: var(--bg-primary); - border: none; -} - -.reallocation-actions .btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 4px 15px rgba(100, 255, 218, 0.3); -} - -.reallocation-actions .btn-secondary { - background: transparent; - color: var(--text-secondary); - border: 1px solid rgba(255, 255, 255, 0.2); -} - -.reallocation-actions .btn-secondary:hover { - background: rgba(255, 255, 255, 0.1); -} - -/* Alerts List */ -.alerts-list { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.alert-item { - display: flex; - gap: 1rem; - padding: 1rem; - background: rgba(255, 255, 255, 0.03); - border-radius: 10px; - align-items: flex-start; -} - -.alert-icon { - width: 36px; - height: 36px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1rem; -} - -.alert-item.anomaly .alert-icon { - background: rgba(255, 107, 107, 0.2); - color: #ff6b6b; -} - -.alert-item.prediction .alert-icon { - background: rgba(84, 160, 255, 0.2); - color: #54a0ff; -} - -.alert-item.reallocation .alert-icon { - background: rgba(95, 39, 205, 0.2); - color: #5f27cd; -} - -.alert-item.standard .alert-icon { - background: rgba(254, 202, 87, 0.2); - color: #feca57; -} - -.alert-content { - flex: 1; -} - -.alert-message { - color: var(--text-primary); - margin-bottom: 0.5rem; -} - -.alert-meta { - display: flex; - gap: 0.75rem; - font-size: 0.8rem; -} - -.alert-type { - color: var(--text-secondary); - text-transform: capitalize; -} - -.alert-priority { - padding: 0.15rem 0.5rem; - border-radius: 5px; - font-size: 0.7rem; - text-transform: uppercase; -} - -.alert-priority.critical { - background: rgba(255, 107, 107, 0.2); - color: #ff6b6b; -} - -.alert-priority.high { - background: rgba(255, 159, 67, 0.2); - color: #ff9f43; -} - -.alert-priority.medium { - background: rgba(254, 202, 87, 0.2); - color: #feca57; -} - -.alert-priority.low { - background: rgba(29, 209, 161, 0.2); - color: #1dd1a1; -} - -/* Budget Intelligence Grid */ -.budget-intel-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 1rem; -} - -.budget-intel-card { - background: rgba(255, 255, 255, 0.03); - border-radius: 12px; - padding: 1.25rem; - border: 1px solid rgba(255, 255, 255, 0.05); - transition: all 0.3s ease; -} - -.budget-intel-card:hover { - border-color: rgba(255, 255, 255, 0.1); - transform: translateY(-2px); -} - -.budget-intel-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; -} - -.budget-name { - font-weight: 600; - color: var(--text-primary); -} - -.trend-indicator { - width: 24px; - height: 24px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.75rem; -} - -.trend-indicator.increasing { - background: rgba(255, 107, 107, 0.2); - color: #ff6b6b; -} - -.trend-indicator.decreasing { - background: rgba(29, 209, 161, 0.2); - color: #1dd1a1; -} - -.trend-indicator.stable { - background: rgba(84, 160, 255, 0.2); - color: #54a0ff; -} - -.budget-intel-progress { - margin-bottom: 1rem; -} - -.progress-bar { - height: 8px; - background: rgba(255, 255, 255, 0.1); - border-radius: 4px; - overflow: hidden; - margin-bottom: 0.5rem; -} - -.progress-fill { - height: 100%; - border-radius: 4px; - transition: width 0.5s ease; -} - -.progress-fill.normal { - background: var(--accent-primary); -} - -.progress-fill.caution { - background: #feca57; -} - -.progress-fill.warning { - background: #ff9f43; -} - -.progress-fill.over { - background: #ff6b6b; -} - -.progress-labels { - display: flex; - justify-content: space-between; - font-size: 0.8rem; - color: var(--text-secondary); -} - -.budget-intel-stats { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 0.5rem; -} - -.stat-item { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.stat-label { - font-size: 0.7rem; - color: var(--text-secondary); - text-transform: uppercase; -} - -.stat-value { - font-size: 0.9rem; - font-weight: 600; - color: var(--text-primary); -} - -.stat-value.high { - color: #ff6b6b; -} - -.stat-value.medium { - color: #feca57; -} - -.stat-value.low { - color: #1dd1a1; -} - -.budget-anomaly-badge { - margin-top: 1rem; - padding: 0.5rem; - background: rgba(255, 107, 107, 0.1); - border-radius: 8px; - color: #ff6b6b; - font-size: 0.8rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -/* Transaction Analyzer */ -.transaction-analyzer { - background: rgba(255, 255, 255, 0.03); - border-radius: 15px; - padding: 1.5rem; - border: 1px solid rgba(255, 255, 255, 0.05); -} - -.transaction-analyzer h4 { - color: var(--text-primary); - margin-bottom: 1rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.analyze-form .analyze-row { - display: flex; - gap: 0.75rem; - flex-wrap: wrap; -} - -.analyze-form input, -.analyze-form select { - flex: 1; - min-width: 120px; - padding: 0.75rem 1rem; - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 8px; - color: var(--text-primary); - font-size: 0.9rem; -} - -.analyze-form input:focus, -.analyze-form select:focus { - outline: none; - border-color: var(--accent-primary); -} - -.analyze-form select option { - background: var(--bg-secondary); -} - -.analyze-btn { - background: var(--accent-primary); - color: var(--bg-primary); - border: none; - padding: 0.75rem 1.5rem; - border-radius: 8px; - cursor: pointer; - font-weight: 500; - display: flex; - align-items: center; - gap: 0.5rem; - transition: all 0.3s ease; -} - -.analyze-btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 15px rgba(100, 255, 218, 0.3); -} - -/* Analysis Result */ -.analysis-result { - margin-top: 1rem; - padding: 1rem; - border-radius: 10px; -} - -.analysis-result.normal { - background: rgba(29, 209, 161, 0.1); - border: 1px solid rgba(29, 209, 161, 0.3); -} - -.analysis-result.anomaly { - background: rgba(255, 107, 107, 0.1); - border: 1px solid rgba(255, 107, 107, 0.3); -} - -.analysis-header { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 1rem; - font-weight: 600; -} - -.analysis-result.normal .analysis-header { - color: #1dd1a1; -} - -.analysis-result.anomaly .analysis-header { - color: #ff6b6b; -} - -.analysis-details { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 0.75rem; -} - -.detail-row { - display: flex; - justify-content: space-between; - font-size: 0.9rem; -} - -.detail-row span:first-child { - color: var(--text-secondary); -} - -.detail-row span:last-child { - color: var(--text-primary); - font-weight: 500; -} - -.detail-row .critical { - color: #ff6b6b; -} - -.detail-row .high { - color: #ff9f43; -} - -.detail-row .medium { - color: #feca57; -} - -.analysis-suggestion { - margin-top: 1rem; - padding: 0.75rem; - background: rgba(84, 160, 255, 0.1); - border-radius: 8px; - color: #54a0ff; - font-size: 0.9rem; - display: flex; - align-items: center; - gap: 0.5rem; -} - -/* Notifications */ -.notification { - position: fixed; - bottom: 20px; - right: 20px; - padding: 1rem 1.5rem; - border-radius: 10px; - display: flex; - align-items: center; - gap: 0.75rem; - z-index: 10000; - animation: slideInRight 0.3s ease-out; -} - -.notification.info { - background: var(--bg-secondary); - border: 1px solid rgba(84, 160, 255, 0.3); - color: #54a0ff; -} - -.notification.success { - background: var(--bg-secondary); - border: 1px solid rgba(29, 209, 161, 0.3); - color: #1dd1a1; -} - -.notification.error { - background: var(--bg-secondary); - border: 1px solid rgba(255, 107, 107, 0.3); - color: #ff6b6b; -} - -.notification.fade-out { - animation: fadeOut 0.3s ease-out forwards; -} - -@keyframes fadeOut { - to { - opacity: 0; - transform: translateX(20px); - } -} - -/* Responsive adjustments for Intelligence Dashboard */ -@media (max-width: 768px) { - .intelligence-cards { - grid-template-columns: repeat(2, 1fr); - } - - .intelligence-tabs { - overflow-x: auto; - padding-bottom: 0.5rem; - } - - .intelligence-tab { - white-space: nowrap; - flex-shrink: 0; - } - - .budget-intel-grid { - grid-template-columns: 1fr; - } - - .analyze-form .analyze-row { - flex-direction: column; - } - - .analyze-form input, - .analyze-form select { - width: 100%; - } - - .analysis-details { - grid-template-columns: 1fr; - } - - .reallocation-flow { - flex-direction: column; - align-items: flex-start; - } - - .reallocation-arrow { - transform: rotate(90deg); - align-self: center; - } -} - -@media (max-width: 480px) { - .intelligence-cards { - grid-template-columns: 1fr; - } - - .intel-card { - padding: 1rem; - } - - .intel-card-value { - font-size: 1.25rem; - } - - .volatility-bar-container { - margin-right: 50px; - } -} -/* Theme Toggle Button */ -.theme-toggle-btn { - background: none; - border: none; - font-size: 1.5rem; - cursor: pointer; - color: var(--text-color); - margin-left: auto; -} - -/* Default Light Theme Variables */ -:root { - --bg-color: #f3f6f9; - --text-color: #0f0f23; - --card-bg: #42779b; - --nav-bg: #0f0f23; -} - -/* Dark Theme Variables */ -.dark-theme { - --bg-color: #121212; - --text-color: #e0e0e0; - --card-bg: #1e1e1e; - --nav-bg: #0a0a0a; -} - -/* Apply Variables */ -body { - background-color: var(--bg-color); - color: var(--text-color); - transition: background-color 0.3s, color 0.3s; -} - -/* Example component styling */ -.header, -.navbar { - background-color: var(--nav-bg); -} - -.balance-card, -.income-card, -.expense-card, -.footer { - background-color: var(--card-bg); -} - -/* Subscription Manager Styles */ -.subscriptions-section { - padding: 2rem; - max-width: 1400px; - margin: 0 auto; -} - -.subscription-statistics { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 1.5rem; - margin-bottom: 2rem; -} - -.stat-card { - background: var(--bg-secondary); - padding: 1.5rem; - border-radius: 12px; - display: flex; - align-items: center; - gap: 1rem; - box-shadow: var(--shadow-card); - transition: all 0.3s ease; -} - -.stat-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-hover); -} - -.stat-card.stat-warning { - border-left: 4px solid var(--warning); -} - -.stat-card.stat-info { - border-left: 4px solid var(--accent-primary); -} - -.stat-icon { - font-size: 2.5rem; - opacity: 0.8; -} - -.stat-content { - flex: 1; -} - -.stat-value { - font-size: 1.8rem; - font-weight: bold; - color: var(--text-primary); -} - -.stat-label { - font-size: 0.9rem; - color: var(--text-secondary); - margin-top: 0.25rem; -} - -.stat-sublabel { - font-size: 0.8rem; - color: var(--warning); - margin-top: 0.25rem; -} - -.subscription-filters { - display: flex; - gap: 1rem; - margin-bottom: 2rem; - flex-wrap: wrap; -} - -.subscription-filter { - padding: 0.75rem 1.5rem; - border: 2px solid var(--bg-tertiary); - background: var(--bg-secondary); - color: var(--text-secondary); - border-radius: 8px; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.subscription-filter:hover { - background: var(--bg-tertiary); -} - -.subscription-filter.active { - background: var(--accent-primary); - color: var(--bg-primary); - border-color: var(--accent-primary); -} - -.subscription-list { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); - gap: 1.5rem; -} - -.subscription-card { - background: var(--bg-secondary); - border-radius: 12px; - padding: 1.5rem; - box-shadow: var(--shadow-card); - transition: all 0.3s ease; - border-left: 4px solid transparent; -} - -.subscription-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-hover); -} - -.subscription-card.subscription-active { - border-left-color: var(--success); -} - -.subscription-card.subscription-cancelled { - border-left-color: var(--error); - opacity: 0.7; -} - -.subscription-card.subscription-paused { - border-left-color: var(--warning); -} - -.subscription-card.subscription-trial { - border-left-color: var(--accent-primary); -} - -.subscription-card.subscription-unused { - background: linear-gradient(135deg, var(--bg-secondary) 0%, rgba(255, 107, 157, 0.1) 100%); -} - -.subscription-card.subscription-upcoming { - background: linear-gradient(135deg, var(--bg-secondary) 0%, rgba(255, 193, 7, 0.1) 100%); -} - -.subscription-header { - display: flex; - align-items: center; - gap: 1rem; - margin-bottom: 1rem; -} - -.subscription-logo { - font-size: 2.5rem; - width: 60px; - height: 60px; - display: flex; - align-items: center; - justify-content: center; - background: var(--bg-tertiary); - border-radius: 12px; -} - -.subscription-info { - flex: 1; -} - -.subscription-info h3 { - margin: 0; - font-size: 1.2rem; - color: var(--text-primary); -} - -.subscription-category { - font-size: 0.85rem; - color: var(--text-secondary); - text-transform: capitalize; -} - -.subscription-actions { - display: flex; - gap: 0.5rem; -} - -.btn-icon { - width: 32px; - height: 32px; - border-radius: 6px; - border: none; - background: var(--bg-tertiary); - color: var(--text-secondary); - cursor: pointer; - transition: all 0.2s ease; -} - -.btn-icon:hover { - background: var(--bg-glass); - transform: scale(1.1); -} - -.subscription-details { - margin: 1rem 0; -} - -.detail-row { - display: flex; - justify-content: space-between; - padding: 0.5rem 0; - border-bottom: 1px solid var(--bg-tertiary); -} - -.detail-label { - color: var(--text-secondary); - font-size: 0.9rem; -} - -.detail-value { - color: var(--text-primary); - font-weight: 500; -} - -.detail-value.text-warning { - color: var(--warning); - font-weight: bold; -} - -.subscription-status { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; - margin: 1rem 0; -} - -.status-badge { - padding: 0.4rem 0.8rem; - border-radius: 6px; - font-size: 0.85rem; - font-weight: 500; -} - -.subscription-active .status-badge { - background: rgba(0, 183, 94, 0.2); - color: var(--success); -} - -.subscription-cancelled .status-badge { - background: rgba(255, 133, 94, 0.2); - color: var(--error); -} - -.subscription-paused .status-badge { - background: rgba(255, 193, 7, 0.2); - color: var(--warning); -} - -.subscription-trial .status-badge { - background: rgba(64, 252, 208, 0.2); - color: var(--accent-primary); -} - -.badge-auto { - background: rgba(103, 126, 234, 0.2); - color: #667eea; -} - -.badge-ghost { - background: rgba(255, 107, 157, 0.2); - color: var(--accent-secondary); -} - -.badge-upcoming { - background: rgba(255, 193, 7, 0.2); - color: var(--warning); -} - -.subscription-alert { - padding: 1rem; - border-radius: 8px; - margin: 1rem 0; - display: flex; - justify-content: space-between; - align-items: center; - gap: 1rem; -} - -.alert-warning { - background: rgba(255, 193, 7, 0.1); - border: 1px solid var(--warning); - color: var(--text-primary); -} - -.subscription-footer { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; -} - -.btn-sm { - padding: 0.5rem 1rem; - border: none; - border-radius: 6px; - background: var(--bg-tertiary); - color: var(--text-primary); - cursor: pointer; - font-size: 0.85rem; - transition: all 0.2s ease; -} - -.btn-sm:hover { - background: var(--accent-primary); - color: var(--bg-primary); -} - -.btn-sm.btn-warning { - background: var(--warning); - color: var(--bg-primary); -} - -/* Modals */ -.modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.8); - display: none; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.modal-content { - background: var(--bg-secondary); - border-radius: 12px; - max-width: 600px; - width: 90%; - max-height: 90vh; - overflow-y: auto; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); -} - -.modal-content.large { - max-width: 900px; -} - -.modal-header { - padding: 1.5rem; - border-bottom: 1px solid var(--bg-tertiary); - display: flex; - justify-content: space-between; - align-items: center; -} - -.modal-header h2 { - margin: 0; - color: var(--text-primary); -} - -.modal-close { - background: none; - border: none; - font-size: 1.5rem; - color: var(--text-secondary); - cursor: pointer; - transition: all 0.2s ease; -} - -.modal-close:hover { - color: var(--error); - transform: rotate(90deg); -} - -.modal-body { - padding: 1.5rem; -} - -.detected-list { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.detected-item { - background: var(--bg-tertiary); - padding: 1.5rem; - border-radius: 8px; -} - -.detected-header { - display: flex; - align-items: center; - gap: 1rem; - margin-bottom: 1rem; -} - -.detected-logo { - font-size: 2rem; -} - -.detected-info h3 { - margin: 0; - color: var(--text-primary); -} - -.detected-confidence { - font-size: 0.85rem; - color: var(--success); -} - -.detected-details { - margin: 1rem 0; - color: var(--text-secondary); -} - -.detected-details p { - margin: 0.5rem 0; -} - -/* Empty State */ -.empty-state { - text-align: center; - padding: 3rem; -} - -.empty-icon { - font-size: 4rem; - margin-bottom: 1rem; - opacity: 0.5; -} - -.empty-state h3 { - color: var(--text-primary); - margin-bottom: 0.5rem; -} - -.empty-state p { - color: var(--text-secondary); - margin-bottom: 2rem; -} - -/* Toast Notifications */ -.toast { - position: fixed; - bottom: 20px; - right: 20px; - padding: 1rem 1.5rem; - border-radius: 8px; - background: var(--bg-secondary); - color: var(--text-primary); - box-shadow: var(--shadow-hover); - transform: translateX(400px); - transition: transform 0.3s ease; - z-index: 2000; -} - -.toast.show { - transform: translateX(0); -} - -.toast-success { - border-left: 4px solid var(--success); -} - -.toast-error { - border-left: 4px solid var(--error); -} - -.toast-info { - border-left: 4px solid var(--accent-primary); -} - -/* Loading Placeholder */ -.loading-placeholder { - text-align: center; - padding: 3rem; - color: var(--text-secondary); -} - -.loading-placeholder i { - font-size: 2rem; - margin-bottom: 1rem; - color: var(--accent-primary); -} - -/* ======================== - Financial Wellness & Health Score Styles (Issue #481) - ======================== */ - -.wellness-row { - display: grid; - grid-template-columns: 400px 1fr; - gap: 1.5rem; - margin-bottom: 2rem; -} - -.health-gauge-card, .insights-card { - background: var(--bg-secondary); - border-radius: 12px; - padding: 2rem; - box-shadow: var(--shadow-card); -} - -.health-gauge-widget { - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.health-gauge-header { - display: flex; - justify-content: space-between; - align-items: center; -} - -.health-gauge-header h3 { - margin: 0; - font-size: 1.3rem; - color: var(--text-primary); -} - -.health-trend { - padding: 0.4rem 0.8rem; - border-radius: 6px; - font-size: 0.9rem; - font-weight: 500; -} - -.health-trend-improving { - background: rgba(0, 183, 94, 0.2); - color: var(--success); -} - -.health-trend-declining { - background: rgba(255, 133, 94, 0.2); - color: var(--error); -} - -.health-trend-stable { - background: rgba(79, 172, 254, 0.2); - color: #4facfe; -} - -.health-gauge { - display: flex; - justify-content: center; - margin: 1rem 0; -} - -.gauge-svg { - width: 100%; - max-width: 250px; - height: auto; -} - -.gauge-score { - font-size: 2.5rem; - font-weight: bold; -} - -.gauge-grade { - font-size: 0.9rem; - text-transform: uppercase; - letter-spacing: 1px; -} - -.health-components { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.component-section h4 { - margin: 0 0 0.5rem 0; - font-size: 1rem; - color: var(--text-secondary); -} - -.component-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.75rem; - background: var(--bg-tertiary); - border-radius: 6px; - border-left: 3px solid transparent; -} - -.component-item.strength { - border-left-color: var(--success); -} - -.component-item.weakness { - border-left-color: var(--warning); -} - -.component-label { - font-size: 0.9rem; - color: var(--text-primary); -} - -.component-score { - font-weight: bold; - font-size: 1rem; - color: var(--accent-primary); -} - -.priority-badge { - padding: 0.2rem 0.6rem; - border-radius: 4px; - font-size: 0.75rem; - font-weight: 500; - margin-left: 0.5rem; -} - -.priority-critical { - background: rgba(255, 133, 94, 0.2); - color: var(--error); -} - -.priority-high { - background: rgba(255, 193, 7, 0.2); - color: var(--warning); -} - -.priority-medium { - background: rgba(79, 172, 254, 0.2); - color: #4facfe; -} - -.health-actions { - display: flex; - gap: 0.75rem; -} - -.health-actions .btn { - flex: 1; - padding: 0.75rem; - border-radius: 8px; - border: none; - cursor: pointer; - font-size: 0.9rem; - transition: all 0.2s ease; -} - -.health-actions .btn-primary { - background: var(--accent-primary); - color: var(--bg-primary); -} - -.health-actions .btn-secondary { - background: var(--bg-tertiary); - color: var(--text-primary); -} - -.health-actions .btn:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); -} - -/* Smart Insights Styles */ -.smart-insights { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.insights-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5rem; -} - -.insights-header h3 { - margin: 0; - font-size: 1.3rem; - color: var(--text-primary); -} - -.insights-list { - display: flex; - flex-direction: column; - gap: 1rem; - max-height: 600px; - overflow-y: auto; - padding-right: 0.5rem; -} - -.insights-empty { - text-align: center; - padding: 3rem; - color: var(--text-secondary); -} - -.insight-card { - background: var(--bg-tertiary); - border-radius: 8px; - padding: 1.25rem; - border-left: 4px solid transparent; - transition: all 0.2s ease; -} - -.insight-card:hover { - background: var(--bg-glass); - transform: translateX(4px); -} - -.insight-critical { - border-left-color: var(--error); -} - -.insight-high { - border-left-color: var(--warning); -} - -.insight-medium { - border-left-color: #4facfe; -} - -.insight-low, .insight-info { - border-left-color: var(--accent-primary); -} - -.insight-header { - display: flex; - align-items: flex-start; - gap: 0.75rem; - margin-bottom: 0.75rem; -} - -.insight-icon { - font-size: 1.5rem; -} - -.insight-title-section { - flex: 1; -} - -.insight-title-section h4 { - margin: 0 0 0.25rem 0; - font-size: 1rem; - color: var(--text-primary); -} - -.insight-type { - font-size: 0.8rem; - color: var(--text-secondary); - text-transform: capitalize; -} - -.insight-confidence { - font-size: 0.8rem; - color: var(--success); - padding: 0.2rem 0.6rem; - background: rgba(0, 183, 94, 0.1); - border-radius: 4px; -} - -.insight-message { - color: var(--text-primary); - line-height: 1.5; - margin: 0.75rem 0; -} - -.insight-metrics { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin: 0.75rem 0; -} - -.insight-metrics span { - padding: 0.4rem 0.8rem; - background: var(--bg-secondary); - border-radius: 6px; - font-size: 0.85rem; - color: var(--text-secondary); -} - -.insight-actions { - display: flex; - gap: 0.5rem; - margin: 1rem 0 0.5rem 0; - flex-wrap: wrap; -} - -.insight-actions .btn-action { - padding: 0.5rem 1rem; - background: var(--accent-primary); - color: var(--bg-primary); - border: none; - border-radius: 6px; - cursor: pointer; - font-size: 0.85rem; - transition: all 0.2s ease; -} - -.insight-actions .btn-action:hover { - background: var(--accent-secondary); - transform: translateY(-1px); -} - -.insight-footer { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid var(--bg-secondary); -} - -.insight-age { - font-size: 0.8rem; - color: var(--text-secondary); -} - -.insight-controls { - display: flex; - gap: 1rem; -} - -.btn-text { - background: none; - border: none; - color: var(--accent-primary); - cursor: pointer; - font-size: 0.85rem; - transition: all 0.2s ease; -} - -.btn-text:hover { - color: var(--accent-secondary); - text-decoration: underline; -} - -/* Responsive adjustments */ -@media (max-width: 1200px) { - .wellness-row { - grid-template-columns: 1fr; - } -} - -@media (max-width: 768px) { - .health-gauge-card, .insights-card { - padding: 1rem; - } - - .health-actions { - flex-direction: column; - } - - .insight-header { - flex-direction: column; - } -} - -/* ========================= - Scroll To Top Button - ========================= */ - -.scroll-to-top { - position: fixed; - bottom: 30px; - right: 30px; - width: 52px; - height: 52px; - border-radius: 50%; - border: none; - background: linear-gradient(135deg, #64ffda, #48e0c1); - color: #0f0f23; - font-size: 1.2rem; - cursor: pointer; - display: none; - align-items: center; - justify-content: center; - z-index: 9999; - box-shadow: 0 8px 25px rgba(100, 255, 218, 0.35); - transition: transform 0.25s ease, box-shadow 0.25s ease, opacity 0.25s ease; -} - -.scroll-to-top:hover { - transform: translateY(-4px); - box-shadow: 0 12px 35px rgba(100, 255, 218, 0.5); -} - -.scroll-to-top.show { - display: flex; -} - -/* Mobile adjustment */ -@media (max-width: 768px) { - .scroll-to-top { - bottom: 20px; - right: 20px; - width: 46px; - height: 46px; - } -} - -/* ========================= - FOOTER UX & ACCESSIBILITY - (Issue #549 – Scoped Fix) -========================= */ - -.footer-links a { - color: inherit; - text-decoration: none; - transition: color 0.25s ease, text-decoration-color 0.25s ease; -} - -.footer-links a:hover, -.footer-links a:focus-visible { - color: #64ffda; - text-decoration: underline; - outline: none; -} - -.social-link { - display: inline-flex; - align-items: center; - justify-content: center; - transition: transform 0.25s ease, color 0.25s ease; -} - -.social-link:hover, -.social-link:focus-visible { - transform: scale(1.15); - color: #64ffda; - outline: none; -} diff --git a/goals.html b/goals.html deleted file mode 100644 index 3e2fb7e1..00000000 --- a/goals.html +++ /dev/null @@ -1,791 +0,0 @@ - - - - - - - - - - - - Financial Goals - ExpenseFlow - - - - -
- -
- -
- - -
-

Create New Goal

-
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- - -
-
- -
-
- - - - \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 91f0e856..00000000 --- a/index.html +++ /dev/null @@ -1,1638 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - ExpenseFlow - Smart Money Management - - - - - - - - - - -
-
-

Smart Money Management

-

Take control of your finances with our intuitive expense tracker

-
- -
- - - - - - - - -
- -
-
- - - - -
-
-

Transaction History

-
-
- - -
-
- - - -
-
- -
- - -
-
- - -
- -
-
-
-
    - -
-
- - -
-

Data Management

-
-
-

Export Data

-
- - - -
-
-
-

Import Data

-
- - - -
-
- -
-
-
-
- - -
-
-
-

Export Expenses

- -
- -
- -
-

Export Format

-
- - -
-
- - -
-

Filter Options

-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-

Export Preview

-
-
Loading preview...
-
-
-
- - -
-
- - -
-

Add New Transaction

-
-
-
- - -
-
- - -
-
-
-
- - - Tip: Use negative values for expenses, positive for income -
-
- - -
-
-
-
- - -
-
-
- -
-
- - -
-
-

AI Budget Intelligence

-
- -
-
- - -
-
- Analyzing spending patterns... -
- - -
- - -
- - - - - -
- - -
-
-

Z-Score Anomaly Detection

-

Transactions flagged with Z-Score ≥ 2.0 (statistically unusual spending)

-
-
- -
-

Spending Volatility Analysis

-

Category volatility index based on coefficient of variation

-
-
- -
-

Self-Healing Budget Suggestions

-

AI-suggested fund transfers from surplus to deficit categories

-
-
- -
-

Budget Alerts

-

AI-driven alerts including predictions and anomaly warnings

-
-
- -
-

Budget Intelligence Overview

-

Budgets with AI-enhanced statistics and predictions

-
-
-
- - -
-

Analyze Transaction

-
-
- - - - -
-
-
-
-
- - -
-
-

Recurring Expenses & Subscriptions

- -
- - -
- -
- - -
- -
-
- - -
-
-
-

Add Recurring Expense

- -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- -
-
- -
- - -
- - -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-

ExpenseFlow Assistant

-

Ask me anything about your finances

-
- -
- - -
-
-
-

Hi! 👋 I'm your ExpenseFlow Assistant. How can I help you manage your finances today?

-
-
-
- - -
-
- - -
-
-
- - - -
- - -
-
-
- -
-

Unlock ExpenseFlow

-

Your journey to financial freedom starts here. Sign in to access smart analytics, personalized goals, and secure tracking.

- -
- - -
- -
-

Premium Features:

-
    -
  • Smart Tracking
  • -
  • Budget Planning
  • -
  • Cloud Sync
  • -
  • Data Export
  • -
-
-
-
- - -
- -
- - -
- -
- - - - - - - - - - \ No newline at end of file diff --git a/manifest.json b/manifest.json deleted file mode 100644 index 7d549274..00000000 --- a/manifest.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "ExpenseFlow - Smart Money Management", - "short_name": "ExpenseFlow", - "description": "Take control of your finances with our intuitive expense tracker", - "start_url": "/", - "display": "standalone", - "theme_color": "#64ffda", - "background_color": "#0f0f23", - "icons": [ - { - "src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyIiBoZWlnaHQ9IjE5MiIgdmlld0JveD0iMCAwIDE5MiAxOTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIiByeD0iNDIiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhclswXSkiLz4KPHBhdGggZD0iTTQ4IDY0SDQ4LjA0OEgxNDRWMTI4SDk2VjE0NEgxNDRDMTUyLjgzNyAxNDQgMTYwIDEzNi44MzcgMTYwIDEyOFY2NEM1OCA1Ni4xNjMgNTIuMDQzIDQ4IDQ4IEg0OEM0MC4wNzEgNDggMzIgNTYuMDcxIDMyIDY0VjEyOEMzMiAxMzUuOTI5IDQwLjA3MSAxNDQgNDggMTQ0SDgwVjEyOEg0OFY2NFoiIGZpbGw9IiM2NGZmZGEiLz4KPHBhdGggZD0iTTY0IDgwSDgwVjk2SDY0VjgwWiIgZmlsbD0iIzY0ZmZkYSIvPgo8cGF0aCBkPSJNOTYgODBIMTI4Vjk2SDk2VjgwWiIgZmlsbD0iIzY0ZmZkYSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyWzBdIiB4MT0iMCIgeTE9IjAiIHgyPSIxOTIiIHkyPSIxOTIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzY2N2VlYSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM3NjRiYTIiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K", - "sizes": "192x192", - "type": "image/svg+xml" - } - ] -} \ No newline at end of file diff --git a/.env.example b/server/.env.example similarity index 100% rename from .env.example rename to server/.env.example diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 00000000..80bfec62 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,20 @@ +# Use Node.js 18 as the base image +FROM node:18-alpine + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package.json package-lock.json ./ + +# Install dependencies +RUN npm ci --only=production + +# Copy application code +COPY . . + +# Expose port +EXPOSE 5000 + +# Start the server +CMD ["npm", "start"] diff --git a/chatbot/README.md b/server/chatbot/README.md similarity index 100% rename from chatbot/README.md rename to server/chatbot/README.md diff --git a/chatbot/chatbot.config.js b/server/chatbot/chatbot.config.js similarity index 100% rename from chatbot/chatbot.config.js rename to server/chatbot/chatbot.config.js diff --git a/chatbot/chatbot.css b/server/chatbot/chatbot.css similarity index 100% rename from chatbot/chatbot.css rename to server/chatbot/chatbot.css diff --git a/chatbot/chatbot.data.js b/server/chatbot/chatbot.data.js similarity index 100% rename from chatbot/chatbot.data.js rename to server/chatbot/chatbot.data.js diff --git a/chatbot/chatbot.html b/server/chatbot/chatbot.html similarity index 100% rename from chatbot/chatbot.html rename to server/chatbot/chatbot.html diff --git a/chatbot/chatbot.js b/server/chatbot/chatbot.js similarity index 100% rename from chatbot/chatbot.js rename to server/chatbot/chatbot.js diff --git a/middleware/analyticsValidator.js b/server/middleware/analyticsValidator.js similarity index 100% rename from middleware/analyticsValidator.js rename to server/middleware/analyticsValidator.js diff --git a/middleware/auditMiddleware.js b/server/middleware/auditMiddleware.js similarity index 100% rename from middleware/auditMiddleware.js rename to server/middleware/auditMiddleware.js diff --git a/middleware/auth.js b/server/middleware/auth.js similarity index 100% rename from middleware/auth.js rename to server/middleware/auth.js diff --git a/middleware/authMiddleware.js b/server/middleware/authMiddleware.js similarity index 100% rename from middleware/authMiddleware.js rename to server/middleware/authMiddleware.js diff --git a/middleware/bankingValidator.js b/server/middleware/bankingValidator.js similarity index 100% rename from middleware/bankingValidator.js rename to server/middleware/bankingValidator.js diff --git a/middleware/categorizationValidator.js b/server/middleware/categorizationValidator.js similarity index 100% rename from middleware/categorizationValidator.js rename to server/middleware/categorizationValidator.js diff --git a/middleware/clerkAuth.js b/server/middleware/clerkAuth.js similarity index 100% rename from middleware/clerkAuth.js rename to server/middleware/clerkAuth.js diff --git a/middleware/deviceFingerprint.js b/server/middleware/deviceFingerprint.js similarity index 100% rename from middleware/deviceFingerprint.js rename to server/middleware/deviceFingerprint.js diff --git a/middleware/errorMiddleware.js b/server/middleware/errorMiddleware.js similarity index 100% rename from middleware/errorMiddleware.js rename to server/middleware/errorMiddleware.js diff --git a/middleware/forecastValidator.js b/server/middleware/forecastValidator.js similarity index 100% rename from middleware/forecastValidator.js rename to server/middleware/forecastValidator.js diff --git a/middleware/gamificationValidator.js b/server/middleware/gamificationValidator.js similarity index 100% rename from middleware/gamificationValidator.js rename to server/middleware/gamificationValidator.js diff --git a/middleware/inputValidator.js b/server/middleware/inputValidator.js similarity index 100% rename from middleware/inputValidator.js rename to server/middleware/inputValidator.js diff --git a/middleware/insightValidator.js b/server/middleware/insightValidator.js similarity index 100% rename from middleware/insightValidator.js rename to server/middleware/insightValidator.js diff --git a/middleware/insightsValidator.js b/server/middleware/insightsValidator.js similarity index 100% rename from middleware/insightsValidator.js rename to server/middleware/insightsValidator.js diff --git a/middleware/investmentValidator.js b/server/middleware/investmentValidator.js similarity index 100% rename from middleware/investmentValidator.js rename to server/middleware/investmentValidator.js diff --git a/middleware/policyGuard.js b/server/middleware/policyGuard.js similarity index 100% rename from middleware/policyGuard.js rename to server/middleware/policyGuard.js diff --git a/middleware/rateLimit.js b/server/middleware/rateLimit.js similarity index 100% rename from middleware/rateLimit.js rename to server/middleware/rateLimit.js diff --git a/middleware/rateLimiter.js b/server/middleware/rateLimiter.js similarity index 100% rename from middleware/rateLimiter.js rename to server/middleware/rateLimiter.js diff --git a/middleware/rbac.js b/server/middleware/rbac.js similarity index 100% rename from middleware/rbac.js rename to server/middleware/rbac.js diff --git a/middleware/recurringValidator.js b/server/middleware/recurringValidator.js similarity index 100% rename from middleware/recurringValidator.js rename to server/middleware/recurringValidator.js diff --git a/middleware/roleCheck.js b/server/middleware/roleCheck.js similarity index 100% rename from middleware/roleCheck.js rename to server/middleware/roleCheck.js diff --git a/middleware/sanitization.js b/server/middleware/sanitization.js similarity index 100% rename from middleware/sanitization.js rename to server/middleware/sanitization.js diff --git a/middleware/sanitizer.js b/server/middleware/sanitizer.js similarity index 100% rename from middleware/sanitizer.js rename to server/middleware/sanitizer.js diff --git a/middleware/sharedSpaceValidator.js b/server/middleware/sharedSpaceValidator.js similarity index 100% rename from middleware/sharedSpaceValidator.js rename to server/middleware/sharedSpaceValidator.js diff --git a/middleware/socketAuth.js b/server/middleware/socketAuth.js similarity index 100% rename from middleware/socketAuth.js rename to server/middleware/socketAuth.js diff --git a/middleware/spaceAuth.js b/server/middleware/spaceAuth.js similarity index 100% rename from middleware/spaceAuth.js rename to server/middleware/spaceAuth.js diff --git a/middleware/splitValidator.js b/server/middleware/splitValidator.js similarity index 100% rename from middleware/splitValidator.js rename to server/middleware/splitValidator.js diff --git a/middleware/subscriptionValidator.js b/server/middleware/subscriptionValidator.js similarity index 100% rename from middleware/subscriptionValidator.js rename to server/middleware/subscriptionValidator.js diff --git a/middleware/taxValidator.js b/server/middleware/taxValidator.js similarity index 100% rename from middleware/taxValidator.js rename to server/middleware/taxValidator.js diff --git a/middleware/twoFactorAuthMiddleware.js b/server/middleware/twoFactorAuthMiddleware.js similarity index 100% rename from middleware/twoFactorAuthMiddleware.js rename to server/middleware/twoFactorAuthMiddleware.js diff --git a/middleware/uploadMiddleware.js b/server/middleware/uploadMiddleware.js similarity index 100% rename from middleware/uploadMiddleware.js rename to server/middleware/uploadMiddleware.js diff --git a/models/AIPrediction.js b/server/models/AIPrediction.js similarity index 100% rename from models/AIPrediction.js rename to server/models/AIPrediction.js diff --git a/models/AITrainingData.js b/server/models/AITrainingData.js similarity index 100% rename from models/AITrainingData.js rename to server/models/AITrainingData.js diff --git a/models/Account.js b/server/models/Account.js similarity index 100% rename from models/Account.js rename to server/models/Account.js diff --git a/models/AccountingConnection.js b/server/models/AccountingConnection.js similarity index 100% rename from models/AccountingConnection.js rename to server/models/AccountingConnection.js diff --git a/models/Achievement.js b/server/models/Achievement.js similarity index 100% rename from models/Achievement.js rename to server/models/Achievement.js diff --git a/models/AmortizationSchedule.js b/server/models/AmortizationSchedule.js similarity index 100% rename from models/AmortizationSchedule.js rename to server/models/AmortizationSchedule.js diff --git a/models/AnalyticsCache.js b/server/models/AnalyticsCache.js similarity index 100% rename from models/AnalyticsCache.js rename to server/models/AnalyticsCache.js diff --git a/models/AnomalyEvent.js b/server/models/AnomalyEvent.js similarity index 100% rename from models/AnomalyEvent.js rename to server/models/AnomalyEvent.js diff --git a/models/AnomalyRule.js b/server/models/AnomalyRule.js similarity index 100% rename from models/AnomalyRule.js rename to server/models/AnomalyRule.js diff --git a/models/ApprovalRequest.js b/server/models/ApprovalRequest.js similarity index 100% rename from models/ApprovalRequest.js rename to server/models/ApprovalRequest.js diff --git a/models/ApprovalWorkflow.js b/server/models/ApprovalWorkflow.js similarity index 100% rename from models/ApprovalWorkflow.js rename to server/models/ApprovalWorkflow.js diff --git a/models/Asset.js b/server/models/Asset.js similarity index 100% rename from models/Asset.js rename to server/models/Asset.js diff --git a/models/AssetDepreciation.js b/server/models/AssetDepreciation.js similarity index 100% rename from models/AssetDepreciation.js rename to server/models/AssetDepreciation.js diff --git a/models/AssetTransaction.js b/server/models/AssetTransaction.js similarity index 100% rename from models/AssetTransaction.js rename to server/models/AssetTransaction.js diff --git a/models/AuditLog.js b/server/models/AuditLog.js similarity index 100% rename from models/AuditLog.js rename to server/models/AuditLog.js diff --git a/models/BackOrder.js b/server/models/BackOrder.js similarity index 100% rename from models/BackOrder.js rename to server/models/BackOrder.js diff --git a/models/BalanceHistory.js b/server/models/BalanceHistory.js similarity index 100% rename from models/BalanceHistory.js rename to server/models/BalanceHistory.js diff --git a/models/BankConnection.js b/server/models/BankConnection.js similarity index 100% rename from models/BankConnection.js rename to server/models/BankConnection.js diff --git a/models/BankInstitution.js b/server/models/BankInstitution.js similarity index 100% rename from models/BankInstitution.js rename to server/models/BankInstitution.js diff --git a/models/BankLink.js b/server/models/BankLink.js similarity index 100% rename from models/BankLink.js rename to server/models/BankLink.js diff --git a/models/Bill.js b/server/models/Bill.js similarity index 100% rename from models/Bill.js rename to server/models/Bill.js diff --git a/models/BillPayment.js b/server/models/BillPayment.js similarity index 100% rename from models/BillPayment.js rename to server/models/BillPayment.js diff --git a/models/BlockedEntity.js b/server/models/BlockedEntity.js similarity index 100% rename from models/BlockedEntity.js rename to server/models/BlockedEntity.js diff --git a/models/Budget.js b/server/models/Budget.js similarity index 100% rename from models/Budget.js rename to server/models/Budget.js diff --git a/models/BudgetForecast.js b/server/models/BudgetForecast.js similarity index 100% rename from models/BudgetForecast.js rename to server/models/BudgetForecast.js diff --git a/models/BudgetSnapshot.js b/server/models/BudgetSnapshot.js similarity index 100% rename from models/BudgetSnapshot.js rename to server/models/BudgetSnapshot.js diff --git a/models/CalendarEvent.js b/server/models/CalendarEvent.js similarity index 100% rename from models/CalendarEvent.js rename to server/models/CalendarEvent.js diff --git a/models/CashFlowForecast.js b/server/models/CashFlowForecast.js similarity index 100% rename from models/CashFlowForecast.js rename to server/models/CashFlowForecast.js diff --git a/models/CategoryPattern.js b/server/models/CategoryPattern.js similarity index 100% rename from models/CategoryPattern.js rename to server/models/CategoryPattern.js diff --git a/models/CategoryRule.js b/server/models/CategoryRule.js similarity index 100% rename from models/CategoryRule.js rename to server/models/CategoryRule.js diff --git a/models/CategoryTraining.js b/server/models/CategoryTraining.js similarity index 100% rename from models/CategoryTraining.js rename to server/models/CategoryTraining.js diff --git a/models/Challenge.js b/server/models/Challenge.js similarity index 100% rename from models/Challenge.js rename to server/models/Challenge.js diff --git a/models/ChallengeParticipant.js b/server/models/ChallengeParticipant.js similarity index 100% rename from models/ChallengeParticipant.js rename to server/models/ChallengeParticipant.js diff --git a/models/Chat.js b/server/models/Chat.js similarity index 100% rename from models/Chat.js rename to server/models/Chat.js diff --git a/models/Client.js b/server/models/Client.js similarity index 100% rename from models/Client.js rename to server/models/Client.js diff --git a/models/ComplianceFramework.js b/server/models/ComplianceFramework.js similarity index 100% rename from models/ComplianceFramework.js rename to server/models/ComplianceFramework.js diff --git a/models/ComplianceRule.js b/server/models/ComplianceRule.js similarity index 100% rename from models/ComplianceRule.js rename to server/models/ComplianceRule.js diff --git a/models/ComplianceViolation.js b/server/models/ComplianceViolation.js similarity index 100% rename from models/ComplianceViolation.js rename to server/models/ComplianceViolation.js diff --git a/models/Currency.js b/server/models/Currency.js similarity index 100% rename from models/Currency.js rename to server/models/Currency.js diff --git a/models/CurrencyRate.js b/server/models/CurrencyRate.js similarity index 100% rename from models/CurrencyRate.js rename to server/models/CurrencyRate.js diff --git a/models/CustomDashboard.js b/server/models/CustomDashboard.js similarity index 100% rename from models/CustomDashboard.js rename to server/models/CustomDashboard.js diff --git a/models/DataWarehouse.js b/server/models/DataWarehouse.js similarity index 100% rename from models/DataWarehouse.js rename to server/models/DataWarehouse.js diff --git a/models/DebtAccount.js b/server/models/DebtAccount.js similarity index 100% rename from models/DebtAccount.js rename to server/models/DebtAccount.js diff --git a/models/Deduction.js b/server/models/Deduction.js similarity index 100% rename from models/Deduction.js rename to server/models/Deduction.js diff --git a/models/DeviceFingerprint.js b/server/models/DeviceFingerprint.js similarity index 100% rename from models/DeviceFingerprint.js rename to server/models/DeviceFingerprint.js diff --git a/models/DocumentFolder.js b/server/models/DocumentFolder.js similarity index 100% rename from models/DocumentFolder.js rename to server/models/DocumentFolder.js diff --git a/models/EmployeePerk.js b/server/models/EmployeePerk.js similarity index 100% rename from models/EmployeePerk.js rename to server/models/EmployeePerk.js diff --git a/models/ExchangeHedge.js b/server/models/ExchangeHedge.js similarity index 100% rename from models/ExchangeHedge.js rename to server/models/ExchangeHedge.js diff --git a/models/Expense.js b/server/models/Expense.js similarity index 100% rename from models/Expense.js rename to server/models/Expense.js diff --git a/models/ExpenseSplit.js b/server/models/ExpenseSplit.js similarity index 100% rename from models/ExpenseSplit.js rename to server/models/ExpenseSplit.js diff --git a/models/ExpenseSubmission.js b/server/models/ExpenseSubmission.js similarity index 100% rename from models/ExpenseSubmission.js rename to server/models/ExpenseSubmission.js diff --git a/models/FinancialAlert.js b/server/models/FinancialAlert.js similarity index 100% rename from models/FinancialAlert.js rename to server/models/FinancialAlert.js diff --git a/models/FinancialHealthScore.js b/server/models/FinancialHealthScore.js similarity index 100% rename from models/FinancialHealthScore.js rename to server/models/FinancialHealthScore.js diff --git a/models/FinancialInsight.js b/server/models/FinancialInsight.js similarity index 100% rename from models/FinancialInsight.js rename to server/models/FinancialInsight.js diff --git a/models/FinancialReport.js b/server/models/FinancialReport.js similarity index 100% rename from models/FinancialReport.js rename to server/models/FinancialReport.js diff --git a/models/FixedAsset.js b/server/models/FixedAsset.js similarity index 100% rename from models/FixedAsset.js rename to server/models/FixedAsset.js diff --git a/models/Forecast.js b/server/models/Forecast.js similarity index 100% rename from models/Forecast.js rename to server/models/Forecast.js diff --git a/models/ForecastScenario.js b/server/models/ForecastScenario.js similarity index 100% rename from models/ForecastScenario.js rename to server/models/ForecastScenario.js diff --git a/models/ForecastSnapshot.js b/server/models/ForecastSnapshot.js similarity index 100% rename from models/ForecastSnapshot.js rename to server/models/ForecastSnapshot.js diff --git a/models/FraudDetection.js b/server/models/FraudDetection.js similarity index 100% rename from models/FraudDetection.js rename to server/models/FraudDetection.js diff --git a/models/Goal.js b/server/models/Goal.js similarity index 100% rename from models/Goal.js rename to server/models/Goal.js diff --git a/models/Group.js b/server/models/Group.js similarity index 100% rename from models/Group.js rename to server/models/Group.js diff --git a/models/GroupInvite.js b/server/models/GroupInvite.js similarity index 100% rename from models/GroupInvite.js rename to server/models/GroupInvite.js diff --git a/models/ImmutableAuditLog.js b/server/models/ImmutableAuditLog.js similarity index 100% rename from models/ImmutableAuditLog.js rename to server/models/ImmutableAuditLog.js diff --git a/models/ImportedTransaction.js b/server/models/ImportedTransaction.js similarity index 100% rename from models/ImportedTransaction.js rename to server/models/ImportedTransaction.js diff --git a/models/IncomeSource.js b/server/models/IncomeSource.js similarity index 100% rename from models/IncomeSource.js rename to server/models/IncomeSource.js diff --git a/models/Insight.js b/server/models/Insight.js similarity index 100% rename from models/Insight.js rename to server/models/Insight.js diff --git a/models/Integration.js b/server/models/Integration.js similarity index 100% rename from models/Integration.js rename to server/models/Integration.js diff --git a/models/Investment.js b/server/models/Investment.js similarity index 100% rename from models/Investment.js rename to server/models/Investment.js diff --git a/models/Invoice.js b/server/models/Invoice.js similarity index 100% rename from models/Invoice.js rename to server/models/Invoice.js diff --git a/models/LinkedAccount.js b/server/models/LinkedAccount.js similarity index 100% rename from models/LinkedAccount.js rename to server/models/LinkedAccount.js diff --git a/models/LiquidityThreshold.js b/server/models/LiquidityThreshold.js similarity index 100% rename from models/LiquidityThreshold.js rename to server/models/LiquidityThreshold.js diff --git a/models/MerchantDatabase.js b/server/models/MerchantDatabase.js similarity index 100% rename from models/MerchantDatabase.js rename to server/models/MerchantDatabase.js diff --git a/models/MultiCurrencyWallet.js b/server/models/MultiCurrencyWallet.js similarity index 100% rename from models/MultiCurrencyWallet.js rename to server/models/MultiCurrencyWallet.js diff --git a/models/NetWorthSnapshot.js b/server/models/NetWorthSnapshot.js similarity index 100% rename from models/NetWorthSnapshot.js rename to server/models/NetWorthSnapshot.js diff --git a/models/Notification.js b/server/models/Notification.js similarity index 100% rename from models/Notification.js rename to server/models/Notification.js diff --git a/models/Pattern.js b/server/models/Pattern.js similarity index 100% rename from models/Pattern.js rename to server/models/Pattern.js diff --git a/models/Payment.js b/server/models/Payment.js similarity index 100% rename from models/Payment.js rename to server/models/Payment.js diff --git a/models/PayrollRun.js b/server/models/PayrollRun.js similarity index 100% rename from models/PayrollRun.js rename to server/models/PayrollRun.js diff --git a/models/Policy.js b/server/models/Policy.js similarity index 100% rename from models/Policy.js rename to server/models/Policy.js diff --git a/models/Portfolio.js b/server/models/Portfolio.js similarity index 100% rename from models/Portfolio.js rename to server/models/Portfolio.js diff --git a/models/PriceHistory.js b/server/models/PriceHistory.js similarity index 100% rename from models/PriceHistory.js rename to server/models/PriceHistory.js diff --git a/models/ProcurementOrder.js b/server/models/ProcurementOrder.js similarity index 100% rename from models/ProcurementOrder.js rename to server/models/ProcurementOrder.js diff --git a/models/Project.js b/server/models/Project.js similarity index 100% rename from models/Project.js rename to server/models/Project.js diff --git a/models/ProjectInvoice.js b/server/models/ProjectInvoice.js similarity index 100% rename from models/ProjectInvoice.js rename to server/models/ProjectInvoice.js diff --git a/models/Receipt.js b/server/models/Receipt.js similarity index 100% rename from models/Receipt.js rename to server/models/Receipt.js diff --git a/models/ReceiptDocument.js b/server/models/ReceiptDocument.js similarity index 100% rename from models/ReceiptDocument.js rename to server/models/ReceiptDocument.js diff --git a/models/ReconciliationRule.js b/server/models/ReconciliationRule.js similarity index 100% rename from models/ReconciliationRule.js rename to server/models/ReconciliationRule.js diff --git a/models/RecurringExpense.js b/server/models/RecurringExpense.js similarity index 100% rename from models/RecurringExpense.js rename to server/models/RecurringExpense.js diff --git a/models/ReminderSchedule.js b/server/models/ReminderSchedule.js similarity index 100% rename from models/ReminderSchedule.js rename to server/models/ReminderSchedule.js diff --git a/models/RiskScore.js b/server/models/RiskScore.js similarity index 100% rename from models/RiskScore.js rename to server/models/RiskScore.js diff --git a/models/Rule.js b/server/models/Rule.js similarity index 100% rename from models/Rule.js rename to server/models/Rule.js diff --git a/models/SalaryStructure.js b/server/models/SalaryStructure.js similarity index 100% rename from models/SalaryStructure.js rename to server/models/SalaryStructure.js diff --git a/models/SeasonalPattern.js b/server/models/SeasonalPattern.js similarity index 100% rename from models/SeasonalPattern.js rename to server/models/SeasonalPattern.js diff --git a/models/SecurityEvent.js b/server/models/SecurityEvent.js similarity index 100% rename from models/SecurityEvent.js rename to server/models/SecurityEvent.js diff --git a/models/Session.js b/server/models/Session.js similarity index 100% rename from models/Session.js rename to server/models/Session.js diff --git a/models/Settlement.js b/server/models/Settlement.js similarity index 100% rename from models/Settlement.js rename to server/models/Settlement.js diff --git a/models/SharedGoal.js b/server/models/SharedGoal.js similarity index 100% rename from models/SharedGoal.js rename to server/models/SharedGoal.js diff --git a/models/SharedSpace.js b/server/models/SharedSpace.js similarity index 100% rename from models/SharedSpace.js rename to server/models/SharedSpace.js diff --git a/models/SpaceActivity.js b/server/models/SpaceActivity.js similarity index 100% rename from models/SpaceActivity.js rename to server/models/SpaceActivity.js diff --git a/models/SpendingAnomaly.js b/server/models/SpendingAnomaly.js similarity index 100% rename from models/SpendingAnomaly.js rename to server/models/SpendingAnomaly.js diff --git a/models/SpendingPattern.js b/server/models/SpendingPattern.js similarity index 100% rename from models/SpendingPattern.js rename to server/models/SpendingPattern.js diff --git a/models/SplitExpense.js b/server/models/SplitExpense.js similarity index 100% rename from models/SplitExpense.js rename to server/models/SplitExpense.js diff --git a/models/SplitGroup.js b/server/models/SplitGroup.js similarity index 100% rename from models/SplitGroup.js rename to server/models/SplitGroup.js diff --git a/models/StockItem.js b/server/models/StockItem.js similarity index 100% rename from models/StockItem.js rename to server/models/StockItem.js diff --git a/models/Subscription.js b/server/models/Subscription.js similarity index 100% rename from models/Subscription.js rename to server/models/Subscription.js diff --git a/models/SyncLog.js b/server/models/SyncLog.js similarity index 100% rename from models/SyncLog.js rename to server/models/SyncLog.js diff --git a/models/SyncQueue.js b/server/models/SyncQueue.js similarity index 100% rename from models/SyncQueue.js rename to server/models/SyncQueue.js diff --git a/models/Tag.js b/server/models/Tag.js similarity index 100% rename from models/Tag.js rename to server/models/Tag.js diff --git a/models/TaxAuditPack.js b/server/models/TaxAuditPack.js similarity index 100% rename from models/TaxAuditPack.js rename to server/models/TaxAuditPack.js diff --git a/models/TaxCategory.js b/server/models/TaxCategory.js similarity index 100% rename from models/TaxCategory.js rename to server/models/TaxCategory.js diff --git a/models/TaxConfig.js b/server/models/TaxConfig.js similarity index 100% rename from models/TaxConfig.js rename to server/models/TaxConfig.js diff --git a/models/TaxDocument.js b/server/models/TaxDocument.js similarity index 100% rename from models/TaxDocument.js rename to server/models/TaxDocument.js diff --git a/models/TaxEstimate.js b/server/models/TaxEstimate.js similarity index 100% rename from models/TaxEstimate.js rename to server/models/TaxEstimate.js diff --git a/models/TaxProfile.js b/server/models/TaxProfile.js similarity index 100% rename from models/TaxProfile.js rename to server/models/TaxProfile.js diff --git a/models/TaxRule.js b/server/models/TaxRule.js similarity index 100% rename from models/TaxRule.js rename to server/models/TaxRule.js diff --git a/models/Team.js b/server/models/Team.js similarity index 100% rename from models/Team.js rename to server/models/Team.js diff --git a/models/TimeEntry.js b/server/models/TimeEntry.js similarity index 100% rename from models/TimeEntry.js rename to server/models/TimeEntry.js diff --git a/models/Transaction.js b/server/models/Transaction.js similarity index 100% rename from models/Transaction.js rename to server/models/Transaction.js diff --git a/models/Transfer.js b/server/models/Transfer.js similarity index 100% rename from models/Transfer.js rename to server/models/Transfer.js diff --git a/models/TreasuryVault.js b/server/models/TreasuryVault.js similarity index 100% rename from models/TreasuryVault.js rename to server/models/TreasuryVault.js diff --git a/models/TrustedDevice.js b/server/models/TrustedDevice.js similarity index 100% rename from models/TrustedDevice.js rename to server/models/TrustedDevice.js diff --git a/models/TwoFactorAuth.js b/server/models/TwoFactorAuth.js similarity index 100% rename from models/TwoFactorAuth.js rename to server/models/TwoFactorAuth.js diff --git a/models/User.js b/server/models/User.js similarity index 100% rename from models/User.js rename to server/models/User.js diff --git a/models/UserAchievement.js b/server/models/UserAchievement.js similarity index 100% rename from models/UserAchievement.js rename to server/models/UserAchievement.js diff --git a/models/UserBehaviorProfile.js b/server/models/UserBehaviorProfile.js similarity index 100% rename from models/UserBehaviorProfile.js rename to server/models/UserBehaviorProfile.js diff --git a/models/UserGamification.js b/server/models/UserGamification.js similarity index 100% rename from models/UserGamification.js rename to server/models/UserGamification.js diff --git a/models/Vendor.js b/server/models/Vendor.js similarity index 100% rename from models/Vendor.js rename to server/models/Vendor.js diff --git a/models/Warehouse.js b/server/models/Warehouse.js similarity index 100% rename from models/Warehouse.js rename to server/models/Warehouse.js diff --git a/models/Webhook.js b/server/models/Webhook.js similarity index 100% rename from models/Webhook.js rename to server/models/Webhook.js diff --git a/models/Workspace.js b/server/models/Workspace.js similarity index 100% rename from models/Workspace.js rename to server/models/Workspace.js diff --git a/models/WorkspaceInvite.js b/server/models/WorkspaceInvite.js similarity index 100% rename from models/WorkspaceInvite.js rename to server/models/WorkspaceInvite.js diff --git a/netlify/functions/api.js b/server/netlify/functions/api.js similarity index 100% rename from netlify/functions/api.js rename to server/netlify/functions/api.js diff --git a/package-lock.json b/server/package-lock.json similarity index 100% rename from package-lock.json rename to server/package-lock.json diff --git a/package.json b/server/package.json similarity index 100% rename from package.json rename to server/package.json diff --git a/repositories/baseRepository.js b/server/repositories/baseRepository.js similarity index 100% rename from repositories/baseRepository.js rename to server/repositories/baseRepository.js diff --git a/repositories/budgetRepository.js b/server/repositories/budgetRepository.js similarity index 100% rename from repositories/budgetRepository.js rename to server/repositories/budgetRepository.js diff --git a/repositories/expenseRepository.js b/server/repositories/expenseRepository.js similarity index 100% rename from repositories/expenseRepository.js rename to server/repositories/expenseRepository.js diff --git a/repositories/goalRepository.js b/server/repositories/goalRepository.js similarity index 100% rename from repositories/goalRepository.js rename to server/repositories/goalRepository.js diff --git a/repositories/userRepository.js b/server/repositories/userRepository.js similarity index 100% rename from repositories/userRepository.js rename to server/repositories/userRepository.js diff --git a/routes/accounting.js b/server/routes/accounting.js similarity index 100% rename from routes/accounting.js rename to server/routes/accounting.js diff --git a/routes/accounts.js b/server/routes/accounts.js similarity index 100% rename from routes/accounts.js rename to server/routes/accounts.js diff --git a/routes/ai.js b/server/routes/ai.js similarity index 100% rename from routes/ai.js rename to server/routes/ai.js diff --git a/routes/analytics.js b/server/routes/analytics.js similarity index 100% rename from routes/analytics.js rename to server/routes/analytics.js diff --git a/routes/approvals.js b/server/routes/approvals.js similarity index 100% rename from routes/approvals.js rename to server/routes/approvals.js diff --git a/routes/audit.js b/server/routes/audit.js similarity index 100% rename from routes/audit.js rename to server/routes/audit.js diff --git a/routes/auditCompliance.js b/server/routes/auditCompliance.js similarity index 100% rename from routes/auditCompliance.js rename to server/routes/auditCompliance.js diff --git a/routes/auth.js b/server/routes/auth.js similarity index 100% rename from routes/auth.js rename to server/routes/auth.js diff --git a/routes/backups.js b/server/routes/backups.js similarity index 100% rename from routes/backups.js rename to server/routes/backups.js diff --git a/routes/bills.js b/server/routes/bills.js similarity index 100% rename from routes/bills.js rename to server/routes/bills.js diff --git a/routes/budgets.js b/server/routes/budgets.js similarity index 100% rename from routes/budgets.js rename to server/routes/budgets.js diff --git a/routes/calendar.js b/server/routes/calendar.js similarity index 100% rename from routes/calendar.js rename to server/routes/calendar.js diff --git a/routes/categorization.js b/server/routes/categorization.js similarity index 100% rename from routes/categorization.js rename to server/routes/categorization.js diff --git a/routes/chat.js b/server/routes/chat.js similarity index 100% rename from routes/chat.js rename to server/routes/chat.js diff --git a/routes/clients.js b/server/routes/clients.js similarity index 100% rename from routes/clients.js rename to server/routes/clients.js diff --git a/routes/collaboration.js b/server/routes/collaboration.js similarity index 100% rename from routes/collaboration.js rename to server/routes/collaboration.js diff --git a/routes/compliance.js b/server/routes/compliance.js similarity index 100% rename from routes/compliance.js rename to server/routes/compliance.js diff --git a/routes/contact.js b/server/routes/contact.js similarity index 100% rename from routes/contact.js rename to server/routes/contact.js diff --git a/routes/currency.js b/server/routes/currency.js similarity index 100% rename from routes/currency.js rename to server/routes/currency.js diff --git a/routes/debt.js b/server/routes/debt.js similarity index 100% rename from routes/debt.js rename to server/routes/debt.js diff --git a/routes/expenses.js b/server/routes/expenses.js similarity index 100% rename from routes/expenses.js rename to server/routes/expenses.js diff --git a/routes/export.js b/server/routes/export.js similarity index 100% rename from routes/export.js rename to server/routes/export.js diff --git a/routes/folders.js b/server/routes/folders.js similarity index 100% rename from routes/folders.js rename to server/routes/folders.js diff --git a/routes/forecast.js b/server/routes/forecast.js similarity index 100% rename from routes/forecast.js rename to server/routes/forecast.js diff --git a/routes/forecasting.js b/server/routes/forecasting.js similarity index 100% rename from routes/forecasting.js rename to server/routes/forecasting.js diff --git a/routes/fraudDetection.js b/server/routes/fraudDetection.js similarity index 100% rename from routes/fraudDetection.js rename to server/routes/fraudDetection.js diff --git a/routes/gamification.js b/server/routes/gamification.js similarity index 100% rename from routes/gamification.js rename to server/routes/gamification.js diff --git a/routes/goals.js b/server/routes/goals.js similarity index 100% rename from routes/goals.js rename to server/routes/goals.js diff --git a/routes/groups.js b/server/routes/groups.js similarity index 100% rename from routes/groups.js rename to server/routes/groups.js diff --git a/routes/insights.js b/server/routes/insights.js similarity index 100% rename from routes/insights.js rename to server/routes/insights.js diff --git a/routes/integrations.js b/server/routes/integrations.js similarity index 100% rename from routes/integrations.js rename to server/routes/integrations.js diff --git a/routes/inventory.js b/server/routes/inventory.js similarity index 100% rename from routes/inventory.js rename to server/routes/inventory.js diff --git a/routes/investments.js b/server/routes/investments.js similarity index 100% rename from routes/investments.js rename to server/routes/investments.js diff --git a/routes/invoices.js b/server/routes/invoices.js similarity index 100% rename from routes/invoices.js rename to server/routes/invoices.js diff --git a/routes/multicurrency.js b/server/routes/multicurrency.js similarity index 100% rename from routes/multicurrency.js rename to server/routes/multicurrency.js diff --git a/routes/notifications.js b/server/routes/notifications.js similarity index 100% rename from routes/notifications.js rename to server/routes/notifications.js diff --git a/routes/openBanking.js b/server/routes/openBanking.js similarity index 100% rename from routes/openBanking.js rename to server/routes/openBanking.js diff --git a/routes/payments.js b/server/routes/payments.js similarity index 100% rename from routes/payments.js rename to server/routes/payments.js diff --git a/routes/payroll.js b/server/routes/payroll.js similarity index 100% rename from routes/payroll.js rename to server/routes/payroll.js diff --git a/routes/portfolios.js b/server/routes/portfolios.js similarity index 100% rename from routes/portfolios.js rename to server/routes/portfolios.js diff --git a/routes/procurement.js b/server/routes/procurement.js similarity index 100% rename from routes/procurement.js rename to server/routes/procurement.js diff --git a/routes/project-billing.js b/server/routes/project-billing.js similarity index 100% rename from routes/project-billing.js rename to server/routes/project-billing.js diff --git a/routes/receipts.js b/server/routes/receipts.js similarity index 100% rename from routes/receipts.js rename to server/routes/receipts.js diff --git a/routes/recurring.js b/server/routes/recurring.js similarity index 100% rename from routes/recurring.js rename to server/routes/recurring.js diff --git a/routes/reminders.js b/server/routes/reminders.js similarity index 100% rename from routes/reminders.js rename to server/routes/reminders.js diff --git a/routes/reports.js b/server/routes/reports.js similarity index 100% rename from routes/reports.js rename to server/routes/reports.js diff --git a/routes/rules.js b/server/routes/rules.js similarity index 100% rename from routes/rules.js rename to server/routes/rules.js diff --git a/routes/security.js b/server/routes/security.js similarity index 100% rename from routes/security.js rename to server/routes/security.js diff --git a/routes/sharedSpaces.js b/server/routes/sharedSpaces.js similarity index 100% rename from routes/sharedSpaces.js rename to server/routes/sharedSpaces.js diff --git a/routes/splits.js b/server/routes/splits.js similarity index 100% rename from routes/splits.js rename to server/routes/splits.js diff --git a/routes/subscriptions.js b/server/routes/subscriptions.js similarity index 100% rename from routes/subscriptions.js rename to server/routes/subscriptions.js diff --git a/routes/sync.js b/server/routes/sync.js similarity index 100% rename from routes/sync.js rename to server/routes/sync.js diff --git a/routes/tags.js b/server/routes/tags.js similarity index 100% rename from routes/tags.js rename to server/routes/tags.js diff --git a/routes/tax.js b/server/routes/tax.js similarity index 100% rename from routes/tax.js rename to server/routes/tax.js diff --git a/routes/teams.js b/server/routes/teams.js similarity index 100% rename from routes/teams.js rename to server/routes/teams.js diff --git a/routes/transactions.js b/server/routes/transactions.js similarity index 100% rename from routes/transactions.js rename to server/routes/transactions.js diff --git a/routes/treasury.js b/server/routes/treasury.js similarity index 100% rename from routes/treasury.js rename to server/routes/treasury.js diff --git a/routes/twoFactorAuth.js b/server/routes/twoFactorAuth.js similarity index 100% rename from routes/twoFactorAuth.js rename to server/routes/twoFactorAuth.js diff --git a/routes/user.js b/server/routes/user.js similarity index 100% rename from routes/user.js rename to server/routes/user.js diff --git a/routes/workspaces.js b/server/routes/workspaces.js similarity index 100% rename from routes/workspaces.js rename to server/routes/workspaces.js diff --git a/server.js b/server/server.js similarity index 97% rename from server.js rename to server/server.js index 0db54b9e..36e21444 100644 --- a/server.js +++ b/server/server.js @@ -160,8 +160,9 @@ app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Static files -app.use(express.static('public')); -app.use(express.static('.')); +// Static files served by Nginx in production/docker +// app.use(express.static('public')); +// app.use(express.static('.')); // Security logging middleware app.use((req, res, next) => { @@ -303,9 +304,10 @@ app.use(notFoundHandler); app.use(errorHandler); // Root route to serve the UI -app.get('/', (req, res) => { - res.sendFile(require('path').join(__dirname, 'public', 'index.html')); -}); +// Root route served by Nginx +// app.get('/', (req, res) => { +// res.sendFile(require('path').join(__dirname, 'public', 'index.html')); +// }); server.listen(PORT, () => { console.log(`Server running on port ${PORT}`); diff --git a/services/accountTakeoverAlertingService.js b/server/services/accountTakeoverAlertingService.js similarity index 100% rename from services/accountTakeoverAlertingService.js rename to server/services/accountTakeoverAlertingService.js diff --git a/services/accountingService.js b/server/services/accountingService.js similarity index 100% rename from services/accountingService.js rename to server/services/accountingService.js diff --git a/services/advancedAnalyticsService.js b/server/services/advancedAnalyticsService.js similarity index 100% rename from services/advancedAnalyticsService.js rename to server/services/advancedAnalyticsService.js diff --git a/services/aiInsightsService.js b/server/services/aiInsightsService.js similarity index 100% rename from services/aiInsightsService.js rename to server/services/aiInsightsService.js diff --git a/services/aiService.js b/server/services/aiService.js similarity index 100% rename from services/aiService.js rename to server/services/aiService.js diff --git a/services/alertService.js b/server/services/alertService.js similarity index 100% rename from services/alertService.js rename to server/services/alertService.js diff --git a/services/analysisEngine.js b/server/services/analysisEngine.js similarity index 100% rename from services/analysisEngine.js rename to server/services/analysisEngine.js diff --git a/services/analyticsService.js b/server/services/analyticsService.js similarity index 100% rename from services/analyticsService.js rename to server/services/analyticsService.js diff --git a/services/anomalyDetectionService.js b/server/services/anomalyDetectionService.js similarity index 100% rename from services/anomalyDetectionService.js rename to server/services/anomalyDetectionService.js diff --git a/services/approvalService.js b/server/services/approvalService.js similarity index 100% rename from services/approvalService.js rename to server/services/approvalService.js diff --git a/services/assetService.js b/server/services/assetService.js similarity index 100% rename from services/assetService.js rename to server/services/assetService.js diff --git a/services/auditComplianceService.js b/server/services/auditComplianceService.js similarity index 100% rename from services/auditComplianceService.js rename to server/services/auditComplianceService.js diff --git a/services/auditService.js b/server/services/auditService.js similarity index 100% rename from services/auditService.js rename to server/services/auditService.js diff --git a/services/backupRecoveryService.js b/server/services/backupRecoveryService.js similarity index 100% rename from services/backupRecoveryService.js rename to server/services/backupRecoveryService.js diff --git a/services/backupService.js b/server/services/backupService.js similarity index 100% rename from services/backupService.js rename to server/services/backupService.js diff --git a/services/billReminderService.js b/server/services/billReminderService.js similarity index 100% rename from services/billReminderService.js rename to server/services/billReminderService.js diff --git a/services/billService.js b/server/services/billService.js similarity index 100% rename from services/billService.js rename to server/services/billService.js diff --git a/services/budgetAlertService.js b/server/services/budgetAlertService.js similarity index 100% rename from services/budgetAlertService.js rename to server/services/budgetAlertService.js diff --git a/services/budgetForecastingService.js b/server/services/budgetForecastingService.js similarity index 100% rename from services/budgetForecastingService.js rename to server/services/budgetForecastingService.js diff --git a/services/budgetIntelligenceService.js b/server/services/budgetIntelligenceService.js similarity index 100% rename from services/budgetIntelligenceService.js rename to server/services/budgetIntelligenceService.js diff --git a/services/budgetService.js b/server/services/budgetService.js similarity index 100% rename from services/budgetService.js rename to server/services/budgetService.js diff --git a/services/calendarService.js b/server/services/calendarService.js similarity index 100% rename from services/calendarService.js rename to server/services/calendarService.js diff --git a/services/cashFlowForecastService.js b/server/services/cashFlowForecastService.js similarity index 100% rename from services/cashFlowForecastService.js rename to server/services/cashFlowForecastService.js diff --git a/services/categorizationEngine.js b/server/services/categorizationEngine.js similarity index 100% rename from services/categorizationEngine.js rename to server/services/categorizationEngine.js diff --git a/services/categorizationService.js b/server/services/categorizationService.js similarity index 100% rename from services/categorizationService.js rename to server/services/categorizationService.js diff --git a/services/categoryService.js b/server/services/categoryService.js similarity index 100% rename from services/categoryService.js rename to server/services/categoryService.js diff --git a/services/chatService.js b/server/services/chatService.js similarity index 100% rename from services/chatService.js rename to server/services/chatService.js diff --git a/services/collaborationService.js b/server/services/collaborationService.js similarity index 100% rename from services/collaborationService.js rename to server/services/collaborationService.js diff --git a/services/complianceEngine.js b/server/services/complianceEngine.js similarity index 100% rename from services/complianceEngine.js rename to server/services/complianceEngine.js diff --git a/services/cronJobs.js b/server/services/cronJobs.js similarity index 100% rename from services/cronJobs.js rename to server/services/cronJobs.js diff --git a/services/currencyService.js b/server/services/currencyService.js similarity index 100% rename from services/currencyService.js rename to server/services/currencyService.js diff --git a/services/debtService.js b/server/services/debtService.js similarity index 100% rename from services/debtService.js rename to server/services/debtService.js diff --git a/services/deductionEngine.js b/server/services/deductionEngine.js similarity index 100% rename from services/deductionEngine.js rename to server/services/deductionEngine.js diff --git a/services/discoveryService.js b/server/services/discoveryService.js similarity index 100% rename from services/discoveryService.js rename to server/services/discoveryService.js diff --git a/services/emailService.js b/server/services/emailService.js similarity index 100% rename from services/emailService.js rename to server/services/emailService.js diff --git a/services/expenseService.js b/server/services/expenseService.js similarity index 100% rename from services/expenseService.js rename to server/services/expenseService.js diff --git a/services/exportService.js b/server/services/exportService.js similarity index 100% rename from services/exportService.js rename to server/services/exportService.js diff --git a/services/fileUploadService.js b/server/services/fileUploadService.js similarity index 100% rename from services/fileUploadService.js rename to server/services/fileUploadService.js diff --git a/services/forecastService.js b/server/services/forecastService.js similarity index 100% rename from services/forecastService.js rename to server/services/forecastService.js diff --git a/services/forecastingEngine.js b/server/services/forecastingEngine.js similarity index 100% rename from services/forecastingEngine.js rename to server/services/forecastingEngine.js diff --git a/services/forecastingService.js b/server/services/forecastingService.js similarity index 100% rename from services/forecastingService.js rename to server/services/forecastingService.js diff --git a/services/forensicAuditService.js b/server/services/forensicAuditService.js similarity index 100% rename from services/forensicAuditService.js rename to server/services/forensicAuditService.js diff --git a/services/forexService.js b/server/services/forexService.js similarity index 100% rename from services/forexService.js rename to server/services/forexService.js diff --git a/services/fraudDetectionService.js b/server/services/fraudDetectionService.js similarity index 100% rename from services/fraudDetectionService.js rename to server/services/fraudDetectionService.js diff --git a/services/gamificationService.js b/server/services/gamificationService.js similarity index 100% rename from services/gamificationService.js rename to server/services/gamificationService.js diff --git a/services/goalService.js b/server/services/goalService.js similarity index 100% rename from services/goalService.js rename to server/services/goalService.js diff --git a/services/groupService.js b/server/services/groupService.js similarity index 100% rename from services/groupService.js rename to server/services/groupService.js diff --git a/services/insightService.js b/server/services/insightService.js similarity index 100% rename from services/insightService.js rename to server/services/insightService.js diff --git a/services/integrationService.js b/server/services/integrationService.js similarity index 100% rename from services/integrationService.js rename to server/services/integrationService.js diff --git a/services/intelligenceService.js b/server/services/intelligenceService.js similarity index 100% rename from services/intelligenceService.js rename to server/services/intelligenceService.js diff --git a/services/internationalizationService.js b/server/services/internationalizationService.js similarity index 100% rename from services/internationalizationService.js rename to server/services/internationalizationService.js diff --git a/services/inventoryService.js b/server/services/inventoryService.js similarity index 100% rename from services/inventoryService.js rename to server/services/inventoryService.js diff --git a/services/investmentService.js b/server/services/investmentService.js similarity index 100% rename from services/investmentService.js rename to server/services/investmentService.js diff --git a/services/invitationService.js b/server/services/invitationService.js similarity index 100% rename from services/invitationService.js rename to server/services/invitationService.js diff --git a/services/inviteService.js b/server/services/inviteService.js similarity index 100% rename from services/inviteService.js rename to server/services/inviteService.js diff --git a/services/invoiceService.js b/server/services/invoiceService.js similarity index 100% rename from services/invoiceService.js rename to server/services/invoiceService.js diff --git a/services/invoiceSyncService.js b/server/services/invoiceSyncService.js similarity index 100% rename from services/invoiceSyncService.js rename to server/services/invoiceSyncService.js diff --git a/services/merchantLearningService.js b/server/services/merchantLearningService.js similarity index 100% rename from services/merchantLearningService.js rename to server/services/merchantLearningService.js diff --git a/services/notificationService.js b/server/services/notificationService.js similarity index 100% rename from services/notificationService.js rename to server/services/notificationService.js diff --git a/services/ocrService.js b/server/services/ocrService.js similarity index 100% rename from services/ocrService.js rename to server/services/ocrService.js diff --git a/services/openBankingService.js b/server/services/openBankingService.js similarity index 100% rename from services/openBankingService.js rename to server/services/openBankingService.js diff --git a/services/parsingService.js b/server/services/parsingService.js similarity index 100% rename from services/parsingService.js rename to server/services/parsingService.js diff --git a/services/paymentService.js b/server/services/paymentService.js similarity index 100% rename from services/paymentService.js rename to server/services/paymentService.js diff --git a/services/payrollService.js b/server/services/payrollService.js similarity index 100% rename from services/payrollService.js rename to server/services/payrollService.js diff --git a/services/pdfService.js b/server/services/pdfService.js similarity index 100% rename from services/pdfService.js rename to server/services/pdfService.js diff --git a/services/portfolioService.js b/server/services/portfolioService.js similarity index 100% rename from services/portfolioService.js rename to server/services/portfolioService.js diff --git a/services/priceUpdateService.js b/server/services/priceUpdateService.js similarity index 100% rename from services/priceUpdateService.js rename to server/services/priceUpdateService.js diff --git a/services/procurementService.js b/server/services/procurementService.js similarity index 100% rename from services/procurementService.js rename to server/services/procurementService.js diff --git a/services/projectRevenueService.js b/server/services/projectRevenueService.js similarity index 100% rename from services/projectRevenueService.js rename to server/services/projectRevenueService.js diff --git a/services/recurringService.js b/server/services/recurringService.js similarity index 100% rename from services/recurringService.js rename to server/services/recurringService.js diff --git a/services/reminderService.js b/server/services/reminderService.js similarity index 100% rename from services/reminderService.js rename to server/services/reminderService.js diff --git a/services/replenishmentService.js b/server/services/replenishmentService.js similarity index 100% rename from services/replenishmentService.js rename to server/services/replenishmentService.js diff --git a/services/reportService.js b/server/services/reportService.js similarity index 100% rename from services/reportService.js rename to server/services/reportService.js diff --git a/services/revaluationService.js b/server/services/revaluationService.js similarity index 100% rename from services/revaluationService.js rename to server/services/revaluationService.js diff --git a/services/ruleEngine.js b/server/services/ruleEngine.js similarity index 100% rename from services/ruleEngine.js rename to server/services/ruleEngine.js diff --git a/services/runwayForecaster.js b/server/services/runwayForecaster.js similarity index 100% rename from services/runwayForecaster.js rename to server/services/runwayForecaster.js diff --git a/services/scoreService.js b/server/services/scoreService.js similarity index 100% rename from services/scoreService.js rename to server/services/scoreService.js diff --git a/services/securityMonitor.js b/server/services/securityMonitor.js similarity index 100% rename from services/securityMonitor.js rename to server/services/securityMonitor.js diff --git a/services/securityService.js b/server/services/securityService.js similarity index 100% rename from services/securityService.js rename to server/services/securityService.js diff --git a/services/settlementService.js b/server/services/settlementService.js similarity index 100% rename from services/settlementService.js rename to server/services/settlementService.js diff --git a/services/sharedSpaceService.js b/server/services/sharedSpaceService.js similarity index 100% rename from services/sharedSpaceService.js rename to server/services/sharedSpaceService.js diff --git a/services/splitService.js b/server/services/splitService.js similarity index 100% rename from services/splitService.js rename to server/services/splitService.js diff --git a/services/subscriptionDetector.js b/server/services/subscriptionDetector.js similarity index 100% rename from services/subscriptionDetector.js rename to server/services/subscriptionDetector.js diff --git a/services/subscriptionService.js b/server/services/subscriptionService.js similarity index 100% rename from services/subscriptionService.js rename to server/services/subscriptionService.js diff --git a/services/suspiciousLoginDetectionService.js b/server/services/suspiciousLoginDetectionService.js similarity index 100% rename from services/suspiciousLoginDetectionService.js rename to server/services/suspiciousLoginDetectionService.js diff --git a/services/tagManagementService.js b/server/services/tagManagementService.js similarity index 100% rename from services/tagManagementService.js rename to server/services/tagManagementService.js diff --git a/services/taxOptimizationService.js b/server/services/taxOptimizationService.js similarity index 100% rename from services/taxOptimizationService.js rename to server/services/taxOptimizationService.js diff --git a/services/taxService.js b/server/services/taxService.js similarity index 100% rename from services/taxService.js rename to server/services/taxService.js diff --git a/services/teamManagementService.js b/server/services/teamManagementService.js similarity index 100% rename from services/teamManagementService.js rename to server/services/teamManagementService.js diff --git a/services/transactionImportService.js b/server/services/transactionImportService.js similarity index 100% rename from services/transactionImportService.js rename to server/services/transactionImportService.js diff --git a/services/transactionService.js b/server/services/transactionService.js similarity index 100% rename from services/transactionService.js rename to server/services/transactionService.js diff --git a/services/treasuryService.js b/server/services/treasuryService.js similarity index 100% rename from services/treasuryService.js rename to server/services/treasuryService.js diff --git a/services/twoFactorAuthService.js b/server/services/twoFactorAuthService.js similarity index 100% rename from services/twoFactorAuthService.js rename to server/services/twoFactorAuthService.js diff --git a/services/webhookService.js b/server/services/webhookService.js similarity index 100% rename from services/webhookService.js rename to server/services/webhookService.js diff --git a/services/wellnessService.js b/server/services/wellnessService.js similarity index 100% rename from services/wellnessService.js rename to server/services/wellnessService.js diff --git a/services/workspaceService.js b/server/services/workspaceService.js similarity index 100% rename from services/workspaceService.js rename to server/services/workspaceService.js diff --git a/socket/collabHandler.js b/server/socket/collabHandler.js similarity index 100% rename from socket/collabHandler.js rename to server/socket/collabHandler.js diff --git a/tests/backupService.test.js b/server/tests/backupService.test.js similarity index 100% rename from tests/backupService.test.js rename to server/tests/backupService.test.js diff --git a/tests/output/test-report.csv b/server/tests/output/test-report.csv similarity index 100% rename from tests/output/test-report.csv rename to server/tests/output/test-report.csv diff --git a/tests/output/test-report.pdf b/server/tests/output/test-report.pdf similarity index 100% rename from tests/output/test-report.pdf rename to server/tests/output/test-report.pdf diff --git a/tests/output/test-report.xlsx b/server/tests/output/test-report.xlsx similarity index 100% rename from tests/output/test-report.xlsx rename to server/tests/output/test-report.xlsx diff --git a/utils/AppError.js b/server/utils/AppError.js similarity index 100% rename from utils/AppError.js rename to server/utils/AppError.js diff --git a/utils/ResponseFactory.js b/server/utils/ResponseFactory.js similarity index 100% rename from utils/ResponseFactory.js rename to server/utils/ResponseFactory.js diff --git a/utils/financialModels.js b/server/utils/financialModels.js similarity index 100% rename from utils/financialModels.js rename to server/utils/financialModels.js diff --git a/utils/patternMatcher.js b/server/utils/patternMatcher.js similarity index 100% rename from utils/patternMatcher.js rename to server/utils/patternMatcher.js diff --git a/utils/stockMath.js b/server/utils/stockMath.js similarity index 100% rename from utils/stockMath.js rename to server/utils/stockMath.js diff --git a/utils/taxCalculators.js b/server/utils/taxCalculators.js similarity index 100% rename from utils/taxCalculators.js rename to server/utils/taxCalculators.js diff --git a/verify-backup-system.js b/server/verify-backup-system.js similarity index 100% rename from verify-backup-system.js rename to server/verify-backup-system.js diff --git a/workspace-feature.js b/workspace-feature.js deleted file mode 100644 index d9da36c1..00000000 --- a/workspace-feature.js +++ /dev/null @@ -1,908 +0,0 @@ -/** - * Enterprise-Grade RBAC Workspace Management - * Issue #420: Role-Based Access Control & Workspace Invites - * - * Roles: - * - owner: Full control (transfer ownership, delete workspace) - * - manager: Can manage members and settings - * - editor: Can add/edit expenses - * - viewer: Read-only access - */ - -var WORKSPACE_API_URL = '/api/workspaces'; - -// State management with persistence -let currentWorkspaces = []; -let activeWorkspace = null; -let pendingInvites = []; - -// Role definitions -const ROLES = { - owner: { name: 'Owner', color: '#ff6b6b', icon: 'fa-crown' }, - manager: { name: 'Manager', color: '#4ecdc4', icon: 'fa-user-shield' }, - editor: { name: 'Editor', color: '#45b7d1', icon: 'fa-edit' }, - viewer: { name: 'Viewer', color: '#96ceb4', icon: 'fa-eye' } -}; - -// Permission definitions for UI -const ROLE_PERMISSIONS = { - owner: ['Full workspace control', 'Transfer ownership', 'Delete workspace', 'Manage all members', 'All editor permissions'], - manager: ['Manage workspace settings', 'Invite & remove members', 'Promote/demote members', 'Approve expenses', 'All editor permissions'], - editor: ['Create expenses', 'Edit expenses', 'Delete expenses', 'View budgets', 'Export reports'], - viewer: ['View expenses', 'View budgets', 'View reports'] -}; - -// Enhanced API Functions with better error handling -async function getAuthHeaders() { - const token = localStorage.getItem('authToken') || localStorage.getItem('token'); - return { - 'Content-Type': 'application/json', - 'Authorization': token ? `Bearer ${token}` : '' - }; -} - -/** - * Enhanced workspace fetching with caching - */ -async function fetchWorkspaces() { - try { - const token = localStorage.getItem('authToken') || localStorage.getItem('token'); - if (!token) return []; - - const response = await fetch(WORKSPACE_API_URL, { - headers: await getAuthHeaders() - }); - if (!response.ok) throw new Error('Failed to fetch workspaces'); - const data = await response.json(); - currentWorkspaces = data.data || []; - renderWorkspaceSelection(); - return currentWorkspaces; - } catch (error) { - console.error('Error fetching workspaces:', error); - showWorkspaceNotification('Failed to load workspaces', 'error'); - return []; - } -} - -/** - * Enhanced create workspace with validation - */ -async function createWorkspace(name, description) { - try { - const response = await fetch(WORKSPACE_API_URL, { - method: 'POST', - headers: await getAuthHeaders(), - body: JSON.stringify({ name, description }) - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Workspace created successfully!', 'success'); - await fetchWorkspaces(); - return data.data; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return null; - } -} - -/** - * Update workspace settings - */ -async function updateWorkspace(workspaceId, updates) { - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}`, { - method: 'PUT', - headers: await getAuthHeaders(), - body: JSON.stringify(updates) - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Workspace updated!', 'success'); - await fetchWorkspaces(); - if (activeWorkspace?._id === workspaceId) { - await loadWorkspaceMembers(); - } - return data.data; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return null; - } -} - -/** - * Delete workspace - */ -async function deleteWorkspace(workspaceId) { - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}`, { - method: 'DELETE', - headers: await getAuthHeaders() - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Workspace deleted', 'success'); - if (activeWorkspace?._id === workspaceId) { - selectWorkspace(null); - } - await fetchWorkspaces(); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Invite user to workspace - */ -async function inviteToWorkspace(workspaceId, email, role, message = '') { - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}/invite`, { - method: 'POST', - headers: await getAuthHeaders(), - body: JSON.stringify({ email, role, message }) - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Invitation sent successfully!', 'success'); - await loadPendingInvites(workspaceId); - return data.data; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return null; - } -} - -/** - * Resend invite - */ -async function resendInvite(workspaceId, inviteId) { - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}/invites/${inviteId}/resend`, { - method: 'POST', - headers: await getAuthHeaders() - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Invitation resent!', 'success'); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Revoke invite - */ -async function revokeInvite(workspaceId, inviteId) { - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}/invites/${inviteId}`, { - method: 'DELETE', - headers: await getAuthHeaders() - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Invitation revoked', 'success'); - await loadPendingInvites(workspaceId); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Get pending invites for workspace - */ -async function loadPendingInvites(workspaceId) { - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}/invites`, { - headers: await getAuthHeaders() - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - pendingInvites = data.data || []; - renderPendingInvites(); - return pendingInvites; - } catch (error) { - console.error('Error loading invites:', error); - return []; - } -} - -/** - * Join workspace using token - */ -async function joinWorkspace(token) { - try { - const response = await fetch(`${WORKSPACE_API_URL}/join`, { - method: 'POST', - headers: await getAuthHeaders(), - body: JSON.stringify({ token }) - }); - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification(data.message || 'Joined workspace successfully!', 'success'); - await fetchWorkspaces(); - - // Select the new workspace - if (data.data?.workspace?._id) { - selectWorkspace(data.data.workspace._id); - } - - return data.data; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return null; - } -} - -/** - * Update member role - */ -async function changeMemberRole(userId, newRole) { - if (!activeWorkspace || !newRole) return; - - try { - const response = await fetch(`${WORKSPACE_API_URL}/${activeWorkspace._id}/members/${userId}`, { - method: 'PUT', - headers: await getAuthHeaders(), - body: JSON.stringify({ role: newRole }) - }); - - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Member role updated!', 'success'); - await loadWorkspaceMembers(); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Remove member from workspace - */ -async function removeMember(userId) { - if (!activeWorkspace) return; - - if (!confirm('Are you sure you want to remove this member?')) { - return false; - } - - try { - const response = await fetch(`${WORKSPACE_API_URL}/${activeWorkspace._id}/members/${userId}`, { - method: 'DELETE', - headers: await getAuthHeaders() - }); - - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Member removed', 'success'); - await loadWorkspaceMembers(); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Leave workspace - */ -async function leaveWorkspace(workspaceId) { - if (!confirm('Are you sure you want to leave this workspace?')) { - return false; - } - - try { - const response = await fetch(`${WORKSPACE_API_URL}/${workspaceId}/leave`, { - method: 'POST', - headers: await getAuthHeaders() - }); - - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('You have left the workspace', 'success'); - selectWorkspace(null); - await fetchWorkspaces(); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Transfer ownership - */ -async function transferOwnership(newOwnerId) { - if (!activeWorkspace) return; - - const newOwner = activeWorkspace.members.find(m => m.user._id === newOwnerId); - if (!confirm(`Transfer ownership to ${newOwner?.user?.name || 'this member'}? You will become a manager.`)) { - return false; - } - - try { - const response = await fetch(`${WORKSPACE_API_URL}/${activeWorkspace._id}/transfer`, { - method: 'POST', - headers: await getAuthHeaders(), - body: JSON.stringify({ newOwnerId }) - }); - - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Ownership transferred!', 'success'); - await loadWorkspaceMembers(); - return true; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return false; - } -} - -/** - * Generate shareable invite link - */ -async function generateInviteLink(role = 'viewer', expiryDays = 30) { - if (!activeWorkspace) return; - - try { - const response = await fetch(`${WORKSPACE_API_URL}/${activeWorkspace._id}/invite-link`, { - method: 'POST', - headers: await getAuthHeaders(), - body: JSON.stringify({ role, expiryDays }) - }); - - const data = await response.json(); - if (!response.ok) throw new Error(data.error); - - showWorkspaceNotification('Invite link generated!', 'success'); - return data.data; - } catch (error) { - showWorkspaceNotification(error.message, 'error'); - return null; - } -} - -// ======================== -// UI Rendering Functions -// ======================== - -/** - * Workspace Selection UI - */ -function renderWorkspaceSelection() { - const container = document.getElementById('workspace-selector'); - if (!container) return; - - const userRole = activeWorkspace?.userRole || null; - - container.innerHTML = ` -
-
- ${activeWorkspace ? activeWorkspace.name.charAt(0).toUpperCase() : ''} -
-
- Current Workspace - ${activeWorkspace ? activeWorkspace.name : 'Personal Account'} - ${userRole ? `${ROLES[userRole]?.name || userRole}` : ''} -
- -
-
-
- - Personal Account -
-
Shared Workspaces
- ${currentWorkspaces.length === 0 ? - '
No shared workspaces yet
' : - currentWorkspaces.map(ws => ` -
-
${ws.name.charAt(0).toUpperCase()}
-
- ${ws.name} - ${ROLES[ws.userRole]?.name || ws.userRole} -
- ${ws.isOwner ? '' : ''} -
- `).join('') - } - -
- `; -} - -/** - * Render members list with role management - */ -function renderMembersList(members) { - const membersList = document.getElementById('members-list'); - if (!membersList) return; - - const currentUserId = localStorage.getItem('userId'); - const userRole = activeWorkspace?.userRole || 'viewer'; - const canManageMembers = ['owner', 'manager'].includes(userRole); - const isOwner = userRole === 'owner'; - - membersList.innerHTML = members.map(member => { - const isCurrentUser = member.user._id === currentUserId; - const memberRole = member.role; - const roleInfo = ROLES[memberRole] || { name: memberRole, color: '#888', icon: 'fa-user' }; - - // Can edit if: manager+ AND target is below your role AND not yourself - const canEdit = canManageMembers && !isCurrentUser && - getRoleLevel(userRole) > getRoleLevel(memberRole); - const canRemove = canEdit; - const canTransfer = isOwner && !isCurrentUser; - - return ` -
-
-
- ${member.user.avatar ? - `${member.user.name}` : - (member.user.name || 'U').charAt(0).toUpperCase() - } -
-
-
- ${member.user.name || 'Unknown User'} - ${isCurrentUser ? '(You)' : ''} -
- ${member.user.email} -
-
-
- - - ${roleInfo.name} - -
-
- ${canEdit ? ` -
- -
- ${['manager', 'editor', 'viewer'].map(role => ` -
- - ${ROLES[role].name} - ${role === memberRole ? '' : ''} -
- `).join('')} -
-
- ` : ''} - ${canTransfer ? ` - - ` : ''} - ${canRemove ? ` - - ` : ''} -
-
- `; - }).join(''); -} - -/** - * Render pending invites - */ -function renderPendingInvites() { - const invitesList = document.getElementById('pending-invites-list'); - if (!invitesList) return; - - if (pendingInvites.length === 0) { - invitesList.innerHTML = '
No pending invitations
'; - return; - } - - invitesList.innerHTML = pendingInvites.map(invite => ` -
-
- ${invite.email} - ${ROLES[invite.role]?.name || invite.role} - Expires ${invite.expiresIn || 'soon'} -
-
- - -
-
- `).join(''); -} - -/** - * Select active workspace - */ -function selectWorkspace(id) { - if (!id) { - activeWorkspace = null; - } else { - activeWorkspace = currentWorkspaces.find(ws => ws._id === id); - } - - // Close dropdown - document.getElementById('workspace-dropdown')?.classList.remove('active'); - - // Update UI - renderWorkspaceSelection(); - updateWorkspaceDashboard(); - - // Save preference - localStorage.setItem('activeWorkspaceId', id || 'personal'); - - // Dispatch event for other components - window.dispatchEvent(new CustomEvent('workspaceChanged', { - detail: { workspace: activeWorkspace } - })); -} - -function toggleWorkspaceDropdown() { - document.getElementById('workspace-dropdown')?.classList.toggle('active'); -} - -function toggleRoleDropdown(userId) { - const dropdown = document.getElementById(`role-dropdown-${userId}`); - - // Close all other dropdowns - document.querySelectorAll('.role-dropdown-menu.active').forEach(d => { - if (d !== dropdown) d.classList.remove('active'); - }); - - dropdown?.classList.toggle('active'); -} - -/** - * Update dashboard context based on workspace - */ -function updateWorkspaceDashboard() { - if (typeof updateAllData === 'function') { - updateAllData(activeWorkspace ? activeWorkspace._id : null); - } - - // Show/hide workspace settings - const workspaceSettings = document.getElementById('workspace-settings'); - if (workspaceSettings) { - workspaceSettings.style.display = activeWorkspace ? 'block' : 'none'; - } - - if (activeWorkspace) { - loadWorkspaceMembers(); - updateWorkspaceInfo(); - } -} - -/** - * Load workspace members - */ -async function loadWorkspaceMembers() { - if (!activeWorkspace) return; - - try { - const response = await fetch(`${WORKSPACE_API_URL}/${activeWorkspace._id}`, { - headers: await getAuthHeaders() - }); - - if (!response.ok) throw new Error('Failed to load workspace'); - - const data = await response.json(); - activeWorkspace = { ...activeWorkspace, ...data.data }; - - renderMembersList(activeWorkspace.members); - updateWorkspaceInfo(); - updateInviteButtonVisibility(); - - // Load pending invites if user can manage invites - if (['owner', 'manager'].includes(activeWorkspace.userRole)) { - await loadPendingInvites(activeWorkspace._id); - } - } catch (error) { - console.error('Error loading workspace:', error); - showWorkspaceNotification('Failed to load workspace members', 'error'); - } -} - -/** - * Update workspace info display - */ -function updateWorkspaceInfo() { - if (!activeWorkspace) return; - - const nameEl = document.getElementById('current-workspace-name'); - const descEl = document.getElementById('current-workspace-desc'); - const memberCountEl = document.getElementById('member-count'); - const userRoleEl = document.getElementById('your-role'); - - if (nameEl) nameEl.textContent = activeWorkspace.name; - if (descEl) descEl.textContent = activeWorkspace.description || 'No description'; - if (memberCountEl) memberCountEl.textContent = `${activeWorkspace.members?.length || 0} members`; - - if (userRoleEl) { - const roleInfo = ROLES[activeWorkspace.userRole] || { name: 'Member' }; - userRoleEl.innerHTML = ` - - ${roleInfo.name} - - `; - } -} - -/** - * Update invite button visibility - */ -function updateInviteButtonVisibility() { - const inviteBtn = document.getElementById('invite-btn'); - const pendingSection = document.getElementById('pending-invites-section'); - - const canInvite = activeWorkspace && ['owner', 'manager'].includes(activeWorkspace.userRole); - - if (inviteBtn) inviteBtn.style.display = canInvite ? 'flex' : 'none'; - if (pendingSection) pendingSection.style.display = canInvite ? 'block' : 'none'; -} - -// ======================== -// Modal Functions -// ======================== - -function openCreateWorkspaceModal() { - const modal = document.getElementById('workspace-modal'); - if (modal) modal.classList.add('active'); -} - -function closeWorkspaceModal() { - const modal = document.getElementById('workspace-modal'); - if (modal) modal.classList.remove('active'); -} - -function openInviteModal() { - const modal = document.getElementById('invite-modal'); - if (modal) { - modal.classList.add('active'); - updateRolePermissions('viewer'); - } -} - -function closeInviteModal() { - const modal = document.getElementById('invite-modal'); - if (modal) modal.classList.remove('active'); -} - -function openInviteLinkModal() { - const modal = document.getElementById('invite-link-modal'); - if (modal) modal.classList.add('active'); -} - -function closeInviteLinkModal() { - const modal = document.getElementById('invite-link-modal'); - if (modal) modal.classList.remove('active'); -} - -/** - * Update role permissions display - */ -function updateRolePermissions(role) { - const display = document.getElementById('role-permissions-display'); - if (!display) return; - - const permissions = ROLE_PERMISSIONS[role] || []; - display.innerHTML = permissions.length > 0 - ? `` - : '

Select a role to see permissions

'; -} - -// ======================== -// Helper Functions -// ======================== - -function getRoleLevel(role) { - const levels = { owner: 4, manager: 3, editor: 2, viewer: 1 }; - return levels[role] || 0; -} - -function getWorkspaceColor(name) { - const colors = ['#667eea', '#764ba2', '#f093fb', '#f5576c', '#4facfe', '#00f2fe', '#43e97b', '#fa709a']; - const index = name.charCodeAt(0) % colors.length; - return colors[index]; -} - -function getAvatarColor(name) { - const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dfe6e9', '#ff7675', '#74b9ff']; - const index = (name || 'U').charCodeAt(0) % colors.length; - return colors[index]; -} - -function showWorkspaceNotification(message, type = 'info') { - if (typeof showNotification === 'function') { - showNotification(message, type); - return; - } - - // Fallback notification - const notification = document.createElement('div'); - notification.className = `workspace-notification ${type}`; - notification.innerHTML = ` - - ${message} - `; - notification.style.cssText = ` - position: fixed; top: 20px; right: 20px; padding: 1rem 1.5rem; - border-radius: 8px; color: white; z-index: 10000; display: flex; - align-items: center; gap: 0.5rem; animation: slideIn 0.3s ease; - background: ${type === 'success' ? '#00c853' : type === 'error' ? '#ff5252' : '#2196f3'}; - `; - document.body.appendChild(notification); - setTimeout(() => notification.remove(), 3000); -} - -// ======================== -// Initialization -// ======================== - -function initWorkspaceFeature() { - const workspaceIdPref = localStorage.getItem('activeWorkspaceId'); - - fetchWorkspaces().then(() => { - if (workspaceIdPref && workspaceIdPref !== 'personal') { - selectWorkspace(workspaceIdPref); - } - }); - - // Handle create workspace form - const createForm = document.getElementById('create-workspace-form'); - if (createForm) { - createForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const name = document.getElementById('workspace-name-input')?.value; - const desc = document.getElementById('workspace-desc-input')?.value; - if (name) { - await createWorkspace(name, desc); - closeWorkspaceModal(); - createForm.reset(); - } - }); - } - - // Handle invite form - const inviteForm = document.getElementById('invite-form'); - if (inviteForm) { - inviteForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const email = document.getElementById('invite-email')?.value; - const role = document.getElementById('invite-role')?.value || 'viewer'; - const message = document.getElementById('invite-message')?.value || ''; - - if (!activeWorkspace) { - showWorkspaceNotification('No workspace selected', 'error'); - return; - } - - if (email) { - await inviteToWorkspace(activeWorkspace._id, email, role, message); - closeInviteModal(); - inviteForm.reset(); - updateRolePermissions('viewer'); - } - }); - } - - // Role selection change - const roleSelect = document.getElementById('invite-role'); - if (roleSelect) { - roleSelect.addEventListener('change', (e) => { - updateRolePermissions(e.target.value); - }); - } - - // Handle invite link form - const inviteLinkForm = document.getElementById('invite-link-form'); - if (inviteLinkForm) { - inviteLinkForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const role = document.getElementById('link-role')?.value || 'viewer'; - const expiry = parseInt(document.getElementById('link-expiry')?.value) || 30; - - const result = await generateInviteLink(role, expiry); - if (result) { - const linkDisplay = document.getElementById('generated-link'); - if (linkDisplay) { - linkDisplay.value = result.link; - linkDisplay.style.display = 'block'; - document.getElementById('copy-link-btn')?.style.display = 'inline-flex'; - } - } - }); - } - - // Copy link button - const copyLinkBtn = document.getElementById('copy-link-btn'); - if (copyLinkBtn) { - copyLinkBtn.addEventListener('click', () => { - const linkInput = document.getElementById('generated-link'); - if (linkInput) { - linkInput.select(); - document.execCommand('copy'); - showWorkspaceNotification('Link copied to clipboard!', 'success'); - } - }); - } - - // Handle invitation join from URL - const urlParams = new URLSearchParams(window.location.search); - const inviteToken = urlParams.get('token'); - if (inviteToken && window.location.pathname.includes('join-workspace')) { - joinWorkspace(inviteToken); - } - - // Close dropdowns when clicking outside - document.addEventListener('click', (e) => { - if (!e.target.closest('.workspace-selector')) { - document.getElementById('workspace-dropdown')?.classList.remove('active'); - } - if (!e.target.closest('.role-dropdown')) { - document.querySelectorAll('.role-dropdown-menu.active').forEach(d => d.classList.remove('active')); - } - }); -} - -// Initialize when DOM is ready -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initWorkspaceFeature); -} else { - initWorkspaceFeature(); -} - -// Export functions for module usage -if (typeof module !== 'undefined' && module.exports) { - module.exports = { - fetchWorkspaces, - createWorkspace, - selectWorkspace, - inviteToWorkspace, - changeMemberRole, - removeMember, - leaveWorkspace, - transferOwnership, - ROLES, - ROLE_PERMISSIONS - }; -} - -// Auto-initialize on DOM ready -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initWorkspaceFeature); -} else { - initWorkspaceFeature(); -} \ No newline at end of file