jav_madmin 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +294 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/config/madmin_manifest.js +3 -0
  6. data/app/assets/stylesheets/madmin/actiontext.css +31 -0
  7. data/app/assets/stylesheets/madmin/application-sprockets.css +11 -0
  8. data/app/assets/stylesheets/madmin/application.css +9 -0
  9. data/app/assets/stylesheets/madmin/base.css +144 -0
  10. data/app/assets/stylesheets/madmin/buttons.css +53 -0
  11. data/app/assets/stylesheets/madmin/forms.css +90 -0
  12. data/app/assets/stylesheets/madmin/pagination.css +59 -0
  13. data/app/assets/stylesheets/madmin/reset.css +242 -0
  14. data/app/assets/stylesheets/madmin/sidebar.css +179 -0
  15. data/app/assets/stylesheets/madmin/tables.css +71 -0
  16. data/app/controllers/madmin/application_controller.rb +17 -0
  17. data/app/controllers/madmin/base_controller.rb +15 -0
  18. data/app/controllers/madmin/dashboard_controller.rb +6 -0
  19. data/app/controllers/madmin/resource_controller.rb +119 -0
  20. data/app/helpers/madmin/application_helper.rb +10 -0
  21. data/app/helpers/madmin/nav_helper.rb +34 -0
  22. data/app/helpers/madmin/sort_helper.rb +48 -0
  23. data/app/javascript/madmin/application.js +3 -0
  24. data/app/javascript/madmin/controllers/application.js +12 -0
  25. data/app/javascript/madmin/controllers/index.js +5 -0
  26. data/app/javascript/madmin/controllers/mobile_nav_controller.js +50 -0
  27. data/app/javascript/madmin/controllers/nested_form_controller.js +34 -0
  28. data/app/javascript/madmin/controllers/select_controller.js +37 -0
  29. data/app/views/layouts/madmin/application.html.erb +41 -0
  30. data/app/views/madmin/application/_flash.html.erb +13 -0
  31. data/app/views/madmin/application/_form.html.erb +24 -0
  32. data/app/views/madmin/application/_javascript.html.erb +8 -0
  33. data/app/views/madmin/application/_missing_resource.html.erb +8 -0
  34. data/app/views/madmin/application/_navigation.html.erb +22 -0
  35. data/app/views/madmin/application/edit.html.erb +11 -0
  36. data/app/views/madmin/application/index.html.erb +85 -0
  37. data/app/views/madmin/application/new.html.erb +11 -0
  38. data/app/views/madmin/application/show.html.erb +40 -0
  39. data/app/views/madmin/dashboard/show.html.erb +2 -0
  40. data/app/views/madmin/fields/attachment/_form.html.erb +11 -0
  41. data/app/views/madmin/fields/attachment/_index.html.erb +7 -0
  42. data/app/views/madmin/fields/attachment/_show.html.erb +9 -0
  43. data/app/views/madmin/fields/attachments/_form.html.erb +1 -0
  44. data/app/views/madmin/fields/attachments/_index.html.erb +1 -0
  45. data/app/views/madmin/fields/attachments/_show.html.erb +11 -0
  46. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -0
  47. data/app/views/madmin/fields/belongs_to/_index.html.erb +4 -0
  48. data/app/views/madmin/fields/belongs_to/_show.html.erb +7 -0
  49. data/app/views/madmin/fields/boolean/_form.html.erb +3 -0
  50. data/app/views/madmin/fields/boolean/_index.html.erb +1 -0
  51. data/app/views/madmin/fields/boolean/_show.html.erb +1 -0
  52. data/app/views/madmin/fields/currency/_form.html.erb +1 -0
  53. data/app/views/madmin/fields/currency/_index.html.erb +1 -0
  54. data/app/views/madmin/fields/currency/_show.html.erb +1 -0
  55. data/app/views/madmin/fields/date/_form.html.erb +1 -0
  56. data/app/views/madmin/fields/date/_index.html.erb +1 -0
  57. data/app/views/madmin/fields/date/_show.html.erb +1 -0
  58. data/app/views/madmin/fields/date_time/_form.html.erb +1 -0
  59. data/app/views/madmin/fields/date_time/_index.html.erb +1 -0
  60. data/app/views/madmin/fields/date_time/_show.html.erb +1 -0
  61. data/app/views/madmin/fields/decimal/_form.html.erb +1 -0
  62. data/app/views/madmin/fields/decimal/_index.html.erb +1 -0
  63. data/app/views/madmin/fields/decimal/_show.html.erb +1 -0
  64. data/app/views/madmin/fields/enum/_form.html.erb +1 -0
  65. data/app/views/madmin/fields/enum/_index.html.erb +1 -0
  66. data/app/views/madmin/fields/enum/_show.html.erb +1 -0
  67. data/app/views/madmin/fields/file/_form.html.erb +1 -0
  68. data/app/views/madmin/fields/file/_index.html.erb +1 -0
  69. data/app/views/madmin/fields/file/_show.html.erb +6 -0
  70. data/app/views/madmin/fields/float/_form.html.erb +1 -0
  71. data/app/views/madmin/fields/float/_index.html.erb +1 -0
  72. data/app/views/madmin/fields/float/_show.html.erb +1 -0
  73. data/app/views/madmin/fields/has_many/_form.html.erb +1 -0
  74. data/app/views/madmin/fields/has_many/_index.html.erb +1 -0
  75. data/app/views/madmin/fields/has_many/_show.html.erb +18 -0
  76. data/app/views/madmin/fields/has_one/_form.html.erb +1 -0
  77. data/app/views/madmin/fields/has_one/_index.html.erb +3 -0
  78. data/app/views/madmin/fields/has_one/_show.html.erb +7 -0
  79. data/app/views/madmin/fields/integer/_form.html.erb +1 -0
  80. data/app/views/madmin/fields/integer/_index.html.erb +1 -0
  81. data/app/views/madmin/fields/integer/_show.html.erb +1 -0
  82. data/app/views/madmin/fields/json/_form.html.erb +1 -0
  83. data/app/views/madmin/fields/json/_index.html.erb +1 -0
  84. data/app/views/madmin/fields/json/_show.html.erb +1 -0
  85. data/app/views/madmin/fields/nested_has_many/_fields.html.erb +17 -0
  86. data/app/views/madmin/fields/nested_has_many/_form.html.erb +28 -0
  87. data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -0
  88. data/app/views/madmin/fields/nested_has_many/_show.html.erb +18 -0
  89. data/app/views/madmin/fields/password/_form.html.erb +1 -0
  90. data/app/views/madmin/fields/password/_index.html.erb +1 -0
  91. data/app/views/madmin/fields/password/_show.html.erb +1 -0
  92. data/app/views/madmin/fields/polymorphic/_form.html.erb +4 -0
  93. data/app/views/madmin/fields/polymorphic/_index.html.erb +3 -0
  94. data/app/views/madmin/fields/polymorphic/_show.html.erb +7 -0
  95. data/app/views/madmin/fields/rich_text/_form.html.erb +1 -0
  96. data/app/views/madmin/fields/rich_text/_index.html.erb +1 -0
  97. data/app/views/madmin/fields/rich_text/_show.html.erb +3 -0
  98. data/app/views/madmin/fields/select/_form.html.erb +1 -0
  99. data/app/views/madmin/fields/select/_index.html.erb +1 -0
  100. data/app/views/madmin/fields/select/_show.html.erb +1 -0
  101. data/app/views/madmin/fields/string/_form.html.erb +1 -0
  102. data/app/views/madmin/fields/string/_index.html.erb +1 -0
  103. data/app/views/madmin/fields/string/_show.html.erb +1 -0
  104. data/app/views/madmin/fields/text/_form.html.erb +1 -0
  105. data/app/views/madmin/fields/text/_index.html.erb +1 -0
  106. data/app/views/madmin/fields/text/_show.html.erb +1 -0
  107. data/app/views/madmin/fields/time/_form.html.erb +5 -0
  108. data/app/views/madmin/fields/time/_index.html.erb +1 -0
  109. data/app/views/madmin/fields/time/_show.html.erb +1 -0
  110. data/app/views/madmin/shared/_label.html.erb +4 -0
  111. data/config/importmap.rb +15 -0
  112. data/config/locales/el.yml +42 -0
  113. data/config/locales/en.yml +42 -0
  114. data/config/locales/fr.yml +42 -0
  115. data/config/locales/ko.yml +40 -0
  116. data/lib/generators/madmin/field/field_generator.rb +31 -0
  117. data/lib/generators/madmin/field/templates/_form.html.erb +1 -0
  118. data/lib/generators/madmin/field/templates/_index.html.erb +1 -0
  119. data/lib/generators/madmin/field/templates/_show.html.erb +1 -0
  120. data/lib/generators/madmin/field/templates/field.rb.tt +26 -0
  121. data/lib/generators/madmin/install/install_generator.rb +55 -0
  122. data/lib/generators/madmin/install/templates/controller.rb.tt +15 -0
  123. data/lib/generators/madmin/install/templates/routes.rb.tt +3 -0
  124. data/lib/generators/madmin/resource/resource_generator.rb +73 -0
  125. data/lib/generators/madmin/resource/templates/controller.rb.tt +10 -0
  126. data/lib/generators/madmin/resource/templates/resource.rb.tt +33 -0
  127. data/lib/generators/madmin/views/edit_generator.rb +16 -0
  128. data/lib/generators/madmin/views/form_generator.rb +15 -0
  129. data/lib/generators/madmin/views/index_generator.rb +15 -0
  130. data/lib/generators/madmin/views/javascript_generator.rb +15 -0
  131. data/lib/generators/madmin/views/layout_generator.rb +21 -0
  132. data/lib/generators/madmin/views/navigation_generator.rb +15 -0
  133. data/lib/generators/madmin/views/new_generator.rb +16 -0
  134. data/lib/generators/madmin/views/show_generator.rb +15 -0
  135. data/lib/generators/madmin/views/views_generator.rb +16 -0
  136. data/lib/madmin/engine.rb +47 -0
  137. data/lib/madmin/field.rb +67 -0
  138. data/lib/madmin/fields/attachment.rb +6 -0
  139. data/lib/madmin/fields/attachments.rb +9 -0
  140. data/lib/madmin/fields/belongs_to.rb +29 -0
  141. data/lib/madmin/fields/boolean.rb +6 -0
  142. data/lib/madmin/fields/currency.rb +15 -0
  143. data/lib/madmin/fields/date.rb +6 -0
  144. data/lib/madmin/fields/date_time.rb +6 -0
  145. data/lib/madmin/fields/decimal.rb +6 -0
  146. data/lib/madmin/fields/enum.rb +9 -0
  147. data/lib/madmin/fields/file.rb +9 -0
  148. data/lib/madmin/fields/float.rb +6 -0
  149. data/lib/madmin/fields/has_many.rb +62 -0
  150. data/lib/madmin/fields/has_one.rb +10 -0
  151. data/lib/madmin/fields/integer.rb +6 -0
  152. data/lib/madmin/fields/json.rb +6 -0
  153. data/lib/madmin/fields/nested_has_many.rb +44 -0
  154. data/lib/madmin/fields/password.rb +6 -0
  155. data/lib/madmin/fields/polymorphic.rb +22 -0
  156. data/lib/madmin/fields/rich_text.rb +6 -0
  157. data/lib/madmin/fields/select.rb +9 -0
  158. data/lib/madmin/fields/string.rb +9 -0
  159. data/lib/madmin/fields/text.rb +9 -0
  160. data/lib/madmin/fields/time.rb +6 -0
  161. data/lib/madmin/generator_helpers.rb +59 -0
  162. data/lib/madmin/member_action.rb +19 -0
  163. data/lib/madmin/menu.rb +70 -0
  164. data/lib/madmin/namespace.rb +35 -0
  165. data/lib/madmin/resource.rb +295 -0
  166. data/lib/madmin/resource_builder.rb +88 -0
  167. data/lib/madmin/search.rb +62 -0
  168. data/lib/madmin/version.rb +3 -0
  169. data/lib/madmin/view_generator.rb +43 -0
  170. data/lib/madmin.rb +114 -0
  171. data/lib/tasks/madmin_tasks.rake +11 -0
  172. metadata +284 -0
