diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css
index 8666d2f..13dc3ef 100644
--- a/app/assets/stylesheets/application.tailwind.css
+++ b/app/assets/stylesheets/application.tailwind.css
@@ -2,6 +2,12 @@
@tailwind components;
@tailwind utilities;
+.msg-notice {
+ @apply bg-green-300 text-green-900;
+}
+.msg-alert {
+ @apply bg-red-300 text-red-900;
+}
/*
@layer components {
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
new file mode 100644
index 0000000..f6fd1fe
--- /dev/null
+++ b/app/controllers/sessions_controller.rb
@@ -0,0 +1,40 @@
+class SessionsController < ApplicationController
+ # GET /sessions/new
+ def new
+ @session = Session.new
+ end
+
+ # POST /sessions or /sessions.json
+ def create
+ @session = Session.new(session_params)
+
+ respond_to do |format|
+ if @session.save
+ session[:current_user_id] = @session.user_id
+ Rails.logger.info("ID: #{@session.user_id}")
+
+ format.html { redirect_to root_url, notice: "Session was successfully created." }
+ format.json { render :show, status: :created, location: @session }
+ else
+ format.html { render :new, status: :unprocessable_entity, alert: @session.errors }
+ format.json { render json: @session.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /sessions/1 or /sessions/1.json
+ def destroy
+ session[:current_user_id] = nil
+
+ respond_to do |format|
+ format.html { redirect_to root_url, notice: "Session was successfully destroyed." }
+ format.json { head :no_content }
+ end
+ end
+
+ private
+ # Only allow a list of trusted parameters through.
+ def session_params
+ params.require(:session).permit(:email, :password)
+ end
+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/models/session.rb b/app/models/session.rb
new file mode 100644
index 0000000..c82ce7b
--- /dev/null
+++ b/app/models/session.rb
@@ -0,0 +1,22 @@
+class Session
+ include ActiveModel::Model
+
+ include ActiveModel::Attributes
+ include ActiveModel::Validations
+
+ attr_accessor :user_id
+
+ attribute :email, :string
+ attribute :password, :string
+
+ validates :email, presence: true
+ validates :password, presence: true
+
+ def save
+ user = User.authenticate_by(email: email, password: password)
+
+ @user_id = user && user.id
+
+ user.present? && self || nil
+ end
+end
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index c90e132..fbc9c63 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -19,9 +19,18 @@
<%= link_to "Credit Card Bills", credit_card_bills_path, class: "text-white" %>
<%= link_to "Incomes", incomes_path, class: "text-white" %>
<%= link_to "Members", members_path, class: "text-white" %>
+ <%= link_to "Log out", session_path, data: {turbo_method: :delete}, class: "text-white" %>
+ <% if flash.any? %>
+ <% flash.each do |type, msg| %>
+
+ <%= msg %>
+
+ <% end %>
+ <% end %>
+
<%= yield %>
diff --git a/app/views/sessions/_form.html.erb b/app/views/sessions/_form.html.erb
new file mode 100644
index 0000000..2f59bf6
--- /dev/null
+++ b/app/views/sessions/_form.html.erb
@@ -0,0 +1,27 @@
+<%= form_with(model: session, class: "contents") do |form| %>
+ <% if session.errors.any? %>
+
+
<%= pluralize(session.errors.count, "error") %> prohibited this session from being saved:
+
+
+ <% session.errors.each do |error| %>
+ - <%= error.full_message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :email %>
+ <%= form.text_field :email, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
+
+
+
+ <%= form.label :password %>
+ <%= form.password_field :password, class: "block shadow rounded-md border border-gray-400 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/sessions/_session.html.erb b/app/views/sessions/_session.html.erb
new file mode 100644
index 0000000..0a68161
--- /dev/null
+++ b/app/views/sessions/_session.html.erb
@@ -0,0 +1,2 @@
+
+
diff --git a/app/views/sessions/_session.json.jbuilder b/app/views/sessions/_session.json.jbuilder
new file mode 100644
index 0000000..07f4f9f
--- /dev/null
+++ b/app/views/sessions/_session.json.jbuilder
@@ -0,0 +1,2 @@
+json.extract! session, :id, :created_at, :updated_at
+json.url session_url(session, format: :json)
diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb
new file mode 100644
index 0000000..e4f34d8
--- /dev/null
+++ b/app/views/sessions/new.html.erb
@@ -0,0 +1,7 @@
+
+
New session
+
+ <%= render "form", session: @session %>
+
+ <%= link_to "Back to sessions", sessions_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 74d76c3..d7e63ff 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,6 @@
Rails.application.routes.draw do
+ resources :sessions, only: %i[new create]
+ resource :session, only: :destroy
resources :users
resources :credit_card_bills
resource :dashboard, only: :show
diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb
new file mode 100644
index 0000000..bdd5045
--- /dev/null
+++ b/test/controllers/sessions_controller_test.rb
@@ -0,0 +1,27 @@
+require "test_helper"
+
+class SessionsControllerTest < ActionDispatch::IntegrationTest
+ test "should get new" do
+ get new_session_url
+ assert_response :success
+ end
+
+ test "should create session" do
+ user = users(:one)
+ params = {
+ session: {
+ email: user.email,
+ password: "secret"
+ }
+ }
+ post sessions_url, params: params
+
+ assert_redirected_to root_url
+ end
+
+ test "should destroy session" do
+ delete session_url(@session)
+
+ assert_redirected_to root_url
+ end
+end
diff --git a/test/models/session_test.rb b/test/models/session_test.rb
new file mode 100644
index 0000000..e20618a
--- /dev/null
+++ b/test/models/session_test.rb
@@ -0,0 +1,25 @@
+require "test_helper"
+
+class SessionTest < ActiveSupport::TestCase
+ def test_save_when_exists
+ user = users(:one)
+
+ session = Session.new(email: user.email, password: "secret")
+
+ assert session.save
+ end
+
+ def test_save_when_not_exists
+ session = Session.new(email: "fake@example.org", password: "secret")
+
+ assert_not session.save
+ end
+
+ def test_save_when_password_incorrect
+ user = users(:one)
+
+ session = Session.new(email: user.email, password: "bad_password")
+
+ assert_not session.save
+ end
+end
diff --git a/test/system/sessions_test.rb b/test/system/sessions_test.rb
new file mode 100644
index 0000000..7715288
--- /dev/null
+++ b/test/system/sessions_test.rb
@@ -0,0 +1,25 @@
+require "application_system_test_case"
+
+class SessionsTest < ApplicationSystemTestCase
+ setup do
+ @user = users(:one)
+ end
+
+ test "should create session" do
+ visit new_session_url
+
+ fill_in "Email", with: @user.email
+ fill_in "Password", with: "secret"
+
+ click_on "Create Session"
+
+ assert_text "Session was successfully created"
+ end
+
+ test "should destroy Session" do
+ visit root_url
+ click_on "Log out", match: :first
+
+ assert_text "Session was successfully destroyed"
+ end
+end