@stacksjs/defaults 0.70.358 → 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 +7 -5
- package/ai/skills/stacks-browse/scripts/browse.ts +68 -9
- package/ai/skills/stacks-dashboard/SKILL.md +4 -0
- package/app/Actions/AI/AskAction.ts +12 -17
- package/app/Actions/AI/SummaryAction.ts +12 -17
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +4 -1
- package/app/Actions/Dashboard/Models/model-write.ts +5 -0
- package/app/Actions/LogAction.ts +11 -11
- 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/CampaignDeleteDialog.stx +11 -12
- package/resources/components/Dashboard/Marketing/CampaignDeliveryDialog.stx +5 -8
- package/resources/components/Dashboard/Marketing/CampaignDialog.stx +17 -17
- package/resources/components/Dashboard/Marketing/MarketingListDeleteDialog.stx +15 -15
- package/resources/components/Dashboard/Marketing/MarketingListDialog.stx +17 -17
- package/resources/components/Dashboard/Marketing/MarketingListsTable.stx +1 -1
- package/resources/components/Dashboard/Marketing/SocialPostDeleteDialog.stx +11 -12
- 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 +55 -53
- package/resources/components/Dashboard/UI/Drawer.stx +114 -0
- package/resources/components/Dashboard/UI/Modal.stx +32 -21
- 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
|
@@ -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>
|
|
@@ -120,17 +120,18 @@ function errorMessages(): string[] {
|
|
|
120
120
|
}
|
|
121
121
|
</script>
|
|
122
122
|
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
<Modal
|
|
124
|
+
:isOpen="open()"
|
|
125
|
+
:title="mode() === 'edit' ? 'Edit category' : 'Add category'"
|
|
126
|
+
description="Store hierarchy fields declared by the Category model."
|
|
127
|
+
size="lg"
|
|
128
|
+
@close="emit('close')"
|
|
129
|
+
>
|
|
130
|
+
<form id="category-form" @submit.prevent="submit">
|
|
131
|
+
<ul :if="errorMessages().length > 0" class="mb-5 p-3 space-y-1 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">
|
|
131
132
|
<template :for="message in errorMessages()" :key="message"><li>{{ message }}</li></template>
|
|
132
133
|
</ul>
|
|
133
|
-
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2
|
|
134
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
|
|
134
135
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Name</span><input x-model="name" type="text" maxlength="50" required class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
135
136
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Slug</span><input x-model="slug" type="text" required spellcheck="false" class="mt-1 px-3 py-2 w-full font-mono text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" @input="markSlugTouched" @blur="normalizeSlug" /></label>
|
|
136
137
|
<label class="block sm:col-span-2"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Description</span><textarea x-model="description" rows="3" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700"></textarea></label>
|
|
@@ -139,8 +140,8 @@ function errorMessages(): string[] {
|
|
|
139
140
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Display order</span><input x-model="displayOrder" type="number" min="0" step="1" required class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
140
141
|
<label class="flex gap-3 items-center sm:col-span-2 p-3 border border-gray-200 rounded-lg dark:border-neutral-700"><input x-model="isActive" type="checkbox" class="h-4 w-4 text-blue-600 border-gray-300 rounded" /><span><span class="block font-medium text-gray-700 text-sm dark:text-neutral-200">Active in the storefront</span><span class="block mt-0.5 text-gray-400 text-xs dark:text-neutral-500">Availability is independent from the hierarchy.</span></span></label>
|
|
141
142
|
</div>
|
|
142
|
-
</div>
|
|
143
|
-
<footer class="flex gap-2 justify-end px-6 py-4 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button type="submit" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save category' : 'Add category' }}</Button></footer>
|
|
144
143
|
</form>
|
|
145
|
-
|
|
146
|
-
</div>
|
|
144
|
+
<template #footer>
|
|
145
|
+
<div class="flex gap-2 justify-end w-full"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button type="submit" form="category-form" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save category' : 'Add category' }}</Button></div>
|
|
146
|
+
</template>
|
|
147
|
+
</Modal>
|
|
@@ -5,12 +5,15 @@ const open = useReactiveProp('open', false)
|
|
|
5
5
|
const record = useReactiveProp('record', null as CouponRecord | null)
|
|
6
6
|
const busy = useReactiveProp('busy', false)
|
|
7
7
|
const emit = defineEmits()
|
|
8
|
+
const description = derived(() => `Delete "${record()?.code || 'Unnamed coupon'}" permanently? Recorded order relationships may prevent deletion.`)
|
|
8
9
|
</script>
|
|
9
10
|
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
<ConfirmDialog
|
|
12
|
+
:isOpen="open()"
|
|
13
|
+
title="Delete coupon"
|
|
14
|
+
:description="description()"
|
|
15
|
+
confirmLabel="Delete coupon"
|
|
16
|
+
:busy="busy()"
|
|
17
|
+
@close="emit('close')"
|
|
18
|
+
@confirm="emit('confirm')"
|
|
19
|
+
/>
|
|
@@ -32,22 +32,28 @@ function deleteRecord(): void {
|
|
|
32
32
|
}
|
|
33
33
|
</script>
|
|
34
34
|
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</div>
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
</div>
|
|
35
|
+
<Modal
|
|
36
|
+
:isOpen="open()"
|
|
37
|
+
:title="record()?.code || 'Coupon'"
|
|
38
|
+
:description="record()?.description || 'No description recorded.'"
|
|
39
|
+
size="lg"
|
|
40
|
+
@close="emit('close')"
|
|
41
|
+
>
|
|
42
|
+
<template #header-trailing>
|
|
43
|
+
<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()?.status || 'Not recorded' }}</span>
|
|
44
|
+
</template>
|
|
45
|
+
<dl class="grid gap-4 grid-cols-2">
|
|
46
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Discount</dt><dd class="mt-1 font-semibold text-gray-900 text-sm dark:text-white">{{ discount(record()) }}</dd></div>
|
|
47
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Availability</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.isActive ? 'Enabled' : 'Disabled' }}</dd></div>
|
|
48
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Usage</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ record()?.usageCount || 0 }} / {{ record()?.usageLimit || 'No limit' }}</dd></div>
|
|
49
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Minimum order</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.minOrderAmount ? money(record()?.minOrderAmount || 0) : 'No minimum' }}</dd></div>
|
|
50
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Maximum discount</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.maxDiscountAmount ? money(record()?.maxDiscountAmount || 0) : 'No maximum' }}</dd></div>
|
|
51
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Product relation ID</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ record()?.productId || 'Not linked' }}</dd></div>
|
|
52
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Free product reference</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ record()?.freeProductId || 'Not configured' }}</dd></div>
|
|
53
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Start date</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.startDate || 'Not recorded' }}</dd></div>
|
|
54
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">End date</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.endDate || 'Not recorded' }}</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 coupon</Button></div></div>
|
|
58
|
+
</template>
|
|
59
|
+
</Modal>
|
|
@@ -87,15 +87,18 @@ function errorMessages(): string[] {
|
|
|
87
87
|
}
|
|
88
88
|
</script>
|
|
89
89
|
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
<Modal
|
|
91
|
+
:isOpen="open()"
|
|
92
|
+
:title="mode() === 'edit' ? 'Edit coupon' : 'Add coupon'"
|
|
93
|
+
description="Store fields declared by the Coupon model."
|
|
94
|
+
size="lg"
|
|
95
|
+
@close="emit('close')"
|
|
96
|
+
>
|
|
97
|
+
<form id="coupon-form" @submit.prevent="submit">
|
|
98
|
+
<ul :if="errorMessages().length > 0" class="mb-5 p-3 space-y-1 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">
|
|
96
99
|
<template :for="message in errorMessages()" :key="message"><li>{{ message }}</li></template>
|
|
97
100
|
</ul>
|
|
98
|
-
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2
|
|
101
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
|
|
99
102
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Code</span><input x-model="code" type="text" maxlength="50" required spellcheck="false" class="mt-1 px-3 py-2 w-full font-mono text-gray-900 text-sm uppercase dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
100
103
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Status</span><select x-model="status" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700"><option value="Active">Active</option><option value="Scheduled">Scheduled</option><option value="Expired">Expired</option></select></label>
|
|
101
104
|
<label class="block sm:col-span-2"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Description</span><textarea x-model="description" rows="3" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700"></textarea></label>
|
|
@@ -109,8 +112,8 @@ function errorMessages(): string[] {
|
|
|
109
112
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">End date</span><input x-model="endDate" type="date" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
110
113
|
<label class="flex gap-3 items-center sm:col-span-2 p-3 border border-gray-200 rounded-lg dark:border-neutral-700"><input x-model="isActive" type="checkbox" class="h-4 w-4 text-blue-600 border-gray-300 rounded" /><span><span class="block font-medium text-gray-700 text-sm dark:text-neutral-200">Enabled for redemption</span><span class="block mt-0.5 text-gray-400 text-xs dark:text-neutral-500">Availability is stored separately from lifecycle status.</span></span></label>
|
|
111
114
|
</div>
|
|
112
|
-
</div>
|
|
113
|
-
<footer class="flex gap-2 justify-end px-6 py-4 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button type="submit" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save coupon' : 'Add coupon' }}</Button></footer>
|
|
114
115
|
</form>
|
|
115
|
-
|
|
116
|
-
</div>
|
|
116
|
+
<template #footer>
|
|
117
|
+
<div class="flex gap-2 justify-end w-full"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button type="submit" form="coupon-form" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save coupon' : 'Add coupon' }}</Button></div>
|
|
118
|
+
</template>
|
|
119
|
+
</Modal>
|
|
@@ -5,13 +5,15 @@ const open = useReactiveProp('open', false)
|
|
|
5
5
|
const record = useReactiveProp('record', null as CommerceCustomerRecord | null)
|
|
6
6
|
const busy = useReactiveProp('busy', false)
|
|
7
7
|
const emit = defineEmits()
|
|
8
|
+
const description = derived(() => `Delete "${record()?.name || 'Unnamed customer'}" permanently? Linked orders, reviews, payments, and gift cards may prevent deletion.`)
|
|
8
9
|
</script>
|
|
9
10
|
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
<ConfirmDialog
|
|
12
|
+
:isOpen="open()"
|
|
13
|
+
title="Delete customer"
|
|
14
|
+
:description="description()"
|
|
15
|
+
confirmLabel="Delete customer"
|
|
16
|
+
:busy="busy()"
|
|
17
|
+
@close="emit('close')"
|
|
18
|
+
@confirm="emit('confirm')"
|
|
19
|
+
/>
|
|
@@ -46,19 +46,27 @@ function deleteRecord(): void {
|
|
|
46
46
|
}
|
|
47
47
|
</script>
|
|
48
48
|
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</div>
|
|
49
|
+
<Modal
|
|
50
|
+
:isOpen="open()"
|
|
51
|
+
:title="record()?.name || 'Customer'"
|
|
52
|
+
:description="record()?.email || 'No email recorded'"
|
|
53
|
+
size="lg"
|
|
54
|
+
@close="emit('close')"
|
|
55
|
+
>
|
|
56
|
+
<template #header-leading>
|
|
57
|
+
<span class="flex items-center justify-center h-11 w-11 font-semibold text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-950/40 rounded-lg">{{ (record()?.name || '?').slice(0, 1).toUpperCase() }}</span>
|
|
58
|
+
</template>
|
|
59
|
+
<template #header-trailing>
|
|
60
|
+
<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()?.status || 'Unknown' }}</span>
|
|
61
|
+
</template>
|
|
62
|
+
<dl class="grid gap-4 grid-cols-2">
|
|
63
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Phone</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.phone || 'Not recorded' }}</dd></div>
|
|
64
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Recorded spend</dt><dd class="mt-1 font-medium text-gray-900 text-sm dark:text-white">{{ formatMoney(record()?.totalSpent || 0) }}</dd></div>
|
|
65
|
+
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Last order</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ formatDate(record()?.lastOrder || '') }}</dd></div>
|
|
66
|
+
<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>
|
|
67
|
+
<div class="col-span-2"><dt class="text-gray-400 text-xs dark:text-neutral-500">Avatar URL</dt><dd class="mt-1 break-all text-gray-900 text-sm dark:text-white">{{ record()?.avatar || 'Not configured' }}</dd></div>
|
|
68
|
+
</dl>
|
|
69
|
+
<template #footer>
|
|
70
|
+
<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 customer</Button></div></div>
|
|
71
|
+
</template>
|
|
72
|
+
</Modal>
|
|
@@ -79,17 +79,18 @@ function errorMessages(): string[] {
|
|
|
79
79
|
}
|
|
80
80
|
</script>
|
|
81
81
|
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
<Modal
|
|
83
|
+
:isOpen="open()"
|
|
84
|
+
:title="mode() === 'edit' ? 'Edit customer' : 'Add customer'"
|
|
85
|
+
description="Store fields declared by the native Customer model."
|
|
86
|
+
size="lg"
|
|
87
|
+
@close="emit('close')"
|
|
88
|
+
>
|
|
89
|
+
<form id="customer-form" @submit.prevent="submit">
|
|
90
|
+
<ul :if="errorMessages().length > 0" class="mb-5 p-3 space-y-1 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900">
|
|
90
91
|
<template :for="message in errorMessages()" :key="message"><li>{{ message }}</li></template>
|
|
91
92
|
</ul>
|
|
92
|
-
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2
|
|
93
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
|
|
93
94
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Name</span><input x-model="name" type="text" minlength="2" maxlength="255" required class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
94
95
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Email</span><input x-model="email" type="email" required class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
95
96
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Phone <span class="font-normal text-gray-400 dark:text-neutral-500">(optional)</span></span><input x-model="phone" type="tel" maxlength="50" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
@@ -98,8 +99,8 @@ function errorMessages(): string[] {
|
|
|
98
99
|
<label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Last order</span><input x-model="lastOrder" type="date" class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /></label>
|
|
99
100
|
<label class="block sm:col-span-2"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Avatar URL</span><input x-model="avatar" type="url" placeholder="https://cdn.example.com/customers/avatar.jpg" required class="mt-1 px-3 py-2 w-full text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-900 border border-gray-300 rounded-md dark:border-neutral-700" /><span class="block mt-1 text-gray-400 text-xs dark:text-neutral-500">Use a persisted HTTP asset URL. The dashboard uses initials when rendering the table.</span></label>
|
|
100
101
|
</div>
|
|
101
|
-
</div>
|
|
102
|
-
<footer class="flex gap-2 justify-end px-6 py-4 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button type="submit" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save customer' : 'Add customer' }}</Button></footer>
|
|
103
102
|
</form>
|
|
104
|
-
|
|
105
|
-
</div>
|
|
103
|
+
<template #footer>
|
|
104
|
+
<div class="flex gap-2 justify-end w-full"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button type="submit" form="customer-form" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save customer' : 'Add customer' }}</Button></div>
|
|
105
|
+
</template>
|
|
106
|
+
</Modal>
|
|
@@ -5,13 +5,15 @@ const open = useReactiveProp('open', false)
|
|
|
5
5
|
const record = useReactiveProp('record', null as GiftCardRecord | null)
|
|
6
6
|
const busy = useReactiveProp('busy', false)
|
|
7
7
|
const emit = defineEmits()
|
|
8
|
+
const description = derived(() => `Delete "${record()?.code || 'Unnamed gift card'}" permanently? Recorded customer or order relationships may prevent deletion.`)
|
|
8
9
|
</script>
|
|
9
10
|
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
<ConfirmDialog
|
|
12
|
+
:isOpen="open()"
|
|
13
|
+
title="Delete gift card"
|
|
14
|
+
:description="description()"
|
|
15
|
+
confirmLabel="Delete gift card"
|
|
16
|
+
:busy="busy()"
|
|
17
|
+
@close="emit('close')"
|
|
18
|
+
@confirm="emit('confirm')"
|
|
19
|
+
/>
|
|
@@ -38,15 +38,17 @@ function deleteRecord(): void {
|
|
|
38
38
|
}
|
|
39
39
|
</script>
|
|
40
40
|
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
<Modal
|
|
42
|
+
:isOpen="open()"
|
|
43
|
+
:title="record()?.code || 'Gift card'"
|
|
44
|
+
:description="record()?.recipientName || 'No recipient assigned'"
|
|
45
|
+
size="lg"
|
|
46
|
+
@close="emit('close')"
|
|
47
|
+
>
|
|
48
|
+
<template #header-trailing>
|
|
49
|
+
<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()?.status || 'Not recorded' }}</span>
|
|
50
|
+
</template>
|
|
51
|
+
<dl class="grid gap-4 grid-cols-1 sm:grid-cols-2">
|
|
50
52
|
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Current balance</dt><dd class="mt-1 font-semibold text-gray-900 text-sm dark:text-white">{{ money(record()?.currentBalance || 0, record()?.currency || 'USD') }}</dd></div>
|
|
51
53
|
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Initial balance</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ money(record()?.initialBalance || 0, record()?.currency || 'USD') }}</dd></div>
|
|
52
54
|
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Availability</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.isActive ? 'Enabled' : 'Disabled' }}</dd></div>
|
|
@@ -59,10 +61,10 @@ function deleteRecord(): void {
|
|
|
59
61
|
<div><dt class="text-gray-400 text-xs dark:text-neutral-500">Last used date</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ dateTime(record()?.lastUsedDate || '') }}</dd></div>
|
|
60
62
|
<div class="sm:col-span-2"><dt class="text-gray-400 text-xs dark:text-neutral-500">Personal message</dt><dd class="mt-1 text-gray-900 text-sm whitespace-pre-wrap dark:text-white">{{ record()?.personalMessage || 'No message recorded.' }}</dd></div>
|
|
61
63
|
</dl>
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
+
<template #footer>
|
|
65
|
+
<div class="flex gap-2 justify-between w-full">
|
|
64
66
|
<Button variant="danger" @click="deleteRecord()">Delete</Button>
|
|
65
67
|
<div class="flex gap-2"><Button variant="secondary" @click="emit('close')">Close</Button><Button @click="editRecord()">Edit gift card</Button></div>
|
|
66
|
-
</
|
|
67
|
-
</
|
|
68
|
-
</
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
</Modal>
|