@stacksjs/defaults 0.70.362 → 0.70.365

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/ai/skills/stacks-browse/SKILL.md +2 -1
  2. package/ai/skills/stacks-browse/scripts/browse.ts +20 -2
  3. package/ai/skills/stacks-dashboard/SKILL.md +4 -0
  4. package/app/Actions/Auth/LogoutAction.ts +11 -4
  5. package/app/Actions/Auth/LogoutAllAction.ts +8 -4
  6. package/app/Actions/Dashboard/Models/ModelShowAction.ts +4 -1
  7. package/app/Actions/Dashboard/Models/model-write.ts +5 -0
  8. package/ide/vscode/package.json +1 -1
  9. package/package.json +1 -1
  10. package/resources/components/Buttons/Counter.stx +1 -1
  11. package/resources/components/Buttons/ToggleDark.stx +1 -1
  12. package/resources/components/Dashboard/App/RequestDetailsDialog.stx +42 -0
  13. package/resources/components/Dashboard/App/RequestsOverview.stx +1 -21
  14. package/resources/components/Dashboard/Auth/AccessTokenCreateDialog.stx +155 -0
  15. package/resources/components/Dashboard/Auth/AccessTokens.stx +41 -129
  16. package/resources/components/Dashboard/Ci/CiDashboard.stx +1 -157
  17. package/resources/components/Dashboard/Ci/CiRunHistoryDrawer.stx +127 -0
  18. package/resources/components/Dashboard/Commerce/CommerceCategoryDeleteDialog.stx +10 -8
  19. package/resources/components/Dashboard/Commerce/CommerceCategoryDetailsDialog.stx +22 -17
  20. package/resources/components/Dashboard/Commerce/CommerceCategoryDialog.stx +14 -13
  21. package/resources/components/Dashboard/Commerce/CommerceCouponDeleteDialog.stx +10 -7
  22. package/resources/components/Dashboard/Commerce/CommerceCouponDetailsDialog.stx +25 -19
  23. package/resources/components/Dashboard/Commerce/CommerceCouponDialog.stx +14 -11
  24. package/resources/components/Dashboard/Commerce/CommerceCustomerDeleteDialog.stx +10 -8
  25. package/resources/components/Dashboard/Commerce/CommerceCustomerDetailsDialog.stx +24 -16
  26. package/resources/components/Dashboard/Commerce/CommerceCustomerDialog.stx +14 -13
  27. package/resources/components/Dashboard/Commerce/CommerceGiftCardDeleteDialog.stx +10 -8
  28. package/resources/components/Dashboard/Commerce/CommerceGiftCardDetailsDialog.stx +16 -14
  29. package/resources/components/Dashboard/Commerce/CommerceGiftCardDialog.stx +17 -16
  30. package/resources/components/Dashboard/Commerce/CommerceManufacturerDeleteDialog.stx +14 -16
  31. package/resources/components/Dashboard/Commerce/CommerceManufacturerDialog.stx +17 -16
  32. package/resources/components/Dashboard/Commerce/CommerceOrderDeleteDialog.stx +10 -8
  33. package/resources/components/Dashboard/Commerce/CommerceOrderDetailsDialog.stx +15 -10
  34. package/resources/components/Dashboard/Commerce/CommerceOrderDialog.stx +14 -13
  35. package/resources/components/Dashboard/Commerce/CommercePosCheckoutDialog.stx +30 -25
  36. package/resources/components/Dashboard/Commerce/CommercePosReceiptDialog.stx +27 -20
  37. package/resources/components/Dashboard/Commerce/CommerceProductDeleteDialog.stx +14 -14
  38. package/resources/components/Dashboard/Commerce/CommerceProductDialog.stx +14 -13
  39. package/resources/components/Dashboard/Commerce/CommerceReviewDeleteDialog.stx +10 -18
  40. package/resources/components/Dashboard/Commerce/CommerceReviewEditDialog.stx +17 -16
  41. package/resources/components/Dashboard/Commerce/CommerceTaxDeleteDialog.stx +14 -14
  42. package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +17 -16
  43. package/resources/components/Dashboard/Commerce/CommerceTaxesTable.stx +1 -1
  44. package/resources/components/Dashboard/Commerce/CommerceUnitDeleteDialog.stx +14 -17
  45. package/resources/components/Dashboard/Commerce/CommerceUnitDialog.stx +17 -16
  46. package/resources/components/Dashboard/Commerce/CommerceVariantDeleteDialog.stx +10 -8
  47. package/resources/components/Dashboard/Commerce/CommerceVariantDialog.stx +17 -16
  48. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDeleteDialog.stx +11 -15
  49. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDialog.stx +17 -16
  50. package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDeleteDialog.stx +11 -15
  51. package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDialog.stx +17 -16
  52. package/resources/components/Dashboard/Commerce/Delivery/DriverDeleteDialog.stx +11 -15
  53. package/resources/components/Dashboard/Commerce/Delivery/DriverDialog.stx +17 -16
  54. package/resources/components/Dashboard/Commerce/Delivery/LicenseKeyDeleteDialog.stx +11 -15
  55. package/resources/components/Dashboard/Commerce/Delivery/LicenseKeyDialog.stx +17 -16
  56. package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodDeleteDialog.stx +11 -15
  57. package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodDialog.stx +17 -16
  58. package/resources/components/Dashboard/Commerce/Delivery/ShippingRateDeleteDialog.stx +11 -15
  59. package/resources/components/Dashboard/Commerce/Delivery/ShippingRateDialog.stx +17 -16
  60. package/resources/components/Dashboard/Commerce/Delivery/ShippingZoneDeleteDialog.stx +11 -15
  61. package/resources/components/Dashboard/Commerce/Delivery/ShippingZoneDialog.stx +17 -16
  62. package/resources/components/Dashboard/Commerce/PaymentDetailsDialog.stx +16 -15
  63. package/resources/components/Dashboard/Commerce/PaymentRefundDialog.stx +16 -15
  64. package/resources/components/Dashboard/Commerce/PrintDeviceDeleteDialog.stx +10 -11
  65. package/resources/components/Dashboard/Commerce/PrintDeviceDetailsDialog.stx +16 -14
  66. package/resources/components/Dashboard/Commerce/PrintDeviceDialog.stx +17 -16
  67. package/resources/components/Dashboard/Commerce/PrintLogDeleteDialog.stx +10 -7
  68. package/resources/components/Dashboard/Commerce/PrintLogDetailsDialog.stx +13 -10
  69. package/resources/components/Dashboard/Commerce/ProductWaitlistDeleteDialog.stx +10 -18
  70. package/resources/components/Dashboard/Commerce/ProductWaitlistEntryDialog.stx +96 -105
  71. package/resources/components/Dashboard/Commerce/RestaurantWaitlistDeleteDialog.stx +10 -18
  72. package/resources/components/Dashboard/Commerce/RestaurantWaitlistEntryDialog.stx +92 -95
  73. package/resources/components/Dashboard/Content/AuthorEditorDialog.stx +94 -0
  74. package/resources/components/Dashboard/Content/AuthorsDashboard.stx +39 -31
  75. package/resources/components/Dashboard/Content/BlogDashboard.stx +87 -176
  76. package/resources/components/Dashboard/Content/BlogPostEditorDialog.stx +195 -0
  77. package/resources/components/Dashboard/Content/CommentsDashboard.stx +14 -4
  78. package/resources/components/Dashboard/Content/ContentTaxonomyDashboard.stx +40 -55
  79. package/resources/components/Dashboard/Content/CreateFolderDialog.stx +45 -0
  80. package/resources/components/Dashboard/Content/FileDetailsDrawer.stx +31 -0
  81. package/resources/components/Dashboard/Content/FileManagerDashboard.stx +47 -107
  82. package/resources/components/Dashboard/Content/FileUploadDialog.stx +89 -0
  83. package/resources/components/Dashboard/Content/PageEditorDialog.stx +93 -0
  84. package/resources/components/Dashboard/Content/PagesDashboard.stx +36 -26
  85. package/resources/components/Dashboard/Content/PostEditorDialog.stx +176 -0
  86. package/resources/components/Dashboard/Content/PostsDashboard.stx +40 -64
  87. package/resources/components/Dashboard/Content/SeoDashboard.stx +18 -84
  88. package/resources/components/Dashboard/Content/SeoEditorDialog.stx +108 -0
  89. package/resources/components/Dashboard/Content/TaxonomyEditorDialog.stx +106 -0
  90. package/resources/components/Dashboard/CreateRecordModal.stx +72 -83
  91. package/resources/components/Dashboard/Email/InboxMessageList.stx +1 -1
  92. package/resources/components/Dashboard/GlobalSearch.stx +3 -9
  93. package/resources/components/Dashboard/Infrastructure/CloudDetailsDialog.stx +2 -16
  94. package/resources/components/Dashboard/Infrastructure/DnsRecordDialog.stx +4 -16
  95. package/resources/components/Dashboard/Infrastructure/LogDetailsDialog.stx +4 -19
  96. package/resources/components/Dashboard/Infrastructure/MailboxDetailsDialog.stx +6 -16
  97. package/resources/components/Dashboard/Infrastructure/ServerInspectorDialog.stx +105 -0
  98. package/resources/components/Dashboard/Infrastructure/ServersDashboard.stx +5 -59
  99. package/resources/components/Dashboard/Jobs/JobHistory.stx +1 -11
  100. package/resources/components/Dashboard/Library/ComponentsDashboard.stx +11 -60
  101. package/resources/components/Dashboard/Library/FunctionsDashboard.stx +11 -15
  102. package/resources/components/Dashboard/Library/SourceScaffoldDialog.stx +95 -0
  103. package/resources/components/Dashboard/Management/AssignmentDrawer.stx +93 -0
  104. package/resources/components/Dashboard/Management/PermissionsDashboard.stx +47 -129
  105. package/resources/components/Dashboard/Marketing/CampaignDeliveryDialog.stx +5 -8
  106. package/resources/components/Dashboard/Marketing/CampaignDialog.stx +17 -17
  107. package/resources/components/Dashboard/Marketing/MarketingListDialog.stx +17 -17
  108. package/resources/components/Dashboard/Marketing/MarketingListsTable.stx +1 -1
  109. package/resources/components/Dashboard/Marketing/SocialPostDialog.stx +17 -16
  110. package/resources/components/Dashboard/Models/EditRecordModal.stx +81 -0
  111. package/resources/components/Dashboard/Models/ModelRecordActions.stx +30 -0
  112. package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +42 -94
  113. package/resources/components/Dashboard/Navbar.stx +5 -5
  114. package/resources/components/Dashboard/Notifications/NotificationDeliveryDialog.stx +40 -60
  115. package/resources/components/Dashboard/UI/Button.stx +4 -0
  116. package/resources/components/Dashboard/UI/ConfirmDialog.stx +4 -0
  117. package/resources/components/Dashboard/UI/Drawer.stx +114 -0
  118. package/resources/components/Dashboard/UI/Modal.stx +19 -12
  119. package/resources/components/Docs/Demo/ComboboxDemo.stx +4 -5
  120. package/resources/components/Docs/Demo/DropdownDemo.stx +12 -5
  121. package/resources/components/Docs/Demo/ListboxDemo.stx +14 -6
  122. package/resources/components/Docs/Demo/PopoverDemo.stx +10 -5
  123. package/resources/components/Docs/Demo/RadioGroupDemo.stx +14 -6
  124. package/resources/components/Docs/Demo/StepperDemo.stx +4 -5
  125. package/resources/components/Docs/TeamMember.stx +16 -9
  126. package/resources/components/Forum/ForumReplyForm.stx +7 -7
  127. package/resources/components/Forum/ForumSidebar.stx +10 -6
  128. package/resources/components/Marketing/Faqs.stx +5 -5
  129. package/resources/components/Marketing/Feature.stx +11 -6
  130. package/resources/components/Marketing/Layout.stx +12 -8
  131. package/resources/components/Marketing/Pricing.stx +11 -5
  132. package/resources/components/Marketing/PrimaryFeatures.stx +4 -5
  133. package/resources/components/Marketing/PrimaryFeatures2.stx +6 -5
  134. package/resources/components/Marketing/SecondaryFeatures.stx +10 -5
  135. package/resources/components/Marketing/Testimonials.stx +8 -5
  136. package/resources/views/index.stx +4 -4
  137. package/views/system-tray/index.stx +1 -1
