1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-04 04:55:20 +02:00
This commit is contained in:
Josh Pigford 2024-02-07 11:11:53 -06:00
commit 754c506aad
18 changed files with 168 additions and 73 deletions

View file

@ -13,3 +13,8 @@ SMTP_PORT=465
SMTP_USERNAME= SMTP_USERNAME=
SMTP_PASSWORD= SMTP_PASSWORD=
TLS=true TLS=true
# Database Configuration
DB_HOST=localhost
POSTGRES_PASSWORD=postgres
POSTGRES_USER=postgres

7
.gitignore vendored
View file

@ -40,3 +40,10 @@
# Ignore Jetbrains IDEs # Ignore Jetbrains IDEs
.idea .idea
# Ignore macOS specific files
*/.DS_Store
.DS_Store
# Ignore .devcontainer files
compose-dev.yaml

View file

@ -2,12 +2,39 @@
It means so much that you're interested in contributing to Maybe! Seriously. Thank you. The entire community benefits from these contributions! It means so much that you're interested in contributing to Maybe! Seriously. Thank you. The entire community benefits from these contributions!
Before submitting a new issue or PR, check if it already exists in [issues](https://github.com/maybe-finance/maybe/issues) or [PRs](https://github.com/maybe-finance/maybe/pulls) so you have an idea of where things stand. ## House Rules
Then, once you're ready to begin work, submit a draft PR with your high-level plan (or the full solution). - Before contributing, please check if it already exists in [issues](https://github.com/maybe-finance/maybe/issues) or [PRs](https://github.com/maybe-finance/maybe/pulls)
- Given the speed at which we're moving on the codebase, we don't assign issues or "give" issues to anyone.
- When multiple PRs are submitted for the same issue, we take the one that most succinctly & efficiently solves a given problem and stays within the scope of work.
- Priority is generally given to previous committers as they've proven familiarity with the codebase and product.
Given the speed at which we're moving on the codebase, we don't assign issues or "give" issues to anyone. ## What should I contribute?
When multiple PRs are submitted for the same issue, we take the one that most succinctly & efficiently solves a given problem and stays within the scope of work. As we are still in the early days of this project, we recommend [heading over to the Wiki](https://github.com/maybe-finance/maybe/wiki) to get a better idea of _what_ to contribute.
Priority is also generally given to previous committers as they've proven familiarity with the codebase and product. In general, _full features_ that get us closer to [our Vision](https://github.com/maybe-finance/maybe/wiki/Vision) are the most valuable contributions at this stage.
## Development
### Setup
To get setup for local development, you have two options:
1. [Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers) with VSCode (see the `.devcontainer` folder)
2. Local Development
- [Mac Setup Guide](https://github.com/maybe-finance/maybe/wiki/Mac-Dev-Setup-Guide)
- [Linux Setup Guide](https://github.com/maybe-finance/maybe/wiki/Linux-Dev-Setup-Guide)
- [Windows Setup Guide](https://github.com/maybe-finance/maybe/wiki/Windows-Dev-Setup-Guide)
### Making a Pull Request
1. Fork the repo
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request, and be sure to check the [Allow edits from maintainers](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) option while creating your PR. This allows maintainers to collaborate with you on your PR if needed.
6. If possible, [link your pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) by adding the appropriate keyword (e.g. `fixes issue #XXX`)
7. Before requesting a review, please make sure that all [Github Checks](https://docs.github.com/en/rest/checks?apiVersion=2022-11-28) have passed and your branch is up-to-date with the `main` branch. After doing so, request a review and wait for a maintainer's approval.
All PRs should target the `main` branch.

View file

@ -20,7 +20,7 @@ gem "tailwindcss-rails"
# Hotwire # Hotwire
gem "stimulus-rails" gem "stimulus-rails"
gem "turbo-rails", github: "hotwired/turbo-rails", branch: "main" gem "turbo-rails"
# Other # Other
gem "bcrypt", "~> 3.1.7" gem "bcrypt", "~> 3.1.7"
@ -33,7 +33,7 @@ group :development, :test do
gem "debug", platforms: %i[ mri windows ] gem "debug", platforms: %i[ mri windows ]
gem "brakeman", require: false gem "brakeman", require: false
gem "rubocop-rails-omakase", require: false gem "rubocop-rails-omakase", require: false
gem "dotenv" gem "dotenv-rails"
gem "letter_opener" gem "letter_opener"
gem "i18n-tasks" gem "i18n-tasks"
end end

View file

@ -1,16 +1,6 @@
GIT
remote: https://github.com/hotwired/turbo-rails.git
revision: 3748512710a29b541a1f2b3863cc6fb2422fb7e2
branch: main
specs:
turbo-rails (2.0.0.pre.rc.2)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
GIT GIT
remote: https://github.com/rails/rails.git remote: https://github.com/rails/rails.git
revision: 554e5c2d8e8dd9f9e302a2d11c775f14c512d957 revision: f0d433bb46ac233ec7fd7fae48f458978908d905
branch: main branch: main
specs: specs:
actioncable (7.2.0.alpha) actioncable (7.2.0.alpha)
@ -143,6 +133,9 @@ GEM
irb (~> 1.10) irb (~> 1.10)
reline (>= 0.3.8) reline (>= 0.3.8)
dotenv (2.8.1) dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
drb (2.2.0) drb (2.2.0)
ruby2_keywords ruby2_keywords
erubi (1.12.0) erubi (1.12.0)
@ -352,6 +345,10 @@ GEM
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
thor (1.3.0) thor (1.3.0)
timeout (0.4.1) timeout (0.4.1)
turbo-rails (2.0.0)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0) unicode-display_width (2.5.0)
@ -384,7 +381,7 @@ DEPENDENCIES
brakeman brakeman
capybara capybara
debug debug
dotenv dotenv-rails
hotwire-livereload hotwire-livereload
i18n-tasks i18n-tasks
importmap-rails importmap-rails
@ -402,7 +399,7 @@ DEPENDENCIES
selenium-webdriver selenium-webdriver
stimulus-rails stimulus-rails
tailwindcss-rails tailwindcss-rails
turbo-rails! turbo-rails
tzinfo-data tzinfo-data
web-console web-console

View file

@ -1,2 +1,2 @@
web: bin/rails server web: bin/rails server -b 0.0.0.0
css: bin/rails tailwindcss:watch css: bin/rails tailwindcss:watch

View file

@ -26,22 +26,16 @@ We're now building the app in Ruby on Rails. We realize that's a controversial c
From the start our focus with this is to make it as easy as possible for you to both contribute to and deploy the app, and this move to Rails is a big part of that. From the start our focus with this is to make it as easy as possible for you to both contribute to and deploy the app, and this move to Rails is a big part of that.
## Codebase ## Local Development Setup
The codebase is vanilla [Rails](https://rubyonrails.org/) and [Postgres](https://www.postgresql.org/). Quite a simple setup. ### Requirements
## Setup - Ruby >3 (see `Gemfile`)
- PostgreSQL >9.3 (ideally, latest stable version)
You'll need: After cloning the repo, the basic setup commands are:
- ruby >3 (specific version is in `Gemfile`) ```sh
- postgresql (if using stock `config/database.yml`)
If you prefer devcontainer, this project supports it (entirely optional).
Run the following commands after cloning the repo:
```shell
cd maybe cd maybe
cp .env.example .env cp .env.example .env
bundle install bundle install
@ -49,33 +43,48 @@ rails db:setup
bin/dev bin/dev
``` ```
And visit [http://localhost:3000](http://localhost:3000) And visit http://localhost:3000 to see the app. You can use the following credentials to log in (generated by DB seed):
### Email Email: user@maybe.local
Password: password
In development, we use `letter_opener` to automatically open emails in your browser. However, if you self-host, you'll likely want some basic email sending abilities. For further instructions, see guides below.
You can use any SMTP-based mail service and then simply drop in your SMTP credentials in the `.env` file. ### Setup Guides
[Resend](https://resend.com) is a great option for personal use as they have a very generous free plan. #### Dev Container (optional)
This is 100% optional and meant for devs who don't want to worry about installing requirements manually for their platform. You can follow [this guide](https://code.visualstudio.com/docs/devcontainers/containers) to learn more about Dev Containers.
#### Mac
Please visit our [Mac dev setup guide](https://github.com/maybe-finance/maybe/wiki/Mac-Dev-Setup-Guide).
#### Linux
Please visit our [Linux dev setup guide](https://github.com/maybe-finance/maybe/wiki/Linux-Dev-Setup-Guide).
#### Windows
Please visit our [Windows dev setup guide](https://github.com/maybe-finance/maybe/wiki/Windows-Dev-Setup-Guide).
### Testing Emails
In development, we use `letter_opener` to automatically open emails in your browser. When an email sends locally, a new browser tab will open with a preview.
## Contributing ## Contributing
Before contributing, you'll likely find it helpful to [understand context and general vision/direction](https://github.com/maybe-finance/maybe/wiki). Before contributing, you'll likely find it helpful to [understand context and general vision/direction](https://github.com/maybe-finance/maybe/wiki).
It's still very early days for this so your mileage will vary here and lots of things will break. Once you've done that, please visit our [contributing guide](https://github.com/maybe-finance/maybe/blob/main/CONTRIBUTING.md) to get started!
But almost any contribution will be beneficial at this point. Check the [current Issues](https://github.com/maybe-finance/maybe/issues) to see where you can jump in! ## Self Hosting
If you've got an improvement, just send in a pull request! Our long term goal is to make self-hosting as easy as possible. That said, during these early stages of building the product, we are focusing our efforts on development.
1. Fork it We will update this section as we get closer to an initial release.
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
If you've got feature ideas, simply [open a new issue](https://github.com/maybe-finance/maybe/issues/new)! Please see our [guide on self hosting here](https://github.com/maybe-finance/maybe/wiki/Self-Hosting-Setup-Guide).
## Repo Activity ## Repo Activity

View file

@ -1,2 +1,5 @@
module AccountsHelper module AccountsHelper
def to_accountable_title(accountable)
accountable.model_name.human
end
end end

View file

@ -1,3 +1,15 @@
class Account::Investment < ApplicationRecord class Account::Investment < ApplicationRecord
include Accountable include Accountable
SUBTYPES = [
[ "Brokerage", "brokerage" ],
[ "Pension", "pension" ],
[ "Retirement", "retirement" ],
[ "401(k)", "401k" ],
[ "529 plan", "529_plan" ],
[ "Health Savings Account", "hsa" ],
[ "Mutual Fund", "mutual_fund" ],
[ "Roth IRA", "roth_ira" ],
[ "Roth 401k", "roth_401k" ]
].freeze
end end

View file

@ -0,0 +1,4 @@
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<label for="account_name" class="block text-sm font-medium opacity-50 focus-within:opacity-100">Type</label>
<%= f.select :subtype, options_for_select(Account::Investment::SUBTYPES, selected: ""), {}, class: "block w-full p-0 mt-1 bg-transparent border-none focus:outline-none focus:ring-0" %>
</div>

View file

@ -3,14 +3,14 @@
<h3 class="mt-1 mb-4 text-sm text-gray-500"><%#= number_to_currency Current.family.cash_balance %></h3> <h3 class="mt-1 mb-4 text-sm text-gray-500"><%#= number_to_currency Current.family.cash_balance %></h3>
<% Current.family.accounts.each do |account| %> <% Current.family.accounts.each do |account| %>
<div class="flex items-center justify-between px-3 py-3 mb-2 bg-white shadow-sm rounded-xl"> <div class="flex flex-row items-center justify-between px-3 py-3 mb-2 bg-white shadow-sm rounded-xl">
<div class="flex items-center text-sm"> <div class="flex w-1/3 items-center text-sm">
<%= account.name %> <%= account.name %>
</div> </div>
<div class="flex items-center text-sm"> <div class="flex w-1/3 items-center text-sm">
<%= account.accountable.model_name.human %> <%= to_accountable_title(account.accountable) %>
</div> </div>
<p class="text-sm text-right"> <p class="flex w-1/3 text-sm text-right">
<span class="block mb-1"><%= humanized_money_with_symbol account.balance %></span> <span class="block mb-1"><%= humanized_money_with_symbol account.balance %></span>
</p> </p>
</div> </div>

View file

@ -56,8 +56,10 @@
</nav> </nav>
<div class="flex flex-col mt-6"> <div class="flex flex-col mt-6">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-xs"><%= t('.accounts') %></span> <%= link_to accounts_path, class: 'text-xs' do%>
<%= link_to new_account_path, class: 'block hover:bg-gray-100 p-2 text-sm font-semibold text-gray-900 flex items-center rounded', title: t('.new_accoount') do %> <%= t('.accounts') %>
<% end %>
<%= link_to new_account_path, class: 'block hover:bg-gray-100 p-2 text-sm font-semibold text-gray-900 flex items-center rounded', title: t('.new_account') do %>
<%= inline_svg_tag('icon-add.svg', class: 'text-gray-500 fill-current') %> <%= inline_svg_tag('icon-add.svg', class: 'text-gray-500 fill-current') %>
<% end %> <% end %>
</div> </div>

View file

@ -3,34 +3,34 @@
<%= form_with model: Current.user, url: settings_path, html: { class: "space-y-4" } do |form| %> <%= form_with model: Current.user, url: settings_path, html: { class: "space-y-4" } do |form| %>
<%= form.fields_for :family_attributes do |family_fields| %> <%= form.fields_for :family_attributes do |family_fields| %>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100"> <div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= family_fields.label :name, "Family name", class: "block text-sm font-medium opacity-50 focus-within:opacity-100" %> <%= family_fields.label :name, "Family name", class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= family_fields.text_field :name, placeholder: "Family name", value: Current.family.name, class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %> <%= family_fields.text_field :name, placeholder: "Family name", value: Current.family.name, class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div> </div>
<% end %> <% end %>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100"> <div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :first_name, class: "block text-sm font-medium opacity-50 focus-within:opacity-100" %> <%= form.label :first_name, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.text_field :first_name, placeholder: "First name", value: Current.user.first_name, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %> <%= form.text_field :first_name, placeholder: "First name", value: Current.user.first_name, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div> </div>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100"> <div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :last_name, class: "block text-sm font-medium opacity-50 focus-within:opacity-100" %> <%= form.label :last_name, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.text_field :last_name, placeholder: "Last name", value: Current.user.last_name, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %> <%= form.text_field :last_name, placeholder: "Last name", value: Current.user.last_name, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div> </div>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100"> <div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :email, class: "block text-sm font-medium opacity-50 focus-within:opacity-100" %> <%= form.label :email, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.email_field :email, placeholder: "Email", value: Current.user.email, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %> <%= form.email_field :email, placeholder: "Email", value: Current.user.email, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div> </div>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100"> <div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password, class: "block text-sm font-medium opacity-50 focus-within:opacity-100" %> <%= form.label :password, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.password_field :password, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %> <%= form.password_field :password, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div> </div>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100"> <div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password_confirmation, class: "block text-sm font-medium opacity-50 focus-within:opacity-100" %> <%= form.label :password_confirmation, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.password_field :password_confirmation, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %> <%= form.password_field :password_confirmation, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div> </div>

View file

@ -5,7 +5,7 @@ en:
accounts: Accounts accounts: Accounts
cash: Cash cash: Cash
dashboard: Dashboard dashboard: Dashboard
new_accoount: New Account new_account: New Account
auth: auth:
or: or or: or
privacy_policy: Privacy Policy privacy_policy: Privacy Policy

View file

@ -6,7 +6,7 @@ class ReplaceMoneyField < ActiveRecord::Migration[7.2]
Account.reset_column_information Account.reset_column_information
Account.find_each do |account| Account.find_each do |account|
account.update_columns(balance_cents: Money.from_amount(account.balance, account.currency).cents) account.update_columns(balance_cents: Money.from_amount(account.balance_in_database, account.currency).cents)
end end
remove_column :accounts, :balance remove_column :accounts, :balance

View file

@ -7,3 +7,11 @@
# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| # ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
# MovieGenre.find_or_create_by!(name: genre_name) # MovieGenre.find_or_create_by!(name: genre_name)
# end # end
# Create the default user
family = Family.create_or_find_by!(name: "The Maybe Family")
puts "Family created: #{family.name}"
user = User.create_or_find_by!(
first_name: "Josh", last_name: "Maybe", email: "user@maybe.local",
password: "password", password_confirmation: "password", family_id: family.id)
puts "User created: #{user.email} for family: #{family.name}"

View file

@ -0,0 +1,19 @@
require "test_helper"
class ApplicationHelperTest < ActionView::TestCase
test "#title(page_title)" do
title("Test Title")
assert_equal "Test Title", content_for(:title)
end
test "#header_title(page_title)" do
header_title("Test Header Title")
assert_equal "Test Header Title", content_for(:header_title)
end
test "#permitted_accountable_partial(accountable_type)" do
assert_equal "account", permitted_accountable_partial("Account")
assert_equal "user", permitted_accountable_partial("User")
assert_equal "admin_user", permitted_accountable_partial("AdminUser")
end
end

View file

@ -5,14 +5,16 @@ class AccountsTest < ApplicationSystemTestCase
sign_in @user = users(:bob) sign_in @user = users(:bob)
end end
# test "should create account" do test "should create account" do
# click_on "New account" skip("Disabling this test for now, UI is changing to quickly to do systems testing")
# click_on "Credit Card"
# within "form" do click_on "New account"
# fill_in "Name", with: "VISA" click_on "Credit Card"
# fill_in "Balance", with: "1000" within "form" do
# click_on "Submit" fill_in "Name", with: "VISA"
# end fill_in "Balance", with: "1000"
# assert_text "$1,000" click_on "Submit"
# end end
assert_text "$1,000"
end
end end