system_settings 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/app/controllers/system_settings/application_controller.rb +11 -0
  5. data/app/controllers/system_settings/root_controller.rb +13 -0
  6. data/app/controllers/system_settings/settings_controller.rb +27 -0
  7. data/app/models/system_settings/application_record.rb +5 -0
  8. data/app/models/system_settings/configurator.rb +81 -0
  9. data/app/models/system_settings/integer_list_setting.rb +8 -0
  10. data/app/models/system_settings/integer_setting.rb +8 -0
  11. data/app/models/system_settings/list_of_integers_validator.rb +42 -0
  12. data/app/models/system_settings/setting.rb +8 -0
  13. data/app/models/system_settings/string_list_setting.rb +7 -0
  14. data/app/models/system_settings/string_setting.rb +7 -0
  15. data/app/models/system_settings/type/integer_list.rb +32 -0
  16. data/app/models/system_settings/type/string_list.rb +28 -0
  17. data/config/locales/system_settings.en.yml +32 -0
  18. data/config/routes.rb +7 -0
  19. data/db/migrate/20181007125347_create_system_settings_settings.rb +11 -0
  20. data/frontend/build/asset-manifest.json +11 -0
  21. data/frontend/build/favicon.ico +0 -0
  22. data/frontend/build/index.html +1 -0
  23. data/frontend/build/precache-manifest.130ca067521371497dba783c23aa7e16.js +22 -0
  24. data/frontend/build/service-worker.js +39 -0
  25. data/frontend/build/static/css/main.e262560e.chunk.css +1 -0
  26. data/frontend/build/static/js/2.83a5f4da.chunk.js +1 -0
  27. data/frontend/build/static/js/main.afaa2372.chunk.js +1 -0
  28. data/frontend/build/static/js/runtime~main.a09e9b82.js +1 -0
  29. data/lib/system_settings/engine.rb +17 -0
  30. data/lib/system_settings/version.rb +3 -0
  31. data/lib/system_settings.rb +7 -0
  32. data/lib/tasks/system_settings_tasks.rake +4 -0
  33. metadata +130 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 856385bd0d4333e0d4044a3afaca3dfad12db9cd3ade70940ab588cc8162ac0e
