@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.
Files changed (141) hide show
  1. package/ai/skills/stacks-browse/SKILL.md +7 -5
  2. package/ai/skills/stacks-browse/scripts/browse.ts +68 -9
  3. package/ai/skills/stacks-dashboard/SKILL.md +4 -0
  4. package/app/Actions/AI/AskAction.ts +12 -17
  5. package/app/Actions/AI/SummaryAction.ts +12 -17
  6. package/app/Actions/Dashboard/Models/ModelShowAction.ts +4 -1
  7. package/app/Actions/Dashboard/Models/model-write.ts +5 -0
  8. package/app/Actions/LogAction.ts +11 -11
  9. package/ide/vscode/package.json +1 -1
  10. package/package.json +1 -1
  11. package/resources/components/Buttons/Counter.stx +1 -1
  12. package/resources/components/Buttons/ToggleDark.stx +1 -1
  13. package/resources/components/Dashboard/App/RequestDetailsDialog.stx +42 -0
  14. package/resources/components/Dashboard/App/RequestsOverview.stx +1 -21
  15. package/resources/components/Dashboard/Auth/AccessTokenCreateDialog.stx +155 -0
  16. package/resources/components/Dashboard/Auth/AccessTokens.stx +41 -129
  17. package/resources/components/Dashboard/Ci/CiDashboard.stx +1 -157
  18. package/resources/components/Dashboard/Ci/CiRunHistoryDrawer.stx +127 -0
  19. package/resources/components/Dashboard/Commerce/CommerceCategoryDeleteDialog.stx +10 -8
  20. package/resources/components/Dashboard/Commerce/CommerceCategoryDetailsDialog.stx +22 -17
  21. package/resources/components/Dashboard/Commerce/CommerceCategoryDialog.stx +14 -13
  22. package/resources/components/Dashboard/Commerce/CommerceCouponDeleteDialog.stx +10 -7
  23. package/resources/components/Dashboard/Commerce/CommerceCouponDetailsDialog.stx +25 -19
  24. package/resources/components/Dashboard/Commerce/CommerceCouponDialog.stx +14 -11
  25. package/resources/components/Dashboard/Commerce/CommerceCustomerDeleteDialog.stx +10 -8
  26. package/resources/components/Dashboard/Commerce/CommerceCustomerDetailsDialog.stx +24 -16
  27. package/resources/components/Dashboard/Commerce/CommerceCustomerDialog.stx +14 -13
  28. package/resources/components/Dashboard/Commerce/CommerceGiftCardDeleteDialog.stx +10 -8
  29. package/resources/components/Dashboard/Commerce/CommerceGiftCardDetailsDialog.stx +16 -14
  30. package/resources/components/Dashboard/Commerce/CommerceGiftCardDialog.stx +17 -16
  31. package/resources/components/Dashboard/Commerce/CommerceManufacturerDeleteDialog.stx +14 -16
  32. package/resources/components/Dashboard/Commerce/CommerceManufacturerDialog.stx +17 -16
  33. package/resources/components/Dashboard/Commerce/CommerceOrderDeleteDialog.stx +10 -8
  34. package/resources/components/Dashboard/Commerce/CommerceOrderDetailsDialog.stx +15 -10
  35. package/resources/components/Dashboard/Commerce/CommerceOrderDialog.stx +14 -13
  36. package/resources/components/Dashboard/Commerce/CommercePosCheckoutDialog.stx +30 -25
  37. package/resources/components/Dashboard/Commerce/CommercePosReceiptDialog.stx +27 -20
  38. package/resources/components/Dashboard/Commerce/CommerceProductDeleteDialog.stx +14 -14
  39. package/resources/components/Dashboard/Commerce/CommerceProductDialog.stx +14 -13
  40. package/resources/components/Dashboard/Commerce/CommerceReviewDeleteDialog.stx +10 -18
  41. package/resources/components/Dashboard/Commerce/CommerceReviewEditDialog.stx +17 -16
  42. package/resources/components/Dashboard/Commerce/CommerceTaxDeleteDialog.stx +14 -14
  43. package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +17 -16
  44. package/resources/components/Dashboard/Commerce/CommerceTaxesTable.stx +1 -1
  45. package/resources/components/Dashboard/Commerce/CommerceUnitDeleteDialog.stx +14 -17
  46. package/resources/components/Dashboard/Commerce/CommerceUnitDialog.stx +17 -16
  47. package/resources/components/Dashboard/Commerce/CommerceVariantDeleteDialog.stx +10 -8
  48. package/resources/components/Dashboard/Commerce/CommerceVariantDialog.stx +17 -16
  49. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDeleteDialog.stx +11 -15
  50. package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDialog.stx +17 -16
  51. package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDeleteDialog.stx +11 -15
  52. package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDialog.stx +17 -16
  53. package/resources/components/Dashboard/Commerce/Delivery/DriverDeleteDialog.stx +11 -15
  54. package/resources/components/Dashboard/Commerce/Delivery/DriverDialog.stx +17 -16
  55. package/resources/components/Dashboard/Commerce/Delivery/LicenseKeyDeleteDialog.stx +11 -15
  56. package/resources/components/Dashboard/Commerce/Delivery/LicenseKeyDialog.stx +17 -16
  57. package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodDeleteDialog.stx +11 -15
  58. package/resources/components/Dashboard/Commerce/Delivery/ShippingMethodDialog.stx +17 -16
  59. package/resources/components/Dashboard/Commerce/Delivery/ShippingRateDeleteDialog.stx +11 -15
  60. package/resources/components/Dashboard/Commerce/Delivery/ShippingRateDialog.stx +17 -16
  61. package/resources/components/Dashboard/Commerce/Delivery/ShippingZoneDeleteDialog.stx +11 -15
  62. package/resources/components/Dashboard/Commerce/Delivery/ShippingZoneDialog.stx +17 -16
  63. package/resources/components/Dashboard/Commerce/PaymentDetailsDialog.stx +16 -15
  64. package/resources/components/Dashboard/Commerce/PaymentRefundDialog.stx +16 -15
  65. package/resources/components/Dashboard/Commerce/PrintDeviceDeleteDialog.stx +10 -11
  66. package/resources/components/Dashboard/Commerce/PrintDeviceDetailsDialog.stx +16 -14
  67. package/resources/components/Dashboard/Commerce/PrintDeviceDialog.stx +17 -16
  68. package/resources/components/Dashboard/Commerce/PrintLogDeleteDialog.stx +10 -7
  69. package/resources/components/Dashboard/Commerce/PrintLogDetailsDialog.stx +13 -10
  70. package/resources/components/Dashboard/Commerce/ProductWaitlistDeleteDialog.stx +10 -18
  71. package/resources/components/Dashboard/Commerce/ProductWaitlistEntryDialog.stx +96 -105
  72. package/resources/components/Dashboard/Commerce/RestaurantWaitlistDeleteDialog.stx +10 -18
  73. package/resources/components/Dashboard/Commerce/RestaurantWaitlistEntryDialog.stx +92 -95
  74. package/resources/components/Dashboard/Content/AuthorEditorDialog.stx +94 -0
  75. package/resources/components/Dashboard/Content/AuthorsDashboard.stx +39 -31
  76. package/resources/components/Dashboard/Content/BlogDashboard.stx +87 -176
  77. package/resources/components/Dashboard/Content/BlogPostEditorDialog.stx +195 -0
  78. package/resources/components/Dashboard/Content/CommentsDashboard.stx +14 -4
  79. package/resources/components/Dashboard/Content/ContentTaxonomyDashboard.stx +40 -55
  80. package/resources/components/Dashboard/Content/CreateFolderDialog.stx +45 -0
  81. package/resources/components/Dashboard/Content/FileDetailsDrawer.stx +31 -0
  82. package/resources/components/Dashboard/Content/FileManagerDashboard.stx +47 -107
  83. package/resources/components/Dashboard/Content/FileUploadDialog.stx +89 -0
  84. package/resources/components/Dashboard/Content/PageEditorDialog.stx +93 -0
  85. package/resources/components/Dashboard/Content/PagesDashboard.stx +36 -26
  86. package/resources/components/Dashboard/Content/PostEditorDialog.stx +176 -0
  87. package/resources/components/Dashboard/Content/PostsDashboard.stx +40 -64
  88. package/resources/components/Dashboard/Content/SeoDashboard.stx +18 -84
  89. package/resources/components/Dashboard/Content/SeoEditorDialog.stx +108 -0
  90. package/resources/components/Dashboard/Content/TaxonomyEditorDialog.stx +106 -0
  91. package/resources/components/Dashboard/CreateRecordModal.stx +72 -83
  92. package/resources/components/Dashboard/Email/InboxMessageList.stx +1 -1
  93. package/resources/components/Dashboard/GlobalSearch.stx +3 -9
  94. package/resources/components/Dashboard/Infrastructure/CloudDetailsDialog.stx +2 -16
  95. package/resources/components/Dashboard/Infrastructure/DnsRecordDialog.stx +4 -16
  96. package/resources/components/Dashboard/Infrastructure/LogDetailsDialog.stx +4 -19
  97. package/resources/components/Dashboard/Infrastructure/MailboxDetailsDialog.stx +6 -16
  98. package/resources/components/Dashboard/Infrastructure/ServerInspectorDialog.stx +105 -0
  99. package/resources/components/Dashboard/Infrastructure/ServersDashboard.stx +5 -59
  100. package/resources/components/Dashboard/Jobs/JobHistory.stx +1 -11
  101. package/resources/components/Dashboard/Library/ComponentsDashboard.stx +11 -60
  102. package/resources/components/Dashboard/Library/FunctionsDashboard.stx +11 -15
  103. package/resources/components/Dashboard/Library/SourceScaffoldDialog.stx +95 -0
  104. package/resources/components/Dashboard/Management/AssignmentDrawer.stx +93 -0
  105. package/resources/components/Dashboard/Management/PermissionsDashboard.stx +47 -129
  106. package/resources/components/Dashboard/Marketing/CampaignDeleteDialog.stx +11 -12
  107. package/resources/components/Dashboard/Marketing/CampaignDeliveryDialog.stx +5 -8
  108. package/resources/components/Dashboard/Marketing/CampaignDialog.stx +17 -17
  109. package/resources/components/Dashboard/Marketing/MarketingListDeleteDialog.stx +15 -15
  110. package/resources/components/Dashboard/Marketing/MarketingListDialog.stx +17 -17
  111. package/resources/components/Dashboard/Marketing/MarketingListsTable.stx +1 -1
  112. package/resources/components/Dashboard/Marketing/SocialPostDeleteDialog.stx +11 -12
  113. package/resources/components/Dashboard/Marketing/SocialPostDialog.stx +17 -16
  114. package/resources/components/Dashboard/Models/EditRecordModal.stx +81 -0
  115. package/resources/components/Dashboard/Models/ModelRecordActions.stx +30 -0
  116. package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +42 -94
  117. package/resources/components/Dashboard/Navbar.stx +5 -5
  118. package/resources/components/Dashboard/Notifications/NotificationDeliveryDialog.stx +40 -60
  119. package/resources/components/Dashboard/UI/Button.stx +4 -0
  120. package/resources/components/Dashboard/UI/ConfirmDialog.stx +55 -53
  121. package/resources/components/Dashboard/UI/Drawer.stx +114 -0
  122. package/resources/components/Dashboard/UI/Modal.stx +32 -21
  123. package/resources/components/Docs/Demo/ComboboxDemo.stx +4 -5
  124. package/resources/components/Docs/Demo/DropdownDemo.stx +12 -5
  125. package/resources/components/Docs/Demo/ListboxDemo.stx +14 -6
  126. package/resources/components/Docs/Demo/PopoverDemo.stx +10 -5
  127. package/resources/components/Docs/Demo/RadioGroupDemo.stx +14 -6
  128. package/resources/components/Docs/Demo/StepperDemo.stx +4 -5
  129. package/resources/components/Docs/TeamMember.stx +16 -9
  130. package/resources/components/Forum/ForumReplyForm.stx +7 -7
  131. package/resources/components/Forum/ForumSidebar.stx +10 -6
  132. package/resources/components/Marketing/Faqs.stx +5 -5
  133. package/resources/components/Marketing/Feature.stx +11 -6
  134. package/resources/components/Marketing/Layout.stx +12 -8
  135. package/resources/components/Marketing/Pricing.stx +11 -5
  136. package/resources/components/Marketing/PrimaryFeatures.stx +4 -5
  137. package/resources/components/Marketing/PrimaryFeatures2.stx +6 -5
  138. package/resources/components/Marketing/SecondaryFeatures.stx +10 -5
  139. package/resources/components/Marketing/Testimonials.stx +8 -5
  140. package/resources/views/index.stx +4 -4
  141. package/views/system-tray/index.stx +1 -1
