diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml new file mode 100644 index 0000000..711d62e --- /dev/null +++ b/.github/workflows/github-actions-demo.yml @@ -0,0 +1,17 @@ +name: GitHub Actions Demo +on: [push] +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + - name: Check out repository code + uses: actions/checkout@v2 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + - run: echo "🖥️ The workflow is now ready to test your code on the runner." + - name: List files in the repository + run: | + ls ${{ github.workspace }} + - run: echo "🍏 This job's status is ${{ job.status }}." diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..0a9fcf5 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,62 @@ +AllCops: + NewCops: enable + Exclude: + - "db/**/*" + - "bin/*" + - "config/**/*" + - "Guardfile" + - "Rakefile" + - "node_modules/**/*" + + DisplayCopNames: true + +Layout/LineLength: + Max: 120 +Metrics/MethodLength: + Include: + - "app/controllers/*" + - "app/models/*" + Max: 20 +Metrics/AbcSize: + Include: + - "app/controllers/*" + - "app/models/*" + Max: 50 +Metrics/ClassLength: + Max: 150 +Metrics/BlockLength: + IgnoredMethods: ['describe'] + Max: 60 + +Style/Documentation: + Enabled: false +Style/ClassAndModuleChildren: + Enabled: false +Style/EachForSimpleLoop: + Enabled: false +Style/AndOr: + Enabled: false +Style/DefWithParentheses: + Enabled: false +Style/FrozenStringLiteralComment: + EnforcedStyle: never + +Layout/HashAlignment: + EnforcedColonStyle: key +Layout/ExtraSpacing: + AllowForAlignment: false +Layout/MultilineMethodCallIndentation: + Enabled: true + EnforcedStyle: indented +Lint/RaiseException: + Enabled: false +Lint/StructNewOverride: + Enabled: false +Style/HashEachMethods: + Enabled: false +Style/HashTransformKeys: + Enabled: false +Style/HashTransformValues: + Enabled: false +Layout/EndOfLine: + Enabled: False \ No newline at end of file diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..6b53590 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,20 @@ +{ + "extends": [ + "stylelint-config-standard" + ], + "plugins": [ + "stylelint-scss", + "stylelint-csstree-validator" + ], + "rules": { + "at-rule-no-unknown": null, + "scss/at-rule-no-unknown": true, + "csstree/validator": true + }, + "ignoreFiles": [ + "build/**", + "dist/**", + "**/reset*.css", + "**/bootstrap*.css" + ] + } \ No newline at end of file diff --git a/Gemfile b/Gemfile index 06650fe..6296150 100644 --- a/Gemfile +++ b/Gemfile @@ -23,22 +23,30 @@ gem 'jbuilder', '~> 2.7' # gem 'bcrypt', '~> 3.1.7' # Use Active Storage variant -# gem 'image_processing', '~> 1.2' +gem 'image_processing', '~> 1.2' # Reduces boot times through caching; required in config/boot.rb gem 'bootsnap', '>= 1.4.4', require: false +# Include images using gravaar +gem 'gravatar_image_tag', '~> 1.2' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'byebug', platforms: %i[mri mingw x64_mingw] + # Rspec to test rails applications + gem 'rspec-rails', '~> 3.4', '>= 3.4.2' end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem 'web-console', '>= 4.1.0' + + gem 'listen', '~> 3.3' # Display performance information such as SQL time and flame graphs for each request in your browser. # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md gem 'rack-mini-profiler', '~> 2.0' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' end group :test do @@ -50,4 +58,4 @@ group :test do end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 305e97a..93af7d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,14 +78,22 @@ GEM childprocess (3.0.0) concurrent-ruby (1.1.9) crass (1.0.6) + diff-lcs (1.4.4) erubi (1.10.0) ffi (1.15.3-x64-mingw32) globalid (0.4.2) activesupport (>= 4.2.0) + gravatar_image_tag (1.2.0) i18n (1.8.10) concurrent-ruby (~> 1.0) + image_processing (1.12.1) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) jbuilder (2.11.2) activesupport (>= 5.0.0) + listen (3.6.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) loofah (2.10.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) @@ -93,6 +101,7 @@ GEM mini_mime (>= 0.1.1) marcel (1.0.1) method_source (1.0.0) + mini_magick (4.11.0) mini_mime (1.1.0) minitest (5.14.4) msgpack (1.4.2) @@ -137,7 +146,29 @@ GEM rake (>= 0.13) thor (~> 1.0) rake (13.0.6) + rb-fsevent (0.11.0) + rb-inotify (0.10.1) + ffi (~> 1.0) regexp_parser (2.1.1) + rspec-core (3.9.3) + rspec-support (~> 3.9.3) + rspec-expectations (3.9.4) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.9.0) + rspec-mocks (3.9.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.9.0) + rspec-rails (3.9.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.9.0) + rspec-expectations (~> 3.9.0) + rspec-mocks (~> 3.9.0) + rspec-support (~> 3.9.0) + rspec-support (3.9.4) + ruby-vips (2.1.2) + ffi (~> 1.12) rubyzip (2.3.2) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) @@ -153,6 +184,7 @@ GEM childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) semantic_range (3.0.0) + spring (2.1.1) sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -198,12 +230,17 @@ DEPENDENCIES bootsnap (>= 1.4.4) byebug capybara (>= 3.26) + gravatar_image_tag (~> 1.2) + image_processing (~> 1.2) jbuilder (~> 2.7) + listen (~> 3.3) puma (~> 5.0) rack-mini-profiler (~> 2.0) rails (~> 6.1.3, >= 6.1.3.2) + rspec-rails (~> 3.4, >= 3.4.2) sass-rails (>= 6) selenium-webdriver + spring sqlite3 (~> 1.4) turbolinks (~> 5) tzinfo-data diff --git a/README.md b/README.md index 7db80e4..a472fa8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1 @@ -# README - -This README would normally document whatever steps are necessary to get the -application up and running. - -Things you may want to cover: - -* Ruby version - -* System dependencies - -* Configuration - -* Database creation - -* Database initialization - -* How to run the test suite - -* Services (job queues, cache servers, search engines, etc.) - -* Deployment instructions - -* ... +# Group by Transactions Software \ No newline at end of file diff --git a/app/assets/images/default_profile.jpeg b/app/assets/images/default_profile.jpeg new file mode 100644 index 0000000..4eb5022 Binary files /dev/null and b/app/assets/images/default_profile.jpeg differ diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index d05ea0f..bcd61d3 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,15 +1,74 @@ /* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's - * vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS - * files in this directory. Styles in this file should be added after the last require_* statement. - * It is generally better to create a new file per style scope. - * - *= require_tree . - *= require_self - */ +* This is a manifest file that'll be compiled into application.css, which will include all the files +* listed below. +* +* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's +* vendor/assets/stylesheets directory can be referenced here using a relative path. +* +* You're free to add application-wide styles to this file and they'll appear at the bottom of the +* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS +* files in this directory. Styles in this file should be added after the last require_* statement. +* It is generally better to create a new file per style scope. +* +*= require_tree . +*= require_self +*/ + +body { + background-image: linear-gradient(to top, #fff1eb 0%, #ace0f9 100%); + background-repeat: no-repeat; + min-height: 100vh; +} + +a:hover { + opacity: 50%; + background: transparent; +} + +.navbar { + border-radius: 16px; + background-color: rgba(255, 255, 255, 0.34); +} + +.alert, +.notice { + font-size: 16px; + padding: 1rem; + border-radius: 36px; + color: #fff; + font-weight: 500; + letter-spacing: 1px; +} + +.alert { + background-image: linear-gradient(to top, #c571f5b4 0%, #fa71ccad 100%); +} + +.notice { + background-image: linear-gradient(to top, #0ba361b4 0%, #3cba92a3 100%); +} + +.container-show { + background-image: linear-gradient(120deg, #89f6fe9c 0%, #66a6ffb6 100%); + padding: 3rem 2rem; + border-radius: 36px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-width: 300px; + max-width: 600px; + margin: 0 auto; +} + +.container-show p { + font-size: 22px; + margin: 1.5rem 0; +} + +.container-show a, +.container-show a:visited { + margin: 1rem; + text-decoration: none; + color: royalblue; +} diff --git a/app/assets/stylesheets/groups.scss b/app/assets/stylesheets/groups.scss new file mode 100644 index 0000000..a075b54 --- /dev/null +++ b/app/assets/stylesheets/groups.scss @@ -0,0 +1,96 @@ +// Place all the styles related to the Groups controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ + +.card-group { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + flex-wrap: wrap; + min-width: 300px; + max-width: 600px; + margin: 0 auto; + padding: 6rem 2rem; + border: 1px solid rgb(154, 154, 154); + border-radius: 2rem; + background-color: rgba(255, 255, 255, 0.14); + + label { + display: block; + text-align: center; + margin: 1rem 0; + font-size: 18px; + } + + input { + min-width: 250px; + padding: 0.25rem 1rem; + border: 1px solid #aaa; + border-radius: 2rem; + font-size: 18px; + } + + a { + margin: 1rem; + width: 32px; + width: 100%; + text-align: center; + color: #5bb; + text-decoration: none; + } + + a:visited { + width: 100%; + text-align: center; + color: #5bb; + text-decoration: none; + } + + div { + display: block; + width: 100%; + text-align: center; + } +} + +.groups-container { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + + .group { + display: block; + padding: 2rem; + border-radius: 36px; + margin: 1rem; + text-align: center; + background-image: linear-gradient(120deg, #89f6fe9c 0%, #66a6ffb6 100%); + + h3 { + color: #555; + } + + a { + color: #388; + text-decoration: none; + + :visited { + color: #388; + } + } + + img { + width: 64px; + height: 64px; + margin-bottom: 1rem; + } + } +} + +textarea:focus, +input:focus, +select:focus { + outline: none; + border: 1px solid #5bb; +} diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss new file mode 100644 index 0000000..1031a7a --- /dev/null +++ b/app/assets/stylesheets/home.scss @@ -0,0 +1,45 @@ +// Place all the styles related to the home controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ + +.links-card { + margin: 2rem; + background-image: linear-gradient(120deg, #89f6fe9c 0%, #66a6ffb6 100%); + padding: 3rem 2rem; + border-radius: 36px; + + a { + font-size: 16px; + } + + li { + list-style-type: none; + width: 100%; + text-align: center; + } + + li:hover { + background-color: rgba(255, 255, 255, 0.34); + border-radius: 36px; + color: #2d558e; + + a { + color: #2d558e; + opacity: 100%; + } + } +} + +h1, +p { + text-align: center; + color: #777; +} + +img { + border-radius: 36px; + width: 150px; + height: 150px; + margin: 0 auto; + display: block; +} diff --git a/app/assets/stylesheets/investments.scss b/app/assets/stylesheets/investments.scss new file mode 100644 index 0000000..2a07f64 --- /dev/null +++ b/app/assets/stylesheets/investments.scss @@ -0,0 +1,88 @@ +// Place all the styles related to the Investments controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ + +.link-container { + a { + display: block; + text-align: center; + margin: 0 30%; + } +} + +.content-investments { + display: flex; + justify-content: space-evenly; + flex-wrap: wrap; +} + +.card-form-investment { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 4rem 2rem; + border: 1px solid rgb(154, 154, 154); + border-radius: 2rem; + background-color: rgba(255, 255, 255, 0.14); + min-width: 300px; + max-width: 550px; + margin: 0 auto; + + label { + display: block; + text-align: center; + margin: 1rem 0; + font-size: 18px; + } + + input, + select { + min-width: 250px; + padding: 0.25rem 1rem; + border: 1px solid #aaa; + border-radius: 2rem; + font-size: 18px; + } + + a { + width: 32px; + } +} + +.card-investment { + background-color: rgba($color: #fff, $alpha: 0.35); + padding: 2rem; + margin: 1rem; + border: 1px solid #aaa; + border-radius: 36px; + min-width: 200px; + text-align: center; + + a, + a:visited { + text-decoration: none; + color: #599; + display: inline; + margin: 0; + } + + :last-child::before { + content: " | "; + color: #599; + margin: 0 0.75rem; + } + + a:hover { + color: #266; + opacity: 100%; + } + + h3 { + color: #777; + } + + h4 { + color: #555; + } +} diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000..5d95461 --- /dev/null +++ b/app/assets/stylesheets/scaffolds.scss @@ -0,0 +1,84 @@ +body { + background-color: #fff; + color: #333; + margin: 33px; +} + +body, +p, +ol, +ul, +td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; +} + +a:visited { + color: #666; +} + +a:hover { + color: #fff; + background-color: #000; +} + +th { + padding-bottom: 5px; +} + +td { + padding: 0 5px 7px; +} + +div.field, +div.actions { + margin-bottom: 10px; +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px 7px 0; + margin-bottom: 20px; + background-color: #f0f0f0; +} + +#error_explanation h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px -7px 0; + background-color: #c00; + color: #fff; +} + +#error_explanation ul li { + font-size: 12px; + list-style: square; +} + +label { + display: block; +} diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 0000000..f986bd4 --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..d37ad70 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,44 @@ +// Place all the styles related to the Users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ + +.card-auth { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + padding: 6rem 2rem; + border: 1px solid rgb(154, 154, 154); + border-radius: 2rem; + background-color: rgba(255, 255, 255, 0.14); + + label { + display: block; + text-align: center; + margin: 1rem 0; + font-size: 18px; + } + + input { + min-width: 250px; + max-width: 300px; + padding: 0.25rem 1rem; + border: 1px solid #aaa; + border-radius: 2rem; + font-size: 18px; + } + + .field { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } +} + +textarea:focus, +input:focus { + outline: none; + border: 1px solid #5bb; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d1..bcbc6be 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,11 @@ class ApplicationController < ActionController::Base + helper_method :current_user + + def current_user + if session[:user_id] + @current_user ||= User.find(session[:user_id]) + else + @current_user = nil + end + end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb new file mode 100644 index 0000000..c28395f --- /dev/null +++ b/app/controllers/groups_controller.rb @@ -0,0 +1,85 @@ +class GroupsController < ApplicationController + before_action :set_group, only: %i[show edit update destroy] + before_action :authenticated, only: %i[edit update show destory new index] + + # GET /groups or /groups.json + def index + @groups = Group.all + end + + # GET /groups/1 or /groups/1.json + def show + @investments = Investment.all.where('user_id = ? AND group_id = ?', current_user.id, @group.id) + end + + # GET /groups/new + def new + @group = Group.new + end + + # GET /groups/1/edit + def edit; end + + # POST /groups or /groups.json + def create + @group = Group.new(group_params) + @group.user_id = current_user.id + + respond_to do |format| + if @group.save + format.html { redirect_to @group, notice: 'Group was successfully created.' } + format.json { render :show, status: :created, location: @group } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @group.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /groups/1 or /groups/1.json + def update + respond_to do |format| + if @group.update(group_params) + format.html { redirect_to @group, notice: 'Group was successfully updated.' } + format.json { render :show, status: :ok, location: @group } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @group.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /groups/1 or /groups/1.json + def destroy + @group.destroy + respond_to do |format| + format.html { redirect_to groups_url, notice: 'Group was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + + helper_method :logged_in? + + def logged_in? + !current_user.nil? + end + + def authenticated + return if logged_in? + + flash[:alert] = 'You need to login or sign up to access' + redirect_to '/login' + end + + # Use callbacks to share common setup or constraints between actions. + def set_group + @group = Group.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def group_params + params.require(:group).permit(:name, :user_id, :icon) + end +end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..71f11c3 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,22 @@ +class HomeController < ApplicationController + before_action :authenticated, only: %i[index] + + def index + Group.create(id: 5, name: 'No group', user_id: 1) if Group.all.where(id: 5).first.nil? + end + + private + + helper_method :logged_in? + + def logged_in? + !current_user.nil? + end + + def authenticated + return if logged_in? + + flash[:alert] = 'You need to login or sign up to access' + redirect_to '/login' + end +end diff --git a/app/controllers/investments_controller.rb b/app/controllers/investments_controller.rb new file mode 100644 index 0000000..d08295b --- /dev/null +++ b/app/controllers/investments_controller.rb @@ -0,0 +1,89 @@ +class InvestmentsController < ApplicationController + before_action :set_investment, only: %i[show edit update destroy] + before_action :authenticated, only: %i[edit update destroy home show new index] + + # GET /investments or /investments.json + def index + @investments = Investment.all + end + + def index_external + @investments = Investment.all + end + + # GET /investments/1 or /investments/1.json + def show; end + + # GET /investments/new + def new + @investment = Investment.new + end + + # GET /investments/1/edit + def edit; end + + # POST /investments or /investments.json + def create + @investment = Investment.new(investment_params) + @investment.group_id = Group.all.where('name = ?', params['investment']['group_id']).select(:id).first.id + + @investment.user_id = current_user.id + + respond_to do |format| + if @investment.save + format.html { redirect_to @investment, notice: 'Investment was successfully created.' } + format.json { render :show, status: :created, location: @investment } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @investment.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /investments/1 or /investments/1.json + def update + respond_to do |format| + if @investment.update(investment_params) + format.html { redirect_to @investment, notice: 'Investment was successfully updated.' } + format.json { render :show, status: :ok, location: @investment } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @investment.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /investments/1 or /investments/1.json + def destroy + @investment.destroy + respond_to do |format| + format.html { redirect_to investments_url, notice: 'Investment was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + + helper_method :logged_in? + + def logged_in? + !current_user.nil? + end + + def authenticated + return if logged_in? + + flash[:alert] = 'You need to login or sign up to access' + redirect_to '/login' + end + + # Use callbacks to share common setup or constraints between actions. + def set_investment + @investment = Investment.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def investment_params + params.require(:investment).permit(:crypto, :amount, :user_id) + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..53cffed --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,19 @@ +class SessionsController < ApplicationController + def new; end + + def create + user = User.find_by_name(params[:name]) + if user + session[:user_id] = user.id + redirect_to root_url, notice: 'Logged in!' + else + flash.now[:alert] = 'Account is invalid' + render 'new' + end + end + + def destroy + session[:user_id] = nil + redirect_to login_path, notice: 'Logged out!' + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..0df6a7b --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,68 @@ +class UsersController < ApplicationController + before_action :set_user, only: %i[show edit update destroy] + + # GET /users or /users.json + def index + @users = User.all + end + + # GET /users/1 or /users/1.json + def show; end + + # GET /users/new + def new + @user = User.new + end + + # GET /users/1/edit + def edit; end + + # POST /users or /users.json + def create + @user = User.new(user_params) + + respond_to do |format| + if @user.save + format.html { redirect_to @user, notice: 'User was successfully created.' } + format.json { render :show, status: :created, location: @user } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /users/1 or /users/1.json + def update + respond_to do |format| + if @user.update(user_params) + format.html { redirect_to @user, notice: 'User was successfully updated.' } + format.json { render :show, status: :ok, location: @user } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @user.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /users/1 or /users/1.json + def destroy + @user.destroy + respond_to do |format| + format.html { redirect_to users_url, notice: 'User was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_user + @user = User.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def user_params + params.require(:user).permit(:name, :avatar) + end +end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb new file mode 100644 index 0000000..c091b2f --- /dev/null +++ b/app/helpers/groups_helper.rb @@ -0,0 +1,2 @@ +module GroupsHelper +end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 0000000..23de56a --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/helpers/investments_helper.rb b/app/helpers/investments_helper.rb new file mode 100644 index 0000000..92f9c27 --- /dev/null +++ b/app/helpers/investments_helper.rb @@ -0,0 +1,2 @@ +module InvestmentsHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..309f8b2 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/models/group.rb b/app/models/group.rb new file mode 100644 index 0000000..7b7ab77 --- /dev/null +++ b/app/models/group.rb @@ -0,0 +1,7 @@ +class Group < ApplicationRecord + belongs_to :user + + has_many :investments + + has_one_attached :icon +end diff --git a/app/models/investment.rb b/app/models/investment.rb new file mode 100644 index 0000000..b942996 --- /dev/null +++ b/app/models/investment.rb @@ -0,0 +1,4 @@ +class Investment < ApplicationRecord + belongs_to :user + has_and_belongs_to_many :groups +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..96f64c4 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,8 @@ +class User < ApplicationRecord + validates :name, presence: true, uniqueness: true + + has_many :investments + has_many :groups + + has_one_attached :avatar +end diff --git a/app/views/groups/_form.html.erb b/app/views/groups/_form.html.erb new file mode 100644 index 0000000..186e0f6 --- /dev/null +++ b/app/views/groups/_form.html.erb @@ -0,0 +1,27 @@ +<%= form_with(model: @group) do |form| %> + <% if @group.errors.any? %> +
+

