update views, mailers, styles, etc.

This commit is contained in:
ZippyDev 2020-04-25 19:41:40 -06:00
parent c36423e53a
commit e31b2782e6
49 changed files with 1162 additions and 203 deletions

View File

@ -15,6 +15,7 @@ gem 'rails', '~> 6.0'
gem 'redis-rails', '~> 5.0'
gem 'responders', '~> 3.0'
gem 'sidekiq', '~> 6.0'
gem 'simple_form', '~> 5.0'
gem 'validates_email_format_of', '~> 1.6'
gem 'webpacker', '~> 4.2'
@ -25,7 +26,7 @@ gem 'devise', '~> 4.7'
# styles
gem 'bulma-rails', '~> 0.8.0'
gem 'foundation_emails', '~> 2.2'
gem 'premailer-rails', '~> 1.11'
group :development, :test do
gem 'factory_bot_rails', '~> 5.1'

View File

@ -303,6 +303,9 @@ GEM
rack (~> 2.0)
rack-protection (>= 2.0.0)
redis (>= 4.1.0)
simple_form (5.0.2)
actionpack (>= 5.0)
activemodel (>= 5.0)
simplecov (0.18.5)
docile (~> 1.1)
simplecov-html (~> 0.11)
@ -370,6 +373,7 @@ DEPENDENCIES
shoulda-callback-matchers (~> 1.1)
shoulda-matchers (~> 3.1)
sidekiq (~> 6.0)
simple_form (~> 5.0)
simplecov (~> 0.16)
spring
spring-watcher-listen (~> 2.0)

4
Procfile Normal file
View File

@ -0,0 +1,4 @@
web: bundle exec puma -C config/puma.rb
webpack: bin/webpack
worker: bundle exec sidekiq -C config/sidekiq.yml
release: bin/rake db:migrate

View File

@ -14,6 +14,7 @@ This repo is a designed to be a Dockerized Rails 6 template:
- `bundle`
- `yarn install --check-files`
- `Find/replace all TODO`
- `docker-compose up --build`
### Dependencies

View File

@ -13,3 +13,22 @@
*/
@import "bulma"; // https://bulma.io/documentation/
@import "./devise";
html {
background-image: radial-gradient(circle at 13% 47%, rgba(140, 140, 140,0.03) 0%, rgba(140, 140, 140,0.03) 25%,transparent 25%, transparent 100%),radial-gradient(circle at 28% 63%, rgba(143, 143, 143,0.03) 0%, rgba(143, 143, 143,0.03) 16%,transparent 16%, transparent 100%),radial-gradient(circle at 81% 56%, rgba(65, 65, 65,0.03) 0%, rgba(65, 65, 65,0.03) 12%,transparent 12%, transparent 100%),radial-gradient(circle at 26% 48%, rgba(60, 60, 60,0.03) 0%, rgba(60, 60, 60,0.03) 6%,transparent 6%, transparent 100%),radial-gradient(circle at 97% 17%, rgba(150, 150, 150,0.03) 0%, rgba(150, 150, 150,0.03) 56%,transparent 56%, transparent 100%),radial-gradient(circle at 50% 100%, rgba(25, 25, 25,0.03) 0%, rgba(25, 25, 25,0.03) 36%,transparent 36%, transparent 100%),radial-gradient(circle at 55% 52%, rgba(69, 69, 69,0.03) 0%, rgba(69, 69, 69,0.03) 6%,transparent 6%, transparent 100%),linear-gradient(90deg, rgb(255,255,255),rgb(255,255,255));
background-repeat: no-repeat;
background-size: cover;
}
.toggleable {
display: none;
&.is-active {
display: inherit;
}
}
.notifications-container {
padding: $size-7;
}

View File

@ -0,0 +1,15 @@
#registrations, #passwords {
padding-top: $size-1;
}
#registrations-column {
background-color: $white;
padding: $size-6;
}
#registrations-column-form, #registrations-column-form-submit-button,
#registration-edit-account-deletion-container, #registrations-edit-passwords,
#registrations-edit-update-password, #passwords-column-form, #unlocks-column-form,
#confirmations-column-form {
padding: $size-3;
}

