diff --git a/app/controllers/portfolios_controller.rb b/app/controllers/portfolios_controller.rb new file mode 100644 index 00000000..dcbf0643 --- /dev/null +++ b/app/controllers/portfolios_controller.rb @@ -0,0 +1,63 @@ +class PortfoliosController < ApplicationController + before_action :authenticate_user! + before_action :set_portfolio, only: %i[ show edit update destroy ] + + def index + @portfolios = current_user.portfolios + end + + def show + end + + def new + @portfolio = Portfolio.new + end + + def edit + end + + def create + @portfolio = current_user.portfolios.new(portfolio_params) + + respond_to do |format| + if @portfolio.save + format.html { redirect_to portfolio_url(@portfolio), notice: "Portfolio was successfully created." } + format.json { render :show, status: :created, location: @portfolio } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @portfolio.errors, status: :unprocessable_entity } + end + end + end + + def update + respond_to do |format| + if @portfolio.update(portfolio_params) + format.html { redirect_to portfolio_url(@portfolio), notice: "Portfolio was successfully updated." } + format.json { render :show, status: :ok, location: @portfolio } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @portfolio.errors, status: :unprocessable_entity } + end + end + end + + def destroy + @portfolio.destroy! + + respond_to do |format| + format.html { redirect_to portfolios_url, notice: "Portfolio was successfully destroyed." } + format.json { head :no_content } + end + end + + private + def set_portfolio + @portfolio = current_user.portfolios.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def portfolio_params + params.require(:portfolio).permit(:name) + end +end diff --git a/app/helpers/portfolios_helper.rb b/app/helpers/portfolios_helper.rb new file mode 100644 index 00000000..04f40c72 --- /dev/null +++ b/app/helpers/portfolios_helper.rb @@ -0,0 +1,2 @@ +module PortfoliosHelper +end diff --git a/app/models/portfolio.rb b/app/models/portfolio.rb new file mode 100644 index 00000000..1abf1f0f --- /dev/null +++ b/app/models/portfolio.rb @@ -0,0 +1,5 @@ +class Portfolio < ApplicationRecord + belongs_to :user + + validates :name, presence: true +end diff --git a/app/models/user.rb b/app/models/user.rb index 25925b9b..51120e7f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,4 +2,6 @@ class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :lockable, :timeoutable, :trackable + + has_many :portfolios, dependent: :destroy end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 08b66181..cbaefcee 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -33,12 +33,12 @@ <% end %>
  • - <%= link_to root_path, class: "flex p-2 text-sm font-semibold leading-6 text-gray-600 rounded-md group gap-x-3" do %> + <%= link_to portfolios_path, class: "flex p-2 text-sm font-semibold leading-6 text-gray-600 rounded-md group gap-x-3" do %> - Portfolio + Portfolios <% end %>
  • diff --git a/app/views/portfolios/_form.html.erb b/app/views/portfolios/_form.html.erb new file mode 100644 index 00000000..23ebcd4b --- /dev/null +++ b/app/views/portfolios/_form.html.erb @@ -0,0 +1,22 @@ +<%= form_with(model: portfolio, class: "contents") do |form| %> + <% if portfolio.errors.any? %> +
    +

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

    + + +
    + <% end %> + +
    + <%= form.label :name %> + <%= form.text_field :name, class: "block shadow rounded-md border border-gray-200 outline-none px-3 py-2 mt-2 w-full" %> +
    + +
    + <%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %> +
    +<% end %> diff --git a/app/views/portfolios/_portfolio.html.erb b/app/views/portfolios/_portfolio.html.erb new file mode 100644 index 00000000..fe6c7d72 --- /dev/null +++ b/app/views/portfolios/_portfolio.html.erb @@ -0,0 +1,11 @@ +
    +

    + <%= portfolio.name %> +

    + + <% if action_name != "show" %> + <%= link_to "Show this portfolio", portfolio, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> + <%= link_to "Edit this portfolio", edit_portfolio_path(portfolio), class: "rounded-lg py-3 ml-2 px-5 bg-gray-100 inline-block font-medium" %> +
    + <% end %> +
    diff --git a/app/views/portfolios/edit.html.erb b/app/views/portfolios/edit.html.erb new file mode 100644 index 00000000..22477163 --- /dev/null +++ b/app/views/portfolios/edit.html.erb @@ -0,0 +1,8 @@ +
    +

    Editing portfolio

    + + <%= render "form", portfolio: @portfolio %> + + <%= link_to "Show this portfolio", @portfolio, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> + <%= link_to "Back to portfolios", portfolios_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> +
    diff --git a/app/views/portfolios/index.html.erb b/app/views/portfolios/index.html.erb new file mode 100644 index 00000000..77d90eba --- /dev/null +++ b/app/views/portfolios/index.html.erb @@ -0,0 +1,18 @@ +
    + <% if notice.present? %> +

    <%= notice %>

    + <% end %> + +
    +

    Portfolios

    + <%= link_to "New portfolio", new_portfolio_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %> +
    + + +
    diff --git a/app/views/portfolios/new.html.erb b/app/views/portfolios/new.html.erb new file mode 100644 index 00000000..027c7c34 --- /dev/null +++ b/app/views/portfolios/new.html.erb @@ -0,0 +1,7 @@ +
    +

    New portfolio

    + + <%= render "form", portfolio: @portfolio %> + + <%= link_to "Back to portfolios", portfolios_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> +
    diff --git a/app/views/portfolios/show.html.erb b/app/views/portfolios/show.html.erb new file mode 100644 index 00000000..5a4d5db9 --- /dev/null +++ b/app/views/portfolios/show.html.erb @@ -0,0 +1,17 @@ +
    + <% if notice.present? %> +

    <%= notice %>

    + <% end %> + +
    +

    <%= @portfolio.name %>

    + +
    + <%= link_to "Edit this portfolio", edit_portfolio_path(@portfolio), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> +
    + <%= button_to "Destroy this portfolio", portfolio_path(@portfolio), method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %> +
    + <%= link_to "Back to portfolios", portfolios_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> +
    +
    +
    diff --git a/config/routes.rb b/config/routes.rb index 3639306b..1370a095 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + resources :portfolios + devise_for :users # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. diff --git a/db/migrate/20240102020519_create_portfolios.rb b/db/migrate/20240102020519_create_portfolios.rb new file mode 100644 index 00000000..0e5d50b1 --- /dev/null +++ b/db/migrate/20240102020519_create_portfolios.rb @@ -0,0 +1,10 @@ +class CreatePortfolios < ActiveRecord::Migration[7.2] + def change + create_table :portfolios, id: :uuid do |t| + t.references :user, null: false, foreign_key: true, type: :uuid + t.string :name + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 2cdd8b18..09d9bef0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_01_01_232802) do +ActiveRecord::Schema[7.2].define(version: 2024_01_02_020519) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" + create_table "portfolios", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.uuid "user_id", null: false + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_portfolios_on_user_id" + end + create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -41,4 +49,5 @@ ActiveRecord::Schema[7.2].define(version: 2024_01_01_232802) do t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true end + add_foreign_key "portfolios", "users" end diff --git a/test/controllers/portfolios_controller_test.rb b/test/controllers/portfolios_controller_test.rb new file mode 100644 index 00000000..9771a689 --- /dev/null +++ b/test/controllers/portfolios_controller_test.rb @@ -0,0 +1,48 @@ +require "test_helper" + +class PortfoliosControllerTest < ActionDispatch::IntegrationTest + setup do + @portfolio = portfolios(:one) + end + + test "should get index" do + get portfolios_url + assert_response :success + end + + test "should get new" do + get new_portfolio_url + assert_response :success + end + + test "should create portfolio" do + assert_difference("Portfolio.count") do + post portfolios_url, params: { portfolio: { name: @portfolio.name, user_id: @portfolio.user_id } } + end + + assert_redirected_to portfolio_url(Portfolio.last) + end + + test "should show portfolio" do + get portfolio_url(@portfolio) + assert_response :success + end + + test "should get edit" do + get edit_portfolio_url(@portfolio) + assert_response :success + end + + test "should update portfolio" do + patch portfolio_url(@portfolio), params: { portfolio: { name: @portfolio.name, user_id: @portfolio.user_id } } + assert_redirected_to portfolio_url(@portfolio) + end + + test "should destroy portfolio" do + assert_difference("Portfolio.count", -1) do + delete portfolio_url(@portfolio) + end + + assert_redirected_to portfolios_url + end +end diff --git a/test/fixtures/portfolios.yml b/test/fixtures/portfolios.yml new file mode 100644 index 00000000..56ad3115 --- /dev/null +++ b/test/fixtures/portfolios.yml @@ -0,0 +1,9 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user: one + name: MyString + +two: + user: two + name: MyString diff --git a/test/models/portfolio_test.rb b/test/models/portfolio_test.rb new file mode 100644 index 00000000..2f0dddc7 --- /dev/null +++ b/test/models/portfolio_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PortfolioTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/system/portfolios_test.rb b/test/system/portfolios_test.rb new file mode 100644 index 00000000..2e4b37c2 --- /dev/null +++ b/test/system/portfolios_test.rb @@ -0,0 +1,43 @@ +require "application_system_test_case" + +class PortfoliosTest < ApplicationSystemTestCase + setup do + @portfolio = portfolios(:one) + end + + test "visiting the index" do + visit portfolios_url + assert_selector "h1", text: "Portfolios" + end + + test "should create portfolio" do + visit portfolios_url + click_on "New portfolio" + + fill_in "Name", with: @portfolio.name + fill_in "User", with: @portfolio.user_id + click_on "Create Portfolio" + + assert_text "Portfolio was successfully created" + click_on "Back" + end + + test "should update Portfolio" do + visit portfolio_url(@portfolio) + click_on "Edit this portfolio", match: :first + + fill_in "Name", with: @portfolio.name + fill_in "User", with: @portfolio.user_id + click_on "Update Portfolio" + + assert_text "Portfolio was successfully updated" + click_on "Back" + end + + test "should destroy Portfolio" do + visit portfolio_url(@portfolio) + click_on "Destroy this portfolio", match: :first + + assert_text "Portfolio was successfully destroyed" + end +end