@@ -0,0 +1,59 @@
1
+ .pagination {
2
+ padding: 0.5rem;
3
+ }
4
+
5
+ .pagy {
6
+ display: inline-flex;
7
+ isolation: isolate;
8
+ border-radius: 0.375rem;
9
+
10
+ a {
11
+ display: inline-flex;
12
+ position: relative;
13
+ padding-top: 0.5rem;
14
+ padding-bottom: 0.5rem;
15
+ padding-left: 0.75rem;
16
+ padding-right: 0.75rem;
17
+ align-items: center;
18
+ box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
19
+ --tw-ring-inset: inset;
20
+ --tw-ring-color: #D1D5DB;
21
+ font-size: 0.875rem;
22
+ line-height: 1.25rem;
23
+ color: #111827;
24
+ background-color: #ffffff;
25
+ border-radius: 0.25rem;
26
+ text-decoration: none;
27
+ }
28
+
29
+ a:hover {
30
+ background-color: #F3F4F6;
31
+ }
32
+
33
+ a:not([href]) {
34
+ color: #D1D5DB;
35
+ cursor: default;
36
+ }
37
+
38
+ a[aria-current="page"] {
39
+ color: #ffffff;
40
+ background-color: var(--primary-color);
41
+ }
42
+
43
+ label {
44
+ display: inline-block;
45
+ padding-top: 0.125rem;
46
+ padding-bottom: 0.125rem;
47
+ padding-left: 0.75rem;
48
+ padding-right: 0.75rem;
49
+ border-radius: 0.5rem;
50
+ white-space: nowrap;
51
+ background-color: #E5E7EB;
52
+ }
53
+
54
+ label input {
55
+ border-radius: 0.375rem;
56
+ border-style: none;
57
+ background-color: #F3F4F6;
58
+ }
59
+ }
@@ -0,0 +1,242 @@
1
+ *, ::before, ::after {
2
+ box-sizing: border-box;
3
+ border-width: 0;
4
+ border-style: solid;
5
+ border-color: #e5e7eb;
6
+ }
7
+
8
+ body {
9
+ line-height: 1.5;
10
+ -webkit-font-smoothing: antialiased;
11
+ -moz-osx-font-smoothing: grayscale;
12
+ }
13
+ img, picture, video, canvas, svg {
14
+ display: block;
15
+ max-width: 100%;
16
+ }
17
+ input, button, textarea, select {
18
+ font: inherit;
19
+ }
20
+ p, h1, h2, h3, h4, h5, h6 {
21
+ overflow-wrap: break-word;
22
+ }
23
+ p {
24
+ text-wrap: pretty;
25
+ }
26
+ h1, h2, h3, h4, h5, h6 {
27
+ text-wrap: balance;
28
+ }
29
+
30
+ /*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */
31
+
32
+ /*
33
+ Document
34
+ ========
35
+ */
36
+
37
+ /**
38
+ Use a better box model (opinionated).
39
+ */
40
+
41
+ *,
42
+ ::before,
43
+ ::after {
44
+ box-sizing: border-box;
45
+ }
46
+
47
+ html {
48
+ /* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */
49
+ font-family:
50
+ system-ui,
51
+ 'Segoe UI',
52
+ Roboto,
53
+ Helvetica,
54
+ Arial,
55
+ sans-serif,
56
+ 'Apple Color Emoji',
57
+ 'Segoe UI Emoji';
58
+ line-height: 1.15; /* 1. Correct the line height in all browsers. */
59
+ -webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */
60
+ tab-size: 4; /* 3. Use a more readable tab size (opinionated). */
61
+ }
62
+
63
+ /*
64
+ Sections
65
+ ========
66
+ */
67
+
68
+ body {
69
+ margin: 0; /* Remove the margin in all browsers. */
70
+ }
71
+
72
+ /*
73
+ Text-level semantics
74
+ ====================
75
+ */
76
+
77
+ /**
78
+ Add the correct font weight in Chrome and Safari.
79
+ */
80
+
81
+ b,
82
+ strong {
83
+ font-weight: bolder;
84
+ }
85
+
86
+ /**
87
+ 1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
88
+ 2. Correct the odd 'em' font sizing in all browsers.
89
+ */
90
+
91
+ code,
92
+ kbd,
93
+ samp,
94
+ pre {
95
+ font-family:
96
+ ui-monospace,
97
+ SFMono-Regular,
98
+ Consolas,
99
+ 'Liberation Mono',
100
+ Menlo,
101
+ monospace; /* 1 */
102
+ font-size: 1em; /* 2 */
103
+ }
104
+
105
+ /**
106
+ Add the correct font size in all browsers.
107
+ */
108
+
109
+ small {
110
+ font-size: 80%;
111
+ }
112
+
113
+ /**
114
+ Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
115
+ */
116
+
117
+ sub,
118
+ sup {
119
+ font-size: 75%;
120
+ line-height: 0;
121
+ position: relative;
122
+ vertical-align: baseline;
123
+ }
124
+
125
+ sub {
126
+ bottom: -0.25em;
127
+ }
128
+
129
+ sup {
130
+ top: -0.5em;
131
+ }
132
+
133
+ /*
134
+ Tabular data
135
+ ============
136
+ */
137
+
138
+ /**
139
+ Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
140
+ */
141
+
142
+ table {
143
+ border-color: currentcolor;
144
+ }
145
+
146
+ /*
147
+ Forms
148
+ =====
149
+ */
150
+
151
+ /**
152
+ 1. Change the font styles in all browsers.
153
+ 2. Remove the margin in Firefox and Safari.
154
+ */
155
+
156
+ button,
157
+ input,
158
+ optgroup,
159
+ select,
160
+ textarea {
161
+ font-family: inherit; /* 1 */
162
+ font-size: 100%; /* 1 */
163
+ line-height: 1.15; /* 1 */
164
+ margin: 0; /* 2 */
165
+ }
166
+
167
+ /**
168
+ Correct the inability to style clickable types in iOS and Safari.
169
+ */
170
+
171
+ button,
172
+ [type='button'],
173
+ [type='reset'],
174
+ [type='submit'] {
175
+ -webkit-appearance: button;
176
+ }
177
+
178
+ /**
179
+ Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
180
+ */
181
+
182
+ legend {
183
+ padding: 0;
184
+ }
185
+
186
+ /**
187
+ Add the correct vertical alignment in Chrome and Firefox.
188
+ */
189
+
190
+ progress {
191
+ vertical-align: baseline;
192
+ }
193
+
194
+ /**
195
+ Correct the cursor style of increment and decrement buttons in Safari.
196
+ */
197
+
198
+ ::-webkit-inner-spin-button,
199
+ ::-webkit-outer-spin-button {
200
+ height: auto;
201
+ }
202
+
203
+ /**
204
+ 1. Correct the odd appearance in Chrome and Safari.
205
+ 2. Correct the outline style in Safari.
206
+ */
207
+
208
+ [type='search'] {
209
+ -webkit-appearance: textfield; /* 1 */
210
+ outline-offset: -2px; /* 2 */
211
+ }
212
+
213
+ /**
214
+ Remove the inner padding in Chrome and Safari on macOS.
215
+ */
216
+
217
+ ::-webkit-search-decoration {
218
+ -webkit-appearance: none;
219
+ }
220
+
221
+ /**
222
+ 1. Correct the inability to style clickable types in iOS and Safari.
223
+ 2. Change font properties to 'inherit' in Safari.
224
+ */
225
+
226
+ ::-webkit-file-upload-button {
227
+ -webkit-appearance: button; /* 1 */
228
+ font: inherit; /* 2 */
229
+ }
230
+
231
+ /*
232
+ Interactive
233
+ ===========
234
+ */
235
+
236
+ /*
237
+ Add the correct display in Chrome and Safari.
238
+ */
239
+
240
+ summary {
241
+ display: list-item;
242
+ }
@@ -0,0 +1,179 @@
1
+ #navbar {
2
+ border-bottom: 1px solid var(--border-color);
3
+
4
+ padding-top: 0.5rem;
5
+ padding-bottom: 0.5rem;
6
+ padding-left: 0.5rem;
7
+ padding-right: 0.5rem;
8
+
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: space-between;
12
+
13
+ .nav-group {
14
+ display: flex;
15
+ gap: 0.5em;
16
+ align-items: center;
17
+ }
18
+
19
+ a {
20
+ border-radius: .375rem;
21
+ color: var(--text-color);
22
+ display: block;
23
+ font-weight: 500;
24
+ padding: 0.5rem;
25
+ text-decoration: none;
26
+
27
+ margin-top: 0.1rem;
28
+ margin-bottom: 0.1rem;
29
+
30
+ &:hover {
31
+ background-color: rgb(243 244 246);
32
+ }
33
+
34
+ &.active {
35
+ background-color: rgb(243 244 246);
36
+ font-weight: 600;
37
+ }
38
+ }
39
+ }
40
+
41
+ main {
42
+ overflow: auto;
43
+ padding-right: 1rem;
44
+ padding-bottom: 1rem;
45
+ padding-left: calc(1rem + var(--sidebar-width));
46
+ position: relative;
47
+ }
48
+
49
+ #sidebar {
50
+ position: absolute;
51
+ inset: 0;
52
+ /* Ensure sidebar is above main content */
53
+ z-index: 10;
54
+
55
+ border-right: 1px solid var(--border-color);
56
+ height: 100%;
57
+ margin: 0;
58
+ overflow: auto;
59
+ padding: 1rem;
60
+ width: var(--sidebar-width);
61
+
62
+ h1 {
63
+ margin-top: 0;
64
+
65
+ a {
66
+ color: var(--text-color);
67
+ text-decoration: none;
68
+
69
+ &:hover {
70
+ text-decoration: underline;
71
+ }
72
+ }
73
+ }
74
+
75
+ nav {
76
+ h4 {
77
+ margin-top: 0.5rem;
78
+ margin-bottom: 0.25rem;
79
+ }
80
+
81
+ a {
82
+ border-radius: .375rem;
83
+ color: var(--text-color);
84
+ display: block;
85
+ font-weight: 500;
86
+ padding: 0.5rem;
87
+ text-decoration: none;
88
+
89
+ margin-top: 0.1rem;
90
+ margin-bottom: 0.1rem;
91
+
92
+ &:hover {
93
+ background-color: rgb(243 244 246);
94
+ }
95
+
96
+ &.active {
97
+ background-color: rgb(243 244 246);
98
+ font-weight: 600;
99
+ }
100
+ }
101
+ }
102
+
103
+ footer {
104
+ border-top: 1px solid var(--border-color);
105
+ padding-top: 1rem;
106
+ margin-top: 1rem;
107
+
108
+ a {
109
+ color: rgb(75 85 99);
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 0.25rem;
113
+ padding: 0.5rem;
114
+ text-decoration: none;
115
+
116
+ svg {
117
+ display: inline-block;
118
+ }
119
+ }
120
+
121
+ &:hover {
122
+ color: var(--primary-color);
123
+ }
124
+ }
125
+ }
126
+
127
+ /* Mobile Navigation Styles */
128
+ #hamburger-menu {
129
+ display: none;
130
+ background: none;
131
+ border: none;
132
+ padding: 0.5rem;
133
+ cursor: pointer;
134
+ color: var(--text-color);
135
+ }
136
+
137
+ .mobile-nav-overlay {
138
+ opacity: 0;
139
+ position: fixed;
140
+ top: 0;
141
+ left: 0;
142
+ width: 100%;
143
+ height: 100%;
144
+ background: rgba(0, 0, 0, 0.15);
145
+ z-index: 40;
146
+ transition: 0.2s ease-in-out;
147
+ pointer-events: none;
148
+ }
149
+
150
+ .mobile-nav-overlay.is-active {
151
+ opacity: 1;
152
+ pointer-events: auto;
153
+ }
154
+
155
+ @media (max-width: 768px) {
156
+ #hamburger-menu {
157
+ display: block;
158
+ }
159
+
160
+ main {
161
+ /* reset padding */
162
+ padding-left: 1rem;
163
+ }
164
+
165
+ #sidebar {
166
+ position: fixed;
167
+ top: 0;
168
+ left: 0;
169
+ height: 100vh;
170
+ transform: translateX(-100%);
171
+ transition: transform 0.2s ease-in-out;
172
+ background-color: var(--background-color, #fff);
173
+ z-index: 50;
174
+ }
175
+
176
+ #sidebar.open {
177
+ transform: translateX(0);
178
+ }
179
+ }
@@ -0,0 +1,71 @@
1
+ .table-scroll {
2
+ border-radius: 6px;
3
+ border: 1px solid var(--border-color);
4
+ overflow-x: auto;
5
+ position: relative;
6
+ min-width: 0;
7
+ width: 100%;
8
+
9
+ &:not(:has(tbody tr)) th {
10
+ border-bottom: none;
11
+ }
12
+ }
13
+
14
+ table {
15
+ border-collapse: collapse;
16
+ min-width: 100%;
17
+
18
+ th {
19
+ background-color: var(--background-color);
20
+ border-bottom: 1px solid var(--border-color);
21
+ text-align: left;
22
+
23
+ a {
24
+ color: rgb(17 24 39);
25
+ text-decoration: none;
26
+ }
27
+
28
+ svg {
29
+ display: inline-block;
30
+ vertical-align: middle;
31
+ }
32
+
33
+ &.label {
34
+ background-color: var(--background-color);
35
+ border-right: 1px solid var(--border-color);
36
+ width: 16rem;
37
+ }
38
+ }
39
+
40
+ td {
41
+ a {
42
+ font-weight: 500;
43
+ }
44
+
45
+ &.actions {
46
+ align-items: center;
47
+ display: flex;
48
+ gap: 0.5rem;
49
+ }
50
+ }
51
+
52
+ tr {
53
+ border-bottom: 1px solid var(--border-color);
54
+
55
+ &:last-child {
56
+ border-bottom: none;
57
+ }
58
+ }
59
+
60
+ th, td {
61
+ /* force scroll */
62
+ min-width: 60px;
63
+ white-space: nowrap;
64
+
65
+ /* responsive padding */
66
+ padding-top: clamp(0.5rem, 0.25rem + 1.25vw, 0.75rem);
67
+ padding-bottom: clamp(0.5rem, 0.25rem + 1.25vw, 0.75rem);
68
+ padding-left: clamp(0.5rem, 0.125rem + 1.875vw, 0.875rem);
69
+ padding-right: clamp(0.5rem, 0.125rem + 1.875vw, 0.875rem);
70
+ }
71
+ }
@@ -0,0 +1,17 @@
1
+ module Madmin
2
+ class ApplicationController < Madmin::BaseController
3
+ include Rails.application.routes.url_helpers
4
+
5
+ before_action :authenticate_admin_user
6
+
7
+ def authenticate_admin_user
8
+ # TODO: Add your authentication logic here
9
+
10
+ # For example, with Rails 8 authentication
11
+ # redirect_to "/", alert: "Not authorized." unless authenticated? && Current.user.admin?
12
+
13
+ # Or with Devise
14
+ # redirect_to "/", alert: "Not authorized." unless current_user&.admin?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Madmin
2
+ class BaseController < ActionController::Base
3
+ include ::ActiveStorage::SetCurrent if defined?(::ActiveStorage)
4
+
5
+ if Gem::Version.new(Pagy::VERSION) >= Gem::Version.new("43.0.0.rc")
6
+ include Pagy::Method
7
+ else
8
+ include Pagy::Backend
9
+ end
10
+
11
+ protect_from_forgery with: :exception
12
+
13
+ ActiveSupport.run_load_hooks(:madmin_base_controller, self)
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module Madmin
2
+ class DashboardController < Madmin::ApplicationController
3
+ def show
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,119 @@
1
+ module Madmin
2
+ class ResourceController < Madmin::ApplicationController
3
+ include SortHelper
4
+
5
+ before_action :set_record, except: [:index, :new, :create]
6
+ before_action :enforce_readonly, only: [:new, :create, :edit, :update, :destroy]
7
+
8
+ # Assign current_user for paper_trail gem
9
+ before_action :set_paper_trail_whodunnit, if: -> { respond_to?(:set_paper_trail_whodunnit, true) }
10
+
11
+ def index
12
+ @pagy, @records = paginate_collection(scoped_resources)
13
+
14
+ respond_to do |format|
15
+ format.html
16
+ format.json {
17
+ render json: @records.map { |r| {name: @resource.display_name(r), id: r.id} }
18
+ }
19
+ end
20
+ end
21
+
22
+ def show
23
+ end
24
+
25
+ def new
26
+ @record = resource.model.new(new_resource_params)
27
+ end
28
+
29
+ def create
30
+ @record = resource.model.new(resource_params)
31
+ if @record.save
32
+ redirect_to resource.show_path(@record)
33
+ else
34
+ render :new, status: :unprocessable_entity
35
+ end
36
+ end
37
+
38
+ def edit
39
+ end
40
+
41
+ def update
42
+ if @record.update(resource_params)
43
+ redirect_to resource.show_path(@record)
44
+ else
45
+ render :edit, status: :unprocessable_entity
46
+ end
47
+ end
48
+
49
+ def destroy
50
+ @record.destroy
51
+ redirect_to resource.index_path
52
+ end
53
+
54
+ private
55
+
56
+ def set_record
57
+ @record = resource.model_find(params[:id])
58
+ end
59
+
60
+ def resource
61
+ @resource ||= resource_name.constantize
62
+ end
63
+ helper_method :resource
64
+
65
+ def resource_name
66
+ "#{controller_path.singularize}_resource".delete_prefix("madmin/").classify
67
+ end
68
+
69
+ def scoped_resources
70
+ resources = resource.model.send(valid_scope)
71
+ resources = Madmin::Search.new(resources, resource, search_term).run
72
+
73
+ return resources if sort_column.blank?
74
+
75
+ resources.reorder(sort_column => sort_direction)
76
+ end
77
+
78
+ def paginate_collection(collection)
79
+ pagy(collection)
80
+ end
81
+
82
+ def valid_scope
83
+ scope = params.fetch(:scope, "all")
84
+ resource.scopes.include?(scope.to_sym) ? scope : :all
85
+ end
86
+
87
+ def resource_params
88
+ params.require(resource.param_key)
89
+ .permit(*resource.permitted_params)
90
+ .transform_values { |v| change_polymorphic(v) }
91
+ end
92
+
93
+ def new_resource_params
94
+ params.fetch(resource.param_key, {}).permit!
95
+ .permit(*resource.permitted_params)
96
+ .transform_values { |v| change_polymorphic(v) }
97
+ end
98
+
99
+ def change_polymorphic(data)
100
+ return data unless data.is_a?(ActionController::Parameters) && data[:type]
101
+
102
+ if data[:type] == "polymorphic"
103
+ GlobalID::Locator.locate(data[:value])
104
+ else
105
+ raise "Unrecognised param data: #{data.inspect}"
106
+ end
107
+ end
108
+
109
+ def search_term
110
+ @search_term ||= params[:q].to_s.strip
111
+ end
112
+
113
+ def enforce_readonly
114
+ redirect_to resource.index_path, alert: t("madmin.flash.readonly", name: resource.friendly_name) if resource.readonly?
115
+ end
116
+
117
+ ActiveSupport.run_load_hooks(:madmin_resource_controller, self)
118
+ end
119
+ end
@@ -0,0 +1,10 @@
1
+ module Madmin
2
+ module ApplicationHelper
3
+ include Pagy::Frontend if defined? Pagy::Frontend
4
+ include Rails.application.routes.url_helpers
5
+
6
+ def clear_search_params
7
+ resource.index_path(sort: params[:sort], direction: params[:direction])
8
+ end
9
+ end
10
+ end