View File

@ -0,0 +1,18 @@
@import "bulma";
body {
background-color: $grey-light;
}
.email-main-container {
padding: $size-3;
background-color: $white;
}
.email-details {
padding: $size-1 $size-5 ;
}
.email-action-button-container {
padding: $size-1;
}

View File

@ -8,7 +8,7 @@ class ApplicationController < ActionController::Base
authenticate_user!
return if current_user.admin?
redirect_to root_path, flash: { alert: 'You are not allowed to do that.' }
redirect_to root_path, flash: { alert: "Whoops! We don't know where that path goes ¯\\_(ツ)_/¯" }
end
protected

View File

@ -1,4 +1,27 @@
# frozen_string_literal: true
module ApplicationHelper
# Javascript Pack helper for controller-based JS
def javascript_controller_pack_tag_helper
javascript_pack_tag params[:controller]
rescue Webpacker::Manifest::MissingEntryError
begin
# Fallback retry for nested controllers
namespace = params[:controller].split('/').first
javascript_pack_tag namespace
rescue StandardError
nil
end
end
def notification_type(name)
case name.to_sym
when :notice
'info'
when :alert
'danger'
else
name
end
end
end

View File

@ -4,8 +4,10 @@
// that code so it'll be compiled.
require("@rails/ujs").start()
require("turbolinks").start()
// require("turbolinks").start()
require("channels")
require("./bulma/base")
require("jquery")
// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)

View File

@ -0,0 +1 @@
require("./notification")

View File

@ -0,0 +1,16 @@
// https://bulma.io/documentation/elements/notification/#javascript-example
// document.addEventListener('DOMContentLoaded', () => {
// (document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
// $notification = $delete.parentNode;
//
// $delete.addEventListener('click', () => {
// $notification.parentNode.removeChild($notification);
// });
// });
// });
$(function(){
$('.notification .delete').on('click', function(){
$(this).parent().hide();
});
});

View File

@ -0,0 +1,11 @@
$(function() {
$('#registration-edit-password-button').on('click', function(){
$('#registrations-edit-update-password').toggleClass('is-active');
return false;
});
$('#registration-edit-account-delete-button').on('click', function(){
$('#registration-edit-account-delete').toggleClass('is-active');
return false;
});
});

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class BulmaDeviseMailer < Devise::Mailer
default from: App::Config::DEFAULT_EMAIL
layout 'mailer'
end

View File

@ -3,10 +3,23 @@
class User < ApplicationRecord
acts_as_paranoid
before
# Include default devise modules. Others available are:
# :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable,
:confirmable, :lockable, :timeoutable, :trackable
validates :username, uniqueness: true
validates :email, presence: true, email_format: { message: 'format is not valid' }
before_validation :set_username, on: :create
private
def set_username
return if username.present?
self.username = Digest::MD5.hexdigest(Time.now.to_f.to_s + email)
end
end

View File