@@ -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>
@@ -9,20 +9,16 @@ const record = useReactiveProp('record', null as ShippingZoneRecord | 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 zone'} permanently. Zones referenced by shipping rates 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-zone-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-zone-delete-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete shipping zone?</h2>
20
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete {{ record()?.name }} permanently. Zones referenced by shipping rates 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 zone</Button>
26
- </footer>
27
- </section>
28
- </div>
15
+ <ConfirmDialog
16
+ :isOpen="open() && Boolean(record())"
17
+ title="Delete shipping zone?"
18
+ :description="description()"
19
+ confirmLabel="Delete zone"
20
+ :busy="busy()"
21
+ :error="error()"
22
+ @close="emit('close')"
23
+ @confirm="emit('confirm')"
24
+ />
@@ -107,16 +107,17 @@ function submit(): void {
107
107
  }
108
108
  </script>
109
109
 
110
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
111
- <button type="button" aria-label="Close shipping zone dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
112
- <section role="dialog" aria-modal="true" aria-labelledby="shipping-zone-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">
113
- <form @submit.prevent="submit()">
114
- <div class="p-6">
115
- <h2 id="shipping-zone-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit shipping zone' : 'Add shipping zone' }}</h2>
116
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Separate list values with commas or new lines. Values are stored as normalized JSON arrays.</p>
117
- <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>
110
+ <Modal
111
+ :isOpen="open()"
112
+ :title="mode() === 'edit' ? 'Edit shipping zone' : 'Add shipping zone'"
113
+ description="Separate list values with commas or new lines. Values are stored as normalized JSON arrays."
114
+ size="lg"
115
+ @close="emit('close')"
116
+ >
117
+ <form id="shipping-zone-form" @submit.prevent="submit()">
118
+ <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>
118
119
 
119
- <div class="mt-5 space-y-4">
120
+ <div class="space-y-4">
120
121
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Zone name</span><input x-model="name" type="text" required maxlength="100" autocomplete="off" placeholder="North America" 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>
121
122
  <label class="block">
122
123
  <span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Shipping method</span>
@@ -130,11 +131,11 @@ function submit(): void {
130
131
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Postal codes</span><textarea x-model="postalCodes" rows="2" maxlength="1000" placeholder="90001, 90002" 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>
131
132
  <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>
132
133
  </div>
133
- </div>
134
- <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">
135
- <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
136
- <Button type="submit" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add zone' }}</Button>
137
- </footer>
138
134
  </form>
139
- </section>
140
- </div>
135
+ <template #footer>
136
+ <div class="flex gap-2 justify-end w-full">
137
+ <Button :disabled="busy()" variant="secondary" @click="emit('close')">Cancel</Button>
138
+ <Button type="submit" form="shipping-zone-form" :loading="busy()">{{ mode() === 'edit' ? 'Save changes' : 'Add zone' }}</Button>
139
+ </div>
140
+ </template>
141
+ </Modal>
@@ -40,16 +40,17 @@ function refundable(): boolean {
40
40
  }
41
41
  </script>
42
42
 
43
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
44
- <button type="button" aria-label="Close payment details" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
45
- <section role="dialog" aria-modal="true" aria-labelledby="payment-details-title" class="relative max-w-xl w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
46
- <div class="p-6">
47
- <div class="flex items-start justify-between">
48
- <div><p class="text-gray-400 text-xs dark:text-neutral-500">Payment #{{ record()?.id }}</p><h2 id="payment-details-title" class="mt-1 font-semibold text-gray-900 text-lg dark:text-white">{{ money(record()?.amount || 0, record()?.currency || 'USD') }}</h2></div>
49
- <span class="px-2 py-1 capitalize font-medium text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ label(record()?.status || '') }}</span>
50
- </div>
51
-
52
- <dl class="grid gap-x-6 gap-y-4 sm:grid-cols-2 mt-6">
43
+ <Modal
44
+ :isOpen="open() && Boolean(record())"
45
+ :title="money(record()?.amount || 0, record()?.currency || 'USD')"
46
+ :description="`Payment #${record()?.id || 'not recorded'}`"
47
+ size="lg"
48
+ @close="emit('close')"
49
+ >
50
+ <template #header-trailing>
51
+ <span class="px-2 py-1 capitalize font-medium text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ label(record()?.status || '') }}</span>
52
+ </template>
53
+ <dl class="grid gap-x-6 gap-y-4 sm:grid-cols-2">
53
54
  <template :for="item in detailRows()" :key="item.label">
54
55
  <div><dt class="text-gray-400 text-xs dark:text-neutral-500">{{ item.label }}</dt><dd class="mt-1 break-all text-gray-800 text-sm dark:text-neutral-100">{{ item.value }}</dd></div>
55
56
  </template>
@@ -59,10 +60,10 @@ function refundable(): boolean {
59
60
 
60
61
  <div :if="record()?.notes" class="mt-5 p-3 text-gray-600 text-sm dark:text-neutral-300 bg-gray-50 dark:bg-neutral-900/60 rounded-lg">{{ record()?.notes }}</div>
61
62
  <div :if="refundable()" class="mt-5 p-3 text-amber-800 text-xs dark:text-amber-200 bg-amber-50 dark:bg-amber-950/30 border border-amber-200 rounded-lg dark:border-amber-900">Only record a refund after the payment processor confirms it. This updates the local ledger and does not contact the processor.</div>
62
- </div>
63
- <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">
63
+ <template #footer>
64
+ <div class="flex gap-2 justify-end w-full">
64
65
  <Button variant="secondary" @click="emit('close')">Close</Button>
65
66
  <Button :if="refundable()" @click="emit('request-refund')">Record refund</Button>
66
- </footer>
67
- </section>
68
- </div>
67
+ </div>
68
+ </template>
69
+ </Modal>
@@ -51,20 +51,21 @@ function submit(): void {
51
51
  }
52
52
  </script>
53
53
 
54
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
55
- <button type="button" aria-label="Close refund dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
56
- <section role="dialog" aria-modal="true" aria-labelledby="payment-refund-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">
57
- <form @submit.prevent="submit">
58
- <div class="p-6">
59
- <h2 id="payment-refund-title" class="font-semibold text-gray-900 text-lg dark:text-white">Record confirmed refund</h2>
60
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Payment #{{ record()?.id }} has {{ money(remaining(), record()?.currency || 'USD') }} remaining.</p>
61
- <label class="block mt-5"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Refund amount</span><input x-model="amount" type="number" min="0.01" :max="remaining() / 100" step="0.01" 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>
54
+ <Modal
55
+ :isOpen="open() && Boolean(record())"
56
+ title="Record confirmed refund"
57
+ :description="`Payment #${record()?.id || 'not recorded'} has ${money(remaining(), record()?.currency || 'USD')} remaining.`"
58
+ size="sm"
59
+ @close="emit('close')"
60
+ >
61
+ <form id="payment-refund-form" @submit.prevent="submit">
62
+ <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Refund amount</span><input x-model="amount" type="number" min="0.01" :max="remaining() / 100" step="0.01" 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>
62
63
  <p class="mt-2 text-amber-700 text-xs dark:text-amber-300">This records an already confirmed processor refund. It does not initiate money movement.</p>
63
- </div>
64
- <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">
65
- <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
66
- <Button type="submit" :loading="busy()" :disabled="!valid()">Record refund</Button>
67
- </footer>
68
64
  </form>
69
- </section>
70
- </div>
65
+ <template #footer>
66
+ <div class="flex gap-2 justify-end w-full">
67
+ <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
68
+ <Button type="submit" form="payment-refund-form" :loading="busy()" :disabled="!valid()">Record refund</Button>
69
+ </div>
70
+ </template>
71
+ </Modal>
@@ -5,16 +5,15 @@ const open = useReactiveProp('open', false)
5
5
  const record = useReactiveProp('record', null as PrintDeviceRecord | null)
6
6
  const busy = useReactiveProp('busy', false)
7
7
  const emit = defineEmits()
8
+ const description = derived(() => `Delete "${record()?.name || 'Unnamed printer'}" permanently? Existing receipt logs remain separate records.`)
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 print device dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
12
- <section role="dialog" aria-modal="true" aria-labelledby="print-device-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="print-device-delete-title" class="font-semibold text-gray-900 text-lg dark:text-white">Delete print device</h2>
14
- <p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">Delete "{{ record()?.name || 'Unnamed printer' }}" permanently? Existing receipt logs remain separate records.</p>
15
- <footer class="flex gap-2 justify-end mt-6">
16
- <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
17
- <Button variant="danger" :loading="busy()" @click="emit('confirm')">Delete printer</Button>
18
- </footer>
19
- </section>
20
- </div>
11
+ <ConfirmDialog
12
+ :isOpen="open()"
13
+ title="Delete print device"
14
+ :description="description()"
15
+ confirmLabel="Delete printer"
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
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
42
- <button type="button" aria-label="Close print device details" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
43
- <section role="dialog" aria-modal="true" aria-labelledby="print-device-details-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">
44
- <div class="p-6">
45
- <div class="flex gap-4 items-start justify-between">
46
- <div><h2 id="print-device-details-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ record()?.name || 'Print device' }}</h2><p class="mt-1 font-mono text-gray-500 text-xs dark:text-neutral-400">{{ record()?.macAddress || 'No MAC address recorded' }}</p></div>
47
- <span class="px-2 py-1 capitalize font-semibold text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ label(record()?.status || '') }}</span>
48
- </div>
49
- <dl class="grid gap-4 grid-cols-2 mt-6">
41
+ <Modal
42
+ :isOpen="open()"
43
+ :title="record()?.name || 'Print device'"
44
+ :description="record()?.macAddress || 'No MAC address recorded'"
45
+ size="md"
46
+ @close="emit('close')"
47
+ >
48
+ <template #header-trailing>
49
+ <span class="px-2 py-1 capitalize font-semibold text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ label(record()?.status || '') }}</span>
50
+ </template>
51
+ <dl class="grid gap-4 grid-cols-2">
50
52
  <div><dt class="text-gray-400 text-xs dark:text-neutral-500">Location</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ record()?.location || 'Not recorded' }}</dd></div>
51
53
  <div><dt class="text-gray-400 text-xs dark:text-neutral-500">Terminal</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ record()?.terminal || 'Not recorded' }}</dd></div>
