@shamar/adonis 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.
- package/assets/admin.css +2 -0
- package/assets/alpine.min.js +5 -0
- package/assets/input.css +2286 -0
- package/assets/shamar-ui.js +3658 -0
- package/dist/assets-controller.d.ts +12 -0
- package/dist/assets-controller.d.ts.map +1 -0
- package/dist/assets-controller.js +32 -0
- package/dist/assets-controller.js.map +1 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +21 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +20 -0
- package/dist/configure.js.map +1 -0
- package/dist/context.d.ts +22 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/controller.d.ts +31 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +45 -0
- package/dist/controller.js.map +1 -0
- package/dist/controllers/admin_controller.d.ts +72 -0
- package/dist/controllers/admin_controller.d.ts.map +1 -0
- package/dist/controllers/admin_controller.js +1075 -0
- package/dist/controllers/admin_controller.js.map +1 -0
- package/dist/discover.d.ts +7 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +50 -0
- package/dist/discover.js.map +1 -0
- package/dist/form-state.d.ts +41 -0
- package/dist/form-state.d.ts.map +1 -0
- package/dist/form-state.js +74 -0
- package/dist/form-state.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/require_api_key_middleware.d.ts +30 -0
- package/dist/middleware/require_api_key_middleware.d.ts.map +1 -0
- package/dist/middleware/require_api_key_middleware.js +59 -0
- package/dist/middleware/require_api_key_middleware.js.map +1 -0
- package/dist/provider.d.ts +9 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +66 -0
- package/dist/provider.js.map +1 -0
- package/dist/resources/api_key_resource.d.ts +35 -0
- package/dist/resources/api_key_resource.d.ts.map +1 -0
- package/dist/resources/api_key_resource.js +225 -0
- package/dist/resources/api_key_resource.js.map +1 -0
- package/dist/routes.d.ts +5 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +97 -0
- package/dist/routes.js.map +1 -0
- package/dist/runtime.d.ts +27 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +74 -0
- package/dist/runtime.js.map +1 -0
- package/dist/shamar/auth.d.ts +30 -0
- package/dist/shamar/auth.d.ts.map +1 -0
- package/dist/shamar/auth.js +218 -0
- package/dist/shamar/auth.js.map +1 -0
- package/dist/shamar/branding.d.ts +24 -0
- package/dist/shamar/branding.d.ts.map +1 -0
- package/dist/shamar/branding.js +91 -0
- package/dist/shamar/branding.js.map +1 -0
- package/dist/shamar/display-relations.d.ts +7 -0
- package/dist/shamar/display-relations.d.ts.map +1 -0
- package/dist/shamar/display-relations.js +89 -0
- package/dist/shamar/display-relations.js.map +1 -0
- package/dist/shamar/list-headers.d.ts +38 -0
- package/dist/shamar/list-headers.d.ts.map +1 -0
- package/dist/shamar/list-headers.js +218 -0
- package/dist/shamar/list-headers.js.map +1 -0
- package/dist/shamar/list-query.d.ts +229 -0
- package/dist/shamar/list-query.d.ts.map +1 -0
- package/dist/shamar/list-query.js +564 -0
- package/dist/shamar/list-query.js.map +1 -0
- package/dist/shamar/menu.d.ts +48 -0
- package/dist/shamar/menu.d.ts.map +1 -0
- package/dist/shamar/menu.js +93 -0
- package/dist/shamar/menu.js.map +1 -0
- package/dist/shamar/paths.d.ts +5 -0
- package/dist/shamar/paths.d.ts.map +1 -0
- package/dist/shamar/paths.js +16 -0
- package/dist/shamar/paths.js.map +1 -0
- package/dist/shamar/relation-fields.d.ts +54 -0
- package/dist/shamar/relation-fields.d.ts.map +1 -0
- package/dist/shamar/relation-fields.js +94 -0
- package/dist/shamar/relation-fields.js.map +1 -0
- package/dist/shamar/resource-actions.d.ts +9 -0
- package/dist/shamar/resource-actions.d.ts.map +1 -0
- package/dist/shamar/resource-actions.js +42 -0
- package/dist/shamar/resource-actions.js.map +1 -0
- package/dist/shamar/view-context.d.ts +79 -0
- package/dist/shamar/view-context.d.ts.map +1 -0
- package/dist/shamar/view-context.js +74 -0
- package/dist/shamar/view-context.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/resources/views/shamar/dashboard.edge +24 -0
- package/resources/views/shamar/form.edge +87 -0
- package/resources/views/shamar/index.edge +149 -0
- package/resources/views/shamar/partials/boolean-value.edge +25 -0
- package/resources/views/shamar/partials/csrf.edge +1 -0
- package/resources/views/shamar/partials/detail-entry.edge +72 -0
- package/resources/views/shamar/partials/dialog-host.edge +125 -0
- package/resources/views/shamar/partials/field-affix-icon.edge +23 -0
- package/resources/views/shamar/partials/field-input.edge +585 -0
- package/resources/views/shamar/partials/field-relation-checkbox.edge +104 -0
- package/resources/views/shamar/partials/field-relation-m2m.edge +110 -0
- package/resources/views/shamar/partials/field-relation-m2o.edge +116 -0
- package/resources/views/shamar/partials/field-relation-table.edge +139 -0
- package/resources/views/shamar/partials/form-container.edge +82 -0
- package/resources/views/shamar/partials/icon.edge +14 -0
- package/resources/views/shamar/partials/list-row.edge +73 -0
- package/resources/views/shamar/partials/list-toolbar.edge +269 -0
- package/resources/views/shamar/partials/nav-mobile.edge +21 -0
- package/resources/views/shamar/partials/nav.edge +58 -0
- package/resources/views/shamar/partials/page-heading.edge +130 -0
- package/resources/views/shamar/partials/pagination.edge +73 -0
- package/resources/views/shamar/partials/record-pager.edge +37 -0
- package/resources/views/shamar/partials/schema-node.edge +289 -0
- package/resources/views/shamar/partials/shell-close.edge +20 -0
- package/resources/views/shamar/partials/shell-open.edge +51 -0
- package/resources/views/shamar/partials/toast.edge +5 -0
- package/resources/views/shamar/partials/topbar.edge +115 -0
- package/resources/views/shamar/show.edge +17 -0
- package/stubs/config/shamar.stub +29 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
{{--
|
|
2
|
+
Recursive Filament-style schema node.
|
|
3
|
+
Expects: node, schemaMode ('form' | 'detail'), mode (create|edit for forms), record (for detail)
|
|
4
|
+
--}}
|
|
5
|
+
@if(node.kind === 'field' && schemaMode === 'form' && node.field)
|
|
6
|
+
@let(field = node.field)
|
|
7
|
+
@if(!field.hiddenOnForm && !(field.createOnly && mode === 'edit'))
|
|
8
|
+
<div
|
|
9
|
+
class="shamar-field-cell"
|
|
10
|
+
style="{{ resolveGridItemStyle(field, 2) }}"
|
|
11
|
+
x-show="isVisible('{{ field.name }}')"
|
|
12
|
+
>
|
|
13
|
+
@include('shamar::partials/field-input')
|
|
14
|
+
</div>
|
|
15
|
+
@endif
|
|
16
|
+
|
|
17
|
+
@elseif(node.kind === 'entry' && schemaMode === 'detail' && node.entry)
|
|
18
|
+
@let(field = node.entry)
|
|
19
|
+
@include('shamar::partials/detail-entry')
|
|
20
|
+
|
|
21
|
+
@elseif(node.kind === 'placeholder')
|
|
22
|
+
<div class="shamar-placeholder" style="{{ resolveGridItemStyle(node, 2) }}">
|
|
23
|
+
@if(node.title)
|
|
24
|
+
<div class="shamar-placeholder__label">{{ node.title }}</div>
|
|
25
|
+
@endif
|
|
26
|
+
@if(node.content)
|
|
27
|
+
<div class="shamar-placeholder__content">{{ node.content }}</div>
|
|
28
|
+
@endif
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
@elseif(node.kind === 'callout')
|
|
32
|
+
<div class="shamar-callout shamar-callout--{{ node.status ?? 'info' }}" style="{{ resolveGridItemStyle(node, 2) }}">
|
|
33
|
+
@if(node.title)
|
|
34
|
+
<div class="shamar-callout__title">{{ node.title }}</div>
|
|
35
|
+
@endif
|
|
36
|
+
@if(node.content || node.description)
|
|
37
|
+
<div class="shamar-callout__body">{{ node.content ?? node.description }}</div>
|
|
38
|
+
@endif
|
|
39
|
+
@if(node.children && node.children.length)
|
|
40
|
+
<div class="shamar-callout__children">
|
|
41
|
+
@each(childNode in node.children)
|
|
42
|
+
@!component('shamar::partials/schema-node', {
|
|
43
|
+
node: childNode,
|
|
44
|
+
schemaMode: schemaMode,
|
|
45
|
+
mode: mode,
|
|
46
|
+
record: record,
|
|
47
|
+
formErrors: formErrors,
|
|
48
|
+
relationUi: relationUi,
|
|
49
|
+
})
|
|
50
|
+
@endeach
|
|
51
|
+
</div>
|
|
52
|
+
@endif
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
@elseif(node.kind === 'empty_state')
|
|
56
|
+
<div class="shamar-empty-state" style="{{ resolveGridItemStyle(node, 2) }}">
|
|
57
|
+
@if(node.title)
|
|
58
|
+
<h3 class="shamar-empty-state__title">{{ node.title }}</h3>
|
|
59
|
+
@endif
|
|
60
|
+
@if(node.description)
|
|
61
|
+
<p class="shamar-empty-state__description">{{ node.description }}</p>
|
|
62
|
+
@endif
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
@elseif(node.kind === 'tabs')
|
|
66
|
+
<div
|
|
67
|
+
class="shamar-tabs{{ node.card !== false ? ' shamar-tabs--contained' : '' }}{{ node.vertical ? ' shamar-tabs--vertical' : '' }}"
|
|
68
|
+
style="{{ resolveGridItemStyle(node, 2) }}"
|
|
69
|
+
x-data='shamarTabs({{ node.activeTab ?? 1 }})'
|
|
70
|
+
>
|
|
71
|
+
<div class="shamar-tabs__list" role="tablist">
|
|
72
|
+
@each((tab, tabIndex) in (node.children || []))
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
class="shamar-tabs__tab"
|
|
76
|
+
role="tab"
|
|
77
|
+
:class="{ 'is-active': active === {{ tabIndex + 1 }} }"
|
|
78
|
+
@click="active = {{ tabIndex + 1 }}"
|
|
79
|
+
>
|
|
80
|
+
<span>{{ tab.title || ('Tab ' + (tabIndex + 1)) }}</span>
|
|
81
|
+
@if(tab.badge !== undefined && tab.badge !== null)
|
|
82
|
+
<span class="shamar-tabs__badge">{{ tab.badge }}</span>
|
|
83
|
+
@endif
|
|
84
|
+
</button>
|
|
85
|
+
@endeach
|
|
86
|
+
</div>
|
|
87
|
+
<div class="shamar-tabs__panels">
|
|
88
|
+
@each((tab, tabIndex) in (node.children || []))
|
|
89
|
+
<div class="shamar-tabs__panel" x-show="active === {{ tabIndex + 1 }}" role="tabpanel">
|
|
90
|
+
<div
|
|
91
|
+
class="shamar-form-grid{{ tab.dense ? ' is-dense' : '' }}{{ tab.gap === false ? ' is-gapless' : '' }}"
|
|
92
|
+
style="--shamar-form-cols: {{ tab.columns ?? node.columns ?? 2 }}"
|
|
93
|
+
>
|
|
94
|
+
@each(childNode in (tab.children || []))
|
|
95
|
+
@!component('shamar::partials/schema-node', {
|
|
96
|
+
node: childNode,
|
|
97
|
+
schemaMode: schemaMode,
|
|
98
|
+
mode: mode,
|
|
99
|
+
record: record,
|
|
100
|
+
formErrors: formErrors,
|
|
101
|
+
relationUi: relationUi,
|
|
102
|
+
})
|
|
103
|
+
@endeach
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
@endeach
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
@elseif(node.kind === 'wizard')
|
|
111
|
+
<div
|
|
112
|
+
class="shamar-wizard{{ node.card !== false ? ' shamar-card rounded-xl' : '' }}"
|
|
113
|
+
style="{{ resolveGridItemStyle(node, 2) }}"
|
|
114
|
+
x-data='shamarWizard({{ (node.children || []).length }})'
|
|
115
|
+
>
|
|
116
|
+
<ol class="shamar-wizard__steps">
|
|
117
|
+
@each((step, stepIndex) in (node.children || []))
|
|
118
|
+
<li class="shamar-wizard__step" :class="{ 'is-active': step === {{ stepIndex + 1 }}, 'is-done': step > {{ stepIndex + 1 }} }">
|
|
119
|
+
<span class="shamar-wizard__index">{{ stepIndex + 1 }}</span>
|
|
120
|
+
<span class="shamar-wizard__label">{{ step.title || ('Step ' + (stepIndex + 1)) }}</span>
|
|
121
|
+
</li>
|
|
122
|
+
@endeach
|
|
123
|
+
</ol>
|
|
124
|
+
<div class="shamar-wizard__panels">
|
|
125
|
+
@each((step, stepIndex) in (node.children || []))
|
|
126
|
+
<div class="shamar-wizard__panel" x-show="step === {{ stepIndex + 1 }}">
|
|
127
|
+
@if(step.description)
|
|
128
|
+
<p class="shamar-wizard__description">{{ step.description }}</p>
|
|
129
|
+
@endif
|
|
130
|
+
<div
|
|
131
|
+
class="shamar-form-grid{{ step.dense ? ' is-dense' : '' }}{{ step.gap === false ? ' is-gapless' : '' }}"
|
|
132
|
+
style="--shamar-form-cols: {{ step.columns ?? 2 }}"
|
|
133
|
+
>
|
|
134
|
+
@each(childNode in (step.children || []))
|
|
135
|
+
@!component('shamar::partials/schema-node', {
|
|
136
|
+
node: childNode,
|
|
137
|
+
schemaMode: schemaMode,
|
|
138
|
+
mode: mode,
|
|
139
|
+
record: record,
|
|
140
|
+
formErrors: formErrors,
|
|
141
|
+
relationUi: relationUi,
|
|
142
|
+
})
|
|
143
|
+
@endeach
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
@endeach
|
|
147
|
+
</div>
|
|
148
|
+
<div class="shamar-wizard__nav">
|
|
149
|
+
<button type="button" class="shamar-wizard__btn" x-show="step > 1" @click="prev()">Previous</button>
|
|
150
|
+
<button type="button" class="shamar-wizard__btn shamar-wizard__btn--primary" x-show="step < total" @click="next()">Next</button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
@elseif(node.kind === 'flex')
|
|
155
|
+
<div
|
|
156
|
+
class="shamar-flex shamar-flex--from-{{ node.from ?? 'md' }}"
|
|
157
|
+
style="{{ resolveGridItemStyle(node, 2) }}"
|
|
158
|
+
>
|
|
159
|
+
@each(childNode in (node.children || []))
|
|
160
|
+
<div class="shamar-flex__item{{ childNode.grow === false ? ' shamar-flex__item--fixed' : '' }}">
|
|
161
|
+
@!component('shamar::partials/schema-node', {
|
|
162
|
+
node: childNode,
|
|
163
|
+
schemaMode: schemaMode,
|
|
164
|
+
mode: mode,
|
|
165
|
+
record: record,
|
|
166
|
+
formErrors: formErrors,
|
|
167
|
+
relationUi: relationUi,
|
|
168
|
+
})
|
|
169
|
+
</div>
|
|
170
|
+
@endeach
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
@elseif(node.kind === 'grid' || node.kind === 'group' || node.kind === 'plain')
|
|
174
|
+
<div
|
|
175
|
+
class="{{ node.kind === 'group' ? 'shamar-group' : (node.kind === 'grid' ? 'shamar-grid-wrap' : 'shamar-form-plain') }}"
|
|
176
|
+
style="{{ resolveGridItemStyle(node, 2) }}"
|
|
177
|
+
>
|
|
178
|
+
<div
|
|
179
|
+
class="{{ schemaMode === 'detail' ? 'shamar-detail-grid' : 'shamar-form-grid' }}{{ node.dense ? ' is-dense' : '' }}{{ node.gap === false ? ' is-gapless' : '' }}"
|
|
180
|
+
style="--shamar-form-cols: {{ node.columns || 2 }}; --shamar-detail-cols: {{ node.columns || 2 }}"
|
|
181
|
+
>
|
|
182
|
+
@each(childNode in (node.children || []))
|
|
183
|
+
@!component('shamar::partials/schema-node', {
|
|
184
|
+
node: childNode,
|
|
185
|
+
schemaMode: schemaMode,
|
|
186
|
+
mode: mode,
|
|
187
|
+
record: record,
|
|
188
|
+
formErrors: formErrors,
|
|
189
|
+
relationUi: relationUi,
|
|
190
|
+
})
|
|
191
|
+
@endeach
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
@elseif(node.kind === 'fieldset')
|
|
196
|
+
<fieldset
|
|
197
|
+
class="shamar-form-fieldset{{ node.card ? ' shamar-form-fieldset--card' : '' }}{{ node.dense ? ' is-dense' : '' }}"
|
|
198
|
+
style="{{ resolveGridItemStyle(node, 2) }}"
|
|
199
|
+
>
|
|
200
|
+
@if(node.title)
|
|
201
|
+
<legend class="shamar-form-fieldset__legend">{{ node.title }}</legend>
|
|
202
|
+
@endif
|
|
203
|
+
@if(node.description)
|
|
204
|
+
<p class="shamar-form-fieldset__description">{{ node.description }}</p>
|
|
205
|
+
@endif
|
|
206
|
+
<div
|
|
207
|
+
class="{{ schemaMode === 'detail' ? 'shamar-detail-grid' : 'shamar-form-grid' }}{{ node.gap === false ? ' is-gapless' : '' }}"
|
|
208
|
+
style="--shamar-form-cols: {{ node.columns || 2 }}; --shamar-detail-cols: {{ node.columns || 2 }}"
|
|
209
|
+
>
|
|
210
|
+
@each(childNode in (node.children || []))
|
|
211
|
+
@!component('shamar::partials/schema-node', {
|
|
212
|
+
node: childNode,
|
|
213
|
+
schemaMode: schemaMode,
|
|
214
|
+
mode: mode,
|
|
215
|
+
record: record,
|
|
216
|
+
formErrors: formErrors,
|
|
217
|
+
relationUi: relationUi,
|
|
218
|
+
})
|
|
219
|
+
@endeach
|
|
220
|
+
</div>
|
|
221
|
+
</fieldset>
|
|
222
|
+
|
|
223
|
+
@elseif(node.kind === 'section' || node.kind === 'tab' || node.kind === 'step')
|
|
224
|
+
<section
|
|
225
|
+
class="shamar-form-section rounded-xl{{ node.card !== false && node.kind === 'section' ? ' shamar-card' : '' }}{{ node.card === false && node.kind === 'section' ? ' shamar-form-section--plain' : '' }}"
|
|
226
|
+
style="{{ resolveGridItemStyle(node, 2) }}"
|
|
227
|
+
@if(node.collapsible)
|
|
228
|
+
x-data="{ open: {{ node.collapsed ? 'false' : 'true' }} }"
|
|
229
|
+
@endif
|
|
230
|
+
>
|
|
231
|
+
@if(node.title || node.icon || node.description || node.collapsible)
|
|
232
|
+
<header
|
|
233
|
+
class="shamar-form-section__header{{ node.description ? '' : ' shamar-form-section__header--centered' }}{{ node.collapsible ? ' shamar-form-section__header--collapsible' : '' }}"
|
|
234
|
+
@if(node.collapsible)
|
|
235
|
+
@click="open = !open"
|
|
236
|
+
role="button"
|
|
237
|
+
:aria-expanded="open"
|
|
238
|
+
@endif
|
|
239
|
+
>
|
|
240
|
+
@if(node.icon)
|
|
241
|
+
<span class="shamar-form-section__icon" aria-hidden="true" data-icon="{{ node.icon }}">
|
|
242
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="w-5 h-5">
|
|
243
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
|
244
|
+
</svg>
|
|
245
|
+
</span>
|
|
246
|
+
@endif
|
|
247
|
+
@if(node.title || node.description)
|
|
248
|
+
<div class="shamar-form-section__titles min-w-0">
|
|
249
|
+
@if(node.title)
|
|
250
|
+
<h3 class="shamar-form-section__title">{{ node.title }}</h3>
|
|
251
|
+
@endif
|
|
252
|
+
@if(node.description)
|
|
253
|
+
<p class="shamar-form-section__description">{{ node.description }}</p>
|
|
254
|
+
@endif
|
|
255
|
+
</div>
|
|
256
|
+
@endif
|
|
257
|
+
@if(node.collapsible)
|
|
258
|
+
<span class="shamar-form-section__chevron" aria-hidden="true" :class="{ 'is-open': open }">
|
|
259
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
|
|
260
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
|
261
|
+
</svg>
|
|
262
|
+
</span>
|
|
263
|
+
@endif
|
|
264
|
+
</header>
|
|
265
|
+
@endif
|
|
266
|
+
<div
|
|
267
|
+
class="shamar-form-section__body"
|
|
268
|
+
@if(node.collapsible)
|
|
269
|
+
x-show="open"
|
|
270
|
+
@endif
|
|
271
|
+
>
|
|
272
|
+
<div
|
|
273
|
+
class="{{ schemaMode === 'detail' ? 'shamar-detail-grid' : 'shamar-form-grid' }}{{ node.dense ? ' is-dense' : '' }}{{ node.gap === false ? ' is-gapless' : '' }}"
|
|
274
|
+
style="--shamar-form-cols: {{ node.columns || 2 }}; --shamar-detail-cols: {{ node.columns || 2 }}"
|
|
275
|
+
>
|
|
276
|
+
@each(childNode in (node.children || []))
|
|
277
|
+
@!component('shamar::partials/schema-node', {
|
|
278
|
+
node: childNode,
|
|
279
|
+
schemaMode: schemaMode,
|
|
280
|
+
mode: mode,
|
|
281
|
+
record: record,
|
|
282
|
+
formErrors: formErrors,
|
|
283
|
+
relationUi: relationUi,
|
|
284
|
+
})
|
|
285
|
+
@endeach
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
</section>
|
|
289
|
+
@endif
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
</main>
|
|
2
|
+
</div>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
@include('shamar::partials/dialog-host')
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
function shamarShell() {
|
|
9
|
+
return {
|
|
10
|
+
sidebarOpen: false,
|
|
11
|
+
toggleTheme() {
|
|
12
|
+
const root = document.documentElement;
|
|
13
|
+
const next = root.classList.toggle('dark');
|
|
14
|
+
localStorage.setItem('shamar-theme', next ? 'dark' : 'light');
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{ pageTitle }} · {{ panelTitle }}</title>
|
|
7
|
+
@if(branding.fontUrl)
|
|
8
|
+
<link rel="stylesheet" href="{{ branding.fontUrl }}" />
|
|
9
|
+
@endif
|
|
10
|
+
<link rel="stylesheet" href="{{ basePath }}/assets/admin.css" />
|
|
11
|
+
<link rel="stylesheet" href="{{ basePath }}/assets/branding.css" />
|
|
12
|
+
<meta name="csrf-token" content="{{ csrfToken }}" />
|
|
13
|
+
<script>
|
|
14
|
+
(function () {
|
|
15
|
+
const stored = localStorage.getItem('shamar-theme');
|
|
16
|
+
if (stored === 'dark' || (!stored && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
17
|
+
document.documentElement.classList.add('dark');
|
|
18
|
+
}
|
|
19
|
+
})();
|
|
20
|
+
</script>
|
|
21
|
+
<script defer src="{{ basePath }}/assets/shamar-ui.js"></script>
|
|
22
|
+
<script defer src="{{ basePath }}/assets/alpine.min.js"></script>
|
|
23
|
+
<style>[x-cloak] { display: none !important; }</style>
|
|
24
|
+
</head>
|
|
25
|
+
<body
|
|
26
|
+
class="min-h-screen bg-shamar-shell text-body antialiased flex md:flex-row"
|
|
27
|
+
x-data="shamarShell()"
|
|
28
|
+
data-shamar-base-path="{{ basePath }}"
|
|
29
|
+
>
|
|
30
|
+
<div
|
|
31
|
+
x-show="sidebarOpen"
|
|
32
|
+
x-cloak
|
|
33
|
+
x-transition.opacity
|
|
34
|
+
class="fixed inset-0 z-20 bg-black/30 md:hidden"
|
|
35
|
+
@click="sidebarOpen = false"
|
|
36
|
+
></div>
|
|
37
|
+
|
|
38
|
+
@include('shamar::partials/nav')
|
|
39
|
+
|
|
40
|
+
<div class="flex min-w-0 flex-1 flex-col min-h-screen w-full">
|
|
41
|
+
@include('shamar::partials/topbar')
|
|
42
|
+
|
|
43
|
+
<div class="flex-1 flex flex-col min-h-0 overflow-x-hidden w-full p-3 md:p-4 gap-4">
|
|
44
|
+
|
|
45
|
+
@include('shamar::partials/toast')
|
|
46
|
+
|
|
47
|
+
@if(pageTitle)
|
|
48
|
+
@include('shamar::partials/page-heading')
|
|
49
|
+
@endif
|
|
50
|
+
|
|
51
|
+
<main class="flex-1 min-h-0 overflow-auto">
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<header class="flex items-center gap-3 px-4 md:px-6 min-h-[60px] shrink-0 bg-shamar-shell sticky top-0 z-30 overflow-visible">
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
@click="sidebarOpen = true"
|
|
5
|
+
aria-label="Open menu"
|
|
6
|
+
class="md:hidden p-1.5 -ml-1.5 rounded-md text-body hover:bg-surface-hover"
|
|
7
|
+
>
|
|
8
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
9
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/>
|
|
10
|
+
</svg>
|
|
11
|
+
</button>
|
|
12
|
+
|
|
13
|
+
<div class="hidden md:flex flex-1 min-w-0 overflow-visible">
|
|
14
|
+
<nav
|
|
15
|
+
class="shamar-app-menu flex-1 min-w-0 flex flex-wrap items-center gap-1 overflow-visible"
|
|
16
|
+
x-data="{ openKey: '' }"
|
|
17
|
+
@click.outside="openKey = ''"
|
|
18
|
+
@keydown.escape.window="openKey = ''"
|
|
19
|
+
aria-label="{{ menuActiveRoot ? `${menuActiveRoot.label} menu` : 'App menu' }}"
|
|
20
|
+
>
|
|
21
|
+
@if(menuActiveRoot)
|
|
22
|
+
@if(!menuSecondary.length)
|
|
23
|
+
<span class="shrink-0 text-sm font-semibold text-heading truncate max-w-[12rem]">{{ menuActiveRoot.label }}</span>
|
|
24
|
+
@endif
|
|
25
|
+
@endif
|
|
26
|
+
|
|
27
|
+
@each(item in menuSecondary)
|
|
28
|
+
@if(item.children && item.children.length)
|
|
29
|
+
<div class="relative shrink-0">
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
@click="openKey = openKey === 'sec-{{ $index }}' ? '' : 'sec-{{ $index }}'"
|
|
33
|
+
class="flex items-center gap-1 px-2.5 py-1.5 rounded-md text-sm whitespace-nowrap {{ item.active ? 'bg-surface-inset text-fg-brand font-medium' : 'text-body hover:text-heading hover:bg-surface-hover' }}"
|
|
34
|
+
:aria-expanded="(openKey === 'sec-{{ $index }}').toString()"
|
|
35
|
+
>
|
|
36
|
+
{{ item.label }}
|
|
37
|
+
<svg class="w-3 h-3 text-body-subtle transition-transform" :class="{ 'rotate-180': openKey === 'sec-{{ $index }}' }" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
|
38
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
|
|
39
|
+
</svg>
|
|
40
|
+
</button>
|
|
41
|
+
<div
|
|
42
|
+
x-show="openKey === 'sec-{{ $index }}'"
|
|
43
|
+
x-cloak
|
|
44
|
+
x-transition
|
|
45
|
+
class="absolute left-0 top-full z-50 mt-1 min-w-[10rem] shamar-card rounded-xl shadow-lg py-1"
|
|
46
|
+
>
|
|
47
|
+
@each(child in item.children)
|
|
48
|
+
<a href="{{ child.href }}" class="block px-3 py-1.5 text-sm whitespace-nowrap {{ child.active ? 'text-fg-brand font-medium bg-surface-inset' : 'text-body hover:bg-surface-hover hover:text-heading' }}">{{ child.label }}</a>
|
|
49
|
+
@endeach
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
@else
|
|
53
|
+
<a href="{{ item.href }}" class="shrink-0 px-2.5 py-1.5 rounded-md text-sm whitespace-nowrap {{ item.active ? 'bg-surface-inset text-fg-brand font-medium' : 'text-body hover:text-heading hover:bg-surface-hover' }}">{{ item.label }}</a>
|
|
54
|
+
@endif
|
|
55
|
+
@endeach
|
|
56
|
+
</nav>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
@if(menuActiveRoot)
|
|
60
|
+
<p class="md:hidden flex-1 min-w-0 text-sm font-semibold text-heading truncate px-0.5">{{ menuActiveRoot.label }}</p>
|
|
61
|
+
@else
|
|
62
|
+
<div class="md:hidden flex-1 min-w-0" aria-hidden="true"></div>
|
|
63
|
+
@endif
|
|
64
|
+
|
|
65
|
+
<div class="flex items-center gap-2 shrink-0">
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
@click="toggleTheme()"
|
|
69
|
+
aria-label="Toggle dark mode"
|
|
70
|
+
class="p-1.5 rounded-md text-body hover:bg-surface-hover hover:text-heading"
|
|
71
|
+
>
|
|
72
|
+
<svg class="w-4 h-4 dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.8">
|
|
73
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
|
|
74
|
+
</svg>
|
|
75
|
+
<svg class="w-4 h-4 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.8">
|
|
76
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364-6.364l-.707.707M6.343 17.657l-.707.707M17.657 17.657l-.707-.707M6.343 6.343l-.707-.707M12 8a4 4 0 100 8 4 4 0 000-8z"/>
|
|
77
|
+
</svg>
|
|
78
|
+
</button>
|
|
79
|
+
|
|
80
|
+
<div class="relative" x-data="{ open: false }" @click.outside="open = false">
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
@click="open = !open"
|
|
84
|
+
aria-haspopup="true"
|
|
85
|
+
:aria-expanded="open.toString()"
|
|
86
|
+
class="flex items-center gap-2 pl-1 pr-2 py-1 rounded-md hover:bg-surface-hover"
|
|
87
|
+
>
|
|
88
|
+
<span class="w-7 h-7 rounded-full bg-fg-brand text-white text-xs font-semibold flex items-center justify-center shrink-0">{{ userInitial }}</span>
|
|
89
|
+
<span class="hidden sm:inline text-sm font-medium text-body max-w-[120px] truncate">{{ user.name }}</span>
|
|
90
|
+
<svg class="w-3 h-3 text-body-subtle hidden sm:block" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
91
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
|
|
92
|
+
</svg>
|
|
93
|
+
</button>
|
|
94
|
+
<div x-show="open" x-cloak x-transition class="absolute right-0 top-full mt-2 w-56 shamar-card rounded-xl shadow-lg overflow-hidden z-50">
|
|
95
|
+
<div class="px-4 py-3 border-b border-default">
|
|
96
|
+
<p class="text-sm font-semibold text-heading truncate">{{ user.name }}</p>
|
|
97
|
+
@if(user.email)
|
|
98
|
+
<p class="text-xs text-body-subtle">{{ user.email }}</p>
|
|
99
|
+
@endif
|
|
100
|
+
</div>
|
|
101
|
+
<div class="py-1 text-sm">
|
|
102
|
+
<a href="{{ basePath }}" class="block px-4 py-1.5 text-body hover:bg-surface-hover hover:text-heading">Admin</a>
|
|
103
|
+
@if(logoutPath)
|
|
104
|
+
<form method="post" action="{{ logoutPath }}" class="block">
|
|
105
|
+
@include('shamar::partials/csrf')
|
|
106
|
+
<button type="submit" class="w-full text-left px-4 py-1.5 text-body hover:bg-surface-hover hover:text-heading">
|
|
107
|
+
Sign out
|
|
108
|
+
</button>
|
|
109
|
+
</form>
|
|
110
|
+
@endif
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</header>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@let(mode = 'show')
|
|
2
|
+
@include('shamar::partials/shell-open')
|
|
3
|
+
|
|
4
|
+
<div class="mx-auto max-w-5xl space-y-6" data-shamar-record-view="show">
|
|
5
|
+
@each(node in detailSchema)
|
|
6
|
+
@!component('shamar::partials/schema-node', {
|
|
7
|
+
node: node,
|
|
8
|
+
schemaMode: 'detail',
|
|
9
|
+
mode: mode,
|
|
10
|
+
record: record,
|
|
11
|
+
formErrors: formErrors,
|
|
12
|
+
relationUi: relationUi,
|
|
13
|
+
})
|
|
14
|
+
@endeach
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
@include('shamar::partials/shell-close')
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{{{
|
|
2
|
+
exports({ to: app.configPath('shamar.ts') })
|
|
3
|
+
}}}
|
|
4
|
+
import { defineConfig, panel } from '@shamar/adonis'
|
|
5
|
+
// import UserResource from '#resources/user_resource'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Persistence: {{ orm }}
|
|
9
|
+
* - lucid → Lucid SQL models (@shamar/lucid)
|
|
10
|
+
* - mongoose → Mongoose models (@shamar/mongoose)
|
|
11
|
+
*
|
|
12
|
+
* App owns the DB connection lifecycle (Lucid database provider or mongoose.connect).
|
|
13
|
+
*
|
|
14
|
+
* Prefer panels + discoverResources. Legacy `path` + `resources` still works
|
|
15
|
+
* as a single default panel.
|
|
16
|
+
*/
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
orm: '{{ orm }}',
|
|
19
|
+
branding: {
|
|
20
|
+
name: 'Shamar Admin',
|
|
21
|
+
},
|
|
22
|
+
panels: [
|
|
23
|
+
panel('admin')
|
|
24
|
+
.path('/admin')
|
|
25
|
+
.discoverResources('app/resources/admin')
|
|
26
|
+
// .resources([UserResource])
|
|
27
|
+
,
|
|
28
|
+
],
|
|
29
|
+
})
|