@ -0,0 +1,46 @@
<div class="container is-fluid" id="passwords">
<div class="columns is-centered is-mobile">
<div id="registrations-column" class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Resend Confirmation Email
</h1>
</div>
</div>
</section>
<div id="confirmations-column-form">
<%= simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<div class="field">
<%= f.label :email, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :email,
required: true,
autofocus: true,
autocomplete: "email",
value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email),
class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</p>
<%= f.full_error :email, class: "help is-danger" %>
</div>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Resend confirmation", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<div class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Account Confirmation
</h1>
</div>
</div>
</div>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column">
<div class="email-details">
<p>
Welcome <%= @email %>! Please confirm your email address to verify your account.
</p>
<div class="email-action-button-container">
<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token), class: "button is-primary is-fullwidth is-rounded" %>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,28 @@
<div class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Account Email Updated
</h1>
</div>
</div>
</div>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column">
<div class="email-details">
<p>
Your email has been chnaged to <strong><%= @resource.try(:unconfirmed_email?) ? @resource.unconfirmed_email : @resource.email %></strong>.
</p>
<div class="notification is-danger">
<p>
Please contact support if you did not make these changes.
</p>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,26 @@
<div class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Account Password Updated
</h1>
</div>
</div>
</div>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column">
<div class="email-details">
<p>We're contacting you to notify you that your password has been changed.</p>
<div class="notification is-danger">
<p>
Please contact support if you did not make these changes.
</p>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,26 @@
<div class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Reset Password
</h1>
</div>
</div>
</div>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column">
<div class="email-details">
<p>Someone has requested a link to change your password. You can do this through the link below.</p>
<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>
<div class="email-action-button-container">
<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token), class: "button is-primary is-fullwidth is-rounded" %>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<div class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Account Unlock
</h1>
</div>
</div>
</div>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column">
<div class="email-details">
<p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
<p>Click the link below to unlock your account:</p>
<div class="email-action-button-container">
<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token), class: "button is-primary is-fullwidth is-rounded" %>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,66 @@
<div class="container is-fluid" id="passwords">
<div class="columns is-centered is-mobile">
<div id="registrations-column" class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Update Password
</h1>
</div>
</div>
</section>
<div id="passwords-column-form">
<%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
<%= f.input :reset_password_token, as: :hidden %>
<%= f.full_error :reset_password_token %>
<div class="field">
<%= f.label :password, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :password,
required: true,
autofocus: true,
autocomplete: "new-password",
hint: ("#{@minimum_password_length} characters minimum"),
class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.full_error :password, class: "help is-danger" %>
</div>
<div class="field">
<%= f.label :password_confirmation, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :password_confirmation,
required: true,
autocomplete: "new-password",
class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.full_error :password_confirmation, class: "help is-danger" %>
</div>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Update password", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
<div class="container is-fluid" id="passwords">
<div class="columns is-centered is-mobile">
<div id="registrations-column" class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Reset My Password
</h1>
</div>
</div>
</section>
<div id="passwords-column-form">
<%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
<div class="field">
<%= f.label :email, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :email,
required: true,
autofocus: true,
autocomplete: "email",
class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</p>
<%= f.full_error :email, class: "help is-danger" %>
</div>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Reset password", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,124 @@
<div class="container is-fluid" id="registrations" data-controller="notification">
<div class="columns is-centered">
<div id="registrations-column" class="column is-four-fifths is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Edit Account
</h1>
</div>
</div>
</section>
<div id="registrations-column-form">
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div class="notification is-warning">
<button class="delete"></button>
Account confirmation pending for <strong><%= resource.unconfirmed_email %></strong>
</div>
<% end %>
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<div class="field">
<%= f.label :email, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :email, required: true, autofocus: true, autocomplete: "email", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</p>
<%= f.full_error :email, class: "help is-danger" %>
</div>
<div id="registrations-edit-passwords">
<%= link_to "Change password", "#", id: "registration-edit-password-button", class: "button is-fullwidth is-rounded" %>
<div class="toggleable" id="registrations-edit-update-password">
<div class="notification">
<div class="field">
<%= f.label :password, class: "label" %>
<p class="control has-icons-left">
<%= f.input_field :password, required: false, autocomplete: "new-password", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.hint "Leave blank to not change password", class: "help" %>
<%= f.full_error :password, class: "help is-danger" %>
</div>
<div class="field">
<%= f.label :password_confirmation, class: "label" %>
<p class="control has-icons-left">
<%= f.input_field :password_confirmation, required: false, autocomplete: "new-password", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.full_error :password_confirmation, class: "help is-danger" %>
</div>
</div>
</div>
</div>
<div class="field">
<%= f.label :current_password, class: "label" %>
<p class="control has-icons-left">
<%= f.input_field :current_password, required: true, autocomplete: "current-password", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.hint "Password confirmation needed for changes", class: "help" %>
<%= f.full_error :current_password, class: "help is-danger" %>
</div>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Update Account", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
</div>
<div class="columns">
<div class="column">
<%= link_to "Back", :back, class: "button is-primary is-fullwidth is-rounded is-light" %>
</div>
<div class="column">
<%= link_to "Close my account", "#registration-edit-account-delete", id: "registration-edit-account-delete-button", class: "button is-danger is-fullwidth is-rounded is-light" %>
</div>
</div>
<div id="registration-edit-account-delete" class="has-text-centered toggleable">
<article class="message is-danger">
<div class="message-body">
<strong>Warning!</strong> Account deletion is permanant and can not be recovered.
<div class="is-centered" id="registration-edit-account-deletion-container">
<%= link_to "Delete My Account", registration_path(resource_name),
data: { confirm: "Are you sure?" }, method: :delete,
class: "button is-danger is-two-thirds is-rounded" %>
</div>
</div>
</article>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,71 @@
<div class="container is-fluid" id="registrations">
<div class="columns is-centered is-mobile">
<div id="registrations-column" class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Sign up
</h1>
</div>
</div>
</section>
<div id="registrations-column-form">
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :email, required: true, autofocus: true, autocomplete: "email", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</p>
<%= f.full_error :email, class: "help is-danger" %>
</div>
<div class="field">
<%= f.label :password, class: "label" %>
<p class="control has-icons-left">
<%= f.input_field :password, required: true,
hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length),
autocomplete: "new-password", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.full_error :password, class: "help is-danger" %>
</div>
<div class="field">
<%= f.label :password_confirmation, class: "label" %>
<p class="control has-icons-left">
<%= f.input_field :password_confirmation, required: true, autocomplete: "new-password", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.full_error :password_confirmation, class: "help is-danger" %>
</div>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Sign up", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,58 @@
<div class="container is-fluid" id="registrations">
<div class="columns is-centered">
<div id="registrations-column" class="column is-four-fifths is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Log in <%= resource_name %>
</h1>
</div>
</div>
</section>
<div id="registrations-column-form">
<%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :email, required: true, autofocus: true, autocomplete: "email", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</p>
<%= f.full_error :email, class: "help is-danger" %>
</div>
<div class="field">
<%= f.label :password, class: "label" %>
<p class="control has-icons-left">
<%= f.input_field :password, required: true, autocomplete: "current-password", class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
<%= f.full_error :password, class: "help is-danger" %>
</div>
<%= f.check_box :remember_me, as: :boolean, class: "checkbox" %>
<%= f.label :remember_me, class: "checkbox" %>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Sign in", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,12 @@
<% if resource.errors.any? %>
<div id="error_explanation">
<h2>
<%= I18n.t("errors.messages.not_saved", count: resource.errors.count, resource: resource.class.model_name.human.downcase) %>
</h2>
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -0,0 +1,39 @@
<div class="columns is-centered is-mobile has-text-centered is-size-7">
<% if controller_name != 'sessions' %>
<div class="column">
<%= link_to "Log in", new_session_path(resource_name) %>
</div>
<% end %>
<% if devise_mapping.registerable? && controller_name != 'registrations' %>
<div class="column">
<%= link_to "Sign up", new_registration_path(resource_name) %>
</div>
<% end %>
<% if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<div class="column">
<%= link_to "Password Recovery", new_password_path(resource_name) %>
</div>
<% end %>
<% if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<div class="column">
<%= link_to "Confirm Account", new_confirmation_path(resource_name) %>
</div>
<% end %>
<% if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<div class="column">
<%= link_to "Unlock Account", new_unlock_path(resource_name) %>
</div>
<% end %>
<% if devise_mapping.omniauthable? %>
<% resource_class.omniauth_providers.each do |provider| %>
<div class="column">
<%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
</div>
<% end %>
<% end %>
</div>