<%= pluralize(@group.errors.count, "error") %> Prohibited this group from being saved:

+ + +
+ <% end %> + +
+ <%= form.label :name, "Name for the new group" %> + <%= form.text_field :name %> +
+ +
+ <%= form.label :icon, "Upload an icon for the group" %> + <%= form.file_field :icon %> +
+ +
+ <%= form.submit class: "btn btn-outline-primary" %> +
+<% end %> diff --git a/app/views/groups/create.html.erb b/app/views/groups/create.html.erb new file mode 100644 index 0000000..dad7c51 --- /dev/null +++ b/app/views/groups/create.html.erb @@ -0,0 +1,3 @@ +

Groups#create

+

Find me in app/views/groups/create.html.erb

+ diff --git a/app/views/groups/destroy.html.erb b/app/views/groups/destroy.html.erb new file mode 100644 index 0000000..0bf1bdc --- /dev/null +++ b/app/views/groups/destroy.html.erb @@ -0,0 +1,2 @@ +

Groups#destroy

+

Find me in app/views/groups/destroy.html.erb

diff --git a/app/views/groups/edit.html.erb b/app/views/groups/edit.html.erb new file mode 100644 index 0000000..e43d808 --- /dev/null +++ b/app/views/groups/edit.html.erb @@ -0,0 +1,10 @@ +

