system_settings 0.1.0.pre → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +108 -9
- data/app/controllers/system_settings/root_controller.rb +1 -1
- data/app/models/system_settings/configurator.rb +18 -12
- data/app/models/system_settings/errors/settings_read_error.rb +6 -0
- data/app/models/system_settings/integer_list_setting.rb +1 -1
- data/app/models/system_settings/list_of_integers_validator.rb +6 -13
- data/app/models/system_settings/list_of_strings_validator.rb +36 -0
- data/app/models/system_settings/setting.rb +1 -1
- data/app/models/system_settings/string_list_setting.rb +2 -1
- data/app/models/system_settings/string_setting.rb +1 -0
- data/app/models/system_settings/type/integer_list.rb +10 -4
- data/app/models/system_settings/type/string_list.rb +2 -1
- data/config/locales/system_settings.en.yml +2 -1
- data/db/migrate/20181007125347_create_system_settings_settings.rb +1 -1
- data/frontend/build/asset-manifest.json +4 -4
- data/frontend/build/index.html +1 -1
- data/frontend/build/precache-manifest.2450e38293bed26dfe321bf03ff5acbb.js +22 -0
- data/frontend/build/service-worker.js +1 -1
- data/frontend/build/static/css/main.493112c7.chunk.css +1 -0
- data/frontend/build/static/js/2.17170219.chunk.js +1 -0
- data/frontend/build/static/js/main.dbe11b19.chunk.js +1 -0
- data/lib/system_settings/engine.rb +5 -0
- data/lib/system_settings/version.rb +1 -1
- data/lib/system_settings.rb +16 -0
- data/lib/tasks/system_settings_tasks.rake +18 -4
- metadata +17 -55
- data/frontend/build/precache-manifest.130ca067521371497dba783c23aa7e16.js +0 -22
- data/frontend/build/static/css/main.e262560e.chunk.css +0 -1
- data/frontend/build/static/js/2.83a5f4da.chunk.js +0 -1
- data/frontend/build/static/js/main.afaa2372.chunk.js +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b0d4b4f84f7a19cd6befcc89649419e39607bb9c0e48a6b201d9c4eb8f07ba0
|
4
|
+
data.tar.gz: abc9fae1214a54c4fd6f5baeaec24a3879efa4a895c413a31b7966040a7ca274
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f83ef8531ffbaf9f01c407baa8afd93848d9e1a4ff7671e0c656298f76d1e2d71449b30fa78847f8333efdf791cfb55e93e9d7b96eafafb9e27bd3e4e5fe8264
|
7
|
+
data.tar.gz: 7a9c5493d48d403de25deac639c1055cc36cbd3a6129457bdccd99b220883ac808492b3eb39f5c3029fb04e1b687307ff9a7c0a8b1bbb12bb06fd74731452db8
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# System Settings
|
2
|
+
System Settings is a Rails engine that adds settings functionality.
|
3
3
|
|
4
|
-
|
5
|
-
How to use my plugin.
|
4
|
+
Initial setting values can be loaded from file and later edited in a System Settings provided admin panel.
|
6
5
|
|
7
|
-
|
6
|
+
|
7
|
+
## Getting started
|
8
8
|
Add this line to your application's Gemfile:
|
9
9
|
|
10
10
|
```ruby
|
@@ -16,13 +16,112 @@ And then execute:
|
|
16
16
|
$ bundle
|
17
17
|
```
|
18
18
|
|
19
|
-
|
19
|
+
Copy migrations:
|
20
|
+
```bash
|
21
|
+
$ bin/rails system_settings:install:migrations
|
22
|
+
```
|
23
|
+
|
24
|
+
And then run the migrations:
|
20
25
|
```bash
|
21
|
-
$
|
26
|
+
$ bin/rails db:migrate
|
27
|
+
```
|
28
|
+
|
29
|
+
Create settings file where all settings will be defined:
|
30
|
+
```bash
|
31
|
+
$ touch config/system_settings.rb
|
32
|
+
```
|
33
|
+
|
34
|
+
Add your first setting to `config/system_settings.rb`:
|
35
|
+
```ruby
|
36
|
+
# String type values
|
37
|
+
string :default_mail_from, value: "Example Company <noreply@example.com>", description: "This email will be used for all outgoing emails"
|
38
|
+
string :date_format, value: "%Y-%m-%d"
|
39
|
+
string :default_locale, value: "en"
|
40
|
+
|
41
|
+
# Integer type values
|
42
|
+
integer :default_records_per_page, value: 25
|
43
|
+
integer :remainder_interval_in_hours, value: 48
|
44
|
+
|
45
|
+
# Array type strings and integers
|
46
|
+
string_list :admin_emails, description: "Will receive alerts"
|
47
|
+
integer_list :lucky_numbers, description: "Prime numbers are more effective", value: [2, 3, 5, 11]
|
48
|
+
```
|
49
|
+
|
50
|
+
Load values from `config/system_settings.rb` into database:
|
51
|
+
```bash
|
52
|
+
$ ./bin/rails system_settings:load
|
53
|
+
```
|
54
|
+
|
55
|
+
Add System Settings admin panel to Rails routes:
|
56
|
+
```ruby
|
57
|
+
Rails.application.routes.draw do
|
58
|
+
mount SystemSettings::Engine, at: "/system_settings"
|
59
|
+
# rest of your routes..
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
Final step. Access settings values anywhere in your code:
|
64
|
+
```ruby
|
65
|
+
SystemSettings[:date_format] # => "%Y-%m-%d"
|
66
|
+
SystemSettings[:lucky_numbers] # => [2, 3, 5, 11]
|
67
|
+
|
68
|
+
# You can change setting's value like any other Rails model.
|
69
|
+
SystemSettings::Setting.find_by(name: "default_mail_from").update({value: "No-Reply <noreply@example.com>"})
|
70
|
+
```
|
71
|
+
|
72
|
+
|
73
|
+
## Do not forget!
|
74
|
+
Before using System settings in production please protect the `/system_settings` endpoint with routing constraint. You can read more about it in [Rails Guides: Rails Routing from the Outside In](https://guides.rubyonrails.org/routing.html#advanced-constraints)
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
Rails.application.routes.draw do
|
78
|
+
mount SystemSettings::Engine, at: "/system_settings", constraints: AdminRoutingConstraint.new
|
79
|
+
# rest of your routes..
|
80
|
+
end
|
22
81
|
```
|
23
82
|
|
24
|
-
|
25
|
-
|
83
|
+
|
84
|
+
## Few more things
|
85
|
+
|
86
|
+
When you run `./bin/rails system_settings:load` task it will read `config/system_settings.rb` file and add new entries to the database. If you would like to replace all values with the ones from the file then run `./bin/rails system_settings:reset`
|
87
|
+
|
88
|
+
System Settings admin panel is precompiled at gem's build time. So it does not require any Javascript runtime and can be used with api-only Rails applications.
|
89
|
+
|
90
|
+
If you would like to store your settings somewhere else than `config/system_settings.rb` you can use ENV variable `SYSTEM_SETTINGS_PATH` to specify custom path.
|
91
|
+
|
92
|
+
## Development
|
93
|
+
|
94
|
+
Required development dependencies:
|
95
|
+
* [Node.js](https://nodejs.org/) - JavaScript runtime
|
96
|
+
* [Yarn](https://yarnpkg.com/) - package manager
|
97
|
+
|
98
|
+
Optional development tools:
|
99
|
+
* [overmind](https://github.com/DarthSim/overmind) - Process manager for Procfile-based applications and tmux
|
100
|
+
* [direnv](https://direnv.net/) - Unclutter your .profile
|
101
|
+
|
102
|
+
Required environment variables:
|
103
|
+
* `RAILS_VERSION`
|
104
|
+
* `SQLITE3_VERSION`
|
105
|
+
|
106
|
+
As System Settings gem is being developed to be compatible with multiple [Rails](https://github.com/rails/rails) versions,
|
107
|
+
you need to set `RAILS_VERSION` and `SQLITE3_VERSION` environment variables when running `bundle install` or any `./bin/rails` command.
|
108
|
+
It is recommended to set these up using [direnv](https://direnv.net/) and `.envrc` file.
|
109
|
+
|
110
|
+
|
111
|
+
Getting started with development:
|
112
|
+
1) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 bundle`
|
113
|
+
2) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails db:create db:migrate`
|
114
|
+
3) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails test`
|
115
|
+
4) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails frontend:install`
|
116
|
+
4) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails app:system_settings:load`
|
117
|
+
4) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 overmind start`
|
118
|
+
|
119
|
+
|
120
|
+
## Build status
|
121
|
+
System Settings is being tested with Rails versions - `5.0`, `5.1`, `5.2`, `6.0`, `rails repo master branch`
|
122
|
+
|
123
|
+
[![Build Status](https://dev.azure.com/kristsozols/System%20Settings/_apis/build/status/krists.system_settings?branchName=master)](https://dev.azure.com/kristsozols/System%20Settings/_build/latest?definitionId=1&branchName=master)
|
124
|
+
|
26
125
|
|
27
126
|
## License
|
28
127
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -3,7 +3,7 @@ require_relative "./application_controller"
|
|
3
3
|
module SystemSettings
|
4
4
|
class RootController < ApplicationController
|
5
5
|
def index
|
6
|
-
if File.
|
6
|
+
if File.exist?(SystemSettings::Engine.frontend_build_index_html_path)
|
7
7
|
render file: SystemSettings::Engine.frontend_build_index_html_path
|
8
8
|
else
|
9
9
|
render plain: "Frontend application has not been compiled", status: :not_implemented
|
@@ -1,25 +1,30 @@
|
|
1
1
|
module SystemSettings
|
2
2
|
class Configurator
|
3
|
-
|
4
3
|
class << self
|
5
4
|
def from_file(path)
|
6
|
-
|
5
|
+
path_str = path.to_s
|
6
|
+
raise SystemSettings::Errors::SettingsReadError, "#{path_str} file does not exist" unless File.exist?(path_str)
|
7
|
+
raise SystemSettings::Errors::SettingsReadError, "#{path_str} file not readable" unless File.readable?(path_str)
|
8
|
+
file_content = File.read(path_str)
|
7
9
|
new.tap do |obj|
|
8
|
-
obj.instance_eval(file_content,
|
10
|
+
obj.instance_eval(file_content, path_str, 1)
|
9
11
|
end
|
10
12
|
end
|
13
|
+
|
14
|
+
def purge
|
15
|
+
new.purge
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
attr_reader :items
|
14
20
|
|
15
21
|
def initialize(&block)
|
16
22
|
@items = []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
+
return unless block_given?
|
24
|
+
if block.arity == 1
|
25
|
+
yield self
|
26
|
+
else
|
27
|
+
instance_exec(&block)
|
23
28
|
end
|
24
29
|
end
|
25
30
|
|
@@ -28,7 +33,7 @@ module SystemSettings
|
|
28
33
|
end
|
29
34
|
|
30
35
|
def string_list(name, value: nil, description: nil, &blk)
|
31
|
-
add(name, SystemSettings::StringListSetting, value: value, description: description, &blk)
|
36
|
+
add(name, SystemSettings::StringListSetting, value: value || [], description: description, &blk)
|
32
37
|
end
|
33
38
|
|
34
39
|
def integer(name, value: nil, description: nil, &blk)
|
@@ -36,7 +41,7 @@ module SystemSettings
|
|
36
41
|
end
|
37
42
|
|
38
43
|
def integer_list(name, value: nil, description: nil, &blk)
|
39
|
-
add(name, SystemSettings::IntegerListSetting, value: value, description: description, &blk)
|
44
|
+
add(name, SystemSettings::IntegerListSetting, value: value || [], description: description, &blk)
|
40
45
|
end
|
41
46
|
|
42
47
|
def persist
|
@@ -51,8 +56,9 @@ module SystemSettings
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|
59
|
+
true
|
54
60
|
else
|
55
|
-
|
61
|
+
warn "SystemSettings: Settings table has not been created!"
|
56
62
|
false
|
57
63
|
end
|
58
64
|
end
|
@@ -3,6 +3,6 @@ require_relative "./setting"
|
|
3
3
|
module SystemSettings
|
4
4
|
class IntegerListSetting < Setting
|
5
5
|
attribute :value, SystemSettings::Type::IntegerList.new
|
6
|
-
validates :value, list_of_integers:
|
6
|
+
validates :value, "system_settings/list_of_integers": true
|
7
7
|
end
|
8
8
|
end
|
@@ -1,31 +1,24 @@
|
|
1
1
|
module SystemSettings
|
2
2
|
class ListOfIntegersValidator < ActiveModel::EachValidator
|
3
|
-
LIST_REGEXP = /\A[+-]?\d+(?:; *[+-]?\d+)*\z
|
4
|
-
SINGLE_REGEXP = /\A[+-]?\d+\z
|
3
|
+
LIST_REGEXP = /\A[+-]?\d+(?:; *[+-]?\d+)*\z/.freeze
|
4
|
+
SINGLE_REGEXP = /\A[+-]?\d+\z/.freeze
|
5
5
|
|
6
6
|
def validate_each(record, attr_name, value)
|
7
7
|
came_from_user = :"#{attr_name}_came_from_user?"
|
8
8
|
|
9
|
-
if record.respond_to?(came_from_user) && record.public_send(came_from_user)
|
10
|
-
raw_value = record.read_attribute_before_type_cast(attr_name)
|
11
|
-
end
|
9
|
+
raw_value = record.read_attribute_before_type_cast(attr_name) if record.respond_to?(came_from_user) && record.public_send(came_from_user)
|
12
10
|
raw_value ||= value
|
13
11
|
|
14
|
-
if record_attribute_changed_in_place?(record, attr_name)
|
15
|
-
raw_value = value
|
16
|
-
end
|
12
|
+
raw_value = value if record_attribute_changed_in_place?(record, attr_name)
|
17
13
|
|
18
|
-
unless matches_list_of_integers_regexp?(raw_value)
|
19
|
-
record.errors.add(attr_name, :not_a_list_of_integers)
|
20
|
-
return
|
21
|
-
end
|
14
|
+
record.errors.add(attr_name, :not_a_list_of_integers) unless matches_list_of_integers_regexp?(raw_value)
|
22
15
|
end
|
23
16
|
|
24
17
|
private
|
25
18
|
|
26
19
|
def record_attribute_changed_in_place?(record, attr_name)
|
27
20
|
record.respond_to?(:attribute_changed_in_place?) &&
|
28
|
-
|
21
|
+
record.attribute_changed_in_place?(attr_name.to_s)
|
29
22
|
end
|
30
23
|
|
31
24
|
def matches_list_of_integers_regexp?(raw_value)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SystemSettings
|
2
|
+
class ListOfStringsValidator < ActiveModel::EachValidator
|
3
|
+
NON_WHITESPACE_REGEXP = /[^[:space:]]/.freeze
|
4
|
+
|
5
|
+
def validate_each(record, attr_name, value)
|
6
|
+
came_from_user = :"#{attr_name}_came_from_user?"
|
7
|
+
|
8
|
+
raw_value = record.read_attribute_before_type_cast(attr_name) if record.respond_to?(came_from_user) && record.public_send(came_from_user)
|
9
|
+
raw_value ||= value
|
10
|
+
|
11
|
+
raw_value = value if record_attribute_changed_in_place?(record, attr_name)
|
12
|
+
|
13
|
+
record.errors.add(attr_name, :not_a_list_of_strings) unless matches_list_of_strings_regexp?(raw_value)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def record_attribute_changed_in_place?(record, attr_name)
|
19
|
+
record.respond_to?(:attribute_changed_in_place?) &&
|
20
|
+
record.attribute_changed_in_place?(attr_name.to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
def matches_list_of_strings_regexp?(raw_value)
|
24
|
+
case raw_value
|
25
|
+
when String
|
26
|
+
raw_value.split(SystemSettings::Type::StringList::DELIMITER_REGEXP).all? do |value|
|
27
|
+
NON_WHITESPACE_REGEXP.match?(value)
|
28
|
+
end
|
29
|
+
when Array
|
30
|
+
raw_value.all? { |v| NON_WHITESPACE_REGEXP.match?(v.to_s) }
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -20,11 +20,17 @@ module SystemSettings
|
|
20
20
|
def cast_value(value)
|
21
21
|
case value
|
22
22
|
when Array
|
23
|
-
value.map
|
23
|
+
value.map do |v|
|
24
|
+
v.to_i
|
25
|
+
rescue StandardError
|
26
|
+
nil
|
27
|
+
end
|
24
28
|
when String
|
25
|
-
value.split(SEPARATOR).map
|
26
|
-
|
27
|
-
|
29
|
+
value.split(SEPARATOR).map do |v|
|
30
|
+
v.to_i
|
31
|
+
rescue StandardError
|
32
|
+
nil
|
33
|
+
end
|
28
34
|
end
|
29
35
|
end
|
30
36
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module SystemSettings
|
2
2
|
module Type
|
3
3
|
class StringList < ActiveModel::Type::Value
|
4
|
+
DELIMITER_REGEXP = /(?<=[^\\]);/.freeze
|
4
5
|
def type
|
5
6
|
:string_list
|
6
7
|
end
|
@@ -20,7 +21,7 @@ module SystemSettings
|
|
20
21
|
when Array
|
21
22
|
value.map { |v| String(v).strip }
|
22
23
|
when String
|
23
|
-
value.split(
|
24
|
+
value.split(DELIMITER_REGEXP).map(&:strip).map { |str| str.gsub("\\;", ";") }
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"files": {
|
3
|
-
"main.css": "/system_settings/static/css/main.
|
4
|
-
"main.js": "/system_settings/static/js/main.
|
3
|
+
"main.css": "/system_settings/static/css/main.493112c7.chunk.css",
|
4
|
+
"main.js": "/system_settings/static/js/main.dbe11b19.chunk.js",
|
5
5
|
"runtime~main.js": "/system_settings/static/js/runtime~main.a09e9b82.js",
|
6
|
-
"static/js/2.
|
6
|
+
"static/js/2.17170219.chunk.js": "/system_settings/static/js/2.17170219.chunk.js",
|
7
7
|
"index.html": "/system_settings/index.html",
|
8
|
-
"precache-manifest.
|
8
|
+
"precache-manifest.2450e38293bed26dfe321bf03ff5acbb.js": "/system_settings/precache-manifest.2450e38293bed26dfe321bf03ff5acbb.js",
|
9
9
|
"service-worker.js": "/system_settings/service-worker.js"
|
10
10
|
}
|
11
11
|
}
|
data/frontend/build/index.html
CHANGED
@@ -1 +1 @@
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="shortcut icon" href="/system_settings/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><title>System Settings</title><link href="/system_settings/static/css/main.
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="shortcut icon" href="/system_settings/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><title>System Settings</title><link href="/system_settings/static/css/main.493112c7.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="/system_settings/static/js/runtime~main.a09e9b82.js"></script><script src="/system_settings/static/js/2.17170219.chunk.js"></script><script src="/system_settings/static/js/main.dbe11b19.chunk.js"></script></body></html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
2
|
+
{
|
3
|
+
"revision": "0acf133c6751acd69af511a671c2fd1e",
|
4
|
+
"url": "/system_settings/index.html"
|
5
|
+
},
|
6
|
+
{
|
7
|
+
"revision": "e07baceb8e059f51d274",
|
8
|
+
"url": "/system_settings/static/css/main.493112c7.chunk.css"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"revision": "f336fd31562a5938e83a",
|
12
|
+
"url": "/system_settings/static/js/2.17170219.chunk.js"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"revision": "e07baceb8e059f51d274",
|
16
|
+
"url": "/system_settings/static/js/main.dbe11b19.chunk.js"
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"revision": "1b52e032205e59a9e84f",
|
20
|
+
"url": "/system_settings/static/js/runtime~main.a09e9b82.js"
|
21
|
+
}
|
22
|
+
]);
|
@@ -14,7 +14,7 @@
|
|
14
14
|
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.0/workbox-sw.js");
|
15
15
|
|
16
16
|
importScripts(
|
17
|
-
"/system_settings/precache-manifest.
|
17
|
+
"/system_settings/precache-manifest.2450e38293bed26dfe321bf03ff5acbb.js"
|
18
18
|
);
|
19
19
|
|
20
20
|
self.addEventListener('message', (event) => {
|
@@ -0,0 +1 @@
|
|
1
|
+
.App_container__BLGGc{min-height:100vh;display:flex;flex-direction:column;position:relative;z-index:auto}.App_header-wrap__3DnwX{position:fixed;width:100%;background:#fff;border-bottom:1px solid #e1e1e1;z-index:1}.App_header__1o2Y8{max-width:1020px;margin:0 auto;display:flex}.App_header-name__2XXxc{font-size:1.5rem;line-height:2rem;font-weight:700;padding:.5rem 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none}.App_header-name__2XXxc:active,.App_header-name__2XXxc:focus,.App_header-name__2XXxc:hover{text-decoration:none}.App_header-spacer__3F0KU{margin-top:calc(3rem + 1px);height:3rem}.App_content-wrap__1oPWs{flex-grow:5;box-sizing:border-box;display:flex}.App_content__3dh9P{max-width:1020px;min-width:0;margin:0 auto;display:flex;flex-grow:1;flex-direction:column}.NotFoundPage_container__35vs4{flex-grow:1;align-items:center;justify-content:center;display:flex}.ListPage_table__2qY_8{table-layout:fixed;white-space:nowrap;width:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ListPage_table__2qY_8 td:empty:after{content:"-"}.ListPage_empty-tr__2VxYl{color:#a6a6a6}.ListPage_empty-tr__2VxYl td{text-align:center}.ListPage_name__1-Gs-{width:20%;overflow:hidden;text-overflow:ellipsis}.ListPage_table__2qY_8 tbody .ListPage_description__1Sci0,.ListPage_table__2qY_8 tbody .ListPage_name__1-Gs-,.ListPage_table__2qY_8 tbody .ListPage_value__3X3Vq{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ListPage_description__1Sci0{width:50%}.ListPage_description__1Sci0,.ListPage_value__3X3Vq{overflow:hidden;text-overflow:ellipsis}.ListPage_actions__2iqTu{width:50px;text-align:right}.PageLoadError_wrap__mpF75{display:flex;align-items:flex-start;justify-content:center}.PageLoadError_container__kcD9o{display:flex;flex-direction:column;background:#ffe5e5;padding:.25rem .5rem;border-radius:4px;color:#b94343;text-align:center}.PageLoadError_error-small__14Fex{font-size:.85em}#nprogress{pointer-events:none}#nprogress .bar{background:#ffc25b;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #ffc25b,0 0 5px #ffc25b;opacity:1;-webkit-transform:rotate(3deg) translateY(-4px);transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:.9rem;right:.9rem}#nprogress .spinner-icon{width:1.2rem;height:1.2rem;box-sizing:border-box;border-color:#ffc25b transparent transparent #ffc25b;border-style:solid;border-width:2px;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.Value_wrap__3J_8z{white-space:nowrap}.Value_section__cgQmL{max-width:100%;margin-right:.25em}.Value_section__cgQmL:last-child{margin-right:0}.Attribute_wrap__1Zy9J{display:flex;flex-direction:column;margin-bottom:1rem}.Attribute_name__3a-cD{font-size:.8em;color:#737373;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.Attribute_value__IxZCj:empty:after{content:"-"}.ButtonBar_container__21aEr{border-top:1px dotted #f2f2f2;padding-top:1rem;margin-bottom:1rem}.ButtonBar_container__21aEr>*{margin-right:.5rem}.LabeledInput_wrap__2KAyr{position:relative;margin-bottom:1em}.LabeledInput_label__1AyjZ{font-size:.8em;color:#737373;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:block}.LabeledInput_input-and-error-wrap__2iJKd{display:flex;align-items:baseline}.LabeledInput_error__2QBpW{flex-grow:0;display:block;color:#ff3656;margin-left:1rem;white-space:nowrap}.LabeledInput_hint__rAeG8{font-size:.8em;display:block}.SettingForm_error-wrap__3SdnI{display:flex;padding:.5rem 0}.SettingForm_error-message__uQK1Y{background:#ffe5e5;padding:.25rem .5rem;border-radius:4px;color:#b94343}.SettingForm_error-small__3owRp{font-size:.85em}.ClassicSpinner_wrap__1LF2_{display:block}html{box-sizing:border-box;overflow-y:scroll;font-size:14px}*,:after,:before{box-sizing:inherit}:root,body{background:#fff}body{color:#233656;font-family:Arial,Helvetica,sans-serif;font-weight:400;font-size:1rem;line-height:1.5rem;margin:0;padding:0;min-height:100vh;position:relative;display:flex;flex-direction:column}table{border-collapse:collapse;border-spacing:0}table td,table th{text-align:left}a{color:#233656;text-decoration:none}a:hover{text-decoration:underline}a.button,button,input[type=button],input[type=submit]{display:inline-block;cursor:pointer;border:none;border-radius:3px;padding:.375rem .75rem;font-size:1rem;line-height:1.5;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}button,input[type=button],input[type=submit]{color:#4d4d4d;background-color:#d9d9d9;-webkit-transition:background-color .1s ease-in-out;transition:background-color .1s ease-in-out;outline:none}button:focus,button:hover,input[type=button]:focus,input[type=button]:hover,input[type=submit]:focus,input[type=submit]:hover{background-color:#b8b8b8}button:active,input[type=button]:active,input[type=submit]:active{background-color:#828282}a.button{color:#4d4d4d;background-color:initial;-webkit-transition:background-color .1s ease-in-out;transition:background-color .1s ease-in-out;outline:none;text-decoration:none}a.button:focus,a.button:hover{background-color:rgba(0,0,0,.15)}a.button:active{background-color:rgba(0,0,0,.4)}a.button.primary,button.primary,input[type=button].primary,input[type=submit].primary{color:#4d4d4d;background-color:#a6a6a6;-webkit-transition:background-color .1s ease-in-out;transition:background-color .1s ease-in-out;outline:none}a.button.primary:focus,a.button.primary:hover,button.primary:focus,button.primary:hover,input[type=button].primary:focus,input[type=button].primary:hover,input[type=submit].primary:focus,input[type=submit].primary:hover{background-color:#8d8d8d}a.button.primary:active,button.primary:active,input[type=button].primary:active,input[type=submit].primary:active{background-color:#646464}input[type=text]{border:none;border-radius:3px;padding:.5em;font-size:1rem;line-height:normal;color:#4d4d4d;background-color:#e6e6e6;-webkit-transition:background-color .1s ease-in-out;transition:background-color .1s ease-in-out;outline:none;box-sizing:initial;width:auto;min-width:12ch;max-width:calc(100% - 2em);will-change:width}input[type=text]:focus{background-color:#c4c4c4}input[type=text]:active{background-color:#bfbfbf}.button-wrap{display:flex;align-items:center}.svg-wrap{display:block}code{border-radius:2px;padding:0 .2rem}.sysname,code{background:#f2f2f2}.sysname{display:inline-block;border-radius:3px;padding:0 .5em;font-family:Courier New,Courier,monospace;font-size:.75em;max-width:100%}#root{z-index:1;position:relative}
|