diff --git a/app/assets/images/check-circle.svg b/app/assets/images/check-circle.svg new file mode 100644 index 0000000..6fe171b --- /dev/null +++ b/app/assets/images/check-circle.svg @@ -0,0 +1,3 @@ + diff --git a/app/assets/images/exclamation-circle.svg b/app/assets/images/exclamation-circle.svg new file mode 100644 index 0000000..f0f1cb7 --- /dev/null +++ b/app/assets/images/exclamation-circle.svg @@ -0,0 +1,3 @@ + diff --git a/app/assets/images/information-circle.svg b/app/assets/images/information-circle.svg new file mode 100644 index 0000000..0da5f6a --- /dev/null +++ b/app/assets/images/information-circle.svg @@ -0,0 +1,3 @@ + diff --git a/app/components/flash_alerts.rb b/app/components/flash_alerts.rb new file mode 100644 index 0000000..7a7a310 --- /dev/null +++ b/app/components/flash_alerts.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class Components::FlashAlerts < Components::Base + attr_reader :flash + + def initialize(flash) + @flash = flash + end + def view_template + flash.keys.each do |key| + variant = find_variant(key) + + RBUI::Alert(variant: variant, class: find_alert_class(variant), data: { controller: "flash-alerts" }) do + div(class: "w-5 inline-block me-2") { render find_icon(variant) } + render RBUI::AlertDescription.new { flash[key] } + end + end + end + + def find_variant(key) + case key + when "notice" + :success + when "alert" + :destructive + end + end + + def find_icon(variant) + case variant + when :success + InlineSvg.new("check-circle.svg") + when :destructive + InlineSvg.new("exclamation-circle.svg") + else + InlineSvg.new("information-circle.svg") + end + end + + def find_alert_class(variant) + base_class = "flex items-center mb-3" + case variant + when :success + [ base_class, "text-primary ring-primary/20" ].join(" ") + when :destructive + [ base_class, "text-red-500 ring-red-500/20" ].join(" ") + else + [ base_class, "text-gray-500 ring-gray-500/20" ].join(" ") + end + end +end diff --git a/app/components/form_card.rb b/app/components/form_card.rb new file mode 100644 index 0000000..8f1f76c --- /dev/null +++ b/app/components/form_card.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class Components::FormCard < Components::Base + def view_template(&block) + div(class: base_class) { yield } + end + + def base_class + " + w-full max-w-sm p-4 bg-white border border-gray-200 rounded-lg shadowsm:p-6 md:p-8 + dark:bg-gray-800 dark:border-gray-700 + " + end +end diff --git a/app/components/forms/inputable.rb b/app/components/forms/inputable.rb new file mode 100644 index 0000000..1cd120c --- /dev/null +++ b/app/components/forms/inputable.rb @@ -0,0 +1,34 @@ +module Components::Forms::Inputable + def label_class + "block mb-2 text-sm font-medium text-gray-900 dark:text-white" + end + + def input_class + " + bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 + block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white + " + end + + def floating_label_input_class + "block py-2.5 px-0 w-full text-sm bg-transparent border-0 border-b-2 appearance-none focus:outline-none focus:ring-0 peer" + end + + def floating_label_class + " + peer-focus:font-medium text-gray-500 peer-focus:text-primary absolute text-sm duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] + peer-focus:start-0 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto peer-placeholder-shown:scale-100 + peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6 + " + end + + def floating_label_input(form, attr, field_type = :text_field, options = {}) + base_class = "relative z-0 w-full mb-5 group" + combined_class = [ base_class, options.delete(:class) ].join(" ") + + div(class: combined_class) { + form.send(field_type, attr, class: floating_label_input_class, placeholder: " ", **options) + form.label attr, class: floating_label_class + } + end +end diff --git a/app/components/forms/submit.rb b/app/components/forms/submit.rb new file mode 100644 index 0000000..8f45cf7 --- /dev/null +++ b/app/components/forms/submit.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Components::Forms::Submit < Components::Base + def initialize(**options) + @options = options + end + + def view_template(&block) + render RBUI::Button.new(type: :submit, class: "w-full py-5", **@options) { yield } + end +end diff --git a/app/components/navigation.rb b/app/components/navigation.rb index 2d24eeb..c3162e1 100644 --- a/app/components/navigation.rb +++ b/app/components/navigation.rb @@ -5,7 +5,7 @@ class Components::Navigation < Components::Base def view_template nav(class: nav_classes) { - div(class: "max-w-screen-xl flex flex-wrap items-center justify-between mx-auto px-4 py-2") { + div(class: "flex flex-wrap items-center justify-between mx-auto px-4 py-2") { link_to(root_path, class: logo_classes) { "PostIt" } diff --git a/app/components/passwords/new.rb b/app/components/passwords/new.rb new file mode 100644 index 0000000..2a35901 --- /dev/null +++ b/app/components/passwords/new.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Components::Passwords::New < Components::Base + include Phlex::Rails::Helpers::FormWith + include Components::Forms::Inputable + + def view_template + render Components::FormCard do + form_with url: passwords_path, class: "space-y-6" do |form| + h1(class: "font-bold text-2xl") { "Forgot your password?" } + div { + render RBUI::TypographySmall.new { "Enter your email address and we'll send you a link to reset your password." } + } + div { + floating_label_input( + form, :email_address, :email_field, + required: true, autocomplete: "username", + autofocus: true, value: helpers.params[:email_address] + ) + } + div { + render Components::Forms::Submit.new(class: "mt-3") { "Email reset instructions" } + } + + div { + RBUI::Link(href: new_session_path, class: "px-0") { "Sign in" } + } + end + end + end +end diff --git a/app/components/sessions/new.rb b/app/components/sessions/new.rb new file mode 100644 index 0000000..89993d4 --- /dev/null +++ b/app/components/sessions/new.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Components::Sessions::New < Components::Base + include Phlex::Rails::Helpers::FormWith + include Components::Forms::Inputable + + def view_template + render Components::FormCard do + form_with url: session_url, class: "space-y-6" do |form| + h1(class: "font-bold text-2xl") { "Sign in" } + div { + floating_label_input(form, :email_address, :email_field, required: true) + } + div { + floating_label_input(form, :password, :password_field, required: true) + } + div { + RBUI::Link(href: new_password_path, class: "px-0") { "Forgot password?" } + } + div { + render Components::Forms::Submit.new { "Sign into your account" } + } + div(class: "mt-4 flex items-center") { + RBUI::TypographyMuted() { "Not signed up yet?" } + RBUI::Link(href: "#") { "Create account" } + } + end + end + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5faea8a..3469b5a 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -9,7 +9,7 @@ def new def create if user = User.authenticate_by(params.permit(:email_address, :password)) start_new_session_for user - redirect_to after_authentication_url + redirect_to after_authentication_url, notice: "Welcome back #{user.name}!" else redirect_to new_session_url, alert: "Try another email address or password." end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be79..d8e5b47 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,3 @@ module ApplicationHelper + include Components::Forms::Inputable end diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js index 1213e85..1e2a4f8 100644 --- a/app/javascript/controllers/application.js +++ b/app/javascript/controllers/application.js @@ -7,3 +7,6 @@ application.debug = false window.Stimulus = application export { application } + +import RBUI from "rbui-js"; +RBUI.initialize(application); \ No newline at end of file diff --git a/app/javascript/controllers/flash_alerts_controller.js b/app/javascript/controllers/flash_alerts_controller.js new file mode 100644 index 0000000..8b7d917 --- /dev/null +++ b/app/javascript/controllers/flash_alerts_controller.js @@ -0,0 +1,14 @@ +import { Controller } from "@hotwired/stimulus" + +// Connects to data-controller="flash-alerts" +export default class extends Controller { + connect() { + this.closeAlert() + } + + closeAlert() { + setTimeout(() => { + this.element.classList.add('hidden') + }, 5000) + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 79307ff..393b0e7 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -4,9 +4,8 @@ import { application } from "./application" +import FlashAlertsController from "./flash_alerts_controller" +application.register("flash-alerts", FlashAlertsController) + import HelloController from "./hello_controller" application.register("hello", HelloController) - - -import RBUI from "rbui-js"; -RBUI.initialize(application); \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 048f13f..aebb192 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -31,12 +31,15 @@ <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module", defer: true %> -
- <%= render Components::Navigation.new %> - - -<%= alert %>
- <% end %> - -<%= notice %>
- <% end %> - <% content_for :title, "Posts" %><%= alert %>
- <% end %> - - <% if notice = flash[:notice] %> -<%= notice %>
- <% end %> - -