View File

@ -0,0 +1,45 @@
<div class="container is-fluid" id="passwords">
<div class="columns is-centered is-mobile">
<div id="registrations-column" class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop">
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-centered">
Account Unlock
</h1>
</div>
</div>
</section>
<div id="unlocks-column-form">
<%= simple_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<div class="field">
<%= f.label :email, class: "label" %>
<p class="control has-icons-left has-icons-right">
<%= f.input_field :email,
required: true,
autofocus: true,
autocomplete: "email",
class: "input" %>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
</p>
<%= f.full_error :email, class: "help is-danger" %>
</div>
<div class="field">
<p class="control" id="registrations-column-form-submit-button">
<%= f.button :submit, "Send unlock instructions", class: "button is-primary is-fullwidth is-rounded" %>
</p>
</div>
<% end %>
<%= render "devise/shared/links" %>
</div>
</div>
</div>
</div>

View File

@ -12,12 +12,14 @@
</head>
<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= render "shared/notifications" %>
<%= yield %>
<%= render "shared/developer_view" %>
</body>
<%= javascript_pack_tag 'application' %>
<%= javascript_controller_pack_tag_helper %>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</html>

View File

@ -1,13 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
/* Email styles need to be inline */
</style>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html" />
<%= stylesheet_link_tag :mailer, media: "all" %>
</head>
<body>
<%= yield %>
<div class="email-main-container">
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column is-two-thirds">
<%= yield %>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,25 @@
<% if Rails.env.development? %>
<div class="developer-pane" style="padding:2rem;">
<div class="columns is-centered">
<div class="column is-four-fifths">
<article class="message">
<div class="message-header">
<p>Developer Pane</p>
</div>
<div class="message-body">
<% params.each do |key, value| %>
<div class="columns">
<div class="column is-one-third">
<%= key %>
</div>
<div class="column">
<%= value %>
</div>
</div>
<% end %>
</div>
</article>
</div>
</div>
</div>
<% end %>