Editing Group

+ +
+ <%= render 'form', user: @group %> + +
+ <%= link_to 'Show', @group %> | + <%= link_to 'Groups', groups_path %> +
+
\ No newline at end of file diff --git a/app/views/groups/index.html.erb b/app/views/groups/index.html.erb new file mode 100644 index 0000000..2f36b52 --- /dev/null +++ b/app/views/groups/index.html.erb @@ -0,0 +1,25 @@ +

Groups

+ +<%= link_to 'New Group', new_group_path, class: "d-block text-center btn btn-outline-primary w-50 mx-auto my-5" %> + +
+ +<% @groups.all.where("id != 5").order("created_at DESC").each do |group| %> + +
+ + <% if group.icon.persisted? %> + <%= link_to image_tag(group.icon), groups_path %> + <% else %> + <%= link_to image_tag("default_profile.jpeg"), groups_path %> + <% end %> + +

<%= group.name %>

+

<%= group.created_at.to_s(:long) %>

+ + <%= link_to 'Show', group %> | + <%= link_to 'Edit', edit_group_path(group) %> +
+<% end %> + +
diff --git a/app/views/groups/new.html.erb b/app/views/groups/new.html.erb new file mode 100644 index 0000000..649e1a0 --- /dev/null +++ b/app/views/groups/new.html.erb @@ -0,0 +1,9 @@ +

