super_settings 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +110 -16
  4. data/VERSION +1 -1
  5. data/app/helpers/super_settings/settings_helper.rb +13 -3
  6. data/app/views/layouts/super_settings/settings.html.erb +1 -1
  7. data/config/routes.rb +1 -1
  8. data/db/migrate/20210414004553_create_super_settings.rb +1 -7
  9. data/lib/super_settings/application/api.js +4 -1
  10. data/lib/super_settings/application/helper.rb +56 -17
  11. data/lib/super_settings/application/images/arrow-down-short.svg +3 -0
  12. data/lib/super_settings/application/images/arrow-up-short.svg +3 -0
  13. data/lib/super_settings/application/images/info-circle.svg +4 -0
  14. data/lib/super_settings/application/images/pencil-square.svg +4 -0
  15. data/lib/super_settings/application/images/plus.svg +3 -1
  16. data/lib/super_settings/application/images/trash3.svg +3 -0
  17. data/lib/super_settings/application/images/x-circle.svg +4 -0
  18. data/lib/super_settings/application/index.html.erb +54 -37
  19. data/lib/super_settings/application/layout.html.erb +5 -2
  20. data/lib/super_settings/application/layout_styles.css +7 -151
  21. data/lib/super_settings/application/layout_vars.css.erb +21 -0
  22. data/lib/super_settings/application/scripts.js +100 -21
  23. data/lib/super_settings/application/style_vars.css.erb +62 -0
  24. data/lib/super_settings/application/styles.css +183 -14
  25. data/lib/super_settings/application.rb +18 -11
  26. data/lib/super_settings/attributes.rb +1 -8
  27. data/lib/super_settings/configuration.rb +9 -0
  28. data/lib/super_settings/controller_actions.rb +2 -2
  29. data/lib/super_settings/engine.rb +1 -1
  30. data/lib/super_settings/history_item.rb +1 -1
  31. data/lib/super_settings/http_client.rb +165 -0
  32. data/lib/super_settings/rack_application.rb +3 -3
  33. data/lib/super_settings/rest_api.rb +5 -4
  34. data/lib/super_settings/setting.rb +13 -2
  35. data/lib/super_settings/storage/active_record_storage.rb +7 -0
  36. data/lib/super_settings/storage/history_attributes.rb +31 -0
  37. data/lib/super_settings/storage/http_storage.rb +60 -184
  38. data/lib/super_settings/storage/json_storage.rb +201 -0
  39. data/lib/super_settings/storage/mongodb_storage.rb +238 -0
  40. data/lib/super_settings/storage/redis_storage.rb +49 -111
  41. data/lib/super_settings/storage/s3_storage.rb +165 -0
  42. data/lib/super_settings/storage/storage_attributes.rb +64 -0
  43. data/lib/super_settings/storage/test_storage.rb +3 -5
  44. data/lib/super_settings/storage/transaction.rb +67 -0
  45. data/lib/super_settings/storage.rb +13 -6
  46. data/lib/super_settings/time_precision.rb +36 -0
  47. data/lib/super_settings.rb +11 -0
  48. data/super_settings.gemspec +4 -2
  49. metadata +22 -9
  50. data/lib/super_settings/application/images/edit.svg +0 -1
  51. data/lib/super_settings/application/images/info.svg +0 -1
  52. data/lib/super_settings/application/images/slash.svg +0 -1
  53. data/lib/super_settings/application/images/trash.svg +0 -1
@@ -1,33 +1,46 @@
1
1
  <%= style_tag %>
2
2
 
3
- <main>
4
- <form class="form-inline" style="display:block;" onsubmit="return false">
3
+ <main class="super-settings" data-api-base-url="<%= html_escape(api_base_url) %>">
4
+ <form class="super-settings-form-inline" style="display:block;" onsubmit="return false">
5
5
  <div class="super-settings-sticky-top">
6
6
  <span class="js-settings-count" style="display:inline-block; margin-right:1rem;"></span>
7
7
 
8
- <label for="filter" class="super-settings-sr-only">Filter</label>
9
- <input type="text" name="filter" value="" placeholder="Filter Keys" size="20" class="form-control" title="Filter Keys" id="filter" style="margin-right:1rem;">
8
+ <label for="super-settings-filter" class="super-settings-sr-only">Filter</label>
9
+ <input type="text" name="filter" value="" placeholder="Filter Keys" size="20" class="super-settings-form-control" title="Filter Keys" id="super-settings-filter" style="margin-right:1rem;">
10
10
 
