@motor-cms/ui-admin 1.0.1-alpha.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.
Files changed (103) hide show
  1. package/README.md +77 -0
  2. package/app/components/form/inputs/CategoryTreeInput.vue +154 -0
  3. package/app/components/form/inputs/CategoryTreePicker.vue +355 -0
  4. package/app/components/form/inputs/NestedDraggable.vue +217 -0
  5. package/app/components/form/inputs/QuicklinksInput.vue +186 -0
  6. package/app/lang/de/motor-admin/CLAUDE.md +21 -0
  7. package/app/lang/de/motor-admin/ai_system_prompts.json +12 -0
  8. package/app/lang/de/motor-admin/categories.json +12 -0
  9. package/app/lang/de/motor-admin/category_trees.json +14 -0
  10. package/app/lang/de/motor-admin/clients.json +26 -0
  11. package/app/lang/de/motor-admin/config_variables.json +14 -0
  12. package/app/lang/de/motor-admin/domains.json +19 -0
  13. package/app/lang/de/motor-admin/email_templates.json +38 -0
  14. package/app/lang/de/motor-admin/global.json +5 -0
  15. package/app/lang/de/motor-admin/languages.json +16 -0
  16. package/app/lang/de/motor-admin/permissions.json +14 -0
  17. package/app/lang/de/motor-admin/roles.json +15 -0
  18. package/app/lang/de/motor-admin/users.json +22 -0
  19. package/app/lang/en/motor-admin/CLAUDE.md +7 -0
  20. package/app/lang/en/motor-admin/ai_system_prompts.json +12 -0
  21. package/app/lang/en/motor-admin/categories.json +12 -0
  22. package/app/lang/en/motor-admin/category_trees.json +14 -0
  23. package/app/lang/en/motor-admin/clients.json +26 -0
  24. package/app/lang/en/motor-admin/config_variables.json +14 -0
  25. package/app/lang/en/motor-admin/domains.json +18 -0
  26. package/app/lang/en/motor-admin/email_templates.json +33 -0
  27. package/app/lang/en/motor-admin/global.json +5 -0
  28. package/app/lang/en/motor-admin/languages.json +16 -0
  29. package/app/lang/en/motor-admin/permissions.json +14 -0
  30. package/app/lang/en/motor-admin/roles.json +15 -0
  31. package/app/lang/en/motor-admin/users.json +22 -0
  32. package/app/pages/dashboard.vue +5 -0
  33. package/app/pages/index.vue +39 -0
  34. package/app/pages/login.vue +85 -0
  35. package/app/pages/motor-admin/ai-system-prompts/CLAUDE.md +7 -0
  36. package/app/pages/motor-admin/ai-system-prompts/[id]/edit.vue +48 -0
  37. package/app/pages/motor-admin/ai-system-prompts/create.vue +40 -0
  38. package/app/pages/motor-admin/ai-system-prompts/index.vue +68 -0
  39. package/app/pages/motor-admin/category-trees/CLAUDE.md +7 -0
  40. package/app/pages/motor-admin/category-trees/[id]/CLAUDE.md +7 -0
  41. package/app/pages/motor-admin/category-trees/[id]/categories/[categoryId]/edit.vue +73 -0
  42. package/app/pages/motor-admin/category-trees/[id]/categories/create.vue +64 -0
  43. package/app/pages/motor-admin/category-trees/[id]/edit.vue +45 -0
  44. package/app/pages/motor-admin/category-trees/[id]/index.vue +81 -0
  45. package/app/pages/motor-admin/category-trees/create.vue +37 -0
  46. package/app/pages/motor-admin/category-trees/index.vue +54 -0
  47. package/app/pages/motor-admin/clients/CLAUDE.md +11 -0
  48. package/app/pages/motor-admin/clients/[id]/CLAUDE.md +11 -0
  49. package/app/pages/motor-admin/clients/[id]/edit.vue +45 -0
  50. package/app/pages/motor-admin/clients/create.vue +37 -0
  51. package/app/pages/motor-admin/clients/index.vue +46 -0
  52. package/app/pages/motor-admin/config-variables/CLAUDE.md +11 -0
  53. package/app/pages/motor-admin/config-variables/[id]/edit.vue +44 -0
  54. package/app/pages/motor-admin/config-variables/create.vue +36 -0
  55. package/app/pages/motor-admin/config-variables/index.vue +66 -0
  56. package/app/pages/motor-admin/domains/CLAUDE.md +11 -0
  57. package/app/pages/motor-admin/domains/[id]/edit.vue +54 -0
  58. package/app/pages/motor-admin/domains/create.vue +46 -0
  59. package/app/pages/motor-admin/domains/index.vue +98 -0
  60. package/app/pages/motor-admin/email-templates/CLAUDE.md +12 -0
  61. package/app/pages/motor-admin/email-templates/[id]/CLAUDE.md +7 -0
  62. package/app/pages/motor-admin/email-templates/[id]/edit.vue +56 -0
  63. package/app/pages/motor-admin/email-templates/create.vue +48 -0
  64. package/app/pages/motor-admin/email-templates/index.vue +67 -0
  65. package/app/pages/motor-admin/index.vue +12 -0
  66. package/app/pages/motor-admin/languages/CLAUDE.md +7 -0
  67. package/app/pages/motor-admin/languages/[id]/edit.vue +44 -0
  68. package/app/pages/motor-admin/languages/create.vue +36 -0
  69. package/app/pages/motor-admin/languages/index.vue +44 -0
  70. package/app/pages/motor-admin/permission-groups/CLAUDE.md +14 -0
  71. package/app/pages/motor-admin/permission-groups/[id]/CLAUDE.md +11 -0
  72. package/app/pages/motor-admin/permission-groups/[id]/edit.vue +49 -0
  73. package/app/pages/motor-admin/permission-groups/create.vue +41 -0
  74. package/app/pages/motor-admin/permission-groups/index.vue +43 -0
  75. package/app/pages/motor-admin/roles/CLAUDE.md +7 -0
  76. package/app/pages/motor-admin/roles/[id]/edit.vue +47 -0
  77. package/app/pages/motor-admin/roles/create.vue +40 -0
  78. package/app/pages/motor-admin/roles/index.vue +45 -0
  79. package/app/pages/motor-admin/theme-preview/CLAUDE.md +7 -0
  80. package/app/pages/motor-admin/theme-preview/index.vue +4801 -0
  81. package/app/pages/motor-admin/theme-preview/themes/CLAUDE.md +11 -0
  82. package/app/pages/motor-admin/theme-preview/themes/asymmetric-brutalist.md +381 -0
  83. package/app/pages/motor-admin/theme-preview/themes/bold-modern.md +231 -0
  84. package/app/pages/motor-admin/theme-preview/themes/geometric-minimal.md +778 -0
  85. package/app/pages/motor-admin/theme-preview/themes/gradient-flow.md +1057 -0
  86. package/app/pages/motor-admin/theme-preview/themes/liquid-glass.md +823 -0
  87. package/app/pages/motor-admin/theme-preview/themes/neon-amber.md +1223 -0
  88. package/app/pages/motor-admin/theme-preview/themes/neon-terminal.md +779 -0
  89. package/app/pages/motor-admin/theme-preview/themes/neon-violet.md +1134 -0
  90. package/app/pages/motor-admin/theme-preview/themes/professional-clean.md +232 -0
  91. package/app/pages/motor-admin/theme-preview/themes/refined-brutalist.md +462 -0
  92. package/app/pages/motor-admin/theme-preview/themes/wild-card.md +263 -0
  93. package/app/pages/motor-admin/users/CLAUDE.md +17 -0
  94. package/app/pages/motor-admin/users/[id]/CLAUDE.md +11 -0
  95. package/app/pages/motor-admin/users/[id]/edit.vue +83 -0
  96. package/app/pages/motor-admin/users/create.vue +40 -0
  97. package/app/pages/motor-admin/users/index.vue +66 -0
  98. package/app/pages/profile.vue +363 -0
  99. package/app/pages/search.vue +91 -0
  100. package/app/types/generated/form-meta.ts +258 -0
  101. package/app/types/generated/grid-meta.ts +172 -0
  102. package/nuxt.config.ts +1 -0
  103. package/package.json +26 -0
