├── .dockerignore ├── .editorconfig ├── .env.example ├── .env.release ├── .env.testing ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── images │ ├── any-bank.png │ ├── cashflow.png │ ├── global.png │ ├── header.png │ ├── preview.png │ ├── private-sync.png │ ├── self-host.png │ └── tagging.png └── workflows │ ├── action_app-publish-edge.yml │ ├── action_app-publish-release.yml │ ├── action_marketing-site-preview.yml │ ├── action_marketing-site-publish.yml │ ├── scheduled-task_update-sponsors.yml │ ├── service_deploy-static-site.yml │ ├── service_docker-build-and-publish.yml │ ├── service_install-and-build-node.yml │ └── service_install-and-test-php.yml ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── .infrastructure ├── conf │ └── traefik │ │ ├── dev │ │ ├── certificates │ │ │ ├── local-dev-key.pem │ │ │ └── local-dev.pem │ │ ├── traefik-certs.yml │ │ └── traefik.yml │ │ └── prod │ │ └── traefik.yml └── volume_data │ └── .gitignore ├── CONTRIBUTING.md ├── Dockerfile.node ├── Dockerfile.php ├── LICENSE ├── Modules ├── Initialize │ ├── app │ │ ├── Actions │ │ │ ├── CreateAccounts.php │ │ │ ├── CreateTransactions.php │ │ │ └── CreateUser.php │ │ ├── Console │ │ │ ├── InitializeDemo.php │ │ │ └── InitializeFresh.php │ │ ├── Data │ │ │ └── merchants.json │ │ └── Providers │ │ │ ├── .gitkeep │ │ │ ├── EventServiceProvider.php │ │ │ └── InitializeServiceProvider.php │ ├── composer.json │ ├── config │ │ ├── .gitkeep │ │ └── config.php │ ├── database │ │ ├── factories │ │ │ └── .gitkeep │ │ ├── migrations │ │ │ └── .gitkeep │ │ └── seeders │ │ │ ├── .gitkeep │ │ │ └── InitializeDatabaseSeeder.php │ ├── module.json │ └── tests │ │ ├── Feature │ │ └── .gitkeep │ │ └── Unit │ │ └── .gitkeep └── Transaction │ ├── app │ ├── Data │ │ └── TransactionData.php │ ├── Http │ │ ├── Controllers │ │ │ ├── .gitkeep │ │ │ ├── Api │ │ │ │ └── TransactionController.php │ │ │ ├── ImportController.php │ │ │ └── TransactionController.php │ │ └── Requests │ │ │ ├── StoreTransactionRequest.php │ │ │ └── UpdateTransactionRequest.php │ ├── Models │ │ └── Transaction.php │ ├── Providers │ │ ├── .gitkeep │ │ ├── EventServiceProvider.php │ │ ├── RouteServiceProvider.php │ │ └── TransactionServiceProvider.php │ └── Services │ │ ├── ImportTransactions.php │ │ ├── IndexTransactions.php │ │ ├── StoreTransaction.php │ │ └── UpdateTransaction.php │ ├── composer.json │ ├── config │ ├── .gitkeep │ └── config.php │ ├── database │ ├── factories │ │ └── .gitkeep │ ├── migrations │ │ ├── .gitkeep │ │ ├── 2024_02_03_175535_added_transactions.php │ │ └── 2024_06_03_224410_renamed_description_to_notes_transactions.php │ └── seeders │ │ ├── .gitkeep │ │ └── TransactionsDatabaseSeeder.php │ ├── module.json │ ├── routes │ ├── .gitkeep │ ├── api.php │ └── web.php │ └── tests │ ├── Feature │ └── .gitkeep │ └── Unit │ └── .gitkeep ├── README.md ├── SECURITY.md ├── app ├── Console │ ├── Commands │ │ ├── SeedCategories.php │ │ └── SetUpInstitutions.php │ └── Kernel.php ├── Data │ ├── CashAccountData.php │ ├── CreditCardData.php │ ├── LoanData.php │ └── Rules │ │ └── StoreRuleData.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── AccountController.php │ │ ├── Auth │ │ │ ├── AuthenticatedSessionController.php │ │ │ ├── ConfirmablePasswordController.php │ │ │ ├── EmailVerificationNotificationController.php │ │ │ ├── EmailVerificationPromptController.php │ │ │ ├── NewPasswordController.php │ │ │ ├── PasswordController.php │ │ │ ├── PasswordResetLinkController.php │ │ │ ├── RegisteredUserController.php │ │ │ └── VerifyEmailController.php │ │ ├── BudgetController.php │ │ ├── CashAccountController.php │ │ ├── CashFlowController.php │ │ ├── CategoryController.php │ │ ├── Controller.php │ │ ├── CreditCardController.php │ │ ├── DashboardController.php │ │ ├── GoalsController.php │ │ ├── InstitutionController.php │ │ ├── LoanController.php │ │ ├── PortfolioController.php │ │ ├── ProfileController.php │ │ ├── RulesController.php │ │ └── SettingsController.php │ ├── Kernel.php │ ├── Middleware │ │ ├── Authenticate.php │ │ ├── EncryptCookies.php │ │ ├── HandleInertiaRequests.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── TrustProxies.php │ │ ├── ValidateSignature.php │ │ └── VerifyCsrfToken.php │ └── Requests │ │ ├── Accounts │ │ └── StoreAccountRequest.php │ │ ├── Auth │ │ └── LoginRequest.php │ │ ├── Categories │ │ ├── StoreCategoryRequest.php │ │ └── UpdateCategoryRequest.php │ │ ├── Institutions │ │ ├── StoreInstitutionRequest.php │ │ └── UpdateInstitutionRequest.php │ │ ├── Portfolio │ │ └── UpdatePortfolioRequest.php │ │ ├── ProfileUpdateRequest.php │ │ └── Rules │ │ └── StoreRuleRequest.php ├── Listeners │ └── Registered │ │ └── SeedDefaultCategories.php ├── Models │ ├── CashAccount.php │ ├── Category.php │ ├── CreditCard.php │ ├── Group.php │ ├── Institution.php │ ├── Loan.php │ ├── Rule.php │ └── User.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php └── Services │ ├── Accounts │ └── StoreAccount.php │ ├── CashAccounts │ ├── IndexCashAccounts.php │ └── StoreCashAccount.php │ ├── Categories │ ├── DeleteCategory.php │ ├── IndexCategories.php │ ├── SeedUserCategories.php │ ├── StoreCategory.php │ └── UpdateCategory.php │ ├── CreditCards │ ├── IndexCreditCards.php │ ├── StoreCreditCard.php │ └── UpdateCreditCard.php │ ├── Groups │ └── IndexGroups.php │ ├── Institutions │ ├── DeleteInstitution.php │ ├── IndexInstitutions.php │ ├── SeedInstitutions.php │ ├── StoreInstitution.php │ └── UpdateInstitution.php │ ├── Loans │ ├── IndexLoans.php │ └── StoreLoanAccount.php │ ├── Portfolio │ └── UpdatePortfolio.php │ └── Rules │ └── StoreRule.php ├── artisan ├── bootstrap ├── app.php └── cache │ └── .gitignore ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── cors.php ├── database.php ├── filesystems.php ├── financial-freedom.php ├── hashing.php ├── logging.php ├── mail.php ├── modules.php ├── queue.php ├── sanctum.php ├── services.php ├── session.php └── view.php ├── database ├── .gitignore ├── factories │ └── UserFactory.php ├── migrations │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_reset_tokens_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2019_12_14_000001_create_personal_access_tokens_table.php │ ├── 2024_01_13_180043_added_default_currency_to_users.php │ ├── 2024_01_13_184932_added_groups.php │ ├── 2024_01_13_184933_added_categories.php │ ├── 2024_01_19_213458_added_institutions.php │ ├── 2024_01_20_190002_added_cash_accounts.php │ ├── 2024_01_20_190547_added_credit_cards.php │ ├── 2024_01_20_190818_added_loans.php │ ├── 2024_04_24_030908_added_import_map_to_credit_cards.php │ ├── 2024_04_24_030948_added_import_map_to_loans.php │ ├── 2024_04_24_031010_added_import_map_to_cash_accounts.php │ └── 2024_05_15_212852_added_rules.php └── seeders │ └── DatabaseSeeder.php ├── docker-compose.ci.yml ├── docker-compose.dev.yml ├── docker-compose.gitpod.yml ├── docker-compose.prod.yml ├── docker-compose.yml ├── docs ├── .env.example ├── .gitignore ├── .npmrc ├── .nvmrc ├── README.md ├── assets │ └── css │ │ ├── animations.css │ │ ├── docsearch.css │ │ ├── hamburger.css │ │ └── tailwind.css ├── components │ ├── Docs │ │ ├── Anchor.vue │ │ ├── Eyebrow.vue │ │ ├── Footer.vue │ │ ├── Header.vue │ │ ├── Logo.vue │ │ ├── ModeToggle.vue │ │ ├── Navigation.vue │ │ ├── NavigationGroup.vue │ │ ├── PageLink.vue │ │ ├── Search.vue │ │ ├── SmallPrint.vue │ │ ├── Tag.vue │ │ └── TopLevelNavItem.vue │ ├── DocumentDrivenNotFound.vue │ ├── Global │ │ ├── MobileMenu.vue │ │ └── ServerSideUp.vue │ ├── Icons │ │ ├── Anchor.vue │ │ ├── Arrow.vue │ │ ├── ChatBubbleIcon.vue │ │ ├── Check.vue │ │ ├── CheckIcon.vue │ │ ├── ClipboardIcon.vue │ │ ├── EnvelopeIcon.vue │ │ ├── Moon.vue │ │ ├── Resource.vue │ │ ├── Search.vue │ │ ├── Social │ │ │ ├── Discord.vue │ │ │ ├── GitHub.vue │ │ │ └── Twitter.vue │ │ ├── Sun.vue │ │ ├── UserIcon.vue │ │ └── UsersIcon.vue │ └── content │ │ ├── About.vue │ │ ├── AppButton.vue │ │ ├── AppHeading2.vue │ │ ├── AppHeading3.vue │ │ ├── AppHeading4.vue │ │ ├── AppLink.vue │ │ ├── Code │ │ ├── ClipboardIcon.vue │ │ ├── CopyButton.vue │ │ └── PanelHeader.vue │ │ ├── CodeGroup.vue │ │ ├── CodePanel.vue │ │ ├── Column.vue │ │ ├── GridPattern.vue │ │ ├── Guide.vue │ │ ├── Guides.vue │ │ ├── HeroPattern.vue │ │ ├── InfoIcon.vue │ │ ├── LandingDecentralized.vue │ │ ├── LandingHero.vue │ │ ├── LandingScreenshot.vue │ │ ├── LandingSignup.vue │ │ ├── LandingStack.vue │ │ ├── LeadP.vue │ │ ├── MarketingFollowAlong.vue │ │ ├── MarketingFooter.vue │ │ ├── MarketingHeader.vue │ │ ├── NotProse.vue │ │ ├── Note.vue │ │ ├── Properties.vue │ │ ├── Property.vue │ │ ├── Resources.vue │ │ ├── Resources │ │ ├── Pattern.vue │ │ ├── Resource.vue │ │ └── ResourceIcon.vue │ │ └── Row.vue ├── composables │ ├── states.ts │ └── useBreakpoints.ts ├── content │ ├── docs │ │ ├── 1.index.md │ │ ├── 1.installation │ │ │ └── 2.install-on-synology.md │ │ └── 6.community │ │ │ └── 1.contributing.md │ └── index.md ├── layouts │ ├── docs.vue │ └── marketing.vue ├── nuxt.config.ts ├── package.json ├── public │ └── images │ │ ├── docs │ │ └── install-synology │ │ │ ├── container-manager.png │ │ │ ├── create-folder.png │ │ │ ├── create-project-completed.png │ │ │ └── create-project.png │ │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── mstile-150x150.png │ │ ├── safari-pinned-tab.svg │ │ └── site.webmanifest │ │ ├── icons │ │ ├── bank.svg │ │ ├── heart.svg │ │ ├── search-icon.svg │ │ ├── server.svg │ │ └── zap.svg │ │ ├── logos │ │ ├── amplitude.svg │ │ ├── docker.svg │ │ ├── financial-freedom-white-logo.svg │ │ ├── github-white.svg │ │ ├── laravel.svg │ │ ├── mariadb.svg │ │ ├── meilisearch.svg │ │ ├── redis.svg │ │ ├── server-side-up-footer.svg │ │ ├── server-side-up-logo-horizontal.svg │ │ ├── spin-logo.svg │ │ ├── tailwindcss.svg │ │ └── x-logo.svg │ │ ├── social-image.png │ │ └── ui │ │ ├── app-screenshot.png │ │ ├── background-pattern.svg │ │ ├── credit-cards.png │ │ ├── dan.png │ │ ├── full-screen-mockup.png │ │ ├── jay.png │ │ └── light-accent.png ├── server │ └── routes │ │ └── sitemap.xml.ts ├── tailwind.config.js ├── tsconfig.json ├── typography.js └── yarn.lock ├── jsconfig.json ├── modules_statuses.json ├── package.json ├── phpunit.xml ├── postcss.config.js ├── public ├── .htaccess ├── favicon.ico ├── img │ └── ui │ │ └── institution-placeholder.png ├── index.php └── robots.txt ├── resources ├── css │ ├── animations.css │ └── app.css ├── js │ ├── Components │ │ ├── ApplicationLogo.vue │ │ ├── Checkbox.vue │ │ ├── DangerButton.vue │ │ ├── Dropdown.vue │ │ ├── DropdownLink.vue │ │ ├── Icons │ │ │ ├── BankIcon.vue │ │ │ ├── BankModalIcon.vue │ │ │ ├── BudgetPlanIcon.vue │ │ │ ├── CashFlowIcon.vue │ │ │ ├── CoinsIcon.vue │ │ │ ├── DashboardIcon.vue │ │ │ ├── FolderIcon.vue │ │ │ ├── FolderModalIcon.vue │ │ │ ├── GoalsIcon.vue │ │ │ ├── LeftArrowIcon.vue │ │ │ ├── ModalCloseIcon.vue │ │ │ ├── RightArrowIcon.vue │ │ │ ├── SettingsIcon.vue │ │ │ ├── SuccessNotificationIcon.vue │ │ │ ├── SwitchIcon.vue │ │ │ ├── TransactionsIcon.vue │ │ │ ├── TrashModalIcon.vue │ │ │ └── UploadIcon.vue │ │ ├── InputError.vue │ │ ├── InputLabel.vue │ │ ├── Modal.vue │ │ ├── NavLink.vue │ │ ├── Notification.vue │ │ ├── PrefixTextInput.vue │ │ ├── PrimaryButton.vue │ │ ├── ResponsiveNavLink.vue │ │ ├── SecondaryButton.vue │ │ ├── SlideOut.vue │ │ ├── SuffixTextInput.vue │ │ └── TextInput.vue │ ├── Composables │ │ ├── useCategoryColor.js │ │ ├── useDisplay.js │ │ ├── useFormatters.js │ │ └── useImportTransactions.js │ ├── Layouts │ │ ├── AuthenticatedLayout.vue │ │ └── GuestLayout.vue │ ├── Pages │ │ ├── Accounts │ │ │ ├── Index.vue │ │ │ └── Partials │ │ │ │ ├── AccountSummary.vue │ │ │ │ ├── AddAccountModal.vue │ │ │ │ ├── CashAccountsTable.vue │ │ │ │ ├── CreditCardsTable.vue │ │ │ │ ├── EmptyState.vue │ │ │ │ ├── LoansTable.vue │ │ │ │ └── NetWorth.vue │ │ ├── Auth │ │ │ ├── ConfirmPassword.vue │ │ │ ├── ForgotPassword.vue │ │ │ ├── Login.vue │ │ │ ├── Register.vue │ │ │ ├── ResetPassword.vue │ │ │ └── VerifyEmail.vue │ │ ├── Cash │ │ │ └── Show.vue │ │ ├── CreditCards │ │ │ └── Show.vue │ │ ├── Dashboard │ │ │ └── Index.vue │ │ ├── Demo │ │ │ └── Index.vue │ │ ├── Loans │ │ │ └── Show.vue │ │ ├── Profile │ │ │ ├── Edit.vue │ │ │ └── Partials │ │ │ │ ├── DeleteUserForm.vue │ │ │ │ ├── UpdatePasswordForm.vue │ │ │ │ └── UpdateProfileInformationForm.vue │ │ ├── Settings │ │ │ ├── Categories │ │ │ │ ├── Index.vue │ │ │ │ └── Partials │ │ │ │ │ ├── AddCategoryModal.vue │ │ │ │ │ ├── CategoriesTable.vue │ │ │ │ │ ├── DeleteCategoryModal.vue │ │ │ │ │ ├── EditCategoryModal.vue │ │ │ │ │ └── GroupTable.vue │ │ │ ├── Index.vue │ │ │ ├── Institutions │ │ │ │ ├── Index.vue │ │ │ │ └── Partials │ │ │ │ │ ├── AddInstitutionModal.vue │ │ │ │ │ ├── DeleteInstitutionModal.vue │ │ │ │ │ ├── EditInstitutionModal.vue │ │ │ │ │ └── InstitutionTable.vue │ │ │ └── Partials │ │ │ │ └── Navigation.vue │ │ ├── Transactions │ │ │ ├── Import │ │ │ │ ├── Index.vue │ │ │ │ └── Partials │ │ │ │ │ ├── AddRuleSlideout.vue │ │ │ │ │ ├── CheckDuplicateModal.vue │ │ │ │ │ ├── FloatingActions.vue │ │ │ │ │ ├── ImportRow.vue │ │ │ │ │ ├── ImportStep.vue │ │ │ │ │ ├── ImportTable.vue │ │ │ │ │ ├── MapFields.vue │ │ │ │ │ ├── SelectAccount.vue │ │ │ │ │ └── UploadFile.vue │ │ │ ├── Index.vue │ │ │ ├── Partials │ │ │ │ ├── AddTransactionModal.vue │ │ │ │ ├── Filters.vue │ │ │ │ └── TransactionsTable.vue │ │ │ └── Show.vue │ │ └── Welcome.vue │ ├── app.js │ └── bootstrap.js └── views │ └── app.blade.php ├── routes ├── api.php ├── auth.php ├── channels.php ├── console.php └── web.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tailwind.config.js ├── tests ├── CreatesApplication.php ├── Feature │ ├── Auth │ │ ├── AuthenticationTest.php │ │ ├── EmailVerificationTest.php │ │ ├── PasswordConfirmationTest.php │ │ ├── PasswordResetTest.php │ │ ├── PasswordUpdateTest.php │ │ └── RegistrationTest.php │ ├── ExampleTest.php │ └── ProfileTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php ├── vite-module-loader.js ├── vite.config.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .vault-password 2 | .github 3 | .git 4 | .infrastructure 5 | Dockerfile 6 | docker-*.yml 7 | .gitlab-ci.yml 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ############################################ 2 | # Application Settings: Ensure these are correct. 3 | ############################################ 4 | 5 | # App Settings 6 | APP_NAME="Financial Freedom" 7 | APP_ENV=local 8 | APP_URL="https://financialfreedom.dev.test" 9 | APP_DEBUG=true 10 | APP_KEY=base64:1234567890abcdefghijklmnopqrstuvwxyz # Run `php artisan key:generate` 11 | VITE_APP_NAME="Financial Freedom" 12 | 13 | # Database Settings 14 | DB_CONNECTION=sqlite 15 | DB_DATABASE=/var/www/html/.infrastructure/volume_data/database.sqlite 16 | 17 | # Mail Settings 18 | MAIL_MAILER=smtp 19 | MAIL_HOST=mailpit 20 | MAIL_PORT=1025 21 | MAIL_USERNAME=null 22 | MAIL_PASSWORD=null 23 | MAIL_ENCRYPTION=null 24 | MAIL_FROM_ADDRESS=noreply@financialfreedom.dev.test 25 | MAIL_FROM_NAME="${APP_NAME}" 26 | 27 | # serversideup/php settings 28 | SSL_MODE=full 29 | AUTORUN_ENABLED=true 30 | AUTORUN_LARAVEL_EVENT_CACHE=false 31 | AUTORUN_LARAVEL_ROUTE_CACHE=false 32 | AUTORUN_LARAVEL_STORAGE_LINK=false 33 | AUTORUN_LARAVEL_VIEW_CACHE=false 34 | 35 | # Financial Freedom Settings 36 | FINANCIAL_FREEDOM_ALLOW_REGISTRATION=false 37 | 38 | ############################################ 39 | # Development Settings (don't change these) 40 | ############################################ 41 | LOG_CHANNEL=stack 42 | 43 | BROADCAST_DRIVER=log 44 | CACHE_DRIVER=file 45 | QUEUE_CONNECTION=sync 46 | SESSION_LIFETIME=120 47 | 48 | SESSION_DRIVER=cookie -------------------------------------------------------------------------------- /.env.release: -------------------------------------------------------------------------------- 1 | ############################################ 2 | # Application Settings: Ensure these are correct. 3 | ############################################ 4 | 5 | # App Settings 6 | APP_NAME="Financial Freedom" 7 | APP_ENV=production 8 | 9 | APP_DEBUG=false # Change to "true" for development 10 | 11 | # Database Settings 12 | DB_CONNECTION=sqlite 13 | DB_DATABASE=/var/www/html/.infrastructure/volume_data/database.sqlite 14 | 15 | # Financial Freedom Settings 16 | FINANCIAL_FREEDOM_ALLOW_REGISTRATION=false 17 | 18 | ############################################ 19 | # Development Settings (don't change these) 20 | ############################################ 21 | LOG_CHANNEL=stack 22 | 23 | BROADCAST_DRIVER=log 24 | CACHE_DRIVER=file 25 | QUEUE_CONNECTION=sync 26 | SESSION_LIFETIME=120 27 | 28 | SESSION_DRIVER=cookie -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Coming Soon! 😃 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: serversideup 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: "You found a bug in the code \U0001F914" 4 | title: '' 5 | labels: "Bug: Needs Confirmation 🧐" 6 | 7 | --- 8 | 9 | ## Issue description 10 | 11 | 12 | ## Environment 13 | 14 | 15 | - What's My Browser Support link: {{ paste your support link here }} 16 | 17 | ## Steps to reproduce the issue 18 | 19 | 1. 20 | 2. 21 | 3. 22 | 23 | ## What is expected? 24 | 25 | 26 | ## Link to where issue can be reproduced 27 | 28 | 29 | ## Additional details / screenshots 30 | 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: ❓ Support Question 4 | url: https://github.com/serversideup/financial-freedom/discussions 5 | about: Get friendly support from the community on our forum. 6 | 7 | - name: ✨ Request a feature 8 | url: https://github.com/serversideup/financial-freedom/discussions/63 9 | about: Learn how to request a new feature. 10 | 11 | - name: 🤵 Get Professional Support & Customizations 12 | url: https://serversideup.net/professional-support 13 | about: Skip the line and get priority support directly from the creators of Financial Freedom. -------------------------------------------------------------------------------- /.github/images/any-bank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/any-bank.png -------------------------------------------------------------------------------- /.github/images/cashflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/cashflow.png -------------------------------------------------------------------------------- /.github/images/global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/global.png -------------------------------------------------------------------------------- /.github/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/header.png -------------------------------------------------------------------------------- /.github/images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/preview.png -------------------------------------------------------------------------------- /.github/images/private-sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/private-sync.png -------------------------------------------------------------------------------- /.github/images/self-host.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/self-host.png -------------------------------------------------------------------------------- /.github/images/tagging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/.github/images/tagging.png -------------------------------------------------------------------------------- /.github/workflows/action_app-publish-edge.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish (Edge Image) 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - 'docs/**' 9 | schedule: 10 | - cron: '0 8 * * 1' 11 | 12 | jobs: 13 | build-edge: 14 | uses: ./.github/workflows/service_docker-build-and-publish.yml 15 | with: 16 | docker-tags: "serversideup/financial-freedom:edge" 17 | dockerfile: "./Dockerfile.php" 18 | secrets: inherit 19 | 20 | update_container_readme: 21 | runs-on: ubuntu-22.04 22 | name: Push README to Docker Hub 23 | steps: 24 | - name: git checkout 25 | uses: actions/checkout@v4 26 | with: 27 | ref: main 28 | 29 | - name: push README to Dockerhub 30 | uses: peter-evans/dockerhub-description@v4 31 | with: 32 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 33 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 34 | repository: serversideup/financial-freedom 35 | short-description: "🔥🔥🔥 An open source alternative to Mint, YNAB, and more. Stay on budget and build wealth." -------------------------------------------------------------------------------- /.github/workflows/action_app-publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish (Release) 2 | on: 3 | workflow_dispatch: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release: 9 | uses: ./.github/workflows/service_docker-build-and-publish.yml 10 | with: 11 | docker-tags: "serversideup/financial-freedom:latest,serversideup/financial-freedom:${{ github.ref_name }}" 12 | dockerfile: "./Dockerfile.php" 13 | secrets: inherit -------------------------------------------------------------------------------- /.github/workflows/action_marketing-site-preview.yml: -------------------------------------------------------------------------------- 1 | name: Site Deployment - Preview 👨‍🔬 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | paths: 7 | - docs/** 8 | 9 | jobs: 10 | publish-preview-site: 11 | uses: ./.github/workflows/service_deploy-static-site.yml 12 | secrets: inherit 13 | with: 14 | environment-name: 'site-financial-freedom (Preview)' -------------------------------------------------------------------------------- /.github/workflows/action_marketing-site-publish.yml: -------------------------------------------------------------------------------- 1 | name: Site Deployment - Production 🚀 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - docs/** 8 | 9 | jobs: 10 | publish-production-site: 11 | secrets: inherit 12 | uses: ./.github/workflows/service_deploy-static-site.yml 13 | with: 14 | environment-name: 'site-financial-freedom (Production)' -------------------------------------------------------------------------------- /.github/workflows/scheduled-task_update-sponsors.yml: -------------------------------------------------------------------------------- 1 | name: Generate Sponsors README 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: 30 15 * * 0-6 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 🛎️ 11 | uses: actions/checkout@v3 12 | 13 | - name: Generate Sponsors 💖 14 | uses: JamesIves/github-sponsors-readme-action@v1 15 | with: 16 | organization: true 17 | maximum: 500 18 | fallback: '