11
- <button type="button" class="btn btn-default" id="add-setting"><%= icon_image(:plus) %> Add Setting</button>
11
+ <button type="button" class="super-settings-btn super-settings-btn-default" id="super-settings-add-setting"><%= icon_image(:plus, style: {"vertical-align": "text-top"}) %> Add Setting</button>
12
12
 
13
- <button type="button" class="btn btn-default" id="discard-changes" disabled>
13
+ <button type="button" class="super-settings-btn super-settings-btn-default" id="super-settings-discard-changes" disabled>
14
14
  Discard Changes
15
15
  </button>
16
16
 
17
- <button type="button" class="btn btn-primary" id="save-settings" disabled>
17
+ <button type="button" class="super-settings-btn super-settings-btn-primary" id="super-settings-save-settings" disabled>
18
18
  Save <span class="count"></span> Changes
19
19
  </button>
20
20
 
21
21
  <strong class="js-flash" style="display:none; margin-left:3rem;"></strong>
22
22
  </div>
23
23
 
24
- <table class="table table-striped" id="settings-table">
24
+ <table class="super-settings-table super-settings-table-striped" id="settings-table">
25
25
  <thead>
26
26
  <tr>
27
- <th scope="col" class="super-settings-key">Key</th>
27
+ <th scope="col" class="super-settings-key">
28
+ Key
29
+ <button class="super-settings-sort-control super-settings-btn-no-chrome" data-field="key" data-order="asc" data-selected="true" title="Sort by key">
30
+ <%= icon_image("arrow-down-short", data: {order: :asc}, style: {display: "inline-block"}) %>
31
+ <%= icon_image("arrow-up-short", data: {order: :desc}, style: {display: "none"}) %>
32
+ </button>
33
+ </th>
28
34
  <th scope="col" class="super-settings-value">Value</th>
29
35
  <th scope="col" class="super-settings-value-type">Type</th>
30
36
  <th scope="col" class="super-settings-description">Description</th>
37
+ <th scope="col" data-field="updated_at">
38
+ Modified
39
+ <button class="super-settings-sort-control super-settings-btn-no-chrome" data-field="updated_at" data-order="asc" title="Sort by modified time">
40
+ <%= icon_image("arrow-down-short", data: {order: :asc}, style: {display: "inline-block"}) %>
41
+ <%= icon_image("arrow-up-short", data: {order: :desc}, style: {display: "none"}) %>
42
+ </button>
43
+ </th>
31
44
  <th scope="col" class="super-settings-controls"><span class="super-settings-sr-only">Controls</span></th>
32
45
  </tr>
33
46
  </thead>
@@ -35,15 +48,15 @@
35
48
  </tbody>
36
49
  </table>
37
50
  </form>
38
- </main>
39
51
 
40
- <div id="modal" class="super-settings-modal js-close-modal" aria-hidden="true" aria-role="dialog">
41
- <div class="super-settings-modal-dialog">
42
- <button type="button" title="Close Dialog" class="super-settings-modal-close js-close-modal">&times;</button>
43
- <div class="super-settings-modal-content">
52
+ <div id="super-settings-modal" class="super-settings-modal js-close-modal" aria-hidden="true" aria-role="dialog">
53
+ <div class="super-settings-modal-dialog">
54
+ <button type="button" title="Close Dialog" class="super-settings-modal-close super-settings-btn-no-chrome js-close-modal">&times;</button>
55
+ <div class="super-settings-modal-content">
56
+ </div>
44
57
  </div>
45
58
  </div>
46
- </div>
59
+ </main>
47
60
 
48
61
  <template id="setting-row-template" style="display:none;">
49
62
  <tr>
@@ -65,11 +78,15 @@
65
78
  <div class="js-value-placeholder super-settings-max-height-text"></div>
66
79
  </td>
67
80
 
81
+ <td class="super-settings-last-modified super-settings-text-nowrap">
82
+ <div class="js-value-placeholder"></div>
83
+ </td>
84
+
68
85
  <td class="super-settings-controls">
