diff --git a/.kateproject b/.kateproject
new file mode 100644
index 0000000..ba44e44
--- /dev/null
+++ b/.kateproject
@@ -0,0 +1,4 @@
+{
+ "name": "CAF"
+, "files": [ { "git": 1 } ]
+}
diff --git a/Gemfile b/Gemfile
index 8fd1c41..21b65ac 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,8 @@
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+gem 'devise'
+
ruby "3.0.3"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
diff --git a/Gemfile.lock b/Gemfile.lock
index 6aad243..830e484 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -68,6 +68,7 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
+ bcrypt (3.1.16)
bindex (0.8.1)
bootsnap (1.10.3)
msgpack (~> 1.2)
@@ -87,6 +88,12 @@ GEM
debug (1.4.0)
irb (>= 1.3.6)
reline (>= 0.2.7)
+ devise (4.8.1)
+ bcrypt (~> 3.0)
+ orm_adapter (~> 0.1)
+ railties (>= 4.1.0)
+ responders
+ warden (~> 1.2.3)
digest (3.1.0)
erubi (1.10.0)
globalid (1.0.0)
@@ -135,6 +142,7 @@ GEM
nokogiri (1.13.1)
mini_portile2 (~> 2.7.0)
racc (~> 1.4)
+ orm_adapter (0.5.0)
public_suffix (4.0.6)
puma (5.6.1)
nio4r (~> 2.0)
@@ -172,6 +180,9 @@ GEM
regexp_parser (2.2.0)
reline (0.3.1)
io-console (~> 0.5)
+ responders (3.0.1)
+ actionpack (>= 5.0)
+ railties (>= 5.0)
rexml (3.2.5)
rubyzip (2.3.2)
selenium-webdriver (4.1.0)
@@ -195,6 +206,8 @@ GEM
railties (>= 6.0.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
+ warden (1.2.9)
+ rack (>= 2.0.9)
web-console (4.2.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
@@ -218,6 +231,7 @@ DEPENDENCIES
bootsnap
capybara
debug
+ devise
importmap-rails
jbuilder
mysql2 (~> 0.5)
diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb
new file mode 100644
index 0000000..dade852
--- /dev/null
+++ b/app/controllers/companies_controller.rb
@@ -0,0 +1,70 @@
+class CompaniesController < ApplicationController
+ before_action :set_company, only: %i[ show edit update destroy ]
+
+ # GET /companies or /companies.json
+ def index
+ @companies = Company.all
+ end
+
+ # GET /companies/1 or /companies/1.json
+ def show
+ end
+
+ # GET /companies/new
+ def new
+ @company = Company.new
+ end
+
+ # GET /companies/1/edit
+ def edit
+ end
+
+ # POST /companies or /companies.json
+ def create
+ @company = Company.new(company_params)
+
+ respond_to do |format|
+ if @company.save
+ format.html { redirect_to company_url(@company), notice: "Company was successfully created." }
+ format.json { render :show, status: :created, location: @company }
+ else
+ format.html { render :new, status: :unprocessable_entity }
+ format.json { render json: @company.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # PATCH/PUT /companies/1 or /companies/1.json
+ def update
+ respond_to do |format|
+ if @company.update(company_params)
+ format.html { redirect_to company_url(@company), notice: "Company was successfully updated." }
+ format.json { render :show, status: :ok, location: @company }
+ else
+ format.html { render :edit, status: :unprocessable_entity }
+ format.json { render json: @company.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /companies/1 or /companies/1.json
+ def destroy
+ @company.destroy
+
+ respond_to do |format|
+ format.html { redirect_to companies_url, notice: "Company was successfully destroyed." }
+ format.json { head :no_content }
+ end
+ end
+
+ private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_company
+ @company = Company.find(params[:id])
+ end
+
+ # Only allow a list of trusted parameters through.
+ def company_params
+ params.require(:company).permit(:name)
+ end
+end
diff --git a/app/helpers/companies_helper.rb b/app/helpers/companies_helper.rb
new file mode 100644
index 0000000..099b151
--- /dev/null
+++ b/app/helpers/companies_helper.rb
@@ -0,0 +1,2 @@
+module CompaniesHelper
+end
diff --git a/app/models/company.rb b/app/models/company.rb
new file mode 100644
index 0000000..f3ebdf4
--- /dev/null
+++ b/app/models/company.rb
@@ -0,0 +1,5 @@
+class Company < ApplicationRecord
+ validates :name, presence: true
+ validates :name, uniqueness: true
+
+end
diff --git a/app/views/companies/_company.html.erb b/app/views/companies/_company.html.erb
new file mode 100644
index 0000000..de7d585
--- /dev/null
+++ b/app/views/companies/_company.html.erb
@@ -0,0 +1,7 @@
+
+
+ Name:
+ <%= company.name %>
+
+
+
diff --git a/app/views/companies/_company.json.jbuilder b/app/views/companies/_company.json.jbuilder
new file mode 100644
index 0000000..9226bea
--- /dev/null
+++ b/app/views/companies/_company.json.jbuilder
@@ -0,0 +1,2 @@
+json.extract! company, :id, :name, :created_at, :updated_at
+json.url company_url(company, format: :json)
diff --git a/app/views/companies/_form.html.erb b/app/views/companies/_form.html.erb
new file mode 100644
index 0000000..f2cef8f
--- /dev/null
+++ b/app/views/companies/_form.html.erb
@@ -0,0 +1,22 @@
+<%= form_with(model: company) do |form| %>
+ <% if company.errors.any? %>
+
+
<%= pluralize(company.errors.count, "error") %> prohibited this company from being saved:
+
+
+ <% company.errors.each do |error| %>
+ - <%= error.full_message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :name, style: "display: block" %>
+ <%= form.text_field :name %>
+
+
+
+ <%= form.submit %>
+
+<% end %>
diff --git a/app/views/companies/edit.html.erb b/app/views/companies/edit.html.erb
new file mode 100644
index 0000000..b42076c
--- /dev/null
+++ b/app/views/companies/edit.html.erb
@@ -0,0 +1,10 @@
+Editing company
+
+<%= render "form", company: @company %>
+
+
+
+
+ <%= link_to "Show this company", @company %> |
+ <%= link_to "Back to companies", companies_path %>
+
diff --git a/app/views/companies/index.html.erb b/app/views/companies/index.html.erb
new file mode 100644
index 0000000..8d09448
--- /dev/null
+++ b/app/views/companies/index.html.erb
@@ -0,0 +1,14 @@
+<%= notice %>
+
+Companies
+
+
+ <% @companies.each do |company| %>
+ <%= render company %>
+
+ <%= link_to "Show this company", company %>
+
+ <% end %>
+
+
+<%= link_to "New company", new_company_path %>
diff --git a/app/views/companies/index.json.jbuilder b/app/views/companies/index.json.jbuilder
new file mode 100644
index 0000000..1d4d190
--- /dev/null
+++ b/app/views/companies/index.json.jbuilder
@@ -0,0 +1 @@
+json.array! @companies, partial: "companies/company", as: :company
diff --git a/app/views/companies/new.html.erb b/app/views/companies/new.html.erb
new file mode 100644
index 0000000..490be0e
--- /dev/null
+++ b/app/views/companies/new.html.erb
@@ -0,0 +1,9 @@
+New company
+
+<%= render "form", company: @company %>
+
+
+
+
+ <%= link_to "Back to companies", companies_path %>
+
diff --git a/app/views/companies/show.html.erb b/app/views/companies/show.html.erb
new file mode 100644
index 0000000..e749c7e
--- /dev/null
+++ b/app/views/companies/show.html.erb
@@ -0,0 +1,10 @@
+<%= notice %>
+
+<%= render @company %>
+
+
+ <%= link_to "Edit this company", edit_company_path(@company) %> |
+ <%= link_to "Back to companies", companies_path %>
+
+ <%= button_to "Destroy this company", @company, method: :delete %>
+
diff --git a/app/views/companies/show.json.jbuilder b/app/views/companies/show.json.jbuilder
new file mode 100644
index 0000000..f7a3204
--- /dev/null
+++ b/app/views/companies/show.json.jbuilder
@@ -0,0 +1 @@
+json.partial! "companies/company", company: @company
diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb
new file mode 100644
index 0000000..b12dd0c
--- /dev/null
+++ b/app/views/devise/confirmations/new.html.erb
@@ -0,0 +1,16 @@
+Resend confirmation instructions
+
+<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
+
+
+
+ <%= f.submit "Resend confirmation instructions" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
new file mode 100644
index 0000000..dc55f64
--- /dev/null
+++ b/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -0,0 +1,5 @@
+Welcome <%= @email %>!
+
+You can confirm your account email through the link below:
+
+<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb
new file mode 100644
index 0000000..32f4ba8
--- /dev/null
+++ b/app/views/devise/mailer/email_changed.html.erb
@@ -0,0 +1,7 @@
+Hello <%= @email %>!
+
+<% if @resource.try(:unconfirmed_email?) %>
+ We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
+<% else %>
+ We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
+<% end %>
diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb
new file mode 100644
index 0000000..b41daf4
--- /dev/null
+++ b/app/views/devise/mailer/password_change.html.erb
@@ -0,0 +1,3 @@
+Hello <%= @resource.email %>!
+
+We're contacting you to notify you that your password has been changed.
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb
new file mode 100644
index 0000000..f667dc1
--- /dev/null
+++ b/app/views/devise/mailer/reset_password_instructions.html.erb
@@ -0,0 +1,8 @@
+Hello <%= @resource.email %>!
+
+Someone has requested a link to change your password. You can do this through the link below.
+
+<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
+
+If you didn't request this, please ignore this email.
+Your password won't change until you access the link above and create a new one.
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
new file mode 100644
index 0000000..41e148b
--- /dev/null
+++ b/app/views/devise/mailer/unlock_instructions.html.erb
@@ -0,0 +1,7 @@
+Hello <%= @resource.email %>!
+
+Your account has been locked due to an excessive number of unsuccessful sign in attempts.
+
+Click the link below to unlock your account:
+
+<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb
new file mode 100644
index 0000000..5fbb9ff
--- /dev/null
+++ b/app/views/devise/passwords/edit.html.erb
@@ -0,0 +1,25 @@
+Change your password
+
+<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+ <%= f.hidden_field :reset_password_token %>
+
+
+ <%= f.label :password, "New password" %>
+ <% if @minimum_password_length %>
+ (<%= @minimum_password_length %> characters minimum)
+ <% end %>
+ <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
+
+
+
+ <%= f.label :password_confirmation, "Confirm new password" %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
+
+
+
+ <%= f.submit "Change my password" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb
new file mode 100644
index 0000000..9b486b8
--- /dev/null
+++ b/app/views/devise/passwords/new.html.erb
@@ -0,0 +1,16 @@
+Forgot your password?
+
+<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.submit "Send me reset password instructions" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
new file mode 100644
index 0000000..38d95b8
--- /dev/null
+++ b/app/views/devise/registrations/edit.html.erb
@@ -0,0 +1,43 @@
+Edit <%= resource_name.to_s.humanize %>
+
+<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
+ Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+ <% end %>
+
+
+ <%= f.label :password %> (leave blank if you don't want to change it)
+ <%= f.password_field :password, autocomplete: "new-password" %>
+ <% if @minimum_password_length %>
+
+ <%= @minimum_password_length %> characters minimum
+ <% end %>
+
+
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
+
+
+
+ <%= f.label :current_password %> (we need your current password to confirm your changes)
+ <%= f.password_field :current_password, autocomplete: "current-password" %>
+
+
+
+ <%= f.submit "Update" %>
+
+<% end %>
+
+Cancel my account
+
+Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
+
+<%= link_to "Back", :back %>
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
new file mode 100644
index 0000000..d655b66
--- /dev/null
+++ b/app/views/devise/registrations/new.html.erb
@@ -0,0 +1,29 @@
+Sign up
+
+<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.label :password %>
+ <% if @minimum_password_length %>
+ (<%= @minimum_password_length %> characters minimum)
+ <% end %>
+ <%= f.password_field :password, autocomplete: "new-password" %>
+
+
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
+
+
+
+ <%= f.submit "Sign up" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
new file mode 100644
index 0000000..5ede964
--- /dev/null
+++ b/app/views/devise/sessions/new.html.erb
@@ -0,0 +1,26 @@
+Log in
+
+<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.label :password %>
+ <%= f.password_field :password, autocomplete: "current-password" %>
+
+
+ <% if devise_mapping.rememberable? %>
+
+ <%= f.check_box :remember_me %>
+ <%= f.label :remember_me %>
+
+ <% end %>
+
+
+ <%= f.submit "Log in" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb
new file mode 100644
index 0000000..ba7ab88
--- /dev/null
+++ b/app/views/devise/shared/_error_messages.html.erb
@@ -0,0 +1,15 @@
+<% if resource.errors.any? %>
+
+
+ <%= I18n.t("errors.messages.not_saved",
+ count: resource.errors.count,
+ resource: resource.class.model_name.human.downcase)
+ %>
+
+
+ <% resource.errors.full_messages.each do |message| %>
+ - <%= message %>
+ <% end %>
+
+
+<% end %>
diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb
new file mode 100644
index 0000000..96a9412
--- /dev/null
+++ b/app/views/devise/shared/_links.html.erb
@@ -0,0 +1,25 @@
+<%- if controller_name != 'sessions' %>
+ <%= link_to "Log in", new_session_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+ <%= link_to "Sign up", new_registration_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
+ <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
+ <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
+ <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.omniauthable? %>
+ <%- resource_class.omniauth_providers.each do |provider| %>
+ <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post %>
+ <% end %>
+<% end %>
diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb
new file mode 100644
index 0000000..ffc34de
--- /dev/null
+++ b/app/views/devise/unlocks/new.html.erb
@@ -0,0 +1,16 @@
+Resend unlock instructions
+
+<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.submit "Resend unlock instructions" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index b077552..338a075 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -10,6 +10,9 @@
+ <%= notice %>
+ <%= alert %>
+
<%= yield %>