@@ -5,13 +5,15 @@ const open = useReactiveProp('open', false)
5
5
  const record = useReactiveProp('record', null as ProductVariantRecord | null)
6
6
  const busy = useReactiveProp('busy', false)
7
7
  const emit = defineEmits()
8
+ const description = derived(() => `${record()?.variant || 'This product variant'} will be permanently removed.${record()?.productName ? ` The linked product ${record()?.productName} will remain.` : ''}`)
8
9
  </script>
9
10
 
10
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
11
- <button type="button" aria-label="Close delete product variant dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
12
- <section role="dialog" aria-modal="true" aria-labelledby="product-variant-delete-title" class="relative p-6 max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
13
- <h2 id="product-variant-delete-title" class="font-semibold text-gray-900 text-lg dark:text-white">Delete product variant?</h2>
14
- <p class="mt-2 text-gray-600 text-sm dark:text-neutral-300"><span class="font-medium">{{ record()?.variant }}</span> will be permanently removed. <span :if="record()?.productName">The linked product {{ record()?.productName }} will remain.</span></p>
15
- <footer class="flex gap-2 justify-end mt-6"><Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button><Button variant="danger" :loading="busy()" @click="emit('confirm')">Delete product variant</Button></footer>
16
- </section>
17
- </div>
11
+ <ConfirmDialog
12
+ :isOpen="open()"
13
+ title="Delete product variant?"
14
+ :description="description()"
15
+ confirmLabel="Delete product variant"
16
+ :busy="busy()"
17
+ @close="emit('close')"
18
+ @confirm="emit('confirm')"
19
+ />
@@ -56,17 +56,18 @@ function errorMessages(): string[] {
56
56
  }