69
- <%= icon_button(:info, title: "Setting Info", color: "royalblue", js_class: "js-show-history") %>
70
- <%= icon_button(:edit, title: "Edit Setting", color: "green", js_class: "js-edit-setting") %>
71
- <%= icon_button(:trash, title: "Remove Setting", color: "firebrick", js_class: "js-remove-setting") %>
72
- <%= icon_button(:slash, title: "Cancel Changes", color: "firebrick", js_class: "js-restore-setting", link_style: "display:none;") %>
86
+ <%= icon_button("info-circle", title: "Setting Info", color: "#0d7ff0", js_class: "js-show-history") %>
87
+ <%= icon_button("pencil-square", title: "Edit Setting", color: "#0c8024", js_class: "js-edit-setting") %>
88
+ <%= icon_button("trash3", title: "Remove Setting", color: "#dc3545", js_class: "js-remove-setting") %>
89
+ <%= icon_button("x-circle", title: "Cancel Changes", color: "#dc3545", js_class: "js-restore-setting", link_style: "display:none;") %>
73
90
  </td>
74
91
  </tr>
75
92
  </template>
@@ -79,7 +96,7 @@
79
96
  <td class="super-settings-key">
80
97
  <div>
81
98
  <label for="settings_{{id}}_key" class="super-settings-sr-only">Key</label>
82
- <input type="text" id="settings_{{id}}_key" name="settings[{{id}}][key]" value="" maxlength="190" class="form-control js-setting-key" required>
99
+ <input type="text" id="settings_{{id}}_key" name="settings[{{id}}][key]" value="" maxlength="190" class="super-settings-form-control js-setting-key" required>
83
100
  </div>
84
101
  </td>
85
102
 
@@ -88,72 +105,72 @@
88
105
  <label for="settings_{{id}}_value" class="super-settings-sr-only">Value</label>
89
106
  <span class="js-value-placeholder"></span>
90
107
  </div>
91
- <div class="container text-danger js-setting-errors" style="display:none;">
108
+ <div class="super-settings-container super-settings-text-danger js-setting-errors" style="display:none;">
92
109
  </div>
93
110
  </td>
94
111
 
95
112
  <td class="super-settings-value-type">
96
113
  <div>
97
114
  <label for="settings_{{id}}_value_type" class="super-settings-sr-only">Value Type</label>
98
- <select name="settings[{{id}}][value_type]" class="form-control js-setting-value-type" id="settings_{{id}}_value_type">
115
+ <select name="settings[{{id}}][value_type]" class="super-settings-form-control js-setting-value-type" id="settings_{{id}}_value_type">
99
116
  <% SuperSettings::Setting::VALUE_TYPES.each do |value_type| %>
100
- <option value="<%= ERB::Util.html_escape(value_type) %>"><%= ERB::Util.html_escape(value_type) %></option>
117
+ <option value="<%= html_escape(value_type) %>"><%= html_escape(value_type) %></option>
101
118
  <% end %>
102
119
  </select>
103
120
  </div>
104
121
  </td>
105
122
 
106
- <td class="super-settings-description">
123
+ <td class="super-settings-description" colspan="2">
107
124
  <div>
108
125
  <label for="settings_{{id}}_description" class="super-settings-sr-only">Description</label>
109
- <textarea id="settings_{{id}}_description" name="settings[{{id}}][description]" value="" class="form-control" rows="4"></textarea>
126
+ <textarea id="settings_{{id}}_description" name="settings[{{id}}][description]" value="" class="super-settings-form-control" rows="4"></textarea>
110
127
  </div>
111
128
  </td>
112
129
 
113
130
  <td class="super-settings-controls">
114
- <%= icon_button(:info, title: "Setting Info", color: "royalblue", js_class: "js-show-history") %>
115
- <%= icon_button(:edit, title: "Edit Setting", color: "gray", js_class: "js-no-op", disabled: true) %>
116
- <%= icon_button(:slash, title: "Cancel Changes", color: "firebrick", js_class: "js-restore-setting") %>
131
+ <%= icon_button("info-circle", title: "Setting Info", color: "#0d7ff0", js_class: "js-show-history") %>
132
+ <%= icon_button("pencil-square", title: "Edit Setting", color: "#c0c0c0", js_class: "js-no-op", disabled: true) %>
133
+ <%= icon_button("x-circle", title: "Cancel Changes", color: "#dc3545", js_class: "js-restore-setting") %>
117
134
  </td>
118
135
  </tr>
119
136
  </template>
120
137
 
121
138
  <template id="setting-value-field-template" style="display:none;">