4
+ data.tar.gz: 2ccd7b81d43f94bc33e473eef401ae2cdaa0b6ae212e64e8b0d4826717be16c1
5
+ SHA512:
6
+ metadata.gz: 31b69d9e2d3663a773cc6539211a2a53644f33a2aec0a40c1c8da1791a9dd81941dfb38d3e0615a4fcd0090e5b45b080f5ed2891303fc05fb8bae7ed0b804fd4
7
+ data.tar.gz: 415a9d5a5968a4ed30be94b6ba4812d803be1c5a2d5ebbe15fbb8d716beacd9caa378669e471b3284ca31bcb42ab7ac90226fd3cd4ad8a639ce4f5dcf2c2f9f0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Krists Ozols
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # SystemSettings
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'system_settings'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install system_settings
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,11 @@
1
+ module SystemSettings
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+
5
+ private
6
+
7
+ def add_authenticity_token
8
+ response.headers["Authenticity-Token"] = form_authenticity_token if protect_against_forgery?
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "./application_controller"
2
+
3
+ module SystemSettings
4
+ class RootController < ApplicationController
5
+ def index
6
+ if File.exists?(SystemSettings::Engine.frontend_build_index_html_path)
7
+ render file: SystemSettings::Engine.frontend_build_index_html_path
8
+ else
9
+ render plain: "Frontend application has not been compiled", status: :not_implemented
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "./application_controller"
2
+
3
+ module SystemSettings
4
+ class SettingsController < ApplicationController
5
+ RETURN_ATTRIBUTES = ["id", "name", "type", "value", "description"].freeze
6
+
7
+ def index
8
+ @settings = SystemSettings::Setting.order(:name)
9
+ render json: @settings.map { |s| s.as_json(only: RETURN_ATTRIBUTES) }
10
+ end
11
+
12
+ def show
13
+ @setting = SystemSettings::Setting.find(params[:id])
14
+ add_authenticity_token
15
+ render json: @setting.as_json(only: RETURN_ATTRIBUTES)
16
+ end
17
+
18
+ def update
19
+ @setting = SystemSettings::Setting.find(params[:id])
20
+ if @setting.update(value: params[:value])
21
+ render json: @setting.as_json(only: RETURN_ATTRIBUTES)
22
+ else
23
+ render json: {errors: @setting.errors.as_json}, status: :unprocessable_entity
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ module SystemSettings
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,81 @@
1
+ module SystemSettings
2
+ class Configurator
3
+
4
+ class << self
5
+ def from_file(path)
6
+ file_content = File.read(path)
7
+ new.tap do |obj|
8
+ obj.instance_eval(file_content, path, 1)
9
+ end
10
+ end
11
+ end
12
+
13
+ attr_reader :items
14
+
15
+ def initialize(&block)
16
+ @items = []
17
+ if block_given?
18
+ if block.arity == 1
19
+ yield self
20
+ else
21
+ instance_exec(&block)
22
+ end
23
+ end
24
+ end
25
+
26
+ def string(name, value: nil, description: nil, &blk)
27
+ add(name, SystemSettings::StringSetting, value: value, description: description, &blk)
28
+ end
29
+
30
+ def string_list(name, value: nil, description: nil, &blk)
31
+ add(name, SystemSettings::StringListSetting, value: value, description: description, &blk)
32
+ end
33
+
34
+ def integer(name, value: nil, description: nil, &blk)
35
+ add(name, SystemSettings::IntegerSetting, value: value, description: description, &blk)
36
+ end
37
+
38
+ def integer_list(name, value: nil, description: nil, &blk)
39
+ add(name, SystemSettings::IntegerListSetting, value: value, description: description, &blk)
40
+ end
41
+
42
+ def persist
43
+ if settings_table_exists?
44
+ SystemSettings::Setting.transaction do
45
+ @items.each do |entry|
46
+ persisted_record = entry[:class].find_by(name: entry[:name])
47
+ if persisted_record
48
+ persisted_record.update_attributes!(description: entry[:description])
49
+ else
50
+ entry[:class].create!(name: entry[:name], value: entry[:value], description: entry[:description])
51
+ end
52
+ end
53
+ end
54
+ else
55
+ $stderr.puts "SystemSettings: Settings table has not been created!"
56
+ false
57
+ end
58
+ end
59
+
60
+ def purge
61
+ if settings_table_exists?
62
+ SystemSettings::Setting.delete_all
63
+ true
64
+ else
65
+ false
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def settings_table_exists?
72
+ SystemSettings::Setting.table_exists?
73
+ end
74
+
75
+ def add(name, class_const, value:, description:)
76
+ value = yield(value) if block_given?
77
+ value = value.call if value.is_a?(Proc)
78
+ @items.push(name: name, class: class_const, value: value, description: description)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "./setting"
2
+
3
+ module SystemSettings
4
+ class IntegerListSetting < Setting
5
+ attribute :value, SystemSettings::Type::IntegerList.new
6
+ validates :value, list_of_integers: { allow_blank: true }
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "./setting"
2
+
3
+ module SystemSettings
4
+ class IntegerSetting < Setting
5
+ attribute :value, :integer
6
+ validates :value, numericality: { only_integer: true }
7
+ end
8
+ end
@@ -0,0 +1,42 @@
1
+ module SystemSettings
2
+ class ListOfIntegersValidator < ActiveModel::EachValidator
3
+ LIST_REGEXP = /\A[+-]?\d+(?:; *[+-]?\d+)*\z/
4
+ SINGLE_REGEXP = /\A[+-]?\d+\z/
5
+
6
+ def validate_each(record, attr_name, value)
7
+ came_from_user = :"#{attr_name}_came_from_user?"
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
12
+ raw_value ||= value
13
+
14
+ if record_attribute_changed_in_place?(record, attr_name)
15
+ raw_value = value
16
+ end
17
+
18
+ unless matches_list_of_integers_regexp?(raw_value)
19
+ record.errors.add(attr_name, :not_a_list_of_integers)
20
+ return
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def record_attribute_changed_in_place?(record, attr_name)
27
+ record.respond_to?(:attribute_changed_in_place?) &&
28
+ record.attribute_changed_in_place?(attr_name.to_s)
29
+ end
30
+
31
+ def matches_list_of_integers_regexp?(raw_value)
32
+ case raw_value
33
+ when String
34
+ LIST_REGEXP.match?(raw_value)
35
+ when Array
36
+ raw_value.all? { |v| SINGLE_REGEXP.match?(v.to_s) }
37
+ else
38
+ false
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "./application_record"
2
+
3
+ module SystemSettings
4
+ class Setting < ApplicationRecord
5
+ validates :type, presence: true
6
+ validates :name, presence: true, uniqueness: true
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "./setting"
2
+
3
+ module SystemSettings
4
+ class StringListSetting < Setting
5
+ attribute :value, SystemSettings::Type::StringList.new
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "./setting"
2
+
3
+ module SystemSettings
4
+ class StringSetting < Setting
5
+ attribute :value, :string
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ module SystemSettings
2
+ module Type
3
+ class IntegerList < ActiveModel::Type::Value
4
+ SEPARATOR = ";".freeze
5
+
6
+ def type
7
+ :integer_list
8
+ end
9
+
10
+ def deserialize(value)
11
+ value.presence && JSON.parse(value)
12
+ end
13
+
14
+ def serialize(value)
15
+ JSON.dump(value) unless value.nil?
16
+ end
17
+
18
+ private
19
+
20
+ def cast_value(value)
21
+ case value
22
+ when Array
23
+ value.map { |v| v.to_i rescue nil }
24
+ when String
25
+ value.split(SEPARATOR).map { |v| v.to_i rescue nil }
26
+ else
27
+ nil
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ module SystemSettings
2
+ module Type
3
+ class StringList < ActiveModel::Type::Value
4
+ def type
5
+ :string_list
6
+ end
7
+
8
+ def deserialize(value)
9
+ value.presence && JSON.parse(value)
10
+ end
11
+
12
+ def serialize(value)
13
+ JSON.dump(value) unless value.nil?
14
+ end
15
+
16
+ private
17
+
18
+ def cast_value(value)
19
+ case value
20
+ when Array
21
+ value.map { |v| String(v).strip }
22
+ when String
23
+ value.split(/(?<=[^\\]);/).map(&:strip)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ en:
2
+ activerecord:
3
+ models:
4
+ system_settings/setting:
5
+ one: "System Setting"
6
+ other: "System Settings"
7
+ system_settings/integer_list_setting:
8
+ one: "System Setting (Integer list)"
9
+ other: "System Settings (Integer list)"
10
+ system_settings/integer_setting:
11
+ one: "System Setting (Integer)"
12
+ other: "System Settings (Integer)"
13
+ system_settings/string_list_setting:
14
+ one: "System Setting (String list)"
15
+ other: "System Settings (String list)"
16
+ system_settings/string_setting:
17
+ one: "System Setting (String)"
18
+ other: "System Settings (String)"
19
+ attributes:
20
+ system_settings/setting:
21
+ id: "ID"
22
+ name: "Name"
23
+ type: "Type"
24
+ description: "Description"
25
+ created_at: "Created at"
26
+ uploaded_at: "Updated at"
27
+ errors:
28
+ models:
29
+ system_settings/setting:
30
+ attributes:
31
+ value:
32
+ not_a_list_of_integers: "not a list of integers"
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ SystemSettings::Engine.routes.draw do
2
+ scope "api" do
3
+ resources "settings"
4
+ end
5
+ root to: "root#index"
6
+ get "*rest", to: "root#index", as: :catch_all
7
+ end
@@ -0,0 +1,11 @@
1
+ class CreateSystemSettingsSettings < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :system_settings_settings do |t|
4
+ t.string :name, null: false, index: { unique: true }
5
+ t.string :type, null: false, index: true
6
+ t.text :value
7
+ t.text :description
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ {
2
+ "files": {
3
+ "main.css": "/system_settings/static/css/main.e262560e.chunk.css",
4
+ "main.js": "/system_settings/static/js/main.afaa2372.chunk.js",
5
+ "runtime~main.js": "/system_settings/static/js/runtime~main.a09e9b82.js",
6
+ "static/js/2.83a5f4da.chunk.js": "/system_settings/static/js/2.83a5f4da.chunk.js",
7
+ "index.html": "/system_settings/index.html",
8
+ "precache-manifest.130ca067521371497dba783c23aa7e16.js": "/system_settings/precache-manifest.130ca067521371497dba783c23aa7e16.js",
9
+ "service-worker.js": "/system_settings/service-worker.js"
10
+ }
11
+ }
Binary file
@@ -0,0 +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.e262560e.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.83a5f4da.chunk.js"></script><script src="/system_settings/static/js/main.afaa2372.chunk.js"></script></body></html>
@@ -0,0 +1,22 @@
1
+ self.__precacheManifest = (self.__precacheManifest || []).concat([
2
+ {
3
+ "revision": "e04ed1ed3403d21f675ed787109671dd",
4
+ "url": "/system_settings/index.html"
5
+ },
6
+ {
7
+ "revision": "b12967943154ae7a6202",
8
+ "url": "/system_settings/static/css/main.e262560e.chunk.css"
9
+ },
10
+ {
11
+ "revision": "fbb0a923d9fbb93afddb",
12
+ "url": "/system_settings/static/js/2.83a5f4da.chunk.js"
13
+ },
14
+ {
15
+ "revision": "b12967943154ae7a6202",
16
+ "url": "/system_settings/static/js/main.afaa2372.chunk.js"
17
+ },
18
+ {
19
+ "revision": "1b52e032205e59a9e84f",
20
+ "url": "/system_settings/static/js/runtime~main.a09e9b82.js"
21
+ }
22
+ ]);
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Welcome to your Workbox-powered service worker!
3
+ *
4
+ * You'll need to register this file in your web app and you should
5
+ * disable HTTP caching for this file too.
6
+ * See https://goo.gl/nhQhGp
7
+ *
8
+ * The rest of the code is auto-generated. Please don't update this file
9
+ * directly; instead, make changes to your Workbox build configuration
10
+ * and re-run your build process.
11
+ * See https://goo.gl/2aRDsh
12
+ */
13
+
14
+ importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.0/workbox-sw.js");
15
+
16
+ importScripts(
17
+ "/system_settings/precache-manifest.130ca067521371497dba783c23aa7e16.js"
18
+ );
19
+
20
+ self.addEventListener('message', (event) => {
21
+ if (event.data && event.data.type === 'SKIP_WAITING') {
22
+ self.skipWaiting();
23
+ }
24
+ });
25
+
26
+ workbox.core.clientsClaim();
27
+
28
+ /**
29
+ * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30
+ * requests for URLs in the manifest.
31
+ * See https://goo.gl/S9QRab
32
+ */
33
+ self.__precacheManifest = [].concat(self.__precacheManifest || []);
34
+ workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35
+
36
+ workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/system_settings/index.html"), {
37
+
38
+ blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
39
+ });
@@ -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)}}.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}#root{z-index:1;position:relative}