@stacksjs/components 0.2.93 → 0.2.94

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 (187) hide show
  1. package/dist/index.js +4 -4
  2. package/package.json +5 -6
  3. package/src/components/CodeBlock.stx +0 -54
  4. package/src/components/Footer.stx +0 -66
  5. package/src/components/Hero.stx +0 -25
  6. package/src/components/Installation.stx +0 -37
  7. package/src/components/index.ts +0 -4
  8. package/src/composables/index.ts +0 -4
  9. package/src/composables/useCopyCode.ts +0 -73
  10. package/src/composables/useDarkMode.ts +0 -100
  11. package/src/composables/useSEO.ts +0 -230
  12. package/src/composables/utilities/index.ts +0 -7
  13. package/src/composables/utilities/useClickOutside.ts +0 -72
  14. package/src/composables/utilities/useFocusTrap.ts +0 -159
  15. package/src/composables/utilities/useKeyboard.ts +0 -124
  16. package/src/composables/utilities/useLocalStorage.ts +0 -132
  17. package/src/composables/utilities/useMediaQuery.ts +0 -118
  18. package/src/index.ts +0 -99
  19. package/src/stx.d.ts +0 -4
  20. package/src/ui/accordion/Accordion.stx +0 -146
  21. package/src/ui/accordion/AccordionItem.stx +0 -37
  22. package/src/ui/accordion/index.ts +0 -47
  23. package/src/ui/audio/Audio.stx +0 -50
  24. package/src/ui/audio/README.md +0 -41
  25. package/src/ui/audio/index.ts +0 -13
  26. package/src/ui/auth/Login.stx +0 -161
  27. package/src/ui/auth/README.md +0 -142
  28. package/src/ui/auth/Signup.stx +0 -159
  29. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  30. package/src/ui/auth/index.ts +0 -32
  31. package/src/ui/avatar/Avatar.stx +0 -77
  32. package/src/ui/avatar/index.ts +0 -12
  33. package/src/ui/badge/Badge.stx +0 -62
  34. package/src/ui/badge/index.ts +0 -10
  35. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  36. package/src/ui/breadcrumb/index.ts +0 -15
  37. package/src/ui/button/Button.stx +0 -74
  38. package/src/ui/button/README.md +0 -80
  39. package/src/ui/button/index.ts +0 -85
  40. package/src/ui/calendar/Calendar.stx +0 -189
  41. package/src/ui/calendar/README.md +0 -254
  42. package/src/ui/calendar/index.ts +0 -14
  43. package/src/ui/card/Card.stx +0 -68
  44. package/src/ui/card/index.ts +0 -13
  45. package/src/ui/checkbox/Checkbox.stx +0 -86
  46. package/src/ui/checkbox/index.ts +0 -16
  47. package/src/ui/combobox/Combobox.stx +0 -96
  48. package/src/ui/combobox/ComboboxButton.stx +0 -25
  49. package/src/ui/combobox/ComboboxInput.stx +0 -32
  50. package/src/ui/combobox/ComboboxOption.stx +0 -49
  51. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  52. package/src/ui/combobox/README.md +0 -150
  53. package/src/ui/combobox/index.ts +0 -40
  54. package/src/ui/command-palette/CommandPalette.stx +0 -81
  55. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  56. package/src/ui/command-palette/README.md +0 -42
  57. package/src/ui/command-palette/index.ts +0 -15
  58. package/src/ui/dialog/Dialog.stx +0 -41
  59. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  60. package/src/ui/dialog/DialogDescription.stx +0 -11
  61. package/src/ui/dialog/DialogPanel.stx +0 -16
  62. package/src/ui/dialog/DialogTitle.stx +0 -11
  63. package/src/ui/dialog/README.md +0 -121
  64. package/src/ui/dialog/index.ts +0 -32
  65. package/src/ui/drawer/Drawer.stx +0 -120
  66. package/src/ui/drawer/README.md +0 -90
  67. package/src/ui/drawer/index.ts +0 -9
  68. package/src/ui/dropdown/Dropdown.stx +0 -82
  69. package/src/ui/dropdown/DropdownButton.stx +0 -29
  70. package/src/ui/dropdown/DropdownItem.stx +0 -35
  71. package/src/ui/dropdown/DropdownItems.stx +0 -18
  72. package/src/ui/dropdown/README.md +0 -114
  73. package/src/ui/dropdown/index.ts +0 -29
  74. package/src/ui/form/Form.stx +0 -95
  75. package/src/ui/form/index.ts +0 -67
  76. package/src/ui/heatmap/Heatmap.stx +0 -294
  77. package/src/ui/image/Image.stx +0 -165
  78. package/src/ui/image/README.md +0 -164
  79. package/src/ui/image/index.ts +0 -22
  80. package/src/ui/input/EmailInput.stx +0 -30
  81. package/src/ui/input/NumberInput.stx +0 -126
  82. package/src/ui/input/PasswordInput.stx +0 -143
  83. package/src/ui/input/SearchInput.stx +0 -44
  84. package/src/ui/input/TextInput.stx +0 -132
  85. package/src/ui/input/index.ts +0 -180
  86. package/src/ui/listbox/Listbox.stx +0 -101
  87. package/src/ui/listbox/ListboxButton.stx +0 -28
  88. package/src/ui/listbox/ListboxLabel.stx +0 -11
  89. package/src/ui/listbox/ListboxOption.stx +0 -49
  90. package/src/ui/listbox/ListboxOptions.stx +0 -16
  91. package/src/ui/listbox/README.md +0 -135
  92. package/src/ui/listbox/index.ts +0 -39
  93. package/src/ui/navigator/Navigator.stx +0 -113
  94. package/src/ui/navigator/README.md +0 -352
  95. package/src/ui/navigator/index.ts +0 -20
  96. package/src/ui/notification/Notification.stx +0 -117
  97. package/src/ui/notification/README.md +0 -114
  98. package/src/ui/notification/index.ts +0 -12
  99. package/src/ui/pagination/Pagination.stx +0 -150
  100. package/src/ui/pagination/index.ts +0 -11
  101. package/src/ui/payment/Checkout.stx +0 -31
  102. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  103. package/src/ui/payment/PaymentMethods.stx +0 -104
  104. package/src/ui/payment/README.md +0 -345
  105. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  106. package/src/ui/payment/index.ts +0 -59
  107. package/src/ui/popover/Popover.stx +0 -60
  108. package/src/ui/popover/PopoverButton.stx +0 -29
  109. package/src/ui/popover/PopoverPanel.stx +0 -23
  110. package/src/ui/popover/README.md +0 -91
  111. package/src/ui/popover/index.ts +0 -22
  112. package/src/ui/portal/Portal.stx +0 -57
  113. package/src/ui/portal/index.ts +0 -6
  114. package/src/ui/progress/Progress.stx +0 -131
  115. package/src/ui/progress/index.ts +0 -12
  116. package/src/ui/radio/Radio.stx +0 -77
  117. package/src/ui/radio/index.ts +0 -15
  118. package/src/ui/radio-group/README.md +0 -139
  119. package/src/ui/radio-group/RadioGroup.stx +0 -60
  120. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  121. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  123. package/src/ui/radio-group/index.ts +0 -30
  124. package/src/ui/select/README.md +0 -180
  125. package/src/ui/select/Select.stx +0 -82
  126. package/src/ui/select/index.ts +0 -21
  127. package/src/ui/sidebar/Sidebar.stx +0 -476
  128. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  129. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  130. package/src/ui/sidebar/SidebarItem.stx +0 -86
  131. package/src/ui/sidebar/SidebarSection.stx +0 -83
  132. package/src/ui/sidebar/index.ts +0 -153
  133. package/src/ui/sidebar/themes.ts +0 -240
  134. package/src/ui/skeleton/Skeleton.stx +0 -48
  135. package/src/ui/skeleton/index.ts +0 -11
  136. package/src/ui/spinner/Spinner.stx +0 -109
  137. package/src/ui/spinner/index.ts +0 -9
  138. package/src/ui/stepper/README.md +0 -36
  139. package/src/ui/stepper/Stepper.stx +0 -17
  140. package/src/ui/stepper/StepperStep.stx +0 -57
  141. package/src/ui/stepper/index.ts +0 -19
  142. package/src/ui/storage/README.md +0 -193
  143. package/src/ui/storage/index.ts +0 -347
  144. package/src/ui/switch/README.md +0 -114
  145. package/src/ui/switch/Switch.stx +0 -72
  146. package/src/ui/switch/index.ts +0 -10
  147. package/src/ui/table/README.md +0 -39
  148. package/src/ui/table/Table.stx +0 -14
  149. package/src/ui/table/TableBody.stx +0 -10
  150. package/src/ui/table/TableCell.stx +0 -10
  151. package/src/ui/table/TableHead.stx +0 -10
  152. package/src/ui/table/TableHeader.stx +0 -20
  153. package/src/ui/table/TableRow.stx +0 -11
  154. package/src/ui/table/index.ts +0 -35
  155. package/src/ui/tabs/TabPanel.stx +0 -25
  156. package/src/ui/tabs/Tabs.stx +0 -202
  157. package/src/ui/tabs/index.ts +0 -50
  158. package/src/ui/teleport/Teleport.stx +0 -65
  159. package/src/ui/teleport/index.ts +0 -7
  160. package/src/ui/textarea/Textarea.stx +0 -108
  161. package/src/ui/textarea/index.ts +0 -20
  162. package/src/ui/tooltip/Tooltip.stx +0 -71
  163. package/src/ui/tooltip/index.ts +0 -10
  164. package/src/ui/transition/README.md +0 -30
  165. package/src/ui/transition/Transition.stx +0 -102
  166. package/src/ui/transition/index.ts +0 -25
  167. package/src/ui/video/README.md +0 -47
  168. package/src/ui/video/Video.stx +0 -45
  169. package/src/ui/video/index.ts +0 -16
  170. package/src/ui/virtual-list/VirtualList.stx +0 -67
  171. package/src/ui/virtual-list/index.ts +0 -10
  172. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  173. package/src/ui/virtual-table/index.ts +0 -21
  174. package/src/utils/PROP_VALIDATION.md +0 -458
  175. package/src/utils/THEME_SYSTEM.md +0 -702
  176. package/src/utils/accessibility.ts +0 -841
  177. package/src/utils/animation/easing.ts +0 -368
  178. package/src/utils/animation/helpers.ts +0 -223
  179. package/src/utils/animation/index.ts +0 -5
  180. package/src/utils/animation/keyframes.ts +0 -230
  181. package/src/utils/error-handling.ts +0 -475
  182. package/src/utils/highlighter.ts +0 -93
  183. package/src/utils/i18n.ts +0 -634
  184. package/src/utils/keep-alive.ts +0 -819
  185. package/src/utils/lazy-loading.ts +0 -530
  186. package/src/utils/prop-validation.ts +0 -478
  187. package/src/utils/theme.ts +0 -626
