jquard 0.1.0 → 0.2.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/README.md +115 -21
  4. data/app/assets/javascripts/jquard/application.js +6 -0
  5. data/app/assets/stylesheets/jquard/application.css +1 -1
  6. data/app/controllers/concerns/jquard/auth_layout.rb +11 -0
  7. data/app/controllers/jquard/application_controller.rb +16 -0
  8. data/app/helpers/jquard/application_helper.rb +28 -0
  9. data/app/views/devise/confirmations/new.html.erb +14 -0
  10. data/app/views/devise/passwords/edit.html.erb +21 -0
  11. data/app/views/devise/passwords/new.html.erb +16 -0
  12. data/app/views/devise/registrations/edit.html.erb +45 -0
  13. data/app/views/devise/registrations/new.html.erb +29 -0
  14. data/app/views/devise/sessions/new.html.erb +33 -0
  15. data/app/views/devise/shared/_error_messages.html.erb +15 -0
  16. data/app/views/devise/shared/_links.html.erb +27 -0
  17. data/app/views/devise/unlocks/new.html.erb +14 -0
  18. data/app/views/jquard/_breadcrumbs.html.erb +17 -0
  19. data/app/views/jquard/_user_menu.html.erb +23 -0
  20. data/app/views/jquard/resources/edit.html.erb +4 -1
  21. data/app/views/jquard/resources/index.html.erb +11 -5
  22. data/app/views/jquard/resources/new.html.erb +4 -1
  23. data/app/views/layouts/jquard/application.html.erb +10 -12
  24. data/app/views/layouts/jquard/auth.html.erb +44 -0
  25. data/lib/generators/jquard/install/install_generator.rb +5 -1
  26. data/lib/generators/jquard/install/templates/initializer.rb.tt +11 -0
  27. data/lib/jquard/configuration.rb +8 -2
  28. data/lib/jquard/engine.rb +2 -0
  29. data/lib/jquard/tailwind/application.css +162 -16
  30. data/lib/jquard/version.rb +1 -1
  31. metadata +14 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9a254d53fe440d2cc877ee803519e859c381e4a9749cdf7983b61dc1af4417a
4
- data.tar.gz: 8e9367d15e0a8fb939828fdf7c16dd3c96a7192f313e5d8f23edf87d7a6a14b4
3
+ metadata.gz: 4ab4b65421346f5ec6f1fcf29a00390cfe5e67e53b3042570dd1339840622598
4
+ data.tar.gz: d1d46c2784039a9d9b4d8ce11931aa0cb206e8673c41daf29110f19932f92b6e
5
5
  SHA512:
6
- metadata.gz: f188d64fc35d805be9ad3d0c91f92ae15ef9b839a674c47c01530696e3fcb7392845b480ae181acce04dffb778319c1633102474f3efe88b4d473128dd0aaf20
7
- data.tar.gz: 254abf379f90e8205297a6df25a0315ef89b46b52e0c6b9357a06ddf14312d912ef7cc49cdf27edac1d9be445627230050ef0d555ed09ca41e7789a5896802be
6
+ metadata.gz: fa27435408b5ad8af104e4c1f2b0c693510b53f7849138b30b200ed37b1774af2c79595bab30853dfb8462d4059a5cbf1b5d4631c8e29fdcb0473b6a38dca00e
7
+ data.tar.gz: c68eae1736d3fb22cb9a6a03111731f2ccc8efb6a5801d10846613aaf2afab479deb743ef37a1dd780affd13ba8a5b8d045801544e73c9bd40c9228dd49ce7ee
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.0] - 2026-07-18
6
+
7
+ ### Added
8
+ - Authentication: `config.authenticate_with` hook, with Devise as the documented integration
9
+ - Filament-styled Devise views (sign in, password reset, registration) delivered from the engine
10
+ - User menu in the topbar with sign out
11
+ - Breadcrumbs on resource pages
12
+
13
+ ### Changed
14
+ - **Breaking:** the panel now refuses to serve unless authentication is configured.
15
+ Existing installs must add `config.authenticate_with { … }` to
16
+ `config/initializers/jquard.rb`, or opt into a public panel with an empty
17
+ block: `config.authenticate_with { }`. See the README.
18
+ - The brand name renders in black instead of the primary color
19
+
5
20
  ## [0.1.0] - 2026-07-17
6
21
 
7
22
  ### Added
data/README.md CHANGED
@@ -1,28 +1,22 @@
1
1
  # Jquard
2
2
 
3
- Build modern admin panels and apps in Ruby on Rails with a few lines of code.
3
+ Build modern admin panels and apps in Ruby on Rails.
4
4
 