52
54
  <div><dt class="text-gray-400 text-xs dark:text-neutral-500">Recorded prints</dt><dd class="mt-1 font-mono text-gray-900 text-sm dark:text-white">{{ (record()?.printCount || 0).toLocaleString() }}</dd></div>
@@ -54,10 +56,10 @@ function deleteRecord(): void {
54
56
  <div class="col-span-2"><dt class="text-gray-400 text-xs dark:text-neutral-500">Last heartbeat</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ formatDate(record()?.lastPing || 0) }}</dd></div>
55
57
  <div class="col-span-2"><dt class="text-gray-400 text-xs dark:text-neutral-500">Created</dt><dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ formatDate(record()?.createdAt || '') }}</dd></div>
56
58
  </dl>
57
- </div>
58
- <footer class="flex gap-2 justify-between px-6 py-4 bg-gray-50 dark:bg-neutral-900/50 border-gray-200 border-t dark:border-neutral-700">
59
+ <template #footer>
60
+ <div class="flex gap-2 justify-between w-full">
59
61
  <Button variant="danger" @click="deleteRecord()">Delete</Button>
60
62
  <div class="flex gap-2"><Button variant="secondary" @click="emit('close')">Close</Button><Button @click="editRecord()">Edit printer</Button></div>
61
- </footer>
62
- </section>
63
- </div>
63
+ </div>
64
+ </template>
65
+ </Modal>
@@ -58,28 +58,29 @@ function errorMessages(): string[] {
58
58
  }