Creating Group

+ +
+ +<%= render 'form', group: @group %> + +<%= link_to 'Back', groups_path %> + +
\ No newline at end of file diff --git a/app/views/groups/show.html.erb b/app/views/groups/show.html.erb new file mode 100644 index 0000000..54c9629 --- /dev/null +++ b/app/views/groups/show.html.erb @@ -0,0 +1,32 @@ +

Single Group

+ +
+

+ Group: + <%= @group.name %> +

+
<%= @group.created_at.to_s(:long) %>
+
+<%= link_to 'Edit', edit_group_path(@group) %> | +<%= link_to 'Home', root_path %> +
+
+ +

Group investments:

+ +
+ +<% @investments.each do |investment| %> + +
+

<%= investment.crypto %>

+

$ <%= investment.amount %>

+

Registered by: <%= investment.user.name %>

+ + <%= link_to 'Show', investment %> + <%= link_to 'Edit', edit_investment_path(investment) %> +
+ +<% end %> + +
diff --git a/app/views/groups/update.html.erb b/app/views/groups/update.html.erb new file mode 100644 index 0000000..bfed1f5 --- /dev/null +++ b/app/views/groups/update.html.erb @@ -0,0 +1,2 @@ +

Groups#update

+

Find me in app/views/groups/update.html.erb

diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 0000000..b34e3c2 --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1,32 @@ +
+

User Profile

+ + <% if current_user.avatar.persisted? %> + <%= link_to image_tag(current_user.avatar), root_path %> + <% else %> + <%= link_to image_tag("default_profile.jpeg"), root_path %> + <% end %> + +

Hi <%= current_user.name %>!

+ + <%= link_to "Edit profile", edit_user_path(current_user), class: "d-block text-center" %> + + +
+ +
+ +
\ No newline at end of file diff --git a/app/views/investments/_form.html.erb b/app/views/investments/_form.html.erb new file mode 100644 index 0000000..8333bb6 --- /dev/null +++ b/app/views/investments/_form.html.erb @@ -0,0 +1,30 @@ +<%= form_with(model: @investment) do |form| %> + <% if @investment.errors.any? %> +
+

<%= pluralize(@investment.errors.count, "error") %> prohibited this investment from being saved:

+ + +
+ <% end %> + +
+ <%= form.label :crypto, "Select crypto:" %> + <%= form.select :crypto, ['Bitcoin', 'Ethereum', 'XRP', 'Dogecoin', "USD (no crypto)"] %> +
+
+ <%= form.label :group_id, "Select group:" %> + <%= form.select :group_id, Group.select(:name).map(&:name) %> +
+
+ <%= form.label :amount, "Amount:" %> + <%= form.number_field :amount, step: :any %> +
+ +
+ <%= form.submit class: "btn btn-outline-primary" %> +
+<% end %> diff --git a/app/views/investments/create.html.erb b/app/views/investments/create.html.erb new file mode 100644 index 0000000..55c83b0 --- /dev/null +++ b/app/views/investments/create.html.erb @@ -0,0 +1,2 @@ +

Investments#create

+

Find me in app/views/investments/create.html.erb

diff --git a/app/views/investments/destroy.html.erb b/app/views/investments/destroy.html.erb new file mode 100644 index 0000000..cbe908c --- /dev/null +++ b/app/views/investments/destroy.html.erb @@ -0,0 +1,2 @@ +

Investments#destroy

+

Find me in app/views/investments/destroy.html.erb

diff --git a/app/views/investments/edit.html.erb b/app/views/investments/edit.html.erb new file mode 100644 index 0000000..d44ce6e --- /dev/null +++ b/app/views/investments/edit.html.erb @@ -0,0 +1,9 @@ +

Editing Investments

+ +
+<%= render 'form', user: @investment %> +
+<%= link_to 'Show', @investment %> | +<%= link_to 'Investments', investments_path %> +
+
\ No newline at end of file diff --git a/app/views/investments/index.html.erb b/app/views/investments/index.html.erb new file mode 100644 index 0000000..7af13db --- /dev/null +++ b/app/views/investments/index.html.erb @@ -0,0 +1,20 @@ +

Investments

+ +