View File

@ -0,0 +1,13 @@
<div class="container notifications-container is-fluid <%= 'is-hidden' if flash.blank? %>">
<div class="columns is-centered">
<div class="column is-two-thirds">
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, class: "notification app-notifications is-#{notification_type(name)}" do %>
<button class="delete"></button>
<%= msg %>
<% end %>
<% end %>
</div>
</div>
</div>

View File

@ -14,3 +14,4 @@ Rails.application.config.assets.paths << Rails.root.join('node_modules')
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
Rails.application.config.assets.precompile += %w[mailer.css]

View File

@ -18,10 +18,10 @@ Devise.setup do |config|
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
# config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
config.mailer = 'BulmaDeviseMailer'
# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'

View File

@ -0,0 +1,3 @@
# frozen_string_literal: true
Premailer::Rails.config.merge!(preserve_styles: true, remove_ids: true)

View File

@ -0,0 +1,177 @@
# frozen_string_literal: true
#
# Uncomment this and change the path if necessary to include your own
# components.
# See https://github.com/heartcombo/simple_form#custom-components to know
# more about custom components.
# Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
#
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Wrappers are used by the form builder to generate a
# complete input. You can remove any component from the
# wrapper, change the order or even add your own to the
# stack. The options given below are used to wrap the
# whole input.
config.wrappers :default, class: :input,
hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
## Extensions enabled by default
# Any of these extensions can be disabled for a
# given input by passing: `f.input EXTENSION_NAME => false`.
# You can make any of these extensions optional by
# renaming `b.use` to `b.optional`.
# Determines whether to use HTML5 (:email, :url, ...)
# and required attributes
b.use :html5
# Calculates placeholders automatically from I18n
# You can also pass a string as f.input placeholder: "Placeholder"
b.use :placeholder
## Optional extensions
# They are disabled unless you pass `f.input EXTENSION_NAME => true`
# to the input. If so, they will retrieve the values from the model
# if any exists. If you want to enable any of those
# extensions by default, you can change `b.optional` to `b.use`.
# Calculates maxlength from length validations for string inputs
# and/or database column lengths
b.optional :maxlength
# Calculate minlength from length validations for string inputs
b.optional :minlength
# Calculates pattern from format validations for string inputs
b.optional :pattern
# Calculates min and max from length validations for numeric inputs
b.optional :min_max
# Calculates readonly automatically from readonly attributes
b.optional :readonly
## Inputs
# b.use :input, class: 'input', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :label_input
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
## full_messages_for
# If you want to display the full error message for the attribute, you can
# use the component :full_error, like:
#
# b.use :full_error, wrap_with: { tag: :span, class: :error }
end
# The default wrapper to be used by the FormBuilder.
config.default_wrapper = :default
# Define the way to render check boxes / radio buttons with labels.
# Defaults to :nested for bootstrap config.
# inline: input + label
# nested: label > input
config.boolean_style = :nested
# Default class for buttons
config.button_class = 'button'
# Method used to tidy up errors. Specify any Rails Array method.
# :first lists the first message for each field.
# Use :to_sentence to list all errors for each field.
# config.error_method = :first
# Default tag used for error notification helper.
config.error_notification_tag = :div
# CSS class to add for error notification helper.
config.error_notification_class = 'error_notification'
# Series of attempts to detect a default label method for collection.
# config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
# Series of attempts to detect a default value method for collection.
# config.collection_value_methods = [ :id, :to_s ]
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
# config.collection_wrapper_tag = nil
# You can define the class to use on all collection wrappers. Defaulting to none.
# config.collection_wrapper_class = nil
# You can wrap each item in a collection of radio/check boxes with a tag,
# defaulting to :span.
# config.item_wrapper_tag = :span
# You can define a class to use in all item wrappers. Defaulting to none.
# config.item_wrapper_class = nil
# How the label text should be generated altogether with the required text.
# config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
# You can define the class to use on all labels. Default is nil.
# config.label_class = nil
# You can define the default class to be used on forms. Can be overriden
# with `html: { :class }`. Defaulting to none.
# config.default_form_class = nil
# You can define which elements should obtain additional classes
# config.generate_additional_classes_for = [:wrapper, :label, :input]
# Whether attributes are required by default (or not). Default is true.
# config.required_by_default = true
# Tell browsers whether to use the native HTML5 validations (novalidate form option).
# These validations are enabled in SimpleForm's internal config but disabled by default
# in this configuration, which is recommended due to some quirks from different browsers.
# To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
# change this configuration to true.
config.browser_validations = false
# Custom mappings for input types. This should be a hash containing a regexp
# to match as key, and the input type that will be used when the field name
# matches the regexp as value.
# config.input_mappings = { /count/ => :integer }
# Custom wrappers for input types. This should be a hash containing an input
# type as key and the wrapper that will be used for all inputs with specified type.
# config.wrapper_mappings = { string: :prepend }
# Namespaces where SimpleForm should look for custom input classes that
# override default inputs.
# config.custom_inputs_namespaces << "CustomInputs"
# Default priority for time_zone inputs.
# config.time_zone_priority = nil
# Default priority for country inputs.
# config.country_priority = nil
# When false, do not use translations for labels.
# config.translate_labels = true
# Automatically discover new inputs in Rails' autoload path.
# config.inputs_discovery = true
# Cache SimpleForm inputs discovery
# config.cache_discovery = !Rails.env.development?
# Default class for inputs
# config.input_class = nil
# Define the default class of the input wrapper of the boolean input.
config.boolean_label_class = 'checkbox'
# Defines if the default input wrapper class should be included in radio
# collection wrappers.
# config.include_default_input_wrapper_class = true
# Defines which i18n scope will be used in Simple Form.
# config.i18n_scope = 'simple_form'
# Defines validation classes to the input_field. By default it's nil.
config.input_field_valid_class = 'is-success'
config.input_field_error_class = 'is-danger'
end

