maquina 0.1.0

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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/images/maquina/maquina.svg +18 -0
  6. data/app/assets/javascripts/maquina/application.js +4 -0
  7. data/app/assets/javascripts/maquina/controllers/alert_controller.js +29 -0
  8. data/app/assets/javascripts/maquina/controllers/application.js +9 -0
  9. data/app/assets/javascripts/maquina/controllers/file_controller.js +60 -0
  10. data/app/assets/javascripts/maquina/controllers/index.js +11 -0
  11. data/app/assets/javascripts/maquina/controllers/mobile_menu_controller.js +31 -0
  12. data/app/assets/javascripts/maquina/controllers/modal_controller.js +39 -0
  13. data/app/assets/javascripts/maquina/controllers/modal_open_controller.js +15 -0
  14. data/app/assets/javascripts/maquina/controllers/popup_menu_controller.js +17 -0
  15. data/app/assets/javascripts/maquina/controllers/submit_form_controller.js +11 -0
  16. data/app/assets/stylesheets/maquina/application.css +15 -0
  17. data/app/assets/stylesheets/maquina/application.tailwind.css +102 -0
  18. data/app/controllers/concerns/maquina/authenticate.rb +41 -0
  19. data/app/controllers/concerns/maquina/create.rb +27 -0
  20. data/app/controllers/concerns/maquina/destroy.rb +28 -0
  21. data/app/controllers/concerns/maquina/edit.rb +29 -0
  22. data/app/controllers/concerns/maquina/index.rb +33 -0
  23. data/app/controllers/concerns/maquina/new.rb +22 -0
  24. data/app/controllers/concerns/maquina/resourceful.rb +180 -0
  25. data/app/controllers/concerns/maquina/show.rb +27 -0
  26. data/app/controllers/concerns/maquina/update.rb +31 -0
  27. data/app/controllers/maquina/accept_invitations_controller.rb +28 -0
  28. data/app/controllers/maquina/application_controller.rb +19 -0
  29. data/app/controllers/maquina/dashboard_controller.rb +16 -0
  30. data/app/controllers/maquina/invitations_controller.rb +51 -0
  31. data/app/controllers/maquina/plans_controller.rb +56 -0
  32. data/app/controllers/maquina/sessions_controller.rb +56 -0
  33. data/app/controllers/maquina/unauthorized_controller.rb +9 -0
  34. data/app/controllers/maquina/users_controller.rb +24 -0
  35. data/app/helpers/maquina/application_helper.rb +19 -0
  36. data/app/helpers/maquina/navbar_menu_helper.rb +29 -0
  37. data/app/helpers/maquina/views_helper.rb +9 -0
  38. data/app/jobs/maquina/application_job.rb +4 -0
  39. data/app/mailers/maquina/application_mailer.rb +8 -0
  40. data/app/mailers/maquina/user_notifications_mailer.rb +16 -0
  41. data/app/models/concerns/maquina/authenticate_by.rb +33 -0
  42. data/app/models/concerns/maquina/blockeable.rb +28 -0
  43. data/app/models/concerns/maquina/multifactor.rb +80 -0
  44. data/app/models/concerns/maquina/retain_passwords.rb +32 -0
  45. data/app/models/concerns/maquina/searchable.rb +24 -0
  46. data/app/models/maquina/active_session.rb +33 -0
  47. data/app/models/maquina/application_record.rb +11 -0
  48. data/app/models/maquina/current.rb +21 -0
  49. data/app/models/maquina/invitation.rb +15 -0
  50. data/app/models/maquina/plan.rb +38 -0
  51. data/app/models/maquina/used_password.rb +17 -0
  52. data/app/models/maquina/user.rb +33 -0
  53. data/app/policies/maquina/application_policy.rb +50 -0
  54. data/app/policies/maquina/invitation_policy.rb +13 -0
  55. data/app/policies/maquina/navigation_policy.rb +13 -0
  56. data/app/policies/maquina/plan_policy.rb +49 -0
  57. data/app/policies/maquina/user_policy.rb +27 -0
  58. data/app/views/layouts/maquina/application.html.erb +26 -0
  59. data/app/views/layouts/maquina/mailer.html.erb +377 -0
  60. data/app/views/layouts/maquina/mailer.text.erb +12 -0
  61. data/app/views/layouts/maquina/sessions.html.erb +24 -0
  62. data/app/views/maquina/accept_invitations/new.html.erb +9 -0
  63. data/app/views/maquina/accept_invitations/new_view.rb +41 -0
  64. data/app/views/maquina/application/_navbar.html.erb +21 -0
  65. data/app/views/maquina/application/alert.rb +104 -0
  66. data/app/views/maquina/application/components/action_text_component.rb +20 -0
  67. data/app/views/maquina/application/components/checkbox_component.rb +21 -0
  68. data/app/views/maquina/application/components/component_base.rb +60 -0
  69. data/app/views/maquina/application/components/file_component.rb +59 -0
  70. data/app/views/maquina/application/components/input_component.rb +20 -0
  71. data/app/views/maquina/application/components/select_component.rb +44 -0
  72. data/app/views/maquina/application/create.turbo_stream.erb +11 -0
  73. data/app/views/maquina/application/edit.html.erb +9 -0
  74. data/app/views/maquina/application/edit.rb +17 -0
  75. data/app/views/maquina/application/form.rb +77 -0
  76. data/app/views/maquina/application/index.html.erb +10 -0
  77. data/app/views/maquina/application/index_header.rb +46 -0
  78. data/app/views/maquina/application/index_modal.rb +43 -0
  79. data/app/views/maquina/application/index_table.rb +121 -0
  80. data/app/views/maquina/application/new.html.erb +9 -0
  81. data/app/views/maquina/application/new.rb +18 -0
  82. data/app/views/maquina/application/sessions_header.rb +31 -0
  83. data/app/views/maquina/application/show.html.erb +1 -0
  84. data/app/views/maquina/application/update.turbo_stream.erb +11 -0
  85. data/app/views/maquina/application_view.rb +46 -0
  86. data/app/views/maquina/dashboard/index.html.erb +0 -0
  87. data/app/views/maquina/invitations/create.turbo_stream.erb +13 -0
  88. data/app/views/maquina/invitations/new.html.erb +3 -0
  89. data/app/views/maquina/navbar/menu.rb +62 -0
  90. data/app/views/maquina/navbar/menu_item_link.rb +34 -0
  91. data/app/views/maquina/navbar/mobile_button.rb +29 -0
  92. data/app/views/maquina/navbar/mobile_menu.rb +47 -0
  93. data/app/views/maquina/navbar/notification.rb +37 -0
  94. data/app/views/maquina/navbar/profile.rb +16 -0
  95. data/app/views/maquina/navbar/profile_button.rb +24 -0
  96. data/app/views/maquina/navbar/profile_menu.rb +108 -0
  97. data/app/views/maquina/navbar/profile_menu_item_link.rb +41 -0
  98. data/app/views/maquina/navbar/search.rb +40 -0
  99. data/app/views/maquina/navbar/title.rb +22 -0
  100. data/app/views/maquina/sessions/create.turbo_stream.erb +11 -0
  101. data/app/views/maquina/sessions/form.rb +56 -0
  102. data/app/views/maquina/sessions/new.html.erb +9 -0
  103. data/app/views/maquina/unauthorized/401.html.erb +1 -0
  104. data/app/views/maquina/user_notifications_mailer/invitation_email.html.erb +40 -0
  105. data/app/views/maquina/user_notifications_mailer/invitation_email.text.erb +12 -0
  106. data/config/definitions.rb +1 -0
  107. data/config/initializers/importmap.rb +17 -0
  108. data/config/initializers/money.rb +116 -0
  109. data/config/initializers/pagy.rb +235 -0
  110. data/config/locales/flash.en.yml +44 -0
  111. data/config/locales/forms.en.yml +58 -0
  112. data/config/locales/mailers.en.yml +35 -0
  113. data/config/locales/models.en.yml +34 -0
  114. data/config/locales/routes.en.yml +7 -0
  115. data/config/locales/views.en.yml +45 -0
  116. data/config/routes.rb +17 -0
  117. data/db/migrate/20221109010726_create_maquina_plans.rb +13 -0
  118. data/db/migrate/20221113000409_create_maquina_users.rb +19 -0
  119. data/db/migrate/20221113020108_create_maquina_used_passwords.rb +10 -0
  120. data/db/migrate/20221115223414_create_maquina_active_sessions.rb +15 -0
  121. data/db/migrate/20230201203922_create_maquina_invitations.rb +12 -0
  122. data/db/schema.rb +1 -0
  123. data/lib/generators/maquina/install_generator.rb +32 -0
  124. data/lib/generators/maquina/install_templates/install_templates_generator.rb +31 -0
  125. data/lib/generators/maquina/tailwind_config/tailwind_config_generator.rb +11 -0
  126. data/lib/generators/maquina/tailwind_config/templates/app/assets/config/maquina/tailwind.config.js.tt +68 -0
  127. data/lib/generators/maquina/templates/config/initializers/maquina.rb +3 -0
  128. data/lib/maquina/engine.rb +17 -0
  129. data/lib/maquina/version.rb +3 -0
  130. data/lib/maquina.rb +48 -0
  131. data/lib/tasks/install.rake +19 -0
  132. data/lib/tasks/maquina_tasks.rake +4 -0
  133. data/lib/tasks/tailwind.rake +25 -0
  134. metadata +456 -0