59
59
  </script>
60
60
 
61
- <div :if="open()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
62
- <button type="button" aria-label="Close print device dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
63
- <section role="dialog" aria-modal="true" aria-labelledby="print-device-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">
64
- <form @submit.prevent="submit">
65
- <div class="p-6">
66
- <h2 id="print-device-dialog-title" class="font-semibold text-gray-900 text-lg dark:text-white">{{ mode() === 'edit' ? 'Edit print device' : 'Add print device' }}</h2>
67
- <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Store fields declared by the PrintDevice model.</p>
68
- <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">
61
+ <Modal
62
+ :isOpen="open()"
63
+ :title="mode() === 'edit' ? 'Edit print device' : 'Add print device'"
64
+ description="Store fields declared by the PrintDevice model."
65
+ size="md"
66
+ @close="emit('close')"
67
+ >
68
+ <form id="print-device-form" @submit.prevent="submit">
69
+ <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">
69
70
  <template :for="message in errorMessages()" :key="message"><li>{{ message }}</li></template>
70
71
  </ul>
71
- <div class="grid gap-4 grid-cols-1 sm:grid-cols-2 mt-5">
72
+ <div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
72
73
  <label class="block sm:col-span-2"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Name</span><input x-model="name" 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>
73
74
  <label class="block sm:col-span-2"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">MAC address</span><input x-model="macAddress" type="text" maxlength="50" 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" /></label>