122
- <textarea id="settings_{{id}}_value" name="settings[{{id}}][value]" class="form-control js-setting-value" rows="4"></textarea>
139
+ <textarea id="settings_{{id}}_value" name="settings[{{id}}][value]" class="super-settings-form-control js-setting-value" rows="4"></textarea>
123
140
  </template>
124
141
 
125
142
  <template id="setting-value-field-integer-template" style="display:none;">
126
- <input type="number" step="1" id="settings_{{id}}_value" name="settings[{{id}}][value]" value="" class="form-control js-setting-value">
143
+ <input type="number" step="1" id="settings_{{id}}_value" name="settings[{{id}}][value]" value="" class="super-settings-form-control js-setting-value">
127
144
  </template>
128
145
 
129
146
  <template id="setting-value-field-float-template" style="display:none;">
130
- <input type="number" step="any" id="settings_{{id}}_value" name="settings[{{id}}][value]" value="" class="form-control js-setting-value">
147
+ <input type="number" step="any" id="settings_{{id}}_value" name="settings[{{id}}][value]" value="" class="super-settings-form-control js-setting-value">
131
148
  </template>
132
149
 
133
150
  <template id="setting-value-field-datetime-template" style="display:none;">
134
151
  <span>
135
- <input type="date" id="settings_{{id}}_value" name="_settings[{{id}}][date]" value="" class="form-control js-date-input">
152
+ <input type="date" id="settings_{{id}}_value" name="_settings[{{id}}][date]" value="" class="super-settings-form-control js-date-input">
136
153
  <label for="settings_{{id}}_value_time" class="super-settings-sr-only">Time</label>
137
- <input type="time" id="settings_{{id}}_value_time" name="_settings[{{id}}][time]" value="" class="form-control js-time-input" aria-label="Time">
154
+ <input type="time" id="settings_{{id}}_value_time" name="_settings[{{id}}][time]" value="" class="super-settings-form-control js-time-input" aria-label="Time">
138
155
  <input type="hidden" name="settings[{{id}}][value]" value="" class="js-setting-value">
139
- <small class="text-muted">Time Zone: <span class="timezone"></span></small>
156
+ <small class="super-settings-text-muted">Time Zone: <span class="timezone"></span></small>
140
157
  </span>
141
158
  </template>
142
159
 
143
160
  <template id="setting-value-field-boolean-template" style="display:none;">
144
- <span class="form-check">
161
+ <span class="super-settings-form-check">
145
162
  <input type="checkbox" id="settings_{{id}}_value" name="settings[{{id}}][value]" value="true" class="js-setting-value">
146
163
  <label for="settings_{{id}}_value">Enabled</label>
147
164
  </span>
148
165
  </template>
149
166
 
150
167
  <template id="setting-value-field-array-template" style="display:none;">
151
- <textarea id="settings_{{id}}_value" name="settings[{{id}}][value]" value="" class="form-control js-setting-value" rows="8" placeholder="one entry per line"></textarea>
168
+ <textarea id="settings_{{id}}_value" name="settings[{{id}}][value]" value="" class="super-settings-form-control js-setting-value" rows="8" placeholder="one entry per line"></textarea>
152
169
  </template>
153
170
 
154
171
  <template id="setting-history-table" style="display:none">
155
172
  <h3>Setting History: <span class="super-settings-history-key"></span></h3>
156
- <table class="table table-striped" id="super-settings-history">
173
+ <table class="super-settings-table super-settings-table-striped" id="super-settings-history">
157
174
  <thead>
158
175
  <tr>
159
176
  <th scope="col" class="super-settings-text-nowrap">Time</th>
@@ -2,9 +2,12 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <title><%= application_name %> Settings</title>
5
+ <meta charset="utf-8">
5
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
7
  <meta name="pinterest" content="nopin" />
7
- <meta name="format-detection" content="telephone=no">
8
+ <meta name="format-detection" content="telephone=no email=no date=no address=no">
9
+ <meta name="robots" content="noindex, nofollow">
10
+ <meta name="referrer" content="no-referrer-when-downgrade">
8
11
  <%= layout_style_tag %>
9
12
  <%= add_to_head %>
10
13
  </head>
@@ -15,7 +18,7 @@
15
18
  <%= application_header %>
16
19
  </h1>
17
20
  </header>
18
- <div class="container">
21
+ <div class="super-settings-container">
19
22
  <%= yield %>
