athena_admin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +6 -0
- data/app/assets/fonts/athena_admin/Inter-Bold.woff2 +0 -0
- data/app/assets/fonts/athena_admin/Inter-Regular.woff2 +0 -0
- data/app/assets/fonts/athena_admin/Inter-SemiBold.woff2 +0 -0
- data/app/assets/fonts/athena_admin/JetBrainsMono-Medium.woff2 +0 -0
- data/app/assets/fonts/athena_admin/JetBrainsMono-Regular.woff2 +0 -0
- data/app/assets/stylesheets/athena_admin.css +521 -0
- data/app/views/active_admin/_html_head.html.erb +35 -0
- data/app/views/active_admin/_main_navigation.html.erb +65 -0
- data/app/views/active_admin/_site_header.html.erb +34 -0
- data/config/routes.rb +2 -0
- data/lib/athena_admin/engine.rb +11 -0
- data/lib/athena_admin/version.rb +3 -0
- data/lib/athena_admin.rb +6 -0
- data/lib/generators/athena_admin/install/install_generator.rb +51 -0
- data/lib/tasks/athena_admin_tasks.rake +4 -0
- metadata +95 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 51c701bedf6216f3398002f879be8edefe563a8bd21cf029b3f776614cd2e26a
|
|
4
|
+
data.tar.gz: 89be415ca46b8ca6ad093ca1e128a7d7b944879ef69b851df33b67dc336a84be
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c7eb73e4dab995c8665a55c55951095c7ac17e8ea25cab7eb37c80664ac2e38d800f7fe5c5c6b35207a35349cfcba71027313ae0ae4498448dbdf042e7f29e03
|
|
7
|
+
data.tar.gz: 1fe4571c0d3f47291d881c462e9bd0e24f66222eece14393cf20e2af4e58a09429b6dcceb769ef6ca4bfd3ef58f66322a7974be72f0ce2229d28983dcd88c528
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright TODO: Write your name
|
|
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
|
+
# AthenaAdmin
|
|
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 "athena_admin"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install athena_admin
|
|
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).
|
data/Rakefile
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/* Athena — Ember. Tailwind v4 design tokens + components. */
|
|
2
|
+
/* Self-hosted fonts served statically from public/athena-fonts (copied by the installer). */
|
|
3
|
+
@font-face { font-family:"Inter"; font-weight:400; font-display:swap; src:url("/athena-fonts/Inter-Regular.woff2") format("woff2"); }
|
|
4
|
+
@font-face { font-family:"Inter"; font-weight:600; font-display:swap; src:url("/athena-fonts/Inter-SemiBold.woff2") format("woff2"); }
|
|
5
|
+
@font-face { font-family:"Inter"; font-weight:700; font-display:swap; src:url("/athena-fonts/Inter-Bold.woff2") format("woff2"); }
|
|
6
|
+
@font-face { font-family:"JetBrains Mono"; font-weight:400; font-display:swap; src:url("/athena-fonts/JetBrainsMono-Regular.woff2") format("woff2"); }
|
|
7
|
+
@font-face { font-family:"JetBrains Mono"; font-weight:500; font-display:swap; src:url("/athena-fonts/JetBrainsMono-Medium.woff2") format("woff2"); }
|
|
8
|
+
|
|
9
|
+
@theme {
|
|
10
|
+
--font-sans: "Inter", system-ui, -apple-system, sans-serif;
|
|
11
|
+
--font-mono: "JetBrains Mono", "SF Mono", ui-monospace, monospace;
|
|
12
|
+
|
|
13
|
+
/* Ember — dark reference values (light/dark resolved via --athena-* below) */
|
|
14
|
+
--color-ember-bg: #0A0A0B;
|
|
15
|
+
--color-ember-panel: #100C09;
|
|
16
|
+
--color-ember-surface: #16110D;
|
|
17
|
+
--color-ember-surface2: #1C1611;
|
|
18
|
+
--color-ember-line: #241A12;
|
|
19
|
+
--color-ember-line2: #2E2118;
|
|
20
|
+
--color-ember-text: #F5EDE6;
|
|
21
|
+
--color-ember-muted: #8C8076;
|
|
22
|
+
--color-ember-dim: #5E554D;
|
|
23
|
+
--color-fire-1: #FF6A00;
|
|
24
|
+
--color-fire-2: #FF2D00;
|
|
25
|
+
--color-pos: #34D399;
|
|
26
|
+
--color-neg: #F87171;
|
|
27
|
+
--color-amber: #F59E0B;
|
|
28
|
+
|
|
29
|
+
--radius-athena: 13px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Recolor: remap Tailwind's neutral scale to WARM Ember tones (dark ends pushed to
|
|
33
|
+
Ember near-blacks). AA4's chrome uses bg-white / gray-50…950 + dark: variants, so this
|
|
34
|
+
single remap re-skins the whole admin in both modes without touching partials. */
|
|
35
|
+
@theme {
|
|
36
|
+
--color-white: #ffffff;
|
|
37
|
+
--color-gray-50: #F7F3EE;
|
|
38
|
+
--color-gray-100: #EFE9E0;
|
|
39
|
+
--color-gray-200: #E7DCCD; /* light borders */
|
|
40
|
+
--color-gray-300: #D6C8B6;
|
|
41
|
+
--color-gray-400: #A99C8E; /* muted */
|
|
42
|
+
--color-gray-500: #8A7E72;
|
|
43
|
+
--color-gray-600: #6B6055; /* light secondary text */
|
|
44
|
+
--color-gray-700: #2E2218; /* dark borders (warm) */
|
|
45
|
+
--color-gray-800: #1C1611; /* dark elevated */
|
|
46
|
+
--color-gray-900: #16110D; /* dark surface */
|
|
47
|
+
--color-gray-950: #0A0A0B; /* dark bg */
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Accent remap: AA4's chrome (focus rings, checkboxes, buttons, links) is hardwired to
|
|
51
|
+
Tailwind's `blue` scale (see activeadmin plugin.js: colors.blue.600), and scaffolds use
|
|
52
|
+
`indigo`. Remap BOTH to the Ember fire scale so every accent burns orange in one stroke.
|
|
53
|
+
600 is AA's primary accent → fire red-orange; 500 → fire orange. */
|
|
54
|
+
@theme {
|
|
55
|
+
--color-blue-50: #FFF3EC; --color-indigo-50: #FFF3EC;
|
|
56
|
+
--color-blue-100: #FFE3D2; --color-indigo-100: #FFE3D2;
|
|
57
|
+
--color-blue-200: #FFC9A8; --color-indigo-200: #FFC9A8;
|
|
58
|
+
--color-blue-300: #FFA770; --color-indigo-300: #FFA770;
|
|
59
|
+
--color-blue-400: #FF8438; --color-indigo-400: #FF8438;
|
|
60
|
+
--color-blue-500: #FF6A00; --color-indigo-500: #FF6A00; /* fire1 */
|
|
61
|
+
--color-blue-600: #F23005; --color-indigo-600: #F23005; /* fire2 — primary accent */
|
|
62
|
+
--color-blue-700: #C41F00; --color-indigo-700: #C41F00;
|
|
63
|
+
--color-blue-800: #9A1C04; --color-indigo-800: #9A1C04;
|
|
64
|
+
--color-blue-900: #7C1D0A; --color-indigo-900: #7C1D0A;
|
|
65
|
+
--color-blue-950: #430D03; --color-indigo-950: #430D03;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@layer base {
|
|
69
|
+
body { font-family: var(--font-sans); }
|
|
70
|
+
/* warm paper page background in light mode (AA body is bg-white dark:bg-gray-950) */
|
|
71
|
+
body { background-color: var(--athena-bg); }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Light is the base (html without .dark); dark tokens apply under .dark. */
|
|
75
|
+
:root {
|
|
76
|
+
--athena-bg: #FAF6F1; --athena-panel: #FFFFFF; --athena-surface: #FFFFFF;
|
|
77
|
+
--athena-surface2: #F6F0E8; --athena-line: #ECE2D5; --athena-line2: #E2D6C5;
|
|
78
|
+
--athena-text: #1A1411; --athena-muted: #8A7E72; --athena-dim: #B3A899;
|
|
79
|
+
--athena-fire1: #FF6A00; --athena-fire2: #F23005;
|
|
80
|
+
--athena-pos: #15803D; --athena-neg: #DC2626; --athena-amber: #B45309;
|
|
81
|
+
}
|
|
82
|
+
.dark {
|
|
83
|
+
--athena-bg: #0A0A0B; --athena-panel: #100C09; --athena-surface: #16110D;
|
|
84
|
+
--athena-surface2: #1C1611; --athena-line: #241A12; --athena-line2: #2E2118;
|
|
85
|
+
--athena-text: #F5EDE6; --athena-muted: #8C8076; --athena-dim: #5E554D;
|
|
86
|
+
--athena-fire1: #FF6A00; --athena-fire2: #FF2D00;
|
|
87
|
+
--athena-pos: #34D399; --athena-neg: #F87171; --athena-amber: #F59E0B;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@layer components {
|
|
91
|
+
.athena-fire-text {
|
|
92
|
+
background: linear-gradient(90deg, var(--athena-fire1), var(--athena-fire2));
|
|
93
|
+
-webkit-background-clip: text; background-clip: text; color: transparent;
|
|
94
|
+
}
|
|
95
|
+
.athena-btn-fire {
|
|
96
|
+
background: linear-gradient(90deg, var(--athena-fire1), var(--athena-fire2));
|
|
97
|
+
color: #fff; border: none; border-radius: 9px; padding: 8px 13px;
|
|
98
|
+
font-weight: 600; box-shadow: 0 6px 18px rgba(242,48,5,.30); cursor: pointer;
|
|
99
|
+
}
|
|
100
|
+
.athena-tag { font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
|
|
101
|
+
.athena-tag-posted { background: color-mix(in srgb, var(--athena-pos) 16%, transparent); color: var(--athena-pos); }
|
|
102
|
+
.athena-tag-pending { background: color-mix(in srgb, var(--athena-amber) 16%, transparent); color: var(--athena-amber); }
|
|
103
|
+
.athena-tag-vanished { background: color-mix(in srgb, var(--athena-muted) 16%, transparent); color: var(--athena-muted); }
|
|
104
|
+
.athena-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
|
|
105
|
+
.athena-card { background: var(--athena-surface); border: 1px solid var(--athena-line); border-radius: var(--radius-athena); }
|
|
106
|
+
.athena-fab {
|
|
107
|
+
position: fixed; right: 16px; bottom: 18px; width: 52px; height: 52px;
|
|
108
|
+
border-radius: 16px; display: flex; align-items: center; justify-content: center;
|
|
109
|
+
background: linear-gradient(150deg, var(--athena-fire1), var(--athena-fire2));
|
|
110
|
+
color: #fff; box-shadow: 0 10px 24px rgba(242,48,5,.45); z-index: 40;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* ── Ember Command Rail (main navigation) ─────────────────────────────────────── */
|
|
114
|
+
#main-menu.athena-rail {
|
|
115
|
+
/* ember glow bleeding down from the top of the rail */
|
|
116
|
+
background:
|
|
117
|
+
radial-gradient(135% 50% at 50% -8%, color-mix(in srgb, var(--athena-fire2) 9%, transparent), transparent 72%),
|
|
118
|
+
var(--athena-bg);
|
|
119
|
+
border-inline-end: 1px solid var(--athena-line);
|
|
120
|
+
}
|
|
121
|
+
.athena-rail-cap {
|
|
122
|
+
margin: 4px 8px 12px; font-family: var(--font-mono);
|
|
123
|
+
font-size: 10px; font-weight: 600; letter-spacing: .22em; text-transform: uppercase;
|
|
124
|
+
color: var(--athena-dim);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.athena-nav {
|
|
128
|
+
position: relative; display: flex; align-items: center; gap: 11px;
|
|
129
|
+
padding: 8px 10px; border-radius: 12px; text-decoration: none;
|
|
130
|
+
font-size: 13.5px; font-weight: 500; color: var(--athena-muted);
|
|
131
|
+
border: 1px solid transparent;
|
|
132
|
+
transition: background .18s, color .18s, border-color .18s, transform .12s, box-shadow .18s;
|
|
133
|
+
}
|
|
134
|
+
.athena-nav:hover { color: var(--athena-text); background: var(--athena-surface2); transform: translateX(2px); }
|
|
135
|
+
|
|
136
|
+
.athena-nav-ic {
|
|
137
|
+
flex: 0 0 auto; width: 31px; height: 31px; display: grid; place-items: center;
|
|
138
|
+
border-radius: 9px; color: var(--athena-muted);
|
|
139
|
+
background: var(--athena-surface2); border: 1px solid var(--athena-line2);
|
|
140
|
+
transition: all .18s;
|
|
141
|
+
}
|
|
142
|
+
.athena-nav-ic svg { width: 17px; height: 17px; }
|
|
143
|
+
.athena-nav:hover .athena-nav-ic {
|
|
144
|
+
color: var(--athena-fire1);
|
|
145
|
+
border-color: color-mix(in srgb, var(--athena-fire1) 45%, var(--athena-line2));
|
|
146
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--athena-fire1) 9%, transparent);
|
|
147
|
+
}
|
|
148
|
+
.athena-nav-label { flex: 1 1 auto; min-width: 0; }
|
|
149
|
+
.athena-nav-spark { width: 6px; height: 6px; border-radius: 99px; background: transparent; flex: 0 0 auto; }
|
|
150
|
+
|
|
151
|
+
/* Active: fire-tinted pill, glowing icon tile, glowing left bar */
|
|
152
|
+
.athena-nav.is-active {
|
|
153
|
+
color: #fff; font-weight: 600;
|
|
154
|
+
background: linear-gradient(95deg,
|
|
155
|
+
color-mix(in srgb, var(--athena-fire2) 24%, transparent),
|
|
156
|
+
color-mix(in srgb, var(--athena-fire1) 9%, transparent));
|
|
157
|
+
border-color: color-mix(in srgb, var(--athena-fire1) 26%, transparent);
|
|
158
|
+
box-shadow: 0 10px 26px -12px rgba(242, 48, 5, .75);
|
|
159
|
+
}
|
|
160
|
+
.athena-nav.is-active .athena-nav-ic {
|
|
161
|
+
color: #fff; border-color: transparent;
|
|
162
|
+
background: linear-gradient(150deg, var(--athena-fire1), var(--athena-fire2));
|
|
163
|
+
box-shadow: 0 5px 14px -3px rgba(242, 48, 5, .65);
|
|
164
|
+
}
|
|
165
|
+
.athena-nav.is-active .athena-nav-spark {
|
|
166
|
+
background: var(--athena-fire1); box-shadow: 0 0 9px 1px var(--athena-fire1);
|
|
167
|
+
}
|
|
168
|
+
.athena-nav.is-active::before {
|
|
169
|
+
content: ""; position: absolute; inset-inline-start: -3px; top: 50%; transform: translateY(-50%);
|
|
170
|
+
width: 3px; height: 22px; border-radius: 99px;
|
|
171
|
+
background: linear-gradient(180deg, var(--athena-fire1), var(--athena-fire2));
|
|
172
|
+
box-shadow: 0 0 11px 1px var(--athena-fire1);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Parent (section) rows + caret */
|
|
176
|
+
.athena-nav-parent { cursor: pointer; }
|
|
177
|
+
.athena-nav-caret { width: 16px; height: 16px; flex: 0 0 auto; color: var(--athena-dim); transition: transform .18s; }
|
|
178
|
+
|
|
179
|
+
/* Sub-navigation */
|
|
180
|
+
.athena-subnav { margin: 4px 0 2px; padding-inline-start: 18px;
|
|
181
|
+
border-inline-start: 1px solid var(--athena-line2); margin-inline-start: 23px; }
|
|
182
|
+
.athena-subnav-link {
|
|
183
|
+
display: flex; align-items: center; gap: 9px; padding: 6px 9px; border-radius: 9px;
|
|
184
|
+
font-size: 13px; color: var(--athena-muted); text-decoration: none; transition: all .15s;
|
|
185
|
+
}
|
|
186
|
+
.athena-subnav-link:hover { color: var(--athena-text); background: var(--athena-surface2); }
|
|
187
|
+
.athena-subnav-dot { width: 5px; height: 5px; border-radius: 99px; background: var(--athena-dim); flex: 0 0 auto; transition: all .15s; }
|
|
188
|
+
.athena-subnav-link.is-active { color: var(--athena-fire1); font-weight: 600; }
|
|
189
|
+
.athena-subnav-link.is-active .athena-subnav-dot { background: var(--athena-fire1); box-shadow: 0 0 8px 1px var(--athena-fire1); }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* The whole paginated collection — table + "Showing…" + download footer — is ONE ember
|
|
193
|
+
panel, so they share a single border and radius (instead of AA's outer .paginated-collection
|
|
194
|
+
box nesting around our inner table box, which produced a double, mismatched-radius border).
|
|
195
|
+
The inner .index-as-table is just the horizontal scroll container for wide tables. */
|
|
196
|
+
.paginated-collection {
|
|
197
|
+
border: 1px solid var(--athena-line2);
|
|
198
|
+
border-radius: 14px;
|
|
199
|
+
overflow: hidden;
|
|
200
|
+
background: var(--athena-surface);
|
|
201
|
+
box-shadow:
|
|
202
|
+
inset 0 1px 0 color-mix(in srgb, var(--athena-fire1) 8%, transparent),
|
|
203
|
+
0 18px 42px -28px rgba(0, 0, 0, .6);
|
|
204
|
+
}
|
|
205
|
+
.index-as-table { overflow-x: auto; }
|
|
206
|
+
/* footer rows (pagination info + downloads) sit inside the panel, divided from the table */
|
|
207
|
+
.paginated-collection-pagination,
|
|
208
|
+
.paginated-collection-footer {
|
|
209
|
+
padding: 11px 14px; border-top: 1px solid var(--athena-line);
|
|
210
|
+
font-size: 13px; color: var(--athena-muted);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Mobile/tablet-portrait: restack the index table into Ember cards. The _html_head enhancer
|
|
214
|
+
stamps each cell with data-label from its column header; here we surface those as labels. */
|
|
215
|
+
@media (max-width: 767px) {
|
|
216
|
+
/* cards provide their own borders — drop the panel chrome so they aren't double-boxed */
|
|
217
|
+
.index-as-table { overflow-x: visible; }
|
|
218
|
+
.paginated-collection { border: 0; border-radius: 0; box-shadow: none; background: transparent; overflow: visible; }
|
|
219
|
+
.paginated-collection-pagination, .paginated-collection-footer { padding-inline: 0; }
|
|
220
|
+
.index-as-table table.data-table,
|
|
221
|
+
.index-as-table table.data-table tbody { display: block; width: 100%; }
|
|
222
|
+
.index-as-table table.data-table thead { display: none; }
|
|
223
|
+
|
|
224
|
+
.index-as-table table.data-table tr {
|
|
225
|
+
display: block; margin-bottom: 12px; padding: 4px 13px;
|
|
226
|
+
background: var(--athena-surface);
|
|
227
|
+
border: 1px solid var(--athena-line); border-radius: var(--radius-athena);
|
|
228
|
+
}
|
|
229
|
+
.index-as-table table.data-table td {
|
|
230
|
+
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
|
231
|
+
padding: 9px 0; border: 0; border-bottom: 1px solid var(--athena-line);
|
|
232
|
+
text-align: end; white-space: normal;
|
|
233
|
+
}
|
|
234
|
+
.index-as-table table.data-table tr td:last-child { border-bottom: 0; }
|
|
235
|
+
.index-as-table table.data-table td::before {
|
|
236
|
+
content: attr(data-label); text-align: start;
|
|
237
|
+
font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
|
|
238
|
+
color: var(--athena-muted); flex: 0 0 auto;
|
|
239
|
+
}
|
|
240
|
+
/* selection checkbox has no column header → left-align, drop the empty label gap */
|
|
241
|
+
.index-as-table table.data-table td:not([data-label]) { justify-content: flex-start; }
|
|
242
|
+
.index-as-table table.data-table td:not([data-label])::before { content: none; }
|
|
243
|
+
|
|
244
|
+
/* Row actions become a full-width, evenly-spaced, touch-friendly button row. */
|
|
245
|
+
.index-as-table table.data-table td:has(.data-table-resource-actions) { padding-top: 4px; }
|
|
246
|
+
.index-as-table table.data-table .data-table-resource-actions {
|
|
247
|
+
display: flex; width: 100%; gap: 8px;
|
|
248
|
+
}
|
|
249
|
+
.index-as-table table.data-table .data-table-resource-actions a {
|
|
250
|
+
flex: 1; text-align: center; padding: 9px 10px; font-weight: 600;
|
|
251
|
+
border: 1px solid var(--athena-line2); border-radius: 9px;
|
|
252
|
+
background: var(--athena-surface2); text-decoration: none;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* ════ Ember component pass — "same intensity" art across the whole admin ════ */
|
|
257
|
+
/* Declared in a trailing `athena` layer so these overrides outrank ActiveAdmin's plugin
|
|
258
|
+
defaults (which live in the `components` layer and would otherwise win on layer order,
|
|
259
|
+
e.g. doubling the row border by styling both tbody>tr AND our td). */
|
|
260
|
+
@layer athena {
|
|
261
|
+
|
|
262
|
+
/* ── Top bar ─────────────────────────────────────────────────────────────────── */
|
|
263
|
+
.athena-topbar {
|
|
264
|
+
background: color-mix(in srgb, var(--athena-panel) 82%, transparent);
|
|
265
|
+
border-bottom: 1px solid var(--athena-line);
|
|
266
|
+
box-shadow:
|
|
267
|
+
inset 0 -1px 0 color-mix(in srgb, var(--athena-fire1) 16%, transparent),
|
|
268
|
+
0 8px 24px -14px rgba(0, 0, 0, .7);
|
|
269
|
+
}
|
|
270
|
+
.athena-brand-mark {
|
|
271
|
+
width: 30px; height: 30px; flex: 0 0 auto; border-radius: 9px; display: grid; place-items: center;
|
|
272
|
+
background: linear-gradient(150deg, var(--athena-fire1), var(--athena-fire2));
|
|
273
|
+
box-shadow: 0 5px 14px -3px rgba(242, 48, 5, .6), inset 0 1px 0 rgba(255, 255, 255, .28);
|
|
274
|
+
}
|
|
275
|
+
.athena-brand-mark svg { width: 17px; height: 17px; }
|
|
276
|
+
.athena-brand-word {
|
|
277
|
+
letter-spacing: .14em;
|
|
278
|
+
background: linear-gradient(90deg, var(--athena-fire1), var(--athena-fire2));
|
|
279
|
+
-webkit-background-clip: text; background-clip: text; color: transparent;
|
|
280
|
+
}
|
|
281
|
+
.athena-icon-btn {
|
|
282
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
283
|
+
width: 38px; height: 38px; border-radius: 10px; cursor: pointer;
|
|
284
|
+
color: var(--athena-muted); background: transparent; border: 1px solid transparent;
|
|
285
|
+
transition: color .16s, background .16s, border-color .16s, box-shadow .16s;
|
|
286
|
+
}
|
|
287
|
+
.athena-icon-btn:hover {
|
|
288
|
+
color: var(--athena-fire1); background: var(--athena-surface2);
|
|
289
|
+
border-color: color-mix(in srgb, var(--athena-fire1) 32%, var(--athena-line2));
|
|
290
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--athena-fire1) 8%, transparent);
|
|
291
|
+
}
|
|
292
|
+
.athena-menu {
|
|
293
|
+
background: var(--athena-surface); border: 1px solid var(--athena-line2); border-radius: 12px;
|
|
294
|
+
box-shadow: 0 20px 46px -14px rgba(0, 0, 0, .65); overflow: hidden;
|
|
295
|
+
}
|
|
296
|
+
.athena-menu-item { padding: 8px 14px; color: var(--athena-text); transition: background .14s, color .14s; }
|
|
297
|
+
.athena-menu-item:hover { background: var(--athena-surface2); color: var(--athena-fire1); }
|
|
298
|
+
.athena-menu-danger:hover { color: var(--athena-neg); }
|
|
299
|
+
|
|
300
|
+
/* ── Page header ─────────────────────────────────────────────────────────────── */
|
|
301
|
+
[data-test-page-header] {
|
|
302
|
+
background:
|
|
303
|
+
radial-gradient(130% 130% at 0% 0%, color-mix(in srgb, var(--athena-fire2) 8%, transparent), transparent 46%),
|
|
304
|
+
var(--athena-surface2);
|
|
305
|
+
border-bottom: 1px solid var(--athena-line);
|
|
306
|
+
}
|
|
307
|
+
[data-test-page-header] h2 { position: relative; padding-inline-start: 15px; }
|
|
308
|
+
[data-test-page-header] h2::before {
|
|
309
|
+
content: ""; position: absolute; inset-inline-start: 0; top: 50%; transform: translateY(-50%);
|
|
310
|
+
width: 4px; height: 1.05em; border-radius: 99px;
|
|
311
|
+
background: linear-gradient(180deg, var(--athena-fire1), var(--athena-fire2));
|
|
312
|
+
box-shadow: 0 0 11px 0 var(--athena-fire1);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* ── Buttons ─────────────────────────────────────────────────────────────────── */
|
|
316
|
+
/* Primary — fire gradient + glow (submits, filter button) */
|
|
317
|
+
.filters-form-submit,
|
|
318
|
+
input[type="submit"],
|
|
319
|
+
button[type="submit"] {
|
|
320
|
+
background: linear-gradient(95deg, var(--athena-fire1), var(--athena-fire2)) !important;
|
|
321
|
+
color: #fff !important; border: none !important; border-radius: 10px !important;
|
|
322
|
+
font-weight: 600 !important; padding: 9px 17px !important; cursor: pointer;
|
|
323
|
+
box-shadow: 0 10px 24px -8px rgba(242, 48, 5, .55) !important;
|
|
324
|
+
transition: box-shadow .16s, transform .1s !important;
|
|
325
|
+
}
|
|
326
|
+
.filters-form-submit:hover,
|
|
327
|
+
input[type="submit"]:hover,
|
|
328
|
+
button[type="submit"]:hover { box-shadow: 0 13px 32px -6px rgba(242, 48, 5, .72) !important; }
|
|
329
|
+
.filters-form-submit:active,
|
|
330
|
+
input[type="submit"]:active,
|
|
331
|
+
button[type="submit"]:active { transform: translateY(1px); }
|
|
332
|
+
|
|
333
|
+
/* Secondary — outline action item with fire hover (e.g. New User) */
|
|
334
|
+
.action-item-button {
|
|
335
|
+
display: inline-flex; align-items: center; gap: 7px;
|
|
336
|
+
padding: 8px 14px; border-radius: 10px; font-weight: 600; font-size: 13.5px;
|
|
337
|
+
color: var(--athena-text); text-decoration: none;
|
|
338
|
+
background: var(--athena-surface); border: 1px solid var(--athena-line2);
|
|
339
|
+
transition: color .16s, border-color .16s, box-shadow .16s, transform .1s;
|
|
340
|
+
}
|
|
341
|
+
.action-item-button:hover {
|
|
342
|
+
color: var(--athena-fire1);
|
|
343
|
+
border-color: color-mix(in srgb, var(--athena-fire1) 48%, var(--athena-line2));
|
|
344
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--athena-fire1) 8%, transparent);
|
|
345
|
+
transform: translateY(-1px);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* ── Index table ─────────────────────────────────────────────────────────────── */
|
|
349
|
+
.data-table { border-collapse: separate; border-spacing: 0; width: 100%; }
|
|
350
|
+
|
|
351
|
+
/* header band */
|
|
352
|
+
.data-table thead tr { background: var(--athena-surface2); }
|
|
353
|
+
.data-table thead th {
|
|
354
|
+
padding: 12px 12px; text-align: start;
|
|
355
|
+
font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
|
|
356
|
+
letter-spacing: .1em; text-transform: uppercase; color: var(--athena-muted);
|
|
357
|
+
border-bottom: 1px solid color-mix(in srgb, var(--athena-fire1) 22%, var(--athena-line2));
|
|
358
|
+
white-space: nowrap;
|
|
359
|
+
}
|
|
360
|
+
.data-table thead th a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 4px; }
|
|
361
|
+
.data-table thead th a:hover { color: var(--athena-fire1); }
|
|
362
|
+
|
|
363
|
+
/* row rhythm + hover glow with inset fire bar */
|
|
364
|
+
.data-table tbody td {
|
|
365
|
+
padding: 11px 10px; border-bottom: 1px solid var(--athena-line);
|
|
366
|
+
vertical-align: middle;
|
|
367
|
+
}
|
|
368
|
+
.data-table tbody tr:last-child td { border-bottom: 0; }
|
|
369
|
+
/* AA styles the divider on tbody>tr; we draw ours on td — kill theirs to avoid a double line */
|
|
370
|
+
.data-table tbody tr { border-bottom: 0; transition: background .14s, box-shadow .14s; }
|
|
371
|
+
.data-table tbody tr:hover { background: color-mix(in srgb, var(--athena-fire1) 7%, transparent); }
|
|
372
|
+
.data-table tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 0 var(--athena-fire1); }
|
|
373
|
+
|
|
374
|
+
/* numeric admin links read mono; the ID column becomes a fire rank chip (targeted by the
|
|
375
|
+
data-label the _html_head enhancer stamps, so it's column-position independent). */
|
|
376
|
+
.data-table tbody td > a[href*="/admin/"]:not(.data-table-resource-actions a) { font-family: var(--font-mono); }
|
|
377
|
+
.data-table tbody td[data-label="Id" i] > a {
|
|
378
|
+
display: inline-flex; align-items: center; justify-content: center; min-width: 28px;
|
|
379
|
+
padding: 2px 9px; border-radius: 7px; font-size: 12px; font-weight: 600;
|
|
380
|
+
color: var(--athena-fire1); text-decoration: none;
|
|
381
|
+
background: color-mix(in srgb, var(--athena-fire1) 12%, transparent);
|
|
382
|
+
border: 1px solid color-mix(in srgb, var(--athena-fire1) 26%, transparent);
|
|
383
|
+
transition: background .14s, box-shadow .14s;
|
|
384
|
+
}
|
|
385
|
+
.data-table tbody td[data-label="Id" i] > a:hover {
|
|
386
|
+
background: color-mix(in srgb, var(--athena-fire1) 20%, transparent);
|
|
387
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--athena-fire1) 10%, transparent);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* temporal & numeric columns → mono tabular numerics (the Ember data-console look),
|
|
391
|
+
keyed off the stamped data-label so text columns (name/email) stay in Inter. */
|
|
392
|
+
.data-table tbody td[data-label*="at" i],
|
|
393
|
+
.data-table tbody td[data-label*="date" i],
|
|
394
|
+
.data-table tbody td[data-label*="amount" i],
|
|
395
|
+
.data-table tbody td[data-label*="balance" i],
|
|
396
|
+
.data-table tbody td[data-label*="total" i],
|
|
397
|
+
.data-table tbody td[data-label*="price" i],
|
|
398
|
+
.data-table tbody td[data-label*="qty" i],
|
|
399
|
+
.data-table tbody td[data-label*="count" i],
|
|
400
|
+
.data-table tbody td[data-label*="number" i] {
|
|
401
|
+
font-family: var(--font-mono); font-variant-numeric: tabular-nums;
|
|
402
|
+
font-size: 12px; letter-spacing: -.02em; color: var(--athena-muted);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* row action links: muted ghost chips that light fire (delete → danger) on hover */
|
|
406
|
+
.data-table-resource-actions { display: inline-flex; align-items: center; gap: 3px; }
|
|
407
|
+
.data-table-resource-actions a {
|
|
408
|
+
display: inline-flex; align-items: center; padding: 4px 8px; border-radius: 7px;
|
|
409
|
+
font-size: 12.5px; font-weight: 600; text-decoration: none;
|
|
410
|
+
color: var(--athena-muted); border: 1px solid transparent;
|
|
411
|
+
transition: color .14s, background .14s, border-color .14s;
|
|
412
|
+
}
|
|
413
|
+
.data-table-resource-actions a:hover {
|
|
414
|
+
color: var(--athena-fire1);
|
|
415
|
+
background: color-mix(in srgb, var(--athena-fire1) 12%, transparent);
|
|
416
|
+
border-color: color-mix(in srgb, var(--athena-fire1) 28%, transparent);
|
|
417
|
+
}
|
|
418
|
+
.data-table-resource-actions a[data-method="delete"]:hover {
|
|
419
|
+
color: var(--athena-neg);
|
|
420
|
+
background: color-mix(in srgb, var(--athena-neg) 12%, transparent);
|
|
421
|
+
border-color: color-mix(in srgb, var(--athena-neg) 30%, transparent);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* export links → mono chip buttons (CSV / XML / JSON) */
|
|
425
|
+
div:has(> a[href$=".csv"]) { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
426
|
+
div:has(> a[href$=".csv"]) > span {
|
|
427
|
+
font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
|
|
428
|
+
letter-spacing: .12em; text-transform: uppercase; color: var(--athena-dim);
|
|
429
|
+
}
|
|
430
|
+
a[href$=".csv"], a[href$=".xml"], a[href$=".json"] {
|
|
431
|
+
display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 8px;
|
|
432
|
+
font-family: var(--font-mono); font-size: 12px; font-weight: 600; text-decoration: none;
|
|
433
|
+
color: var(--athena-muted); background: var(--athena-surface); border: 1px solid var(--athena-line2);
|
|
434
|
+
transition: color .14s, border-color .14s, box-shadow .14s, transform .1s;
|
|
435
|
+
}
|
|
436
|
+
a[href$=".csv"]:hover, a[href$=".xml"]:hover, a[href$=".json"]:hover {
|
|
437
|
+
color: var(--athena-fire1);
|
|
438
|
+
border-color: color-mix(in srgb, var(--athena-fire1) 42%, var(--athena-line2));
|
|
439
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--athena-fire1) 8%, transparent);
|
|
440
|
+
transform: translateY(-1px);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* ── Attributes table (show) → ember panel ───────────────────────────────────── */
|
|
444
|
+
.attributes-table {
|
|
445
|
+
background: var(--athena-surface); border: 1px solid var(--athena-line2);
|
|
446
|
+
border-radius: 14px; overflow: hidden;
|
|
447
|
+
box-shadow:
|
|
448
|
+
inset 0 1px 0 color-mix(in srgb, var(--athena-fire1) 9%, transparent),
|
|
449
|
+
0 18px 42px -26px rgba(0, 0, 0, .65);
|
|
450
|
+
}
|
|
451
|
+
.attributes-table th {
|
|
452
|
+
font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
|
|
453
|
+
letter-spacing: .08em; text-transform: uppercase; color: var(--athena-muted);
|
|
454
|
+
background: var(--athena-surface2);
|
|
455
|
+
}
|
|
456
|
+
.attributes-table tr + tr th, .attributes-table tr + tr td { border-top: 1px solid var(--athena-line); }
|
|
457
|
+
|
|
458
|
+
/* ── Filters panel → ember card ──────────────────────────────────────────────── */
|
|
459
|
+
.filters-form {
|
|
460
|
+
background: var(--athena-surface); border: 1px solid var(--athena-line2);
|
|
461
|
+
border-radius: 14px; padding: 16px 16px 6px;
|
|
462
|
+
box-shadow:
|
|
463
|
+
inset 0 1px 0 color-mix(in srgb, var(--athena-fire1) 8%, transparent),
|
|
464
|
+
0 18px 42px -28px rgba(0, 0, 0, .6);
|
|
465
|
+
}
|
|
466
|
+
.filters-form-title {
|
|
467
|
+
font-family: var(--font-mono); text-transform: uppercase; letter-spacing: .14em;
|
|
468
|
+
font-size: 12px; font-weight: 600; color: var(--athena-text);
|
|
469
|
+
display: flex; align-items: center; gap: 8px; margin-bottom: 14px;
|
|
470
|
+
}
|
|
471
|
+
.filters-form-title::before {
|
|
472
|
+
content: ""; width: 4px; height: 14px; border-radius: 99px;
|
|
473
|
+
background: linear-gradient(180deg, var(--athena-fire1), var(--athena-fire2));
|
|
474
|
+
box-shadow: 0 0 9px 0 var(--athena-fire1);
|
|
475
|
+
}
|
|
476
|
+
.filters-form-field { margin-bottom: 14px; }
|
|
477
|
+
.filters-form-field > label.label {
|
|
478
|
+
display: block; margin-bottom: 6px;
|
|
479
|
+
font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
|
|
480
|
+
color: var(--athena-muted);
|
|
481
|
+
}
|
|
482
|
+
/* string filter: operator select stacks above the value input (keeps each readable in the
|
|
483
|
+
narrow sidebar instead of crushing the "Contains/Equals/…" select to a sliver). */
|
|
484
|
+
.filters-form-input-group { display: flex; flex-direction: column; gap: 7px; }
|
|
485
|
+
.filters-form-input-group > select { width: 100%; font-size: 12.5px; color: var(--athena-muted); }
|
|
486
|
+
.filters-form-input-group > input { width: 100%; min-width: 0; }
|
|
487
|
+
/* date range: two inputs side by side */
|
|
488
|
+
.date_range .filters-form-input-group { flex-direction: row; gap: 8px; }
|
|
489
|
+
.date_range .filters-form-input-group input { flex: 1 1 0; width: auto; min-width: 0; }
|
|
490
|
+
/* consistent fire focus on every filter control */
|
|
491
|
+
.filters-form input:focus, .filters-form select:focus {
|
|
492
|
+
border-color: var(--athena-fire1) !important;
|
|
493
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--athena-fire1) 18%, transparent) !important;
|
|
494
|
+
}
|
|
495
|
+
/* button row */
|
|
496
|
+
.filters-form-buttons {
|
|
497
|
+
display: flex; align-items: center; gap: 12px;
|
|
498
|
+
margin-top: 4px; padding-top: 14px; border-top: 1px solid var(--athena-line);
|
|
499
|
+
}
|
|
500
|
+
.filters-form-clear {
|
|
501
|
+
font-size: 12.5px; font-weight: 600; color: var(--athena-muted); text-decoration: none;
|
|
502
|
+
transition: color .14s;
|
|
503
|
+
}
|
|
504
|
+
.filters-form-clear:hover { color: var(--athena-fire1); }
|
|
505
|
+
|
|
506
|
+
/* ── Status tags ─────────────────────────────────────────────────────────────── */
|
|
507
|
+
.status_tag, .status-tag {
|
|
508
|
+
font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
|
|
509
|
+
padding: 3px 9px; border-radius: 999px; text-transform: uppercase;
|
|
510
|
+
background: color-mix(in srgb, var(--athena-muted) 16%, transparent); color: var(--athena-muted);
|
|
511
|
+
}
|
|
512
|
+
.status_tag.yes, .status_tag.completed, .status_tag.active, .status_tag.published, .status_tag.posted {
|
|
513
|
+
background: color-mix(in srgb, var(--athena-pos) 16%, transparent); color: var(--athena-pos);
|
|
514
|
+
}
|
|
515
|
+
.status_tag.no, .status_tag.error, .status_tag.failed, .status_tag.declined {
|
|
516
|
+
background: color-mix(in srgb, var(--athena-neg) 16%, transparent); color: var(--athena-neg);
|
|
517
|
+
}
|
|
518
|
+
.status_tag.warning, .status_tag.pending {
|
|
519
|
+
background: color-mix(in srgb, var(--athena-amber) 16%, transparent); color: var(--athena-amber);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<%# Athena override of ActiveAdmin's _html_head: keeps AA's head verbatim, then appends a %>
|
|
2
|
+
<%# tiny enhancer that copies column headers onto each table cell as data-label, so the %>
|
|
3
|
+
<%# CSS card layout (max-width:640px) can label fields when the table restacks on mobile. %>
|
|
4
|
+
<%= stylesheet_link_tag "active_admin" %>
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<%= csrf_meta_tags %>
|
|
7
|
+
<%= csp_meta_tag %>
|
|
8
|
+
<% # On page load or when changing themes, best to add inline in `head` to avoid FOUC %>
|
|
9
|
+
<%= javascript_tag nonce: true do %>
|
|
10
|
+
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
11
|
+
document.documentElement.classList.add('dark')
|
|
12
|
+
} else {
|
|
13
|
+
document.documentElement.classList.remove('dark')
|
|
14
|
+
}
|
|
15
|
+
<% end %>
|
|
16
|
+
<%= javascript_importmap_tags "active_admin", importmap: ActiveAdmin.importmap %>
|
|
17
|
+
<%= javascript_tag nonce: true do %>
|
|
18
|
+
(function () {
|
|
19
|
+
function athenaLabelize() {
|
|
20
|
+
document.querySelectorAll('table.data-table').forEach(function (t) {
|
|
21
|
+
var heads = Array.prototype.map.call(t.querySelectorAll('thead th'), function (th) {
|
|
22
|
+
return th.textContent.trim();
|
|
23
|
+
});
|
|
24
|
+
t.querySelectorAll('tbody tr').forEach(function (tr) {
|
|
25
|
+
Array.prototype.forEach.call(tr.children, function (td, i) {
|
|
26
|
+
if (heads[i]) td.setAttribute('data-label', heads[i]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
document.addEventListener('DOMContentLoaded', athenaLabelize);
|
|
32
|
+
document.addEventListener('turbo:load', athenaLabelize);
|
|
33
|
+
document.addEventListener('turbo:render', athenaLabelize);
|
|
34
|
+
})();
|
|
35
|
+
<% end %>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Athena "Ember Command Rail" — artful main navigation.
|
|
3
|
+
Keeps AA's #main-menu container + translate classes intact (Flowbite drawer toggle),
|
|
4
|
+
and restyles items as icon-tile rows with a glowing fire active state.
|
|
5
|
+
%>
|
|
6
|
+
<%
|
|
7
|
+
athena_nav_icon = lambda do |label|
|
|
8
|
+
case label.to_s.strip.downcase
|
|
9
|
+
when "dashboard"
|
|
10
|
+
'<rect x="3" y="3" width="7.5" height="7.5" rx="2"/><rect x="13.5" y="3" width="7.5" height="7.5" rx="2"/><rect x="3" y="13.5" width="7.5" height="7.5" rx="2"/><rect x="13.5" y="13.5" width="7.5" height="7.5" rx="2"/>'
|
|
11
|
+
when "users", "admin users", "accounts"
|
|
12
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M15 19.5a6 6 0 0 0-12 0M9 11.25a3.75 3.75 0 1 0 0-7.5 3.75 3.75 0 0 0 0 7.5ZM21 19.5a5.99 5.99 0 0 0-4.5-5.805M15.75 3.97a3.75 3.75 0 0 1 0 7.06"/>'
|
|
13
|
+
when "transactions", "ledger"
|
|
14
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-9L21 3m0 0-4.5 4.5M21 3H7.5"/>'
|
|
15
|
+
when "loans", "charity", "budget"
|
|
16
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m-3-2.818.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33"/>'
|
|
17
|
+
when "properties", "real estate"
|
|
18
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21"/>'
|
|
19
|
+
when "businessmen", "people", "motivation"
|
|
20
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z"/>'
|
|
21
|
+
else
|
|
22
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M12 3c1.6 3.2 4.5 4.6 4.5 8.2a4.5 4.5 0 1 1-9 0c0-1.3.5-2.4 1.3-3.2.3 1.1 1 1.8 1.9 2C10.5 7.6 10.6 5.2 12 3Z"/>'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
%>
|
|
26
|
+
<div id="main-menu" class="athena-rail fixed top-0 xl:top-16 bottom-0 inset-s-0 z-40 w-72 xl:w-60 p-3 overflow-y-auto transition-transform duration-200 -translate-x-full xl:translate-x-0" tabindex="-1">
|
|
27
|
+
<p class="athena-rail-cap">Navigation</p>
|
|
28
|
+
<ul role="list" class="flex flex-1 flex-col gap-1">
|
|
29
|
+
<% current_menu.items(self).each do |item| %>
|
|
30
|
+
<% children = item.items(self).presence %>
|
|
31
|
+
<% url = item.url(self) %>
|
|
32
|
+
<% active = current_menu_item?(item, children: false) %>
|
|
33
|
+
<li class="athena-nav-li group" <%= "data-open" if current_menu_item?(item) %> data-item-id="<%= item.id %>">
|
|
34
|
+
<% if children %>
|
|
35
|
+
<button data-menu-button class="athena-nav athena-nav-parent w-full" aria-label="<%= t('active_admin.toggle_section') %>">
|
|
36
|
+
<span class="athena-nav-ic">
|
|
37
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><%== athena_nav_icon.call(item.label(self)) %></svg>
|
|
38
|
+
</span>
|
|
39
|
+
<span class="athena-nav-label"><%= item.label(self) %></span>
|
|
40
|
+
<svg class="athena-nav-caret group-data-open:rotate-90 rtl:-scale-x-100" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
41
|
+
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 0 1 .02-1.06L11.168 10 7.23 6.29a.75.75 0 1 1 1.04-1.08l4.5 4.25a.75.75 0 0 1 0 1.08l-4.5 4.25a.75.75 0 0 1-1.06-.02z" clip-rule="evenodd" />
|
|
42
|
+
</svg>
|
|
43
|
+
</button>
|
|
44
|
+
<ul role="list" class="athena-subnav hidden group-data-open:block">
|
|
45
|
+
<% children.each do |j| %>
|
|
46
|
+
<li data-item-id="<%= j.id %>">
|
|
47
|
+
<%= link_to j.url(self), j.html_options.merge(class: "athena-subnav-link #{'is-active' if current_menu_item?(j)}") do %>
|
|
48
|
+
<span class="athena-subnav-dot"></span><%= j.label(self) %>
|
|
49
|
+
<% end %>
|
|
50
|
+
</li>
|
|
51
|
+
<% end %>
|
|
52
|
+
</ul>
|
|
53
|
+
<% else %>
|
|
54
|
+
<%= link_to url, item.html_options.merge(class: "athena-nav #{'is-active selected' if active}") do %>
|
|
55
|
+
<span class="athena-nav-ic">
|
|
56
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><%== athena_nav_icon.call(item.label(self)) %></svg>
|
|
57
|
+
</span>
|
|
58
|
+
<span class="athena-nav-label"><%= item.label(self) %></span>
|
|
59
|
+
<span class="athena-nav-spark"></span>
|
|
60
|
+
<% end %>
|
|
61
|
+
<% end %>
|
|
62
|
+
</li>
|
|
63
|
+
<% end %>
|
|
64
|
+
</ul>
|
|
65
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<%# Athena top bar — ember gradient, flame brand mark, glowing icon buttons. Keeps all of
|
|
2
|
+
AA's functional hooks: drawer toggle, .dark-mode-toggle, #user-menu-button + #user-menu. %>
|
|
3
|
+
<div class="athena-topbar px-4 py-2 flex items-center fixed top-0 z-20 h-16 w-full backdrop-blur-md">
|
|
4
|
+
<button class="athena-icon-btn xl:hidden me-1" data-drawer-target="main-menu" data-drawer-show="main-menu" aria-controls="main-menu" aria-label="<%= t('active_admin.toggle_main_navigation_menu') %>">
|
|
5
|
+
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/></svg>
|
|
6
|
+
</button>
|
|
7
|
+
|
|
8
|
+
<div class="grow flex items-center gap-2.5">
|
|
9
|
+
<span class="athena-brand-mark" aria-hidden="true">
|
|
10
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="1.7"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3c1.7 3.3 4.7 4.7 4.7 8.4a4.7 4.7 0 1 1-9.4 0c0-1.35.55-2.5 1.35-3.3.3 1.15 1.05 1.85 2 2.05C11 7.7 11.1 5.2 12 3Z"/></svg>
|
|
11
|
+
</span>
|
|
12
|
+
<h1 data-test-site-title class="athena-brand-word text-lg font-semibold">
|
|
13
|
+
<%= title %>
|
|
14
|
+
</h1>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<button type="button" class="dark-mode-toggle athena-icon-btn me-1" aria-label="<%= t('active_admin.toggle_dark_mode') %>">
|
|
18
|
+
<svg class="hidden dark:block w-5 h-5 rtl:-scale-x-100" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.509 5.75c0-1.493.394-2.96 1.144-4.25h-.081a8.5 8.5 0 1 0 7.356 12.746A8.5 8.5 0 0 1 8.509 5.75Z"/></svg>
|
|
19
|
+
<svg class="dark:hidden w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 3V1m0 18v-2M5.05 5.05 3.636 3.636m12.728 12.728L14.95 14.95M3 10H1m18 0h-2M5.05 14.95l-1.414 1.414M16.364 3.636 14.95 5.05M14 10a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"/></svg>
|
|
20
|
+
</button>
|
|
21
|
+
|
|
22
|
+
<button id="user-menu-button" class="athena-icon-btn athena-avatar-btn" data-dropdown-toggle="user-menu" data-dropdown-offset-distance="6" data-dropdown-placement="bottom-end" aria-label="<%= t('active_admin.toggle_user_menu') %>">
|
|
23
|
+
<svg class="w-7 h-7" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/></svg>
|
|
24
|
+
</button>
|
|
25
|
+
|
|
26
|
+
<div id="user-menu" class="athena-menu z-50 hidden min-w-max focus:outline-hidden py-1.5 text-sm" aria-labelledby="user-menu-button">
|
|
27
|
+
<ul>
|
|
28
|
+
<% if current_active_admin_user? %>
|
|
29
|
+
<li><%= auto_link current_active_admin_user, class: "athena-menu-item block no-underline" %></li>
|
|
30
|
+
<li><%= link_to I18n.t("active_admin.logout"), auto_logout_link_path, class: "athena-menu-item athena-menu-danger block no-underline", data: { method: :delete } %></li>
|
|
31
|
+
<% end %>
|
|
32
|
+
</ul>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module AthenaAdmin
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
# Prepend the gem's view path so our partials (e.g. active_admin/_html_head)
|
|
4
|
+
# take precedence over ActiveAdmin's same-path partials.
|
|
5
|
+
initializer "athena_admin.prepend_views" do
|
|
6
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
7
|
+
prepend_view_path AthenaAdmin::Engine.root.join("app/views")
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/athena_admin.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require "rails/generators/base"
|
|
2
|
+
|
|
3
|
+
module AthenaAdmin
|
|
4
|
+
module Generators
|
|
5
|
+
# Wires the Athena (Ember) theme into a host ActiveAdmin 4 app:
|
|
6
|
+
# - vendors the token/component CSS into app/assets/tailwind (out of the served path)
|
|
7
|
+
# - imports it into the AA tailwind build
|
|
8
|
+
# - registers the gem's views as a tailwind content source (so partial classes scan)
|
|
9
|
+
# - copies self-hosted fonts to public/athena-fonts
|
|
10
|
+
class InstallGenerator < Rails::Generators::Base
|
|
11
|
+
desc "Install the Athena (Ember) theme into this ActiveAdmin 4 app."
|
|
12
|
+
|
|
13
|
+
AA_TW_SRC = "app/assets/tailwind/active_admin.css".freeze
|
|
14
|
+
TW_CONF = "tailwind-active_admin.config.js".freeze
|
|
15
|
+
|
|
16
|
+
def vendor_theme_css
|
|
17
|
+
css = AthenaAdmin::Engine.root.join("app/assets/stylesheets/athena_admin.css").read
|
|
18
|
+
create_file "app/assets/tailwind/athena_admin.css", css
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def import_into_build
|
|
22
|
+
unless File.exist?(AA_TW_SRC)
|
|
23
|
+
say "Skipped @import: #{AA_TW_SRC} not found — run ActiveAdmin's tailwind setup first.", :yellow
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
import_line = %(@import "./athena_admin.css";\n)
|
|
27
|
+
return if File.read(AA_TW_SRC).include?(import_line)
|
|
28
|
+
inject_into_file AA_TW_SRC, import_line, after: %(@import "tailwindcss";\n)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add_view_content_source
|
|
32
|
+
return unless File.exist?(TW_CONF)
|
|
33
|
+
return if File.read(TW_CONF).include?("athena_admin")
|
|
34
|
+
snippet = " `${execSync('bundle show athena_admin', { encoding: 'utf-8' })" \
|
|
35
|
+
".trim().split(/\\r?\\n/).pop()}/app/views/**/*.{erb,html,arb,rb}`,\n"
|
|
36
|
+
inject_into_file TW_CONF, snippet, after: "content: [\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def copy_fonts
|
|
40
|
+
src = AthenaAdmin::Engine.root.join("app/assets/fonts/athena_admin")
|
|
41
|
+
src.children.select { |c| c.to_s.end_with?(".woff2") }.sort.each do |path|
|
|
42
|
+
create_file "public/athena-fonts/#{path.basename}", File.binread(path)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def done
|
|
47
|
+
say "✓ Athena installed. Rebuild CSS: `yarn build:css` (or `bin/rails css:build`).", :green
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: athena_admin
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sergio Reyes
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: railties
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: activeadmin
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 4.0.0.beta1
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '5'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 4.0.0.beta1
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '5'
|
|
46
|
+
description: 'Athena restyles ActiveAdmin 4 with the Ember design system: orange/fire
|
|
47
|
+
accent, monospace numerics, light + dark modes.'
|
|
48
|
+
email:
|
|
49
|
+
- sdreyesg@gmail.com
|
|
50
|
+
executables: []
|
|
51
|
+
extensions: []
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- MIT-LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- Rakefile
|
|
57
|
+
- app/assets/fonts/athena_admin/Inter-Bold.woff2
|
|
58
|
+
- app/assets/fonts/athena_admin/Inter-Regular.woff2
|
|
59
|
+
- app/assets/fonts/athena_admin/Inter-SemiBold.woff2
|
|
60
|
+
- app/assets/fonts/athena_admin/JetBrainsMono-Medium.woff2
|
|
61
|
+
- app/assets/fonts/athena_admin/JetBrainsMono-Regular.woff2
|
|
62
|
+
- app/assets/stylesheets/athena_admin.css
|
|
63
|
+
- app/views/active_admin/_html_head.html.erb
|
|
64
|
+
- app/views/active_admin/_main_navigation.html.erb
|
|
65
|
+
- app/views/active_admin/_site_header.html.erb
|
|
66
|
+
- config/routes.rb
|
|
67
|
+
- lib/athena_admin.rb
|
|
68
|
+
- lib/athena_admin/engine.rb
|
|
69
|
+
- lib/athena_admin/version.rb
|
|
70
|
+
- lib/generators/athena_admin/install/install_generator.rb
|
|
71
|
+
- lib/tasks/athena_admin_tasks.rake
|
|
72
|
+
homepage: https://github.com/BarbaricCorgi/athena_admin
|
|
73
|
+
licenses:
|
|
74
|
+
- MIT
|
|
75
|
+
metadata:
|
|
76
|
+
homepage_uri: https://github.com/BarbaricCorgi/athena_admin
|
|
77
|
+
source_code_uri: https://github.com/BarbaricCorgi/athena_admin
|
|
78
|
+
rdoc_options: []
|
|
79
|
+
require_paths:
|
|
80
|
+
- lib
|
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '3.2'
|
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
requirements: []
|
|
92
|
+
rubygems_version: 4.0.10
|
|
93
|
+
specification_version: 4
|
|
94
|
+
summary: Ember — a dark-first, mobile-first ActiveAdmin 4 theme.
|
|
95
|
+
test_files: []
|