basecoat 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.idea/.gitignore +8 -0
  3. data/.idea/basecoat.iml +17 -0
  4. data/.idea/misc.xml +4 -0
  5. data/.idea/modules.xml +8 -0
  6. data/.idea/vcs.xml +6 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +194 -0
  9. data/Rakefile +4 -0
  10. data/lib/basecoat/railtie.rb +11 -0
  11. data/lib/basecoat/version.rb +5 -0
  12. data/lib/basecoat.rb +8 -0
  13. data/lib/generators/basecoat/templates/application.html.erb +16 -0
  14. data/lib/generators/basecoat/templates/devise/confirmations/new.html.erb +27 -0
  15. data/lib/generators/basecoat/templates/devise/mailer/confirmation_instructions.html.erb +5 -0
  16. data/lib/generators/basecoat/templates/devise/mailer/email_changed.html.erb +7 -0
  17. data/lib/generators/basecoat/templates/devise/mailer/password_change.html.erb +3 -0
  18. data/lib/generators/basecoat/templates/devise/mailer/reset_password_instructions.html.erb +8 -0
  19. data/lib/generators/basecoat/templates/devise/mailer/unlock_instructions.html.erb +7 -0
  20. data/lib/generators/basecoat/templates/devise/passwords/edit.html.erb +40 -0
  21. data/lib/generators/basecoat/templates/devise/passwords/new.html.erb +31 -0
  22. data/lib/generators/basecoat/templates/devise/registrations/edit.html.erb +63 -0
  23. data/lib/generators/basecoat/templates/devise/registrations/new.html.erb +40 -0
  24. data/lib/generators/basecoat/templates/devise/sessions/new.html.erb +38 -0
  25. data/lib/generators/basecoat/templates/devise/shared/_error_messages.html.erb +15 -0
  26. data/lib/generators/basecoat/templates/devise/shared/_links.html.erb +27 -0
  27. data/lib/generators/basecoat/templates/devise/unlocks/new.html.erb +29 -0
  28. data/lib/generators/basecoat/templates/devise.html.erb +36 -0
  29. data/lib/generators/basecoat/templates/layouts/_alert.html.erb +6 -0
  30. data/lib/generators/basecoat/templates/layouts/_aside.html.erb +21 -0
  31. data/lib/generators/basecoat/templates/layouts/_form_errors.html.erb +13 -0
  32. data/lib/generators/basecoat/templates/layouts/_head.html.erb +21 -0
  33. data/lib/generators/basecoat/templates/layouts/_header.html.erb +12 -0
  34. data/lib/generators/basecoat/templates/layouts/_notice.html.erb +18 -0
  35. data/lib/generators/basecoat/templates/pagy.scss +31 -0
  36. data/lib/generators/basecoat/templates/passwords/edit.html.erb +30 -0
  37. data/lib/generators/basecoat/templates/passwords/new.html.erb +25 -0
  38. data/lib/generators/basecoat/templates/scaffold_hook.rb +36 -0
  39. data/lib/generators/basecoat/templates/sessions/new.html.erb +30 -0
  40. data/lib/generators/basecoat/templates/sessions.html.erb +36 -0
  41. data/lib/tasks/basecoat.rake +281 -0
  42. data/lib/templates/erb/scaffold/_form.html.erb.tt +43 -0
  43. data/lib/templates/erb/scaffold/edit.html.erb.tt +21 -0
  44. data/lib/templates/erb/scaffold/index.html.erb.tt +35 -0
  45. data/lib/templates/erb/scaffold/new.html.erb.tt +20 -0
  46. data/lib/templates/erb/scaffold/partial.html.erb.tt +20 -0
  47. data/lib/templates/erb/scaffold/show.html.erb.tt +23 -0
  48. data/sig/basecoat.rbs +4 -0
  49. metadata +104 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8f01bc1f26666e2b22f7dce37ac14410959de6e6b59b8a6dcce926a810337c3c
