fino-rails 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/fino/rails/app/assets/stylesheets/fino.css +1 -0
- data/lib/fino/rails/app/controllers/fino/rails/application_controller.rb +27 -0
- data/lib/fino/rails/app/controllers/fino/rails/dashboard_controller.rb +7 -0
- data/lib/fino/rails/app/controllers/fino/rails/sections_controller.rb +7 -0
- data/lib/fino/rails/app/controllers/fino/rails/settings_controller.rb +12 -9
- data/lib/fino/rails/app/helpers/fino/rails/application_helper.rb +8 -0
- data/lib/fino/rails/app/helpers/fino/rails/settings_helper.rb +18 -0
- data/lib/fino/rails/app/views/fino/rails/common/_navbar.html.erb +17 -0
- data/lib/fino/rails/app/views/fino/rails/common/_sidebar.html.erb +33 -0
- data/lib/fino/rails/app/views/fino/rails/dashboard/index.html.erb +9 -0
- data/lib/fino/rails/app/views/fino/rails/sections/show.html.erb +11 -0
- data/lib/fino/rails/app/views/fino/rails/settings/_setting.html.erb +52 -0
- data/lib/fino/rails/app/views/fino/rails/settings/edit.html.erb +110 -173
- data/lib/fino/rails/app/views/layouts/fino/rails/application.html.erb +18 -130
- data/lib/fino/rails/config/routes.rb +5 -3
- data/lib/fino/rails/engine.rb +27 -7
- data/lib/fino/rails/preloading/middleware.rb +38 -0
- data/lib/fino/rails/public/fino-assets/fino.css +2 -0
- data/lib/fino/rails/public/fino-assets/logo.svg +38 -0
- data/lib/fino/version.rb +1 -1
- metadata +16 -4
- data/lib/fino/rails/app/views/fino/rails/settings/index.html.erb +0 -155
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3410200fba2a47e0eca05d2889da44ebbd9c9919bde2b8dfa501c21c79466ca
|
4
|
+
data.tar.gz: f0c3c36d92f16e516371c54c5d6f4b12fc431940b2528eebf38e299f23bfbc21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f2d6cf96189a72808e9ae950eb4fbf835cd26b98fc2b71fd96c345e7862eece7c788769414b7de90513b02717f35b9b295e76b1b79798e544b47ca16efa2f2d
|
7
|
+
data.tar.gz: 55b8d40dc775215a05d2cc44871d77ea6cff9e4e45b494d8819ae370880b1458c2efacb3df45fc27aab8e1990ec852e877dd29a29fe4f021bd55da1cce31b6ad
|
data/README.md
CHANGED
@@ -56,7 +56,7 @@ Fino.value(:temperature, at: :openai) #=> 0.7
|
|
56
56
|
|
57
57
|
Fino.values(:model, :temperature, at: :openai) #=> ["gpt-4", 0.7]
|
58
58
|
|
59
|
-
Fino.set("gpt-5",
|
59
|
+
Fino.set(model: "gpt-5", at: :openai)
|
60
60
|
Fino.value(:model, at: :openai) #=> "gpt-5"
|
61
61
|
```
|
62
62
|
|
@@ -0,0 +1 @@
|
|
1
|
+
@import "tailwindcss";
|
@@ -1,4 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Fino::Rails::ApplicationController < ActionController::Base
|
4
|
+
GENERAL_SECTION = Fino::Definition::Section.new(name: nil, label: "General")
|
5
|
+
|
6
|
+
helper Fino::Rails::ApplicationHelper
|
7
|
+
helper Fino::Rails::SettingsHelper
|
8
|
+
|
9
|
+
def sections
|
10
|
+
@sections ||= [
|
11
|
+
GENERAL_SECTION,
|
12
|
+
*Fino.registry.section_definitions
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_section
|
17
|
+
return @current_section if defined?(@current_section)
|
18
|
+
|
19
|
+
section_name = params[:section]&.to_sym
|
20
|
+
|
21
|
+
@current_section =
|
22
|
+
case section_name
|
23
|
+
when :general
|
24
|
+
GENERAL_SECTION
|
25
|
+
else
|
26
|
+
Fino.registry.section_definitions.find { |s| s.name == params[:section]&.to_sym }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
helper_method :sections, :current_section
|
4
31
|
end
|
@@ -2,19 +2,15 @@
|
|
2
2
|
|
3
3
|
class Fino::Rails::SettingsController < Fino::Rails::ApplicationController
|
4
4
|
def index
|
5
|
-
@settings = Fino.
|
5
|
+
@settings = Fino.settings
|
6
6
|
end
|
7
7
|
|
8
8
|
def edit
|
9
|
-
setting_name, at
|
10
|
-
|
11
|
-
@setting = Fino.setting(setting_name, at: at)
|
9
|
+
@setting = Fino.setting(setting_name, at: section_name)
|
12
10
|
end
|
13
11
|
|
14
12
|
def update
|
15
|
-
setting_name
|
16
|
-
|
17
|
-
Fino.set(params[:value], setting_name, at: at)
|
13
|
+
Fino.set(setting_name => params[:value], at: section_name)
|
18
14
|
|
19
15
|
redirect_to root_path, notice: "Setting updated successfully"
|
20
16
|
rescue Fino::Registry::UnknownSetting
|
@@ -23,7 +19,14 @@ class Fino::Rails::SettingsController < Fino::Rails::ApplicationController
|
|
23
19
|
|
24
20
|
private
|
25
21
|
|
26
|
-
def
|
27
|
-
|
22
|
+
def setting_name
|
23
|
+
params[:setting]
|
24
|
+
end
|
25
|
+
|
26
|
+
def section_name
|
27
|
+
case params[:section]
|
28
|
+
when "general" then nil
|
29
|
+
else params[:section]
|
30
|
+
end
|
28
31
|
end
|
29
32
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fino::Rails::ApplicationHelper
|
4
|
+
def fino_asset_path(file, version: true)
|
5
|
+
path = "#{Rails.application.config.relative_url_root}/fino-assets/#{file}".gsub("//", "/")
|
6
|
+
version ? "#{path}?v=#{Fino::VERSION}" : path
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fino::Rails::SettingsHelper
|
4
|
+
SETTING_TYPE_TO_COLOR_MAPPING = {
|
5
|
+
string: "pink",
|
6
|
+
boolean: "blue",
|
7
|
+
integer: "yellow",
|
8
|
+
float: "purple"
|
9
|
+
}
|
10
|
+
|
11
|
+
def setting_type_label(setting)
|
12
|
+
color = SETTING_TYPE_TO_COLOR_MAPPING.fetch(setting.type, "gray")
|
13
|
+
|
14
|
+
tag.div class: "flex-none rounded-full bg-#{color}-50 px-2 py-1 text-xs font-medium text-#{color}-700 inset-ring inset-ring-#{color}-700/10" do
|
15
|
+
setting.type.to_s.titleize
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-6 border-b border-gray-200 bg-white px-4 sm:px-6 lg:px-8">
|
2
|
+
<button type="button" command="show-modal" commandfor="sidebar" class="-m-2.5 p-2.5 text-gray-900 xl:hidden">
|
3
|
+
<span class="sr-only">Open sidebar</span>
|
4
|
+
<svg viewBox="0 0 20 20" fill="currentColor" data-slot="icon" aria-hidden="true" class="size-5">
|
5
|
+
<path d="M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75ZM2 10a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 10Zm0 5.25a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd" fill-rule="evenodd" />
|
6
|
+
</svg>
|
7
|
+
</button>
|
8
|
+
|
9
|
+
<div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
|
10
|
+
<form action="#" method="GET" class="grid flex-1 grid-cols-1">
|
11
|
+
<input name="search" placeholder="Search" aria-label="Search" class="col-start-1 row-start-1 block size-full bg-transparent pl-8 text-base text-gray-900 outline-hidden placeholder:text-gray-400 sm:text-sm/6" />
|
12
|
+
<svg viewBox="0 0 20 20" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 size-5 self-center text-gray-400">
|
13
|
+
<path d="M9 3.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11ZM2 9a7 7 0 1 1 12.452 4.391l3.328 3.329a.75.75 0 1 1-1.06 1.06l-3.329-3.328A7 7 0 0 1 2 9Z" clip-rule="evenodd" fill-rule="evenodd" />
|
14
|
+
</svg>
|
15
|
+
</form>
|
16
|
+
</div>
|
17
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<div class="hidden xl:fixed xl:inset-y-0 xl:z-50 xl:flex xl:w-72 xl:flex-col">
|
2
|
+
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-50 px-4 ring-1 ring-gray-200">
|
3
|
+
<div class="flex h-16 shrink-0 items-center">
|
4
|
+
<%= image_tag "/fino-assets/logo.svg", class: "h-8 w-auto", alt: "Fino" %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<nav class="flex flex-1 flex-col gap-y-7">
|
8
|
+
<div>
|
9
|
+
<%= link_to root_path, class: "flex gap-x-3 rounded-md p-2 text-sm/6 font-semibold #{current_page?(:root) ? 'text-indigo-600' : 'text-gray-700'} hover:bg-gray-100 hover:text-indigo-600" do %>
|
10
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" data-slot="icon" aria-hidden="true" class="size-6 shrink-0 text-indigo-600">
|
11
|
+
<path d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" stroke-linecap="round" stroke-linejoin="round" />
|
12
|
+
</svg>
|
13
|
+
Dashboard
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div>
|
18
|
+
<div class="text-xs/6 font-semibold text-gray-400">Sections</div>
|
19
|
+
<ul role="list" class="space-y-1">
|
20
|
+
<% sections.each do |section_definition| %>
|
21
|
+
<li>
|
22
|
+
<%= link_to settings_section_path(section: section_definition.name || :general), class: "group flex gap-x-3 rounded-md p-2 text-sm/6 font-semibold #{current_section && (current_section.name == section_definition.name) ? 'text-indigo-600' : 'text-gray-700'} hover:bg-gray-100 hover:text-indigo-600" do %>
|
23
|
+
<span class="flex size-6 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-white text-[0.625rem] font-medium text-gray-400 group-hover:border-indigo-600 group-hover:text-indigo-600"><%= section_definition.label.first %></span>
|
24
|
+
<span class="break-words"><%= section_definition.label %></span>
|
25
|
+
<span aria-hidden="true" class="ml-auto w-9 min-w-max rounded-full bg-white px-2.5 py-0.5 text-center text-xs/5 font-medium whitespace-nowrap text-gray-600 outline-1 -outline-offset-1 outline-gray-200"><%=Fino.settings(at: section_definition.name).count %></span>
|
26
|
+
<% end %>
|
27
|
+
</li>
|
28
|
+
<% end %>
|
29
|
+
</ul>
|
30
|
+
</div>
|
31
|
+
</nav>
|
32
|
+
</div>
|
33
|
+
</div>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<div class="m-3">
|
2
|
+
<ul role="list" class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
3
|
+
<% @settings.each do |setting| %>
|
4
|
+
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white border-1 border-gray-200">
|
5
|
+
<%= render "fino/rails/settings/setting", locals: { setting: setting } %>
|
6
|
+
</li>
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
</div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% content_for(:title, "#{current_section.label}") %>
|
2
|
+
|
3
|
+
<div class="m-3">
|
4
|
+
<ul role="list" class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
5
|
+
<% @settings.each do |setting| %>
|
6
|
+
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white border-1 border-gray-200">
|
7
|
+
<%= render "fino/rails/settings/setting", locals: { setting: setting } %>
|
8
|
+
</li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<% setting = locals.fetch(:setting) %>
|
2
|
+
|
3
|
+
<%= link_to edit_setting_path(section: setting.section_name || :general, setting: setting.name) do %>
|
4
|
+
<div class="flex w-full h-full justify-between p-6 hover:bg-gray-50 transition-colors rounded-lg">
|
5
|
+
<div class="flex flex-col space-y-3 justify-between">
|
6
|
+
<div class="flex flex-col gap-1">
|
7
|
+
<div class="flex items-center gap-x-3">
|
8
|
+
<h2 class="min-w-0 text-sm/6 font-semibold text-gray-900">
|
9
|
+
<div class="flex gap-x-2">
|
10
|
+
<span class="truncate"><%= setting.definition.section_definition&.label || "General" %></span>
|
11
|
+
<span class="text-gray-500">/</span>
|
12
|
+
<span class="whitespace-nowrap"><%= setting.name %></span>
|
13
|
+
</div>
|
14
|
+
</h2>
|
15
|
+
|
16
|
+
<%= setting_type_label(setting) %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<% if setting.description.present? %>
|
20
|
+
<p class="text-sm text-gray-500 whitespace-normal"><%= setting.description %></p>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="">
|
25
|
+
<% case setting %>
|
26
|
+
<% when Fino::Settings::Boolean %>
|
27
|
+
<div class="flex items-center gap-x-2">
|
28
|
+
<div class="flex-none rounded-full p-1 <%= setting.value ? 'text-green-500 bg-green-100' : 'text-red-500 bg-red-100' %>">
|
29
|
+
<div class="size-2 rounded-full bg-current"></div>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<span class="text-lg font-medium text-gray-800"><%= setting.value ? 'Enabled' : 'Disabled' %></span>
|
33
|
+
</div>
|
34
|
+
<% when Fino::Settings::String %>
|
35
|
+
<div class="truncate text-xl font-semibold text-gray-800">
|
36
|
+
"<%= setting.value %>"
|
37
|
+
</div>
|
38
|
+
<% else %>
|
39
|
+
<div class="truncate text-xl font-semibold text-gray-800">
|
40
|
+
<%= setting.value %>
|
41
|
+
</div>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="flex items-center">
|
47
|
+
<svg viewBox="0 0 20 20" fill="currentColor" data-slot="icon" aria-hidden="true" class="size-5 flex-none text-gray-400">
|
48
|
+
<path d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
49
|
+
</svg>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
<% end %>
|
@@ -1,179 +1,116 @@
|
|
1
|
-
<% content_for(:title,
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
border: 1px solid #e2e8f0;
|
25
|
-
border-radius: 6px;
|
26
|
-
transition: border-color 0.15s ease-in-out;
|
27
|
-
}
|
28
|
-
|
29
|
-
.form-input:focus {
|
30
|
-
outline: none;
|
31
|
-
border-color: #0f172a;
|
32
|
-
box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.1);
|
33
|
-
}
|
34
|
-
|
35
|
-
.form-select {
|
36
|
-
display: block;
|
37
|
-
width: 100%;
|
38
|
-
padding: 0.5rem 0.75rem;
|
39
|
-
font-size: 0.875rem;
|
40
|
-
line-height: 1.5;
|
41
|
-
color: #0f172a;
|
42
|
-
background-color: #ffffff;
|
43
|
-
border: 1px solid #e2e8f0;
|
44
|
-
border-radius: 6px;
|
45
|
-
transition: border-color 0.15s ease-in-out;
|
46
|
-
}
|
47
|
-
|
48
|
-
.form-select:focus {
|
49
|
-
outline: none;
|
50
|
-
border-color: #0f172a;
|
51
|
-
box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.1);
|
52
|
-
}
|
53
|
-
|
54
|
-
.form-checkbox {
|
55
|
-
width: 1rem;
|
56
|
-
height: 1rem;
|
57
|
-
border: 1px solid #e2e8f0;
|
58
|
-
border-radius: 4px;
|
59
|
-
margin-right: 0.5rem;
|
60
|
-
}
|
61
|
-
|
62
|
-
.form-help {
|
63
|
-
font-size: 0.75rem;
|
64
|
-
color: #64748b;
|
65
|
-
margin-top: 0.25rem;
|
66
|
-
}
|
67
|
-
|
68
|
-
.button-group {
|
69
|
-
display: flex;
|
70
|
-
gap: 0.75rem;
|
71
|
-
margin-top: 2rem;
|
72
|
-
}
|
73
|
-
|
74
|
-
.breadcrumb {
|
75
|
-
margin-bottom: 1.5rem;
|
76
|
-
font-size: 0.875rem;
|
77
|
-
color: #64748b;
|
78
|
-
}
|
79
|
-
|
80
|
-
.breadcrumb a {
|
81
|
-
color: #0f172a;
|
82
|
-
text-decoration: none;
|
83
|
-
}
|
84
|
-
|
85
|
-
.breadcrumb a:hover {
|
86
|
-
text-decoration: underline;
|
87
|
-
}
|
88
|
-
|
89
|
-
.setting-info {
|
90
|
-
background-color: #f8fafc;
|
91
|
-
border: 1px solid #e2e8f0;
|
92
|
-
border-radius: 6px;
|
93
|
-
padding: 1rem;
|
94
|
-
margin-bottom: 1.5rem;
|
95
|
-
}
|
96
|
-
|
97
|
-
.setting-info-title {
|
98
|
-
font-weight: 600;
|
99
|
-
margin-bottom: 0.5rem;
|
100
|
-
color: #0f172a;
|
101
|
-
}
|
102
|
-
|
103
|
-
.setting-info-detail {
|
104
|
-
font-size: 0.875rem;
|
105
|
-
color: #64748b;
|
106
|
-
margin-bottom: 0.25rem;
|
107
|
-
}
|
108
|
-
</style>
|
109
|
-
<% end %>
|
110
|
-
|
111
|
-
<div class="breadcrumb">
|
112
|
-
<%= link_to "Settings", root_path %> / Edit <%= @setting.name %>
|
113
|
-
</div>
|
114
|
-
|
115
|
-
<div class="header">
|
116
|
-
<h1>Edit Setting</h1>
|
117
|
-
<p>Modify the configuration value for this setting</p>
|
118
|
-
</div>
|
119
|
-
|
120
|
-
<div class="card">
|
121
|
-
<div class="card-header">
|
122
|
-
<h2 class="card-title"><%= @setting.name %></h2>
|
123
|
-
</div>
|
124
|
-
<div class="card-content">
|
125
|
-
<div class="setting-info">
|
126
|
-
<div class="setting-info-title">Setting Information</div>
|
127
|
-
<div class="setting-info-detail"><strong>Type:</strong> <%= @setting.class.name.demodulize %></div>
|
128
|
-
<% if @setting.section_name %>
|
129
|
-
<div class="setting-info-detail"><strong>Section:</strong> <%= @setting.section_name %></div>
|
130
|
-
<% end %>
|
131
|
-
<div class="setting-info-detail"><strong>Default:</strong> <code><%= @setting.definition.default.inspect %></code></div>
|
132
|
-
<% if @setting.definition.options[:description].present? %>
|
133
|
-
<div class="setting-info-detail"><strong>Description:</strong> <%= @setting.definition.options[:description] %></div>
|
134
|
-
<% end %>
|
135
|
-
</div>
|
136
|
-
|
137
|
-
<%= form_with url: update_setting_path(@setting.key), method: :put, local: true do |f| %>
|
138
|
-
<div class="form-group">
|
139
|
-
<%= f.label :value, class: "form-label" do %>
|
140
|
-
Current Value
|
141
|
-
<% if @setting.definition.options[:description].present? %>
|
142
|
-
<div class="form-help"><%= @setting.definition.options[:description] %></div>
|
143
|
-
<% end %>
|
144
|
-
<% end %>
|
145
|
-
|
146
|
-
<% case @setting.class.name.demodulize.downcase %>
|
147
|
-
<% when 'boolean' %>
|
148
|
-
<%= f.check_box :value,
|
149
|
-
{ class: "form-checkbox", checked: @setting.value },
|
150
|
-
'1',
|
151
|
-
'0' %>
|
152
|
-
<% when 'integer' %>
|
153
|
-
<%= f.number_field :value,
|
154
|
-
value: @setting.value,
|
155
|
-
class: "form-input",
|
156
|
-
step: 1 %>
|
157
|
-
<% when 'float' %>
|
158
|
-
<%= f.number_field :value,
|
159
|
-
value: @setting.value,
|
160
|
-
class: "form-input",
|
161
|
-
step: 0.1 %>
|
162
|
-
<% else %>
|
163
|
-
<%= f.text_field :value,
|
164
|
-
value: @setting.value,
|
165
|
-
class: "form-input" %>
|
166
|
-
<% end %>
|
1
|
+
<% content_for(:title, @setting.key) %>
|
2
|
+
|
3
|
+
<div class="m-3">
|
4
|
+
<div class="container mx-auto md:w-xl px-0 pt-10">
|
5
|
+
<div class="bg-white shadow-xs outline outline-gray-900/5 sm:rounded-xl md:col-span-2">
|
6
|
+
<%= form_with url: update_setting_path(section: @setting.section_name || :general, setting: @setting.name), method: :put, local: true do |f| %>
|
7
|
+
<div class="border-b border-gray-100 p-3 md:p-5 flex justify-between">
|
8
|
+
<div class="flex self-center">
|
9
|
+
<h3 class="text-base font-semibold text-gray-900"><%= link_to @setting.section_definition&.label || "General", settings_section_path(section: @setting.section_name || :general) %></h3>
|
10
|
+
<span class="mx-2 text-gray-400">/</span>
|
11
|
+
<h3 class="text-base font-semibold text-gray-900"><%= @setting.name %></h3>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div class="flex gap-3 lg:mt-0 lg:ml-4">
|
15
|
+
<span>
|
16
|
+
<%= link_to "Cancel", :back, class: "inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50" %>
|
17
|
+
</span>
|
18
|
+
|
19
|
+
<span>
|
20
|
+
<%= f.submit "Save", class: "inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-indigo-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" %>
|
21
|
+
</span>
|
22
|
+
</div>
|
23
|
+
</div>
|
167
24
|
|
168
|
-
<div class="
|
169
|
-
|
25
|
+
<div class="px-4 py-6 sm:p-6">
|
26
|
+
<div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
27
|
+
<% case @setting %>
|
28
|
+
<% when Fino::Settings::Boolean %>
|
29
|
+
<div class="col-span-full">
|
30
|
+
<div class="flex gap-3">
|
31
|
+
<div class="flex h-6 shrink-0 items-center">
|
32
|
+
<div class="group grid size-4 grid-cols-1">
|
33
|
+
<%= f.check_box :value,
|
34
|
+
{
|
35
|
+
id: "value",
|
36
|
+
class: "col-start-1 row-start-1 appearance-none rounded-sm border border-gray-300 bg-white checked:border-indigo-600 checked:bg-indigo-600 indeterminate:border-indigo-600 indeterminate:bg-indigo-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:border-gray-300 disabled:bg-gray-100 disabled:checked:bg-gray-100 forced-colors:appearance-auto",
|
37
|
+
checked: @setting.value
|
38
|
+
},
|
39
|
+
'1',
|
40
|
+
'0' %>
|
41
|
+
<svg viewBox="0 0 14 14" fill="none" class="pointer-events-none col-start-1 row-start-1 size-3.5 self-center justify-self-center stroke-white group-has-disabled:stroke-gray-950/25">
|
42
|
+
<path d="M3 8L6 11L11 3.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-0 group-has-checked:opacity-100" />
|
43
|
+
<path d="M3 7H11" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-0 group-has-indeterminate:opacity-100" />
|
44
|
+
</svg>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
<div class="text-sm/6">
|
48
|
+
<label for="value" class="font-medium text-gray-900"><%= @setting.name %></label>
|
49
|
+
<p id="comments-description" class="text-gray-500"><%= @setting.description %></p>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
<% when Fino::Settings::String %>
|
54
|
+
<div class="col-span-full">
|
55
|
+
<div>
|
56
|
+
<label for="username" class="block text-sm/6 font-medium text-gray-900"><%= @setting.name %></label>
|
57
|
+
<div class="mt-2">
|
58
|
+
<div class="flex items-center rounded-md bg-white pl-3 outline-1 -outline-offset-1 outline-gray-300 focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-indigo-600">
|
59
|
+
<%= f.text_field :value,
|
60
|
+
value: @setting.value,
|
61
|
+
class: "block min-w-0 grow bg-white py-1.5 pr-3 pl-1 text-base text-gray-900 placeholder:text-gray-400 focus:outline-none sm:text-sm/6" %>
|
62
|
+
</div>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
<p class="mt-3 text-sm/6 text-gray-600"><%= @setting.description %></p>
|
66
|
+
</div>
|
67
|
+
<% when Fino::Settings::Integer %>
|
68
|
+
<div class="col-span-full">
|
69
|
+
<div>
|
70
|
+
<label for="username" class="block text-sm/6 font-medium text-gray-900"><%= @setting.name %></label>
|
71
|
+
<div class="mt-2">
|
72
|
+
<div class="flex items-center rounded-md bg-white pl-3 outline-1 -outline-offset-1 outline-gray-300 focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-indigo-600">
|
73
|
+
<%= f.number_field :value,
|
74
|
+
value: @setting.value,
|
75
|
+
class: "block min-w-0 grow bg-white py-1.5 pr-3 pl-1 text-base text-gray-900 placeholder:text-gray-400 focus:outline-none sm:text-sm/6",
|
76
|
+
step: 1 %>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
<p class="mt-3 text-sm/6 text-gray-600"><%= @setting.description %></p>
|
81
|
+
</div>
|
82
|
+
<% when Fino::Settings::Float %>
|
83
|
+
<div class="col-span-full">
|
84
|
+
<div>
|
85
|
+
<label for="username" class="block text-sm/6 font-medium text-gray-900"><%= @setting.name %></label>
|
86
|
+
<div class="mt-2">
|
87
|
+
<div class="flex items-center rounded-md bg-white pl-3 outline-1 -outline-offset-1 outline-gray-300 focus-within:outline-2 focus-within:-outline-offset-2 focus-within:outline-indigo-600">
|
88
|
+
<%= f.number_field :value,
|
89
|
+
value: @setting.value,
|
90
|
+
class:"block min-w-0 grow bg-white py-1.5 pr-3 pl-1 text-base text-gray-900 placeholder:text-gray-400 focus:outline-none sm:text-sm/6",
|
91
|
+
step: 0.1 %>
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
</div>
|
95
|
+
<p class="mt-3 text-sm/6 text-gray-600"><%= @setting.description %></p>
|
96
|
+
</div>
|
97
|
+
<% end %>
|
98
|
+
</div>
|
170
99
|
</div>
|
171
|
-
|
100
|
+
<% end %>
|
172
101
|
|
173
|
-
<div class="
|
174
|
-
|
175
|
-
|
102
|
+
<div class="border-t border-gray-100">
|
103
|
+
<dl class="divide-y divide-gray-100">
|
104
|
+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
105
|
+
<dt class="text-sm font-medium text-gray-900">Type</dt>
|
106
|
+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-1 sm:mt-0 flex"><%= setting_type_label(@setting) %></dd>
|
107
|
+
</div>
|
108
|
+
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
109
|
+
<dt class="text-sm font-medium text-gray-900">Default</dt>
|
110
|
+
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @setting.definition.default.inspect %></dd>
|
111
|
+
</div>
|
112
|
+
</dl>
|
176
113
|
</div>
|
177
|
-
|
114
|
+
</div>
|
178
115
|
</div>
|
179
116
|
</div>
|
@@ -1,138 +1,26 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html lang="en">
|
3
|
-
<head>
|
4
|
-
<meta charset="UTF-8">
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
-
<title>Fino - <%= yield(:title) || 'Settings' %></title>
|
7
|
-
<style>
|
8
|
-
* {
|
9
|
-
box-sizing: border-box;
|
10
|
-
margin: 0;
|
11
|
-
padding: 0;
|
12
|
-
}
|
13
2
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
background-color: #ffffff;
|
19
|
-
min-height: 100vh;
|
20
|
-
}
|
3
|
+
<html lang="en" class="h-full bg-white">
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
21
7
|
|
22
|
-
|
23
|
-
max-width: 1200px;
|
24
|
-
margin: 0 auto;
|
25
|
-
padding: 2rem 1rem;
|
26
|
-
}
|
8
|
+
<title><%= yield(:title) || 'Settings' %> | Fino</title>
|
27
9
|
|
28
|
-
.
|
29
|
-
margin-bottom: 2rem;
|
30
|
-
}
|
10
|
+
<link href="<%= fino_asset_path("/fino.css") %>" rel="stylesheet">
|
31
11
|
|
32
|
-
.
|
33
|
-
|
34
|
-
font-weight: 600;
|
35
|
-
color: #0f172a;
|
36
|
-
margin-bottom: 0.5rem;
|
37
|
-
}
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/@tailwindplus/elements@1" type="module"></script>
|
13
|
+
</head>
|
38
14
|
|
39
|
-
|
40
|
-
|
41
|
-
font-size: 1rem;
|
42
|
-
}
|
15
|
+
<body class="h-full">
|
16
|
+
<%= render "fino/rails/common/sidebar" %>
|
43
17
|
|
44
|
-
|
45
|
-
|
46
|
-
border-radius: 8px;
|
47
|
-
border: 1px solid #e2e8f0;
|
48
|
-
}
|
18
|
+
<div class="xl:pl-72">
|
19
|
+
<%= render "fino/rails/common/navbar" %>
|
49
20
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
font-size: 1.125rem;
|
57
|
-
font-weight: 600;
|
58
|
-
color: #0f172a;
|
59
|
-
}
|
60
|
-
|
61
|
-
.card-content {
|
62
|
-
padding: 1.5rem;
|
63
|
-
}
|
64
|
-
|
65
|
-
.btn {
|
66
|
-
display: inline-flex;
|
67
|
-
align-items: center;
|
68
|
-
justify-content: center;
|
69
|
-
border-radius: 6px;
|
70
|
-
font-size: 0.875rem;
|
71
|
-
font-weight: 500;
|
72
|
-
transition: colors 0.15s ease-in-out;
|
73
|
-
cursor: pointer;
|
74
|
-
text-decoration: none;
|
75
|
-
border: 1px solid transparent;
|
76
|
-
}
|
77
|
-
|
78
|
-
.btn-primary {
|
79
|
-
background-color: #0f172a;
|
80
|
-
color: #ffffff;
|
81
|
-
padding: 0.5rem 1rem;
|
82
|
-
}
|
83
|
-
|
84
|
-
.btn-primary:hover {
|
85
|
-
background-color: #1e293b;
|
86
|
-
}
|
87
|
-
|
88
|
-
.btn-secondary {
|
89
|
-
background-color: transparent;
|
90
|
-
color: #0f172a;
|
91
|
-
border-color: #e2e8f0;
|
92
|
-
padding: 0.5rem 1rem;
|
93
|
-
}
|
94
|
-
|
95
|
-
.btn-secondary:hover {
|
96
|
-
background-color: #f8fafc;
|
97
|
-
}
|
98
|
-
|
99
|
-
.btn-ghost {
|
100
|
-
background-color: transparent;
|
101
|
-
color: #0f172a;
|
102
|
-
padding: 0.5rem;
|
103
|
-
}
|
104
|
-
|
105
|
-
.btn-ghost:hover {
|
106
|
-
background-color: #f8fafc;
|
107
|
-
}
|
108
|
-
|
109
|
-
.text-muted {
|
110
|
-
color: #64748b;
|
111
|
-
}
|
112
|
-
|
113
|
-
.text-sm {
|
114
|
-
font-size: 0.875rem;
|
115
|
-
}
|
116
|
-
|
117
|
-
.text-xs {
|
118
|
-
font-size: 0.75rem;
|
119
|
-
}
|
120
|
-
|
121
|
-
@media (max-width: 640px) {
|
122
|
-
.container {
|
123
|
-
padding: 1rem 0.5rem;
|
124
|
-
}
|
125
|
-
|
126
|
-
.header h1 {
|
127
|
-
font-size: 1.5rem;
|
128
|
-
}
|
129
|
-
}
|
130
|
-
</style>
|
131
|
-
<%= yield(:head) %>
|
132
|
-
</head>
|
133
|
-
<body>
|
134
|
-
<div class="container">
|
135
|
-
<%= yield %>
|
136
|
-
</div>
|
137
|
-
</body>
|
138
|
-
</html>
|
21
|
+
<main>
|
22
|
+
<%= yield %>
|
23
|
+
</main>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Fino::Rails::Engine.routes.draw do
|
4
|
-
root to: "
|
4
|
+
root to: "dashboard#index"
|
5
5
|
|
6
|
-
get "
|
7
|
-
|
6
|
+
get ":section", to: "sections#show", as: :settings_section
|
7
|
+
|
8
|
+
get ":section/:setting", to: "settings#edit", as: :edit_setting
|
9
|
+
put ":section/:setting", to: "settings#update", as: :update_setting
|
8
10
|
end
|
data/lib/fino/rails/engine.rb
CHANGED
@@ -9,20 +9,30 @@ class Fino::Rails::Engine < Rails::Engine
|
|
9
9
|
# Engine
|
10
10
|
#
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
gem_root = root.join("lib", "fino", "rails")
|
13
|
+
|
14
|
+
paths["app"] << gem_root.join("app")
|
15
|
+
paths["config/initializers"] << gem_root.join("config", "initializers")
|
14
16
|
|
15
17
|
initializer "fino.rails.engine.views" do |_app|
|
16
18
|
ActiveSupport.on_load :action_controller do
|
17
|
-
prepend_view_path
|
19
|
+
prepend_view_path gem_root.join("app", "views")
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
23
|
initializer "fino.rails.engine.routes", before: :add_routing_paths do |app|
|
22
|
-
custom_routes =
|
24
|
+
custom_routes = gem_root.join("config", "routes.rb")
|
23
25
|
app.routes_reloader.paths << custom_routes.to_s
|
24
26
|
end
|
25
27
|
|
28
|
+
initializer "fino.rails.engine.assets" do
|
29
|
+
config.app_middleware.use(
|
30
|
+
Rack::Static,
|
31
|
+
urls: ["/fino-assets"],
|
32
|
+
root: gem_root.join("public").to_s
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
26
36
|
#
|
27
37
|
# Configuration
|
28
38
|
#
|
@@ -30,7 +40,9 @@ class Fino::Rails::Engine < Rails::Engine
|
|
30
40
|
config.before_configuration do
|
31
41
|
config.fino = ActiveSupport::OrderedOptions.new.update(
|
32
42
|
instrument: Rails.env.development?,
|
33
|
-
log: Rails.env.development
|
43
|
+
log: Rails.env.development?,
|
44
|
+
cache_within_request: true,
|
45
|
+
preload_before_request: false
|
34
46
|
)
|
35
47
|
end
|
36
48
|
|
@@ -55,12 +67,20 @@ class Fino::Rails::Engine < Rails::Engine
|
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
58
|
-
use Fino::Rails::RequestScopedCache::Pipe if defined?(Rails::Server)
|
70
|
+
use Fino::Rails::RequestScopedCache::Pipe if defined?(Rails::Server) && config.cache_within_request
|
59
71
|
end
|
60
72
|
end
|
61
73
|
end
|
62
74
|
|
63
75
|
initializer "fino.request_scoped_caching.middleware" do |app|
|
64
|
-
app.
|
76
|
+
config = app.config.fino
|
77
|
+
|
78
|
+
if defined?(Rails::Server)
|
79
|
+
app.middleware.use Fino::Rails::RequestScopedCache::Middleware if config.cache_within_request
|
80
|
+
|
81
|
+
if config.preload_before_request
|
82
|
+
app.middleware.use Fino::Rails::Preloading::Middleware, preload: config.preload_before_request
|
83
|
+
end
|
84
|
+
end
|
65
85
|
end
|
66
86
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Fino::Rails::Preloading::Middleware
|
4
|
+
def initialize(app, options = {})
|
5
|
+
@app = app
|
6
|
+
@preload = options[:preload]
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
maybe_preload_settings(env) rescue nil # rubocop:disable Style/RescueModifier
|
11
|
+
|
12
|
+
app.call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :app, :preload
|
18
|
+
|
19
|
+
def maybe_preload_settings(env) # rubocop:disable Metrics/MethodLength
|
20
|
+
request = Rack::Request.new(env)
|
21
|
+
|
22
|
+
preload_result =
|
23
|
+
if preload.respond_to?(:call)
|
24
|
+
preload.call(request)
|
25
|
+
else
|
26
|
+
preload
|
27
|
+
end
|
28
|
+
|
29
|
+
case preload_result
|
30
|
+
when TrueClass
|
31
|
+
Fino.logger.debug { "Preloading all settings" }
|
32
|
+
Fino.settings
|
33
|
+
when Hash
|
34
|
+
Fino.logger.debug { "Preloading settings: #{preload_result.inspect}" }
|
35
|
+
Fino.slice(preload_result)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,2 @@
|
|
1
|
+
/*! tailwindcss v4.1.13 | 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-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight: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-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size: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-100:oklch(93.6% .032 17.717);--color-red-500:oklch(63.7% .237 25.331);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-700:oklch(55.4% .135 66.442);--color-green-100:oklch(96.2% .044 156.743);--color-green-500:oklch(72.3% .219 149.579);--color-blue-50:oklch(97% .014 254.604);--color-blue-700:oklch(48.8% .243 264.376);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-700:oklch(49.6% .265 301.924);--color-pink-50:oklch(97.1% .014 343.198);--color-pink-700:oklch(52.5% .223 3.958);--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-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-gray-950:oklch(13% .028 261.692);--color-white:#fff;--spacing:.25rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--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-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--font-weight-medium:500;--font-weight-semibold:600;--radius-sm:.25rem;--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;@layer utilities{.pointer-events-none{pointer-events:none}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.sticky{position:sticky}.end-0{inset-inline-end:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.z-40{z-index:40}.col-span-1{grid-column:span 1/span 1}.col-span-full{grid-column:1/-1}.col-start-1{grid-column-start:1}.row-start-1{grid-row-start:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-m-2\.5{margin:calc(var(--spacing)*-2.5)}.m-3{margin:calc(var(--spacing)*3)}.-mx-2{margin-inline:calc(var(--spacing)*-2)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-5{margin-top:calc(var(--spacing)*5)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-3\.5{width:calc(var(--spacing)*3.5);height:calc(var(--spacing)*3.5)}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-6{width:calc(var(--spacing)*6);height:calc(var(--spacing)*6)}.size-full{width:100%;height:100%}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-16{height:calc(var(--spacing)*16)}.h-auto{height:auto}.h-full{height:100%}.h-max{height:max-content}.h-px{height:1px}.max-h-2\.5{max-height:calc(var(--spacing)*2.5)}.max-h-3{max-height:calc(var(--spacing)*3)}.max-h-4{max-height:calc(var(--spacing)*4)}.max-h-5{max-height:calc(var(--spacing)*5)}.max-h-6{max-height:calc(var(--spacing)*6)}.max-h-7{max-height:calc(var(--spacing)*7)}.max-h-10{max-height:calc(var(--spacing)*10)}.max-h-20{max-height:calc(var(--spacing)*20)}.max-h-full{max-height:100%}.max-h-max{max-height:max-content}.min-h-5{min-height:calc(var(--spacing)*5)}.min-h-32{min-height:calc(var(--spacing)*32)}.w-9{width:calc(var(--spacing)*9)}.w-auto{width:auto}.w-full{width:100%}.w-lg{width:var(--container-lg)}.w-xl{width:var(--container-xl)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-lg{max-width:var(--container-lg)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-max{min-width:max-content}.flex-1{flex:1}.flex-2{flex:2}.flex-none{flex:none}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-items-center{justify-items:center}.justify-items-center-safe{justify-items:safe center}.gap-1{gap:calc(var(--spacing)*1)}.gap-3{gap:calc(var(--spacing)*3)}.gap-10{gap:calc(var(--spacing)*10)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-2{column-gap:calc(var(--spacing)*2)}.gap-x-3{column-gap:calc(var(--spacing)*3)}.gap-x-4{column-gap:calc(var(--spacing)*4)}.gap-x-6{column-gap:calc(var(--spacing)*6)}:where(.space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*6)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-5{row-gap:calc(var(--spacing)*5)}.gap-y-7{row-gap:calc(var(--spacing)*7)}.gap-y-8{row-gap:calc(var(--spacing)*8)}:where(.divide-y>: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)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}.self-center{align-self:center}.self-stretch{align-self:stretch}.justify-self-center{justify-self:center}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.border,.border-1{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-current{background-color:currentColor}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-green-100{background-color:var(--color-green-100)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-pink-50{background-color:var(--color-pink-50)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-50{background-color:var(--color-yellow-50)}.stroke-white{stroke:var(--color-white)}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-2\.5{padding:calc(var(--spacing)*2.5)}.p-3{padding:calc(var(--spacing)*3)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-50{padding-inline:calc(var(--spacing)*50)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-6{padding-block:calc(var(--spacing)*6)}.pt-10{padding-top:calc(var(--spacing)*10)}.pr-3{padding-right:calc(var(--spacing)*3)}.pl-1{padding-left:calc(var(--spacing)*1)}.pl-3{padding-left:calc(var(--spacing)*3)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-sm\/6{font-size:var(--text-sm);line-height:calc(var(--spacing)*6)}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-xs\/5{font-size:var(--text-xs);line-height:calc(var(--spacing)*5)}.text-xs\/6{font-size:var(--text-xs);line-height:calc(var(--spacing)*6)}.text-\[0\.625rem\]{font-size:.625rem}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.break-words{overflow-wrap:break-word}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.text-blue-700{color:var(--color-blue-700)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-500{color:var(--color-green-500)}.text-indigo-600{color:var(--color-indigo-600)}.text-pink-700{color:var(--color-pink-700)}.text-purple-700{color:var(--color-purple-700)}.text-red-500{color:var(--color-red-500)}.text-white{color:var(--color-white)}.text-yellow-700{color:var(--color-yellow-700)}.opacity-0{opacity:0}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--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)}.inset-ring{--tw-inset-ring-shadow:inset 0 0 0 1px var(--tw-inset-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)}.ring-gray-200{--tw-ring-color:var(--color-gray-200)}.inset-ring-blue-700\/10{--tw-inset-ring-color:#1447e61a}@supports (color:color-mix(in lab, red, red)){.inset-ring-blue-700\/10{--tw-inset-ring-color:color-mix(in oklab,var(--color-blue-700)10%,transparent)}}.inset-ring-gray-300{--tw-inset-ring-color:var(--color-gray-300)}.inset-ring-gray-700\/10{--tw-inset-ring-color:#3641531a}@supports (color:color-mix(in lab, red, red)){.inset-ring-gray-700\/10{--tw-inset-ring-color:color-mix(in oklab,var(--color-gray-700)10%,transparent)}}.inset-ring-pink-700\/10{--tw-inset-ring-color:#c4005c1a}@supports (color:color-mix(in lab, red, red)){.inset-ring-pink-700\/10{--tw-inset-ring-color:color-mix(in oklab,var(--color-pink-700)10%,transparent)}}.inset-ring-purple-700\/10{--tw-inset-ring-color:#8200da1a}@supports (color:color-mix(in lab, red, red)){.inset-ring-purple-700\/10{--tw-inset-ring-color:color-mix(in oklab,var(--color-purple-700)10%,transparent)}}.inset-ring-yellow-700\/10{--tw-inset-ring-color:#a361001a}@supports (color:color-mix(in lab, red, red)){.inset-ring-yellow-700\/10{--tw-inset-ring-color:color-mix(in oklab,var(--color-yellow-700)10%,transparent)}}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline,.outline-1{outline-style:var(--tw-outline-style);outline-width:1px}.-outline-offset-1{outline-offset:calc(1px*-1)}.outline-gray-200{outline-color:var(--color-gray-200)}.outline-gray-300{outline-color:var(--color-gray-300)}.outline-gray-900\/5{outline-color:#1018280d}@supports (color:color-mix(in lab, red, red)){.outline-gray-900\/5{outline-color:color-mix(in oklab,var(--color-gray-900)5%,transparent)}}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{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))}.transition-colors{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))}@media (hover:hover){.group-hover\:border-indigo-600:is(:where(.group):hover *){border-color:var(--color-indigo-600)}.group-hover\:text-indigo-600:is(:where(.group):hover *){color:var(--color-indigo-600)}}.group-has-checked\:opacity-100:is(:where(.group):has(:checked) *),.group-has-indeterminate\:opacity-100:is(:where(.group):has(:indeterminate) *){opacity:1}.group-has-disabled\:stroke-gray-950\/25:is(:where(.group):has(:disabled) *){stroke:#03071240}@supports (color:color-mix(in lab, red, red)){.group-has-disabled\:stroke-gray-950\/25:is(:where(.group):has(:disabled) *){stroke:color-mix(in oklab,var(--color-gray-950)25%,transparent)}}.placeholder\:text-gray-400::placeholder{color:var(--color-gray-400)}.checked\:border-indigo-600:checked{border-color:var(--color-indigo-600)}.checked\:bg-indigo-600:checked{background-color:var(--color-indigo-600)}.indeterminate\:border-indigo-600:indeterminate{border-color:var(--color-indigo-600)}.indeterminate\:bg-indigo-600:indeterminate{background-color:var(--color-indigo-600)}.focus-within\:outline-2:focus-within{outline-style:var(--tw-outline-style);outline-width:2px}.focus-within\:-outline-offset-2:focus-within{outline-offset:calc(2px*-1)}.focus-within\:outline-indigo-600:focus-within{outline-color:var(--color-indigo-600)}@media (hover:hover){.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.hover\:text-indigo-600:hover{color:var(--color-indigo-600)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-indigo-600:focus-visible{outline-color:var(--color-indigo-600)}.disabled\:border-gray-300:disabled{border-color:var(--color-gray-300)}.disabled\:bg-gray-100:disabled,.disabled\:checked\:bg-gray-100:disabled:checked{background-color:var(--color-gray-100)}@media (min-width:40rem){.sm\:col-span-1{grid-column:span 1/span 1}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\:ml-3{margin-left:calc(var(--spacing)*3)}.sm\:block{display:block}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:gap-4{gap:calc(var(--spacing)*4)}.sm\:rounded-xl{border-radius:var(--radius-xl)}.sm\:p-5{padding:calc(var(--spacing)*5)}.sm\:p-6{padding:calc(var(--spacing)*6)}.sm\:px-6{padding-inline:calc(var(--spacing)*6)}.sm\:text-sm\/6{font-size:var(--text-sm);line-height:calc(var(--spacing)*6)}}@media (min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:w-xl{width:var(--container-xl)}.md\:justify-between{justify-content:space-between}.md\:p-5{padding:calc(var(--spacing)*5)}.md\:px-50{padding-inline:calc(var(--spacing)*50)}}@media (min-width:64rem){.lg\:mt-0{margin-top:calc(var(--spacing)*0)}.lg\:ml-4{margin-left:calc(var(--spacing)*4)}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:gap-x-6{column-gap:calc(var(--spacing)*6)}.lg\:px-8{padding-inline:calc(var(--spacing)*8)}.lg\:px-20{padding-inline:calc(var(--spacing)*20)}.lg\:px-50{padding-inline:calc(var(--spacing)*50)}}@media (min-width:80rem){.xl\:fixed{position:fixed}.xl\:inset-y-0{inset-block:calc(var(--spacing)*0)}.xl\:z-50{z-index:50}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-72{width:calc(var(--spacing)*72)}.xl\:flex-col{flex-direction:column}.xl\:px-30{padding-inline:calc(var(--spacing)*30)}.xl\:px-50{padding-inline:calc(var(--spacing)*50)}.xl\:pl-72{padding-left:calc(var(--spacing)*72)}}@media (min-width:96rem){.\32 xl\:px-100{padding-inline:calc(var(--spacing)*100)}}@media (forced-colors:active){.forced-colors\:appearance-auto{appearance:auto}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{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-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<svg
|
3
|
+
viewBox="0 0 1147.6376 283.26123"
|
4
|
+
version="1.1"
|
5
|
+
id="svg1"
|
6
|
+
sodipodi:docname="logo_new.svg"
|
7
|
+
width="1147.6376"
|
8
|
+
height="283.26123"
|
9
|
+
inkscape:version="1.4.2 (ebf0e940, 2025-05-08)"
|
10
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
11
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
13
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
14
|
+
<defs
|
15
|
+
id="defs1" />
|
16
|
+
<sodipodi:namedview
|
17
|
+
id="namedview1"
|
18
|
+
pagecolor="#ffffff"
|
19
|
+
bordercolor="#000000"
|
20
|
+
borderopacity="0.25"
|
21
|
+
inkscape:showpageshadow="2"
|
22
|
+
inkscape:pageopacity="0.0"
|
23
|
+
inkscape:pagecheckerboard="0"
|
24
|
+
inkscape:deskcolor="#d1d1d1"
|
25
|
+
inkscape:zoom="0.54166667"
|
26
|
+
inkscape:cx="565.84615"
|
27
|
+
inkscape:cy="167.07692"
|
28
|
+
inkscape:window-width="2560"
|
29
|
+
inkscape:window-height="1302"
|
30
|
+
inkscape:window-x="0"
|
31
|
+
inkscape:window-y="25"
|
32
|
+
inkscape:window-maximized="1"
|
33
|
+
inkscape:current-layer="svg1" />
|
34
|
+
<path
|
35
|
+
fill="#4f39f6"
|
36
|
+
d="m 142.05593,2.3655083 c 0.72,0 1.441,-0.003 2.184,-0.004 2.405,-0.004 4.81,0 7.215,0.003 q 2.619,-10e-4 5.238,-0.005 7.185,-0.005 14.37,10e-4 7.751,0.003 15.504,-0.004 15.18,-0.005 30.36,0 12.33,0.003 24.661,0.001 h 10.671 a 186077,186077 0 0 1 66.827,0.004 q 28.695,0.007 57.39,0 33.307,-0.01 66.615,-0.005 l 7.096,0.001 h 3.526 q 12.326,10e-4 24.653,-0.002 15.006,-0.005 30.012,0.003 7.662,0.004 15.325,0 7.004,-0.003 14.007,0.004 2.543,0.002 5.086,-0.002 3.438,-0.003 6.875,0.005 h 3.87 c 2.945,0.126 2.945,0.126 4.945,1.126 -0.666,6.7749997 -2.173,13.1759997 -3.87,19.7619997 -0.284,1.108 -0.566,2.217 -0.857,3.36 q -0.893,3.499 -1.793,6.996 a 3995,3995 0 0 0 -2.732,10.704 q -0.87,3.408 -1.744,6.815 l -0.82,3.226 -0.78,3.015 -0.677,2.638 c -1.97,6.726 -1.97,6.726 -3.727,8.484 a 79,79 0 0 1 -4.828,0.12 l -3.166,0.004 -3.503,-0.01 h -3.665 q -4.99,-0.001 -9.979,-0.013 -5.209,-0.008 -10.418,-0.008 -9.87,-0.005 -19.74,-0.02 -11.235,-0.017 -22.47,-0.023 a 59370,59370 0 0 1 -46.23,-0.05 c -0.133,0.741 -0.265,1.483 -0.4,2.247 l -0.535,2.97 -0.524,2.935 c -0.753,3.954 -1.722,7.834 -2.742,11.727 l -1.224,4.787 q -0.954,3.704 -1.916,7.406002 c -0.626,2.413 -1.243,4.83 -1.86,7.244 -0.195,0.743 -0.39,1.485 -0.593,2.251 -1.156,3.778 -1.156,3.778 -0.207,7.433 0.856,-0.07 1.712,-0.14 2.595,-0.214 10.651,-0.803 21.243,-0.933 31.92,-0.919 q 2.75,0 5.497,-0.003 5.708,0 11.416,0.005 7.32,0.007 14.637,-0.003 5.651,-0.005 11.303,0 2.701,10e-4 5.402,-0.002 3.753,-0.003 7.508,0.006 l 2.248,-0.006 c 3.1,0.013 5.508,0.147 8.474,1.136 -0.605,6.928 -1.94,13.4 -3.688,20.125 l -0.795,3.115 a 2354,2354 0 0 1 -1.665,6.464 q -1.282,4.968 -2.548,9.94 l -1.617,6.293 -0.77,3.015 -0.718,2.763 -0.63,2.441 c -0.569,1.844 -0.569,1.844 -1.569,2.844 -3.166,0.095 -6.31,0.125 -9.477,0.113 l -3.015,0.001 c -3.306,0 -6.612,-0.009 -9.918,-0.017 l -6.857,-0.004 q -9.043,-0.007 -18.085,-0.024 -9.22,-0.014 -18.439,-0.02 -18.105,-0.016 -36.209,-0.049 l -0.557,2.664 c -1.606,7.598 -3.332,15.143 -5.264,22.664 l -0.788,3.095 q -1.255,4.914 -2.516,9.827 l -0.868,3.39 a 14273,14273 0 0 1 -6.007,23.36 l -0.747,2.898 q -1.922,7.464 -3.87,14.92 a 2121,2121 0 0 0 -1.128,4.337 c -0.51,1.971 -1.029,3.94 -1.548,5.907 l -0.86,3.285 c -0.847,2.653 -0.847,2.653 -2.847,5.653 -2.969,0.43 -2.969,0.43 -6.96,0.507 l -2.288,0.054 c -2.635,0.055 -5.269,0.087 -7.905,0.12 q -2.943,0.048 -5.888,0.1 c -33.543,0.515 -67.1,0.368 -100.648,0.347 q -10.332,-0.004 -20.665,0.002 c -74.462,0.044 -74.462,0.044 -104.512,-0.402 q -3.301,-0.048 -6.602,-0.08 a 1116,1116 0 0 1 -8.778,-0.14 l -2.517003,-0.019 c -4.577,-0.117 -7.594,-0.655 -11.237,-3.489 -4.179,-4.759 -5.323,-7.468 -5.203,-13.688 0.305,-3.48 1.143,-5.48 3.203,-8.312 5.471,-4.513 10.118,-5.727 17.086003,-5.6 1.321,-0.008 1.321,-0.008 2.67,-0.014 2.932,-0.009 5.863,0.02 8.796,0.046 q 3.165,0.003 6.33,-0.002 c 5.713,0 11.427,0.028 17.142,0.063 5.976,0.031 11.952,0.034 17.93,0.04 11.311,0.016 22.622,0.057 33.934,0.107 12.88,0.056 25.76,0.083 38.64,0.108 26.491,0.052 52.981,0.14 79.472,0.252 l 0.308,-2.723 c 0.81,-5.666 2.337,-11.176 3.754,-16.715 l 0.854,-3.373 q 1.037,-4.095 2.084,-8.189 l -1.874,0.001 a 335971,335971 0 0 1 -123.187,0.054 l -2.657,10e-4 q -21.28,0.007 -42.562,0.021 -21.84,0.014 -43.684,0.017 -13.473003,0.002 -26.947003,0.014 -9.245,0.008 -18.488,0.005 -5.33,0 -10.66,0.006 -5.79,0.007 -11.577,0.001 l -3.379,0.01 c -19.357,-0.038 -19.357,-0.038 -24.9849995,-5.13 -3.19,-3.928 -4.73600002,-7.763 -4.45300002,-12.945 1.11100002,-5.039 3.95200002,-8.92 8.01500002,-11.993 4.0069995,-1.746 7.3899995,-2.187 11.7559995,-2.185 l 3.285,-0.006 3.61,0.01 3.833,-0.004 q 5.271,-0.002 10.542,0.007 5.682,0.005 11.366,0.002 9.847,0 19.694,0.008 14.238,0.011 28.475003,0.012 23.1,0.004 46.197,0.018 a 111901,111901 0 0 0 61.55,0.026 q 57.564,0.016 115.13,0.05 l 0.345,-2.066 a 173,173 0 0 1 4.28,-18.622 c 0.23,-0.809 0.46,-1.617 0.695,-2.45 q 0.834,-2.933 1.68,-5.862 l -3.414,0.005 q -41.112,0.068 -82.225,0.098 -19.882,0.015 -39.764,0.048 -17.329,0.03 -34.657,0.037 -9.176,0.004 -18.35,0.023 -8.64,0.02 -17.277,0.015 -3.169,0.001 -6.337,0.012 c -2.888,0.01 -5.775,0.008 -8.662,0.002 l -2.524,0.018 c -6.285,-0.032 -12.01,-0.714 -17.603003,-3.82 -3.502,-3.903 -5.315,-8.323 -5.097,-13.626 0.97,-4.878 2.997,-7.644 6.910003,-10.812 2.813,-1.647 4.843,-2.25 8.094,-2.259 l 2.46,-0.02 2.683,0.007 2.864,-0.015 c 3.176,-0.015 6.351,-0.016 9.526,-0.016 l 6.82,-0.026 q 7.347,-0.027 14.695,-0.037 c 7.738,-0.012 15.477,-0.037 23.215,-0.065 q 16.265,-0.054 32.528,-0.092 l 2.085,-0.005 2.088,-0.005 q 14.658,-0.034 29.317,-0.074 l 2.084,-0.006 q 17.183,-0.049 34.37,-0.105 11.55,-0.038 23.1,-0.06 7.17,-0.016 14.34,-0.039 3.335,-0.009 6.672,-0.013 4.537,-0.007 9.075,-0.025 l 2.698,0.001 c 4.278,0.103 4.278,0.103 8.286,-1.146 0.552,-2.039 0.552,-2.039 0.875,-4.563 1.243,-7.602 3.212,-14.98 5.125,-22.437002 -14.638,-1.044 -29.222,-1.124 -43.892,-1.082 l -7.688,0.01 q -8.223,0.009 -16.446,0.027 -11.9,0.024 -23.799,0.034 -19.33,0.018 -38.66,0.045 l -2.329,0.004 a 71734,71734 0 0 0 -35.136,0.058 l -2.336,0.004 q -19.285,0.034 -38.57,0.047 -11.873,0.01 -23.745,0.04 -9.082,0.021 -18.166003,0.021 -3.723,0.003 -7.446,0.017 c -3.386,0.012 -6.772,0.012 -10.158,0.01 l -2.978,0.018 c -6.46,-0.023 -11.774,-0.488 -17.4,-3.94 -3.223,-3.312 -5.12,-6.622 -5.349,-11.333 0.383,-4.882 1.914,-8.178 5.098,-11.98 4.562,-3.308 8.455,-4.126 14.005,-4.123 l 3.075,-0.006 3.372,0.01 c 1.776,-0.003 1.776,-0.003 3.587,-0.004 q 4.929,0 9.858,0.007 5.314,0.005 10.630003,0.002 9.21,0 18.417,0.008 13.314,0.011 26.63,0.012 21.6,0.005 43.202,0.018 a 97847,97847 0 0 0 44.582,0.022 l 12.976,0.004 q 53.834,0.016 107.666,0.05 l 0.345,-2.066 a 173,173 0 0 1 4.28,-18.622 c 0.23,-0.809 0.46,-1.617 0.695,-2.45 q 0.834,-2.933 1.68,-5.862 l -3.617,-0.005 a 52384,52384 0 0 1 -95.492,-0.203 l -2.105,-0.006 q -16.866,-0.044 -33.73,-0.059 -17.31,-0.02 -34.619,-0.082 c -6.48,-0.022 -12.96,-0.038 -19.44,-0.037 q -9.15,0 -18.3,-0.049 -3.358,-0.013 -6.715,-0.005 c -3.059,0.007 -6.116,-0.01 -9.175,-0.035 l -2.676,0.023 c -5.826,-0.08 -10.263,-1.257 -15.13,-4.542 -3.324,-3.489 -4.273,-6.236 -4.5,-10.938 0.238,-5.132 1.29,-8.03 4.5,-12.0619997 6.982,-4.029 14.688,-4.125 22.568,-4.126 m 545.931,-0.284 3.338,0.032 3.592,0.101 3.707,0.062 q 3.88,0.075 7.76,0.176 c 3.956,0.101 7.91,0.165 11.865,0.224 q 3.772,0.078 7.543,0.163 l 3.58,0.057 c 6.97,0.206 12.126,0.594 17.268,5.755 2.346,3.6059997 3.873,7.2709997 5.41,11.2769997 l 1.027,2.621 c 3.966,10.217 7.696,20.523 11.446,30.821 a 3646,3646 0 0 0 9.089,24.621 21495,21495 0 0 1 18.228,49.220002 l 1.069,2.895 1.979,5.36 q 0.881,2.387 1.768,4.772 a 369,369 0 0 1 1.648,4.557 c 1.476,4.214 1.476,4.214 4.183,7.696 0.625,2.625 0.625,2.625 1,5 h 1 l 0.563,-2.28 1.722,-6.986 2.084,-8.45 5.32,-21.576 3.12,-12.638 0.64,-2.598 a 19095,19095 0 0 0 9.023,-36.734002 q 1.842,-7.522 3.688,-15.045 1.765,-7.203 3.522,-14.409 1.097,-4.496 2.203,-8.99 0.51,-2.076 1.013,-4.15 c 1.672,-6.895 3.412,-13.574 6.102,-20.1439997 13.555,-0.934 27.042,-1.123 40.625,-1.063 l 5.969,0.014 q 7.203,0.017 14.406,0.05 c 1.3,4.484 0.61,7.6679997 -0.518,12.1549997 l -0.532,2.153 c -0.592,2.38 -1.198,4.757 -1.805,7.134 l -1.281,5.13 a 4202,4202 0 0 1 -3.524,13.981 c -1.25,4.941 -2.487,9.885 -3.725,14.83 q -3.552,14.156 -7.125,28.308 a 25786,25786 0 0 0 -7.908,31.436002 54705,54705 0 0 1 -9.97,39.652 l -0.752,2.99 -1.492,5.924 a 21653,21653 0 0 0 -11.836,47.178 q -2.351,9.412 -4.695,18.824 c -2.855,11.471 -5.711,22.942 -8.7,34.38 l -0.618,2.38 c -1.398,5.303 -1.398,5.303 -2.52,7.544 -4.162,1.234 -8.388,1.145 -12.694,1.133 l -2.247,0.004 c -2.394,0.003 -4.788,-0.004 -7.183,-0.012 l -2.457,-0.007 c -19.39,-0.062 -19.39,-0.062 -27.855,-0.368 l -2.194,-0.043 c -3.104,-0.153 -5.318,-0.68 -8.08,-2.12 -3.361,-3.799 -4.764,-7.983 -6.476,-12.712 l -2.332,-6.207 -1.217,-3.288 a 763,763 0 0 0 -3.963,-10.329 l -1.353,-3.47 q -1.29,-3.312 -2.585,-6.623 c -2.181,-5.646 -4.105,-11.225 -5.576,-17.1 l -0.787,-1.858 -3,-1 h 2 l -0.983,-2.51 c -8.765,-22.4 -17.366,-44.86 -25.881,-67.357 l -1.214,-3.203 -2.27,-6 -1.016,-2.684 -0.888,-2.35 c -0.732,-1.939 -0.732,-1.939 -1.748,-3.896 -4.835,14.233 -8.267,28.813 -11.804,43.408 -1.395,5.746 -2.804,11.49 -4.21,17.233 l -1.64,6.713 c -3.846,15.732 -7.762,31.447 -11.692,47.158 l -1.578,6.316 q -1.47,5.888 -2.943,11.773 c -0.29,1.158 -0.577,2.315 -0.875,3.508 -1.335,5.325 -2.704,10.626 -4.258,15.891 q -12.074,0.231 -24.147,0.443 -5.607,0.098 -11.212,0.205 -5.408,0.104 -10.815,0.195 -2.067,0.036 -4.132,0.077 c -1.925,0.04 -3.85,0.07 -5.775,0.102 l -3.324,0.06 c -2.595,-0.082 -2.595,-0.082 -4.595,-1.082 0.638,-8.088 2.503,-15.744 4.473,-23.598 l 1.074,-4.342 q 1.448,-5.844 2.908,-11.686 1.538,-6.171 3.066,-12.345 c 2.289,-9.224 4.589,-18.446 6.89,-27.668 q 1.376,-5.514 2.75,-11.029 a 34186,34186 0 0 1 10.887,-43.546 q 4,-15.968 7.99,-31.94 2.253,-9.015 4.513,-18.029002 1.236,-4.924 2.469,-9.848 l 0.493,-1.967 a 4140,4140 0 0 0 6.637,-26.909 q 1.8,-7.39 3.616,-14.776 c 0.82,-3.344 1.636,-6.689 2.438,-10.038 0.883,-3.674 1.783,-7.344 2.687,-11.013 l 0.778,-3.299 0.754,-3.02 0.635,-2.6189997 c 2.13,-5.26 4.903,-6.839 10.442,-6.738 M 1118.1029,22.620508 c 17.137,14.975 26.922,33.11 29.07,55.934 q 0.186,2.967 0.313,5.937 l 0.125,2.68 c 0.07,3.118 -0.01,6.204 -0.125,9.32 l -0.056,1.932 c -1.927,52.148002 -20.677,112.533002 -58.944,149.068002 -21.777,19.825 -48.89,30.898 -78,34 -1.243,0.14 -1.243,0.14 -2.512,0.285 -2.786,0.314 -5.574,0.61 -8.36297,0.903 l -2.668,0.29 c -5.399,0.528 -10.096,0.322 -15.457,-0.478 a 605,605 0 0 0 -7.25,-0.312 c -25.032,-1.305 -50.525,-9.134 -68.137,-27.797 -6.638,-7.781 -14.613,-18.351 -14.613,-28.891 l -2,-1 c -0.486,-3 -0.85,-5.918 -1.125,-8.937 l -0.253,-2.65 c -1.604,-17.56 1.018,-34.257 4.94,-51.35 l 0.522,-2.308 c 11.206,-49.485 11.206,-49.485 19.916,-69.755002 l 1.014,-2.373 c 6.904,-15.895 15.77,-29.46 26.986,-42.627 q 1.283,-1.624 2.562,-3.25 c 39.167,-44.1879997 127.26597,-56.681 174.05497,-18.621 m -128.61697,59.87 -1.352,1.337 c -13.537,14.26 -17.793,35.237002 -22.133,53.750002 a 1414,1414 0 0 1 -1.94,8.135 c -8.14,31.087 -8.14,31.087 -1.114,61.455 5.378,8.12 13.633,12.225 22.851,14.574 13.367,2.294 27.03497,1.694 39.68797,-3.25 l 2.726,-1.062 c 6.857,-3.057 11.99,-7.681 17.274,-12.938 l 1.867,-1.762 c 18.356,-18.821 21.737,-49.357 27.277,-73.824 0.173,-0.76 0.346,-1.517 0.523,-2.3 3.53,-15.704 5.04,-32.853002 -2.917,-47.400002 -5.68,-8.634 -15.02,-12.427 -24.75,-14.714 l -2.875,-0.687 c -21.04,-2.104 -40.516,2.894 -55.12497,18.687 M 587.39393,2.3615083 c 0.999,-0.002 1.998,-0.005 3.027,-0.01 l 3.276,0.008 3.39,-0.003 q 3.565,-0.002 7.128,0.005 c 3.597,0.005 7.194,0 10.79,-0.006 q 3.472,0 6.942,0.004 l 3.215,-0.007 c 7.48,0.023 14.875,0.476 22.324,1.139 1.5,3.937 0.488,6.8859997 -0.574,10.8649997 l -0.54,2.058 a 842,842 0 0 1 -1.173,4.41 1223,1223 0 0 0 -1.873,7.064 q -0.962,3.672 -1.932,7.343 c -2.096,7.938 -4.103,15.897 -6.091,23.862 l -1.04,4.15 -2.661,10.656 -1.705,6.828 c -10.416,41.709002 -20.773,83.434002 -30.887,125.218002 l -0.551,2.275 q -2.606,10.758 -5.196,21.518 c -3.761,15.63 -7.642,31.218 -11.777,46.753 -15.668,0.932 -31.188,0.992 -46.875,0.563 l -6.492,-0.158 q -7.817,-0.191 -15.633,-0.405 c -1.602,-3.205 -0.136,-6.275 0.712,-9.605 l 0.593,-2.4 c 0.659,-2.657 1.328,-5.312 1.996,-7.967 l 1.402,-5.641 q 1.503,-6.05 3.02,-12.1 2.404,-9.601 4.79,-19.208 2.43,-9.765 4.866,-19.53 a 10659,10659 0 0 0 9.819,-39.645 l 2.117,-8.616 c 7.427,-30.237 14.937,-60.453002 22.47,-90.664002 q 1.71,-6.844 3.41,-13.69 2.007,-8.06 4.02,-16.12 0.748,-2.993 1.492,-5.987 1.024,-4.12 2.057,-8.238 l 0.6,-2.432 c 2.028,-8.0469997 2.028,-8.0469997 3.636,-11.1569997 2.942,-0.98 4.843,-1.126 7.908,-1.13"
|
37
|
+
id="path1" />
|
38
|
+
</svg>
|
data/lib/fino/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fino-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Egor Iskrenkov
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - "~>"
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: 1.
|
18
|
+
version: 1.1.1
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 1.
|
25
|
+
version: 1.1.1
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rails
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,15 +44,27 @@ extensions: []
|
|
44
44
|
extra_rdoc_files: []
|
45
45
|
files:
|
46
46
|
- README.md
|
47
|
+
- lib/fino/rails/app/assets/stylesheets/fino.css
|
47
48
|
- lib/fino/rails/app/controllers/fino/rails/application_controller.rb
|
49
|
+
- lib/fino/rails/app/controllers/fino/rails/dashboard_controller.rb
|
50
|
+
- lib/fino/rails/app/controllers/fino/rails/sections_controller.rb
|
48
51
|
- lib/fino/rails/app/controllers/fino/rails/settings_controller.rb
|
52
|
+
- lib/fino/rails/app/helpers/fino/rails/application_helper.rb
|
53
|
+
- lib/fino/rails/app/helpers/fino/rails/settings_helper.rb
|
54
|
+
- lib/fino/rails/app/views/fino/rails/common/_navbar.html.erb
|
55
|
+
- lib/fino/rails/app/views/fino/rails/common/_sidebar.html.erb
|
56
|
+
- lib/fino/rails/app/views/fino/rails/dashboard/index.html.erb
|
57
|
+
- lib/fino/rails/app/views/fino/rails/sections/show.html.erb
|
58
|
+
- lib/fino/rails/app/views/fino/rails/settings/_setting.html.erb
|
49
59
|
- lib/fino/rails/app/views/fino/rails/settings/edit.html.erb
|
50
|
-
- lib/fino/rails/app/views/fino/rails/settings/index.html.erb
|
51
60
|
- lib/fino/rails/app/views/layouts/fino/rails/application.html.erb
|
52
61
|
- lib/fino/rails/config/routes.rb
|
53
62
|
- lib/fino/rails/engine.rb
|
54
63
|
- lib/fino/rails/instrumentation/log_subscriber.rb
|
55
64
|
- lib/fino/rails/instrumentation/pipe.rb
|
65
|
+
- lib/fino/rails/preloading/middleware.rb
|
66
|
+
- lib/fino/rails/public/fino-assets/fino.css
|
67
|
+
- lib/fino/rails/public/fino-assets/logo.svg
|
56
68
|
- lib/fino/rails/request_scoped_cache/middleware.rb
|
57
69
|
- lib/fino/rails/request_scoped_cache/pipe.rb
|
58
70
|
- lib/fino/version.rb
|
@@ -1,155 +0,0 @@
|
|
1
|
-
<% content_for(:title, 'Application Settings') %>
|
2
|
-
<% content_for(:head) do %>
|
3
|
-
<style>
|
4
|
-
.settings-grid {
|
5
|
-
display: grid;
|
6
|
-
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
7
|
-
gap: 1.5rem;
|
8
|
-
}
|
9
|
-
|
10
|
-
.setting-item {
|
11
|
-
display: block;
|
12
|
-
padding: 1rem;
|
13
|
-
border-bottom: 1px solid #e2e8f0;
|
14
|
-
text-decoration: none;
|
15
|
-
color: inherit;
|
16
|
-
transition: background-color 0.15s ease-in-out;
|
17
|
-
}
|
18
|
-
|
19
|
-
.setting-item:hover {
|
20
|
-
background-color: #f8fafc;
|
21
|
-
}
|
22
|
-
|
23
|
-
.setting-item:last-child {
|
24
|
-
border-bottom: none;
|
25
|
-
}
|
26
|
-
|
27
|
-
.setting-name {
|
28
|
-
font-weight: 600;
|
29
|
-
color: #0f172a;
|
30
|
-
font-size: 0.875rem;
|
31
|
-
margin-bottom: 0.25rem;
|
32
|
-
display: flex;
|
33
|
-
align-items: center;
|
34
|
-
gap: 0.5rem;
|
35
|
-
}
|
36
|
-
|
37
|
-
.setting-type {
|
38
|
-
display: inline-block;
|
39
|
-
padding: 0.125rem 0.5rem;
|
40
|
-
background-color: #f1f5f9;
|
41
|
-
color: #475569;
|
42
|
-
font-size: 0.75rem;
|
43
|
-
font-weight: 500;
|
44
|
-
border-radius: 4px;
|
45
|
-
text-transform: uppercase;
|
46
|
-
letter-spacing: 0.025em;
|
47
|
-
}
|
48
|
-
|
49
|
-
.setting-value {
|
50
|
-
color: #0f172a;
|
51
|
-
font-size: 0.875rem;
|
52
|
-
margin-top: 0.5rem;
|
53
|
-
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;
|
54
|
-
background-color: #f8fafc;
|
55
|
-
padding: 0.5rem;
|
56
|
-
border-radius: 4px;
|
57
|
-
border: 1px solid #e2e8f0;
|
58
|
-
font-weight: 500;
|
59
|
-
}
|
60
|
-
|
61
|
-
.setting-default {
|
62
|
-
color: #64748b;
|
63
|
-
font-size: 0.75rem;
|
64
|
-
margin-top: 0.25rem;
|
65
|
-
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;
|
66
|
-
}
|
67
|
-
|
68
|
-
.setting-description {
|
69
|
-
color: #64748b;
|
70
|
-
font-size: 0.875rem;
|
71
|
-
margin-top: 0.25rem;
|
72
|
-
font-style: italic;
|
73
|
-
}
|
74
|
-
|
75
|
-
.empty-state {
|
76
|
-
text-align: center;
|
77
|
-
padding: 3rem 1rem;
|
78
|
-
color: #64748b;
|
79
|
-
}
|
80
|
-
|
81
|
-
.empty-state svg {
|
82
|
-
width: 48px;
|
83
|
-
height: 48px;
|
84
|
-
margin: 0 auto 1rem;
|
85
|
-
color: #cbd5e1;
|
86
|
-
}
|
87
|
-
|
88
|
-
@media (max-width: 640px) {
|
89
|
-
.settings-grid {
|
90
|
-
grid-template-columns: 1fr;
|
91
|
-
}
|
92
|
-
}
|
93
|
-
</style>
|
94
|
-
<% end %>
|
95
|
-
|
96
|
-
<div class="header">
|
97
|
-
<h1>Application Settings</h1>
|
98
|
-
<p>Global configuration settings for your application</p>
|
99
|
-
</div>
|
100
|
-
|
101
|
-
<%
|
102
|
-
# Group settings by section
|
103
|
-
grouped_settings = @settings.group_by { |setting| setting.section_name || 'Global' }
|
104
|
-
|
105
|
-
if grouped_settings.empty?
|
106
|
-
%>
|
107
|
-
<div class="empty-state">
|
108
|
-
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
109
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
|
110
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
111
|
-
</svg>
|
112
|
-
<h3>No settings configured</h3>
|
113
|
-
<p>Configure settings in your application to see them here.</p>
|
114
|
-
</div>
|
115
|
-
<% else %>
|
116
|
-
<div class="settings-grid">
|
117
|
-
<%
|
118
|
-
# Show Global settings first, then other sections
|
119
|
-
sections_order = grouped_settings.keys.sort_by { |section| section == 'Global' ? 0 : 1 }
|
120
|
-
sections_order.each do |section_name|
|
121
|
-
settings = grouped_settings[section_name]
|
122
|
-
is_global = section_name == 'Global'
|
123
|
-
%>
|
124
|
-
<div class="card">
|
125
|
-
<div class="card-header">
|
126
|
-
<h2 class="card-title">
|
127
|
-
<%= is_global ? 'Global Settings' : section_name.to_s.humanize %>
|
128
|
-
</h2>
|
129
|
-
</div>
|
130
|
-
<div class="card-content" style="padding: 0;">
|
131
|
-
<% settings.each do |setting| %>
|
132
|
-
<%= link_to edit_setting_path(setting.key), class: "setting-item" do %>
|
133
|
-
<div class="setting-name">
|
134
|
-
<%= setting.name %>
|
135
|
-
<span class="setting-type"><%= setting.class.name.demodulize %></span>
|
136
|
-
</div>
|
137
|
-
|
138
|
-
<% if setting.definition.options[:description].present? %>
|
139
|
-
<div class="setting-description"><%= setting.definition.options[:description] %></div>
|
140
|
-
<% end %>
|
141
|
-
|
142
|
-
<div class="setting-value">
|
143
|
-
<%= setting.value.inspect %>
|
144
|
-
</div>
|
145
|
-
|
146
|
-
<div class="setting-default">
|
147
|
-
Default: <%= setting.default.inspect %>
|
148
|
-
</div>
|
149
|
-
<% end %>
|
150
|
-
<% end %>
|
151
|
-
</div>
|
152
|
-
</div>
|
153
|
-
<% end %>
|
154
|
-
</div>
|
155
|
-
<% end %>
|