nitrostack 1.0.54 → 1.0.56

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 (220) hide show
  1. package/dist/cli/commands/init.d.ts.map +1 -1
  2. package/dist/cli/commands/init.js +14 -9
  3. package/dist/cli/commands/init.js.map +1 -1
  4. package/dist/widgets/hooks/index.d.ts +7 -0
  5. package/dist/widgets/hooks/index.d.ts.map +1 -0
  6. package/dist/widgets/hooks/index.js +7 -0
  7. package/dist/widgets/hooks/index.js.map +1 -0
  8. package/dist/widgets/hooks/use-display-mode.d.ts +11 -0
  9. package/dist/widgets/hooks/use-display-mode.d.ts.map +1 -0
  10. package/dist/widgets/hooks/use-display-mode.js +13 -0
  11. package/dist/widgets/hooks/use-display-mode.js.map +1 -0
  12. package/dist/widgets/hooks/use-max-height.d.ts +10 -0
  13. package/dist/widgets/hooks/use-max-height.d.ts.map +1 -0
  14. package/dist/widgets/hooks/use-max-height.js +13 -0
  15. package/dist/widgets/hooks/use-max-height.js.map +1 -0
  16. package/dist/widgets/hooks/use-openai-global.d.ts +12 -0
  17. package/dist/widgets/hooks/use-openai-global.d.ts.map +1 -0
  18. package/dist/widgets/hooks/use-openai-global.js +32 -0
  19. package/dist/widgets/hooks/use-openai-global.js.map +1 -0
  20. package/dist/widgets/hooks/use-theme.d.ts +10 -0
  21. package/dist/widgets/hooks/use-theme.d.ts.map +1 -0
  22. package/dist/widgets/hooks/use-theme.js +12 -0
  23. package/dist/widgets/hooks/use-theme.js.map +1 -0
  24. package/dist/widgets/hooks/use-widget-state.d.ts +18 -0
  25. package/dist/widgets/hooks/use-widget-state.d.ts.map +1 -0
  26. package/dist/widgets/hooks/use-widget-state.js +27 -0
  27. package/dist/widgets/hooks/use-widget-state.js.map +1 -0
  28. package/dist/widgets/hooks/useWidgetSDK.d.ts +47 -0
  29. package/dist/widgets/hooks/useWidgetSDK.d.ts.map +1 -0
  30. package/dist/widgets/hooks/useWidgetSDK.js +67 -0
  31. package/dist/widgets/hooks/useWidgetSDK.js.map +1 -0
  32. package/dist/widgets/index.d.ts +7 -1
  33. package/dist/widgets/index.d.ts.map +1 -1
  34. package/dist/widgets/index.js +11 -1
  35. package/dist/widgets/index.js.map +1 -1
  36. package/dist/widgets/runtime/WidgetLayout.d.ts +32 -0
  37. package/dist/widgets/runtime/WidgetLayout.d.ts.map +1 -0
  38. package/dist/widgets/runtime/WidgetLayout.js +143 -0
  39. package/dist/widgets/runtime/WidgetLayout.js.map +1 -0
  40. package/dist/widgets/runtime/widget-polyfill.d.ts +1 -0
  41. package/dist/widgets/runtime/widget-polyfill.d.ts.map +1 -0
  42. package/dist/widgets/runtime/widget-polyfill.js +28 -0
  43. package/dist/widgets/runtime/widget-polyfill.js.map +1 -0
  44. package/dist/widgets/sdk.d.ts +109 -0
  45. package/dist/widgets/sdk.d.ts.map +1 -0
  46. package/dist/widgets/sdk.js +221 -0
  47. package/dist/widgets/sdk.js.map +1 -0
  48. package/dist/widgets/types.d.ts +89 -0
  49. package/dist/widgets/types.d.ts.map +1 -0
  50. package/dist/widgets/types.js +8 -0
  51. package/dist/widgets/types.js.map +1 -0
  52. package/dist/widgets/utils/media-queries.d.ts +34 -0
  53. package/dist/widgets/utils/media-queries.d.ts.map +1 -0
  54. package/dist/widgets/utils/media-queries.js +42 -0
  55. package/dist/widgets/utils/media-queries.js.map +1 -0
  56. package/package.json +2 -2
  57. package/src/studio/app/chat/page.tsx +274 -137
  58. package/src/studio/app/globals.css +140 -64
  59. package/src/studio/branding.md +807 -0
  60. package/src/studio/components/WidgetRenderer.tsx +222 -16
  61. package/src/studio/lib/llm-service.ts +39 -39
  62. package/templates/typescript-oauth/.env.example +27 -0
  63. package/templates/typescript-oauth/README.md +226 -306
  64. package/templates/typescript-oauth/package-lock.json +4253 -0
  65. package/templates/typescript-oauth/package.json +10 -5
  66. package/templates/typescript-oauth/src/app.module.ts +39 -36
  67. package/templates/typescript-oauth/src/guards/oauth.guard.ts +0 -1
  68. package/templates/typescript-oauth/src/index.ts +22 -30
  69. package/templates/typescript-oauth/src/modules/flights/booking.tools.ts +323 -0
  70. package/templates/typescript-oauth/src/modules/flights/flights.module.ts +14 -0
  71. package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +231 -0
  72. package/templates/typescript-oauth/src/modules/flights/flights.resources.ts +215 -0
  73. package/templates/typescript-oauth/src/modules/flights/flights.tools.ts +457 -0
  74. package/templates/typescript-oauth/src/services/duffel.service.ts +285 -0
  75. package/templates/typescript-oauth/src/widgets/app/airport-search/page.tsx +270 -0
  76. package/templates/typescript-oauth/src/widgets/app/flight-details/page.tsx +261 -0
  77. package/templates/typescript-oauth/src/widgets/app/flight-search-results/page.tsx +378 -0
  78. package/templates/typescript-oauth/src/widgets/app/globals.css +167 -0
  79. package/templates/typescript-oauth/src/widgets/app/layout.tsx +6 -2
  80. package/templates/typescript-oauth/src/widgets/app/order-cancellation/page.tsx +207 -0
  81. package/templates/typescript-oauth/src/widgets/app/order-summary/page.tsx +245 -0
  82. package/templates/typescript-oauth/src/widgets/app/payment-confirmation/page.tsx +152 -0
  83. package/templates/typescript-oauth/src/widgets/app/seat-selection/page.tsx +486 -0
  84. package/templates/typescript-oauth/src/widgets/next-env.d.ts +5 -0
  85. package/templates/typescript-oauth/src/widgets/package-lock.json +155 -126
  86. package/templates/typescript-oauth/src/widgets/widget-manifest.json +374 -27
  87. package/templates/typescript-pizzaz/IMPLEMENTATION.md +98 -0
  88. package/templates/typescript-pizzaz/README.md +233 -0
  89. package/templates/typescript-pizzaz/package.json +31 -0
  90. package/templates/typescript-pizzaz/src/app.module.ts +28 -0
  91. package/templates/typescript-pizzaz/src/index.ts +30 -0
  92. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.data.ts +106 -0
  93. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.module.ts +11 -0
  94. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.service.ts +60 -0
  95. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.tools.ts +197 -0
  96. package/templates/typescript-pizzaz/src/widgets/app/layout.tsx +18 -0
  97. package/templates/typescript-pizzaz/src/widgets/app/pizza-list/page.tsx +272 -0
  98. package/templates/typescript-pizzaz/src/widgets/app/pizza-map/page.tsx +216 -0
  99. package/templates/typescript-pizzaz/src/widgets/app/pizza-shop/page.tsx +374 -0
  100. package/templates/typescript-pizzaz/src/widgets/components/CompactShopCard.tsx +144 -0
  101. package/templates/typescript-pizzaz/src/widgets/components/PizzaCard.tsx +191 -0
  102. package/templates/typescript-pizzaz/src/widgets/package.json +30 -0
  103. package/templates/typescript-pizzaz/src/widgets/widget-manifest.json +253 -0
  104. package/templates/typescript-pizzaz/tsconfig.json +30 -0
  105. package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +0 -1
  106. package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +102 -56
  107. package/templates/typescript-starter/src/widgets/app/layout.tsx +6 -2
  108. package/templates/typescript-auth/AI_AGENT_CLI_REFERENCE.md +0 -702
  109. package/templates/typescript-auth/AI_AGENT_SDK_REFERENCE.md +0 -1260
  110. package/templates/typescript-auth/README.md +0 -402
  111. package/templates/typescript-auth/package.json +0 -36
  112. package/templates/typescript-auth/src/app.module.ts +0 -103
  113. package/templates/typescript-auth/src/db/database.ts +0 -160
  114. package/templates/typescript-auth/src/db/seed.ts +0 -374
  115. package/templates/typescript-auth/src/db/setup.ts +0 -87
  116. package/templates/typescript-auth/src/events/analytics.service.ts +0 -52
  117. package/templates/typescript-auth/src/events/notification.service.ts +0 -40
  118. package/templates/typescript-auth/src/filters/global-exception.filter.ts +0 -28
  119. package/templates/typescript-auth/src/guards/README.md +0 -75
  120. package/templates/typescript-auth/src/guards/jwt.guard.ts +0 -105
  121. package/templates/typescript-auth/src/health/database.health.ts +0 -41
  122. package/templates/typescript-auth/src/index.ts +0 -29
  123. package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +0 -24
  124. package/templates/typescript-auth/src/middleware/logging.middleware.ts +0 -42
  125. package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +0 -16
  126. package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +0 -114
  127. package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +0 -40
  128. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +0 -284
  129. package/templates/typescript-auth/src/modules/auth/auth.module.ts +0 -16
  130. package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +0 -147
  131. package/templates/typescript-auth/src/modules/auth/auth.resources.ts +0 -84
  132. package/templates/typescript-auth/src/modules/auth/auth.tools.ts +0 -139
  133. package/templates/typescript-auth/src/modules/cart/cart.module.ts +0 -16
  134. package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +0 -95
  135. package/templates/typescript-auth/src/modules/cart/cart.resources.ts +0 -44
  136. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +0 -277
  137. package/templates/typescript-auth/src/modules/orders/orders.module.ts +0 -16
  138. package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +0 -88
  139. package/templates/typescript-auth/src/modules/orders/orders.resources.ts +0 -48
  140. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +0 -303
  141. package/templates/typescript-auth/src/modules/products/products.module.ts +0 -16
  142. package/templates/typescript-auth/src/modules/products/products.prompts.ts +0 -146
  143. package/templates/typescript-auth/src/modules/products/products.resources.ts +0 -98
  144. package/templates/typescript-auth/src/modules/products/products.tools.ts +0 -266
  145. package/templates/typescript-auth/src/pipes/validation.pipe.ts +0 -42
  146. package/templates/typescript-auth/src/services/database.service.ts +0 -90
  147. package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +0 -122
  148. package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +0 -116
  149. package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +0 -105
  150. package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +0 -139
  151. package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +0 -153
  152. package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +0 -86
  153. package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +0 -116
  154. package/templates/typescript-auth/src/widgets/app/categories/page.tsx +0 -134
  155. package/templates/typescript-auth/src/widgets/app/layout.tsx +0 -21
  156. package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +0 -129
  157. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +0 -231
  158. package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +0 -225
  159. package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +0 -218
  160. package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +0 -121
  161. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +0 -198
  162. package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +0 -187
  163. package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +0 -165
  164. package/templates/typescript-auth/src/widgets/next.config.js +0 -38
  165. package/templates/typescript-auth/src/widgets/package.json +0 -18
  166. package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +0 -169
  167. package/templates/typescript-auth/src/widgets/tsconfig.json +0 -28
  168. package/templates/typescript-auth/src/widgets/types/tool-data.ts +0 -141
  169. package/templates/typescript-auth/src/widgets/widget-manifest.json +0 -464
  170. package/templates/typescript-auth/tsconfig.json +0 -27
  171. package/templates/typescript-auth-api-key/AI_AGENT_CLI_REFERENCE.md +0 -701
  172. package/templates/typescript-auth-api-key/AI_AGENT_SDK_REFERENCE.md +0 -1260
  173. package/templates/typescript-auth-api-key/README.md +0 -485
  174. package/templates/typescript-auth-api-key/package.json +0 -21
  175. package/templates/typescript-auth-api-key/src/app.module.ts +0 -38
  176. package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +0 -47
  177. package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +0 -157
  178. package/templates/typescript-auth-api-key/src/index.ts +0 -47
  179. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +0 -12
  180. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +0 -73
  181. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +0 -60
  182. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +0 -71
  183. package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +0 -18
  184. package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +0 -155
  185. package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +0 -123
  186. package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +0 -133
  187. package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +0 -134
  188. package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +0 -14
  189. package/templates/typescript-auth-api-key/src/widgets/package.json +0 -24
  190. package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +0 -48
  191. package/templates/typescript-auth-api-key/tsconfig.json +0 -23
  192. package/templates/typescript-oauth/OAUTH_SETUP.md +0 -592
  193. package/templates/typescript-oauth/src/modules/demo/demo.module.ts +0 -16
  194. package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +0 -190
  195. package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +0 -133
  196. package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +0 -134
  197. package/templates/typescript-oauth/src/widgets/out/404.html +0 -1
  198. package/templates/typescript-oauth/src/widgets/out/_next/static/WU9THacVqL52RZbrZOLS1/_buildManifest.js +0 -1
  199. package/templates/typescript-oauth/src/widgets/out/_next/static/WU9THacVqL52RZbrZOLS1/_ssgManifest.js +0 -1
  200. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/117-eb57c7ef86f964a4.js +0 -2
  201. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/_not-found/page-dcb83ba3e4d0aafd.js +0 -1
  202. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/calculator-operations/page-b8913a740073ea8a.js +0 -1
  203. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/calculator-result/page-ddaaab2fce95dea2.js +0 -1
  204. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/layout-cbd3ebdc4ecc5247.js +0 -1
  205. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/fd9d1056-749e5812300142af.js +0 -1
  206. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
  207. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/main-76df43fcef3db344.js +0 -1
  208. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/main-app-f9c40224d04023c5.js +0 -1
  209. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
  210. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
  211. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  212. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/webpack-100b9e646d9c912e.js +0 -1
  213. package/templates/typescript-oauth/src/widgets/out/calculator-operations.html +0 -1
  214. package/templates/typescript-oauth/src/widgets/out/calculator-operations.txt +0 -7
  215. package/templates/typescript-oauth/src/widgets/out/calculator-result.html +0 -1
  216. package/templates/typescript-oauth/src/widgets/out/calculator-result.txt +0 -7
  217. package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +0 -133
  218. /package/templates/{typescript-auth-api-key → typescript-oauth}/src/health/system.health.ts +0 -0
  219. /package/templates/{typescript-auth-api-key → typescript-pizzaz}/src/widgets/next.config.js +0 -0
  220. /package/templates/{typescript-auth-api-key → typescript-pizzaz}/src/widgets/tsconfig.json +0 -0
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+
3
+ import { useWidgetSDK, useTheme } from 'nitrostack/widgets';
4
+
5
+ /**
6
+ * Payment Confirmation Widget - Success state with booking details
7
+ */
8
+
9
+ interface PaymentData {
10
+ orderId: string;
11
+ status: string;
12
+ totalAmount: string;
13
+ totalCurrency: string;
14
+ bookingReference?: string;
15
+ message?: string;
16
+ }
17
+
18
+ export default function PaymentConfirmation() {
19
+ const { getToolOutput } = useWidgetSDK();
20
+ const theme = useTheme();
21
+ const data = getToolOutput<PaymentData>();
22
+
23
+ const isDark = theme === 'dark';
24
+
25
+ if (!data) {
26
+ return <div style={{ padding: '24px', textAlign: 'center' }}>Loading...</div>;
27
+ }
28
+
29
+ const isConfirmed = data.status === 'confirmed';
30
+
31
+ return (
32
+ <div className={isDark ? 'dark' : ''} style={{
33
+ padding: '24px',
34
+ background: isDark ? '#020617' : '#FFFFFF',
35
+ color: isDark ? '#F8FAFC' : '#020617',
36
+ minHeight: '300px',
37
+ display: 'flex',
38
+ alignItems: 'center',
39
+ justifyContent: 'center'
40
+ }}>
41
+ <div style={{ maxWidth: '500px', width: '100%', textAlign: 'center' }}>
42
+ {/* Success Icon */}
43
+ <div style={{
44
+ width: '100px',
45
+ height: '100px',
46
+ margin: '0 auto 24px',
47
+ background: isConfirmed ? 'linear-gradient(135deg, #22C55E 0%, #16A34A 100%)' : 'linear-gradient(135deg, #3B9FFF 0%, #2563EB 100%)',
48
+ borderRadius: '50%',
49
+ display: 'flex',
50
+ alignItems: 'center',
51
+ justifyContent: 'center',
52
+ boxShadow: isConfirmed ? '0 8px 24px rgba(34, 197, 94, 0.3)' : '0 8px 24px rgba(59, 159, 255, 0.3)'
53
+ }}>
54
+ <div style={{ fontSize: '48px', color: 'white' }}>
55
+ {isConfirmed ? '✓' : '💳'}
56
+ </div>
57
+ </div>
58
+
59
+ {/* Title */}
60
+ <h2 style={{ margin: '0 0 12px 0', fontSize: '24px', fontWeight: 700 }}>
61
+ {isConfirmed ? 'Payment Successful!' : 'Complete Payment'}
62
+ </h2>
63
+
64
+ <p style={{ margin: '0 0 24px 0', fontSize: '14px', color: isDark ? '#94A3B8' : '#64748B', lineHeight: '1.6' }}>
65
+ {data.message || (isConfirmed ? 'Your booking has been confirmed.' : 'Review and confirm your payment.')}
66
+ </p>
67
+
68
+ {/* Booking Details */}
69
+ <div style={{
70
+ background: isDark ? '#0F172A' : '#F8FAFC',
71
+ borderRadius: '12px',
72
+ padding: '20px',
73
+ marginBottom: '24px',
74
+ border: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`
75
+ }}>
76
+ {data.bookingReference && (
77
+ <div style={{ marginBottom: '16px' }}>
78
+ <div style={{ fontSize: '11px', color: isDark ? '#94A3B8' : '#64748B', marginBottom: '6px', textTransform: 'uppercase', letterSpacing: '0.5px' }}>
79
+ Booking Reference
80
+ </div>
81
+ <div style={{ fontSize: '20px', fontWeight: 700, color: '#22C55E', letterSpacing: '1px', fontFamily: 'monospace' }}>
82
+ {data.bookingReference}
83
+ </div>
84
+ </div>
85
+ )}
86
+
87
+ <div style={{
88
+ display: 'grid',
89
+ gap: '12px',
90
+ paddingTop: data.bookingReference ? '16px' : '0',
91
+ borderTop: data.bookingReference ? `1px solid ${isDark ? '#334155' : '#E2E8F0'}` : 'none'
92
+ }}>
93
+ <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '12px' }}>
94
+ <span style={{ color: isDark ? '#94A3B8' : '#64748B' }}>Order ID:</span>
95
+ <span style={{ fontWeight: 600, fontFamily: 'monospace' }}>{data.orderId}</span>
96
+ </div>
97
+
98
+ <div style={{
99
+ display: 'flex',
100
+ justifyContent: 'space-between',
101
+ paddingTop: '12px',
102
+ borderTop: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`
103
+ }}>
104
+ <span style={{ fontSize: '14px', fontWeight: 600 }}>
105
+ {isConfirmed ? 'Amount Paid:' : 'Total Amount:'}
106
+ </span>
107
+ <span style={{ color: 'var(--primary)', fontSize: '20px', fontWeight: 700 }}>
108
+ {data.totalCurrency} {parseFloat(data.totalAmount).toFixed(2)}
109
+ </span>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ {/* Action Buttons */}
115
+ {isConfirmed ? (
116
+ <div style={{ display: 'grid', gap: '10px' }}>
117
+ <button className="btn-primary" style={{ width: '100%' }}>
118
+ 📧 Email Confirmation
119
+ </button>
120
+ <button className="btn-secondary" style={{ width: '100%' }}>
121
+ 📄 Download Receipt
122
+ </button>
123
+ </div>
124
+ ) : (
125
+ <div>
126
+ <button className="btn-primary" style={{ width: '100%', marginBottom: '12px' }}>
127
+ 🔒 Confirm & Pay {data.totalCurrency} {parseFloat(data.totalAmount).toFixed(2)}
128
+ </button>
129
+ <div style={{ fontSize: '11px', color: isDark ? '#94A3B8' : '#64748B', lineHeight: '1.5' }}>
130
+ 🔒 Your payment is secure and encrypted
131
+ </div>
132
+ </div>
133
+ )}
134
+
135
+ {/* Footer Note */}
136
+ {isConfirmed && (
137
+ <div style={{
138
+ marginTop: '24px',
139
+ padding: '12px',
140
+ background: '#FEF3C7',
141
+ borderRadius: '8px',
142
+ fontSize: '11px',
143
+ color: '#92400E',
144
+ lineHeight: '1.5'
145
+ }}>
146
+ <strong>📱 Important:</strong> A confirmation email has been sent to your registered email address.
147
+ </div>
148
+ )}
149
+ </div>
150
+ </div>
151
+ );
152
+ }
@@ -0,0 +1,486 @@
1
+ 'use client';
2
+
3
+ import { useWidgetSDK, useTheme } from 'nitrostack/widgets';
4
+ import { useState } from 'react';
5
+
6
+ /**
7
+ * Seat Selection Widget
8
+ *
9
+ * Interactive seat map with real-time selection for multiple passengers.
10
+ */
11
+
12
+ interface Seat {
13
+ id: string;
14
+ column: string;
15
+ available: boolean;
16
+ price?: string;
17
+ type: string;
18
+ }
19
+
20
+ interface Row {
21
+ rowNumber: number;
22
+ seats: Seat[];
23
+ }
24
+
25
+ interface Cabin {
26
+ cabinClass: string;
27
+ rows: Row[];
28
+ }
29
+
30
+ interface SeatMapData {
31
+ offerId: string;
32
+ cabins: Cabin[];
33
+ message?: string;
34
+ }
35
+
36
+ export default function SeatSelection() {
37
+ const { getToolOutput } = useWidgetSDK();
38
+ const theme = useTheme();
39
+ const data = getToolOutput<SeatMapData>();
40
+
41
+ const isDark = theme === 'dark';
42
+ const [selectedSeats, setSelectedSeats] = useState<Record<string, string>>({});
43
+ const [activePassenger, setActivePassenger] = useState(0);
44
+ const [hoveredSeat, setHoveredSeat] = useState<string | null>(null);
45
+
46
+ const passengers = [
47
+ { id: 'pax_1', name: 'Passenger 1' },
48
+ { id: 'pax_2', name: 'Passenger 2' }
49
+ ];
50
+
51
+ const handleSeatClick = (seatId: string, seat: Seat) => {
52
+ if (!seat.available) return;
53
+
54
+ const currentPassengerId = passengers[activePassenger].id;
55
+ const seatOwner = Object.entries(selectedSeats).find(([_, id]) => id === seatId)?.[0];
56
+
57
+ if (seatOwner && seatOwner !== currentPassengerId) return;
58
+
59
+ setSelectedSeats(prev => {
60
+ const newSelections = { ...prev };
61
+ if (newSelections[currentPassengerId] === seatId) {
62
+ delete newSelections[currentPassengerId];
63
+ } else {
64
+ delete newSelections[currentPassengerId];
65
+ newSelections[currentPassengerId] = seatId;
66
+ if (activePassenger < passengers.length - 1) {
67
+ setTimeout(() => setActivePassenger(activePassenger + 1), 200);
68
+ }
69
+ }
70
+ return newSelections;
71
+ });
72
+ };
73
+
74
+ const getSeatStatus = (seatId: string, seat: Seat) => {
75
+ if (!seat.available) return 'unavailable';
76
+ const owner = Object.entries(selectedSeats).find(([_, id]) => id === seatId)?.[0];
77
+ if (owner) {
78
+ const passengerIndex = passengers.findIndex(p => p.id === owner);
79
+ return passengerIndex === activePassenger ? 'selected-active' : 'selected-other';
80
+ }
81
+ return 'available';
82
+ };
83
+
84
+ const getSeatColor = (status: string) => {
85
+ if (isDark) {
86
+ return {
87
+ 'available': '#334155',
88
+ 'selected-active': '#3B9FFF',
89
+ 'selected-other': '#22C55E',
90
+ 'unavailable': '#1E293B'
91
+ }[status] || '#334155';
92
+ }
93
+ return {
94
+ 'available': '#E2E8F0',
95
+ 'selected-active': '#3B9FFF',
96
+ 'selected-other': '#22C55E',
97
+ 'unavailable': '#CBD5E1'
98
+ }[status] || '#E2E8F0';
99
+ };
100
+
101
+ const calculateTotalPrice = () => {
102
+ let total = 0;
103
+ Object.values(selectedSeats).forEach(seatId => {
104
+ data?.cabins.forEach(cabin => {
105
+ cabin.rows.forEach(row => {
106
+ const seat = row.seats.find(s => s.id === seatId);
107
+ if (seat?.price) total += parseFloat(seat.price);
108
+ });
109
+ });
110
+ });
111
+ return total;
112
+ };
113
+
114
+ const getSelectedSeatInfo = (passengerId: string) => {
115
+ const seatId = selectedSeats[passengerId];
116
+ if (!seatId || !data) return null;
117
+
118
+ for (const cabin of data.cabins) {
119
+ for (const row of cabin.rows) {
120
+ const seat = row.seats.find(s => s.id === seatId);
121
+ if (seat) return { seat, row: row.rowNumber };
122
+ }
123
+ }
124
+ return null;
125
+ };
126
+
127
+ if (!data) {
128
+ return <div style={{ padding: '24px', textAlign: 'center' }}>Loading...</div>;
129
+ }
130
+
131
+ return (
132
+ <div className={isDark ? 'dark' : ''} style={{
133
+ padding: '16px',
134
+ background: isDark ? '#020617' : '#FFFFFF',
135
+ color: isDark ? '#F8FAFC' : '#020617'
136
+ }}>
137
+ {/* Header */}
138
+ <div className="card" style={{ marginBottom: '16px' }}>
139
+ <div style={{
140
+ display: 'flex',
141
+ alignItems: 'center',
142
+ justifyContent: 'space-between',
143
+ flexWrap: 'wrap',
144
+ gap: '12px'
145
+ }}>
146
+ <div>
147
+ <h2 style={{
148
+ margin: 0,
149
+ fontSize: '18px',
150
+ fontWeight: 700,
151
+ display: 'flex',
152
+ alignItems: 'center',
153
+ gap: '8px'
154
+ }}>
155
+ <span>💺</span>
156
+ <span>Select Seats</span>
157
+ </h2>
158
+ <p style={{
159
+ margin: '4px 0 0 0',
160
+ fontSize: '12px',
161
+ color: isDark ? '#94A3B8' : '#64748B'
162
+ }}>
163
+ {data.message || 'Choose seats for all passengers'}
164
+ </p>
165
+ </div>
166
+
167
+ {Object.keys(selectedSeats).length > 0 && (
168
+ <div style={{
169
+ background: 'var(--primary)',
170
+ color: 'white',
171
+ padding: '8px 16px',
172
+ borderRadius: '8px',
173
+ textAlign: 'center'
174
+ }}>
175
+ <div style={{ fontSize: '10px', opacity: 0.9 }}>Total</div>
176
+ <div style={{ fontSize: '18px', fontWeight: 700 }}>
177
+ ${calculateTotalPrice().toFixed(2)}
178
+ </div>
179
+ </div>
180
+ )}
181
+ </div>
182
+ </div>
183
+
184
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: '16px' }}>
185
+ {/* Seat Map */}
186
+ <div className="card" style={{ minHeight: '400px' }}>
187
+ {/* Front Indicator */}
188
+ <div style={{
189
+ textAlign: 'center',
190
+ marginBottom: '20px',
191
+ paddingBottom: '12px',
192
+ borderBottom: `2px dashed ${isDark ? '#334155' : '#E2E8F0'}`
193
+ }}>
194
+ <div style={{ fontSize: '24px', marginBottom: '6px' }}>✈️</div>
195
+ <div style={{ fontSize: '12px', fontWeight: 600, color: '#3B9FFF' }}>
196
+ FRONT
197
+ </div>
198
+ </div>
199
+
200
+ {data.cabins.map((cabin, cabinIndex) => (
201
+ <div key={cabinIndex} style={{ marginBottom: '20px' }}>
202
+ <div style={{
203
+ background: 'var(--primary)',
204
+ color: 'white',
205
+ padding: '8px 16px',
206
+ borderRadius: '6px',
207
+ marginBottom: '12px',
208
+ fontSize: '12px',
209
+ fontWeight: 600,
210
+ textTransform: 'uppercase'
211
+ }}>
212
+ {cabin.cabinClass.replace('_', ' ')}
213
+ </div>
214
+
215
+ <div style={{ display: 'grid', gap: '6px' }}>
216
+ {cabin.rows.map((row) => (
217
+ <div key={row.rowNumber} style={{
218
+ display: 'grid',
219
+ gridTemplateColumns: 'auto 1fr auto',
220
+ gap: '12px',
221
+ alignItems: 'center'
222
+ }}>
223
+ <div style={{
224
+ width: '32px',
225
+ textAlign: 'center',
226
+ fontSize: '12px',
227
+ fontWeight: 600,
228
+ color: isDark ? '#94A3B8' : '#64748B'
229
+ }}>
230
+ {row.rowNumber}
231
+ </div>
232
+
233
+ <div style={{
234
+ display: 'flex',
235
+ gap: '6px',
236
+ justifyContent: 'center',
237
+ flexWrap: 'wrap'
238
+ }}>
239
+ {row.seats.map((seat) => {
240
+ const status = getSeatStatus(seat.id, seat);
241
+ const isHovered = hoveredSeat === seat.id;
242
+
243
+ return (
244
+ <div
245
+ key={seat.id}
246
+ onClick={() => handleSeatClick(seat.id, seat)}
247
+ onMouseEnter={() => setHoveredSeat(seat.id)}
248
+ onMouseLeave={() => setHoveredSeat(null)}
249
+ style={{
250
+ width: '40px',
251
+ height: '40px',
252
+ background: getSeatColor(status),
253
+ borderRadius: '6px',
254
+ display: 'flex',
255
+ alignItems: 'center',
256
+ justifyContent: 'center',
257
+ cursor: seat.available ? 'pointer' : 'not-allowed',
258
+ transition: 'all 0.2s ease',
259
+ transform: isHovered && seat.available ? 'scale(1.1)' : 'scale(1)',
260
+ border: status === 'selected-active' ? '2px solid #fff' : 'none',
261
+ position: 'relative',
262
+ opacity: seat.available ? 1 : 0.4
263
+ }}
264
+ >
265
+ <span style={{ fontSize: '16px' }}>💺</span>
266
+ <div style={{
267
+ position: 'absolute',
268
+ bottom: '2px',
269
+ fontSize: '8px',
270
+ fontWeight: 600,
271
+ color: status.includes('selected') ? 'white' : isDark ? '#94A3B8' : '#64748B'
272
+ }}>
273
+ {seat.column}
274
+ </div>
275
+ {seat.price && parseFloat(seat.price) > 0 && isHovered && (
276
+ <div style={{
277
+ position: 'absolute',
278
+ top: '-20px',
279
+ background: '#1a202c',
280
+ color: 'white',
281
+ padding: '3px 6px',
282
+ borderRadius: '4px',
283
+ fontSize: '10px',
284
+ fontWeight: 600,
285
+ whiteSpace: 'nowrap'
286
+ }}>
287
+ ${seat.price}
288
+ </div>
289
+ )}
290
+ </div>
291
+ );
292
+ })}
293
+ </div>
294
+
295
+ <div style={{
296
+ width: '32px',
297
+ textAlign: 'center',
298
+ fontSize: '12px',
299
+ fontWeight: 600,
300
+ color: isDark ? '#94A3B8' : '#64748B'
301
+ }}>
302
+ {row.rowNumber}
303
+ </div>
304
+ </div>
305
+ ))}
306
+ </div>
307
+ </div>
308
+ ))}
309
+
310
+ {/* Legend */}
311
+ <div style={{
312
+ marginTop: '20px',
313
+ paddingTop: '16px',
314
+ borderTop: `2px dashed ${isDark ? '#334155' : '#E2E8F0'}`,
315
+ display: 'flex',
316
+ gap: '16px',
317
+ flexWrap: 'wrap',
318
+ justifyContent: 'center',
319
+ fontSize: '11px'
320
+ }}>
321
+ {[
322
+ { label: 'Available', color: isDark ? '#334155' : '#E2E8F0' },
323
+ { label: 'Your Seat', color: '#3B9FFF' },
324
+ { label: 'Other', color: '#22C55E' },
325
+ { label: 'Taken', color: isDark ? '#1E293B' : '#CBD5E1' }
326
+ ].map(item => (
327
+ <div key={item.label} style={{
328
+ display: 'flex',
329
+ alignItems: 'center',
330
+ gap: '6px'
331
+ }}>
332
+ <div style={{
333
+ width: '20px',
334
+ height: '20px',
335
+ background: item.color,
336
+ borderRadius: '4px'
337
+ }} />
338
+ <span style={{ color: isDark ? '#94A3B8' : '#64748B' }}>
339
+ {item.label}
340
+ </span>
341
+ </div>
342
+ ))}
343
+ </div>
344
+ </div>
345
+
346
+ {/* Sidebar */}
347
+ <div style={{ display: 'grid', gap: '12px', alignContent: 'start' }}>
348
+ {/* Passengers */}
349
+ <div className="card">
350
+ <h3 style={{
351
+ margin: '0 0 12px 0',
352
+ fontSize: '14px',
353
+ fontWeight: 600
354
+ }}>
355
+ Passengers
356
+ </h3>
357
+
358
+ <div style={{ display: 'grid', gap: '8px' }}>
359
+ {passengers.map((passenger, index) => {
360
+ const seatInfo = getSelectedSeatInfo(passenger.id);
361
+ const isActive = activePassenger === index;
362
+
363
+ return (
364
+ <div
365
+ key={passenger.id}
366
+ onClick={() => setActivePassenger(index)}
367
+ className={isActive ? 'nitro-gradient' : ''}
368
+ style={{
369
+ padding: '12px',
370
+ background: isActive ? undefined : (isDark ? '#0F172A' : '#F8FAFC'),
371
+ borderRadius: '8px',
372
+ cursor: 'pointer',
373
+ transition: 'all 0.2s ease',
374
+ border: isActive ? '2px solid #fff' : '2px solid transparent'
375
+ }}
376
+ >
377
+ <div style={{
378
+ display: 'flex',
379
+ justifyContent: 'space-between',
380
+ alignItems: 'center'
381
+ }}>
382
+ <div>
383
+ <div style={{
384
+ fontWeight: 600,
385
+ fontSize: '13px',
386
+ color: isActive ? 'white' : (isDark ? '#F8FAFC' : '#020617'),
387
+ marginBottom: '2px'
388
+ }}>
389
+ {passenger.name}
390
+ </div>
391
+ {seatInfo ? (
392
+ <div style={{
393
+ fontSize: '11px',
394
+ color: isActive ? 'rgba(255,255,255,0.9)' : (isDark ? '#94A3B8' : '#64748B')
395
+ }}>
396
+ {seatInfo.row}{seatInfo.seat.column}
397
+ {seatInfo.seat.price && ` • $${seatInfo.seat.price}`}
398
+ </div>
399
+ ) : (
400
+ <div style={{
401
+ fontSize: '11px',
402
+ color: isActive ? 'rgba(255,255,255,0.8)' : (isDark ? '#64748B' : '#94A3B8')
403
+ }}>
404
+ No seat
405
+ </div>
406
+ )}
407
+ </div>
408
+
409
+ {seatInfo && (
410
+ <div style={{
411
+ width: '24px',
412
+ height: '24px',
413
+ background: isActive ? 'rgba(255,255,255,0.2)' : '#22C55E',
414
+ borderRadius: '50%',
415
+ display: 'flex',
416
+ alignItems: 'center',
417
+ justifyContent: 'center',
418
+ color: 'white',
419
+ fontSize: '12px'
420
+ }}>
421
+
422
+ </div>
423
+ )}
424
+ </div>
425
+ </div>
426
+ );
427
+ })}
428
+ </div>
429
+ </div>
430
+
431
+ {/* Summary */}
432
+ <div className="card">
433
+ <h3 style={{
434
+ margin: '0 0 12px 0',
435
+ fontSize: '14px',
436
+ fontWeight: 600
437
+ }}>
438
+ Summary
439
+ </h3>
440
+
441
+ <div style={{ display: 'grid', gap: '10px', marginBottom: '12px' }}>
442
+ <div style={{
443
+ display: 'flex',
444
+ justifyContent: 'space-between',
445
+ fontSize: '12px'
446
+ }}>
447
+ <span style={{ color: isDark ? '#94A3B8' : '#64748B' }}>Selected:</span>
448
+ <span style={{ fontWeight: 600 }}>
449
+ {Object.keys(selectedSeats).length} / {passengers.length}
450
+ </span>
451
+ </div>
452
+
453
+ <div style={{
454
+ display: 'flex',
455
+ justifyContent: 'space-between',
456
+ fontSize: '12px',
457
+ paddingTop: '10px',
458
+ borderTop: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`
459
+ }}>
460
+ <span style={{ color: isDark ? '#94A3B8' : '#64748B' }}>Total:</span>
461
+ <span style={{ color: 'var(--primary)', fontWeight: 700, fontSize: '16px' }}>
462
+ ${calculateTotalPrice().toFixed(2)}
463
+ </span>
464
+ </div>
465
+ </div>
466
+
467
+ <button
468
+ disabled={Object.keys(selectedSeats).length !== passengers.length}
469
+ className={Object.keys(selectedSeats).length === passengers.length ? 'btn-primary' : 'btn-secondary'}
470
+ style={{
471
+ width: '100%',
472
+ opacity: Object.keys(selectedSeats).length === passengers.length ? 1 : 0.5,
473
+ cursor: Object.keys(selectedSeats).length === passengers.length ? 'pointer' : 'not-allowed'
474
+ }}
475
+ >
476
+ {Object.keys(selectedSeats).length === passengers.length
477
+ ? 'Confirm Selection'
478
+ : `Select ${passengers.length - Object.keys(selectedSeats).length} More`
479
+ }
480
+ </button>
481
+ </div>
482
+ </div>
483
+ </div>
484
+ </div>
485
+ );
486
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.