5
- Jquard is a mountable Rails engine, inspired by [Filament](https://filamentphp.com) from the Laravel ecosystem. With Jquard, you can describe your models with a chainable Ruby DSL and get searchable, sortable, and paginated tables — styled with Tailwind and Heroicons, and driven by Rails defaults.
6
-
7
- The built-in generator reads your models and writes the first version for you, so you never start from a blank file.
5
+ Jquard is a mountable Rails engine, inspired by [Filament PHP](https://filamentphp.com). With Jquard, you can simply describe your models with a chainable Ruby DSL and get a beautiful and functional admin panel.
8
6
 
9
7
  ![The Jquard admin panel](https://raw.githubusercontent.com/jquard/jquard/main/docs/images/posts.png)
10
8
 
11
9
  ## What you get
12
10
 
13
- - **Tables** with search, sortable columns, pagination, status badges, and boolean icons.
14
- - **Forms** for creating and editing records, with section layouts, validation errors, and a "Saved" toast.
15
- - **Row actions** — edit, and delete with a confirmation dialog (not the browser's gray popup).
16
- - **A generator** that turns a model into a working resource in one command.
17
- - **Theming** — set a brand name and a primary color; the whole panel follows it.
18
-
19
- No JavaScript build step, no Tailwind config in your app. Jquard ships its own compiled CSS.
11
+ - **Beautiful Tables** with search, sortable columns, pagination, status badges, and boolean icons.
12
+ - **Beautiful Forms** for creating and editing records, with section layouts, and validation errors.
13
+ - **Authentication** — supports Devise authentication; Jquard restyles its screens to match the panel.
14
+ - **Generators** that magically turns a model into a working resource in one command.
20
15
 
21
16
  ## Requirements
22
17
 
23
18
  - Rails 8.0 or newer
24
19
  - Ruby 3.2 or newer
25
- - Hotwire (`turbo-rails`, included with new Rails apps)
26
20
 
27
21
  ## Getting started
28
22
 
@@ -64,6 +58,11 @@ $ bundle install
64
58
  $ bin/rails generate jquard:install
65
59
  ```
66
60
 
61
+ The generated `config/initializers/jquard.rb` starts with an **open panel** —
62
+ `config.authenticate_with { }` — so you can look around right away. Before you
63
+ deploy, replace it with real authentication: see
64
+ [Authentication](#authentication).
65
+
67
66
  ### 2. Generate a resource for your model
68
67
 
69
68
  ```bash
@@ -223,6 +222,109 @@ class CommentResource < Jquard::Resource
223
222
  end
224
223
  ```
225
224
 
225
+ ## Authentication
226
+
227
+ Jquard does not ship an authentication system. Your app owns the users, the
228
+ sessions, and the sign-in rules; Jquard hooks into whatever you already use and
229
+ restyles its screens to match the panel.
230
+
231
+ The install generator disables authentication with an empty block —
232
+ `config.authenticate_with { }`, which means *no authentication* and leaves the
233
+ panel public. That is fine while you build locally; for production you would want to add authentication.
234
+
235
+ Today [Devise](https://github.com/heartcombo/devise) is the documented and
236
+ supported option.
237
+
238
+ ### 1. Install Devise in your app
239
+
240
+ ```bash
241
+ $ bundle add devise
242
+ $ bin/rails generate devise:install
243
+ $ bin/rails generate devise User
244
+ $ bin/rails db:migrate
245
+ ```
246
+
247
+ For an admin panel you usually **do not** want public sign-up. Remove
248
+ `:registerable` from the generated model, and create your admins from the
249
+ console or seeds:
250
+
251
+ ```ruby
252
+ # app/models/user.rb
253
+ class User < ApplicationRecord
254
+ devise :database_authenticatable, :recoverable, :rememberable, :validatable
255
+ end
256
+ ```
257
+
258
+ ```ruby
259
+ # in bin/rails console
260
+ User.create!(email: "admin@example.com", password: "a-good-password")
261
+ ```
262
+
263
+ Keep `:registerable` if you *do* want sign-up (a SaaS app, for instance) —
264
+ Jquard ships registration views, and they'll be styled and ready to use.
265
+
266
+ ### 2. Point Jquard at it
267
+
268
+ In `config/initializers/jquard.rb`, replace the empty `authenticate_with { }`
269
+ block the generator wrote by uncommenting the three lines below it:
270
+
271
+ ```ruby
272
+ # config/initializers/jquard.rb
273
+ Jquard.configure do |config|
274
+ config.authenticate_with { authenticate_user! }
275
+ config.current_user_method = :current_user
276
+ config.sign_out_path = -> { main_app.destroy_user_session_path }
277
+ end
278
+ ```
279
+
280
+ - **`authenticate_with`** runs as a `before_action` on every panel page, in the
281
+ controller's context. Anything you can call in a controller works here, which
282
+ is also where you put authorization: `authenticate_with { authenticate_user!; head :forbidden unless current_user.admin? }`.
283
+ - **`current_user_method`** tells the user menu who is signed in. Without it the
284
+ menu is hidden.
285
+ - **`sign_out_path`** is the target of the "Sign out" button. Use a lambda when
286
+ the path comes from your app's routes — inside the engine they live under
287
+ `main_app`. If your sign-out route uses a verb other than `DELETE`, set
288
+ `config.sign_out_method = :get`.
289
+
290
+ That's the whole integration. There is no generator and nothing to copy into
291
+ your app.
292
+
293
+ ### What you get
294
+
295
+ Because Devise renders through Jquard's layout and views, your sign-in, password
296
+ reset, and (if enabled) registration screens automatically match the panel —
297
+ same brand name, same primary color, same form styling. Nothing is copied into
298
+ your app, so these stay in sync as Jquard evolves.
299
+
300
+ Password reset **emails** still use Devise's plain default templates; only the
301
+ web pages are styled.
302
+
303
+ ### Other auth systems
304
+
305
+ Any auth library works for locking the panel — the three config options above
306
+ are deliberately generic. For example, with Rails 8's built-in authentication
307
+ generator:
308
+
309
+ ```ruby
310
+ config.authenticate_with { require_authentication }
311
+ config.current_user_method = -> { Current.user }
312
+ config.sign_out_path = -> { main_app.session_path }
313
+ ```
314
+
315
+ What is Devise-specific today is only the *styling* of the auth screens: Jquard
316
+ ships views for Devise's controllers. With another library you get a secured
317
+ panel and your own unstyled sign-in page.
318
+
319
+ ### Running without authentication
320
+
321
+ A public panel (a local prototype, a demo) is opt-in through the empty block the
322
+ generator writes:
323
+
324
+ ```ruby
325
+ config.authenticate_with { }
326
+ ```
327
+
226
328
  ## Theming
227
329
 
228
330
  The install generator wrote `config/initializers/jquard.rb`:
@@ -245,17 +347,9 @@ end
245
347
 
246
348
  The brand name shows in the sidebar; the primary color is used for buttons, links, the active nav item, and form focus rings.
247
349
 
248
- ## How it works
249
-
250
- - **Resources register themselves.** Any class under `app/jquard/resources/` that inherits from `Jquard::Resource` shows up in the panel — no central list to maintain.
251
- - **State lives in the URL.** Search, sort, and page are query parameters, so every view is shareable and survives a refresh.
252
- - **Hotwire drives the updates.** Searching, sorting, and paging swap the table in place through a Turbo Frame instead of reloading the page.
253
-
254
- For the design decisions and the full roadmap, see [docs/DESIGN.md](docs/DESIGN.md).
255
-
256
350
  ## Status
257
351
 
258
- Jquard is early. Today it covers the core admin panel: list, create, edit, and delete, plus the generator. Planned next: authentication, authorization, a read-only view page, relation managers, custom and bulk actions, and a dashboard. It follows semantic versioning.
352
+ Jquard is early. Today it covers the core admin panel: list, create, edit, and delete, plus the generator and authentication. Planned next: authorization, a read-only view page, relation managers, custom and bulk actions, and a dashboard. It follows semantic versioning.
259
353
 
260
354
  ## Development
261
355
 
@@ -38,3 +38,9 @@ if (window.Turbo) {
38
38
  document.addEventListener("change", (event) => {
39
39
  if (event.target.matches("[data-jq-autosubmit]")) event.target.form?.requestSubmit()
40
40
  })
41
+
42
+ document.addEventListener("click", (event) => {
43
+ document.querySelectorAll("details.jq-dropdown[open]").forEach((dropdown) => {
44
+ if (!dropdown.contains(event.target)) dropdown.removeAttribute("open")
45
+ })
46
+ })
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-font-weight:initial;--tw-tracking:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-green-50:oklch(98.2% .018 155.826);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-blue-50:oklch(97% .014 254.604);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-950:oklch(13% .028 261.692);--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components{.jq-body{background-color:var(--color-gray-50);min-height:100vh;font-family:var(--font-sans);color:var(--color-gray-950);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.jq-shell{min-height:100vh;display:flex}.jq-sidebar{z-index:20;width:calc(var(--spacing) * 72);background-color:var(--color-white);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307120d;inset-block:0;flex-direction:column;display:flex;position:fixed;left:0}@supports (color:color-mix(in lab, red, red)){.jq-sidebar{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent)}}.jq-sidebar-header{height:calc(var(--spacing) * 16);padding-inline:calc(var(--spacing) * 6);flex-shrink:0;align-items:center;display:flex}.jq-brand{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight);color:var(--jq-primary-600)}.jq-sidebar-nav{row-gap:calc(var(--spacing) * 7);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 6);flex-direction:column;flex:1;display:flex;overflow-y:auto}.jq-nav-group-label{padding-inline:calc(var(--spacing) * 2);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider);color:var(--color-gray-500);text-transform:uppercase}.jq-nav-items{margin-top:calc(var(--spacing) * 2);row-gap:var(--spacing);flex-direction:column;display:flex}.jq-nav-item{align-items:center;column-gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media (hover:hover){.jq-nav-item:hover{background-color:var(--color-gray-100)}}.jq-nav-item-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-gray-400);flex-shrink:0}.jq-nav-item--active{background-color:var(--color-gray-100);color:var(--jq-primary-600)}.jq-nav-item--active .jq-nav-item-icon{color:var(--jq-primary-600)}.jq-main{padding-left:calc(var(--spacing) * 72);flex:1}.jq-topbar{z-index:10;height:calc(var(--spacing) * 16);background-color:#fffc;justify-content:space-between;align-items:center;display:flex;position:sticky;top:0}@supports (color:color-mix(in lab, red, red)){.jq-topbar{background-color:color-mix(in oklab, var(--color-white) 80%, transparent)}}.jq-topbar{padding-inline:calc(var(--spacing) * 6);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307120d}@supports (color:color-mix(in lab, red, red)){.jq-topbar{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent)}}.jq-topbar{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}@media (min-width:64rem){.jq-topbar{padding-inline:calc(var(--spacing) * 8)}}.jq-breadcrumbs{align-items:center;column-gap:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500);display:flex}.jq-breadcrumb-sep{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400)}.jq-content{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 8)}@media (min-width:64rem){.jq-content{padding-inline:calc(var(--spacing) * 8)}}.jq-page-header{margin-bottom:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.jq-page-title{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight);color:var(--color-gray-950)}.jq-card{border-radius:var(--radius-xl);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);overflow:hidden}.jq-table-toolbar{justify-content:flex-end;align-items:center;column-gap:calc(var(--spacing) * 4);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);display:flex}.jq-search{position:relative}.jq-search-icon{pointer-events:none;top:50%;left:calc(var(--spacing) * 3);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y);color:var(--color-gray-400);position:absolute}.jq-search-input{width:calc(var(--spacing) * 64);border-radius:var(--radius-lg);border-style:var(--tw-border-style);background-color:var(--color-white);padding-block:calc(var(--spacing) * 1.5);padding-right:calc(var(--spacing) * 3);padding-left:calc(var(--spacing) * 10);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-950);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307121a;border-width:0}@supports (color:color-mix(in lab, red, red)){.jq-search-input{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent)}}.jq-search-input::placeholder{color:var(--color-gray-400)}.jq-search-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-outline-style:none;--tw-ring-color:var(--jq-primary-600);outline-style:none}.jq-table-scroll{overflow-x:auto}.jq-table{text-align:left;width:100%;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.jq-table thead tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200)}.jq-table th{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-950)}.jq-table tbody tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.jq-table tbody tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.jq-table tbody tr:hover{background-color:var(--color-gray-50)}.jq-table td{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);color:var(--color-gray-700)}.jq-sort-link{align-items:center;column-gap:var(--spacing);display:inline-flex}@media (hover:hover){.jq-sort-link:hover{color:var(--color-gray-600)}}.jq-sort-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400)}.jq-badge{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 2);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);align-items:center;display:inline-flex}.jq-badge--gray{background-color:var(--color-gray-50);color:var(--color-gray-600);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#6a728233}@supports (color:color-mix(in lab, red, red)){.jq-badge--gray{--tw-ring-color:color-mix(in oklab, var(--color-gray-500) 20%, transparent)}}.jq-badge--gray{--tw-ring-inset:inset}.jq-badge--success{background-color:var(--color-green-50);color:var(--color-green-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#00a54433}@supports (color:color-mix(in lab, red, red)){.jq-badge--success{--tw-ring-color:color-mix(in oklab, var(--color-green-600) 20%, transparent)}}.jq-badge--success{--tw-ring-inset:inset}.jq-badge--warning{background-color:var(--color-amber-50);color:var(--color-amber-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#dd740033}@supports (color:color-mix(in lab, red, red)){.jq-badge--warning{--tw-ring-color:color-mix(in oklab, var(--color-amber-600) 20%, transparent)}}.jq-badge--warning{--tw-ring-inset:inset}.jq-badge--danger{background-color:var(--color-red-50);color:var(--color-red-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#e400141a}@supports (color:color-mix(in lab, red, red)){.jq-badge--danger{--tw-ring-color:color-mix(in oklab, var(--color-red-600) 10%, transparent)}}.jq-badge--danger{--tw-ring-inset:inset}.jq-badge--info{background-color:var(--color-blue-50);color:var(--color-blue-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#1447e61a}@supports (color:color-mix(in lab, red, red)){.jq-badge--info{--tw-ring-color:color-mix(in oklab, var(--color-blue-700) 10%, transparent)}}.jq-badge--info{--tw-ring-inset:inset}.jq-badge--primary{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-inset:inset;background-color:var(--jq-primary-600)}@supports (color:color-mix(in lab, red, red)){.jq-badge--primary{background-color:color-mix(in srgb, var(--jq-primary-600) 8%, white)}}.jq-badge--primary{color:var(--jq-primary-700);--tw-ring-color:var(--jq-primary-600)}@supports (color:color-mix(in lab, red, red)){.jq-badge--primary{--tw-ring-color:color-mix(in srgb, var(--jq-primary-600) 25%, transparent)}}.jq-bool-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6)}.jq-bool-icon--true{color:var(--color-green-500)}.jq-bool-icon--false{color:var(--color-red-500)}.jq-table-footer{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3)}.jq-pagination{align-items:center;column-gap:calc(var(--spacing) * 3);grid-template-columns:1fr auto 1fr;display:grid}.jq-pagination-overview{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700)}.jq-pagination-per-page{grid-column-start:2;justify-self:center}.jq-input-group{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:75ms;align-items:stretch;transition-duration:75ms;display:flex}.jq-input-group:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:var(--jq-primary-600)}.jq-input-group-prefix{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500);align-items:center;display:flex}.jq-per-page-select{appearance:none;border-style:var(--tw-border-style);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-950);--tw-outline-style:none;background-color:#0000;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath stroke='%239ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.15em 1.15em;border-width:0;border-start-end-radius:var(--radius-lg);border-end-end-radius:var(--radius-lg);outline-style:none;padding-inline-start:calc(var(--spacing) * 2.5);padding-inline-end:calc(var(--spacing) * 7)}.jq-pagination-items{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent);grid-column-start:3;justify-self:flex-end;list-style-type:none;display:flex}.jq-pagination-item{border-inline-style:var(--tw-border-style);border-inline-width:.5px;border-color:var(--color-gray-200)}.jq-pagination-item:first-child{border-inline-start-style:var(--tw-border-style);border-inline-start-width:0}.jq-pagination-item:last-child{border-inline-end-style:var(--tw-border-style);border-inline-end-width:0}.jq-pagination-item-btn{padding:calc(var(--spacing) * 2);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:75ms;transition-duration:75ms;display:flex;position:relative}@media (hover:hover){.jq-pagination-item-btn:hover{background-color:var(--color-gray-50)}}.jq-pagination-item:first-child .jq-pagination-item-btn{border-start-start-radius:var(--radius-lg);border-end-start-radius:var(--radius-lg)}.jq-pagination-item:last-child .jq-pagination-item-btn{border-start-end-radius:var(--radius-lg);border-end-end-radius:var(--radius-lg)}.jq-pagination-item-label{padding-inline:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-700)}.jq-pagination-item--active .jq-pagination-item-btn{background-color:var(--color-gray-50)}.jq-pagination-item--active .jq-pagination-item-label{color:var(--jq-primary-700)}.jq-pagination-item--disabled .jq-pagination-item-btn{pointer-events:none}.jq-pagination-item--disabled .jq-pagination-item-label{color:var(--color-gray-500)}.jq-pagination-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-gray-400)}.jq-empty-state{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 12);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-actions-header{width:var(--spacing)}.jq-row-actions{justify-content:flex-end;align-items:center;column-gap:calc(var(--spacing) * 3);white-space:nowrap;display:flex}.jq-row-action{cursor:pointer;align-items:center;column-gap:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600);background-color:#0000;display:inline-flex}@media (hover:hover){.jq-row-action:hover{color:var(--color-gray-950)}}.jq-row-action--danger{color:var(--color-red-600)}@media (hover:hover){.jq-row-action--danger:hover{color:var(--color-red-700)}}.jq-row-action-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4)}.jq-form{max-width:var(--container-3xl);row-gap:calc(var(--spacing) * 6);flex-direction:column;display:flex}.jq-section{border-radius:var(--radius-xl);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent)}.jq-section-header{margin-bottom:calc(var(--spacing) * 6)}.jq-section-heading{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-950)}.jq-section-description{margin-top:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-schema-grid{gap:calc(var(--spacing) * 6);display:grid}.jq-field{row-gap:calc(var(--spacing) * 1.5);flex-direction:column;display:flex}.jq-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-950)}.jq-required{margin-left:calc(var(--spacing) * .5);color:var(--color-red-600)}.jq-input{border-radius:var(--radius-lg);border-style:var(--tw-border-style);background-color:var(--color-white);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-950);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307121a;border-width:0}@supports (color:color-mix(in lab, red, red)){.jq-input{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent)}}.jq-input::placeholder{color:var(--color-gray-400)}.jq-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-outline-style:none;--tw-ring-color:var(--jq-primary-600);outline-style:none}.jq-input--error,.jq-input--error:focus{--tw-ring-color:var(--color-red-600)}.jq-field-error{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-600)}.jq-helper-text{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-field-inline{align-items:center;column-gap:calc(var(--spacing) * 3);display:flex}.jq-checkbox{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);border-color:var(--color-gray-300);accent-color:var(--jq-primary-600);border-radius:.25rem}.jq-toggle{display:inline-flex;position:relative}.jq-toggle-input{z-index:10;cursor:pointer;opacity:0;position:absolute;inset:0}.jq-toggle-track{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 11);background-color:var(--color-gray-200);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;align-items:center;display:inline-flex;position:relative}.jq-toggle-thumb{left:calc(var(--spacing) * .5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.jq-toggle-input:checked+.jq-toggle-track{background-color:var(--jq-primary-600)}.jq-toggle-input:checked+.jq-toggle-track .jq-toggle-thumb{transform:translate(1.25rem)}.jq-form-actions{align-items:center;column-gap:calc(var(--spacing) * 3);display:flex}.jq-btn{cursor:pointer;align-items:center;column-gap:calc(var(--spacing) * 1.5);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);display:inline-flex}.jq-btn-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5)}.jq-btn--primary{color:var(--color-white);background-color:var(--jq-primary-600)}.jq-btn--primary:hover{background-color:var(--jq-primary-500)}.jq-btn--secondary{background-color:var(--color-white);color:var(--color-gray-950);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307121a}@supports (color:color-mix(in lab, red, red)){.jq-btn--secondary{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent)}}@media (hover:hover){.jq-btn--secondary:hover{background-color:var(--color-gray-50)}}.jq-toast{top:calc(var(--spacing) * 4);right:calc(var(--spacing) * 4);z-index:50;align-items:center;column-gap:calc(var(--spacing) * 2);border-radius:var(--radius-xl);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-950);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);animation:4s forwards jq-toast-out;display:flex;position:fixed}.jq-toast-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-green-500)}.jq-modal{width:100%;max-width:var(--container-md);border-radius:var(--radius-xl);background-color:var(--color-white);--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);margin:auto;padding:0}.jq-modal::backdrop{background-color:#03071280}.jq-modal-window{align-items:center;row-gap:calc(var(--spacing) * 1.5);padding:calc(var(--spacing) * 6);text-align:center;flex-direction:column;display:flex}.jq-modal-icon-circle{margin-bottom:calc(var(--spacing) * 2);height:calc(var(--spacing) * 12);width:calc(var(--spacing) * 12);background-color:var(--color-red-100);border-radius:3.40282e38px;justify-content:center;align-items:center;display:flex}.jq-modal-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-red-600)}.jq-modal-heading{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-950)}.jq-modal-description{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-modal-actions{margin-top:calc(var(--spacing) * 4);gap:calc(var(--spacing) * 3);grid-template-columns:repeat(2,minmax(0,1fr));width:100%;display:grid}.jq-modal-actions .jq-btn{justify-content:center}.jq-btn--danger{background-color:var(--color-red-600);color:var(--color-white)}@media (hover:hover){.jq-btn--danger:hover{background-color:var(--color-red-500)}}}@layer utilities;@keyframes jq-toast-out{0%,80%{opacity:1}to{opacity:0;visibility:hidden}}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-divide-y-reverse:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-green-50:oklch(98.2% .018 155.826);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-blue-50:oklch(97% .014 254.604);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-950:oklch(13% .028 261.692);--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-lg:32rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html:has(>.jq-body){background-color:var(--color-gray-50)}}@layer components{.jq-body{background-color:var(--color-gray-50);min-height:100vh;font-family:var(--font-sans);color:var(--color-gray-950);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.jq-shell{min-height:calc(100dvh - 4rem);display:flex}.jq-sidebar{top:calc(var(--spacing) * 16);z-index:20;height:calc(100dvh - 4rem);width:calc(var(--spacing) * 72);flex-direction:column;flex-shrink:0;display:flex;position:sticky}.jq-brand{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight);color:var(--color-gray-950)}.jq-sidebar-nav{row-gap:calc(var(--spacing) * 7);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 8);flex-direction:column;flex:1;display:flex;overflow-y:auto}.jq-nav-group-label{padding-inline:calc(var(--spacing) * 2);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider);color:var(--color-gray-500);text-transform:uppercase}.jq-nav-items{margin-top:calc(var(--spacing) * 2);row-gap:var(--spacing);flex-direction:column;display:flex}.jq-nav-item{align-items:center;column-gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media (hover:hover){.jq-nav-item:hover{background-color:var(--color-gray-100)}}.jq-nav-item-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-gray-400);flex-shrink:0}.jq-nav-item--active{background-color:var(--color-gray-100);color:var(--jq-primary-600)}.jq-nav-item--active .jq-nav-item-icon{color:var(--jq-primary-600)}.jq-main{flex:1;min-width:0}.jq-topbar{z-index:30;height:calc(var(--spacing) * 16);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 6);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);align-items:center;display:flex;position:sticky;top:0}@media (min-width:64rem){.jq-topbar{padding-inline:calc(var(--spacing) * 8)}}.jq-topbar-start{align-items:center;margin-inline-end:calc(var(--spacing) * 6);display:flex}.jq-topbar-end{align-items:center;column-gap:calc(var(--spacing) * 4);margin-inline-start:auto;display:flex}.jq-content{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 8)}@media (min-width:64rem){.jq-content{padding-inline:calc(var(--spacing) * 8)}}.jq-page-header{margin-bottom:calc(var(--spacing) * 6);gap:calc(var(--spacing) * 4);flex-direction:column;display:flex}@media (min-width:40rem){.jq-page-header{flex-direction:row;justify-content:space-between;align-items:center}}.jq-page-header .jq-breadcrumbs{margin-bottom:calc(var(--spacing) * 2);display:none}@media (min-width:40rem){.jq-page-header .jq-breadcrumbs{display:block}}.jq-page-header-actions{align-items:center;gap:calc(var(--spacing) * 3);flex-shrink:0;display:flex}@media (min-width:40rem){.jq-page-header-actions{margin-top:calc(var(--spacing) * 7)}}.jq-breadcrumbs-list{align-items:center;column-gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.jq-breadcrumbs-item{align-items:center;column-gap:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500);display:flex}.jq-breadcrumbs-item a{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:75ms;transition-duration:75ms}@media (hover:hover){.jq-breadcrumbs-item a:hover{color:var(--color-gray-700)}}.jq-breadcrumbs-item-separator{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4);color:var(--color-gray-400);display:flex}.jq-page-title{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight);color:var(--color-gray-950)}.jq-card{border-radius:var(--radius-xl);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);overflow:hidden}.jq-table-toolbar{justify-content:flex-end;align-items:center;column-gap:calc(var(--spacing) * 4);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);display:flex}.jq-search{position:relative}.jq-search-icon{pointer-events:none;top:50%;left:calc(var(--spacing) * 3);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y);color:var(--color-gray-400);position:absolute}.jq-search-input{width:calc(var(--spacing) * 64);border-radius:var(--radius-lg);border-style:var(--tw-border-style);background-color:var(--color-white);padding-block:calc(var(--spacing) * 1.5);padding-right:calc(var(--spacing) * 3);padding-left:calc(var(--spacing) * 10);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-950);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307121a;border-width:0}@supports (color:color-mix(in lab, red, red)){.jq-search-input{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent)}}.jq-search-input::placeholder{color:var(--color-gray-400)}.jq-search-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-outline-style:none;--tw-ring-color:var(--jq-primary-600);outline-style:none}.jq-table-scroll{overflow-x:auto}.jq-table{text-align:left;width:100%;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.jq-table thead tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200)}.jq-table th{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-950)}.jq-table tbody tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.jq-table tbody tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.jq-table tbody tr:hover{background-color:var(--color-gray-50)}.jq-table td{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);color:var(--color-gray-700)}.jq-sort-link{align-items:center;column-gap:var(--spacing);display:inline-flex}@media (hover:hover){.jq-sort-link:hover{color:var(--color-gray-600)}}.jq-sort-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400)}.jq-badge{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 2);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);align-items:center;display:inline-flex}.jq-badge--gray{background-color:var(--color-gray-50);color:var(--color-gray-600);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#6a728233}@supports (color:color-mix(in lab, red, red)){.jq-badge--gray{--tw-ring-color:color-mix(in oklab, var(--color-gray-500) 20%, transparent)}}.jq-badge--gray{--tw-ring-inset:inset}.jq-badge--success{background-color:var(--color-green-50);color:var(--color-green-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#00a54433}@supports (color:color-mix(in lab, red, red)){.jq-badge--success{--tw-ring-color:color-mix(in oklab, var(--color-green-600) 20%, transparent)}}.jq-badge--success{--tw-ring-inset:inset}.jq-badge--warning{background-color:var(--color-amber-50);color:var(--color-amber-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#dd740033}@supports (color:color-mix(in lab, red, red)){.jq-badge--warning{--tw-ring-color:color-mix(in oklab, var(--color-amber-600) 20%, transparent)}}.jq-badge--warning{--tw-ring-inset:inset}.jq-badge--danger{background-color:var(--color-red-50);color:var(--color-red-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#e400141a}@supports (color:color-mix(in lab, red, red)){.jq-badge--danger{--tw-ring-color:color-mix(in oklab, var(--color-red-600) 10%, transparent)}}.jq-badge--danger{--tw-ring-inset:inset}.jq-badge--info{background-color:var(--color-blue-50);color:var(--color-blue-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#1447e61a}@supports (color:color-mix(in lab, red, red)){.jq-badge--info{--tw-ring-color:color-mix(in oklab, var(--color-blue-700) 10%, transparent)}}.jq-badge--info{--tw-ring-inset:inset}.jq-badge--primary{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-inset:inset;background-color:var(--jq-primary-600)}@supports (color:color-mix(in lab, red, red)){.jq-badge--primary{background-color:color-mix(in srgb, var(--jq-primary-600) 8%, white)}}.jq-badge--primary{color:var(--jq-primary-700);--tw-ring-color:var(--jq-primary-600)}@supports (color:color-mix(in lab, red, red)){.jq-badge--primary{--tw-ring-color:color-mix(in srgb, var(--jq-primary-600) 25%, transparent)}}.jq-bool-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6)}.jq-bool-icon--true{color:var(--color-green-500)}.jq-bool-icon--false{color:var(--color-red-500)}.jq-table-footer{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3)}.jq-pagination{align-items:center;column-gap:calc(var(--spacing) * 3);grid-template-columns:1fr auto 1fr;display:grid}.jq-pagination-overview{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700)}.jq-pagination-per-page{grid-column-start:2;justify-self:center}.jq-input-group{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:75ms;align-items:stretch;transition-duration:75ms;display:flex}.jq-input-group:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:var(--jq-primary-600)}.jq-input-group-prefix{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500);align-items:center;display:flex}.jq-per-page-select{appearance:none;border-style:var(--tw-border-style);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-950);--tw-outline-style:none;background-color:#0000;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath stroke='%239ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.15em 1.15em;border-width:0;border-start-end-radius:var(--radius-lg);border-end-end-radius:var(--radius-lg);outline-style:none;padding-inline-start:calc(var(--spacing) * 2.5);padding-inline-end:calc(var(--spacing) * 7)}.jq-pagination-items{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent);grid-column-start:3;justify-self:flex-end;list-style-type:none;display:flex}.jq-pagination-item{border-inline-style:var(--tw-border-style);border-inline-width:.5px;border-color:var(--color-gray-200)}.jq-pagination-item:first-child{border-inline-start-style:var(--tw-border-style);border-inline-start-width:0}.jq-pagination-item:last-child{border-inline-end-style:var(--tw-border-style);border-inline-end-width:0}.jq-pagination-item-btn{padding:calc(var(--spacing) * 2);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:75ms;transition-duration:75ms;display:flex;position:relative}@media (hover:hover){.jq-pagination-item-btn:hover{background-color:var(--color-gray-50)}}.jq-pagination-item:first-child .jq-pagination-item-btn{border-start-start-radius:var(--radius-lg);border-end-start-radius:var(--radius-lg)}.jq-pagination-item:last-child .jq-pagination-item-btn{border-start-end-radius:var(--radius-lg);border-end-end-radius:var(--radius-lg)}.jq-pagination-item-label{padding-inline:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-700)}.jq-pagination-item--active .jq-pagination-item-btn{background-color:var(--color-gray-50)}.jq-pagination-item--active .jq-pagination-item-label{color:var(--jq-primary-700)}.jq-pagination-item--disabled .jq-pagination-item-btn{pointer-events:none}.jq-pagination-item--disabled .jq-pagination-item-label{color:var(--color-gray-500)}.jq-pagination-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-gray-400)}.jq-empty-state{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 12);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-actions-header{width:var(--spacing)}.jq-row-actions{justify-content:flex-end;align-items:center;column-gap:calc(var(--spacing) * 3);white-space:nowrap;display:flex}.jq-row-action{cursor:pointer;align-items:center;column-gap:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600);background-color:#0000;display:inline-flex}@media (hover:hover){.jq-row-action:hover{color:var(--color-gray-950)}}.jq-row-action--danger{color:var(--color-red-600)}@media (hover:hover){.jq-row-action--danger:hover{color:var(--color-red-700)}}.jq-row-action-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4)}.jq-form{max-width:var(--container-3xl);row-gap:calc(var(--spacing) * 6);flex-direction:column;display:flex}.jq-section{border-radius:var(--radius-xl);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent)}.jq-section-header{margin-bottom:calc(var(--spacing) * 6)}.jq-section-heading{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-950)}.jq-section-description{margin-top:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-schema-grid{gap:calc(var(--spacing) * 6);display:grid}.jq-field{row-gap:calc(var(--spacing) * 1.5);flex-direction:column;display:flex}.jq-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-950)}.jq-required{margin-left:calc(var(--spacing) * .5);color:var(--color-red-600)}.jq-input{border-radius:var(--radius-lg);border-style:var(--tw-border-style);background-color:var(--color-white);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-950);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307121a;border-width:0}@supports (color:color-mix(in lab, red, red)){.jq-input{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent)}}.jq-input::placeholder{color:var(--color-gray-400)}.jq-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-outline-style:none;--tw-ring-color:var(--jq-primary-600);outline-style:none}.jq-input--error,.jq-input--error:focus{--tw-ring-color:var(--color-red-600)}.jq-field-error{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-600)}.jq-helper-text{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-field-inline{align-items:center;column-gap:calc(var(--spacing) * 3);display:flex}.jq-checkbox{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);border-color:var(--color-gray-300);accent-color:var(--jq-primary-600);border-radius:.25rem}.jq-toggle{display:inline-flex;position:relative}.jq-toggle-input{z-index:10;cursor:pointer;opacity:0;position:absolute;inset:0}.jq-toggle-track{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 11);background-color:var(--color-gray-200);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;align-items:center;display:inline-flex;position:relative}.jq-toggle-thumb{left:calc(var(--spacing) * .5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.jq-toggle-input:checked+.jq-toggle-track{background-color:var(--jq-primary-600)}.jq-toggle-input:checked+.jq-toggle-track .jq-toggle-thumb{transform:translate(1.25rem)}.jq-form-actions{align-items:center;column-gap:calc(var(--spacing) * 3);display:flex}.jq-btn{cursor:pointer;align-items:center;column-gap:calc(var(--spacing) * 1.5);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);display:inline-flex}.jq-btn-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5)}.jq-btn--primary{color:var(--color-white);background-color:var(--jq-primary-600)}.jq-btn--primary:hover{background-color:var(--jq-primary-500)}.jq-btn--secondary{background-color:var(--color-white);color:var(--color-gray-950);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#0307121a}@supports (color:color-mix(in lab, red, red)){.jq-btn--secondary{--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 10%, transparent)}}@media (hover:hover){.jq-btn--secondary:hover{background-color:var(--color-gray-50)}}.jq-toast{top:calc(var(--spacing) * 4);right:calc(var(--spacing) * 4);z-index:50;align-items:center;column-gap:calc(var(--spacing) * 2);border-radius:var(--radius-xl);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-950);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);animation:4s forwards jq-toast-out;display:flex;position:fixed}.jq-toast-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-green-500)}.jq-modal{width:100%;max-width:var(--container-md);border-radius:var(--radius-xl);background-color:var(--color-white);--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);margin:auto;padding:0}.jq-modal::backdrop{background-color:#03071280}.jq-modal-window{align-items:center;row-gap:calc(var(--spacing) * 1.5);padding:calc(var(--spacing) * 6);text-align:center;flex-direction:column;display:flex}.jq-modal-icon-circle{margin-bottom:calc(var(--spacing) * 2);height:calc(var(--spacing) * 12);width:calc(var(--spacing) * 12);background-color:var(--color-red-100);border-radius:3.40282e38px;justify-content:center;align-items:center;display:flex}.jq-modal-icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-red-600)}.jq-modal-heading{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-950)}.jq-modal-description{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-modal-actions{margin-top:calc(var(--spacing) * 4);gap:calc(var(--spacing) * 3);grid-template-columns:repeat(2,minmax(0,1fr));width:100%;display:grid}.jq-modal-actions .jq-btn{justify-content:center}.jq-btn--danger{background-color:var(--color-red-600);color:var(--color-white)}@media (hover:hover){.jq-btn--danger:hover{background-color:var(--color-red-500)}}.jq-btn--block{justify-content:center;width:100%}.jq-avatar{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8);background-color:var(--color-gray-950);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-white);border-radius:3.40282e38px;flex-shrink:0;justify-content:center;align-items:center;display:flex}.jq-dropdown{position:relative}.jq-dropdown-trigger{cursor:pointer;list-style-type:none;display:flex}.jq-dropdown-trigger::-webkit-details-marker{display:none}.jq-dropdown-panel{z-index:20;margin-top:calc(var(--spacing) * 2);width:100vw;max-width:calc(var(--spacing) * 56);inset-inline-end:calc(var(--spacing) * 0);position:absolute;top:100%}:where(.jq-dropdown-panel>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-color:var(--color-gray-100)}.jq-dropdown-panel{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent)}.jq-dropdown-header{align-items:center;gap:calc(var(--spacing) * 2);width:100%;padding:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);display:flex}.jq-dropdown-header-label{text-overflow:ellipsis;white-space:nowrap;text-align:start;color:var(--color-gray-700);flex:1;overflow:hidden}.jq-dropdown-icon{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5);color:var(--color-gray-400);flex-shrink:0}.jq-dropdown-list{padding:var(--spacing);row-gap:1px;display:grid}.jq-dropdown-form{display:grid}.jq-dropdown-list-item{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-md);width:100%;padding:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));white-space:nowrap;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:75ms;background-color:#0000;transition-duration:75ms;display:flex;overflow:hidden}@media (hover:hover){.jq-dropdown-list-item:hover{background-color:var(--color-gray-50)}}.jq-dropdown-list-item-label{text-overflow:ellipsis;white-space:nowrap;text-align:start;color:var(--color-gray-700);flex:1;overflow:hidden}.jq-auth{min-height:100dvh;padding-inline:calc(var(--spacing) * 4);flex-direction:column;justify-content:center;align-items:center;display:flex}.jq-auth-card{margin-block:calc(var(--spacing) * 16);width:100%;max-width:var(--container-lg);row-gap:calc(var(--spacing) * 6);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 12);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:color-mix(in oklab, var(--color-gray-950) 5%, transparent);flex-direction:column;display:flex}@media (min-width:40rem){.jq-auth-card{border-radius:var(--radius-xl);padding-inline:calc(var(--spacing) * 12)}}.jq-auth-header{flex-direction:column;align-items:center;display:flex}.jq-auth-header .jq-brand{margin-bottom:calc(var(--spacing) * 4)}.jq-auth-heading{text-align:center;font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight);color:var(--color-gray-950)}.jq-auth-subheading{margin-top:calc(var(--spacing) * 2);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.jq-auth-form{row-gap:calc(var(--spacing) * 6);display:grid}.jq-auth-flash{border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.jq-auth-flash--alert{background-color:var(--color-red-50);color:var(--color-red-700);--tw-ring-color:#e4001433}@supports (color:color-mix(in lab, red, red)){.jq-auth-flash--alert{--tw-ring-color:color-mix(in oklab, var(--color-red-600) 20%, transparent)}}.jq-auth-links{align-items:center;row-gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}.jq-auth-divider{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200)}.jq-label-row{justify-content:space-between;align-items:center;display:flex}.jq-link{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}@media (hover:hover){.jq-link:hover{text-decoration-line:underline}}.jq-link{color:var(--jq-primary-600)}.jq-link:hover{color:var(--jq-primary-500)}.jq-auth-errors{border-radius:var(--radius-lg);background-color:var(--color-red-50);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-700);--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);--tw-ring-color:#e4001433}@supports (color:color-mix(in lab, red, red)){.jq-auth-errors{--tw-ring-color:color-mix(in oklab, var(--color-red-600) 20%, transparent)}}.jq-auth-errors-title{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.jq-auth-errors-list{margin-top:var(--spacing);padding-inline-start:calc(var(--spacing) * 5);list-style-type:disc}}@layer utilities;@keyframes jq-toast-out{0%,80%{opacity:1}to{opacity:0;visibility:hidden}}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}
@@ -0,0 +1,11 @@
1
+ module Jquard
2
+ module AuthLayout
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ layout "jquard/auth"
7
+ helper Jquard::Engine.helpers
8
+ prepend_view_path Jquard::Engine.root.join("app/views")
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,20 @@
1
1
  module Jquard
2
2
  class ApplicationController < ActionController::Base
3
+ before_action :authenticate_jquard_user!
4
+
5
+ private
6
+
7
+ def authenticate_jquard_user!
8
+ auth = Jquard.config.authenticate
9
+
10
+ unless auth
11
+ raise Jquard::Error,
12
+ "Jquard refuses to serve the admin panel without authentication. " \
13
+ "Configure config.authenticate_with in a Jquard.configure block, " \
14
+ "or opt into a public panel explicitly with an empty block: authenticate_with { }."
15
+ end
16
+
17
+ instance_exec(&auth)
18
+ end
3
19
  end
4
20
  end
@@ -18,5 +18,33 @@ module Jquard
18
18
 
19
19
  tag.style(":root { #{variables} }".html_safe)
20
20
  end
21
+
22
+ def jquard_current_user
23
+ method = Jquard.config.current_user_method
24
+ return unless method && controller.respond_to?(method)
25
+
26
+ controller.send(method)
27
+ end
28
+
29
+ def jquard_user_name(user)
30
+ user.try(:name).presence || user.try(:email).presence || user.to_s
31
+ end
32
+
33
+ def jquard_user_initials(user)
34
+ name = jquard_user_name(user).to_s
35
+ name = name.split("@").first.to_s if name.include?("@")
36
+ name.split(/[\s._-]+/).filter_map { |part| part[0] }.join[0, 2].to_s.upcase
37
+ end
38
+
39
+ def jquard_sign_out_path
40
+ path = Jquard.config.sign_out_path
41
+ return instance_exec(&path) if path.respond_to?(:call)
42
+
43
+ path.presence
44
+ end
45
+
46
+ def jquard_sign_out_method
47
+ Jquard.config.sign_out_method || :delete
48
+ end
21
49
  end
22
50
  end
@@ -0,0 +1,14 @@
1
+ <% content_for :heading, "Resend confirmation instructions" %>
2
+
3
+ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: "jq-auth-form" }) do |f| %>
4
+ <%= render "devise/shared/error_messages", resource: resource %>
5
+
6
+ <div class="jq-field">
7
+ <%= f.label :email, "Email address", class: "jq-label" %>
8
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), class: "jq-input" %>
9
+ </div>
10
+
11
+ <%= f.submit "Resend confirmation instructions", class: "jq-btn jq-btn--primary jq-btn--block" %>
12
+ <% end %>
13
+
14
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,21 @@
1
+ <% content_for :heading, "Reset your password" %>
2
+
3
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: "jq-auth-form" }) do |f| %>
4
+ <%= render "devise/shared/error_messages", resource: resource %>
5
+ <%= f.hidden_field :reset_password_token %>
6
+
7
+ <div class="jq-field">
8
+ <%= f.label :password, class: "jq-label" %>
9
+ <%= f.password_field :password, autofocus: true, autocomplete: "new-password", class: "jq-input" %>
10
+ <% if @minimum_password_length %>
11
+ <p class="jq-helper-text"><%= @minimum_password_length %> characters minimum</p>
12
+ <% end %>
13
+ </div>
14
+
15
+ <div class="jq-field">
16
+ <%= f.label :password_confirmation, "Confirm password", class: "jq-label" %>
17
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "jq-input" %>
18
+ </div>
19
+
20
+ <%= f.submit "Reset password", class: "jq-btn jq-btn--primary jq-btn--block" %>
21
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <% content_for :heading, "Forgot password?" %>
2
+
3
+ <% content_for :subheading do %>
4
+ or <%= link_to "back to login", new_session_path(resource_name), class: "jq-link" %>
5
+ <% end %>
6
+
7
+ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: "jq-auth-form" }) do |f| %>
8
+ <%= render "devise/shared/error_messages", resource: resource %>
9
+
10
+ <div class="jq-field">
11
+ <%= f.label :email, "Email address", class: "jq-label" %>
12
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "jq-input" %>
13
+ </div>
14
+
15
+ <%= f.submit "Send email", class: "jq-btn jq-btn--primary jq-btn--block" %>
16
+ <% end %>
@@ -0,0 +1,45 @@
1
+ <% content_for :heading, "Edit #{resource_name.to_s.humanize.downcase}" %>
2
+
3
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: "jq-auth-form" }) do |f| %>
4
+ <%= render "devise/shared/error_messages", resource: resource %>
5
+
6
+ <div class="jq-field">
7
+ <%= f.label :email, "Email address", class: "jq-label" %>
8
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "jq-input" %>
9
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
10
+ <p class="jq-helper-text">Currently waiting confirmation for: <%= resource.unconfirmed_email %></p>
11
+ <% end %>
12
+ </div>
13
+
14
+ <div class="jq-field">
15
+ <%= f.label :password, class: "jq-label" %>
16
+ <%= f.password_field :password, autocomplete: "new-password", class: "jq-input" %>
17
+ <p class="jq-helper-text">
18
+ Leave blank if you don't want to change it<% if @minimum_password_length %> — <%= @minimum_password_length %> characters minimum<% end %>
19
+ </p>
20
+ </div>
21
+
22
+ <div class="jq-field">
23
+ <%= f.label :password_confirmation, "Confirm password", class: "jq-label" %>
24
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "jq-input" %>
25
+ </div>
26
+
27
+ <div class="jq-field">
28
+ <%= f.label :current_password, class: "jq-label" %>
29
+ <%= f.password_field :current_password, autocomplete: "current-password", class: "jq-input" %>
30
+ <p class="jq-helper-text">We need your current password to confirm your changes</p>
31
+ </div>
32
+
33
+ <%= f.submit "Update", class: "jq-btn jq-btn--primary jq-btn--block" %>
34
+ <% end %>
35
+
36
+ <div class="jq-auth-divider"></div>
37
+
38
+ <h2 class="jq-auth-heading">Cancel my account</h2>
39
+
40
+ <%# The auth layout loads no JS, so data-turbo-confirm would silently not confirm. %>
41
+ <%= button_to "Cancel my account", registration_path(resource_name), method: :delete, form: { onsubmit: "return confirm('Are you sure?')" }, class: "jq-btn jq-btn--danger jq-btn--block" %>
42
+
43
+ <div class="jq-auth-links">
44
+ <%= link_to "Back", :back, class: "jq-link" %>
45
+ </div>
@@ -0,0 +1,29 @@
1
+ <% content_for :heading, "Sign up" %>
2
+
3
+ <% content_for :subheading do %>
4
+ or <%= link_to "sign in to your account", new_session_path(resource_name), class: "jq-link" %>
5
+ <% end %>
6
+
7
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "jq-auth-form" }) do |f| %>
8
+ <%= render "devise/shared/error_messages", resource: resource %>
9
+
10
+ <div class="jq-field">
11
+ <%= f.label :email, "Email address", class: "jq-label" %>
12
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "jq-input" %>
13
+ </div>
14
+
15
+ <div class="jq-field">
16
+ <%= f.label :password, class: "jq-label" %>
17
+ <%= f.password_field :password, autocomplete: "new-password", class: "jq-input" %>
18
+ <% if @minimum_password_length %>
19
+ <p class="jq-helper-text"><%= @minimum_password_length %> characters minimum</p>
20
+ <% end %>
21
+ </div>
22
+
23
+ <div class="jq-field">
24
+ <%= f.label :password_confirmation, "Confirm password", class: "jq-label" %>
25
+ <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "jq-input" %>
26
+ </div>
27
+
28
+ <%= f.submit "Sign up", class: "jq-btn jq-btn--primary jq-btn--block" %>
29
+ <% end %>
@@ -0,0 +1,33 @@
1
+ <% content_for :heading, "Sign in" %>
2
+
3
+ <% if devise_mapping.registerable? %>
4
+ <% content_for :subheading do %>
5
+ or <%= link_to "sign up for an account", new_registration_path(resource_name), class: "jq-link" %>
6
+ <% end %>
7
+ <% end %>
8
+
9
+ <%= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "jq-auth-form" }) do |f| %>
10
+ <div class="jq-field">
11
+ <%= f.label :email, "Email address", class: "jq-label" %>
12
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "jq-input" %>
13
+ </div>
14
+
15
+ <div class="jq-field">
16
+ <div class="jq-label-row">
17
+ <%= f.label :password, class: "jq-label" %>
18
+ <% if devise_mapping.recoverable? %>
19
+ <%= link_to "Forgot password?", new_password_path(resource_name), class: "jq-link", tabindex: -1 %>
20
+ <% end %>
21
+ </div>
22
+ <%= f.password_field :password, autocomplete: "current-password", class: "jq-input" %>
23
+ </div>
24
+
25
+ <% if devise_mapping.rememberable? %>
26
+ <div class="jq-field-inline">
27
+ <%= f.check_box :remember_me, class: "jq-checkbox" %>
28
+ <%= f.label :remember_me, class: "jq-label" %>
29
+ </div>
30
+ <% end %>
31
+
32
+ <%= f.submit "Sign in", class: "jq-btn jq-btn--primary jq-btn--block" %>
33
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <% if resource.errors.any? %>
2
+ <div id="error_explanation" class="jq-auth-errors" data-turbo-temporary>
3
+ <p class="jq-auth-errors-title">
4
+ <%= I18n.t("errors.messages.not_saved",
5
+ count: resource.errors.count,
6
+ resource: resource.class.model_name.human.downcase)
7
+ %>
8
+ </p>
9
+ <ul class="jq-auth-errors-list">
10
+ <% resource.errors.full_messages.each do |message| %>
11
+ <li><%= message %></li>
12
+ <% end %>
13
+ </ul>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <div class="jq-auth-links">
2
+ <%- if controller_name != 'sessions' %>
3
+ <%= link_to "Log in", new_session_path(resource_name), class: "jq-link" %>
4
+ <% end %>
5
+
6
+ <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
7
+ <%= link_to "Sign up", new_registration_path(resource_name), class: "jq-link" %>
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: "jq-link" %>
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: "jq-link" %>
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: "jq-link" %>
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: "jq-btn jq-btn--secondary jq-btn--block" %>
25
+ <% end %>
26
+ <% end %>
27
+ </div>
@@ -0,0 +1,14 @@
1
+ <% content_for :heading, "Resend unlock instructions" %>
2
+
3
+ <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: "jq-auth-form" }) do |f| %>
4
+ <%= render "devise/shared/error_messages", resource: resource %>
5
+
6
+ <div class="jq-field">
7
+ <%= f.label :email, "Email address", class: "jq-label" %>
8
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "jq-input" %>
9
+ </div>
10
+
11
+ <%= f.submit "Resend unlock instructions", class: "jq-btn jq-btn--primary jq-btn--block" %>
12
+ <% end %>
13
+
14
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,17 @@
1
+ <nav class="jq-breadcrumbs">
2
+ <ol class="jq-breadcrumbs-list">
3
+ <% crumbs.each_with_index do |(label, url), index| %>
4
+ <li class="jq-breadcrumbs-item">
5
+ <% unless index.zero? %>
6
+ <%= jquard_icon("solid/chevron-right", css_class: "jq-breadcrumbs-item-separator") %>
7
+ <% end %>
8
+
9
+ <% if url %>
10
+ <%= link_to label, url %>
11
+ <% else %>
12
+ <span><%= label %></span>
13
+ <% end %>
14
+ </li>
15
+ <% end %>
16
+ </ol>
17
+ </nav>
@@ -0,0 +1,23 @@
1
+ <% if (user = jquard_current_user) %>
2
+ <details class="jq-dropdown jq-user-menu">
3
+ <summary class="jq-dropdown-trigger" aria-label="Open user menu">
4
+ <span class="jq-avatar"><%= jquard_user_initials(user) %></span>
5
+ </summary>
6
+
7
+ <div class="jq-dropdown-panel">
8
+ <div class="jq-dropdown-header">
9
+ <%= jquard_icon("user-circle", css_class: "jq-dropdown-icon") %>
10
+ <span class="jq-dropdown-header-label"><%= jquard_user_name(user) %></span>
11
+ </div>
12
+
13
+ <% if (sign_out_path = jquard_sign_out_path) %>
14
+ <div class="jq-dropdown-list">
15
+ <%= button_to sign_out_path, method: jquard_sign_out_method, form_class: "jq-dropdown-form", class: "jq-dropdown-list-item" do %>
16
+ <%= jquard_icon("arrow-left-end-on-rectangle", css_class: "jq-dropdown-icon") %>
17
+ <span class="jq-dropdown-list-item-label">Sign out</span>
18
+ <% end %>
19
+ </div>
20
+ <% end %>
21
+ </div>
22
+ </details>
23
+ <% end %>
@@ -1,5 +1,8 @@
1
1
  <header class="jq-page-header">
2
- <h1 class="jq-page-title"><%= @page.title %></h1>
2
+ <div>
3
+ <%= render "jquard/breadcrumbs", crumbs: [ [ @resource.navigation_label, resource_path(@resource.slug) ], [ "Edit", nil ] ] %>
4
+ <h1 class="jq-page-title"><%= @page.title %></h1>
5
+ </div>
3
6
  </header>
4
7
 
5
8
  <%= render Jquard::Schemas::FormComponent.new(schema: @form_schema, record: @record, resource: @resource) %>
@@ -1,9 +1,15 @@
1
1
  <header class="jq-page-header">
2
- <h1 class="jq-page-title"><%= @page.title %></h1>
3
- <%= link_to new_resource_path(@resource.slug), class: "jq-btn jq-btn--primary" do %>
4
- <%= jquard_icon("plus", css_class: "jq-btn-icon") %>
5
- <span>New <%= @resource.singular_label.downcase %></span>
6
- <% end %>
2
+ <div>
3
+ <%= render "jquard/breadcrumbs", crumbs: [ [ @resource.navigation_label, resource_path(@resource.slug) ], [ "List", nil ] ] %>
4
+ <h1 class="jq-page-title"><%= @page.title %></h1>
5
+ </div>
6
+
7
+ <div class="jq-page-header-actions">
8
+ <%= link_to new_resource_path(@resource.slug), class: "jq-btn jq-btn--primary" do %>
9
+ <%= jquard_icon("plus", css_class: "jq-btn-icon") %>
10
+ <span>New <%= @resource.singular_label.downcase %></span>
11
+ <% end %>
12
+ </div>
7
13
  </header>
8
14
 
9
15
  <%= turbo_frame_tag "jquard-table", data: { turbo_action: "advance" } do %>
@@ -1,5 +1,8 @@
1
1
  <header class="jq-page-header">
2
- <h1 class="jq-page-title"><%= @page.title %></h1>
2
+ <div>
3
+ <%= render "jquard/breadcrumbs", crumbs: [ [ @resource.navigation_label, resource_path(@resource.slug) ], [ "Create", nil ] ] %>
4
+ <h1 class="jq-page-title"><%= @page.title %></h1>
5
+ </div>
3
6
  </header>
4
7
 
5
8
  <%= render Jquard::Schemas::FormComponent.new(schema: @form_schema, record: @record, resource: @resource) %>
@@ -14,12 +14,18 @@
14
14
  <%= javascript_include_tag "jquard/application", type: "module" %>
15
15
  </head>
16
16
  <body class="jq-body">
17
+ <header class="jq-topbar">
18
+ <div class="jq-topbar-start">
19
+ <span class="jq-brand"><%= Jquard.config.brand_name %></span>
20
+ </div>
21
+
22
+ <div class="jq-topbar-end">
23
+ <%= render "jquard/user_menu" %>
24
+ </div>
25
+ </header>
26
+
17
27
  <div class="jq-shell">
18
28
  <aside class="jq-sidebar">
19
- <div class="jq-sidebar-header">
20
- <span class="jq-brand"><%= Jquard.config.brand_name %></span>
21
- </div>
22
-
23
29
  <nav class="jq-sidebar-nav">
24
30
  <% jquard_navigation.each do |group, resources| %>
25
31
  <div>
@@ -43,14 +49,6 @@
43
49
  </aside>
44
50
 
45
51
  <div class="jq-main">
46
- <header class="jq-topbar">
47
- <nav class="jq-breadcrumbs">
48
- <% if @resource %>
49
- <span><%= @resource.navigation_label %></span>
50
- <% end %>
51
- </nav>
52
- </header>
53
-
54
52
  <main class="jq-content">
55
53
  <% if notice.present? %>
56
54
  <div class="jq-toast" role="status">
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= Jquard.config.brand_name %></title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <%= csrf_meta_tags %>
7
+ <%= csp_meta_tag %>
8
+
9
+ <%= jquard_theme_style %>
10
+ <%= yield :head %>
11
+
12
+ <%= stylesheet_link_tag "jquard/application", media: "all" %>
13
+ </head>
14
+ <body class="jq-body">
15
+ <% if notice.present? %>
16
+ <div class="jq-toast" role="status">
17
+ <%= jquard_icon("check-circle", css_class: "jq-toast-icon") %>
18
+ <span><%= notice %></span>
19
+ </div>
20
+ <% end %>
21
+
22
+ <main class="jq-auth">
23
+ <div class="jq-auth-card">
24
+ <header class="jq-auth-header">
25
+ <span class="jq-brand"><%= Jquard.config.brand_name %></span>
26
+
27
+ <% if content_for?(:heading) %>
28
+ <h1 class="jq-auth-heading"><%= yield :heading %></h1>
29
+ <% end %>
30
+
31
+ <% if content_for?(:subheading) %>
32
+ <p class="jq-auth-subheading"><%= yield :subheading %></p>
33
+ <% end %>
34
+ </header>
35
+
36
+ <% if alert.present? %>
37
+ <p class="jq-auth-flash jq-auth-flash--alert" role="alert"><%= alert %></p>
38
+ <% end %>
39
+
40
+ <%= yield %>
41
+ </div>
42
+ </main>
43
+ </body>
44
+ </html>
@@ -19,7 +19,11 @@ module Jquard
19
19
  def show_next_steps
20
20
  say ""
21
21
  say "Jquard is mounted at /#{options[:path]}.", :green
22
- say "Generate your first resource with:", :green
22
+ say ""
23
+ say "Next: install devise and configure authentication in config/initializers/jquard.rb —", :yellow
24
+ say "if you do not want your admin panel exposed.", :yellow
25
+ say ""
26
+ say "Then generate your first resource with:", :green
23
27
  say ""
24
28
  say " bin/rails generate jquard:resource YourModel", :bold
25
29
  say ""
@@ -6,4 +6,15 @@ Jquard.configure do |config|
6
6
  #
7
7
  # config.primary_color = :ruby
8
8
  # config.primary_color = { 50 => "#eff6ff", 100 => "#dbeafe", ..., 950 => "#172554" }
9
+
10
+ # WARNING: the panel is currently PUBLIC — an empty block means "no
11
+ # authentication". Do not deploy it this way.
12
+ #
13
+ # Jquard does not ship authentication; bring your own. Once Devise is
14
+ # installed, replace the empty block with the three lines below.
15
+ # See the README for the full walkthrough.
16
+ config.authenticate_with { }
17
+ # config.authenticate_with { authenticate_user! }
18
+ # config.current_user_method = :current_user
19
+ # config.sign_out_path = -> { main_app.destroy_user_session_path }
9
20
  end
@@ -18,14 +18,20 @@ module Jquard
18
18
 
19
19
  REQUIRED_SHADES = [ 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950 ].freeze
20
20
 
21
- attr_accessor :brand_name
22
- attr_reader :primary_color, :primary_color_palette
21
+ attr_accessor :brand_name, :current_user_method, :sign_out_path, :sign_out_method
22
+ attr_reader :primary_color, :primary_color_palette, :authenticate
23
23
 
24
24
  def initialize
25
25
  @brand_name = "Jquard"
26
26
  self.primary_color = :ruby
27
27
  end
28
28
 
29
+ def authenticate_with(&block)
30
+ raise Jquard::Error, "authenticate_with requires a block" unless block
31
+
32
+ @authenticate = block
33
+ end
34
+
29
35
  def primary_color=(value)
30
36
  @primary_color_palette =
31
37
  if value.is_a?(Hash)
data/lib/jquard/engine.rb CHANGED
@@ -18,6 +18,8 @@ module Jquard
18
18
  config.to_prepare do
19
19
  Jquard.registry.clear
20
20
  Jquard.eager_load_resources!
21
+
22
+ ::DeviseController.include(Jquard::AuthLayout) if defined?(::Devise)
21
23
  end
22
24
  end
23
25
  end
@@ -1,29 +1,30 @@
1
1
  @import "tailwindcss" source(none);
2
2
 
3
+ @layer base {
4
+ html:has(> .jq-body) {
5
+ @apply bg-gray-50;
6
+ }
7
+ }
8
+
3
9
  @layer components {
4
10
  .jq-body {
5
11
  @apply min-h-screen bg-gray-50 font-sans text-gray-950 antialiased;
6
12
  }
7
13
 
8
14
  .jq-shell {
9
- @apply flex min-h-screen;
15
+ @apply flex min-h-[calc(100dvh-4rem)];
10
16
  }
11
17
 
12
18
  .jq-sidebar {
13
- @apply fixed inset-y-0 left-0 z-20 flex w-72 flex-col bg-white ring-1 ring-gray-950/5;
14
- }
15
-
16
- .jq-sidebar-header {
17
- @apply flex h-16 shrink-0 items-center px-6;
19
+ @apply sticky top-16 z-20 flex h-[calc(100dvh-4rem)] w-72 shrink-0 flex-col;
18
20
  }
19
21
 
20
22
  .jq-brand {
21
- @apply text-xl font-bold tracking-tight;
22
- color: var(--jq-primary-600);
23
+ @apply text-xl font-bold tracking-tight text-gray-950;
23
24
  }
24
25
 
25
26
  .jq-sidebar-nav {
26
- @apply flex flex-1 flex-col gap-y-7 overflow-y-auto px-4 py-6;
27
+ @apply flex flex-1 flex-col gap-y-7 overflow-y-auto px-6 py-8;
27
28
  }
28
29
 
29
30
  .jq-nav-group-label {
@@ -52,19 +53,19 @@
52
53
  }
53
54
 
54
55
  .jq-main {
55
- @apply flex-1 pl-72;
56
+ @apply min-w-0 flex-1;
56
57
  }
57
58
 
58
59
  .jq-topbar {
59
- @apply sticky top-0 z-10 flex h-16 items-center justify-between bg-white/80 px-6 ring-1 ring-gray-950/5 backdrop-blur lg:px-8;
60
+ @apply sticky top-0 z-30 flex h-16 items-center bg-white px-6 shadow-sm ring-1 ring-gray-950/5 lg:px-8;
60
61
  }
61
62
 
62
- .jq-breadcrumbs {
63
- @apply flex items-center gap-x-2 text-sm font-medium text-gray-500;
63
+ .jq-topbar-start {
64
+ @apply me-6 flex items-center;
64
65
  }
65
66
 
66
- .jq-breadcrumb-sep {
67
- @apply h-4 w-4 text-gray-400;
67
+ .jq-topbar-end {
68
+ @apply ms-auto flex items-center gap-x-4;
68
69
  }
69
70
 
70
71
  .jq-content {
@@ -72,7 +73,31 @@
72
73
  }
73
74
 
74
75
  .jq-page-header {
75
- @apply mb-6 flex items-center justify-between;
76
+ @apply mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between;
77
+ }
78
+
79
+ .jq-page-header .jq-breadcrumbs {
80
+ @apply mb-2 hidden sm:block;
81
+ }
82
+
83
+ .jq-page-header-actions {
84
+ @apply flex shrink-0 items-center gap-3 sm:mt-7;
85
+ }
86
+
87
+ .jq-breadcrumbs-list {
88
+ @apply flex flex-wrap items-center gap-x-2;
89
+ }
90
+
91
+ .jq-breadcrumbs-item {
92
+ @apply flex items-center gap-x-2 text-sm font-medium text-gray-500;
93
+ }
94
+
95
+ .jq-breadcrumbs-item a {
96
+ @apply transition duration-75 hover:text-gray-700;
97
+ }
98
+
99
+ .jq-breadcrumbs-item-separator {
100
+ @apply flex size-4 text-gray-400;
76
101
  }
77
102
 
78
103
  .jq-page-title {
@@ -458,6 +483,127 @@
458
483
  .jq-btn--danger {
459
484
  @apply bg-red-600 text-white hover:bg-red-500;
460
485
  }
486
+
487
+ .jq-btn--block {
488
+ @apply w-full justify-center;
489
+ }
490
+
491
+ .jq-avatar {
492
+ @apply flex size-8 shrink-0 items-center justify-center rounded-full bg-gray-950 text-xs font-medium text-white;
493
+ }
494
+
495
+ .jq-dropdown {
496
+ @apply relative;
497
+ }
498
+
499
+ .jq-dropdown-trigger {
500
+ @apply flex cursor-pointer list-none;
501
+ }
502
+
503
+ .jq-dropdown-trigger::-webkit-details-marker {
504
+ @apply hidden;
505
+ }
506
+
507
+ .jq-dropdown-panel {
508
+ @apply absolute end-0 top-full z-20 mt-2 w-screen max-w-56 divide-y divide-gray-100 rounded-lg bg-white shadow-lg ring-1 ring-gray-950/5;
509
+ }
510
+
511
+ .jq-dropdown-header {
512
+ @apply flex w-full items-center gap-2 p-3 text-sm font-medium;
513
+ }
514
+
515
+ .jq-dropdown-header-label {
516
+ @apply flex-1 truncate text-start text-gray-700;
517
+ }
518
+
519
+ .jq-dropdown-icon {
520
+ @apply size-5 shrink-0 text-gray-400;
521
+ }
522
+
523
+ .jq-dropdown-list {
524
+ @apply grid gap-y-px p-1;
525
+ }
526
+
527
+ .jq-dropdown-form {
528
+ @apply grid;
529
+ }
530
+
531
+ .jq-dropdown-list-item {
532
+ @apply flex w-full cursor-pointer items-center gap-2 overflow-hidden rounded-md bg-transparent p-2 text-sm whitespace-nowrap transition-colors duration-75 hover:bg-gray-50;
533
+ }
534
+
535
+ .jq-dropdown-list-item-label {
536
+ @apply flex-1 truncate text-start text-gray-700;
537
+ }
538
+
539
+ .jq-auth {
540
+ @apply flex min-h-dvh flex-col items-center justify-center px-4;
541
+ }
542
+
543
+ .jq-auth-card {
544
+ @apply my-16 flex w-full max-w-lg flex-col gap-y-6 bg-white px-6 py-12 shadow-sm ring-1 ring-gray-950/5 sm:rounded-xl sm:px-12;
545
+ }
546
+
547
+ .jq-auth-header {
548
+ @apply flex flex-col items-center;
549
+ }
550
+
551
+ .jq-auth-header .jq-brand {
552
+ @apply mb-4;
553
+ }
554
+
555
+ .jq-auth-heading {
556
+ @apply text-center text-2xl font-bold tracking-tight text-gray-950;
557
+ }
558
+
559
+ .jq-auth-subheading {
560
+ @apply mt-2 text-center text-sm text-gray-500;
561
+ }
562
+
563
+ .jq-auth-form {
564
+ @apply grid gap-y-6;
565
+ }
566
+
567
+ .jq-auth-flash {
568
+ @apply rounded-lg px-4 py-3 text-center text-sm font-medium ring-1;
569
+ }
570
+
571
+ .jq-auth-flash--alert {
572
+ @apply bg-red-50 text-red-700 ring-red-600/20;
573
+ }
574
+
575
+ .jq-auth-links {
576
+ @apply flex flex-col items-center gap-y-2;
577
+ }
578
+
579
+ .jq-auth-divider {
580
+ @apply border-t border-gray-200;
581
+ }
582
+
583
+ .jq-label-row {
584
+ @apply flex items-center justify-between;
585
+ }
586
+
587
+ .jq-link {
588
+ @apply text-sm font-semibold hover:underline;
589
+ color: var(--jq-primary-600);
590
+ }
591
+
592
+ .jq-link:hover {
593
+ color: var(--jq-primary-500);
594
+ }
595
+
596
+ .jq-auth-errors {
597
+ @apply rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 ring-1 ring-red-600/20;
598
+ }
599
+
600
+ .jq-auth-errors-title {
601
+ @apply font-semibold;
602
+ }
603
+
604
+ .jq-auth-errors-list {
605
+ @apply mt-1 list-disc ps-5;
606
+ }
461
607
  }
462
608
 
463
609
  @keyframes jq-toast-out {
@@ -1,3 +1,3 @@
1
1
  module Jquard
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jquard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wieland Pfesdorf
@@ -102,16 +102,29 @@ files:
102
102
  - app/components/jquard/tables/columns/text_column_component.rb
103
103
  - app/components/jquard/tables/table_component.html.erb
104
104
  - app/components/jquard/tables/table_component.rb
105
+ - app/controllers/concerns/jquard/auth_layout.rb
105
106
  - app/controllers/jquard/application_controller.rb
106
107
  - app/controllers/jquard/resources_controller.rb
107
108
  - app/helpers/jquard/application_helper.rb
108
109
  - app/jobs/jquard/application_job.rb
109
110
  - app/mailers/jquard/application_mailer.rb
110
111
  - app/models/jquard/application_record.rb
112
+ - app/views/devise/confirmations/new.html.erb
113
+ - app/views/devise/passwords/edit.html.erb
114
+ - app/views/devise/passwords/new.html.erb
115
+ - app/views/devise/registrations/edit.html.erb
116
+ - app/views/devise/registrations/new.html.erb
117
+ - app/views/devise/sessions/new.html.erb
118
+ - app/views/devise/shared/_error_messages.html.erb
119
+ - app/views/devise/shared/_links.html.erb
120
+ - app/views/devise/unlocks/new.html.erb
121
+ - app/views/jquard/_breadcrumbs.html.erb
122
+ - app/views/jquard/_user_menu.html.erb
111
123
  - app/views/jquard/resources/edit.html.erb
112
124
  - app/views/jquard/resources/index.html.erb
113
125
  - app/views/jquard/resources/new.html.erb
114
126
  - app/views/layouts/jquard/application.html.erb
127
+ - app/views/layouts/jquard/auth.html.erb
115
128
  - config/routes.rb
116
129
  - lib/generators/jquard/install/install_generator.rb
117
130
  - lib/generators/jquard/install/templates/initializer.rb.tt