74
75
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Location</span><input x-model="location" 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>
75
76
  <label class="block"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Terminal</span><input x-model="terminal" type="text" maxlength="50" 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" /></label>
76
77
  <label class="block sm:col-span-2"><span class="font-medium text-gray-700 text-sm dark:text-neutral-200">Operational 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="online">Online</option><option value="warning">Warning</option><option value="offline">Offline</option></select></label>
77
78
  </div>
78
- </div>
79
- <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">
80
- <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
81
- <Button type="submit" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save printer' : 'Add printer' }}</Button>
82
- </footer>
83
79
  </form>
84
- </section>
85
- </div>
80
+ <template #footer>
81
+ <div class="flex gap-2 justify-end w-full">
82
+ <Button variant="secondary" :disabled="busy()" @click="emit('close')">Cancel</Button>
83
+ <Button type="submit" form="print-device-form" :loading="busy()" :disabled="!valid()">{{ mode() === 'edit' ? 'Save printer' : 'Add printer' }}</Button>
84
+ </div>
85
+ </template>
86
+ </Modal>
@@ -5,12 +5,15 @@ const open = useReactiveProp('open', false)
5
5
  const record = useReactiveProp('record', null as PrintLogRecord | null)
6
6
  const busy = useReactiveProp('busy', false)