Sponsors

' 19 | token: ${{ secrets.SPONSORS_README_ACTION_PERSONAL_ACCESS_TOKEN }} 20 | marker: 'supporters' 21 | template: '{{{ login }}}  ' 22 | file: 'README.md' 23 | 24 | - name: Deploy to GitHub Pages 🚀 25 | uses: JamesIves/github-pages-deploy-action@v4 26 | with: 27 | branch: main 28 | folder: '.' -------------------------------------------------------------------------------- /.github/workflows/service_deploy-static-site.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | environment-name: 5 | required: true 6 | type: string 7 | 8 | jobs: 9 | deploy-static-site: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | deployments: write 14 | environment: 15 | name: ${{ inputs.environment-name }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set env file 20 | run: | 21 | if [[ -z "$BASE_64_SECRET" ]]; then 22 | echo '🚨🚨🚨 ENV File not set 🚨🚨🚨' 1>&2 23 | exit 1 24 | fi 25 | echo $BASE_64_SECRET | base64 -d > .env 26 | working-directory: ./docs 27 | env: 28 | BASE_64_SECRET: ${{ secrets.ENV_FILE_BASE64 }} 29 | 30 | - run: | 31 | yarn install --frozen-lockfile 32 | yarn build 33 | npx nuxi generate 34 | working-directory: ./docs 35 | 36 | - name: Publish to Cloudflare Pages 37 | uses: cloudflare/pages-action@v1 38 | with: 39 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 40 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 41 | projectName: site-financial-freedom 42 | directory: docs/.output/public 43 | branch: ${{ github.head_ref || github.ref_name }} 44 | gitHubToken: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /public/build 5 | /public/css/app.css 6 | /public/js/app.js 7 | /public/js/bug-snatch.js 8 | /public/js/bug-snatch.js.map 9 | /storage/*.key 10 | /vendor 11 | .env 12 | .env.backup 13 | .phpunit.result.cache 14 | .nova 15 | Homestead.json 16 | Homestead.yaml 17 | npm-debug.log 18 | yarn-error.log 19 | report.xml 20 | 21 | # Ignore the _volumes folder, except certain Traefik configs 22 | /_volumes/* 23 | !/_volumes/traefik 24 | .vault-password 25 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-base:latest 2 | 3 | ENV SPIN_ENV="dev,gitpod" 4 | 5 | RUN echo 'export PATH="$HOME/.spin/bin:$PATH"' >> ~/.bashrc && \ 6 | touch /home/gitpod/.bash_profile && \ 7 | chown gitpod:gitpod /home/gitpod/.bash_profile -------------------------------------------------------------------------------- /.infrastructure/conf/traefik/dev/traefik-certs.yml: -------------------------------------------------------------------------------- 1 | tls: 2 | stores: 3 | default: 4 | defaultCertificate: 5 | certFile: /certificates/local-dev.pem 6 | keyFile: /certificates/local-dev-key.pem 7 | certificates: 8 | - certFile: /certificates/local-dev.pem 9 | keyFile: /certificates/local-dev-key.pem 10 | stores: 11 | - default -------------------------------------------------------------------------------- /.infrastructure/conf/traefik/dev/traefik.yml: -------------------------------------------------------------------------------- 1 | # Allow self-signed certificates 2 | serversTransport: 3 | insecureSkipVerify: true 4 | 5 | providers: 6 | docker: 7 | exposedByDefault: false 8 | file: 9 | filename: /traefik-certs.yml 10 | watch: true 11 | entryPoints: 12 | web: 13 | address: ":80" 14 | http: 15 | redirections: 16 | entrypoint: 17 | to: websecure 18 | scheme: https 19 | 20 | websecure: 21 | address: ":443" 22 | 23 | vite: 24 | address: ":5173" 25 | 26 | accessLog: {} 27 | log: 28 | level: ERROR 29 | 30 | api: 31 | dashboard: true 32 | insecure: true -------------------------------------------------------------------------------- /.infrastructure/volume_data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # View our contributing guidelines 2 | 👉 https://serversideup.net/open-source/financial-freedom/docs/community/contributing -------------------------------------------------------------------------------- /Dockerfile.node: -------------------------------------------------------------------------------- 1 | FROM node:20 as base 2 | 3 | ### Development 4 | FROM base as development 5 | ARG USER_ID 6 | 7 | RUN usermod -u $USER_ID node 8 | 9 | USER node -------------------------------------------------------------------------------- /Modules/Initialize/app/Actions/CreateUser.php: -------------------------------------------------------------------------------- 1 | $name, 14 | 'email' => $email, 15 | 'password' => Hash::make($password), 16 | ]); 17 | } 18 | } -------------------------------------------------------------------------------- /Modules/Initialize/app/Providers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/app/Providers/.gitkeep -------------------------------------------------------------------------------- /Modules/Initialize/app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 13 | */ 14 | protected $listen = []; 15 | 16 | /** 17 | * Indicates if events should be discovered. 18 | * 19 | * @var bool 20 | */ 21 | protected static $shouldDiscoverEvents = true; 22 | 23 | /** 24 | * Configure the proper event listeners for email verification. 25 | * 26 | * @return void 27 | */ 28 | protected function configureEmailVerification(): void 29 | { 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Modules/Initialize/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "financial-freedom/initialize", 3 | "description": "", 4 | "authors": [ 5 | { 6 | "name": "Dan Pastori", 7 | "email": "dan@serversideup.net" 8 | } 9 | ], 10 | "extra": { 11 | "laravel": { 12 | "providers": [], 13 | "aliases": { 14 | 15 | } 16 | } 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "Modules\\Initialize\\": "app/", 21 | "Modules\\Initialize\\Database\\Factories\\": "database/factories/", 22 | "Modules\\Initialize\\Database\\Seeders\\": "database/seeders/" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "Modules\\Initialize\\Tests\\": "tests/" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Modules/Initialize/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/config/.gitkeep -------------------------------------------------------------------------------- /Modules/Initialize/config/config.php: -------------------------------------------------------------------------------- 1 | 'Initialize', 5 | ]; 6 | -------------------------------------------------------------------------------- /Modules/Initialize/database/factories/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/database/factories/.gitkeep -------------------------------------------------------------------------------- /Modules/Initialize/database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/database/migrations/.gitkeep -------------------------------------------------------------------------------- /Modules/Initialize/database/seeders/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/database/seeders/.gitkeep -------------------------------------------------------------------------------- /Modules/Initialize/database/seeders/InitializeDatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call([]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Modules/Initialize/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Initialize", 3 | "alias": "initialize", 4 | "description": "", 5 | "keywords": [], 6 | "priority": 0, 7 | "providers": [ 8 | "Modules\\Initialize\\Providers\\InitializeServiceProvider" 9 | ], 10 | "files": [] 11 | } 12 | -------------------------------------------------------------------------------- /Modules/Initialize/tests/Feature/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/tests/Feature/.gitkeep -------------------------------------------------------------------------------- /Modules/Initialize/tests/Unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Initialize/tests/Unit/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/app/Http/Controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/app/Http/Controllers/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/app/Http/Controllers/Api/TransactionController.php: -------------------------------------------------------------------------------- 1 | execute( $request, false ); 16 | 17 | return response()->json( $transactions ); 18 | } 19 | } -------------------------------------------------------------------------------- /Modules/Transaction/app/Http/Controllers/ImportController.php: -------------------------------------------------------------------------------- 1 | 'transactions', 22 | 'groups' => fn () => ( new IndexGroups() )->index( $request ), 23 | 'cashAccounts' => fn() => ( new IndexCashAccounts() )->index(), 24 | 'creditCards' => fn() => ( new IndexCreditCards() )->index(), 25 | 'loans' => fn() => ( new IndexLoans() )->index(), 26 | ]); 27 | } 28 | 29 | public function store( Request $request ): RedirectResponse 30 | { 31 | ( new ImportTransactions() ) 32 | ->execute( 33 | $request->get('account'), 34 | $request->get('transactions') 35 | ); 36 | 37 | return redirect('/transactions'); 38 | } 39 | } -------------------------------------------------------------------------------- /Modules/Transaction/app/Http/Requests/StoreTransactionRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 16 | 'merchant' => 'required', 17 | 'date' => 'required', 18 | 'amount' => 'required', 19 | 'direction' => 'required|in:credit,debit,transfer,payment', 20 | 'category' => 'required', 21 | ]; 22 | } 23 | 24 | /** 25 | * Determine if the user is authorized to make this request. 26 | */ 27 | public function authorize(): bool 28 | { 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Modules/Transaction/app/Http/Requests/UpdateTransactionRequest.php: -------------------------------------------------------------------------------- 1 | morphTo(); 32 | } 33 | 34 | public function category() 35 | { 36 | return $this->belongsTo(Category::class); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Modules/Transaction/app/Providers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/app/Providers/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 13 | */ 14 | protected $listen = []; 15 | 16 | /** 17 | * Indicates if events should be discovered. 18 | * 19 | * @var bool 20 | */ 21 | protected static $shouldDiscoverEvents = true; 22 | 23 | /** 24 | * Configure the proper event listeners for email verification. 25 | * 26 | * @return void 27 | */ 28 | protected function configureEmailVerification(): void 29 | { 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Modules/Transaction/app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 26 | 27 | $this->mapWebRoutes(); 28 | } 29 | 30 | /** 31 | * Define the "web" routes for the application. 32 | * 33 | * These routes all receive session state, CSRF protection, etc. 34 | */ 35 | protected function mapWebRoutes(): void 36 | { 37 | Route::middleware('web')->group(module_path('Transaction', '/routes/web.php')); 38 | } 39 | 40 | /** 41 | * Define the "api" routes for the application. 42 | * 43 | * These routes are typically stateless. 44 | */ 45 | protected function mapApiRoutes(): void 46 | { 47 | Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Transaction', '/routes/api.php')); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Modules/Transaction/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nwidart/transaction", 3 | "description": "", 4 | "authors": [ 5 | { 6 | "name": "Nicolas Widart", 7 | "email": "n.widart@gmail.com" 8 | } 9 | ], 10 | "extra": { 11 | "laravel": { 12 | "providers": [], 13 | "aliases": { 14 | 15 | } 16 | } 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "Modules\\Transaction\\": "app/", 21 | "Modules\\Transaction\\Database\\Factories\\": "database/factories/", 22 | "Modules\\Transaction\\Database\\Seeders\\": "database/seeders/" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "Modules\\Transaction\\Tests\\": "tests/" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Modules/Transaction/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/config/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/config/config.php: -------------------------------------------------------------------------------- 1 | 'Transaction', 5 | ]; 6 | -------------------------------------------------------------------------------- /Modules/Transaction/database/factories/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/database/factories/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/database/migrations/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/database/migrations/2024_06_03_224410_renamed_description_to_notes_transactions.php: -------------------------------------------------------------------------------- 1 | renameColumn('description', 'notes'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('transactions', function (Blueprint $table) { 25 | $table->renameColumn('notes', 'description'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /Modules/Transaction/database/seeders/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/database/seeders/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/database/seeders/TransactionsDatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call([]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Modules/Transaction/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Transaction", 3 | "alias": "transaction", 4 | "description": "", 5 | "keywords": [], 6 | "priority": 0, 7 | "providers": [ 8 | "Modules\\Transaction\\Providers\\TransactionServiceProvider" 9 | ], 10 | "files": [] 11 | } 12 | -------------------------------------------------------------------------------- /Modules/Transaction/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/routes/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/routes/api.php: -------------------------------------------------------------------------------- 1 | 'transactions'], function () { 18 | Route::get('/', [TransactionController::class, 'index']) 19 | ->name('transaction.index'); 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /Modules/Transaction/tests/Feature/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/tests/Feature/.gitkeep -------------------------------------------------------------------------------- /Modules/Transaction/tests/Unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/Modules/Transaction/tests/Unit/.gitkeep -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a Security Vulnerability 2 | If you find a security vulnerability, please let us know as soon as possible. 3 | 4 | [View Our Responsible Disclosure Policy →](https://www.notion.so/Responsible-Disclosure-Policy-421a6a3be1714d388ebbadba7eebbdc8) -------------------------------------------------------------------------------- /app/Console/Commands/SeedCategories.php: -------------------------------------------------------------------------------- 1 | seed( $user ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Console/Commands/SetUpInstitutions.php: -------------------------------------------------------------------------------- 1 | seedUnitedStates(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 16 | } 17 | 18 | /** 19 | * Register the commands for the application. 20 | */ 21 | protected function commands(): void 22 | { 23 | $this->load(__DIR__.'/Commands'); 24 | 25 | require base_path('routes/console.php'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Data/CashAccountData.php: -------------------------------------------------------------------------------- 1 | user()->id, 26 | institutionId: $request->input('institution_id'), 27 | type: $request->input('type'), 28 | name: $request->input('name'), 29 | description: $request->input('description'), 30 | balance: $request->input('balance'), 31 | interestRate: $request->input('interest_rate') ? $request->input('interest_rate') : 0.00, 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /app/Data/CreditCardData.php: -------------------------------------------------------------------------------- 1 | user()->id, 27 | institutionId: $request->input('institution_id'), 28 | brand: $request->input('brand'), 29 | name: $request->input('name'), 30 | description: $request->input('description'), 31 | interestRate: $request->input('interest_rate') ? $request->input('interest_rate') : 0.00, 32 | creditLimit: $request->input('credit_limit'), 33 | balance: $request->input('balance'), 34 | ); 35 | } 36 | } -------------------------------------------------------------------------------- /app/Data/Rules/StoreRuleData.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $dontFlash = [ 16 | 'current_password', 17 | 'password', 18 | 'password_confirmation', 19 | ]; 20 | 21 | /** 22 | * Register the exception handling callbacks for the application. 23 | */ 24 | public function register(): void 25 | { 26 | $this->reportable(function (Throwable $e) { 27 | // 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ConfirmablePasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 30 | 'email' => $request->user()->email, 31 | 'password' => $request->password, 32 | ])) { 33 | throw ValidationException::withMessages([ 34 | 'password' => __('auth.password'), 35 | ]); 36 | } 37 | 38 | $request->session()->put('auth.password_confirmed_at', time()); 39 | 40 | return redirect()->intended(RouteServiceProvider::HOME); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 18 | return redirect()->intended(RouteServiceProvider::HOME); 19 | } 20 | 21 | $request->user()->sendEmailVerificationNotification(); 22 | 23 | return back()->with('status', 'verification-link-sent'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/EmailVerificationPromptController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail() 20 | ? redirect()->intended(RouteServiceProvider::HOME) 21 | : Inertia::render('Auth/VerifyEmail', ['status' => session('status')]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 19 | 'current_password' => ['required', 'current_password'], 20 | 'password' => ['required', Password::defaults(), 'confirmed'], 21 | ]); 22 | 23 | $request->user()->update([ 24 | 'password' => Hash::make($validated['password']), 25 | ]); 26 | 27 | return back(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 19 | return redirect()->intended(RouteServiceProvider::HOME.'?verified=1'); 20 | } 21 | 22 | if ($request->user()->markEmailAsVerified()) { 23 | event(new Verified($request->user())); 24 | } 25 | 26 | return redirect()->intended(RouteServiceProvider::HOME.'?verified=1'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Controllers/BudgetController.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/app/Http/Controllers/BudgetController.php -------------------------------------------------------------------------------- /app/Http/Controllers/CashAccountController.php: -------------------------------------------------------------------------------- 1 | back(); 17 | } 18 | 19 | // public function destroy( Account $account ): RedirectResponse 20 | // { 21 | // ( new DeleteAccount() )->delete( $account ); 22 | // return redirect()->back(); 23 | // } 24 | } -------------------------------------------------------------------------------- /app/Http/Controllers/CashFlowController.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/app/Http/Controllers/CashFlowController.php -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 'accounts', 18 | 'creditCard' => $creditCard, 19 | ]); 20 | } 21 | 22 | public function update( Request $request, CreditCard $creditCard ): RedirectResponse 23 | { 24 | ( new UpdateCreditCard( $request, $creditCard ) )->update(); 25 | return redirect()->back(); 26 | } 27 | 28 | // public function destroy( Account $account ): RedirectResponse 29 | // { 30 | // ( new DeleteAccount() )->delete( $account ); 31 | // return redirect()->back(); 32 | // } 33 | } -------------------------------------------------------------------------------- /app/Http/Controllers/DashboardController.php: -------------------------------------------------------------------------------- 1 | 'dashboard' 18 | ] ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Controllers/GoalsController.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/app/Http/Controllers/GoalsController.php -------------------------------------------------------------------------------- /app/Http/Controllers/LoanController.php: -------------------------------------------------------------------------------- 1 | back(); 17 | } 18 | 19 | // public function destroy( Account $account ): RedirectResponse 20 | // { 21 | // ( new DeleteAccount() )->delete( $account ); 22 | // return redirect()->back(); 23 | // } 24 | } -------------------------------------------------------------------------------- /app/Http/Controllers/PortfolioController.php: -------------------------------------------------------------------------------- 1 | update( $request ); 16 | return redirect()->back(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/Controllers/RulesController.php: -------------------------------------------------------------------------------- 1 | execute( $request->toDto() ); 17 | 18 | return redirect()->back(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Controllers/SettingsController.php: -------------------------------------------------------------------------------- 1 | 'settings', 16 | 'subGroup' => 'portfolio' 17 | ] ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson() ? null : route('login'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/HandleInertiaRequests.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | public function share(Request $request): array 31 | { 32 | return [ 33 | ...parent::share($request), 34 | 'auth' => [ 35 | 'user' => $request->user(), 36 | ], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Http/Middleware/PreventRequestsDuringMaintenance.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 24 | return redirect(RouteServiceProvider::HOME); 25 | } 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | 'current_password', 16 | 'password', 17 | 'password_confirmation', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public function hosts(): array 15 | { 16 | return [ 17 | $this->allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | |string|null 14 | */ 15 | protected $proxies; 16 | 17 | /** 18 | * The headers that should be used to detect proxies. 19 | * 20 | * @var int 21 | */ 22 | protected $headers = 23 | Request::HEADER_X_FORWARDED_FOR | 24 | Request::HEADER_X_FORWARDED_HOST | 25 | Request::HEADER_X_FORWARDED_PORT | 26 | Request::HEADER_X_FORWARDED_PROTO | 27 | Request::HEADER_X_FORWARDED_AWS_ELB; 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Middleware/ValidateSignature.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 'fbclid', 16 | // 'utm_campaign', 17 | // 'utm_content', 18 | // 'utm_medium', 19 | // 'utm_source', 20 | // 'utm_term', 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /app/Http/Requests/Accounts/StoreAccountRequest.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | public function rules(): array 15 | { 16 | return [ 17 | 'account_type' => 'required|string', 18 | 'name' => 'required|string', 19 | 'institution_id' => 'required|exists:institutions,id', 20 | 'description' => 'nullable|string', 21 | 'type' => 'nullable|required_unless:account_type,credit-card|string', 22 | 'balance' => 'nullable|numeric', 23 | 'remaining_balance' => 'nullable|required_if:account_type,loan|numeric', 24 | 'original_balance' => 'nullable|required_if:account_type,loan|numeric', 25 | 'payment_amount' => 'nullable|required_if:account_type,loan|numeric', 26 | 'interest_rate' => 'nullable|numeric', 27 | 'date_opened' => 'nullable|date', 28 | 'credit_limit' => 'nullable|numeric', 29 | 'brand' => 'nullable|required_if:account_type,credit-card|string', 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Requests/Categories/StoreCategoryRequest.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | public function rules(): array 15 | { 16 | return [ 17 | 'name' => 'required|string', 18 | 'color' => 'required|string', 19 | 'group_id' => 'required|numeric' 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Requests/Categories/UpdateCategoryRequest.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | public function rules(): array 15 | { 16 | return [ 17 | 'name' => 'sometimes|string', 18 | 'color' => 'sometimes|string', 19 | 'group_id' => 'sometimes|numeric' 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Requests/Institutions/StoreInstitutionRequest.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | public function rules(): array 15 | { 16 | return [ 17 | 'name' => 'required|string' 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Requests/Institutions/UpdateInstitutionRequest.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | public function rules(): array 15 | { 16 | return [ 17 | 'name' => 'required|string' 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Requests/Portfolio/UpdatePortfolioRequest.php: -------------------------------------------------------------------------------- 1 | |string> 13 | */ 14 | public function rules(): array 15 | { 16 | return [ 17 | 'name' => ['required', 'string'], 18 | 'default_currency' => ['required', 'string'], 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Requests/ProfileUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | public function rules(): array 17 | { 18 | return [ 19 | 'name' => ['required', 'string', 'max:255'], 20 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)], 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Http/Requests/Rules/StoreRuleRequest.php: -------------------------------------------------------------------------------- 1 | |string> 22 | */ 23 | public function rules(): array 24 | { 25 | return [ 26 | 'search_string' => 'required|string', 27 | 'replace_string' => 'required|string', 28 | ]; 29 | } 30 | 31 | /** 32 | * Build and return a DTO. 33 | * 34 | * @return StoreRuleData 35 | */ 36 | public function toDto(): StoreRuleData 37 | { 38 | return new StoreRuleData( 39 | account: $this->account, 40 | searchString: $this->search_string, 41 | replaceString: $this->replace_string, 42 | category: $this->category, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Listeners/Registered/SeedDefaultCategories.php: -------------------------------------------------------------------------------- 1 | user; 25 | 26 | ( new SeedUserCategories() )->seed( $user ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Models/CashAccount.php: -------------------------------------------------------------------------------- 1 | 'object' 29 | ]; 30 | 31 | public function user() 32 | { 33 | return $this->hasOne('App\Models\User', 'id', 'user_id'); 34 | } 35 | 36 | public function institution() 37 | { 38 | return $this->hasOne('App\Models\Institution', 'id', 'institution_id'); 39 | } 40 | 41 | public function rules() 42 | { 43 | return $this->morphMany(Rule::class, 'accountable'); 44 | } 45 | 46 | public function getTypeAttribute() 47 | { 48 | return 'cash-account'; 49 | } 50 | } -------------------------------------------------------------------------------- /app/Models/Category.php: -------------------------------------------------------------------------------- 1 | hasOne('App\Models\Group', 'id', 'group_id'); 24 | } 25 | } -------------------------------------------------------------------------------- /app/Models/CreditCard.php: -------------------------------------------------------------------------------- 1 | 'object' 29 | ]; 30 | 31 | public function user() 32 | { 33 | return $this->hasOne(User::class, 'id', 'user_id'); 34 | } 35 | 36 | public function institution() 37 | { 38 | return $this->hasOne(Institution::class, 'id', 'institution_id'); 39 | } 40 | 41 | public function rules() 42 | { 43 | return $this->morphMany(Rule::class, 'accountable'); 44 | } 45 | 46 | public function getTypeAttribute() 47 | { 48 | return 'credit-card'; 49 | } 50 | } -------------------------------------------------------------------------------- /app/Models/Group.php: -------------------------------------------------------------------------------- 1 | hasMany('App\Models\Category', 'group_id', 'id'); 23 | } 24 | } -------------------------------------------------------------------------------- /app/Models/Institution.php: -------------------------------------------------------------------------------- 1 | 'object' 31 | ]; 32 | 33 | public function user() 34 | { 35 | return $this->hasOne('App\Models\User', 'id', 'user_id'); 36 | } 37 | 38 | public function institution() 39 | { 40 | return $this->hasOne('App\Models\Institution', 'id', 'institution_id'); 41 | } 42 | 43 | public function rules() 44 | { 45 | return $this->morphMany(Rule::class, 'accountable'); 46 | } 47 | 48 | public function getTypeAttribute() 49 | { 50 | return 'loan'; 51 | } 52 | } -------------------------------------------------------------------------------- /app/Models/Rule.php: -------------------------------------------------------------------------------- 1 | morphTo(); 23 | } 24 | 25 | public function category() 26 | { 27 | return $this->belongsTo(Category::class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $policies = [ 16 | // 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | */ 22 | public function boot(): void 23 | { 24 | // 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 17 | */ 18 | protected $listen = [ 19 | Registered::class => [ 20 | SendEmailVerificationNotification::class, 21 | SeedDefaultCategories::class, 22 | ], 23 | ]; 24 | 25 | /** 26 | * Register any events for your application. 27 | */ 28 | public function boot(): void 29 | { 30 | // 31 | } 32 | 33 | /** 34 | * Determine if events and listeners should be automatically discovered. 35 | */ 36 | public function shouldDiscoverEvents(): bool 37 | { 38 | return false; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | by($request->user()?->id ?: $request->ip()); 29 | }); 30 | 31 | $this->routes(function () { 32 | Route::middleware('api') 33 | ->prefix('api') 34 | ->group(base_path('routes/api.php')); 35 | 36 | Route::middleware('web') 37 | ->group(base_path('routes/web.php')); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Services/Accounts/StoreAccount.php: -------------------------------------------------------------------------------- 1 | get('account_type') ){ 17 | case 'cash': 18 | $cashAccount = CashAccountData::fromCreateRequest( $request ); 19 | ( new StoreCashAccount() )->store( $cashAccount ); 20 | break; 21 | case 'loan': 22 | $loanAccount = LoanData::fromCreateRequest( $request ); 23 | ( new StoreLoanAccount() )->store( $loanAccount ); 24 | break; 25 | case 'credit-card': 26 | $creditCard = CreditCardData::fromCreateRequest( $request ); 27 | ( new StoreCreditCard() )->store( $creditCard ); 28 | break; 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /app/Services/CashAccounts/IndexCashAccounts.php: -------------------------------------------------------------------------------- 1 | with('rules') 13 | ->where('user_id', auth()->id()) 14 | ->orderBy('name', 'ASC') 15 | ->get(); 16 | 17 | return $cashAccounts; 18 | } 19 | } -------------------------------------------------------------------------------- /app/Services/CashAccounts/StoreCashAccount.php: -------------------------------------------------------------------------------- 1 | $cashAccountData->userId, 14 | 'institution_id' => $cashAccountData->institutionId, 15 | 'type' => $cashAccountData->type, 16 | 'name' => $cashAccountData->name, 17 | 'description' => $cashAccountData->description, 18 | 'balance' => $cashAccountData->balance, 19 | 'interest_rate' => $cashAccountData->interestRate, 20 | ]); 21 | } 22 | } -------------------------------------------------------------------------------- /app/Services/Categories/DeleteCategory.php: -------------------------------------------------------------------------------- 1 | delete(); 10 | } 11 | } -------------------------------------------------------------------------------- /app/Services/Categories/IndexCategories.php: -------------------------------------------------------------------------------- 1 | request = $request; 18 | $this->query = Category::query(); 19 | 20 | $this->applySearch(); 21 | $this->applyOrder(); 22 | 23 | if( $this->paginate ){ 24 | $categories = $this->query->paginate( $this->take ); 25 | }else{ 26 | $categories = $this->query->get(); 27 | } 28 | 29 | return $categories; 30 | } 31 | 32 | private function applySearch() 33 | { 34 | if( $this->request->has('term') ){ 35 | $this->query->where( 'name', 'LIKE', "%{$this->term}%" ); 36 | } 37 | } 38 | 39 | private function applyOrder() 40 | { 41 | $this->query->orderBy( 42 | $this->request->input('orderBy', 'name'), 43 | $this->request->input('orderDirection', 'asc') 44 | ); 45 | } 46 | } -------------------------------------------------------------------------------- /app/Services/Categories/StoreCategory.php: -------------------------------------------------------------------------------- 1 | $request->user()->id, 13 | 'group_id' => $request->input('group_id'), 14 | 'name' => $request->input('name'), 15 | 'color' => $request->input('color'), 16 | ]); 17 | 18 | return $category; 19 | } 20 | } -------------------------------------------------------------------------------- /app/Services/Categories/UpdateCategory.php: -------------------------------------------------------------------------------- 1 | update( $request->validated() ); 10 | } 11 | } -------------------------------------------------------------------------------- /app/Services/CreditCards/IndexCreditCards.php: -------------------------------------------------------------------------------- 1 | with('rules') 13 | ->where('user_id', auth()->id()) 14 | ->orderBy('name', 'ASC') 15 | ->get(); 16 | 17 | return $creditCards; 18 | } 19 | } -------------------------------------------------------------------------------- /app/Services/CreditCards/StoreCreditCard.php: -------------------------------------------------------------------------------- 1 | $creditCardData->userId, 14 | 'institution_id' => $creditCardData->institutionId, 15 | 'name' => $creditCardData->name, 16 | 'brand' => $creditCardData->brand, 17 | 'description' => $creditCardData->description, 18 | 'interest_rate' => $creditCardData->interestRate, 19 | 'credit_limit' => $creditCardData->creditLimit, 20 | 'balance' => $creditCardData->balance, 21 | ]); 22 | } 23 | } -------------------------------------------------------------------------------- /app/Services/CreditCards/UpdateCreditCard.php: -------------------------------------------------------------------------------- 1 | request = $request; 13 | $this->creditCard = $creditCard; 14 | } 15 | 16 | public function update() 17 | { 18 | if( $this->request->has('import_map') ){ 19 | $this->creditCard->import_map = $this->request->get('import_map'); 20 | } 21 | 22 | $this->creditCard->save(); 23 | } 24 | } -------------------------------------------------------------------------------- /app/Services/Groups/IndexGroups.php: -------------------------------------------------------------------------------- 1 | get(); 14 | 15 | return $groups; 16 | } 17 | } -------------------------------------------------------------------------------- /app/Services/Institutions/DeleteInstitution.php: -------------------------------------------------------------------------------- 1 | delete(); 10 | } 11 | } -------------------------------------------------------------------------------- /app/Services/Institutions/IndexInstitutions.php: -------------------------------------------------------------------------------- 1 | request = $request; 17 | $this->query = Institution::query(); 18 | 19 | $this->applyOrder(); 20 | 21 | if( $this->paginate ){ 22 | $institutions = $this->query->paginate( $this->take ); 23 | }else{ 24 | $institutions = $this->query->get(); 25 | } 26 | 27 | return $institutions; 28 | } 29 | 30 | private function applyOrder() 31 | { 32 | $this->query->orderBy( 33 | $this->request->input('orderBy', 'name'), 34 | $this->request->input('orderDirection', 'asc') 35 | ); 36 | } 37 | } -------------------------------------------------------------------------------- /app/Services/Institutions/StoreInstitution.php: -------------------------------------------------------------------------------- 1 | $request->input('name'), 13 | 'url' => $request->input('url', ''), 14 | ]); 15 | 16 | return $institution; 17 | } 18 | } -------------------------------------------------------------------------------- /app/Services/Institutions/UpdateInstitution.php: -------------------------------------------------------------------------------- 1 | update([ 12 | 'name' => $request->input('name'), 13 | 'url' => $request->input('url', ''), 14 | ]); 15 | 16 | return $institution; 17 | } 18 | } -------------------------------------------------------------------------------- /app/Services/Loans/IndexLoans.php: -------------------------------------------------------------------------------- 1 | with('rules') 13 | ->where('user_id', auth()->id()) 14 | ->orderBy('name', 'ASC') 15 | ->get(); 16 | 17 | return $loans; 18 | } 19 | } -------------------------------------------------------------------------------- /app/Services/Loans/StoreLoanAccount.php: -------------------------------------------------------------------------------- 1 | $loanData->userId, 14 | 'institution_id' => $loanData->institutionId, 15 | 'name' => $loanData->name, 16 | 'type' => $loanData->type, 17 | 'description' => $loanData->description, 18 | 'opened_at' => $loanData->dateOpened, 19 | 'interest_rate' => $loanData->interestRate, 20 | 'remaining_balance' => $loanData->remainingBalance, 21 | 'original_balance' => $loanData->originalBalance, 22 | 'payment_amount' => $loanData->paymentAmount 23 | ]); 24 | } 25 | } -------------------------------------------------------------------------------- /app/Services/Portfolio/UpdatePortfolio.php: -------------------------------------------------------------------------------- 1 | user()->update( $request->validated() ); 12 | } 13 | } -------------------------------------------------------------------------------- /app/Services/Rules/StoreRule.php: -------------------------------------------------------------------------------- 1 | findAccount( $data->account ); 16 | 17 | Rule::create([ 18 | 'accountable_id' => $account->id, 19 | 'accountable_type' => get_class( $account ), 20 | 'search_string' => $data->searchString, 21 | 'replace_string' => $data->replaceString, 22 | 'category_id' => $data->category['id'], 23 | ]); 24 | } 25 | 26 | private function findAccount( $account ) 27 | { 28 | switch( $account['type'] ){ 29 | case 'cash-account': 30 | return CashAccount::find( $account['id'] ); 31 | break; 32 | case 'credit-card': 33 | return CreditCard::find( $account['id'] ); 34 | break; 35 | case 'loan': 36 | return Loan::find( $account['id'] ); 37 | break; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/financial-freedom.php: -------------------------------------------------------------------------------- 1 | env('FINANCIAL_FREEDOM_ALLOW_REGISTRATION', false), 17 | ]; -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | 'scheme' => 'https', 22 | ], 23 | 24 | 'postmark' => [ 25 | 'token' => env('POSTMARK_TOKEN'), 26 | ], 27 | 28 | 'ses' => [ 29 | 'key' => env('AWS_ACCESS_KEY_ID'), 30 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 31 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 32 | ], 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UserFactory extends Factory 13 | { 14 | /** 15 | * The current password being used by the factory. 16 | */ 17 | protected static ?string $password; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition(): array 25 | { 26 | return [ 27 | 'name' => fake()->name(), 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => static::$password ??= Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | 35 | /** 36 | * Indicate that the model's email address should be unverified. 37 | */ 38 | public function unverified(): static 39 | { 40 | return $this->state(fn (array $attributes) => [ 41 | 'email_verified_at' => null, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->rememberToken(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('users'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php: -------------------------------------------------------------------------------- 1 | string('email')->primary(); 16 | $table->string('token'); 17 | $table->timestamp('created_at')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::dropIfExists('password_reset_tokens'); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('uuid')->unique(); 17 | $table->text('connection'); 18 | $table->text('queue'); 19 | $table->longText('payload'); 20 | $table->longText('exception'); 21 | $table->timestamp('failed_at')->useCurrent(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('failed_jobs'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('tokenable'); 17 | $table->string('name'); 18 | $table->string('token', 64)->unique(); 19 | $table->text('abilities')->nullable(); 20 | $table->timestamp('last_used_at')->nullable(); 21 | $table->timestamp('expires_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('personal_access_tokens'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/migrations/2024_01_13_180043_added_default_currency_to_users.php: -------------------------------------------------------------------------------- 1 | string('default_currency')->default('USD')->after('remember_token'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('users', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2024_01_13_184932_added_groups.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 16 | $table->bigInteger('user_id')->unsigned(); 17 | $table->foreign('user_id')->references('id')->on('users'); 18 | $table->string('name'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | */ 26 | public function down(): void 27 | { 28 | Schema::dropIfExists('groups'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2024_01_13_184933_added_categories.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id')->unsigned(); 16 | $table->bigInteger('user_id')->unsigned(); 17 | $table->foreign('user_id')->references('id')->on('users'); 18 | $table->bigInteger('group_id')->unsigned(); 19 | $table->foreign('group_id')->references('id')->on('groups'); 20 | $table->string('name'); 21 | $table->string('color'); 22 | $table->timestamps(); 23 | }); 24 | 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('categories'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2024_01_19_213458_added_institutions.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 16 | $table->string('name'); 17 | $table->string('url')->nullable(); 18 | $table->text('logo')->nullable(); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | */ 26 | public function down(): void 27 | { 28 | Schema::drop('institutions'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2024_01_20_190002_added_cash_accounts.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 16 | $table->bigInteger('user_id')->unsigned(); 17 | $table->foreign('user_id')->references('id')->on('users'); 18 | $table->bigInteger('institution_id')->unsigned(); 19 | $table->foreign('institution_id')->references('id')->on('institutions'); 20 | $table->string('type'); 21 | $table->string('name'); 22 | $table->string('description')->nullable(); 23 | $table->string('account_number')->nullable(); 24 | $table->decimal('balance', 10, 3)->default(0); 25 | $table->decimal('interest_rate', 5, 3)->default(0); 26 | $table->timestamps(); 27 | $table->softDeletes(); 28 | }); 29 | } 30 | 31 | /** 32 | * Reverse the migrations. 33 | */ 34 | public function down(): void 35 | { 36 | Schema::drop('cash_accounts'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /database/migrations/2024_01_20_190547_added_credit_cards.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 16 | $table->bigInteger('user_id')->unsigned(); 17 | $table->foreign('user_id')->references('id')->on('users'); 18 | $table->bigInteger('institution_id')->unsigned(); 19 | $table->foreign('institution_id')->references('id')->on('institutions'); 20 | $table->string('brand')->nullable(); 21 | $table->string('name'); 22 | $table->string('description')->nullable(); 23 | $table->decimal('interest_rate', 5, 3)->default(0); 24 | $table->decimal('credit_limit', 10, 3)->default(0); 25 | $table->decimal('balance', 10, 3)->default(0); 26 | $table->timestamps(); 27 | $table->softDeletes(); 28 | }); 29 | } 30 | 31 | /** 32 | * Reverse the migrations. 33 | */ 34 | public function down(): void 35 | { 36 | Schema::drop('credit_cards'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /database/migrations/2024_04_24_030908_added_import_map_to_credit_cards.php: -------------------------------------------------------------------------------- 1 | json('import_map')->nullable()->after('balance'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('credit_cards', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2024_04_24_030948_added_import_map_to_loans.php: -------------------------------------------------------------------------------- 1 | json('import_map')->nullable()->after('payment_amount'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('loans', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2024_04_24_031010_added_import_map_to_cash_accounts.php: -------------------------------------------------------------------------------- 1 | json('import_map')->nullable()->after('interest_rate'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('cash_accounts', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2024_05_15_212852_added_rules.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 16 | $table->bigInteger('accountable_id')->unsigned(); 17 | $table->string('accountable_type'); 18 | $table->string('search_string'); 19 | $table->string('replace_string'); 20 | $table->bigInteger('category_id')->unsigned()->nullable(); 21 | $table->foreign('category_id')->references('id')->on('categories'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('rules'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | // \App\Models\User::factory()->create([ 18 | // 'name' => 'Test User', 19 | // 'email' => 'test@example.com', 20 | // ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docker-compose.ci.yml: -------------------------------------------------------------------------------- 1 | services: 2 | php: 3 | build: 4 | context: . 5 | dockerfile: Dockerfile.php 6 | target: ci 7 | networks: 8 | - ci 9 | volumes: 10 | - .:/var/www/html/ 11 | working_dir: /var/www/html/ 12 | 13 | node: 14 | build: 15 | context: . 16 | dockerfile: Dockerfile.node 17 | target: base 18 | networks: 19 | - ci 20 | volumes: 21 | - .:/usr/src/app/:cached 22 | working_dir: /usr/src/app/ 23 | 24 | mailpit: 25 | image: axllent/mailpit 26 | networks: 27 | - ci 28 | 29 | networks: 30 | ci: -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | traefik: 4 | image: traefik:v3.0 5 | 6 | php: 7 | depends_on: 8 | - traefik -------------------------------------------------------------------------------- /docs/.env.example: -------------------------------------------------------------------------------- 1 | NUXT_APP_BASE_URL=/open-source/financial-freedom 2 | TOP_LEVEL_DOMAIN=http://localhost:3000 3 | ALGOLIA_API_KEY=changeme 4 | ALGOLIA_APPLICATION_ID=changeme 5 | ALGOLIA_INDEX=changeme -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | .nuxt 4 | .nitro 5 | .cache 6 | .output 7 | .env 8 | dist 9 | -------------------------------------------------------------------------------- /docs/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /docs/.nvmrc: -------------------------------------------------------------------------------- 1 | v20 2 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation & Static Site, generated with Nuxt Content 2 | This is a documentation site built on top of Nuxt Content (v3). 3 | 4 | # Docs location 5 | All docs are located in the [./content](./content/docs) folder if you're just looking for the docs in plain text. 6 | 7 | ## Setup 8 | 9 | Ensure you're in the right directory. 10 | 11 | ```bash 12 | cd docs/ 13 | ``` 14 | 15 | Copy over the environment variable example file. 16 | 17 | ```bash 18 | cp .env.example .env 19 | ``` 20 | 21 | We use [NVM](https://github.com/nvm-sh/nvm) to manage our local Node version (Docker support coming soon after some JS libraries support it better). Ensure you have NVM installed and configured and use the following command to install the correct version of Node: 22 | 23 | ```bash 24 | nvm use 25 | ``` 26 | 27 | Make sure to install the dependencies: 28 | 29 | ```bash 30 | yarn install 31 | ``` 32 | 33 | ## Development Server 34 | 35 | Start the development server on http://localhost:3000 36 | 37 | ```bash 38 | yarn dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | yarn build 47 | ``` 48 | 49 | Locally preview production build: 50 | 51 | ```bash 52 | yarn preview 53 | ``` 54 | 55 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. -------------------------------------------------------------------------------- /docs/assets/css/docsearch.css: -------------------------------------------------------------------------------- 1 | button.DocSearch-Button{ 2 | background: transparent; 3 | font-family: "Inter", sans-serif; 4 | border-radius: 4px; 5 | margin: 0px; 6 | } 7 | 8 | button.DocSearch-Button .DocSearch-Search-Icon{ 9 | margin-right: 8px; 10 | width: 20px; 11 | height: 20px; 12 | stroke-width: 2; 13 | stroke-linecap: round; 14 | stroke-linejoin: round; 15 | color: #CBD5E1; 16 | } 17 | 18 | button.DocSearch-Button .DocSearch-Button-Placeholder{ 19 | font-weight: bold; 20 | color: rgb(203, 213, 225); 21 | font-size: 0.875rem/* 14px */; 22 | line-height: 1.5rem/* 24px */; 23 | padding: 0px; 24 | display: none; 25 | } 26 | 27 | @media (min-width: 1024px) { 28 | button.DocSearch-Button .DocSearch-Button-Placeholder{ 29 | display: block; 30 | } 31 | } 32 | 33 | @media (min-width: 1280px) { 34 | button.DocSearch-Button .DocSearch-Button-Placeholder{ 35 | font-size: 1.125rem/* 18px */; 36 | line-height: 1.75rem/* 28px */; 37 | } 38 | } 39 | 40 | button.DocSearch-Button:hover, 41 | button.DocSearch-Button:active, 42 | button.DocSearch-Button:focus { 43 | background: rgb(17, 24, 39); 44 | box-shadow: none; 45 | } 46 | 47 | button.DocSearch-Button .DocSearch-Button-Keys{ 48 | display: none; 49 | } 50 | -------------------------------------------------------------------------------- /docs/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @import "animations.css"; 2 | @import "hamburger.css"; 3 | @import "docsearch.css"; 4 | @import 'tailwindcss/base'; 5 | @import 'tailwindcss/components'; 6 | @import 'tailwindcss/utilities'; 7 | ::-webkit-scrollbar { 8 | width: 0px; 9 | height: 0px; 10 | } -------------------------------------------------------------------------------- /docs/components/Docs/Anchor.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /docs/components/Docs/Eyebrow.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/components/Docs/Footer.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | -------------------------------------------------------------------------------- /docs/components/Docs/ModeToggle.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /docs/components/Docs/PageLink.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /docs/components/Docs/Search.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /docs/components/Docs/SmallPrint.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | -------------------------------------------------------------------------------- /docs/components/Docs/TopLevelNavItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /docs/components/DocumentDrivenNotFound.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /docs/components/Icons/Anchor.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Arrow.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /docs/components/Icons/ChatBubbleIcon.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /docs/components/Icons/Check.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/CheckIcon.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /docs/components/Icons/ClipboardIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/EnvelopeIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Moon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Resource.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /docs/components/Icons/Search.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Social/Discord.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Social/GitHub.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Social/Twitter.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/Sun.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/UserIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/Icons/UsersIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/content/AppHeading2.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /docs/components/content/AppHeading3.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /docs/components/content/AppHeading4.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /docs/components/content/Code/ClipboardIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/content/Code/PanelHeader.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /docs/components/content/CodePanel.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /docs/components/content/Column.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /docs/components/content/GridPattern.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 40 | -------------------------------------------------------------------------------- /docs/components/content/Guide.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /docs/components/content/Guides.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /docs/components/content/HeroPattern.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 31 | -------------------------------------------------------------------------------- /docs/components/content/InfoIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/content/LandingScreenshot.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /docs/components/content/LeadP.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /docs/components/content/NotProse.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/content/Note.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/content/Properties.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/content/Property.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /docs/components/content/Resources/ResourceIcon.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /docs/components/content/Row.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/composables/states.ts: -------------------------------------------------------------------------------- 1 | export const usePreferredProgrammingLanguage = () => useState('programming-language', () => '') -------------------------------------------------------------------------------- /docs/composables/useBreakpoints.ts: -------------------------------------------------------------------------------- 1 | const screens = { 2 | xs: 320, 3 | sm: 640, 4 | md: 768, 5 | lg: 1024, 6 | xl: 1280 7 | } 8 | 9 | const breakpoints = reactive({ w: 0, h: 0, is: 'xs' }) 10 | 11 | const xs = (val: number) => val >= screens.xs && val < screens.sm 12 | const sm = (val: number) => val >= screens.sm && val < screens.md 13 | const md = (val: number) => val >= screens.md && val < screens.lg 14 | const lg = (val: number) => val >= screens.lg && val < screens.xl 15 | const xl = (val: number) => val >= screens.xl 16 | 17 | const getBreakpoint = (w: number) => { 18 | if (xs(w)) return 'xs' 19 | else if (sm(w)) return 'sm' 20 | else if (md(w)) return 'md' 21 | else if (lg(w)) return 'lg' 22 | else if (xl(w)) return 'xl' 23 | else return 'all' 24 | } 25 | 26 | const setBreakpoint = () => { 27 | breakpoints.w = window.innerWidth 28 | breakpoints.h = window.innerHeight 29 | breakpoints.is = getBreakpoint(window.innerWidth) 30 | } 31 | 32 | const useBreakpoint = () => { 33 | onMounted(() => { 34 | setBreakpoint() 35 | window.addEventListener('resize', () => { 36 | setBreakpoint() 37 | }) 38 | }) 39 | 40 | return { 41 | breakpoints 42 | } 43 | } 44 | 45 | export default useBreakpoint -------------------------------------------------------------------------------- /docs/content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: marketing 3 | --- 4 | ::landing-hero 5 | :: 6 | 7 | ::landing-screenshot 8 | :: 9 | 10 | ::landing-decentralized 11 | :: 12 | 13 | ::landing-stack 14 | :: 15 | 16 | ::landing-signup 17 | :: 18 | 19 | ::marketing-footer 20 | :: -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "nuxt build", 5 | "dev": "nuxt dev", 6 | "generate": "nuxt generate", 7 | "preview": "nuxt preview", 8 | "postinstall": "nuxt prepare" 9 | }, 10 | "devDependencies": { 11 | "@headlessui/vue": "^1.7.8", 12 | "@nuxt/content": "^2.9.0", 13 | "@nuxtjs/color-mode": "^3.3.0", 14 | "@nuxtjs/plausible": "^0.2.3", 15 | "@nuxtjs/tailwindcss": "^6.9.4", 16 | "@tailwindcss/typography": "^0.5.9", 17 | "@vueuse/core": "^10.6.1", 18 | "@vueuse/nuxt": "^10.6.1", 19 | "nuxt": "^3.8.1", 20 | "sitemap": "^7.1.1", 21 | "surge": "^0.23.1" 22 | }, 23 | "dependencies": { 24 | "@docsearch/css": "^3.3.3", 25 | "@docsearch/js": "^3.3.3", 26 | "@nuxtjs/algolia": "^1.10.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/public/images/docs/install-synology/container-manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/docs/install-synology/container-manager.png -------------------------------------------------------------------------------- /docs/public/images/docs/install-synology/create-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/docs/install-synology/create-folder.png -------------------------------------------------------------------------------- /docs/public/images/docs/install-synology/create-project-completed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/docs/install-synology/create-project-completed.png -------------------------------------------------------------------------------- /docs/public/images/docs/install-synology/create-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/docs/install-synology/create-project.png -------------------------------------------------------------------------------- /docs/public/images/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/public/images/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/public/images/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/public/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/public/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /docs/public/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /docs/public/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/favicon.ico -------------------------------------------------------------------------------- /docs/public/images/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /docs/public/images/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/public/images/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /docs/public/images/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/icons/search-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/docker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/public/images/logos/github-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/tailwindcss.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/logos/x-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/public/images/social-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/social-image.png -------------------------------------------------------------------------------- /docs/public/images/ui/app-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/ui/app-screenshot.png -------------------------------------------------------------------------------- /docs/public/images/ui/credit-cards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/ui/credit-cards.png -------------------------------------------------------------------------------- /docs/public/images/ui/dan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/ui/dan.png -------------------------------------------------------------------------------- /docs/public/images/ui/full-screen-mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/ui/full-screen-mockup.png -------------------------------------------------------------------------------- /docs/public/images/ui/jay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/ui/jay.png -------------------------------------------------------------------------------- /docs/public/images/ui/light-accent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/docs/public/images/ui/light-accent.png -------------------------------------------------------------------------------- /docs/server/routes/sitemap.xml.ts: -------------------------------------------------------------------------------- 1 | import { serverQueryContent } from '#content/server' 2 | import { SitemapStream, streamToPromise } from 'sitemap' 3 | export default defineEventHandler(async (event) => { 4 | // Fetch all documents 5 | const docs = await serverQueryContent(event).find() 6 | const sitemap = new SitemapStream({ 7 | hostname: 'https://financialfreedom.app' 8 | }) 9 | 10 | for (const doc of docs) { 11 | sitemap.write({ 12 | url: doc._path, 13 | changefreq: 'monthly' 14 | }) 15 | } 16 | 17 | 18 | sitemap.end() 19 | return streamToPromise(sitemap) 20 | }) -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["resources/js/*"], 6 | "ziggy-js": ["./vendor/tightenco/ziggy"] 7 | } 8 | }, 9 | "exclude": ["node_modules", "public"] 10 | } 11 | -------------------------------------------------------------------------------- /modules_statuses.json: -------------------------------------------------------------------------------- 1 | { 2 | "Initialize": true, 3 | "Transaction": true 4 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite --host", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "@inertiajs/vue3": "^1.0.0", 10 | "@tailwindcss/forms": "^0.5.3", 11 | "@vitejs/plugin-basic-ssl": "^1.0.1", 12 | "@vitejs/plugin-vue": "^5.0.0", 13 | "autoprefixer": "^10.4.12", 14 | "axios": "^1.6.4", 15 | "laravel-vite-plugin": "^1.0.0", 16 | "postcss": "^8.4.31", 17 | "tailwindcss": "^3.2.1", 18 | "vite": "^5.0.0", 19 | "vue": "^3.4.0" 20 | }, 21 | "dependencies": { 22 | "@headlessui/vue": "^1.7.17", 23 | "@heroicons/vue": "^2.1.1", 24 | "@vueuse/core": "^10.7.1", 25 | "chart.js": "^4.4.1", 26 | "moment": "^2.30.1", 27 | "momentum-modal": "^0.2.2", 28 | "papaparse": "^5.4.1", 29 | "vue-chartjs": "^5.3.0", 30 | "zxcvbn": "^4.4.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/public/favicon.ico -------------------------------------------------------------------------------- /public/img/ui/institution-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/serversideup/financial-freedom/1c21c06ae0cd2ca597e6b272721db84de4775b34/public/img/ui/institution-placeholder.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @import "animations.css"; 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | -------------------------------------------------------------------------------- /resources/js/Components/Checkbox.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /resources/js/Components/DangerButton.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /resources/js/Components/Dropdown.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/DropdownLink.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/BudgetPlanIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/CashFlowIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/CoinsIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/DashboardIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/FolderIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/FolderModalIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/GoalsIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/LeftArrowIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/ModalCloseIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/RightArrowIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/SuccessNotificationIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/SwitchIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/TransactionsIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/TrashModalIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/Icons/UploadIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Components/InputError.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /resources/js/Components/InputLabel.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | -------------------------------------------------------------------------------- /resources/js/Components/NavLink.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /resources/js/Components/PrefixTextInput.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /resources/js/Components/PrimaryButton.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /resources/js/Components/ResponsiveNavLink.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /resources/js/Components/SecondaryButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /resources/js/Components/SuffixTextInput.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /resources/js/Components/TextInput.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /resources/js/Composables/useCategoryColor.js: -------------------------------------------------------------------------------- 1 | const colors = { 2 | 'green': '#16B364', 3 | 'purple': '#7A5AF8', 4 | 'orange': '#EF6820', 5 | 'gray': '#737373', 6 | 'blue': '#2E90FA', 7 | 'pink': '#EE46BC', 8 | 'red': '#F63D68', 9 | }; 10 | 11 | export const useCategoryColor = () => { 12 | const getCategoryColor = (categoryColor) => { 13 | return colors[categoryColor]; 14 | } 15 | 16 | return { 17 | getCategoryColor, 18 | } 19 | } -------------------------------------------------------------------------------- /resources/js/Composables/useDisplay.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | 3 | const minimizeSidebar = ref(false); 4 | 5 | export const useDisplay = () => { 6 | 7 | return { 8 | minimizeSidebar 9 | } 10 | } -------------------------------------------------------------------------------- /resources/js/Composables/useFormatters.js: -------------------------------------------------------------------------------- 1 | export const useFormatters = () => { 2 | const currency = new Intl.NumberFormat('en-US', { 3 | style: 'currency', 4 | currency: 'USD', 5 | 6 | // These options are needed to round to whole numbers if that's what you want. 7 | //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) 8 | //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) 9 | }); 10 | 11 | const percentage = new Intl.NumberFormat('en-US', { 12 | style: 'percent', 13 | minimumFractionDigits: 1, 14 | maximumFractionDigits: 3 15 | }); 16 | 17 | return { 18 | currency, 19 | percentage 20 | } 21 | } -------------------------------------------------------------------------------- /resources/js/Layouts/GuestLayout.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 19 | -------------------------------------------------------------------------------- /resources/js/Pages/Accounts/Partials/AccountSummary.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /resources/js/Pages/Accounts/Partials/NetWorth.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /resources/js/Pages/Cash/Show.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Pages/Dashboard/Index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /resources/js/Pages/Loans/Show.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/js/Pages/Settings/Partials/Navigation.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | import '../css/app.css'; 3 | 4 | import { createApp, h } from 'vue'; 5 | import { createInertiaApp } from '@inertiajs/vue3'; 6 | import { modal } from "momentum-modal" 7 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; 8 | import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m'; 9 | 10 | const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; 11 | 12 | createInertiaApp({ 13 | title: (title) => `${title} - ${appName}`, 14 | resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), 15 | setup({ el, App, props, plugin }) { 16 | return createApp({ render: () => h(App, props) }) 17 | .use(modal, { 18 | resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue")), 19 | }) 20 | .use(plugin) 21 | .use(ZiggyVue) 22 | .mount(el); 23 | }, 24 | progress: { 25 | color: '#4B5563', 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We'll load the axios HTTP library which allows us to easily issue requests 3 | * to our Laravel back-end. This library automatically handles sending the 4 | * CSRF token as a header based on the value of the "XSRF" token cookie. 5 | */ 6 | 7 | import axios from 'axios'; 8 | window.axios = axios; 9 | 10 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 11 | 12 | /** 13 | * Echo exposes an expressive API for subscribing to channels and listening 14 | * for events that are broadcast by Laravel. Echo and event broadcasting 15 | * allows your team to easily build robust real-time web applications. 16 | */ 17 | 18 | // import Echo from 'laravel-echo'; 19 | 20 | // import Pusher from 'pusher-js'; 21 | // window.Pusher = Pusher; 22 | 23 | // window.Echo = new Echo({ 24 | // broadcaster: 'pusher', 25 | // key: import.meta.env.VITE_PUSHER_APP_KEY, 26 | // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', 27 | // wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, 28 | // wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, 29 | // wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, 30 | // forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', 31 | // enabledTransports: ['ws', 'wss'], 32 | // }); 33 | -------------------------------------------------------------------------------- /resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ config('app.name', 'Laravel') }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @routes 16 | @vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"]) 17 | @inertiaHead 18 | 19 | 20 | @inertia 21 | 22 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 18 | return $request->user(); 19 | }); 20 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from 'tailwindcss/defaultTheme'; 2 | import forms from '@tailwindcss/forms'; 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: [ 7 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', 8 | './storage/framework/views/*.php', 9 | './resources/views/**/*.blade.php', 10 | './resources/js/**/*.vue', 11 | ], 12 | 13 | theme: { 14 | extend: { 15 | fontFamily: { 16 | sans: ['Inter', ...defaultTheme.fontFamily.sans], 17 | }, 18 | }, 19 | }, 20 | 21 | plugins: [forms], 22 | }; 23 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 18 | 19 | return $app; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Feature/Auth/PasswordConfirmationTest.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | $response = $this->actingAs($user)->get('/confirm-password'); 18 | 19 | $response->assertStatus(200); 20 | } 21 | 22 | public function test_password_can_be_confirmed(): void 23 | { 24 | $user = User::factory()->create(); 25 | 26 | $response = $this->actingAs($user)->post('/confirm-password', [ 27 | 'password' => 'password', 28 | ]); 29 | 30 | $response->assertRedirect(); 31 | $response->assertSessionHasNoErrors(); 32 | } 33 | 34 | public function test_password_is_not_confirmed_with_invalid_password(): void 35 | { 36 | $user = User::factory()->create(); 37 | 38 | $response = $this->actingAs($user)->post('/confirm-password', [ 39 | 'password' => 'wrong-password', 40 | ]); 41 | 42 | $response->assertSessionHasErrors(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | get('/register'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | 20 | public function test_new_users_can_register(): void 21 | { 22 | $response = $this->post('/register', [ 23 | 'name' => 'Test User', 24 | 'email' => 'test@example.com', 25 | 'password' => 'password', 26 | 'password_confirmation' => 'password', 27 | ]); 28 | 29 | $this->assertAuthenticated(); 30 | $response->assertRedirect(RouteServiceProvider::HOME); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | import vue from '@vitejs/plugin-vue'; 4 | import basicSsl from '@vitejs/plugin-basic-ssl' 5 | 6 | const env = loadEnv('', process.cwd()); 7 | 8 | const host = env.VITE_HOST; 9 | 10 | export default defineConfig({ 11 | plugins: [ 12 | basicSsl(), 13 | laravel({ 14 | input: 'resources/js/app.js', 15 | refresh: true, 16 | }), 17 | vue({ 18 | template: { 19 | transformAssetUrls: { 20 | base: null, 21 | includeAbsolute: false, 22 | }, 23 | }, 24 | }), 25 | ], 26 | server: { 27 | host, 28 | hmr: { 29 | host, 30 | clientPort: 443 31 | }, 32 | }, 33 | }); --------------------------------------------------------------------------------