@@ -0,0 +1,377 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" xmlns:v="urn:schemas-microsoft-com:vml">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="x-apple-disable-message-reformatting">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
8
+ <meta name="color-scheme" content="light dark">
9
+ <meta name="supported-color-schemes" content="light dark">
10
+ <!--[if mso]>
11
+ <noscript>
12
+ <xml>
13
+ <o:OfficeDocumentSettings xmlns:o="urn:schemas-microsoft-com:office:office">
14
+ <o:PixelsPerInch>96</o:PixelsPerInch>
15
+ </o:OfficeDocumentSettings>
16
+ </xml>
17
+ </noscript>
18
+ <style>
19
+ td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
20
+ </style>
21
+ <![endif]-->
22
+
23
+ <style>
24
+ /* Tailwind components that are generated by plugins */
25
+ /**
26
+ * @import here any custom components - classes that you'd want loaded
27
+ * before the Tailwind utilities, so that the utilities could still
28
+ * override them.
29
+ */
30
+ .button {
31
+ display: inline-block;
32
+ color: #fff;
33
+ text-decoration-line: none;
34
+ background-color: #3869D4;
35
+ border-top: 10px solid #3869D4;
36
+ border-right: 18px solid #3869D4;
37
+ border-bottom: 10px solid #3869D4;
38
+ border-left: 18px solid #3869D4;
39
+ border-radius: 3px;
40
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
41
+ }
42
+ .button--green {
43
+ background-color: #22BC66;
44
+ border-top: 10px solid #22BC66;
45
+ border-right: 18px solid #22BC66;
46
+ border-bottom: 10px solid #22BC66;
47
+ border-left: 18px solid #22BC66;
48
+ }
49
+ .button--red {
50
+ background-color: #FF6136;
51
+ border-top: 10px solid #FF6136;
52
+ border-right: 18px solid #FF6136;
53
+ border-bottom: 10px solid #FF6136;
54
+ border-left: 18px solid #FF6136;
55
+ }
56
+ @media (max-width: 600px) {
57
+ .button {
58
+ display: block !important;
59
+ text-align: center !important;
60
+ }
61
+ }
62
+ .purchase_heading {
63
+ border-bottom-width: 1px;
64
+ border-bottom-color: #EAEAEC;
65
+ border-bottom-style: solid;
66
+ }
67
+ .purchase_heading p {
68
+ margin: 0;
69
+ font-size: 12px;
70
+ line-height: 24px;
71
+ color: #85878E;
72
+ }
73
+ .purchase_footer {
74
+ padding-top: 16px;
75
+ vertical-align: middle;
76
+ font-size: 16px;
77
+ border-top-width: 1px;
78
+ border-top-color: #EAEAEC;
79
+ border-top-style: solid;
80
+ }
81
+ .body-sub {
82
+ margin-top: 25px;
83
+ border-top-width: 1px;
84
+ padding-top: 25px;
85
+ border-top-color: #EAEAEC;
86
+ border-top-style: solid;
87
+ }
88
+ .discount {
89
+ width: 100%;
90
+ background-color: #F4F4F7;
91
+ padding: 96px;
92
+ border: 2px dashed #cbcccf;
93
+ }
94
+ @media (prefers-color-scheme: dark) {
95
+ body,
96
+ .email-body,
97
+ .email-body_inner,
98
+ .email-content,
99
+ .email-wrapper,
100
+ .email-masthead,
101
+ .email-footer {
102
+ background-color: #333333 !important;
103
+ color: #fff !important;
104
+ }
105
+
106
+ p,
107
+ ul,
108
+ ol,
109
+ blockquote,
110
+ h1,
111
+ h2,
112
+ h3 {
113
+ color: #fff !important;
114
+ }
115
+
116
+ .attributes_content,
117
+ .discount {
118
+ background-color: #222222 !important;
119
+ }
120
+
121
+ .email-masthead_name {
122
+ text-shadow: none !important;
123
+ }
124
+ }
125
+ /* Tailwind utility classes */
126
+ .m-0 {
127
+ margin: 0;
128
+ }
129
+ .w-auto {
130
+ width: auto;
131
+ }
132
+ .mx-auto {
133
+ margin-left: auto;
134
+ margin-right: auto;
135
+ }
136
+ .my-7 {
137
+ margin-top: 28px;
138
+ margin-bottom: 28px;
139
+ }
140
+ .my-7_5 {
141
+ margin-top: 30px;
142
+ margin-bottom: 30px;
143
+ }
144
+ .mb-5 {
145
+ margin-bottom: 20px;
146
+ }
147
+ .mb-21px {
148
+ margin-bottom: 21px;
149
+ }
150
+ .mb-5px {
151
+ margin-bottom: 5px;
152
+ }
153
+ .mt-0 {
154
+ margin-top: 0;
155
+ }
156
+ .mt-1 {
157
+ margin-top: 4px;
158
+ }
159
+ .mt-1_5 {
160
+ margin-top: 6px;
161
+ }
162
+ .m-2_5 {
163
+ margin-top: 10px;
164
+ }
165
+ .h-8 {
166
+ height: 32px;
167
+ }
168
+ .block {
169
+ display: block;
170
+ }
171
+ .table {
172
+ display: table;
173
+ }
174
+ .hidden {
175
+ display: none;
176
+ }
177
+ .w-1-5 {
178
+ width: 20%;
179
+ }
180
+ .w-4-5 {
181
+ width: 80%;
182
+ }
183
+ .w-570px {
184
+ width: 570px;
185
+ }
186
+ .w-full {
187
+ width: 100%;
188
+ }
189
+ .bg-gray-postmark-lighter {
190
+ background-color: #94a3b8;
191
+ }
192
+ .bg-gray-postmark-lightest {
193
+ background-color: #F4F4F7;
194
+ }
195
+ .bg-white {
196
+ background-color: #fff;
197
+ }
198
+ .p-0 {
199
+ padding: 0;
200
+ }
201
+ .p-4 {
202
+ padding: 16px;
203
+ }
204
+ .p-45px {
205
+ padding: 45px;
206
+ }
207
+ .py-2 {
208
+ padding-top: 8px;
209
+ padding-bottom: 8px;
210
+ }
211
+ .py-2_5 {
212
+ padding-top: 10px;
213
+ padding-bottom: 10px;
214
+ }
215
+ .py-25px {
216
+ padding-top: 25px;
217
+ padding-bottom: 25px;
218
+ }
219
+ .py-35px {
220
+ padding-top: 35px;
221
+ padding-bottom: 35px;
222
+ }
223
+ .pb-2 {
224
+ padding-bottom: 8px;
225
+ }
226
+ .pb-4 {
227
+ padding-bottom: 16px;
228
+ }
229
+ .pr-4 {
230
+ padding-right: 16px;
231
+ }
232
+ .pt-25px {
233
+ padding-top: 25px;
234
+ }
235
+ .text-left {
236
+ text-align: left;
237
+ }
238
+ .text-center {
239
+ text-align: center;
240
+ }
241
+ .text-right {
242
+ text-align: right;
243
+ }
244
+ .font-sans {
245
+ font-family: "Nunito Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
246
+ }
247
+ .text-2xl {
248
+ font-size: 24px;
249
+ }
250
+ .text-base {
251
+ font-size: 16px;
252
+ }
253
+ .text-sm {
254
+ font-size: 14px;
255
+ }
256
+ .text-xs {
257
+ font-size: 12px;
258
+ }
259
+ .font-bold {
260
+ font-weight: 700;
261
+ }
262
+ .leading-4 {
263
+ line-height: 16px;
264
+ }
265
+ .leading-4_5 {
266
+ line-height: 18px;
267
+ }
268
+ .leading-6 {
269
+ line-height: 24px;
270
+ }
271
+ .text-blue-postmark {
272
+ color: #0063EB;
273
+ }
274
+ .text-gray-postmark-dark {
275
+ color: #51545E;
276
+ }
277
+ .text-gray-postmark-darker {
278
+ color: #333333;
279
+ }
280
+ .text-gray-postmark-light {
281
+ color: #64748b;
282
+ }
283
+ .text-gray-postmark-meta {
284
+ color: #85878E;
285
+ }
286
+ .-webkit-font-smoothing-antialiased {
287
+ -webkit-font-smoothing: antialiased;
288
+ }
289
+ .text-decoration-none {
290
+ text-decoration: none;
291
+ }
292
+ .text-shadow-0_1px_0__FFF {
293
+ text-shadow: 0 1px 0 #FFF;
294
+ }
295
+ .word-break-break-word {
296
+ word-break: break-word;
297
+ }
298
+ /* Your custom utility classes */
299
+ /*
300
+ * Here is where you can define your custom utility classes.
301
+ *
302
+ * We wrap them in the `utilities` @layer directive, so
303
+ * that Tailwind moves them to the correct location.
304
+ *
305
+ * More info:
306
+ * https://tailwindcss.com/docs/functions-and-directives#layer
307
+ */
308
+ :root {
309
+ color-scheme: light dark;
310
+ }
311
+ @media (max-width: 600px) {
312
+ .sm-w-full {
313
+ width: 100% !important;
314
+ }
315
+ }
316
+
317
+ </style>
318
+ </head>
319
+
320
+ <body class="m-0 p-0 w-full word-break-break-word -webkit-font-smoothing-antialiased ">
321
+ <div role="article" aria-roledescription="email" aria-label="" lang="en">
322
+ <table class="email-wrapper w-full font-sans" cellpadding="0" cellspacing="0" role="presentation">
323
+ <tr>
324
+ <td align="center">
325
+ <table class="email-content w-full" cellpadding="0" cellspacing="0" role="presentation">
326
+ <tr>
327
+ <td align="center" class="email-masthead py-25px text-base text-center">
328
+ <%= link_to main_app.root_url, class: "email-masthead_name text-base font-bold text-decoration-none text-gray-postmark-light text-shadow-0_1px_0__FFF" do %>
329
+ <%= image_tag "maquina/maquina.svg", class: "mx-auto h-8 w-auto" %>
330
+ <p class="leading-6"><%= t("application_name") %></p>
331
+ <% end %>
332
+ </td>
333
+ </tr>
334
+ <tr>
335
+ <td class="email-body w-full bg-white">
336
+ <table align="center" class="email-body_inner w-570px bg-white mx-auto sm-w-full" cellpadding="0" cellspacing="0" role="presentation">
337
+ <tr>
338
+ <td class="p-45px">
339
+ <div class="text-base">
340
+
341
+ <%= yield %>
342
+
343
+ </div>
344
+ </td>
345
+ </tr>
346
+ </table>
347
+ </td>
348
+ </tr>
349
+
350
+ <tr>
351
+ <td>
352
+ <table align="center" class="email-footer w-570px mx-auto text-center sm-w-full" cellpadding="0" cellspacing="0" role="presentation">
353
+ <tr>
354
+ <td align="center" class="content-cell p-45px text-base">
355
+ <p class="mt-1_5 mb-5 text-xs leading-6 text-center text-gray-postmark-light">
356
+ <%= t("mailers.copyright") %>
357
+ </p>
358
+ <p class="mt-1_5 mb-5 text-xs leading-6 text-center text-gray-postmark-light">
359
+ <%= t("mailers.company_name") %>
360
+
361
+ <br><%= t("mailers.address_1") %>
362
+ <br><%= t("mailers.address_2") %>
363
+ </p>
364
+ </td>
365
+ </tr>
366
+ </table>
367
+ </td>
368
+ </tr>
369
+
370
+ </table>
371
+ </td>
372
+ </tr>
373
+ </table>
374
+
375
+ </div>
376
+ </body>
377
+ </html>
@@ -0,0 +1,12 @@
1
+ <%= t("application_name") %>
2
+
3
+ <%= yield %>
4
+
5
+ -----------
6
+
7
+ <%= t("mailers.copyright") %>
8
+
9
+ <%= t("mailers.company_name") %>
10
+
11
+ <%= t("mailers.address_1") %>
12
+ <%= t("mailers.address_2") %>
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html class="h-full bg-gray-50">
3
+ <head>
4
+ <title><%= t("application_name") %></title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <%= csrf_meta_tags %>
7
+ <%= csp_meta_tag %>
8
+
9
+ <%= stylesheet_link_tag "maquina/tailwind", "inter-font", "data-turbo-track": "reload" %>
10
+ <%= stylesheet_link_tag "maquina/application", "data-turbo-track": "reload" %>
11
+
12
+ <%= maquina_importmap_tags %>
13
+ </head>
14
+
15
+ <body class="h-full">
16
+ <%= turbo_frame_tag :alert do %>
17
+ <%= render Maquina::Application::Alert.new(flash) %>
18
+ <% end %>
19
+
20
+ <main class="container mx-auto">
21
+ <%= yield %>
22
+ </main>
23
+ </body>
24
+ </html>
@@ -0,0 +1,9 @@
1
+ <div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
2
+ <%= render Maquina::Application::SessionsHeader.new(brand_icon: brand_icon) do |component| %>
3
+ <% component.description do %>
4
+ <%= t(".description", org: t("application_name")) %>
5
+ <% end %>
6
+ <% end %>
7
+
8
+ <%= render Maquina::AcceptInvitations::NewView.new(resource: @invitation) %>
9
+ </div>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module AcceptInvitations
5
+ class NewView < Phlex::HTML
6
+ include Maquina::ApplicationView
7
+ include Phlex::Rails::Helpers::FormWith
8
+ include Phlex::Rails::Helpers::HiddenField
9
+ include Phlex::Rails::Helpers::Routes
10
+
11
+ def initialize(resource:)
12
+ @resource = resource
13
+ end
14
+
15
+ def template
16
+ div(class: "mt-8 sm:mx-auto sm:w-full sm:max-w-md") do
17
+ div(class: "bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10") do
18
+ @resource.new_record? ? invalid_invitation : build_form
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def invalid_invitation
26
+ p(class: "text-center text-skin-muted") { t("maquina.accept_invitations.new.expired") }
27
+ end
28
+
29
+ def build_form
30
+ form_with(model: @resource, url: accept_invitations_path, method: :patch, local: true, class: "space-y-6", data: {"turbo-frame": "_top"}) do |form|
31
+ form.hidden_field :invitation_token
32
+ div(class: "field") do
33
+ div(class: "control") do
34
+ form.submit t("form.accept_invitations.submit"), class: "button button-accented w-full"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ <nav class="transition-all ease-in-out delay-300 backdrop-blur-sm bg-white shadow-sm fixed top-0 left-0 right-0 z-20" data-controller="mobile-menu" data-navbar-target="navbar">
2
+ <div class="max-w-7xl mx-auto px-2 sm:px-4 lg:px-8">
3
+ <div class="flex justify-between h-16">
4
+ <div class="flex px-2 lg:px-0">
5
+ <%= render Maquina::Navbar::Title.new(brand_icon: brand_icon) %>
6
+ <%= render Maquina::Navbar::Menu.new %>
7
+ </div>
8
+
9
+ <%= render Maquina::Navbar::Search.new(query: params[:q], url: collection_path) if action_name == "index" && respond_to?(:collection_path) && collection_path.present? %>
10
+
11
+ <%= render Maquina::Navbar::MobileButton.new %>
12
+
13
+ <div class="hidden sm:ml-6 lg:flex sm:items-center">
14
+ <%= render Maquina::Navbar::Notification.new %>
15
+ <%= render Maquina::Navbar::Profile.new %>
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+ <%= render Maquina::Navbar::MobileMenu.new %>
21
+ </nav>
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Application
5
+ class Alert < Phlex::HTML
6
+ include Maquina::ApplicationView
7
+
8
+ def initialize(flash)
9
+ notice = flash.notice
10
+ alert = flash.alert
11
+
12
+ @flash = notice || alert
13
+ @success = notice.present?
14
+ end
15
+
16
+ def template
17
+ return if @flash.blank?
18
+
19
+ div(aria_live: "assertive", class: "pointer-events-none fixed inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6 z-30", data: {controller: "alert", "turbo-cache": false}) do
20
+ div(class: "flex w-full flex-col items-center space-y-4 sm:items-end hidden", data: transition_attributes) do
21
+ div(class: "pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5") do
22
+ div(class: "p-4") do
23
+ div(class: "flex items-start") do
24
+ div(class: "flex-shrink-0") do
25
+ svg_icon(:outline, css_class: classes("h-6 w-6", alert_success?: "text-green-400", alert_failure?: "text-red-400").dig(:class), icon: alert_icon)
26
+ end
27
+ div(class: "ml-3 w-0 flex-1 pt-0.5") do
28
+ p(class: "text-sm font-medium text-skin-base") { alert_title }
29
+ p(class: "mt-1 text-sm text-skin-dimmed") { alert_description }
30
+ end
31
+ div(class: "ml-4 flex flex-shrink-0") do
32
+ button(type: "button", class: "inline-flex rounded-md bg-white text-gray-dimmed hover:text-skin-muted focus:outline-none focus:ring-2 focus:ring-skin-accented focus:ring-offset-2", data: {action: "alert#close"}) do
33
+ span(class: "sr-only") { "Close" }
34
+ svg_icon(:fill, view_box: "0 0 20 20", css_class: "h-5 w-5", icon: close_icon)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def alert_title
47
+ return @flash.dig(:title) || @flash.dig("title") if @flash.is_a?(Hash)
48
+
49
+ @flash
50
+ end
51
+
52
+ def alert_description
53
+ return @flash.dig(:description) || @flash.dig("description") if @flash.is_a?(Hash)
54
+
55
+ @flash
56
+ end
57
+
58
+ def alert_icon
59
+ alert_success? ? success_icon : failure_icon
60
+ end
61
+
62
+ def alert_success?
63
+ @success
64
+ end
65
+
66
+ def alert_failure?
67
+ !alert_success?
68
+ end
69
+
70
+ def transition_attributes
71
+ {
72
+ "alert-target": "alert",
73
+ "transition-enter": "transform ease-out duration-300 transition",
74
+ "transition-enter-active": "translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2",
75
+ "transition-enter-to": "translate-y-0 opacity-100 sm:translate-x-0",
76
+ "transition-leave": "transition ease-in duration-100",
77
+ "transition-leave-active": "opacity-100",
78
+ "transition-leave-to": "opacity-0"
79
+ }
80
+ end
81
+
82
+ def svg_attributes(attrs = {})
83
+ {
84
+ xmlns: "http://www.w3.org/2000/svg",
85
+ viewbox: "0 0 24 24",
86
+ fill: "currentColor",
87
+ aria_hidden: "true"
88
+ }.merge(attrs)
89
+ end
90
+
91
+ def success_icon
92
+ "M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
93
+ end
94
+
95
+ def failure_icon
96
+ "M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
97
+ end
98
+
99
+ def close_icon
100
+ "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Application
5
+ module Components
6
+ class ActionTextComponent < ComponentBase
7
+ def template
8
+ div(**control_html) do
9
+ @form.label attribute_name, class: "label #{label_css_class}"
10
+ div(class: "mt-1") do
11
+ @form.rich_text_area attribute_name, **input_html
12
+ help_template
13
+ error_template
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Application
5
+ module Components
6
+ class CheckboxComponent < ComponentBase
7
+ def template
8
+ div(**control_html) do
9
+ div(class: "flex h-5 items-center") do
10
+ @form.check_box attribute_name, **input_html
11
+ end
12
+ div(class: "text-sm leading-6") do
13
+ @form.label attribute_name, class: "label #{label_css_class}"
14
+ help_template
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Application
5
+ module Components
6
+ class ComponentBase < Phlex::HTML
7
+ include ApplicationView
8
+
9
+ def initialize(resource:, form:, options: {})
10
+ @resource = resource
11
+ @form = form
12
+ @options = options
13
+ end
14
+
15
+ protected
16
+
17
+ def control_html
18
+ options = @options.fetch(:control_html, {})
19
+ options[:class] ||= ""
20
+ options[:class] += " control-error" if @resource.errors[attribute_name].any?
21
+
22
+ options
23
+ end
24
+
25
+ def input_html(no_helpers: false)
26
+ options = @options.fetch(:input_html, {})
27
+
28
+ if !no_helpers
29
+ options[:maxlength] = t("helpers.maxlength.#{@resource.model_name.i18n_key}.#{attribute_name}", default: t("helpers.maxlength.default"))
30
+ options[:placeholder] = t("helpers.placeholder.#{@resource.model_name.i18n_key}.#{attribute_name}", default: nil)
31
+ end
32
+
33
+ options
34
+ end
35
+
36
+ def attribute_name
37
+ @attribute_name ||= @options.fetch(:name, "")
38
+ end
39
+
40
+ def label_css_class
41
+ @options.dig(:control_html, :label_class) || ""
42
+ end
43
+
44
+ def help_template
45
+ help = t("helpers.help.#{@resource.model_name.i18n_key}.#{attribute_name}", default: nil)
46
+ return if help.blank?
47
+
48
+ p(class: "help") { unsafe_raw help }
49
+ end
50
+
51
+ def error_template
52
+ error_message = @resource.errors[attribute_name].first
53
+ return if error_message.blank?
54
+
55
+ p(class: "help help-error") { error_message }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end