20
23
  </div>
21
24
  </body>
@@ -1,12 +1,14 @@
1
- * {box-sizing:border-box;}
1
+ * {
2
+ box-sizing: border-box;
3
+ }
2
4
 
3
5
  body {
4
6
  font-family: sans-serif;
5
7
  font-size: 1rem;
6
8
  line-height: 1.5;
7
9
  text-align: left;
8
- color: #212529;
9
- background-color: #ffffff;
10
+ color: var(--text-color);
11
+ background-color: var(--background-color);
10
12
  margin: 0;
11
13
  padding: 0;
12
14
  }
@@ -37,157 +39,11 @@ header h1.logo img {
37
39
  margin-right: 1rem;
38
40
  }
39
41
 
40
- .align-center {
41
- text-align: center;
42
- }
43
-
44
- .container {
45
- padding-left: 15px;
46
- padding-right: 15px;
47
- margin-left: auto;
48
- margin-right: auto;
49
- }
50
-
51
42
  a {
52
43
  text-decoration: none;
53
- color: #369;
44
+ color: var(--link-color);
54
45
  }
55
46
 
56
47
  a:visited {
57
- color: #369;n
58
- }
59
-
60
- .btn {
61
- box-shadow:inset 0px 1px 0px 0px #ffffff;
62
- background:linear-gradient(to bottom, #f9f9f9 5%, #e9e9e9 100%);
63
- background-color:#f9f9f9;
64
- border-radius:6px;
65
- border:1px solid #dcdcdc;
66
- display:inline-block;
67
- color:#666666;
68
- font-family:Arial;
69
- font-size:15px;
70
- font-weight:bold;
71
- padding:9px 16px;
72
- text-decoration:none;
73
- text-shadow:0px 1px 0px #ffffff;
74
- vertical-align: middle;
75
- }
76
- .btn:hover {
77
- background:linear-gradient(to bottom, #e9e9e9 5%, #f9f9f9 100%);
78
- background-color:#e9e9e9;
79
- }
80
- .btn:active {
81
- position:relative;
82
- top:1px;
83
- }
84
- .btn:disabled {
85
- background:linear-gradient(to bottom, #f9f9f9 5%, #e9e9e9 100%);
86
- background-color:#f9f9f9;
87
- opacity: 0.65;
88
- box-shadow: none;
89
- }
90
-
91
- .btn-primary {
92
- box-shadow:inset 0px 1px 0px 0px #9fb4f2;
93
- background:linear-gradient(to bottom, #7892c2 5%, #476e9e 100%);
94
- background-color:#7892c2;
95
- color:#ffffff;
96
- text-shadow:0px 1px 0px #283966;
97
- }
98
- .btn-primary:hover {
99
- background:linear-gradient(to bottom, #476e9e 5%, #7892c2 100%);
100
- background-color:#476e9e;
101
- }
102
- .btn-primary:disabled {
103
- background:linear-gradient(to bottom, #7892c2 5%, #476e9e 100%);
104
- background-color:#7892c2;
105
- }
106
-
107
- .btn:not(:disabled) {
108
- cursor: pointer;
109
- }
110
- .btn:disabled {
111
- opacity: .65;
112
- }
113
-
114
- .table {
115
- width: 100%;
116
- max-width: 100%;
117
- margin-bottom: 1rem;
118
- border-collapse: collapse;
119
- }
120
-
121
- .table thead th {
122
- vertical-align: bottom;
123
- border-bottom: 2px solid #dee2e6;
124
- }
125
-
126
- .table td, .table th {
127
- padding: 0.75rem;
128
- vertical-align: top;
129
- border-top: 1px solid #dee2e6;
130
- }
131
-
132
- .table-striped tbody tr:nth-of-type(odd) {
133
- background-color: rgba(0, 0, 0, .05);
134
- }
135
-
136
- textarea {
137
- font-family: inherit;
138
- margin: 0;
139
- overflow: auto;
140
- resize: vertical;
141
- }
142
-
143
- .form-control {
144
- font-family: inherit;
145
- margin: 0;
146
- overflow: visible;
147
- display: block;
148
- width: 100%;
149
- padding: .375rem .75rem;
150
- font-size: 1rem;
151
- line-height: 1.5;
152
- color: #495057;
153
- background-color: #fff;
154
- background-clip: padding-box;
155
- border: 1px solid #ced4da;
156
- border-radius: .25rem;
157
- transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
158
- }
159
-
160
- select.form-control:not([size]):not([multiple]) {
161
- height: calc(2.25rem + 2px);
162
- }
163
-
164
- .form-check {
165
- margin-top: .5rem;
166
- display: inline-block;
167
- }
168
-
169
- .form-check input[type=checkbox] {
170
- vertical-align: middle;
171
- }
172
-
173
- .form-inline {
174
- display: inline-block;
175
- }
176
-
177
- .form-inline .form-control {
178
- display: inline-block;
179
- width: auto;
180
- vertical-align: middle;
181
- }
182
-
183
- .text-success {
184
- color: green !important;
185
- }
186
-
187
- .text-danger {
188
- color: firebrick !important;
189
- }
190
-
191
- .text-muted {
192
- color: #666 !important;
48
+ color: var(--link-color);
193
49
  }
@@ -0,0 +1,21 @@
1
+ <% unless color_scheme != :dark %>
2
+ :root {
3
+ --text-color: #212529;
4
+ --background-color: #ffffff;
5
+ --link-color: #369;
6
+ }
7
+ <% end %>
8
+
9
+ <% if color_scheme == :system %>
10
+ @media (prefers-color-scheme: dark) {
11
+ <% end %>
12
+ <% if color_scheme == :system || color_scheme == :dark %>
13
+ :root {
14
+ --text-color: #fff;
15
+ --background-color: #333;
16
+ --link-color: rgb(133, 179, 225);
17
+ }
18
+ <% end %>
19
+ <% if color_scheme == :system %>
20
+ }
21
+ <% end %>
@@ -15,8 +15,8 @@
15
15
 
16
16
  // Set the enabled status of the save button for submitting the form.
17
17
  function enableSaveButton() {
18
- const saveButton = document.querySelector("#save-settings");
19
- const discardButton = document.querySelector("#discard-changes");
18
+ const saveButton = document.querySelector("#super-settings-save-settings");
19
+ const discardButton = document.querySelector("#super-settings-discard-changes");
20
20
  if (saveButton) {
21
21
  const count = changesCount();
22
22
  const countSpan = saveButton.querySelector(".count");
@@ -117,6 +117,14 @@
117
117
  if (setting.description !== null && setting.description !== undefined) {
118
118
  row.querySelector(".super-settings-description .js-value-placeholder").innerHTML = escapeHTML(setting.description).replaceAll("\n", "<br>");
119
119
  }
120
+ if (setting.updated_at !== null && setting.updated_at !== undefined) {
121
+ const lastModified = new Date(Date.parse(setting.updated_at));
122
+ const lastModifiedFormatter = new Intl.DateTimeFormat(navigator.language, {month: "short", day: "numeric", year: "numeric"});
123
+ const lastModifiedString = lastModifiedFormatter.format(lastModified);
124
+ const lastModifiedElement = row.querySelector(".super-settings-last-modified .js-value-placeholder")
125
+ lastModifiedElement.innerText = lastModifiedString;
126
+ lastModifiedElement.title = dateFormatter().format(lastModified);
127
+ }
120
128
 
121
129
  return row
122
130
  }
@@ -182,6 +190,7 @@
182
190
  function editSettingRow(setting) {
183
191
  const row = elementFromSettingTemplate(setting, "#setting-row-edit-template");
184
192
  row.dataset.id = setting.id
193
+ row.dataset.key = setting.key
185
194
 
186
195
  row.querySelector(".super-settings-key input").value = setting.key;
187
196
  if (setting.description) {
@@ -239,7 +248,7 @@
239
248
  document.querySelector("#settings-table tbody").prepend(row);
240
249
  }
241
250
  bindSettingControlEvents(row);
242
- filterSettings(document.querySelector("#filter").value);
251
+ filterSettings(document.querySelector("#super-settings-filter").value);
243
252
  row.scrollIntoView({block: "nearest"});
244
253
  enableSaveButton();
245
254
  return row;
@@ -301,7 +310,7 @@
301
310
 
302
311
  // Programatically apply the filter again to keep it up to date with other changes.
303
312
  function applyFilter(value) {
304
- const filter = document.querySelector("#filter");
313
+ const filter = document.querySelector("#super-settings-filter");
305
314
  if (filter) {
306
315
  if (value) {
307
316
  filter.value = value;
@@ -330,11 +339,11 @@
330
339
  function showFlash(message, success) {
331
340
  const flash = document.querySelector(".js-flash");
332
341
  if (success) {
333
- flash.classList.add("text-success");
334
- flash.classList.remove("text-danger");
342
+ flash.classList.add("super-settings-text-success");
343
+ flash.classList.remove("super-settings-text-danger");
335
344
  } else {
336
- flash.classList.add("text-danger");
337
- flash.classList.remove("text-success");
345
+ flash.classList.add("tsuper-settings-ext-danger");
346
+ flash.classList.remove("super-settings-text-success");
338
347
  }
339
348
  flash.innerText = message;
340
349
  flash.style.display = "inline-block";
@@ -378,7 +387,7 @@
378
387
  tbody.insertAdjacentHTML("beforeend", rowsHTML);
379
388
 
380
389
  if (payload.previous_page_params || payload.next_page_params) {
381
- let paginationHTML = `<div class="align-center">`;
390
+ let paginationHTML = `<div class="super-settings-align-center">`;
382
391
  if (payload.previous_page_params) {
383
392
  paginationHTML += `<div style="float:left;"><a href="#" class="js-show-history" title="Newer" data-offset="${payload.previous_page_params.offset}" data-limit="${payload.previous_page_params.limit}" data-key="${payload.previous_page_params.key}")>&#8592; Newer</a></div>`;
384
393
  }
@@ -393,7 +402,7 @@
393
402
 
394
403
  // Show a modal window overlayed on the page.
395
404
  function showModal() {
396
- const modal = document.querySelector("#modal");
405
+ const modal = document.querySelector("#super-settings-modal");
397
406
  const content = document.querySelector(".super-settings-modal-content");
398
407
  modal.style.display = "block";
399
408
  modal.setAttribute("aria-hidden", "false");
@@ -409,7 +418,7 @@
409
418
 
410
419
  // Hide the modal window overlayed on the page.
411
420
  function hideModal() {
412
- const modal = document.querySelector("#modal");
421
+ const modal = document.querySelector("#super-settings-modal");
413
422
  const content = document.querySelector(".super-settings-modal-content");
414
423
  modal.style.display = "none";
415
424
  modal.setAttribute("aria-hidden", "true");
@@ -456,7 +465,7 @@
456
465
  return;
457
466
  }
458
467
 
459
- const modal = document.querySelector("#modal");
468
+ const modal = document.querySelector("#super-settings-modal");
460
469
  const content = document.querySelector(".super-settings-modal-content");
461
470
  let key = event.target.dataset.key;
462
471
  if (!key) {
@@ -592,7 +601,12 @@
592
601
  document.querySelectorAll("#settings-table tbody tr[data-edited=true]").forEach(function(row) {
593
602
  const data = {};
594
603
  settingsData.push(data);
604
+
595
605
  data.key = row.querySelector(".js-setting-key").value;
606
+ if (data.key != row.dataset.key) {
607
+ data.key_was = row.dataset.key;
608
+ }
609
+
596
610
  const deleted = row.querySelector(".js-setting-deleted");
597
611
  if (deleted && deleted.value === "1") {
598
612
  data.deleted = true;
@@ -635,7 +649,7 @@
635
649
  function refreshPage(event) {
636
650
  event.preventDefault();
637
651
  let url = window.location.href.replace(/\?.*/, "");
638
- const filter = document.querySelector("#filter").value;
652
+ const filter = document.querySelector("#super-settings-filter").value;
639
653
  if (filter !== "") {
640
654
  url += "?filter=" + escape(filter);
641
655
  }
@@ -694,8 +708,9 @@
694
708
  const tbody = document.querySelector("#settings-table tbody");
695
709
  tbody.innerHTML = "";
696
710
  let count = settings.length;
697
- settings.forEach(function(setting) {
698
- const randomId = "setting" + Math.floor((Math.random() * 0xFFFFFFFFFFFFFF)).toString(16);
711
+
712
+ sortSettings(settings).forEach(function(setting) {
713
+ const randomId = "setting" + Math.floor((Math.random() * 0xFFFFFFFFFFFFF)).toString(16);
699
714
  setting.id = (setting.id || randomId);
700
715
  const row = settingRow(setting);
701
716
  tbody.appendChild(row);
@@ -703,12 +718,73 @@
703
718
  });
704
719
  document.querySelector(".js-settings-count").textContent = `${count} ${count === 1 ? "Setting" : "Settings"}`;
705
720
 
706
- const filter = document.querySelector("#filter");
721
+ const filter = document.querySelector("#super-settings-filter");
707
722
  if (filter) {
708
723
  filter.dispatchEvent(new Event("input"));
709
724
  }
710
725
  }
711
726
 
727
+ function sortOrder() {
728
+ const selectedSort = document.querySelector(".super-settings-sort-control[data-selected=true]");
729
+ const field = selectedSort.dataset.field;
730
+ const order = selectedSort.dataset.order;
731
+ return {field: field, order: order};
732
+ }
733
+
734
+ // Sort settings by the selected sort option.
735
+ function sortSettings(settings) {
736
+ const sort = sortOrder();
737
+ return settings.sort(function(a, b) {
738
+ let aValue = a[sort.field];
739
+ let bValue = b[sort.field];
740
+ if (sort.field == "updated_at") {
741
+ aValue = new Date(Date.parse(aValue));
742
+ bValue = new Date(Date.parse(bValue));
743
+ }
744
+
745
+ if (aValue === bValue) {
746
+ return 0;
747
+ } else if (sort.order === "asc") {
748
+ return (aValue < bValue) ? -1 : 1;
749
+ } else {
750
+ return (aValue > bValue) ? -1 : 1;
751
+ }
752
+ })
753
+ }
754
+
755
+ function setSortOrder(event) {
756
+ event.preventDefault();
757
+
758
+ const target = event.target.closest(".super-settings-sort-control");
759
+ let prevSelection = document.querySelector(".super-settings-sort-control[data-selected=true]");
760
+
761
+ if (prevSelection == target) {
762
+ selectSortElement(prevSelection, false);
763
+ target.querySelector(`[data-order=${target.dataset.order}]`).style.display = "none";
764
+ target.dataset.order = (target.dataset.order === "asc" ? "desc" : "asc");
765
+ target.querySelector(`[data-order=${target.dataset.order}]`).style.display = "inline-block";
766
+ } else {
767
+ selectSortElement(prevSelection, false);
768
+ }
769
+
770
+ selectSortElement(target, true);
771
+
772
+ renderSettingsTable(activeSettings);
773
+ }
774
+
775
+ function selectSortElement(element, selected) {
776
+ element.dataset.selected = selected;
777
+
778
+ const svg = element.querySelector(`[data-order=${element.dataset.order}]`).querySelector("svg");
779
+ if (selected) {
780
+ svg.style.backgroundColor = getComputedStyle(document.querySelector(".super-settings")).getPropertyValue("--primary-color");
781
+ svg.style.fill = "white";
782
+ } else {
783
+ svg.style.backgroundColor = null;
784
+ svg.style.fill = null;
785
+ }
786
+ }
787
+
712
788
  function promptUnsavedChanges(event) {
713
789
  if (changesCount() > 0) {
714
790
  return "Are you sure you want to leave?";
@@ -740,15 +816,18 @@
740
816
  docReady(function() {
741
817
  storeAccessToken();
742
818
 
743
- addListener(document.querySelector("#filter"), "input", filterListener);
744
- addListener(document.querySelector("#add-setting"), "click", addSetting);
745
- addListener(document.querySelector("#discard-changes"), "click", refreshPage);
746
- addListener(document.querySelector("#save-settings"), "click", updateSettings);
747
- addListener(document.querySelector("#modal"), "click", closeModal);
819
+ addListener(document.querySelector("#super-settings-filter"), "input", filterListener);
820
+ addListener(document.querySelector("#super-settings-add-setting"), "click", addSetting);
821
+ addListener(document.querySelector("#super-settings-discard-changes"), "click", refreshPage);
822
+ addListener(document.querySelector("#super-settings-save-settings"), "click", updateSettings);
823
+ addListener(document.querySelector("#super-settings-modal"), "click", closeModal);
824
+ addListener(document.querySelectorAll(".super-settings-sort-control"), "click", setSortOrder);
748
825
 
749
826
  const queryParams = new URLSearchParams(window.location.search);
750
827
  applyFilter(queryParams.get("filter"));
751
828
 
829
+ selectSortElement(document.querySelector(".super-settings-sort-control[data-selected=true]"), true);
830
+
752
831
  fetchActiveSettings();
753
832
 
754
833
  window.onbeforeunload = promptUnsavedChanges;