4
+ data.tar.gz: d6c89871d8c7786c7919c2f2cb3ce42535590db0ca058febfe6f8a709f12207f
5
+ SHA512:
6
+ metadata.gz: 143506fdec46f25c4ac200bd81c5c8aa74363ec4cd3bc99c63921613a830e0a8f971579bcc766d57ce42ce3d7818792830a881cf05a50dab70b6c8f13fbdfd05
7
+ data.tar.gz: be5d2db1f6b559a1937e257e07cf8cb2ce98a138be26e99b145c7090fd476f363684ff19993288feae77858c2a79a86b51951c7ed217b596e36c5ac1ec0c3958
data/.idea/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="RUBY_MODULE" version="4">
3
+ <component name="ModuleRunConfigurationManager">
4
+ <shared />
5
+ </component>
6
+ <component name="NewModuleRootManager">
7
+ <content url="file://$MODULE_DIR$">
8
+ <sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
9
+ <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
10
+ <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
11
+ </content>
12
+ <orderEntry type="inheritedJdk" />
13
+ <orderEntry type="sourceFolder" forTests="false" />
14
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v1.17.2, ruby-2.6.10-p210) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="irb (v1.0.0, ruby-2.6.10-p210) [gem]" level="application" />
16
+ </component>
17
+ </module>
data/.idea/misc.xml ADDED
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" project-jdk-name="ruby-2.6.10-p210" project-jdk-type="RUBY_SDK" />
4
+ </project>
data/.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/basecoat.iml" filepath="$PROJECT_DIR$/.idea/basecoat.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
data/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Martijn Lafeber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # All of the shadcn/ui styling, all of the Rails power, no React
2
+
3
+ This gem provides you with amazing frontend based on [Basecoat CSS](https://basecoatui.com).
4
+ It is especially powerful for admin applications with a lot of CRUD actions.
5
+
6
+ Experience beautiful Rails scaffolds, pages for authentication and Devise, and pagy styling.
7
+ All responsive, all dark & light mode. See all the features below.
8
+
9
+ ## Why?
10
+
11
+ There is a new default component library for the web; it's called shadcn.
12
+ As a developer you are standing on the shoulders of a giant with every component they provide.
13
+ However... in many (most?) applications you don't need complicated components -
14
+ e.g. an input field can just be a html tag, not a separate component with its own shadow DOM.
15
+
16
+ This is where basecoat-css comes in.
17
+
18
+ You can still include complicated shadcn React components if you need them,
19
+ but most of your application is just the simple rails views, leveraging the power of Rails.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'basecoat'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ ```bash
32
+ bundle install
33
+ ```
34
+
35
+ **Note:** Basecoat requires Tailwind CSS. If you haven't installed it yet, follow the instructions at [https://github.com/rails/tailwindcss-rails](https://github.com/rails/tailwindcss-rails) to set up Tailwind CSS in your Rails application.
36
+
37
+ ## Usage
38
+
39
+ Run the rake tasks, run a scaffold and observe beauty.
40
+
41
+ ### Install Application Layout
42
+
43
+ Install the Basecoat application layout and partials:
44
+
45
+ ```bash
46
+ rake basecoat:install
47
+ ```
48
+
49
+ This will:
50
+ - Install `basecoat-css` via yarn/npm (if package.json exists) or importmap
51
+ - Add basecoat-css import to `app/javascript/application.js`
52
+ - Add view transition JavaScript for turbo frames
53
+ - Add dark mode toggle functionality
54
+ - Add view transition CSS animations and form validation styles
55
+ - Copy application layout to `app/views/layouts/application.html.erb`
56
+ - Copy layout partials (`_head.html.erb`, `_header.html.erb`, `_aside.html.erb`, `_notice.html.erb`, `_alert.html.erb`, `_form_errors.html.erb`)
57
+ - Copy scaffold hook initializer to `config/initializers/scaffold_hook.rb`
58
+
59
+ The scaffold templates are automatically available from the gem, so you can immediately generate scaffolds:
60
+
61
+ ```bash
62
+ rails generate scaffold Post title:string body:text published:boolean
63
+ ```
64
+
65
+ The generated views will include:
66
+ * Basecoat CSS styling
67
+ * Turbo Frame support for SPA-like navigation
68
+ * View transitions
69
+ * Responsive design
70
+ * Dark mode support
71
+ * Form validation with required fields
72
+ * Boolean fields styled as switches
73
+ * Automatic sidebar navigation links
74
+
75
+ ### Install Devise Views
76
+
77
+ Install the Basecoat-styled Devise views and layout:
78
+
79
+ ```bash
80
+ rake basecoat:install:devise
81
+ ```
82
+
83
+ This will copy:
84
+ - All Devise views to `app/views/devise/`
85
+ - Devise layout to `app/views/layouts/devise.html.erb`
86
+
87
+ The Devise views include:
88
+ - Beautiful login/signup forms
89
+ * Two-column layout with image placeholder
90
+ * Dark mode toggle
91
+ * Responsive design
92
+ * Password reset flows
93
+ * Email confirmation views
94
+
95
+ ### Install Pagy Pagination Styles
96
+
97
+ Install the Basecoat-styled Pagy pagination:
98
+
99
+ ```bash
100
+ rake basecoat:install:pagy
101
+ ```
102
+
103
+ This will copy:
104
+ - Pagy styles to `app/assets/stylesheets/pagy.scss`
105
+
106
+ The Pagy styles include:
107
+
108
+ * Basecoat CSS button styling using `@apply`
109
+ * Proper spacing and layout
110
+ * Active page highlighting
111
+ * Disabled state styling
112
+
113
+ ### Install Authentication Views
114
+
115
+ Install the Basecoat-styled authentication views (for Rails built-in authentication):
116
+
117
+ ```bash
118
+ rake basecoat:install:authentication
119
+ ```
120
+
121
+ This will copy:
122
+ - Sessions views to `app/views/sessions/`
123
+ - Passwords views to `app/views/passwords/`
124
+ - Sessions layout to `app/views/layouts/sessions.html.erb`
125
+ - Adds `layout "sessions"` to `app/controllers/passwords_controller.rb`
126
+
127
+ The authentication views include:
128
+
129
+ * Beautiful sign in form
130
+ * Password reset flows
131
+ * Two-column layout with image placeholder
132
+ * Dark mode toggle
133
+ * Responsive design
134
+ * Consistent styling with Devise views
135
+
136
+ ## Features
137
+
138
+ ### Application Layout
139
+
140
+ - **Sidebar Navigation**: Collapsible sidebar with automatic active state detection
141
+ - **Header**: User dropdown with sign out functionality
142
+ - **Alerts & Notices**: Beautiful toast notifications for flash messages
143
+ - **Form Errors**: Consistent error message styling
144
+ - **Dark Mode**: Built-in theme toggle
145
+
146
+ ### Scaffold Templates
147
+
148
+ - **Modern UI**: Clean, professional design using Basecoat CSS
149
+ - **Turbo Frames**: SPA-like navigation without full page reloads
150
+ - **View Transitions**: Smooth slide animations between pages
151
+ - **Smart Forms**: Automatic required field detection based on database schema
152
+ - **Auto Sidebar Links**: New scaffolds automatically add navigation links to sidebar
153
+ - **Dark Mode**: Built-in dark mode support
154
+ - **Responsive**: Mobile-first responsive design
155
+
156
+ ### Devise Views
157
+
158
+ - **Professional Design**: Matches modern authentication UI patterns
159
+ - **Two-Column Layout**: Image placeholder with form on desktop
160
+ - **Complete Views**: All Devise views including confirmations, passwords, registrations
161
+ - **Dark Mode**: Toggle between light and dark themes
162
+ - **Accessible**: ARIA labels and semantic HTML
163
+
164
+ ## Requirements
165
+
166
+ - Rails 8.0+
167
+ - Tailwind CSS ([installation instructions](https://github.com/rails/tailwindcss-rails))
168
+ - Basecoat CSS
169
+ - Turbo Rails (for scaffold templates)
170
+
171
+ ## How It Works
172
+
173
+ The gem uses Rails' template resolution system to provide scaffold templates automatically. When you run `rails generate scaffold`, Rails will use the templates from the Basecoat gem instead of the default ones.
174
+
175
+ The application layout and partials are copied to your application so you can customize them as needed.
176
+
177
+ ## Discussion
178
+
179
+ I tried not to be too opinionated with my scaffolds - they're very close to the original ones. With the exception of
180
+ using a turbo frame main_content. I love this too much not to include it.
181
+
182
+ However, I have my doubts with the index page reusing the show partial. Especially in admin applications you might want
183
+ to have a (responsive) table in the index.
184
+
185
+ Also, the arguably ugliest part of the views are the svg tags which contains the lovely lucide icons.
186
+ Since these icons are the default for shadcn I'm considering including https://github.com/heyvito/lucide-rails to clean up the views.
187
+
188
+ ## Contributing
189
+
190
+ Bug reports and pull requests are welcome on GitHub.
191
+
192
+ ## License
193
+
194
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,11 @@
1
+ module Basecoat
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load File.expand_path("../tasks/basecoat.rake", __dir__)
5
+ end
6
+
7
+ config.app_generators do |g|
8
+ g.templates.unshift File.expand_path("../templates", __dir__)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basecoat
4
+ VERSION = "0.1.1"
5
+ end
data/lib/basecoat.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "basecoat/version"
4
+ require_relative "basecoat/railtie" if defined?(Rails)
5
+
6
+ module Basecoat
7
+ class Error < StandardError; end
8
+ end
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <%= render "layouts/head" %>
4
+ <body>
5
+ <%= render 'layouts/alert', alert: alert if alert %>
6
+ <%= render "layouts/aside" %>
7
+ <main>
8
+ <%= render "layouts/header" %>
9
+ <div class="py-8">
10
+ <%= turbo_frame_tag "main_content" do %>
11
+ <%= yield %>
12
+ <% end %>
13
+ </div>
14
+ </main>
15
+ </body>
16
+ </html>
@@ -0,0 +1,27 @@
1
+ <div class="container mx-auto px-4 py-8">
2
+ <div class="max-w-2xl mx-auto">
3
+ <div class="card">
4
+ <header>
5
+ <h2>Resend confirmation instructions</h2>
6
+ <p>Enter your email address to receive confirmation instructions.</p>
7
+ </header>
8
+ <section class="space-y-4">
9
+ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: "grid gap-6" }) do |f| %>
10
+ <%= render "devise/shared/error_messages", resource: resource %>
11
+
12
+ <div class="grid gap-3">
13
+ <%= f.label :email, class: "label" %>
14
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), class: "input" %>
15
+ </div>
16
+
17
+ <div class="grid gap-3">
18
+ <%= f.submit "Resend confirmation instructions", class: "btn" %>
19
+ </div>
20
+ <% end %>
21
+ </section>
22
+ </div>
23
+ <div class="mt-6">
24
+ <%= render "devise/shared/links" %>
25
+ </div>
26
+ </div>
27
+ </div>
@@ -0,0 +1,5 @@
1
+ <p>Welcome <%= @email %>!</p>
2
+
3
+ <p>You can confirm your account email through the link below:</p>
4
+
5
+ <p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
@@ -0,0 +1,7 @@
1
+ <p>Hello <%= @email %>!</p>
2
+
3
+ <% if @resource.try(:unconfirmed_email?) %>
4
+ <p>We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.</p>
5
+ <% else %>
6
+ <p>We're contacting you to notify you that your email has been changed to <%= @resource.email %>.</p>
7
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>We're contacting you to notify you that your password has been changed.</p>
@@ -0,0 +1,8 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Someone has requested a link to change your password. You can do this through the link below.</p>
4
+
5
+ <p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
6
+
7
+ <p>If you didn't request this, please ignore this email.</p>
8
+ <p>Your password won't change until you access the link above and create a new one.</p>
@@ -0,0 +1,7 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
4
+
5
+ <p>Click the link below to unlock your account:</p>
6
+
7
+ <p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>
@@ -0,0 +1,40 @@
1
+
2
+
3
+
4
+
5
+ <div class="container mx-auto px-4 py-8">
6
+ <div class="max-w-2xl mx-auto">
7
+ <div class="card">
8
+ <header>
9
+ <h2>Change your password</h2>
10
+ <p>Enter your new password below.</p>
11
+ </header>
12
+ <section class="space-y-4">
13
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: "grid gap-6" }) do |f| %>
14
+ <%= render "devise/shared/error_messages", resource: resource %>
15
+ <%= f.hidden_field :reset_password_token %>
16
+
17
+ <div class="grid gap-3">
18
+ <%= f.label :password, "New password", class: "label" %>
19
+ <% if @minimum_password_length %>
20
+ <span class="text-sm text-muted-foreground">(<%= @minimum_password_length %> characters minimum)</span>
21
+ <% end %>
22
+ <%= f.password_field :password, autofocus: true, autocomplete: "new-password", class: "input" %>
23
+ </div>
24
+
25
+ <div class="grid gap-3">
26
+ <%= f.label :password_confirmation, "Confirm new password", class: "label" %>
27
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "input" %>
28
+ </div>
29
+
30
+ <div class="grid gap-3">
31
+ <%= f.submit "Change my password", class: "btn" %>
32
+ </div>
33
+ <% end %>
34
+ </section>
35
+ </div>
36
+ <div class="mt-6">
37
+ <%= render "devise/shared/links" %>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,31 @@
1
+
2
+
3
+
4
+
5
+ <div class="container mx-auto px-4 py-8">
6
+ <div class="max-w-2xl mx-auto">
7
+ <div class="card">
8
+ <header>
9
+ <h2>Forgot your password?</h2>
10
+ <p>Enter your email address and we'll send you instructions to reset your password.</p>
11
+ </header>
12
+ <section class="space-y-4">
13
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: "grid gap-6" }) do |f| %>
14
+ <%= render "devise/shared/error_messages", resource: resource %>
15
+
16
+ <div class="grid gap-3">
17
+ <%= f.label :email, class: "label" %>
18
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "input" %>
19
+ </div>
20
+
21
+ <div class="grid gap-3">
22
+ <%= f.submit "Send reset link", class: "btn" %>
23
+ </div>
24
+ <% end %>
25
+ </section>
26
+ </div>
27
+ <div class="mt-6">
28
+ <%= render "devise/shared/links" %>
29
+ </div>
30
+ </div>
31
+ </div>
@@ -0,0 +1,63 @@
1
+ <div class="container mx-auto px-4 py-8">
2
+ <div class="max-w-2xl mx-auto">
3
+ <div class="card">
4
+ <header>
5
+ <h2>Edit <%= resource_name.to_s.humanize %></h2>
6
+ <p>Update your account settings.</p>
7
+ </header>
8
+
9
+ <section class="space-y-4">
10
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: "grid gap-6" }) do |f| %>
11
+ <%= render "devise/shared/error_messages", resource: resource %>
12
+
13
+ <div class="grid gap-3">
14
+ <%= f.label :email, class: "label" %>
15
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "input" %>
16
+ </div>
17
+
18
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
19
+ <div class="text-sm text-muted-foreground">Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
20
+ <% end %>
21
+
22
+ <div class="grid gap-3">
23
+ <%= f.label :password, class: "label" %>
24
+ <span class="text-sm text-muted-foreground">(leave blank if you don't want to change it)</span>
25
+ <%= f.password_field :password, autocomplete: "new-password", class: "input" %>
26
+ <% if @minimum_password_length %>
27
+ <span class="text-sm text-muted-foreground"><%= @minimum_password_length %> characters minimum</span>
28
+ <% end %>
29
+ </div>
30
+
31
+ <div class="grid gap-3">
32
+ <%= f.label :password_confirmation, class: "label" %>
33
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "input" %>
34
+ </div>
35
+
36
+ <div class="grid gap-3">
37
+ <%= f.label :current_password, class: "label" %>
38
+ <span class="text-sm text-muted-foreground">(we need your current password to confirm your changes)</span>
39
+ <%= f.password_field :current_password, autocomplete: "current-password", class: "input" %>
40
+ </div>
41
+
42
+ <div class="grid gap-3">
43
+ <%= f.submit "Update", class: "btn" %>
44
+ </div>
45
+ <% end %>
46
+ </section>
47
+ </div>
48
+
49
+ <div class="card mt-6">
50
+ <header>
51
+ <h3>Cancel my account</h3>
52
+ <p>Unhappy? You can delete your account below.</p>
53
+ </header>
54
+ <footer>
55
+ <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete, class: "btn-outline" %>
56
+ </footer>
57
+ </div>
58
+
59
+ <div class="mt-6">
60
+ <%= link_to "Back", :back, class: "btn-ghost" %>
61
+ </div>
62
+ </div>
63
+ </div>
@@ -0,0 +1,40 @@
1
+ <%= render "devise/shared/error_messages", resource: resource %>
2
+
3
+ <div class="container mx-auto px-4 py-8">
4
+ <div class="max-w-2xl mx-auto">
5
+ <div class="card">
6
+ <header>
7
+ <h2>Sign up</h2>
8
+ <p>Create a new account to get started.</p>
9
+ </header>
10
+ <section class="space-y-4">
11
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "grid gap-6" }) do |f| %>
12
+ <div class="grid gap-2">
13
+ <%= f.label :email, class: "label" %>
14
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "input" %>
15
+ </div>
16
+
17
+ <div class="grid gap-2">
18
+ <%= f.label :password, class: "label" %>
19
+ <% if @minimum_password_length %>
20
+ <span class="text-sm text-muted-foreground">(<%= @minimum_password_length %> characters minimum)</span>
21
+ <% end %>
22
+ <%= f.password_field :password, autocomplete: "new-password", class: "input" %>
23
+ </div>
24
+
25
+ <div class="grid gap-2">
26
+ <%= f.label :password_confirmation, class: "label" %>
27
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "input" %>
28
+ </div>
29
+
30
+ <div class="grid gap-3">
31
+ <%= f.submit "Sign up", class: "btn" %>
32
+ </div>
33
+ <% end %>
34
+ </section>
35
+ </div>
36
+ <div class="mt-6">
37
+ <%= render "devise/shared/links" %>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,38 @@
1
+ <div class="container mx-auto px-4 py-8">
2
+ <div class="max-w-2xl mx-auto">
3
+ <div class="card">
4
+ <header>
5
+ <h2>Log in</h2>
6
+ <p>Sign in to your account to continue.</p>
7
+ </header>
8
+ <section class="space-y-4">
9
+ <%= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "grid gap-6" }) do |f| %>
10
+ <div class="grid gap-3">
11
+ <%= f.label :email, class: "label" %>
12
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "input" %>
13
+ </div>
14
+
15
+ <div class="grid gap-2">
16
+ <%= f.label :password, class: "label" %>
17
+ <%= f.password_field :password, autocomplete: "current-password", class: "input" %>
18
+ </div>
19
+
20
+ <% if devise_mapping.rememberable? %>
21
+ <div class="grid gap-2">
22
+ <%= f.label :remember_me, class: "label" do |builder| %>
23
+ <%= f.check_box :remember_me, class: "input", role: "switch" %>
24
+ <%= builder.translation %>
25
+ <% end %>
26
+ </div>
27
+ <% end %>
28
+ <div class="grid gap-2">
29
+ <%= f.submit "Log in", class: "btn" %>
30
+ </div>
31
+ <% end %>
32
+ </section>
33
+ </div>
34
+ <div class="mt-6">
35
+ <%= render "devise/shared/links" %>
36
+ </div>
37
+ </div>
38
+ </div>
@@ -0,0 +1,15 @@
1
+ <% if resource.errors.any? %>
2
+ <div class="alert-destructive max-w-2xl mx-auto" data-turbo-cache="false">
3
+ <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
4
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
5
+ </svg>
6
+ <h2><%= pluralize(resource.errors.count, "error") %> prohibited this <%= resource.class.model_name.human.downcase %> from being saved</h2>
7
+ <section>
8
+ <ul>
9
+ <% resource.errors.full_messages.each do |message| %>
10
+ <li><%= message %></li>
11
+ <% end %>
12
+ </ul>
13
+ </section>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <div class="flex flex-col gap-2 text-sm">
2
+ <%- if controller_name != 'sessions' %>
3
+ <%= link_to "Log in", new_session_path(resource_name), class: "btn-ghost" %>
4
+ <% end %>
5
+
6
+ <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
7
+ <%= link_to "Sign up", new_registration_path(resource_name), class: "btn-ghost" %>
8
+ <% end %>
9
+
10
+ <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
11
+ <%= link_to "Forgot your password?", new_password_path(resource_name), class: "btn-ghost" %>
12
+ <% end %>
13
+
14
+ <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
15
+ <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: "btn-ghost" %>
16
+ <% end %>
17
+
18
+ <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
19
+ <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: "btn-ghost" %>
20
+ <% end %>
21
+
22
+ <%- if devise_mapping.omniauthable? %>
23
+ <%- resource_class.omniauth_providers.each do |provider| %>
24
+ <%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false }, class: "btn-outline" %>
25
+ <% end %>
26
+ <% end %>
27
+ </div>