@stacksjs/defaults 0.70.362 → 0.70.363
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/ai/skills/stacks-browse/SKILL.md +2 -1
- package/ai/skills/stacks-browse/scripts/browse.ts +20 -2
- package/ai/skills/stacks-dashboard/SKILL.md +4 -0
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +4 -1
- package/app/Actions/Dashboard/Models/model-write.ts +5 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +1 -1
- package/resources/components/Buttons/Counter.stx +1 -1
- package/resources/components/Buttons/ToggleDark.stx +1 -1
- package/resources/components/Dashboard/App/RequestDetailsDialog.stx +42 -0
- package/resources/components/Dashboard/App/RequestsOverview.stx +1 -21
- package/resources/components/Dashboard/Auth/AccessTokenCreateDialog.stx +155 -0
- package/resources/components/Dashboard/Auth/AccessTokens.stx +41 -129
- package/resources/components/Dashboard/Ci/CiDashboard.stx +1 -157
- package/resources/components/Dashboard/Ci/CiRunHistoryDrawer.stx +127 -0
- package/resources/components/Dashboard/Commerce/CommerceCategoryDeleteDialog.stx +10 -8
- package/resources/components/Dashboard/Commerce/CommerceCategoryDetailsDialog.stx +22 -17
- package/resources/components/Dashboard/Commerce/CommerceCategoryDialog.stx +14 -13
- package/resources/components/Dashboard/Commerce/CommerceCouponDeleteDialog.stx +10 -7
- package/resources/components/Dashboard/Commerce/CommerceCouponDetailsDialog.stx +25 -19
- package/resources/components/Dashboard/Commerce/CommerceCouponDialog.stx +14 -11
- package/resources/components/Dashboard/Commerce/CommerceCustomerDeleteDialog.stx +10 -8
- package/resources/components/Dashboard/Commerce/CommerceCustomerDetailsDialog.stx +24 -16
- package/resources/components/Dashboard/Commerce/CommerceCustomerDialog.stx +14 -13
- package/resources/components/Dashboard/Commerce/CommerceGiftCardDeleteDialog.stx +10 -8
- package/resources/components/Dashboard/Commerce/CommerceGiftCardDetailsDialog.stx +16 -14
- package/resources/components/Dashboard/Commerce/CommerceGiftCardDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/CommerceManufacturerDeleteDialog.stx +14 -16
- package/resources/components/Dashboard/Commerce/CommerceManufacturerDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/CommerceOrderDeleteDialog.stx +10 -8
- package/resources/components/Dashboard/Commerce/CommerceOrderDetailsDialog.stx +15 -10
- package/resources/components/Dashboard/Commerce/CommerceOrderDialog.stx +14 -13
- package/resources/components/Dashboard/Commerce/CommercePosCheckoutDialog.stx +30 -25
- package/resources/components/Dashboard/Commerce/CommercePosReceiptDialog.stx +27 -20
- package/resources/components/Dashboard/Commerce/CommerceProductDeleteDialog.stx +14 -14
- package/resources/components/Dashboard/Commerce/CommerceProductDialog.stx +14 -13
- package/resources/components/Dashboard/Commerce/CommerceReviewDeleteDialog.stx +10 -18
- package/resources/components/Dashboard/Commerce/CommerceReviewEditDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/CommerceTaxDeleteDialog.stx +14 -14
- package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/CommerceTaxesTable.stx +1 -1
- package/resources/components/Dashboard/Commerce/CommerceUnitDeleteDialog.stx +14 -17
- package/resources/components/Dashboard/Commerce/CommerceUnitDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/CommerceVariantDeleteDialog.stx +10 -8
- package/resources/components/Dashboard/Commerce/CommerceVariantDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/DriverDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/DriverDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeyDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/LicenseKeyDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRateDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/ShippingRateDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZoneDeleteDialog.stx +11 -15
- package/resources/components/Dashboard/Commerce/Delivery/ShippingZoneDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/PaymentDetailsDialog.stx +16 -15
- package/resources/components/Dashboard/Commerce/PaymentRefundDialog.stx +16 -15
- package/resources/components/Dashboard/Commerce/PrintDeviceDeleteDialog.stx +10 -11
- package/resources/components/Dashboard/Commerce/PrintDeviceDetailsDialog.stx +16 -14
- package/resources/components/Dashboard/Commerce/PrintDeviceDialog.stx +17 -16
- package/resources/components/Dashboard/Commerce/PrintLogDeleteDialog.stx +10 -7
- package/resources/components/Dashboard/Commerce/PrintLogDetailsDialog.stx +13 -10
- package/resources/components/Dashboard/Commerce/ProductWaitlistDeleteDialog.stx +10 -18
- package/resources/components/Dashboard/Commerce/ProductWaitlistEntryDialog.stx +96 -105
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistDeleteDialog.stx +10 -18
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistEntryDialog.stx +92 -95
- package/resources/components/Dashboard/Content/AuthorEditorDialog.stx +94 -0
- package/resources/components/Dashboard/Content/AuthorsDashboard.stx +39 -31
- package/resources/components/Dashboard/Content/BlogDashboard.stx +87 -176
- package/resources/components/Dashboard/Content/BlogPostEditorDialog.stx +195 -0
- package/resources/components/Dashboard/Content/CommentsDashboard.stx +14 -4
- package/resources/components/Dashboard/Content/ContentTaxonomyDashboard.stx +40 -55
- package/resources/components/Dashboard/Content/CreateFolderDialog.stx +45 -0
- package/resources/components/Dashboard/Content/FileDetailsDrawer.stx +31 -0
- package/resources/components/Dashboard/Content/FileManagerDashboard.stx +47 -107
- package/resources/components/Dashboard/Content/FileUploadDialog.stx +89 -0
- package/resources/components/Dashboard/Content/PageEditorDialog.stx +93 -0
- package/resources/components/Dashboard/Content/PagesDashboard.stx +36 -26
- package/resources/components/Dashboard/Content/PostEditorDialog.stx +176 -0
- package/resources/components/Dashboard/Content/PostsDashboard.stx +40 -64
- package/resources/components/Dashboard/Content/SeoDashboard.stx +18 -84
- package/resources/components/Dashboard/Content/SeoEditorDialog.stx +108 -0
- package/resources/components/Dashboard/Content/TaxonomyEditorDialog.stx +106 -0
- package/resources/components/Dashboard/CreateRecordModal.stx +72 -83
- package/resources/components/Dashboard/Email/InboxMessageList.stx +1 -1
- package/resources/components/Dashboard/GlobalSearch.stx +3 -9
- package/resources/components/Dashboard/Infrastructure/CloudDetailsDialog.stx +2 -16
- package/resources/components/Dashboard/Infrastructure/DnsRecordDialog.stx +4 -16
- package/resources/components/Dashboard/Infrastructure/LogDetailsDialog.stx +4 -19
- package/resources/components/Dashboard/Infrastructure/MailboxDetailsDialog.stx +6 -16
- package/resources/components/Dashboard/Infrastructure/ServerInspectorDialog.stx +105 -0
- package/resources/components/Dashboard/Infrastructure/ServersDashboard.stx +5 -59
- package/resources/components/Dashboard/Jobs/JobHistory.stx +1 -11
- package/resources/components/Dashboard/Library/ComponentsDashboard.stx +11 -60
- package/resources/components/Dashboard/Library/FunctionsDashboard.stx +11 -15
- package/resources/components/Dashboard/Library/SourceScaffoldDialog.stx +95 -0
- package/resources/components/Dashboard/Management/AssignmentDrawer.stx +93 -0
- package/resources/components/Dashboard/Management/PermissionsDashboard.stx +47 -129
- package/resources/components/Dashboard/Marketing/CampaignDeliveryDialog.stx +5 -8
- package/resources/components/Dashboard/Marketing/CampaignDialog.stx +17 -17
- package/resources/components/Dashboard/Marketing/MarketingListDialog.stx +17 -17
- package/resources/components/Dashboard/Marketing/MarketingListsTable.stx +1 -1
- package/resources/components/Dashboard/Marketing/SocialPostDialog.stx +17 -16
- package/resources/components/Dashboard/Models/EditRecordModal.stx +81 -0
- package/resources/components/Dashboard/Models/ModelRecordActions.stx +30 -0
- package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +42 -94
- package/resources/components/Dashboard/Navbar.stx +5 -5
- package/resources/components/Dashboard/Notifications/NotificationDeliveryDialog.stx +40 -60
- package/resources/components/Dashboard/UI/Button.stx +4 -0
- package/resources/components/Dashboard/UI/ConfirmDialog.stx +4 -0
- package/resources/components/Dashboard/UI/Drawer.stx +114 -0
- package/resources/components/Dashboard/UI/Modal.stx +19 -12
- package/resources/components/Docs/Demo/ComboboxDemo.stx +4 -5
- package/resources/components/Docs/Demo/DropdownDemo.stx +12 -5
- package/resources/components/Docs/Demo/ListboxDemo.stx +14 -6
- package/resources/components/Docs/Demo/PopoverDemo.stx +10 -5
- package/resources/components/Docs/Demo/RadioGroupDemo.stx +14 -6
- package/resources/components/Docs/Demo/StepperDemo.stx +4 -5
- package/resources/components/Docs/TeamMember.stx +16 -9
- package/resources/components/Forum/ForumReplyForm.stx +7 -7
- package/resources/components/Forum/ForumSidebar.stx +10 -6
- package/resources/components/Marketing/Faqs.stx +5 -5
- package/resources/components/Marketing/Feature.stx +11 -6
- package/resources/components/Marketing/Layout.stx +12 -8
- package/resources/components/Marketing/Pricing.stx +11 -5
- package/resources/components/Marketing/PrimaryFeatures.stx +4 -5
- package/resources/components/Marketing/PrimaryFeatures2.stx +6 -5
- package/resources/components/Marketing/SecondaryFeatures.stx +10 -5
- package/resources/components/Marketing/Testimonials.stx +8 -5
- package/resources/views/index.stx +4 -4
- package/views/system-tray/index.stx +1 -1
|
@@ -28,13 +28,11 @@ interface CreatedTokenResponse {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{ name: 'admin', description: 'Use administrative operations' },
|
|
37
|
-
]
|
|
31
|
+
interface TokenDraft {
|
|
32
|
+
name: string
|
|
33
|
+
scopes: string[]
|
|
34
|
+
expiresInMinutes: number
|
|
35
|
+
}
|
|
38
36
|
|
|
39
37
|
const tokens = state<AccessTokenRow[]>([])
|
|
40
38
|
const loading = state(true)
|
|
@@ -44,16 +42,16 @@ const actionError = state('')
|
|
|
44
42
|
const actionMessage = state('')
|
|
45
43
|
const showCreate = state(false)
|
|
46
44
|
const creating = state(false)
|
|
47
|
-
const tokenName = state('')
|
|
48
|
-
const expiryMinutes = state('10080')
|
|
49
|
-
const selectedScopes = state<string[]>(['read'])
|
|
50
45
|
const createdToken = state('')
|
|
51
|
-
const copied = state(false)
|
|
52
46
|
const confirmingRevoke = state<number | null>(null)
|
|
53
47
|
const revoking = state<number | null>(null)
|
|
54
48
|
|
|
55
49
|
const activeCount = derived(() => tokens().filter(token => tokenStatus(token) === 'active').length)
|
|
56
50
|
const expiredCount = derived(() => tokens().filter(token => tokenStatus(token) === 'expired').length)
|
|
51
|
+
const tokenToRevoke = derived(() => tokens().find(token => token.id === confirmingRevoke()) ?? null)
|
|
52
|
+
const revokeDescription = derived(() => tokenToRevoke()
|
|
53
|
+
? `${tokenToRevoke()?.name} will immediately stop authorizing API requests.`
|
|
54
|
+
: 'This access token will immediately stop authorizing API requests.')
|
|
57
55
|
|
|
58
56
|
function tokenStatus(token: AccessTokenRow): 'active' | 'expired' {
|
|
59
57
|
if (!token.expires_at)
|
|
@@ -74,33 +72,18 @@ function formatDate(value: string | null): string {
|
|
|
74
72
|
return Number.isNaN(date.getTime()) ? 'Unknown' : date.toLocaleString()
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
function toggleScope(event: Event, scope: string): void {
|
|
78
|
-
const enabled = (event.target as HTMLInputElement).checked
|
|
79
|
-
selectedScopes.set(enabled
|
|
80
|
-
? [...new Set([...selectedScopes(), scope])]
|
|
81
|
-
: selectedScopes().filter(item => item !== scope))
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function resetCreateForm(): void {
|
|
85
|
-
tokenName.set('')
|
|
86
|
-
expiryMinutes.set('10080')
|
|
87
|
-
selectedScopes.set(['read'])
|
|
88
|
-
createdToken.set('')
|
|
89
|
-
copied.set(false)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
75
|
function openCreate(): void {
|
|
93
76
|
if (authenticationRequired())
|
|
94
77
|
return
|
|
95
78
|
|
|
96
|
-
|
|
79
|
+
createdToken.set('')
|
|
97
80
|
actionError.set('')
|
|
98
81
|
showCreate.set(true)
|
|
99
82
|
}
|
|
100
83
|
|
|
101
84
|
function closeCreate(): void {
|
|
102
85
|
showCreate.set(false)
|
|
103
|
-
|
|
86
|
+
createdToken.set('')
|
|
104
87
|
}
|
|
105
88
|
|
|
106
89
|
async function loadTokens(): Promise<void> {
|
|
@@ -139,27 +122,18 @@ async function loadTokens(): Promise<void> {
|
|
|
139
122
|
}
|
|
140
123
|
}
|
|
141
124
|
|
|
142
|
-
async function createAccessToken(): Promise<void> {
|
|
125
|
+
async function createAccessToken(draft: TokenDraft): Promise<void> {
|
|
143
126
|
actionError.set('')
|
|
144
127
|
actionMessage.set('')
|
|
145
128
|
|
|
146
|
-
if (!tokenName().trim()) {
|
|
147
|
-
actionError.set('Enter a name for this token.')
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
if (selectedScopes().length === 0) {
|
|
151
|
-
actionError.set('Select at least one scope.')
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
129
|
creating.set(true)
|
|
156
130
|
try {
|
|
157
131
|
const response = await dashboardApi<CreatedTokenResponse>('/auth/token', {
|
|
158
132
|
method: 'POST',
|
|
159
133
|
body: {
|
|
160
|
-
name:
|
|
161
|
-
scopes:
|
|
162
|
-
expires_in_minutes:
|
|
134
|
+
name: draft.name,
|
|
135
|
+
scopes: draft.scopes,
|
|
136
|
+
expires_in_minutes: draft.expiresInMinutes,
|
|
163
137
|
with_refresh_token: false,
|
|
164
138
|
},
|
|
165
139
|
})
|
|
@@ -182,22 +156,10 @@ async function createAccessToken(): Promise<void> {
|
|
|
182
156
|
}
|
|
183
157
|
}
|
|
184
158
|
|
|
185
|
-
async function
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
await globalThis.navigator.clipboard.writeText(createdToken())
|
|
189
|
-
copied.set(true)
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
actionError.set('Clipboard access is unavailable. Select and copy the token manually.')
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async function revokeToken(id: number): Promise<void> {
|
|
197
|
-
if (confirmingRevoke() !== id) {
|
|
198
|
-
confirmingRevoke.set(id)
|
|
159
|
+
async function revokeToken(): Promise<void> {
|
|
160
|
+
const id = confirmingRevoke()
|
|
161
|
+
if (id === null)
|
|
199
162
|
return
|
|
200
|
-
}
|
|
201
163
|
|
|
202
164
|
actionError.set('')
|
|
203
165
|
actionMessage.set('')
|
|
@@ -260,8 +222,8 @@ onMount(() => {
|
|
|
260
222
|
<Button variant="secondary" size="sm" @click="loadTokens">Try again</Button>
|
|
261
223
|
</div>
|
|
262
224
|
|
|
263
|
-
<div :if="actionError()" role="alert" class="px-4 py-3 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/40 border border-red-200 rounded-lg dark:border-red-900">{{ actionError() }}</div>
|
|
264
|
-
<div :if="actionMessage()" role="status" class="px-4 py-3 text-green-700 text-sm dark:text-green-300 bg-green-50 dark:bg-green-950/40 border border-green-200 rounded-lg dark:border-green-900">{{ actionMessage() }}</div>
|
|
225
|
+
<div :if="actionError() && !showCreate()" role="alert" class="px-4 py-3 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/40 border border-red-200 rounded-lg dark:border-red-900">{{ actionError() }}</div>
|
|
226
|
+
<div :if="actionMessage() && !showCreate()" role="status" class="px-4 py-3 text-green-700 text-sm dark:text-green-300 bg-green-50 dark:bg-green-950/40 border border-green-200 rounded-lg dark:border-green-900">{{ actionMessage() }}</div>
|
|
265
227
|
|
|
266
228
|
<Card padding="none">
|
|
267
229
|
<div class="flex items-center justify-between p-5 border-b border-neutral-200 dark:border-neutral-700">
|
|
@@ -333,8 +295,8 @@ onMount(() => {
|
|
|
333
295
|
<td class="px-4 py-3.5"><span :class="'inline-flex px-2 py-0.5 font-medium text-xs capitalize rounded ' + statusClass(token)">{{ tokenStatus(token) }}</span></td>
|
|
334
296
|
<td class="px-4 py-3.5 text-neutral-500 text-right text-xs whitespace-nowrap dark:text-neutral-400">{{ formatDate(token.expires_at) }}</td>
|
|
335
297
|
<td class="pr-5 py-3.5 text-right whitespace-nowrap">
|
|
336
|
-
<Button variant="danger" size="sm" :loading="revoking() === token.id" @click="
|
|
337
|
-
{{ revoking() === token.id ? 'Revoking...' :
|
|
298
|
+
<Button variant="danger" size="sm" :loading="revoking() === token.id" @click="confirmingRevoke.set(token.id)">
|
|
299
|
+
{{ revoking() === token.id ? 'Revoking...' : 'Revoke' }}
|
|
338
300
|
</Button>
|
|
339
301
|
</td>
|
|
340
302
|
</tr>
|
|
@@ -343,73 +305,23 @@ onMount(() => {
|
|
|
343
305
|
</div>
|
|
344
306
|
</Card>
|
|
345
307
|
|
|
346
|
-
<
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
</label>
|
|
366
|
-
<div class="flex gap-2 justify-end">
|
|
367
|
-
<Button variant="secondary" @click="closeCreate">Done</Button>
|
|
368
|
-
<Button @click="copyCreatedToken">
|
|
369
|
-
<span class="h-4 w-4 i-hugeicons-copy-01"></span>
|
|
370
|
-
{{ copied() ? 'Copied' : 'Copy Token' }}
|
|
371
|
-
</Button>
|
|
372
|
-
</div>
|
|
373
|
-
</div>
|
|
374
|
-
|
|
375
|
-
<form :if="!createdToken()" class="mt-6 space-y-5" @submit.prevent="createAccessToken">
|
|
376
|
-
<label class="block">
|
|
377
|
-
<span class="font-medium text-neutral-700 text-sm dark:text-neutral-300">Token name</span>
|
|
378
|
-
<input x-model="tokenName" type="text" maxlength="255" required placeholder="CI deployment" class="block mt-2 px-3 py-2 w-full text-neutral-900 text-sm dark:text-white bg-white dark:bg-neutral-950 border border-neutral-300 rounded-lg dark:border-neutral-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20">
|
|
379
|
-
</label>
|
|
380
|
-
|
|
381
|
-
<label class="block">
|
|
382
|
-
<span class="font-medium text-neutral-700 text-sm dark:text-neutral-300">Expires after</span>
|
|
383
|
-
<select x-model="expiryMinutes" class="block mt-2 px-3 py-2 w-full text-neutral-900 text-sm dark:text-white bg-white dark:bg-neutral-950 border border-neutral-300 rounded-lg dark:border-neutral-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20">
|
|
384
|
-
<option value="60">1 hour</option>
|
|
385
|
-
<option value="1440">1 day</option>
|
|
386
|
-
<option value="10080">7 days</option>
|
|
387
|
-
<option value="43200">30 days</option>
|
|
388
|
-
</select>
|
|
389
|
-
</label>
|
|
390
|
-
|
|
391
|
-
<fieldset>
|
|
392
|
-
<legend class="font-medium text-neutral-700 text-sm dark:text-neutral-300">Scopes</legend>
|
|
393
|
-
<div class="grid gap-2 grid-cols-1 sm:grid-cols-2 mt-2">
|
|
394
|
-
<label :for="scope in availableScopes" class="flex gap-3 items-start p-3 border border-neutral-200 rounded-lg dark:border-neutral-700">
|
|
395
|
-
<input type="checkbox" :checked="selectedScopes().includes(scope.name)" class="mt-0.5 h-4 w-4 text-blue-600 rounded" @change="toggleScope($event, scope.name)">
|
|
396
|
-
<span>
|
|
397
|
-
<span class="block font-medium text-neutral-900 text-sm dark:text-white">{{ scope.name }}</span>
|
|
398
|
-
<span class="block mt-0.5 text-neutral-500 text-xs dark:text-neutral-400">{{ scope.description }}</span>
|
|
399
|
-
</span>
|
|
400
|
-
</label>
|
|
401
|
-
</div>
|
|
402
|
-
</fieldset>
|
|
403
|
-
|
|
404
|
-
<div :if="actionError()" role="alert" class="px-3 py-2 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-lg">{{ actionError() }}</div>
|
|
405
|
-
|
|
406
|
-
<div class="flex gap-2 justify-end pt-2">
|
|
407
|
-
<Button variant="secondary" :disabled="creating()" @click="closeCreate">Cancel</Button>
|
|
408
|
-
<Button type="submit" :loading="creating()">
|
|
409
|
-
{{ creating() ? 'Creating...' : 'Create Token' }}
|
|
410
|
-
</Button>
|
|
411
|
-
</div>
|
|
412
|
-
</form>
|
|
413
|
-
</div>
|
|
414
|
-
</div>
|
|
308
|
+
<AccessTokenCreateDialog
|
|
309
|
+
:open="showCreate()"
|
|
310
|
+
:busy="creating()"
|
|
311
|
+
:createdToken="createdToken()"
|
|
312
|
+
:message="actionMessage()"
|
|
313
|
+
:error="actionError()"
|
|
314
|
+
@close="closeCreate"
|
|
315
|
+
@submit="createAccessToken($event)"
|
|
316
|
+
/>
|
|
317
|
+
|
|
318
|
+
<ConfirmDialog
|
|
319
|
+
:isOpen="confirmingRevoke() !== null"
|
|
320
|
+
title="Revoke this access token?"
|
|
321
|
+
:description="revokeDescription()"
|
|
322
|
+
:busy="revoking() !== null"
|
|
323
|
+
confirmLabel="Revoke"
|
|
324
|
+
@close="confirmingRevoke.set(null)"
|
|
325
|
+
@confirm="revokeToken"
|
|
326
|
+
/>
|
|
415
327
|
</div>
|
|
@@ -83,50 +83,6 @@
|
|
|
83
83
|
return str.length > len ? str.slice(0, len) + '...' : str
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// ─── Drilldown drawer state (stacksjs/stacks#1848) ──────────────
|
|
87
|
-
const drilldownTarget = derived(() => ci.drilldown())
|
|
88
|
-
const drilldownRuns = derived(() => ci.drilldownRuns())
|
|
89
|
-
const loadingDrilldown = derived(() => ci.loadingDrilldown())
|
|
90
|
-
const drilldownError = derived(() => ci.drilldownError())
|
|
91
|
-
const expandedRunId = derived(() => ci.expandedRunId())
|
|
92
|
-
const jobsByRunId = derived(() => ci.drilldownJobsByRunId())
|
|
93
|
-
const loadingJobsRunId = derived(() => ci.loadingJobsForRunId())
|
|
94
|
-
|
|
95
|
-
function runRowClass(conclusion, status) {
|
|
96
|
-
const base = 'px-3 py-2 -mx-3 border-l-2 cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-900 transition-colors '
|
|
97
|
-
if (status !== 'completed') return base + 'border-yellow-400'
|
|
98
|
-
if (conclusion === 'success') return base + 'border-emerald-400'
|
|
99
|
-
if (conclusion === 'failure' || conclusion === 'timed_out') return base + 'border-red-400'
|
|
100
|
-
if (conclusion === 'cancelled') return base + 'border-zinc-400'
|
|
101
|
-
return base + 'border-zinc-200 dark:border-zinc-800'
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function runStatusBadge(conclusion, status) {
|
|
105
|
-
if (status !== 'completed') return { label: status, cls: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300' }
|
|
106
|
-
if (conclusion === 'success') return { label: 'success', cls: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300' }
|
|
107
|
-
if (conclusion === 'failure') return { label: 'failure', cls: 'bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300' }
|
|
108
|
-
return { label: conclusion || 'unknown', cls: 'bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300' }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function formatDuration(ms) {
|
|
112
|
-
if (ms == null) return ''
|
|
113
|
-
const s = Math.floor(ms / 1000)
|
|
114
|
-
if (s < 60) return s + 's'
|
|
115
|
-
const m = Math.floor(s / 60)
|
|
116
|
-
const rem = s % 60
|
|
117
|
-
if (m < 60) return m + 'm' + (rem ? ' ' + rem + 's' : '')
|
|
118
|
-
const h = Math.floor(m / 60)
|
|
119
|
-
return h + 'h ' + (m % 60) + 'm'
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function jobsForRun(runId) {
|
|
123
|
-
return jobsByRunId()[runId] || []
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function failedJobs(runId) {
|
|
127
|
-
return jobsForRun(runId).filter(j => j.conclusion && j.conclusion !== 'success' && j.conclusion !== 'skipped' && j.conclusion !== 'neutral')
|
|
128
|
-
}
|
|
129
|
-
|
|
130
86
|
// ─── Runner pressure sparkline (stacksjs/stacks#1850) ────────────
|
|
131
87
|
//
|
|
132
88
|
// Inline SVG sparkline - no chart lib. The path is built off the
|
|
@@ -342,118 +298,6 @@
|
|
|
342
298
|
</div>
|
|
343
299
|
</div>
|
|
344
300
|
|
|
345
|
-
|
|
346
|
-
<!-- Slide-in side panel triggered by a repo-card click. Lists the
|
|
347
|
-
last 20 workflow runs as a vertical timeline; failing runs
|
|
348
|
-
expand inline to their failed-jobs list. Closes on backdrop
|
|
349
|
-
click + Escape. -->
|
|
350
|
-
<div
|
|
351
|
-
:if="drilldownTarget()"
|
|
352
|
-
class="flex fixed inset-0 z-50 justify-end dashboard-modal-layer"
|
|
353
|
-
@click="ci.closeDrilldown()"
|
|
354
|
-
@keydown.escape.window="ci.closeDrilldown()"
|
|
355
|
-
>
|
|
356
|
-
<div class="absolute inset-0 bg-black/40"></div>
|
|
357
|
-
<aside
|
|
358
|
-
@click.stop
|
|
359
|
-
class="overflow-y-auto relative h-full max-w-2xl w-full bg-white dark:bg-zinc-950 border-l border-zinc-200 dark:border-zinc-800 shadow-2xl"
|
|
360
|
-
>
|
|
361
|
-
<!-- Header -->
|
|
362
|
-
<div class="flex sticky top-0 z-10 gap-3 items-start px-5 py-4 bg-white dark:bg-zinc-950 border-b border-zinc-200 dark:border-zinc-800">
|
|
363
|
-
<div class="flex-1 min-w-0">
|
|
364
|
-
<p class="text-xs text-zinc-500 tracking-wide uppercase">Run history</p>
|
|
365
|
-
<h3 class="font-semibold text-lg text-zinc-900 truncate dark:text-zinc-100" :text="drilldownTarget().fullName"></h3>
|
|
366
|
-
</div>
|
|
367
|
-
<Button variant="ghost" size="sm" iconOnly ariaLabel="Close run history" @click="ci.closeDrilldown()">
|
|
368
|
-
<span aria-hidden="true" class="h-5 w-5 i-hugeicons-cancel-01"></span>
|
|
369
|
-
</Button>
|
|
370
|
-
</div>
|
|
371
|
-
|
|
372
|
-
<!-- Error -->
|
|
373
|
-
<div :if="drilldownError()" class="mt-4 mx-5 px-4 py-3 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900/40">
|
|
374
|
-
<p class="text-red-700 text-xs dark:text-red-300" :text="drilldownError()"></p>
|
|
375
|
-
</div>
|
|
376
|
-
|
|
377
|
-
<!-- Loading -->
|
|
378
|
-
<div :if="loadingDrilldown() && drilldownRuns().length === 0" class="px-5 py-12 text-center">
|
|
379
|
-
<span aria-hidden="true" class="mx-auto h-6 w-6 text-violet-500 animate-spin i-hugeicons-loading-03"></span>
|
|
380
|
-
<p class="mt-2 text-xs text-zinc-500">Fetching recent runs…</p>
|
|
381
|
-
</div>
|
|
382
|
-
|
|
383
|
-
<!-- Runs list -->
|
|
384
|
-
<ul :if="drilldownRuns().length > 0" class="px-5 py-3">
|
|
385
|
-
<li
|
|
386
|
-
:for="run in drilldownRuns"
|
|
387
|
-
class="border-b border-zinc-100 dark:border-zinc-900 last:border-0"
|
|
388
|
-
>
|
|
389
|
-
<div
|
|
390
|
-
@click="ci.toggleRunJobs(run.id)"
|
|
391
|
-
:class="runRowClass(run.conclusion, run.status)"
|
|
392
|
-
>
|
|
393
|
-
<div class="flex gap-3 items-start justify-between">
|
|
394
|
-
<div class="flex-1 min-w-0">
|
|
395
|
-
<div class="flex gap-2 items-center">
|
|
396
|
-
<span
|
|
397
|
-
:class="`px-1.5 py-0.5 text-[10px] font-medium rounded ${runStatusBadge(run.conclusion, run.status).cls}`"
|
|
398
|
-
:text="runStatusBadge(run.conclusion, run.status).label"
|
|
399
|
-
></span>
|
|
400
|
-
<span class="font-medium text-sm text-zinc-800 truncate dark:text-zinc-200" :text="run.name"></span>
|
|
401
|
-
</div>
|
|
402
|
-
<p :if="run.commitMessage" class="mt-1 text-xs text-zinc-500 truncate" :text="truncate(run.commitMessage, 80)"></p>
|
|
403
|
-
<div class="flex gap-2 items-center mt-1 text-[10px] text-zinc-400">
|
|
404
|
-
<span class="font-mono" :text="run.headShaShort"></span>
|
|
405
|
-
<span :if="run.headBranch" :text="run.headBranch"></span>
|
|
406
|
-
<span :if="run.commitAuthor">·</span>
|
|
407
|
-
<span :if="run.commitAuthor" :text="run.commitAuthor"></span>
|
|
408
|
-
<span>·</span>
|
|
409
|
-
<span :text="timeAgo(run.updatedAt)"></span>
|
|
410
|
-
<span :if="run.durationMs">·</span>
|
|
411
|
-
<span :if="run.durationMs" :text="formatDuration(run.durationMs)"></span>
|
|
412
|
-
</div>
|
|
413
|
-
</div>
|
|
414
|
-
<a
|
|
415
|
-
:href="run.url"
|
|
416
|
-
target="_blank"
|
|
417
|
-
@click.stop
|
|
418
|
-
class="flex-shrink-0 no-underline text-violet-600 text-xs dark:text-violet-400 hover:underline"
|
|
419
|
-
>view ↗</a>
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
|
-
|
|
423
|
-
<!-- Expanded: failed job list -->
|
|
424
|
-
<div :if="expandedRunId() === run.id" class="px-3 py-2 bg-zinc-50 dark:bg-zinc-900/50 border-t border-zinc-100 dark:border-zinc-900">
|
|
425
|
-
<div :if="loadingJobsRunId() === run.id" class="py-2 text-xs text-zinc-500">
|
|
426
|
-
Loading jobs…
|
|
427
|
-
</div>
|
|
428
|
-
<div :if="loadingJobsRunId() !== run.id && jobsForRun(run.id).length === 0" class="py-1 italic text-xs text-zinc-400">
|
|
429
|
-
No job detail.
|
|
430
|
-
</div>
|
|
431
|
-
<div :if="loadingJobsRunId() !== run.id && failedJobs(run.id).length > 0" class="space-y-1.5">
|
|
432
|
-
<p class="text-[10px] text-zinc-500 tracking-wide uppercase">Failed jobs</p>
|
|
433
|
-
<a
|
|
434
|
-
:for="job in failedJobs(run.id)"
|
|
435
|
-
:href="job.url"
|
|
436
|
-
target="_blank"
|
|
437
|
-
@click.stop
|
|
438
|
-
class="flex gap-1.5 items-center no-underline text-red-700 text-xs dark:text-red-400 hover:underline"
|
|
439
|
-
>
|
|
440
|
-
<span aria-hidden="true" class="h-2.5 w-2.5 i-hugeicons-cancel-circle"></span>
|
|
441
|
-
<span :text="job.name"></span>
|
|
442
|
-
<span :if="job.durationMs" class="text-zinc-400" :text="'(' + formatDuration(job.durationMs) + ')'"></span>
|
|
443
|
-
</a>
|
|
444
|
-
</div>
|
|
445
|
-
<div :if="loadingJobsRunId() !== run.id && jobsForRun(run.id).length > 0 && failedJobs(run.id).length === 0" class="py-1 text-xs text-zinc-500">
|
|
446
|
-
All <span :text="jobsForRun(run.id).length"></span> jobs passed.
|
|
447
|
-
</div>
|
|
448
|
-
</div>
|
|
449
|
-
</li>
|
|
450
|
-
</ul>
|
|
451
|
-
|
|
452
|
-
<!-- Empty -->
|
|
453
|
-
<div :if="!loadingDrilldown() && !drilldownError() && drilldownRuns().length === 0" class="px-5 py-12 text-center">
|
|
454
|
-
<p class="text-sm text-zinc-500">No recent runs.</p>
|
|
455
|
-
</div>
|
|
456
|
-
</aside>
|
|
457
|
-
</div>
|
|
301
|
+
<CiRunHistoryDrawer />
|
|
458
302
|
</div>
|
|
459
303
|
</template>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script client>
|
|
2
|
+
import { ciStore } from '~/storage/framework/defaults/views/dashboard/stores/ci'
|
|
3
|
+
|
|
4
|
+
const ci = ciStore
|
|
5
|
+
const target = derived(() => ci.drilldown())
|
|
6
|
+
const runs = derived(() => ci.drilldownRuns())
|
|
7
|
+
const loading = derived(() => ci.loadingDrilldown())
|
|
8
|
+
const error = derived(() => ci.drilldownError())
|
|
9
|
+
const expandedRunId = derived(() => ci.expandedRunId())
|
|
10
|
+
const jobsByRunId = derived(() => ci.drilldownJobsByRunId())
|
|
11
|
+
const loadingJobsRunId = derived(() => ci.loadingJobsForRunId())
|
|
12
|
+
const title = derived(() => target()?.fullName || 'Run history')
|
|
13
|
+
|
|
14
|
+
function runRowClass(conclusion, status) {
|
|
15
|
+
const base = 'px-3 py-2 -mx-3 border-l-2 cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-900 transition-colors '
|
|
16
|
+
if (status !== 'completed') return base + 'border-yellow-400'
|
|
17
|
+
if (conclusion === 'success') return base + 'border-emerald-400'
|
|
18
|
+
if (conclusion === 'failure' || conclusion === 'timed_out') return base + 'border-red-400'
|
|
19
|
+
if (conclusion === 'cancelled') return base + 'border-zinc-400'
|
|
20
|
+
return base + 'border-zinc-200 dark:border-zinc-800'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function runStatusBadge(conclusion, status) {
|
|
24
|
+
if (status !== 'completed') return { label: status, cls: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300' }
|
|
25
|
+
if (conclusion === 'success') return { label: 'success', cls: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300' }
|
|
26
|
+
if (conclusion === 'failure') return { label: 'failure', cls: 'bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300' }
|
|
27
|
+
return { label: conclusion || 'unknown', cls: 'bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300' }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function formatDuration(ms) {
|
|
31
|
+
if (ms == null) return ''
|
|
32
|
+
const seconds = Math.floor(ms / 1000)
|
|
33
|
+
if (seconds < 60) return seconds + 's'
|
|
34
|
+
const minutes = Math.floor(seconds / 60)
|
|
35
|
+
const remainder = seconds % 60
|
|
36
|
+
if (minutes < 60) return minutes + 'm' + (remainder ? ' ' + remainder + 's' : '')
|
|
37
|
+
const hours = Math.floor(minutes / 60)
|
|
38
|
+
return hours + 'h ' + (minutes % 60) + 'm'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function timeAgo(dateStr) {
|
|
42
|
+
if (!dateStr) return ''
|
|
43
|
+
const diff = Date.now() - new Date(dateStr).getTime()
|
|
44
|
+
const minutes = Math.floor(diff / 60000)
|
|
45
|
+
if (minutes < 60) return minutes + 'm ago'
|
|
46
|
+
const hours = Math.floor(minutes / 60)
|
|
47
|
+
if (hours < 24) return hours + 'h ago'
|
|
48
|
+
return Math.floor(hours / 24) + 'd ago'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function truncate(value, length) {
|
|
52
|
+
if (!value) return ''
|
|
53
|
+
return value.length > length ? value.slice(0, length) + '...' : value
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function jobsForRun(runId) {
|
|
57
|
+
return jobsByRunId()[runId] || []
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function failedJobs(runId) {
|
|
61
|
+
return jobsForRun(runId).filter(job => job.conclusion && job.conclusion !== 'success' && job.conclusion !== 'skipped' && job.conclusion !== 'neutral')
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<Drawer
|
|
66
|
+
:isOpen="Boolean(target())"
|
|
67
|
+
:title="title()"
|
|
68
|
+
description="Run history"
|
|
69
|
+
size="lg"
|
|
70
|
+
@close="ci.closeDrilldown()"
|
|
71
|
+
>
|
|
72
|
+
<div :if="error()" class="mt-4 mx-5 px-4 py-3 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900/40">
|
|
73
|
+
<p class="text-red-700 text-xs dark:text-red-300" :text="error()"></p>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div :if="loading() && runs().length === 0" class="px-5 py-12 text-center">
|
|
77
|
+
<span aria-hidden="true" class="mx-auto h-6 w-6 text-violet-500 animate-spin i-hugeicons-loading-03"></span>
|
|
78
|
+
<p class="mt-2 text-zinc-500 text-xs">Fetching recent runs...</p>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<ul :if="runs().length > 0" class="px-5 py-3">
|
|
82
|
+
<li :for="run in runs" class="border-b border-zinc-100 dark:border-zinc-900 last:border-0">
|
|
83
|
+
<div @click="ci.toggleRunJobs(run.id)" :class="runRowClass(run.conclusion, run.status)">
|
|
84
|
+
<div class="flex gap-3 items-start justify-between">
|
|
85
|
+
<div class="flex-1 min-w-0">
|
|
86
|
+
<div class="flex gap-2 items-center">
|
|
87
|
+
<span :class="`px-1.5 py-0.5 text-[10px] font-medium rounded ${runStatusBadge(run.conclusion, run.status).cls}`" :text="runStatusBadge(run.conclusion, run.status).label"></span>
|
|
88
|
+
<span class="font-medium text-sm text-zinc-800 truncate dark:text-zinc-200" :text="run.name"></span>
|
|
89
|
+
</div>
|
|
90
|
+
<p :if="run.commitMessage" class="mt-1 text-zinc-500 text-xs truncate" :text="truncate(run.commitMessage, 80)"></p>
|
|
91
|
+
<div class="flex gap-2 items-center mt-1 text-[10px] text-zinc-400">
|
|
92
|
+
<span class="font-mono" :text="run.headShaShort"></span>
|
|
93
|
+
<span :if="run.headBranch" :text="run.headBranch"></span>
|
|
94
|
+
<span :if="run.commitAuthor">·</span>
|
|
95
|
+
<span :if="run.commitAuthor" :text="run.commitAuthor"></span>
|
|
96
|
+
<span>·</span>
|
|
97
|
+
<span :text="timeAgo(run.updatedAt)"></span>
|
|
98
|
+
<span :if="run.durationMs">·</span>
|
|
99
|
+
<span :if="run.durationMs" :text="formatDuration(run.durationMs)"></span>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<a :href="run.url" target="_blank" rel="noreferrer" @click.stop class="flex-shrink-0 no-underline text-violet-600 text-xs dark:text-violet-400 hover:underline">View ↗</a>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div :if="expandedRunId() === run.id" class="px-3 py-2 bg-zinc-50 dark:bg-zinc-900/50 border-t border-zinc-100 dark:border-zinc-900">
|
|
107
|
+
<div :if="loadingJobsRunId() === run.id" class="py-2 text-zinc-500 text-xs">Loading jobs...</div>
|
|
108
|
+
<div :if="loadingJobsRunId() !== run.id && jobsForRun(run.id).length === 0" class="py-1 italic text-zinc-400 text-xs">No job detail.</div>
|
|
109
|
+
<div :if="loadingJobsRunId() !== run.id && failedJobs(run.id).length > 0" class="space-y-1.5">
|
|
110
|
+
<p class="text-[10px] text-zinc-500 tracking-wide uppercase">Failed jobs</p>
|
|
111
|
+
<a :for="job in failedJobs(run.id)" :href="job.url" target="_blank" rel="noreferrer" @click.stop class="flex gap-1.5 items-center no-underline text-red-700 text-xs dark:text-red-400 hover:underline">
|
|
112
|
+
<span aria-hidden="true" class="h-2.5 w-2.5 i-hugeicons-cancel-circle"></span>
|
|
113
|
+
<span :text="job.name"></span>
|
|
114
|
+
<span :if="job.durationMs" class="text-zinc-400" :text="'(' + formatDuration(job.durationMs) + ')'"></span>
|
|
115
|
+
</a>
|
|
116
|
+
</div>
|
|
117
|
+
<div :if="loadingJobsRunId() !== run.id && jobsForRun(run.id).length > 0 && failedJobs(run.id).length === 0" class="py-1 text-zinc-500 text-xs">
|
|
118
|
+
All <span :text="jobsForRun(run.id).length"></span> jobs passed.
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</li>
|
|
122
|
+
</ul>
|
|
123
|
+
|
|
124
|
+
<div :if="!loading() && !error() && runs().length === 0" class="px-5 py-12 text-center">
|
|
125
|
+
<p class="text-zinc-500 text-sm">No recent runs.</p>
|
|
126
|
+
</div>
|
|
127
|
+
</Drawer>
|
|
@@ -5,13 +5,15 @@ const open = useReactiveProp('open', false)
|
|
|
5
5
|
const record = useReactiveProp('record', null as CommerceCategoryRecord | null)
|
|
6
6
|
const busy = useReactiveProp('busy', false)
|
|
7
7
|
const emit = defineEmits()
|
|
8
|
+
const description = derived(() => `Delete "${record()?.name || 'Unnamed category'}" permanently? Linked products and child categories may prevent deletion.`)
|
|
8
9
|
</script>
|
|
9
10
|
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
<ConfirmDialog
|
|
12
|
+
:isOpen="open()"
|
|
13
|
+
title="Delete category"
|
|
14
|
+
:description="description()"
|
|
15
|
+
confirmLabel="Delete category"
|
|
16
|
+
:busy="busy()"
|
|
17
|
+
@close="emit('close')"
|
|
18
|
+
@confirm="emit('confirm')"
|
|
19
|
+
/>
|
|
@@ -35,20 +35,25 @@ function deleteRecord(): void {
|
|
|
35
35
|
}
|
|
36
36
|
</script>
|
|
37
37
|
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
</div>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
</div>
|
|
38
|
+
<Modal
|
|
39
|
+
:isOpen="open()"
|
|
40
|
+
:title="record()?.name || 'Category'"
|
|
41
|
+
:description="record()?.slug || 'No slug'"
|
|
42
|
+
size="lg"
|
|
43
|
+
@close="emit('close')"
|
|
44
|
+
>
|
|
45
|
+
<template #header-trailing>
|
|
46
|
+
<span class="px-2 py-1 font-semibold text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ record()?.isActive ? 'Active' : 'Inactive' }}</span>
|
|
47
|
+
</template>
|
|
48
|
+
<p class="text-gray-600 text-sm whitespace-pre-wrap dark:text-neutral-300">{{ record()?.description || 'No description recorded.' }}</p>
|
|
49
|
+
<dl class="grid gap-4 grid-cols-2 mt-6">
|
|
50
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Parent</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ parentName() }}</dd></div>
|
|
51
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Display order</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ record()?.displayOrder || 0 }}</dd></div>
|
|
52
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Direct children</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ children().length }}</dd></div>
|
|
53
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Record ID</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ record()?.id || 'Not recorded' }}</dd></div>
|
|
54
|
+
<div class="col-span-2"><dt class="text-gray-400 text-xs dark:text-neutral-500">Image URL</dt><dd class="mt-1 break-all text-gray-900 text-sm dark:text-white">{{ record()?.imageUrl || 'Not configured' }}</dd></div>
|
|
55
|
+
</dl>
|
|
56
|
+
<template #footer>
|
|
57
|
+
<div class="flex gap-2 justify-between w-full"><Button variant="danger" @click="deleteRecord()">Delete</Button><div class="flex gap-2"><Button variant="secondary" @click="emit('close')">Close</Button><Button @click="editRecord()">Edit category</Button></div></div>
|
|
58
|
+
</template>
|
|
59
|
+
</Modal>
|