@@ -1,345 +0,0 @@
1
- # Payment Components
2
-
3
- Comprehensive payment components for Stripe integration, including payment method management and checkout flows.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- bun add @stacksjs/components
9
- ```
10
-
11
- ## Components
12
-
13
- ### Checkout
14
- Main checkout wrapper component that supports both subscription and one-time payment modes.
15
-
16
- ### SubscriptionCheckout
17
- Full subscription checkout flow with Stripe Elements integration.
18
-
19
- ### PaymentMethods
20
- Display and manage a list of saved payment methods with delete and set-default actions.
21
-
22
- ### DefaultPaymentMethod
23
- Display the default payment method with a badge.
24
-
25
- ## Usage
26
-
27
- ### Checkout Component
28
-
29
- ```stx
30
- <script server>
31
- const products = [
32
- {
33
- id: 1,
34
- name: 'Premium Subscription',
35
- price: 99900, // $999.00 in cents
36
- images: '/images/subscription.jpg',
37
- quantity: 1
38
- }
39
- ]
40
-
41
- const mode = 'subscription'
42
- const stripePublicKey = 'pk_test_...'
43
- const apiUrl = 'https://api.example.com'
44
- const redirectUrl = '/thank-you'
45
- const shipping = 4500
46
- const taxes = 552
47
-
48
- const onSubmit = (result) => {
49
- if (result.success) {
50
- console.log('Payment successful!')
51
- window.location.href = redirectUrl
52
- }
53
- }
54
-
55
- const onRemoveProduct = (productId) => {
56
- console.log('Remove product:', productId)
57
- // Update cart state
58
- }
59
- </script>
60
-
61
- <Checkout
62
- :products="products"
63
- :mode="mode"
64
- :stripePublicKey="stripePublicKey"
65
- :apiUrl="apiUrl"
66
- :redirectUrl="redirectUrl"
67
- :shipping="shipping"
68
- :taxes="taxes"
69
- :onSubmit="onSubmit"
70
- :onRemoveProduct="onRemoveProduct"
71
- />
72
- ```
73
-
74
- ### PaymentMethods Component
75
-
76
- ```stx
77
- <script server>
78
- const methods = [
79
- {
80
- id: 1,
81
- brand: 'Visa',
82
- last_four: '4242',
83
- exp_month: 12,
84
- exp_year: 2025
85
- },
86
- {
87
- id: 2,
88
- brand: 'Mastercard',
89
- last_four: '5555',
90
- exp_month: 6,
91
- exp_year: 2026
92
- }
93
- ]
94
-
95
- const handleDelete = (id) => {
96
- console.log('Delete payment method:', id)
97
- // Call your API to delete
98
- }
99
-
100
- const handleMakeDefault = (id) => {
101
- console.log('Make default:', id)
102
- // Call your API to set as default
103
- }
104
- </script>
105
-
106
- <PaymentMethods
107
- :paymentMethods="methods"
108
- :onDeletePaymentMethod="handleDelete"
109
- :onMakeDefault="handleMakeDefault"
110
- />
111
- ```
112
-
113
- ### DefaultPaymentMethod Component
114
-
115
- ```stx
116
- <script server>
117
- const defaultMethod = {
118
- id: 1,
119
- brand: 'Visa',
120
- last_four: '4242',
121
- exp_month: 12,
122
- exp_year: 2025
123
- }
124
- </script>
125
-
126
- <DefaultPaymentMethod :paymentMethod="defaultMethod" />
127
- ```
128
-
129
- ### SubscriptionCheckout Component
130
-
131
- ```stx
132
- <script server>
133
- const cartProducts = [
134
- {
135
- id: 1,
136
- name: 'Premium Subscription',
137
- price: 99900, // $999.00 in cents
138
- images: '/images/subscription.jpg',
139
- quantity: 1
140
- }
141
- ]
142
-
143
- const handleCheckoutComplete = (result) => {
144
- if (result.success) {
145
- console.log('Payment successful!')
146
- // Redirect to success page
147
- }
148
- }
149
- </script>
150
-
151
- <SubscriptionCheckout
152
- :products="cartProducts"
153
- stripePublicKey="pk_test_..."
154
- apiUrl="https://api.example.com/payments"
155
- :shipping="4500"
156
- :taxes="552"
157
- :onSubmit="handleCheckoutComplete"
158
- />
159
- ```
160
-
161
- ## Props
162
-
163
- ### Checkout Props
164
-
165
- | Prop | Type | Default | Description |
166
- |------|------|---------|-------------|
167
- | `products` | `Product[]` | `[]` | Products in cart |
168
- | `mode` | `'subscription' \| 'one-time'` | `'subscription'` | Payment mode |
169
- | `stripePublicKey` | `string` | - | Stripe publishable key (required) |
170
- | `apiUrl` | `string` | - | Base API URL (required) |
171
- | `redirectUrl` | `string` | `''` | Redirect URL after payment |
172
- | `onSubmit` | `function` | - | Callback when payment completes |
173
- | `onRemoveProduct` | `function` | - | Callback when product removed from cart |
174
- | `shipping` | `number` | `0` | Shipping cost in cents |
175
- | `taxes` | `number` | `0` | Tax amount in cents |
176
- | `className` | `string` | `''` | Additional CSS classes |
177
-
178
- ### PaymentMethods Props
179
-
180
- | Prop | Type | Default | Description |
181
- |------|------|---------|-------------|
182
- | `paymentMethods` | `PaymentMethod[]` | `[]` | Array of payment methods |
183
- | `onDeletePaymentMethod` | `function` | - | Callback when delete clicked |
184
- | `onMakeDefault` | `function` | - | Callback when make default clicked |
185
- | `isLoading` | `boolean` | `false` | Show loading state |
186
- | `className` | `string` | `''` | Additional CSS classes |
187
-
188
- ### DefaultPaymentMethod Props
189
-
190
- | Prop | Type | Default | Description |
191
- |------|------|---------|-------------|
192
- | `paymentMethod` | `PaymentMethod \| null` | - | Default payment method or null |
193
- | `isLoading` | `boolean` | `false` | Show loading state |
194
- | `className` | `string` | `''` | Additional CSS classes |
195
-
196
- ### SubscriptionCheckout Props
197
-
198
- | Prop | Type | Default | Description |
199
- |------|------|---------|-------------|
200
- | `products` | `Product[]` | `[]` | Products in cart |
201
- | `stripePublicKey` | `string` | - | Stripe publishable key (required) |
202
- | `apiUrl` | `string` | - | Base API URL (required) |
203
- | `onSubmit` | `function` | - | Callback when payment completes |
204
- | `onRemoveProduct` | `function` | - | Callback when product removed from cart |
205
- | `shipping` | `number` | `0` | Shipping cost in cents |
206
- | `taxes` | `number` | `0` | Tax amount in cents |
207
- | `className` | `string` | `''` | Additional CSS classes |
208
-
209
- ## TypeScript Interfaces
210
-
211
- ```typescript
212
- interface PaymentMethod {
213
- id: string | number
214
- brand: string
215
- last_four: string
216
- exp_month: number
217
- exp_year: number
218
- is_default?: boolean
219
- }
220
-
221
- interface Product {
222
- id: string | number
223
- name: string
224
- price: number // in cents
225
- images?: string
226
- quantity?: number
227
- }
228
- ```
229
-
230
- ## Backend API Requirements
231
-
232
- ### Create Subscription Endpoint
233
-
234
- Your `apiUrl` should implement a `POST /stripe/create-subscription` endpoint:
235
-
236
- ```typescript
237
- // Example Express.js endpoint
238
- app.post('/stripe/create-subscription', async (req, res) => {
239
- const { amount, quantity } = req.body
240
-
241
- const paymentIntent = await stripe.paymentIntents.create({
242
- amount: amount,
243
- currency: 'usd',
244
- automatic_payment_methods: {
245
- enabled: true,
246
- },
247
- })
248
-
249
- res.json({
250
- client_secret: paymentIntent.client_secret,
251
- })
252
- })
253
- ```
254
-
255
- **Note**: The endpoint path must be `{apiUrl}/stripe/create-subscription` to match the component's expectations.
256
-
257
- ## Features
258
-
259
- - **Stripe Elements** - Integrated Stripe payment form
260
- - **Payment Management** - View, delete, set default payment methods
261
- - **Dark Mode** - Full dark mode support
262
- - **Loading States** - Skeleton loaders while fetching data
263
- - **Empty States** - Friendly messages when no payment methods
264
- - **Responsive** - Mobile-friendly layouts
265
- - **Accessible** - Proper ARIA labels and keyboard navigation
266
- - **Confirmation** - Delete confirmation dialogs
267
- - **Error Handling** - Graceful error messages
268
- - **Modern ES Modules** - Clean export syntax
269
- - **Headwind Styling** - Utility-first CSS classes
270
-
271
- ## Stripe.js Integration
272
-
273
- The `SubscriptionCheckout` component automatically loads Stripe.js from the CDN. Make sure you have a valid Stripe account and publishable key.
274
-
275
- ### Test Cards
276
-
277
- Use these test cards in development:
278
-
279
- - **Success**: `4242 4242 4242 4242`
280
- - **Decline**: `4000 0000 0000 0002`
281
- - **Requires 3DS**: `4000 0027 6000 3184`
282
-
283
- Use any future expiration date and any 3-digit CVC.
284
-
285
- ## Example: Full Payment Flow
286
-
287
- ```stx
288
- <script server>
289
- // Fetch payment methods
290
- const paymentMethods = await fetch('/api/payment-methods').then(r => r.json())
291
-
292
- // Fetch default payment method
293
- const defaultMethod = paymentMethods.find(m => m.is_default)
294
-
295
- // Cart products
296
- const cart = [
297
- { id: 1, name: 'Product 1', price: 5000, quantity: 2 },
298
- { id: 2, name: 'Product 2', price: 3000, quantity: 1 },
299
- ]
300
-
301
- const handleDelete = async (id) => {
302
- await fetch(`/api/payment-methods/${id}`, { method: 'DELETE' })
303
- // Refetch payment methods
304
- }
305
-
306
- const handleMakeDefault = async (id) => {
307
- await fetch(`/api/payment-methods/${id}/default`, { method: 'POST' })
308
- // Refetch payment methods
309
- }
310
-
311
- const handleCheckout = (result) => {
312
- if (result.success) {
313
- window.location.href = '/thank-you'
314
- }
315
- }
316
- </script>
317
-
318
- <!-- Default Payment Method -->
319
- <DefaultPaymentMethod :paymentMethod="defaultMethod" />
320
-
321
- <!-- All Payment Methods -->
322
- <PaymentMethods
323
- :paymentMethods="paymentMethods"
324
- :onDeletePaymentMethod="handleDelete"
325
- :onMakeDefault="handleMakeDefault"
326
- />
327
-
328
- <!-- Checkout Form -->
329
- <SubscriptionCheckout
330
- :products="cart"
331
- stripePublicKey="pk_test_..."
332
- apiUrl="https://api.example.com/payments"
333
- :shipping="1000"
334
- :taxes="800"
335
- :onSubmit="handleCheckout"
336
- />
337
- ```
338
-
339
- ## Security Notes
340
-
341
- - **Never** expose your Stripe secret key in client code
342
- - Always validate payment amounts on the server
343
- - Use HTTPS in production
344
- - Implement proper authentication for payment management endpoints
345
- - Verify webhook signatures from Stripe
@@ -1,323 +0,0 @@
1
- <script server>
2
- export const products = $props.products || []
3
- export const stripePublicKey = $props.stripePublicKey || ''
4
- export const apiUrl = $props.apiUrl || ''
5
- export const showRemoveProduct = $props.showRemoveProduct ?? true
6
- export const shipping = $props.shipping || 0
7
- export const taxes = $props.taxes || 0
8
- export const className = $props.className || ''
9
-
10
- export const subtotal = products.reduce((sum, product) => sum + (product.price * (product.quantity || 1)), 0)
11
- export const total = subtotal + shipping + taxes
12
-
13
- export const containerClasses = `bg-gray-50 dark:bg-blue-gray-900 ${className}`.trim()
14
- </script>
15
-
16
- <script client>
17
- const emit = defineEmits()
18
- const stripePublicKey = {{ stripePublicKey }}
19
- const apiUrl = {{ apiUrl }}
20
- const products = {{ products }}
21
- const total = {{ total }}
22
-
23
- let stripe = null
24
- let elements = null
25
- let paymentElement = null
26
-
27
- function onRemoveProduct(productId) {
28
- emit('removeProduct', productId)
29
- }
30
-
31
- async function setupStripeElements() {
32
- if (!stripe || !apiUrl) return
33
- try {
34
- const response = await fetch(`${apiUrl}/stripe/create-subscription`, {
35
- method: 'POST',
36
- headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
37
- body: JSON.stringify({
38
- amount: total,
39
- quantity: products.reduce((sum, p) => sum + (p.quantity || 1), 0),
40
- }),
41
- })
42
- const paymentIntent = await response.json()
43
- const { clientSecret } = paymentIntent
44
- if (!clientSecret) {
45
- console.error('No client secret returned from API')
46
- return
47
- }
48
- elements = stripe.elements({
49
- clientSecret,
50
- appearance: {
51
- theme: document.documentElement.classList.contains('dark') ? 'night' : 'stripe',
52
- },
53
- })
54
- paymentElement = elements.create('payment')
55
- paymentElement.mount('#payment-element')
56
- }
57
- catch (error) {
58
- console.error('Error setting up Stripe elements:', error)
59
- }
60
- }
61
-
62
- async function initializeStripe() {
63
- if (!stripePublicKey) {
64
- console.error('Stripe public key is required')
65
- return
66
- }
67
- if (window.Stripe) {
68
- stripe = window.Stripe(stripePublicKey)
69
- await setupStripeElements()
70
- return
71
- }
72
- const script = document.createElement('script')
73
- script.src = 'https://js.stripe.com/v3/'
74
- script.async = true
75
- script.onload = async () => {
76
- stripe = window.Stripe(stripePublicKey)
77
- await setupStripeElements()
78
- }
79
- document.head.appendChild(script)
80
- }
81
-
82
- async function onSubmit(event) {
83
- event.preventDefault()
84
- if (!stripe || !elements) {
85
- emit('error', { message: 'Stripe not initialized' })
86
- return
87
- }
88
- try {
89
- const { error } = await stripe.confirmPayment({
90
- elements,
91
- redirect: 'if_required',
92
- confirmParams: { return_url: window.location.href },
93
- })
94
- if (error) {
95
- emit('error', error)
96
- }
97
- else {
98
- emit('submit', { success: true })
99
- }
100
- }
101
- catch (error) {
102
- emit('error', error)
103
- }
104
- }
105
-
106
- onMount(() => initializeStripe())
107
- </script>
108
-
109
- <div class="{{ containerClasses }}">
110
- <div class="mx-auto max-w-2xl px-4 pb-24 pt-16 lg:max-w-7xl lg:px-8 sm:px-6">
111
- <h2 class="sr-only">Checkout</h2>
112
-
113
- <form class="lg:grid lg:grid-cols-2 lg:gap-x-12 xl:gap-x-16" @submit="onSubmit($event)">
114
- <!-- Left Column - Contact & Payment -->
115
- <div>
116
- <!-- Contact Information -->
117
- <div>
118
- <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
119
- Contact information
120
- </h2>
121
-
122
- <div class="mt-4">
123
- <label for="email-address" class="block text-sm text-gray-700 dark:text-gray-300 font-medium">
124
- Email address
125
- </label>
126
- <div class="mt-1">
127
- <input
128
- id="email-address"
129
- type="email"
130
- name="email-address"
131
- autocomplete="email"
132
- required
133
- class="block w-full border border-gray-300 dark:border-gray-600 rounded-md p-2 shadow-sm bg-white dark:bg-blue-gray-700 text-gray-900 dark:text-gray-100 focus:border-indigo-500 dark:focus:border-indigo-400 sm:text-sm focus:ring-indigo-500 dark:focus:ring-indigo-400"
134
- />
135
- </div>
136
- </div>
137
- </div>
138
-
139
- <!-- Billing Information -->
140
- <div class="mt-10 border-t border-gray-200 dark:border-gray-700 pt-10">
141
- <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
142
- Billing information
143
- </h2>
144
-
145
- <div class="mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
146
- <div class="sm:col-span-2">
147
- <label for="card-name" class="block text-sm text-gray-700 dark:text-gray-300 font-medium">
148
- Name on card
149
- </label>
150
- <div class="mt-1">
151
- <input
152
- type="text"
153
- id="card-name"
154
- name="card-name"
155
- autocomplete="cc-name"
156
- required
157
- class="block w-full border border-gray-300 dark:border-gray-600 rounded-md p-2 shadow-sm bg-white dark:bg-blue-gray-700 text-gray-900 dark:text-gray-100 focus:border-indigo-500 sm:text-sm focus:ring-indigo-500"
158
- />
159
- </div>
160
- </div>
161
-
162
- <!-- Stripe Element Mount Point -->
163
- <div class="sm:col-span-2">
164
- <div id="payment-element" class="mt-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 p-3"></div>
165
- </div>
166
- </div>
167
- </div>
168
-
169
- <!-- Payment Type -->
170
- <div class="mt-10 border-t border-gray-200 dark:border-gray-700 pt-10">
171
- <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
172
- Payment
173
- </h2>
174
-
175
- <fieldset class="mt-4">
176
- <legend class="sr-only">Payment type</legend>
177
- <div class="sm:flex sm:items-center space-y-4 sm:space-x-10 sm:space-y-0">
178
- <div class="flex items-center">
179
- <input
180
- id="credit-card"
181
- name="payment-type"
182
- type="radio"
183
- checked
184
- class="h-4 w-4 border-gray-300 dark:border-gray-600 text-indigo-600 dark:text-indigo-400 focus:ring-indigo-500 dark:focus:ring-indigo-400"
185
- />
186
- <label for="credit-card" class="ml-3 block text-sm text-gray-700 dark:text-gray-300 font-medium">
187
- Credit card
188
- </label>
189
- </div>
190
- </div>
191
- </fieldset>
192
- </div>
193
- </div>
194
-
195
- <!-- Right Column - Order Summary -->
196
- <div class="mt-10 lg:mt-0">
197
- <h2 class="text-lg text-gray-900 dark:text-gray-100 font-medium">
198
- Order summary
199
- </h2>
200
-
201
- <div class="mt-4 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-blue-gray-800 shadow-sm">
202
- <h3 class="sr-only">Items in your cart</h3>
203
-
204
- <!-- Product List -->
205
- <ul role="list" class="divide-y divide-gray-200 dark:divide-gray-700">
206
- @foreach(product in products)
207
- <li class="flex px-4 py-6 sm:px-6">
208
- <div class="flex-shrink-0">
209
- @if(product.images)
210
- <img src="{{ product.images }}" alt="{{ product.name }}" class="w-20 rounded-md" />
211
- @else
212
- <div class="w-20 h-20 bg-gray-200 dark:bg-gray-700 rounded-md flex items-center justify-center">
213
- <svg class="h-8 w-8 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
214
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
215
- </svg>
216
- </div>
217
- @endif
218
- </div>
219
-
220
- <div class="ml-6 flex flex-1 flex-col">
221
- <div class="flex">
222
- <div class="min-w-0 flex-1">
223
- <h4 class="text-sm">
224
- <a href="#" class="text-gray-700 dark:text-gray-300 font-medium hover:text-gray-800 dark:hover:text-gray-200">
225
- {{ product.name }}
226
- </a>
227
- </h4>
228
- </div>
229
-
230
- @if(showRemoveProduct)
231
- <div class="ml-4 flow-root flex-shrink-0">
232
- <button
233
- type="button"
234
- class="flex items-center justify-center bg-white dark:bg-blue-gray-700 p-2.5 text-gray-400 dark:text-gray-500 -m-2.5 hover:text-gray-500 dark:hover:text-gray-400"
235
- @click="onRemoveProduct('{{ product.id }}')"
236
- title="Remove"
237
- >
238
- <span class="sr-only">Remove</span>
239
- <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
240
- <path fill-rule="evenodd" d="M8.75 1A2.75 2.75 0 0 0 6 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 1 0 .23 1.482l.149-.022.841 10.518A2.75 2.75 0 0 0 7.596 19h4.807a2.75 2.75 0 0 0 2.742-2.53l.841-10.52.149.023a.75.75 0 0 0 .23-1.482A41.03 41.03 0 0 0 14 4.193V3.75A2.75 2.75 0 0 0 11.25 1h-2.5ZM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4ZM8.58 7.72a.75.75 0 0 0-1.5.06l.3 7.5a.75.75 0 1 0 1.5-.06l-.3-7.5Zm4.34.06a.75.75 0 1 0-1.5-.06l-.3 7.5a.75.75 0 1 0 1.5.06l.3-7.5Z" clip-rule="evenodd" />
241
- </svg>
242
- </button>
243
- </div>
244
- @endif
245
- </div>
246
-
247
- <div class="flex flex-1 items-end justify-between pt-2">
248
- <p class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-medium">
249
- ${{ (product.price / 100).toFixed(2) }}
250
- </p>
251
-
252
- <div class="ml-4">
253
- <label for="quantity-{{ product.id }}" class="sr-only">Quantity</label>
254
- <select
255
- id="quantity-{{ product.id }}"
256
- name="quantity"
257
- class="border border-gray-300 dark:border-gray-600 rounded-md text-left text-base text-gray-700 dark:text-gray-300 font-medium shadow-sm bg-white dark:bg-blue-gray-700 focus:border-indigo-500 sm:text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"
258
- >
259
- <option value="1" {{ (product.quantity || 1) === 1 ? 'selected' : '' }}>1</option>
260
- <option value="2" {{ (product.quantity || 1) === 2 ? 'selected' : '' }}>2</option>
261
- <option value="3" {{ (product.quantity || 1) === 3 ? 'selected' : '' }}>3</option>
262
- <option value="4" {{ (product.quantity || 1) === 4 ? 'selected' : '' }}>4</option>
263
- <option value="5" {{ (product.quantity || 1) === 5 ? 'selected' : '' }}>5</option>
264
- <option value="6" {{ (product.quantity || 1) === 6 ? 'selected' : '' }}>6</option>
265
- <option value="7" {{ (product.quantity || 1) === 7 ? 'selected' : '' }}>7</option>
266
- <option value="8" {{ (product.quantity || 1) === 8 ? 'selected' : '' }}>8</option>
267
- </select>
268
- </div>
269
- </div>
270
- </div>
271
- </li>
272
- @endforeach
273
- </ul>
274
-
275
- <!-- Summary Totals -->
276
- <dl class="border-t border-gray-200 dark:border-gray-700 px-4 py-6 space-y-6 sm:px-6">
277
- <div class="flex items-center justify-between">
278
- <dt class="text-sm text-gray-600 dark:text-gray-400">Subtotal</dt>
279
- <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
280
- ${{ (subtotal / 100).toFixed(2) }}
281
- </dd>
282
- </div>
283
-
284
- @if(shipping > 0)
285
- <div class="flex items-center justify-between">
286
- <dt class="text-sm text-gray-600 dark:text-gray-400">Shipping</dt>
287
- <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
288
- ${{ (shipping / 100).toFixed(2) }}
289
- </dd>
290
- </div>
291
- @endif
292
-
293
- @if(taxes > 0)
294
- <div class="flex items-center justify-between">
295
- <dt class="text-sm text-gray-600 dark:text-gray-400">Taxes</dt>
296
- <dd class="text-sm text-gray-900 dark:text-gray-100 font-medium">
297
- ${{ (taxes / 100).toFixed(2) }}
298
- </dd>
299
- </div>
300
- @endif
301
-
302
- <div class="flex items-center justify-between border-t border-gray-200 dark:border-gray-700 pt-6">
303
- <dt class="text-base text-gray-900 dark:text-gray-100 font-medium">Total</dt>
304
- <dd class="text-base text-gray-900 dark:text-gray-100 font-medium">
305
- ${{ (total / 100).toFixed(2) }}
306
- </dd>
307
- </div>
308
- </dl>
309
-
310
- <!-- Submit Button -->
311
- <div class="border-t border-gray-200 dark:border-gray-700 px-4 py-6 sm:px-6">
312
- <button
313
- type="submit"
314
- class="w-full border border-transparent rounded-md bg-indigo-600 dark:bg-indigo-500 px-4 py-3 text-base text-white font-medium shadow-sm hover:bg-indigo-700 dark:hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 focus:ring-offset-gray-50 dark:focus:ring-offset-gray-900"
315
- >
316
- Confirm order
317
- </button>
318
- </div>
319
- </div>
320
- </div>
321
- </form>
322
- </div>
323
- </div>