7
7
  const emit = defineEmits()
8
+ const description = derived(() => `This permanently deletes ${record()?.document || `print log #${record()?.id}`} from the native Receipt history. It does not affect the printed document.`)
8
9
  </script>
9
10
 
10
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
11
- <button type="button" aria-label="Close delete print log dialog" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
12
- <section role="alertdialog" aria-modal="true" aria-labelledby="delete-print-log-title" aria-describedby="delete-print-log-description" 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">
13
- <div class="p-6"><div class="flex items-center justify-center h-10 w-10 text-red-600 dark:text-red-300 bg-red-50 dark:bg-red-950/40 rounded-full"><span class="h-5 w-5 i-hugeicons-waste"></span></div><h2 id="delete-print-log-title" class="mt-4 font-semibold text-gray-900 text-lg dark:text-white">Delete print log?</h2><p id="delete-print-log-description" class="mt-2 text-gray-500 text-sm dark:text-neutral-400">This permanently deletes {{ record()?.document || 'print log #' + record()?.id }} from the native Receipt history. It does not affect the printed document.</p></div>
14
- <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 variant="danger" :loading="busy()" @click="emit('confirm')">Delete print log</Button></footer>
15
- </section>
16
- </div>
11
+ <ConfirmDialog
12
+ :isOpen="open() && Boolean(record())"
13
+ title="Delete print log?"
14
+ :description="description()"
15
+ confirmLabel="Delete print log"
16
+ :busy="busy()"
17
+ @close="emit('close')"
18
+ @confirm="emit('confirm')"
19
+ />
@@ -29,14 +29,17 @@ function details(): Array<{ label: string, value: string }> {
29
29
  }
30
30
  </script>
31
31
 
32
- <div :if="open() && record()" class="flex fixed inset-0 z-50 items-center justify-center p-4 dashboard-modal-layer">
33
- <button type="button" aria-label="Close print log details" class="absolute inset-0 bg-black/45" @click="emit('close')"></button>
34
- <section role="dialog" aria-modal="true" aria-labelledby="print-log-details-title" class="relative max-w-xl w-full bg-white dark:bg-neutral-800 border border-gray-200 rounded-xl dark:border-neutral-700 shadow-xl">
35
- <div class="p-6">
36
- <div class="flex items-start justify-between"><div><p class="text-gray-400 text-xs dark:text-neutral-500">Print log #{{ record()?.id }}</p><h2 id="print-log-details-title" class="mt-1 font-semibold text-gray-900 text-lg dark:text-white">{{ record()?.document || 'Untitled print job' }}</h2></div><span class="px-2 py-1 capitalize font-medium text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ record()?.status }}</span></div>
37
- <dl class="grid gap-x-6 gap-y-4 sm:grid-cols-2 mt-6"><template :for="item in details()" :key="item.label"><div><dt class="text-gray-400 text-xs dark:text-neutral-500">{{ item.label }}</dt><dd class="mt-1 break-all text-gray-800 text-sm dark:text-neutral-100">{{ item.value }}</dd></div></template></dl>
32
+ <Modal
33
+ :isOpen="open() && Boolean(record())"
34
+ :title="record()?.document || 'Untitled print job'"
35
+ :description="`Print log #${record()?.id || 'not recorded'}`"
36
+ size="lg"
37
+ @close="emit('close')"
38
+ >
39
+ <template #header-trailing>
40
+ <span class="px-2 py-1 capitalize font-medium text-gray-700 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ record()?.status }}</span>
41
+ </template>
42
+ <dl class="grid gap-x-6 gap-y-4 sm:grid-cols-2"><template :for="item in details()" :key="item.label"><div><dt class="text-gray-400 text-xs dark:text-neutral-500">{{ item.label }}</dt><dd class="mt-1 break-all text-gray-800 text-sm dark:text-neutral-100">{{ item.value }}</dd></div></template></dl>
38
43
  <div :if="record()?.metadata && record()?.metadata !== '{}'" class="mt-5"><p class="text-gray-400 text-xs dark:text-neutral-500">Metadata</p><pre class="overflow-x-auto mt-1 p-3 text-gray-700 text-xs whitespace-pre-wrap dark:text-neutral-200 bg-gray-50 dark:bg-neutral-900/60 rounded-lg">{{ record()?.metadata }}</pre></div>
39
- </div>
40
- <footer class="flex 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" @click="emit('close')">Close</Button></footer>
41
- </section>
42
- </div>
44
+ <template #footer><Button variant="secondary" @click="emit('close')">Close</Button></template>
45
+ </Modal>