@@ -0,0 +1,263 @@
1
+ # Wild Card Theme: "Botanical Observatory"
2
+
3
+ ## Mood / Inspiration
4
+
5
+ **Concept:** A dark, warm admin panel inspired by the aesthetic of Victorian-era natural history museums, botanical illustration studios, and scientific observatories. Think aged leather journals, brass instruments, pressed botanical specimens, and handwritten field notes -- translated into a modern digital interface.
6
+
7
+ **Why it works as a wild card:** Nobody expects an admin panel to feel like stepping into a naturalist's study. Most admin themes oscillate between sterile corporate blue/gray and trendy neon-on-dark. This theme goes in a completely different direction: warm, textured, intimate, and scholarly. It uses a serif heading font (almost unheard of in admin UIs), earthy warm tones instead of cool neutrals, and amber accents instead of blue/purple.
8
+
9
+ **The surprise factor:** Serif typography in a dashboard. Dark backgrounds that feel warm rather than cold. An accent color (amber/gold) that evokes candlelight and brass rather than tech-startup energy. The overall effect is an admin panel that feels like a place of thoughtful craft rather than a utilitarian tool.
10
+
11
+ **Reference aesthetics:** Dark academia, botanical illustration, old-world cartography, observatory control rooms, apothecary shelving systems.
12
+
13
+ ---
14
+
15
+ ## Color Palette
16
+
17
+ ### Primary Colors
18
+
19
+ | Role | Name | Hex | OKLCH (Tailwind 4) | Tailwind Family |
20
+ |------|------|-----|---------------------|-----------------|
21
+ | **Background (deep)** | Observatory Dark | `#1C1917` | `oklch(0.205 0.006 56.043)` | `stone-900` |
22
+ | **Background (surface)** | Walnut Panel | `#292524` | `oklch(0.269 0.006 56.043)` | `stone-800` |
23
+ | **Background (elevated)** | Aged Leather | `#44403C` | `oklch(0.371 0.006 56.043)` | `stone-700` |
24
+ | **Border / Divider** | Specimen Frame | `#57534E` | `oklch(0.444 0.006 56.043)` | `stone-600` |
25
+ | **Text (primary)** | Parchment | `#FAFAF9` | `oklch(0.985 0.002 106.424)` | `stone-50` |
26
+ | **Text (secondary)** | Aged Paper | `#D6D3D1` | `oklch(0.87 0.005 56.366)` | `stone-300` |
27
+ | **Text (muted)** | Faded Ink | `#A8A29E` | `oklch(0.709 0.01 56.259)` | `stone-400` |
28
+
29
+ ### Accent Colors
30
+
31
+ | Role | Name | Hex | OKLCH (Tailwind 4) | Tailwind Family |
32
+ |------|------|-----|---------------------|-----------------|
33
+ | **Primary accent** | Brass | `#F59E0B` | `oklch(0.769 0.188 70.08)` | `amber-500` |
34
+ | **Primary accent (hover)** | Polished Brass | `#FBBF24` | `oklch(0.828 0.175 74.011)` | `amber-400` |
35
+ | **Primary accent (muted)** | Patina Gold | `#92400E` | `oklch(0.444 0.107 47.604)` | `amber-800` |
36
+ | **Success** | Botanical Green | `#10B981` | `oklch(0.696 0.17 162.48)` | `emerald-500` |
37
+ | **Success (surface)** | Pressed Leaf | `#064E3B` | `oklch(0.378 0.077 168.94)` | `emerald-900` |
38
+ | **Danger** | Sealing Wax | `#EF4444` | `oklch(0.637 0.237 25.331)` | `red-500` |
39
+ | **Warning** | Candlelight | `#EAB308` | `oklch(0.795 0.184 86.047)` | `yellow-500` |
40
+ | **Info** | Compass Blue | `#0EA5E9` | `oklch(0.685 0.169 222.979)` | `sky-500` |
41
+
42
+ ### Functional Surface Colors
43
+
44
+ | Role | Hex | Usage |
45
+ |------|-----|-------|
46
+ | **Card background** | `#292524` (stone-800) | Cards, panels, dropdowns |
47
+ | **Card background (hover)** | `#44403C` (stone-700) | Hovered cards, selected rows |
48
+ | **Input background** | `#1C1917` (stone-900) | Text inputs, selects |
49
+ | **Input border** | `#57534E` (stone-600) | Input borders at rest |
50
+ | **Input border (focus)** | `#F59E0B` (amber-500) | Focused input ring |
51
+ | **Sidebar background** | `#0C0A09` (stone-950) | Navigation sidebar |
52
+ | **Header background** | `#1C1917` (stone-900) | Top header bar |
53
+
54
+ ---
55
+
56
+ ## Font Pairing
57
+
58
+ ### Heading Font: Fraunces
59
+
60
+ **Google Fonts:** [Fraunces](https://fonts.google.com/specimen/Fraunces)
61
+
62
+ A soft-serif "Old Style" variable typeface inspired by early 20th century display faces. It has four variable axes (weight, optical size, softness, and "wonk") that give it subtle personality -- slightly quirky letterforms that feel hand-drawn without sacrificing legibility. The "wonky" alternates (leaning n/m/h shapes) add the feel of a naturalist's handwriting.
63
+
64
+ - **Use for:** Page titles, section headings, card headers, modal titles
65
+ - **Weight range:** 400-700 (use 600 for headings, 700 for page titles)
66
+ - **Optical size:** 24-48px for headings (uses display-optimized forms automatically)
67
+ - **WONK axis:** Set to 1 for headings to activate playful alternates
68
+ - **SOFT axis:** Set to 50 for a balanced warmth (not too sharp, not too bubbly)
69
+
70
+ **Why it's unexpected:** Serif fonts in admin panels are almost taboo. Fraunces breaks that rule with character and warmth while remaining perfectly legible. Its variable "wonk" axis adds a naturalist's touch that reinforces the botanical theme.
71
+
72
+ ### Body Font: Instrument Sans
73
+
74
+ **Google Fonts:** [Instrument Sans](https://fonts.google.com/specimen/Instrument+Sans)
75
+
76
+ A precise, legible sans-serif with subtle playfulness. Clean enough for data-dense tables and forms, but with enough character to complement Fraunces. Features 12 stylistic sets for fine-tuning.
77
+
78
+ - **Use for:** Body text, form labels, table data, navigation items, buttons
79
+ - **Weight range:** 400-600 (400 for body, 500 for labels, 600 for buttons)
80
+ - **Size:** 14-16px for body, 12-13px for captions and metadata
81
+
82
+ ### Monospace Font: JetBrains Mono
83
+
84
+ **Google Fonts:** [JetBrains Mono](https://fonts.google.com/specimen/JetBrains+Mono)
85
+
86
+ For code snippets, config values, and technical identifiers. Its ligatures and clear character differentiation fit the "precision instruments" sub-theme.
87
+
88
+ - **Use for:** Code blocks, IDs, technical values
89
+ - **Size:** 13-14px
90
+
91
+ ### Google Fonts Import URL
92
+
93
+ ```
94
+ https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,400..700,50,1;1,9..144,400..700,50,1&family=Instrument+Sans:ital,wght@0,400..700;1,400..700&family=JetBrains+Mono:wght@400;500&display=swap
95
+ ```
96
+
97
+ ### CSS Font Stack
98
+
99
+ ```css
100
+ :root {
101
+ --font-heading: 'Fraunces', Georgia, 'Times New Roman', serif;
102
+ --font-body: 'Instrument Sans', 'Segoe UI', system-ui, sans-serif;
103
+ --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Component Styling Notes
110
+
111
+ ### What Makes This Theme Feel Different
112
+
113
+ 1. **Serif headings in a sans-serif world.** Every page title and section header uses Fraunces, creating immediate visual distinction from every other admin panel. The slight "wonkiness" of the letterforms adds warmth and humanity.
114
+
115
+ 2. **Warm dark mode.** Most dark themes use cool blue-grays (slate, zinc). This theme uses stone -- warm brownish grays that feel like dark wood and leather rather than a server room. The difference is subtle but profoundly affects mood.
116
+
117
+ 3. **Amber instead of blue.** The primary accent is amber/gold instead of the ubiquitous blue. Buttons glow like brass fittings. Focus rings feel like candlelight. Links shimmer rather than screech.
118
+
119
+ 4. **Subtle texture hints.** Consider a very faint noise texture overlay (opacity 2-3%) on the sidebar background to evoke aged paper. This is optional but reinforces the tactile quality.
120
+
121
+ 5. **Generous spacing.** Slightly more padding than typical admin panels (think 6 instead of 4, 8 instead of 6 in Tailwind units). The theme should feel unhurried and considered, like a well-organized specimen cabinet.
122
+
123
+ ### Border Radius
124
+
125
+ - **Small elements** (buttons, inputs, badges): `rounded-md` (6px) -- not fully sharp, not overly rounded
126
+ - **Cards and panels**: `rounded-lg` (8px)
127
+ - **Modals and overlays**: `rounded-xl` (12px)
128
+ - **Avatars**: `rounded-full` (keep circular)
129
+ - **No pill shapes** -- avoid `rounded-full` on buttons; it feels too playful for the scholarly tone
130
+
131
+ ### Shadows
132
+
133
+ Use warm-tinted shadows rather than default neutral ones:
134
+
135
+ ```css
136
+ --shadow-sm: 0 1px 2px 0 rgba(28, 25, 23, 0.3);
137
+ --shadow-md: 0 4px 6px -1px rgba(28, 25, 23, 0.4), 0 2px 4px -2px rgba(28, 25, 23, 0.3);
138
+ --shadow-lg: 0 10px 15px -3px rgba(28, 25, 23, 0.5), 0 4px 6px -4px rgba(28, 25, 23, 0.4);
139
+ ```
140
+
141
+ ### Buttons
142
+
143
+ | Variant | Background | Text | Border | Hover |
144
+ |---------|-----------|------|--------|-------|
145
+ | **Primary** | `amber-500` | `stone-900` | none | `amber-400` bg |
146
+ | **Secondary** | `stone-700` | `stone-200` | `stone-600` 1px | `stone-600` bg |
147
+ | **Ghost** | transparent | `stone-300` | none | `stone-800` bg |
148
+ | **Danger** | `red-500/10` | `red-400` | `red-500/20` 1px | `red-500/20` bg |
149
+
150
+ Primary buttons with dark text on amber background create an effect resembling engraved brass plates.
151
+
152
+ ### Tables
153
+
154
+ - **Header row:** `stone-800` background, `stone-300` text, `font-medium` in Instrument Sans
155
+ - **Body rows:** `stone-900` background, `stone-200` text
156
+ - **Alternating rows:** Alternate between `stone-900` and `stone-900/50` (very subtle)
157
+ - **Hover row:** `stone-800` background with left border accent in `amber-500` (2px)
158
+ - **Selected row:** `amber-500/5` background with `amber-500` left border (2px)
159
+
160
+ ### Sidebar Navigation
161
+
162
+ - **Background:** `stone-950` (deepest dark)
163
+ - **Nav item (default):** `stone-400` text, no background
164
+ - **Nav item (hover):** `stone-200` text, `stone-800` background
165
+ - **Nav item (active):** `amber-500` text, `amber-500/10` background, `amber-500` left border (2px)
166
+ - **Section headings:** Fraunces font at 11px, `stone-500` text, uppercase tracking-wider
167
+ - **Dividers:** `stone-800` with 1px height
168
+
169
+ ### Form Inputs
170
+
171
+ - **Background:** `stone-900`
172
+ - **Border:** 1px `stone-600`
173
+ - **Text:** `stone-100`
174
+ - **Placeholder:** `stone-500`
175
+ - **Focus:** `amber-500` ring (2px), `amber-500/10` background tint
176
+ - **Label:** `stone-300`, Instrument Sans 500 weight, 13px
177
+
178
+ ### Cards
179
+
180
+ - **Background:** `stone-800`
181
+ - **Border:** 1px `stone-700`
182
+ - **Header text:** Fraunces 600, `stone-100`
183
+ - **Body text:** Instrument Sans 400, `stone-300`
184
+ - **Footer:** `stone-800` with top border `stone-700`
185
+
186
+ ### Badges / Tags
187
+
188
+ - **Default:** `stone-700` bg, `stone-300` text
189
+ - **Success:** `emerald-900` bg, `emerald-400` text
190
+ - **Warning:** `amber-900` bg, `amber-400` text
191
+ - **Danger:** `red-900` bg, `red-400` text
192
+ - **Info:** `sky-900` bg, `sky-400` text
193
+ - **Style:** `rounded-md`, `text-xs`, `font-medium` (Instrument Sans)
194
+
195
+ ### Toasts / Notifications
196
+
197
+ Follow badge color patterns but with a left border accent (3px) in the status color. Background uses the status color at 5% opacity over `stone-800`.
198
+
199
+ ---
200
+
201
+ ## Implementation Notes for Tailwind / NuxtUI 4
202
+
203
+ ### CSS Custom Properties
204
+
205
+ ```css
206
+ :root {
207
+ /* Botanical Observatory Theme */
208
+ --ui-bg: oklch(0.205 0.006 56.043); /* stone-900 */
209
+ --ui-bg-elevated: oklch(0.269 0.006 56.043); /* stone-800 */
210
+ --ui-bg-muted: oklch(0.371 0.006 56.043); /* stone-700 */
211
+ --ui-border: oklch(0.444 0.006 56.043); /* stone-600 */
212
+ --ui-text: oklch(0.985 0.002 106.424); /* stone-50 */
213
+ --ui-text-muted: oklch(0.709 0.01 56.259); /* stone-400 */
214
+ --ui-primary: oklch(0.769 0.188 70.08); /* amber-500 */
215
+ --ui-primary-hover: oklch(0.828 0.175 74.011); /* amber-400 */
216
+ }
217
+ ```
218
+
219
+ ### NuxtUI 4 App Config Theme Mapping
220
+
221
+ ```typescript
222
+ // app.config.ts
223
+ export default defineAppConfig({
224
+ ui: {
225
+ colors: {
226
+ primary: 'amber',
227
+ secondary: 'stone',
228
+ success: 'emerald',
229
+ info: 'sky',
230
+ warning: 'yellow',
231
+ error: 'red',
232
+ neutral: 'stone' // warm neutrals throughout
233
+ }
234
+ }
235
+ })
236
+ ```
237
+
238
+ ### Key Tailwind Utility Classes
239
+
240
+ ```
241
+ /* Page background */ bg-stone-900
242
+ /* Card surface */ bg-stone-800 border border-stone-700
243
+ /* Primary button */ bg-amber-500 text-stone-900 hover:bg-amber-400
244
+ /* Heading text */ font-[Fraunces] text-stone-50
245
+ /* Body text */ font-[Instrument_Sans] text-stone-300
246
+ /* Muted text */ text-stone-400
247
+ /* Focus ring */ focus:ring-2 focus:ring-amber-500
248
+ /* Active nav indicator */ border-l-2 border-amber-500 bg-amber-500/10
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Summary
254
+
255
+ The **Botanical Observatory** theme transforms the admin panel from a utilitarian tool into a place that feels curated and considered. By combining:
256
+
257
+ - **Warm stone darks** instead of cold slate/zinc grays
258
+ - **Amber/brass accents** instead of corporate blue
259
+ - **Fraunces serif headings** for scholarly distinction
260
+ - **Instrument Sans body** for clean readability
261
+ - **Generous spacing** and warm shadows
262
+
263
+ ...the result is an admin interface that feels like it belongs in a naturalist's mahogany-paneled study, yet remains completely functional for daily CMS operations. It's the theme equivalent of choosing a leather-bound notebook over a spiral-bound one -- both work, but one makes the work feel more meaningful.
@@ -0,0 +1,17 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Feb 12, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ |----|------|---|-------|------|
10
+ | #24263 | 3:10 PM | 🟣 | Users Create Form with Cached Select Options | ~329 |
11
+
12
+ ### Feb 18, 2026
13
+
14
+ | ID | Time | T | Title | Read |
15
+ |----|------|---|-------|------|
16
+ | #27270 | 5:27 PM | 🔵 | Users Grid Page - Bulk Actions Reference Implementation | ~244 |
17
+ </claude-mem-context>
@@ -0,0 +1,11 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ ### Feb 12, 2026
7
+
8
+ | ID | Time | T | Title | Read |
9
+ |----|------|---|-------|------|
10
+ | #24265 | 3:10 PM | 🟣 | Users Edit Form with Cached Select Options and Loading States | ~343 |
11
+ </claude-mem-context>
@@ -0,0 +1,83 @@
1
+ <!-- app/pages/motor-admin/users/[id]/edit.vue -->
2
+ <script setup lang="ts">
3
+ import { userFormMeta } from '../../../../types/generated/form-meta'
4
+ import { userEditFormConfig, userSelectOptionConfigs, userEditExtraFields } from '@motor-cms/ui-core/app/types/config/user'
5
+
6
+ definePageMeta({ layout: 'default', permission: 'users.write' })
7
+
8
+ const route = useRoute()
9
+ const { t } = useI18n()
10
+
11
+ const { fields: rawFields, schema, groups, state, loading, fetching, fetchError, formRef, selectOptions, selectOptionsLoading, onSubmit, onSaveAndContinue, onSaveAndNew, deleteRecord, deleting } = await useEntityForm({
12
+ apiEndpoint: '/api/v2/users',
13
+ routePrefix: '/motor-admin/users',
14
+ translationPrefix: 'motor-admin.users',
15
+ formMeta: userFormMeta,
16
+ formConfig: userEditFormConfig,
17
+ mode: 'edit',
18
+ id: route.params.id as string,
19
+ selectOptionConfigs: userSelectOptionConfigs,
20
+ extraFields: userEditExtraFields(t),
21
+ extraState: { change_password: false, password: '', password_confirmation: '' },
22
+ beforeSubmit: (data) => {
23
+ // Remove client-only fields from payload
24
+ delete data.change_password
25
+ delete data.password_confirmation
26
+
27
+ // Only send password if toggle is on and value is set
28
+ if (!state.change_password || !data.password) {
29
+ delete data.password
30
+ }
31
+ }
32
+ })
33
+
34
+ // Add password confirmation validation to schema
35
+ const refinedSchema = schema.superRefine((data, ctx) => {
36
+ if (data.change_password && data.password && data.password !== data.password_confirmation) {
37
+ ctx.addIssue({
38
+ code: 'custom',
39
+ path: ['password_confirmation'],
40
+ message: t('motor-admin.users.password_mismatch')
41
+ })
42
+ }
43
+ })
44
+
45
+ // Make fields reactive for toggle visibility
46
+ const fields = reactive(rawFields)
47
+
48
+ const passwordField = fields.find(f => f.key === 'password')
49
+ const confirmField = fields.find(f => f.key === 'password_confirmation')
50
+
51
+ watchEffect(() => {
52
+ if (passwordField) passwordField.hidden = !state.change_password
53
+ if (confirmField) confirmField.hidden = !state.change_password
54
+ })
55
+ </script>
56
+
57
+ <template>
58
+ <FormPage
59
+ :title="t('motor-admin.users.edit_title')"
60
+ back-route="/motor-admin/users"
61
+ :loading="fetching"
62
+ :error="fetchError"
63
+ >
64
+ <FormBase
65
+ ref="formRef"
66
+ v-model:state="state"
67
+ :fields="fields"
68
+ :schema="refinedSchema"
69
+ :groups="groups"
70
+ :select-options="selectOptions"
71
+ :select-options-loading="selectOptionsLoading"
72
+ :loading="loading"
73
+ :delete-record="deleteRecord"
74
+ :deleting="deleting"
75
+ cancel-route="/motor-admin/users"
76
+ show-save-and-continue
77
+ show-save-and-new
78
+ @submit="onSubmit"
79
+ @save-and-continue="onSaveAndContinue"
80
+ @save-and-new="onSaveAndNew"
81
+ />
82
+ </FormPage>
83
+ </template>
@@ -0,0 +1,40 @@
1
+ <!-- app/pages/motor-admin/users/create.vue -->
2
+ <script setup lang="ts">
3
+ import { userFormMeta } from '../../../types/generated/form-meta'
4
+ import { userFormConfig, userSelectOptionConfigs } from '@motor-cms/ui-core/app/types/config/user'
5
+
6
+ definePageMeta({ layout: 'default', permission: 'users.write' })
7
+
8
+ const { t } = useI18n()
9
+ const { fields, schema, groups, state, loading, selectOptions, selectOptionsLoading, formRef, onSubmit, onSaveAndNew } = await useEntityForm({
10
+ apiEndpoint: '/api/v2/users',
11
+ routePrefix: '/motor-admin/users',
12
+ translationPrefix: 'motor-admin.users',
13
+ formMeta: userFormMeta,
14
+ formConfig: userFormConfig,
15
+ mode: 'create',
16
+ selectOptionConfigs: userSelectOptionConfigs
17
+ })
18
+ </script>
19
+
20
+ <template>
21
+ <FormPage
22
+ :title="t('motor-admin.users.create_title')"
23
+ back-route="/motor-admin/users"
24
+ >
25
+ <FormBase
26
+ ref="formRef"
27
+ v-model:state="state"
28
+ :fields="fields"
29
+ :schema="schema"
30
+ :groups="groups"
31
+ :select-options="selectOptions"
32
+ :select-options-loading="selectOptionsLoading"
33
+ :loading="loading"
34
+ cancel-route="/motor-admin/users"
35
+ show-save-and-new
36
+ @submit="onSubmit"
37
+ @save-and-new="onSaveAndNew"
38
+ />
39
+ </FormPage>
40
+ </template>
@@ -0,0 +1,66 @@
1
+ <!-- app/pages/motor-admin/users/index.vue -->
2
+ <script setup lang="ts">
3
+ import type { components } from '@motor-cms/ui-core/app/types/generated/api'
4
+ import type { BulkActionDef } from '@motor-cms/ui-core/app/types/grid'
5
+ import { userMeta } from '../../../types/generated/grid-meta'
6
+
7
+ definePageMeta({ permission: 'users.read' })
8
+
9
+ type User = components['schemas']['UserResource']
10
+
11
+ const client = useSanctumClient()
12
+ const { t } = useI18n()
13
+
14
+ const columns = columnsFromMeta<User>(userMeta, t, {
15
+ pick: ['avatar', 'name', 'email', 'roles'],
16
+ overrides: {
17
+ avatar: { label: '', width: '60px', hideable: false },
18
+ email: { sortable: true }
19
+ }
20
+ })
21
+
22
+ columns.push(createdAtColumn(t))
23
+
24
+ const filters = [useClientFilter()]
25
+
26
+ const bulkActions: BulkActionDef[] = [
27
+ {
28
+ key: 'delete',
29
+ label: t('motor-core.grid.delete_selected'),
30
+ icon: 'i-lucide-trash-2',
31
+ color: 'error',
32
+ permission: 'users.delete',
33
+ confirm: count => t('motor-core.grid.confirm_delete', { count }),
34
+ handler: async (ids) => {
35
+ await client('/api/v2/users/bulk-delete', {
36
+ method: 'POST',
37
+ body: { ids }
38
+ })
39
+ }
40
+ }
41
+ ]
42
+
43
+ const { fetch: fetchUsers } = useGridData<User>('/api/v2/users')
44
+ </script>
45
+
46
+ <template>
47
+ <GridPage
48
+ :title="t('motor-admin.users.title')"
49
+ :subtitle="t('motor-admin.users.subtitle')"
50
+ add-route="/motor-admin/users/create"
51
+ :add-label="t('motor-admin.users.add')"
52
+ write-permission="users.write"
53
+ >
54
+ <GridBase
55
+ id="users-grid"
56
+ :fetch="fetchUsers"
57
+ :columns="columns"
58
+ :filters="filters"
59
+ :bulk-actions="bulkActions"
60
+ base-path="/motor-admin/users"
61
+ :row-click-to="(row: any) => `/motor-admin/users/${row.id}/edit`"
62
+ write-permission="users.write"
63
+ delete-permission="users.delete"
64
+ />
65
+ </GridPage>
66
+ </template>