View File

@ -0,0 +1,31 @@
en:
simple_form:
"yes": 'Yes'
"no": 'No'
required:
text: 'required'
mark: '*'
# You can uncomment the line below if you need to overwrite the whole required html.
# When using html, text and mark won't be used.
# html: '<abbr title="required">*</abbr>'
error_notification:
default_message: "Please review the problems below:"
# Examples
# labels:
# defaults:
# password: 'Password'
# user:
# new:
# email: 'E-mail to sign in.'
# edit:
# email: 'E-mail.'
# hints:
# defaults:
# username: 'User name to sign in.'
# password: 'No special characters, please.'
# include_blanks:
# defaults:
# age: 'Rather not say'
# prompts:
# defaults:
# age: 'Select your age'

View File

@ -1,3 +1,11 @@
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
module.exports = environment

View File

@ -50,7 +50,7 @@ default: &default
development:
<<: *default
compile: true
compile: false
# Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
# check_yarn_integrity: true
@ -65,6 +65,7 @@ development:
# Non-docker: localhost:3035
public: webpacker:3035
hmr: false
# hmr: true
# Inline should be set to true if using HMR
inline: true
overlay: true
@ -76,6 +77,7 @@ development:
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
poll: true
ignored: '**/node_modules/**'

View File

@ -13,16 +13,16 @@ services:
POSTGRES_PASSWORD: "password"
ports:
- "5432:5432"
# volumes:
# - "postgres:/var/lib/postgresql/data"
volumes:
- /usr/local/var/postgres/data:/var/lib/postgresql/data
redis:
image: redis:5.0-alpine
command: redis-server
expose:
- "6379"
# volumes:
# - "redis:/var/lib/redis/data"
volumes:
- /usr/local/var/redis/data:/var/lib/redis/data
healthcheck:
test: "redis-cli ping"
interval: 5s
@ -42,6 +42,8 @@ services:
webpacker:
build: .
environment:
- WEBPACKER_DEV_SERVER_HOST=0.0.0.0
env_file:
- '.env.docker'
command: ./bin/webpack-dev-server
@ -67,8 +69,3 @@ services:
tty: true
env_file:
- '.env.docker'
# volumes:
# redis:
# postgres:

View File

@ -0,0 +1,15 @@
<%# frozen_string_literal: true %>
<%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
<%%= f.error_notification %>
<%%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="form-inputs">
<%- attributes.each do |attribute| -%>
<%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
<%- end -%>
</div>
<div class="form-actions">
<%%= f.button :submit %>
</div>
<%% end %>

View File

@ -5,6 +5,7 @@
"@rails/actioncable": "^6.0.0",
"@rails/ujs": "^6.0.0",
"@rails/webpacker": "4.2.2",
"jquery": "^3.4.1",
"stimulus": "^1.1.1",
"turbolinks": "^5.2.0"
},

View File

@ -1,67 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>The page you were looking for doesn't exist (404)</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
.rails-default-error-page {
background-color: #EFEFEF;
color: #2E2F30;
text-align: center;
font-family: arial, sans-serif;
margin: 0;
}
<head>
<title>404</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.2/css/bulma.min.css">
</head>
.rails-default-error-page div.dialog {
width: 95%;
max-width: 33em;
margin: 4em auto 0;
}
<body>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop has-text-centered">
<section class="hero">
<div class="hero-body">
<div class="container">
<h1>
<i class="far fa-frown-open fa-9x"></i>
</h1>
.rails-default-error-page div.dialog > div {
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #BBB;
border-top: #B00100 solid 4px;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
background-color: white;
padding: 7px 12% 0;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
<h1 class="title" style="padding:2rem;">
This page does not exist.
</h1>
</div>
</div>
</section>
.rails-default-error-page h1 {
font-size: 100%;
color: #730E15;
line-height: 1.5em;
}
.rails-default-error-page div.dialog > p {
margin: 0 0 1em;
padding: 1em;
background-color: #F7F7F7;
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #999;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-top-color: #DADADA;
color: #666;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
</style>
</head>
<body class="rails-default-error-page">
<!-- This file lives in public/404.html -->
<div class="dialog">
<div>
<h1>The page you were looking for doesn't exist.</h1>
<p>You may have mistyped the address or the page may have moved.</p>
<p class="subtitle">
Please contact support if the problem persists.
</p>
</div>
</div>
</div>
<p>If you are the application owner check the logs for more information.</p>
</div>
</body>
</body>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</html>

View File

@ -1,67 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>The change you wanted was rejected (422)</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
.rails-default-error-page {
background-color: #EFEFEF;
color: #2E2F30;
text-align: center;
font-family: arial, sans-serif;
margin: 0;
}
<head>
<title>422</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.2/css/bulma.min.css">
</head>
.rails-default-error-page div.dialog {
width: 95%;
max-width: 33em;
margin: 4em auto 0;
}
<body>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop has-text-centered">
<section class="hero">
<div class="hero-body">
<div class="container">
<h1>
<i class="far fa-frown-open fa-9x"></i>
</h1>
.rails-default-error-page div.dialog > div {
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #BBB;
border-top: #B00100 solid 4px;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
background-color: white;
padding: 7px 12% 0;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
.rails-default-error-page h1 {
font-size: 100%;
color: #730E15;
line-height: 1.5em;
}
.rails-default-error-page div.dialog > p {
margin: 0 0 1em;
padding: 1em;
background-color: #F7F7F7;
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #999;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-top-color: #DADADA;
color: #666;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
</style>
</head>
<body class="rails-default-error-page">
<!-- This file lives in public/422.html -->
<div class="dialog">
<div>
<h1>The change you wanted was rejected.</h1>
<p>Maybe you tried to change something you didn't have access to.</p>
<h1 class="title" style="padding:2rem;">
Whoops. That didn't work. Please contact support if problem continues.
</h1>
</div>
</div>
</section>
</div>
</div>
</div>
<p>If you are the application owner check the logs for more information.</p>
</div>
</body>
</body>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</html>

View File

@ -1,66 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>We're sorry, but something went wrong (500)</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
.rails-default-error-page {
background-color: #EFEFEF;
color: #2E2F30;
text-align: center;
font-family: arial, sans-serif;
margin: 0;
}
<head>
<title>500</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.2/css/bulma.min.css">
</head>
.rails-default-error-page div.dialog {
width: 95%;
max-width: 33em;
margin: 4em auto 0;
}
<body>
<div class="container is-fluid">
<div class="columns is-centered is-mobile">
<div class="column is-four-fifths-mobile is-two-thirds-tablet is-half-desktop has-text-centered">
<section class="hero">
<div class="hero-body">
<div class="container">
<h1>
<i class="far fa-frown-open fa-9x"></i>
</h1>
.rails-default-error-page div.dialog > div {
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #BBB;
border-top: #B00100 solid 4px;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
background-color: white;
padding: 7px 12% 0;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
.rails-default-error-page h1 {
font-size: 100%;
color: #730E15;
line-height: 1.5em;
}
.rails-default-error-page div.dialog > p {
margin: 0 0 1em;
padding: 1em;
background-color: #F7F7F7;
border: 1px solid #CCC;
border-right-color: #999;
border-left-color: #999;
border-bottom-color: #999;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-top-color: #DADADA;
color: #666;
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
}
</style>
</head>
<body class="rails-default-error-page">
<!-- This file lives in public/500.html -->
<div class="dialog">
<div>
<h1>We're sorry, but something went wrong.</h1>
<h1 class="title" style="padding:2rem;">
A system error occurred. Please contact support if problem continues.
</h1>
</div>
</div>
</section>
</div>
</div>
</div>
<p>If you are the application owner check the logs for more information.</p>
</div>
</body>
</body>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</html>

View File

@ -3842,6 +3842,11 @@ jest-worker@^25.1.0:
merge-stream "^2.0.0"
supports-color "^7.0.0"
jquery@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2"
integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==
js-base64@^2.1.8:
version "2.5.2"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209"