57
57
  </script>
58
58
 
59
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
60
- <button type="button" aria-label="Close product variant dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
61
- <section role="dialog" aria-modal="true" aria-labelledby="product-variant-dialog-title" class="relative max-w-lg w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
62
- <form @submit.prevent="submit">
63
- <div class="p-6">
64
- <h2 id="product-variant-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit product variant' : 'Add product variant' }}</h2>
65
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Fields and product assignment follow the ProductVariant model.</p>
66
- <ul :if="errorMessages().length > 0" class="mt-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">
59
+ <Modal
60
+ :isOpen="open()"
61
+ :title="mode() === 'edit' ? 'Edit product variant' : 'Add product variant'"
62
+ description="Fields and product assignment follow the ProductVariant model."
63
+ size="md"
64
+ @close="emit('close')"
65
+ >
66
+ <form id="product-variant-form" @submit.prevent="submit">
67
+ <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">
67
68
  <template :for="message in errorMessages()" :key="message"><li>{{ message }}</li></template>
68
69
  </ul>
69
- <div class="mt-5 space-y-4">
70
+ <div class="space-y-4">
70
71
  <div class="grid gap-4 sm:grid-cols-2">
71
72
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Variant</span><input x-model="variant" type="text" maxlength="100" 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>
72
73
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Type</span><input x-model="type" type="text" list="product-variant-types" 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" /><datalist id="product-variant-types"><template :for="variantType in types()" :key="variantType"><option :value="variantType"></option></template></datalist></label>
@@ -76,11 +77,11 @@ function errorMessages(): string[] {
76
77
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Options</span><textarea x-model="options" rows="3" placeholder="Small, Medium, Large" 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><span class="block mt-1 text-gray-500 text-xs dark:text-neutral-400">Separate options with commas or new lines.</span></label>
77
78
  <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="inactive">Inactive</option><option value="draft">Draft</option></select></label>
78
79
  </div>
79
- </div>
80
- <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">
81
- <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
82
- <Button type="submit" :loading="busy()" :disabled="!variant().trim() || !type().trim()">{{ mode() === 'edit' ? 'Save product variant' : 'Add product variant' }}</Button>
83
- </footer>
84
80
  </form>
85
- </section>
86
- </div>
81
+ <template #footer>
82
+ <div class="flex gap-2 justify-end w-full">
83
+ <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
84
+ <Button type="submit" form="product-variant-form" :loading="busy()" :disabled="!variant().trim() || !type().trim()">{{ mode() === 'edit' ? 'Save product variant' : 'Add product variant' }}</Button>
85
+ </div>
86
+ </template>
87
+ </Modal>
@@ -9,20 +9,16 @@ const record = useReactiveProp('record', null as DeliveryRouteRecord | null)
9
9
  const busy = useReactiveProp('busy', false)
10
10
  const error = useReactiveProp('error', '')
11
11
  const emit = defineEmits()
12
+ const description = derived(() => `Delete route #${record()?.id} for ${record()?.driver || 'the selected driver'} permanently.`)
12
13
  </script>
13
14
 
14
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
15
- <button type="button" aria-label="Close route delete confirmation" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
16
- <section role="alertdialog" aria-modal="true" aria-labelledby="delivery-route-delete-title" class="relative max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
17
- <div class="p-6">
18
- <div class="flex items-center justify-center h-10 w-10 text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-delete-02"></span></div>
19
- <h2 id="delivery-route-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete delivery route?</h2>
20
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete route #{{ record()?.id }} for {{ record()?.driver }} permanently.</p>
21
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
22
- </div>
23
- <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">
24
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
25
- <Button :loading="busy()" variant="danger" @click="emit('confirm')">Delete route</Button>
26
- </footer>
27
- </section>
28
- </div>
15
+ <ConfirmDialog
16
+ :isOpen="open() && Boolean(record())"
17
+ title="Delete delivery route?"
18
+ :description="description()"
19
+ confirmLabel="Delete route"
20
+ :busy="busy()"
21
+ :error="error()"
22
+ @close="emit('close')"
23
+ @confirm="emit('confirm')"
24
+ />
@@ -115,16 +115,17 @@ function submit(): void {
115
115
  }
116
116
  </script>
117
117
 
118
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
119
- <button type="button" aria-label="Close delivery route dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
120
- <section role="dialog" aria-modal="true" aria-labelledby="delivery-route-dialog-title" class="overflow-y-auto relative max-h-[92dvh] max-w-xl w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
121
- <form @submit.prevent="submit()">
122
- <div class="p-6">
123
- <h2 id="delivery-route-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit delivery route' : 'Add delivery route' }}</h2>
124
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Assign a persisted driver and record the operational route metrics.</p>
125
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
118
+ <Modal
119
+ :isOpen="open()"
120
+ :title="mode() === 'edit' ? 'Edit delivery route' : 'Add delivery route'"
121
+ description="Assign a persisted driver and record the operational route metrics."
122
+ size="lg"
123
+ @close="emit('close')"
124
+ >
125
+ <form id="delivery-route-form" @submit.prevent="submit()">
126
+ <div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
126
127
 
127
- <div class="mt-5 space-y-4">
128
+ <div class="space-y-4">
128
129
  <label class="block">
129
130
  <span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Driver</span>
130
131
  <select x-model="driverId" 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">
@@ -143,11 +144,11 @@ function submit(): void {
143
144
  </div>
144
145
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Last active</span><input x-model="lastActive" type="datetime-local" 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>
145
146
  </div>
146
- </div>
147
- <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">
148
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
149
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add route' }}</Button>
150
- </footer>
151
147
  </form>
152
- </section>
153
- </div>
148
+ <template #footer>
149
+ <div class="flex gap-2 justify-end w-full">
150
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
151
+ <Button type="submit" form="delivery-route-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add route' }}</Button>
152
+ </div>
153
+ </template>
154
+ </Modal>
@@ -9,20 +9,16 @@ const record = useReactiveProp('record', null as DigitalDeliveryRecord | null)
9
9
  const busy = useReactiveProp('busy', false)
10
10
  const error = useReactiveProp('error', '')
11
11
  const emit = defineEmits()
12
+ const description = derived(() => `Delete ${record()?.name || 'the selected digital delivery'} permanently.`)
12
13
  </script>
13
14
 
14
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
15
- <button type="button" aria-label="Close digital delivery deletion" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
16
- <section role="alertdialog" aria-modal="true" aria-labelledby="digital-delivery-delete-title" class="relative max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
17
- <div class="p-6">
18
- <div class="flex items-center justify-center h-10 w-10 text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-delete-02"></span></div>
19
- <h2 id="digital-delivery-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete digital delivery?</h2>
20
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete {{ record()?.name }} permanently.</p>
21
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
22
- </div>
23
- <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">
24
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
25
- <Button :loading="busy()" variant="danger" @click="emit('confirm')">Delete method</Button>
26
- </footer>
27
- </section>
28
- </div>
15
+ <ConfirmDialog
16
+ :isOpen="open() && Boolean(record())"
17
+ title="Delete digital delivery?"
18
+ :description="description()"
19
+ confirmLabel="Delete delivery"
20
+ :busy="busy()"
21
+ :error="error()"
22
+ @close="emit('close')"
23
+ @confirm="emit('confirm')"
24
+ />
@@ -79,16 +79,17 @@ function submit(): void {
79
79
  }
80
80
  </script>
81
81
 
82
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
83
- <button type="button" aria-label="Close digital delivery dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
84
- <section role="dialog" aria-modal="true" aria-labelledby="digital-delivery-dialog-title" class="overflow-y-auto relative max-h-[92dvh] max-w-xl w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
85
- <form @submit.prevent="submit()">
86
- <div class="p-6">
87
- <h2 id="digital-delivery-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit digital delivery' : 'Add digital delivery' }}</h2>
88
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Configure access, expiry, download limits, and fulfillment behavior.</p>
89
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
82
+ <Modal
83
+ :isOpen="open()"
84
+ :title="mode() === 'edit' ? 'Edit digital delivery' : 'Add digital delivery'"
85
+ description="Configure access, expiry, download limits, and fulfillment behavior."
86
+ size="lg"
87
+ @close="emit('close')"
88
+ >
89
+ <form id="digital-delivery-form" @submit.prevent="submit()">
90
+ <div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
90
91
 
91
- <div class="mt-5 space-y-4">
92
+ <div class="space-y-4">
92
93
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Method name</span><input x-model="name" type="text" required maxlength="255" autocomplete="off" placeholder="Secure download" 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>
93
94
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Description</span><textarea x-model="description" required rows="3" placeholder="Explain how customers receive and access files." 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 resize-y"></textarea></label>
94
95
  <div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
@@ -101,11 +102,11 @@ function submit(): void {
101
102
  </div>
102
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="inactive">Inactive</option></select></label>
103
104
  </div>
104
- </div>
105
- <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">
106
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
107
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add method' }}</Button>
108
- </footer>
109
105
  </form>
110
- </section>
111
- </div>
106
+ <template #footer>
107
+ <div class="flex gap-2 justify-end w-full">
108
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
109
+ <Button type="submit" form="digital-delivery-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add method' }}</Button>
110
+ </div>
111
+ </template>
112
+ </Modal>
@@ -9,20 +9,16 @@ const record = useReactiveProp('record', null as DriverRecord | null)
9
9
  const busy = useReactiveProp('busy', false)
10
10
  const error = useReactiveProp('error', '')
11
11
  const emit = defineEmits()
12
+ const description = derived(() => `Delete ${record()?.name || 'the selected driver'} permanently. Drivers referenced by delivery routes may be protected by the database.`)
12
13
  </script>
13
14
 
14
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
15
- <button type="button" aria-label="Close delete confirmation" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
16
- <section role="alertdialog" aria-modal="true" aria-labelledby="driver-delete-title" class="relative max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
17
- <div class="p-6">
18
- <div class="flex items-center justify-center h-10 w-10 text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-delete-02"></span></div>
19
- <h2 id="driver-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete driver?</h2>
20
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete {{ record()?.name }} permanently. Drivers referenced by delivery routes may be protected by the database.</p>
21
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
22
- </div>
23
- <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">
24
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
25
- <Button :loading="busy()" variant="danger" @click="emit('confirm')">Delete driver</Button>
26
- </footer>
27
- </section>
28
- </div>
15
+ <ConfirmDialog
16
+ :isOpen="open() && Boolean(record())"
17
+ title="Delete driver?"
18
+ :description="description()"
19
+ confirmLabel="Delete driver"
20
+ :busy="busy()"
21
+ :error="error()"
22
+ @close="emit('close')"
23
+ @confirm="emit('confirm')"
24
+ />
@@ -84,16 +84,17 @@ function submit(): void {
84
84
  }
85
85
  </script>
86
86
 
87
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
88
- <button type="button" aria-label="Close driver dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
89
- <section role="dialog" aria-modal="true" aria-labelledby="driver-dialog-title" class="overflow-y-auto relative max-h-[92dvh] max-w-lg w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
90
- <form @submit.prevent="submit()">
91
- <div class="p-6">
92
- <h2 id="driver-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit driver' : 'Add driver' }}</h2>
93
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Manage operational identity, vehicle assignment, availability, and the optional User relationship.</p>
94
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
87
+ <Modal
88
+ :isOpen="open()"
89
+ :title="mode() === 'edit' ? 'Edit driver' : 'Add driver'"
90
+ description="Manage operational identity, vehicle assignment, availability, and the optional User relationship."
91
+ size="md"
92
+ @close="emit('close')"
93
+ >
94
+ <form id="driver-form" @submit.prevent="submit()">
95
+ <div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
95
96
 
96
- <div class="mt-5 space-y-4">
97
+ <div class="space-y-4">
97
98
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Driver name</span><input x-model="name" type="text" required maxlength="255" autocomplete="name" placeholder="Alex Morgan" 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
99
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Phone</span><input x-model="phone" type="tel" required maxlength="255" autocomplete="tel" placeholder="+1 555 0100" 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
  <div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
@@ -109,11 +110,11 @@ function submit(): void {
109
110
  </select>
110
111
  </label>
111
112
  </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">
114
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
115
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add driver' }}</Button>
116
- </footer>
117
113
  </form>
118
- </section>
119
- </div>
114
+ <template #footer>
115
+ <div class="flex gap-2 justify-end w-full">
116
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
117
+ <Button type="submit" form="driver-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add driver' }}</Button>
118
+ </div>
119
+ </template>
120
+ </Modal>
@@ -9,20 +9,16 @@ const record = useReactiveProp('record', null as LicenseKeyRecord | null)
9
9
  const busy = useReactiveProp('busy', false)
10
10
  const error = useReactiveProp('error', '')
11
11
  const emit = defineEmits()
12
+ const description = derived(() => `Delete ${record()?.key || 'the selected license key'} permanently.`)
12
13
  </script>
13
14
 
14
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
15
- <button type="button" aria-label="Close license key deletion" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
16
- <section role="alertdialog" aria-modal="true" aria-labelledby="license-key-delete-title" class="relative max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
17
- <div class="p-6">
18
- <div class="flex items-center justify-center h-10 w-10 text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-delete-02"></span></div>
19
- <h2 id="license-key-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete license key?</h2>
20
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete <span class="font-mono">{{ record()?.key }}</span> permanently.</p>
21
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
22
- </div>
23
- <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">
24
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
25
- <Button :loading="busy()" variant="danger" @click="emit('confirm')">Delete key</Button>
26
- </footer>
27
- </section>
28
- </div>
15
+ <ConfirmDialog
16
+ :isOpen="open() && Boolean(record())"
17
+ title="Delete license key?"
18
+ :description="description()"
19
+ confirmLabel="Delete key"
20
+ :busy="busy()"
21
+ :error="error()"
22
+ @close="emit('close')"
23
+ @confirm="emit('confirm')"
24
+ />
@@ -121,16 +121,17 @@ function submit(): void {
121
121
  }
122
122
  </script>
123
123
 
124
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
125
- <button type="button" aria-label="Close license key dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
126
- <section role="dialog" aria-modal="true" aria-labelledby="license-key-dialog-title" class="overflow-y-auto relative max-h-[92dvh] max-w-xl w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
127
- <form @submit.prevent="submit()">
128
- <div class="p-6">
129
- <h2 id="license-key-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit license key' : 'Add license key' }}</h2>
130
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Generate a unique key and optionally assign its customer, product, and order relationships.</p>
131
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
124
+ <Modal
125
+ :isOpen="open()"
126
+ :title="mode() === 'edit' ? 'Edit license key' : 'Add license key'"
127
+ description="Generate a unique key and optionally assign its customer, product, and order relationships."
128
+ size="lg"
129
+ @close="emit('close')"
130
+ >
131
+ <form id="license-key-form" @submit.prevent="submit()">
132
+ <div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
132
133
 
133
- <div class="mt-5 space-y-4">
134
+ <div class="space-y-4">
134
135
  <div>
135
136
  <label for="license-key-value" class="font-medium text-gray-700 text-sm dark:text-neutral-200">License key</label>
136
137
  <span class="flex gap-2 mt-1">
@@ -149,11 +150,11 @@ function submit(): void {
149
150
  </div>
150
151
  <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="unassigned">Unassigned</option><option value="active">Active</option><option value="inactive">Inactive</option></select></label>
151
152
  </div>
152
- </div>
153
- <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">
154
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
155
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add key' }}</Button>
156
- </footer>
157
153
  </form>
158
- </section>
159
- </div>
154
+ <template #footer>
155
+ <div class="flex gap-2 justify-end w-full">
156
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
157
+ <Button type="submit" form="license-key-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add key' }}</Button>
158
+ </div>
159
+ </template>
160
+ </Modal>
@@ -9,20 +9,16 @@ const record = useReactiveProp('record', null as ShippingMethodRecord | null)
9
9
  const busy = useReactiveProp('busy', false)
10
10
  const error = useReactiveProp('error', '')
11
11
  const emit = defineEmits()
12
+ const description = derived(() => `Delete ${record()?.name || 'the selected shipping method'} permanently. Methods referenced by shipping zones may be protected by the database.`)
12
13
  </script>
13
14
 
14
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
15
- <button type="button" aria-label="Close delete confirmation" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
16
- <section role="alertdialog" aria-modal="true" aria-labelledby="shipping-method-delete-title" class="relative max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
17
- <div class="p-6">
18
- <div class="flex items-center justify-center h-10 w-10 text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-delete-02"></span></div>
19
- <h2 id="shipping-method-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete shipping method?</h2>
20
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete {{ record()?.name }} permanently. Methods referenced by shipping zones may be protected by the database.</p>
21
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
22
- </div>
23
- <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">
24
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
25
- <Button :loading="busy()" variant="danger" @click="emit('confirm')">Delete method</Button>
26
- </footer>
27
- </section>
28
- </div>
15
+ <ConfirmDialog
16
+ :isOpen="open() && Boolean(record())"
17
+ title="Delete shipping method?"
18
+ :description="description()"
19
+ confirmLabel="Delete method"
20
+ :busy="busy()"
21
+ :error="error()"
22
+ @close="emit('close')"
23
+ @confirm="emit('confirm')"
24
+ />
@@ -65,16 +65,17 @@ function submit(): void {
65
65
  }
66
66
  </script>
67
67
 
68
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
69
- <button type="button" aria-label="Close shipping method dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
70
- <section role="dialog" aria-modal="true" aria-labelledby="shipping-method-dialog-title" class="overflow-y-auto relative max-h-[92dvh] max-w-lg w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
71
- <form @submit.prevent="submit">
72
- <div class="p-6">
73
- <h2 id="shipping-method-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit shipping method' : 'Add shipping method' }}</h2>
74
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Rates are stored as integer minor units to keep pricing exact.</p>
75
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
68
+ <Modal
69
+ :isOpen="open()"
70
+ :title="mode() === 'edit' ? 'Edit shipping method' : 'Add shipping method'"
71
+ description="Rates are stored as integer minor units to keep pricing exact."
72
+ size="md"
73
+ @close="emit('close')"
74
+ >
75
+ <form id="shipping-method-form" @submit.prevent="submit">
76
+ <div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
76
77
 
77
- <div class="mt-5 space-y-4">
78
+ <div class="space-y-4">
78
79
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Method name</span><input x-model="name" type="text" required maxlength="100" autocomplete="off" placeholder="Ground shipping" 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>
79
80
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Description</span><textarea x-model="description" rows="3" maxlength="500" placeholder="How and when this method is used" 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>
80
81
  <div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
@@ -83,11 +84,11 @@ function submit(): void {
83
84
  </div>
84
85
  <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="inactive">Inactive</option><option value="draft">Draft</option></select></label>
85
86
  </div>
86
- </div>
87
- <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">
88
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
89
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add method' }}</Button>
90
- </footer>
91
87
  </form>
92
- </section>
93
- </div>
88
+ <template #footer>
89
+ <div class="flex gap-2 justify-end w-full">
90
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
91
+ <Button type="submit" form="shipping-method-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add method' }}</Button>
92
+ </div>
93
+ </template>
94
+ </Modal>
@@ -10,20 +10,16 @@ const record = useReactiveProp('record', null as ShippingRateRecord | null)
10
10
  const busy = useReactiveProp('busy', false)
11
11
  const error = useReactiveProp('error', '')
12
12
  const emit = defineEmits()
13
+ const description = derived(() => `Delete the ${record()?.shipping_method?.name || 'selected method'} rate for ${record()?.shipping_zone?.name || 'the selected zone'} permanently.`)
13
14
  </script>
14
15
 
15
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
16
- <button type="button" aria-label="Close delete confirmation" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
17
- <section role="alertdialog" aria-modal="true" aria-labelledby="shipping-rate-delete-title" class="relative max-w-md w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
18
- <div class="p-6">
19
- <div class="flex items-center justify-center h-10 w-10 text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-delete-02"></span></div>
20
- <h2 id="shipping-rate-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete shipping rate?</h2>
21
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete the {{ record()?.shipping_method?.name || 'selected method' }} rate for {{ record()?.shipping_zone?.name || 'the selected zone' }} permanently.</p>
22
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
23
- </div>
24
- <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">
25
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
26
- <Button :loading="busy()" variant="danger" @click="emit('confirm')">Delete rate</Button>
27
- </footer>
28
- </section>
29
- </div>
16
+ <ConfirmDialog
17
+ :isOpen="open() && Boolean(record())"
18
+ title="Delete shipping rate?"
19
+ :description="description()"
20
+ confirmLabel="Delete rate"
21
+ :busy="busy()"
22
+ :error="error()"
23
+ @close="emit('close')"
24
+ @confirm="emit('confirm')"
25
+ />
@@ -83,16 +83,17 @@ function submit(): void {
83
83
  }
84
84
  </script>
85
85
 
86
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
87
- <button type="button" aria-label="Close shipping rate dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
88
- <section role="dialog" aria-modal="true" aria-labelledby="shipping-rate-dialog-title" class="overflow-y-auto relative max-h-[92dvh] max-w-lg w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
89
- <form @submit.prevent="submit()">
90
- <div class="p-6">
91
- <h2 id="shipping-rate-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit shipping rate' : 'Add shipping rate' }}</h2>
92
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Connect a persisted method and zone to an exact weight band.</p>
93
- <div :if="error()" class="mt-4 p-3 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">{{ error() }}</div>
86
+ <Modal
87
+ :isOpen="open()"
88
+ :title="mode() === 'edit' ? 'Edit shipping rate' : 'Add shipping rate'"
89
+ description="Connect a persisted method and zone to an exact weight band."
90
+ size="md"
91
+ @close="emit('close')"
92
+ >
93
+ <form id="shipping-rate-form" @submit.prevent="submit()">
94
+ <div :if="error()" class="mb-5 p-3 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">{{ error() }}</div>
94
95
 
95
- <div class="mt-5 space-y-4">
96
+ <div class="space-y-4">
96
97
  <label class="block">
97
98
  <span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Shipping method</span>
98
99
  <select x-model="methodId" 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">
@@ -113,11 +114,11 @@ function submit(): void {
113
114
  </div>
114
115
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Rate</span><input x-model="amount" type="number" required min="0" step="0.01" inputmode="decimal" placeholder="5.99" class="mt-1 px-3 py-2 w-full tabular-nums 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>
115
116
  </div>
116
- </div>
117
- <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">
118
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
119
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add rate' }}</Button>
120
- </footer>
121
117
  </form>
122
- </section>
123
- </div>
118
+ <template #footer>
119
+ <div class="flex gap-2 justify-end w-full">
120
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
121
+ <Button type="submit" form="shipping-rate-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add rate' }}</Button>
122
+ </div>
123
+ </template>
124
+ </Modal>