nitrostack 1.0.55 → 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 (222) 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 +1 -1
  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 → .env.example} +4 -10
  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/env.example +0 -25
  112. package/templates/typescript-auth/package.json +0 -36
  113. package/templates/typescript-auth/src/app.module.ts +0 -103
  114. package/templates/typescript-auth/src/db/database.ts +0 -160
  115. package/templates/typescript-auth/src/db/seed.ts +0 -374
  116. package/templates/typescript-auth/src/db/setup.ts +0 -87
  117. package/templates/typescript-auth/src/events/analytics.service.ts +0 -52
  118. package/templates/typescript-auth/src/events/notification.service.ts +0 -40
  119. package/templates/typescript-auth/src/filters/global-exception.filter.ts +0 -28
  120. package/templates/typescript-auth/src/guards/README.md +0 -75
  121. package/templates/typescript-auth/src/guards/jwt.guard.ts +0 -105
  122. package/templates/typescript-auth/src/health/database.health.ts +0 -41
  123. package/templates/typescript-auth/src/index.ts +0 -29
  124. package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +0 -24
  125. package/templates/typescript-auth/src/middleware/logging.middleware.ts +0 -42
  126. package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +0 -16
  127. package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +0 -114
  128. package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +0 -40
  129. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +0 -284
  130. package/templates/typescript-auth/src/modules/auth/auth.module.ts +0 -16
  131. package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +0 -147
  132. package/templates/typescript-auth/src/modules/auth/auth.resources.ts +0 -84
  133. package/templates/typescript-auth/src/modules/auth/auth.tools.ts +0 -139
  134. package/templates/typescript-auth/src/modules/cart/cart.module.ts +0 -16
  135. package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +0 -95
  136. package/templates/typescript-auth/src/modules/cart/cart.resources.ts +0 -44
  137. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +0 -277
  138. package/templates/typescript-auth/src/modules/orders/orders.module.ts +0 -16
  139. package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +0 -88
  140. package/templates/typescript-auth/src/modules/orders/orders.resources.ts +0 -48
  141. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +0 -303
  142. package/templates/typescript-auth/src/modules/products/products.module.ts +0 -16
  143. package/templates/typescript-auth/src/modules/products/products.prompts.ts +0 -146
  144. package/templates/typescript-auth/src/modules/products/products.resources.ts +0 -98
  145. package/templates/typescript-auth/src/modules/products/products.tools.ts +0 -266
  146. package/templates/typescript-auth/src/pipes/validation.pipe.ts +0 -42
  147. package/templates/typescript-auth/src/services/database.service.ts +0 -90
  148. package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +0 -122
  149. package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +0 -116
  150. package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +0 -105
  151. package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +0 -139
  152. package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +0 -153
  153. package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +0 -86
  154. package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +0 -116
  155. package/templates/typescript-auth/src/widgets/app/categories/page.tsx +0 -134
  156. package/templates/typescript-auth/src/widgets/app/layout.tsx +0 -21
  157. package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +0 -129
  158. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +0 -231
  159. package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +0 -225
  160. package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +0 -218
  161. package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +0 -121
  162. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +0 -198
  163. package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +0 -187
  164. package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +0 -165
  165. package/templates/typescript-auth/src/widgets/next.config.js +0 -38
  166. package/templates/typescript-auth/src/widgets/package.json +0 -18
  167. package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +0 -169
  168. package/templates/typescript-auth/src/widgets/tsconfig.json +0 -28
  169. package/templates/typescript-auth/src/widgets/types/tool-data.ts +0 -141
  170. package/templates/typescript-auth/src/widgets/widget-manifest.json +0 -464
  171. package/templates/typescript-auth/tsconfig.json +0 -27
  172. package/templates/typescript-auth-api-key/AI_AGENT_CLI_REFERENCE.md +0 -701
  173. package/templates/typescript-auth-api-key/AI_AGENT_SDK_REFERENCE.md +0 -1260
  174. package/templates/typescript-auth-api-key/README.md +0 -485
  175. package/templates/typescript-auth-api-key/env.example +0 -17
  176. package/templates/typescript-auth-api-key/package.json +0 -21
  177. package/templates/typescript-auth-api-key/src/app.module.ts +0 -38
  178. package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +0 -47
  179. package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +0 -157
  180. package/templates/typescript-auth-api-key/src/index.ts +0 -47
  181. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +0 -12
  182. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +0 -73
  183. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +0 -60
  184. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +0 -71
  185. package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +0 -18
  186. package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +0 -155
  187. package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +0 -123
  188. package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +0 -133
  189. package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +0 -134
  190. package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +0 -14
  191. package/templates/typescript-auth-api-key/src/widgets/package.json +0 -24
  192. package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +0 -48
  193. package/templates/typescript-auth-api-key/tsconfig.json +0 -23
  194. package/templates/typescript-oauth/OAUTH_SETUP.md +0 -592
  195. package/templates/typescript-oauth/src/modules/demo/demo.module.ts +0 -16
  196. package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +0 -190
  197. package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +0 -133
  198. package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +0 -134
  199. package/templates/typescript-oauth/src/widgets/out/404.html +0 -1
  200. package/templates/typescript-oauth/src/widgets/out/_next/static/WU9THacVqL52RZbrZOLS1/_buildManifest.js +0 -1
  201. package/templates/typescript-oauth/src/widgets/out/_next/static/WU9THacVqL52RZbrZOLS1/_ssgManifest.js +0 -1
  202. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/117-eb57c7ef86f964a4.js +0 -2
  203. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/_not-found/page-dcb83ba3e4d0aafd.js +0 -1
  204. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/calculator-operations/page-b8913a740073ea8a.js +0 -1
  205. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/calculator-result/page-ddaaab2fce95dea2.js +0 -1
  206. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/app/layout-cbd3ebdc4ecc5247.js +0 -1
  207. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/fd9d1056-749e5812300142af.js +0 -1
  208. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
  209. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/main-76df43fcef3db344.js +0 -1
  210. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/main-app-f9c40224d04023c5.js +0 -1
  211. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
  212. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
  213. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  214. package/templates/typescript-oauth/src/widgets/out/_next/static/chunks/webpack-100b9e646d9c912e.js +0 -1
  215. package/templates/typescript-oauth/src/widgets/out/calculator-operations.html +0 -1
  216. package/templates/typescript-oauth/src/widgets/out/calculator-operations.txt +0 -7
  217. package/templates/typescript-oauth/src/widgets/out/calculator-result.html +0 -1
  218. package/templates/typescript-oauth/src/widgets/out/calculator-result.txt +0 -7
  219. package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +0 -133
  220. /package/templates/{typescript-auth-api-key → typescript-oauth}/src/health/system.health.ts +0 -0
  221. /package/templates/{typescript-auth-api-key → typescript-pizzaz}/src/widgets/next.config.js +0 -0
  222. /package/templates/{typescript-auth-api-key → typescript-pizzaz}/src/widgets/tsconfig.json +0 -0
@@ -0,0 +1,261 @@
1
+ 'use client';
2
+
3
+ import { useWidgetSDK, useTheme } from 'nitrostack/widgets';
4
+
5
+ /**
6
+ * Flight Details Widget - Compact view with segments, baggage, and fare conditions
7
+ */
8
+
9
+ interface Segment {
10
+ id: string;
11
+ origin: string;
12
+ destination: string;
13
+ departingAt: string;
14
+ arrivingAt: string;
15
+ duration: string;
16
+ airline: { name: string; code: string; flightNumber: string };
17
+ aircraft?: string;
18
+ }
19
+
20
+ interface Slice {
21
+ origin: { code: string; name: string; city: string };
22
+ destination: { code: string; name: string; city: string };
23
+ duration: string;
24
+ segments: Segment[];
25
+ }
26
+
27
+ interface FlightDetailsData {
28
+ id: string;
29
+ totalAmount: string;
30
+ totalCurrency: string;
31
+ slices: Slice[];
32
+ passengers: Array<{ id: string; type: string; baggageAllowance?: Array<{ type: string; quantity: number }> }>;
33
+ conditions: {
34
+ refundBeforeDeparture: { allowed: boolean; penaltyAmount?: string; penaltyCurrency?: string };
35
+ changeBeforeDeparture: { allowed: boolean; penaltyAmount?: string; penaltyCurrency?: string };
36
+ };
37
+ }
38
+
39
+ export default function FlightDetails() {
40
+ const { getToolOutput } = useWidgetSDK();
41
+ const theme = useTheme();
42
+ const data = getToolOutput<FlightDetailsData>();
43
+
44
+ const isDark = theme === 'dark';
45
+
46
+ const formatDuration = (duration: string) => {
47
+ const match = duration.match(/PT(\d+H)?(\d+M)?/);
48
+ if (!match) return duration;
49
+ const hours = match[1] ? parseInt(match[1]) : 0;
50
+ const minutes = match[2] ? parseInt(match[2]) : 0;
51
+ return `${hours}h ${minutes}m`;
52
+ };
53
+
54
+ const formatTime = (isoString: string) => {
55
+ const date = new Date(isoString);
56
+ return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
57
+ };
58
+
59
+ const formatDate = (isoString: string) => {
60
+ const date = new Date(isoString);
61
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
62
+ };
63
+
64
+ if (!data) {
65
+ return <div style={{ padding: '24px', textAlign: 'center' }}>Loading...</div>;
66
+ }
67
+
68
+ return (
69
+ <div className={isDark ? 'dark' : ''} style={{
70
+ padding: '16px',
71
+ background: isDark ? '#020617' : '#FFFFFF',
72
+ color: isDark ? '#F8FAFC' : '#020617'
73
+ }}>
74
+ {/* Header */}
75
+ <div className="card" style={{ marginBottom: '16px' }}>
76
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: '12px' }}>
77
+ <div>
78
+ <h2 style={{ margin: 0, fontSize: '18px', fontWeight: 700 }}>Flight Details</h2>
79
+ <p style={{ margin: '4px 0 0 0', fontSize: '11px', color: isDark ? '#94A3B8' : '#64748B' }}>
80
+ Offer ID: {data.id}
81
+ </p>
82
+ </div>
83
+ <div style={{ textAlign: 'right' }}>
84
+ <div style={{ color: 'var(--primary)', fontSize: '24px', fontWeight: 700 }}>
85
+ {data.totalCurrency} {parseFloat(data.totalAmount).toFixed(2)}
86
+ </div>
87
+ <div style={{ fontSize: '10px', color: isDark ? '#94A3B8' : '#64748B' }}>Total Price</div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ {/* Flight Itinerary */}
93
+ {data.slices.map((slice, sliceIndex) => (
94
+ <div key={sliceIndex} className="card" style={{ marginBottom: '12px' }}>
95
+ <h3 style={{ margin: '0 0 12px 0', fontSize: '14px', fontWeight: 600, display: 'flex', alignItems: 'center', gap: '6px' }}>
96
+ <span>✈️</span>
97
+ <span>{slice.origin.city} → {slice.destination.city}</span>
98
+ </h3>
99
+
100
+ <div style={{
101
+ background: isDark ? '#0F172A' : '#F8FAFC',
102
+ borderRadius: '8px',
103
+ padding: '12px',
104
+ marginBottom: '12px',
105
+ fontSize: '12px',
106
+ textAlign: 'center',
107
+ color: '#3B9FFF',
108
+ fontWeight: 600
109
+ }}>
110
+ Duration: {formatDuration(slice.duration)}
111
+ </div>
112
+
113
+ {/* Segments */}
114
+ {slice.segments.map((segment) => (
115
+ <div key={segment.id} style={{
116
+ background: isDark ? '#1F2937' : '#F9FAFB',
117
+ borderRadius: '8px',
118
+ padding: '12px',
119
+ marginBottom: '8px',
120
+ border: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`
121
+ }}>
122
+ <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '10px' }}>
123
+ <div style={{ fontSize: '14px', fontWeight: 600 }}>
124
+ {segment.airline.name} {segment.airline.flightNumber}
125
+ </div>
126
+ <div style={{ fontSize: '11px', color: isDark ? '#94A3B8' : '#64748B' }}>
127
+ {formatDuration(segment.duration)}
128
+ </div>
129
+ </div>
130
+
131
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr', gap: '12px', alignItems: 'center' }}>
132
+ <div>
133
+ <div style={{ fontSize: '18px', fontWeight: 700 }}>{formatTime(segment.departingAt)}</div>
134
+ <div style={{ fontSize: '12px', color: isDark ? '#94A3B8' : '#64748B', marginTop: '2px' }}>
135
+ {segment.origin}
136
+ </div>
137
+ <div style={{ fontSize: '10px', color: isDark ? '#64748B' : '#94A3B8', marginTop: '2px' }}>
138
+ {formatDate(segment.departingAt)}
139
+ </div>
140
+ </div>
141
+
142
+ <div style={{
143
+ width: '40px',
144
+ height: '2px',
145
+ background: 'linear-gradient(90deg, #3B9FFF 0%, #2563EB 100%)',
146
+ position: 'relative'
147
+ }}>
148
+ <div style={{
149
+ position: 'absolute',
150
+ right: '-4px',
151
+ top: '50%',
152
+ transform: 'translateY(-50%)',
153
+ width: 0,
154
+ height: 0,
155
+ borderLeft: '6px solid #2563EB',
156
+ borderTop: '3px solid transparent',
157
+ borderBottom: '3px solid transparent'
158
+ }} />
159
+ </div>
160
+
161
+ <div style={{ textAlign: 'right' }}>
162
+ <div style={{ fontSize: '18px', fontWeight: 700 }}>{formatTime(segment.arrivingAt)}</div>
163
+ <div style={{ fontSize: '12px', color: isDark ? '#94A3B8' : '#64748B', marginTop: '2px' }}>
164
+ {segment.destination}
165
+ </div>
166
+ <div style={{ fontSize: '10px', color: isDark ? '#64748B' : '#94A3B8', marginTop: '2px' }}>
167
+ {formatDate(segment.arrivingAt)}
168
+ </div>
169
+ </div>
170
+ </div>
171
+
172
+ {segment.aircraft && (
173
+ <div style={{
174
+ marginTop: '10px',
175
+ paddingTop: '10px',
176
+ borderTop: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`,
177
+ fontSize: '11px',
178
+ color: isDark ? '#94A3B8' : '#64748B'
179
+ }}>
180
+ ✈️ {segment.aircraft}
181
+ </div>
182
+ )}
183
+ </div>
184
+ ))}
185
+ </div>
186
+ ))}
187
+
188
+ {/* Baggage */}
189
+ <div className="card" style={{ marginBottom: '12px' }}>
190
+ <h3 style={{ margin: '0 0 12px 0', fontSize: '14px', fontWeight: 600, display: 'flex', alignItems: 'center', gap: '6px' }}>
191
+ <span>🧳</span>
192
+ <span>Baggage Allowance</span>
193
+ </h3>
194
+ {data.passengers.map((passenger, index) => (
195
+ <div key={passenger.id} style={{
196
+ background: isDark ? '#0F172A' : '#F8FAFC',
197
+ borderRadius: '8px',
198
+ padding: '12px',
199
+ marginBottom: index < data.passengers.length - 1 ? '8px' : '0'
200
+ }}>
201
+ <div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '6px' }}>
202
+ Passenger {index + 1} ({passenger.type})
203
+ </div>
204
+ {passenger.baggageAllowance && passenger.baggageAllowance.length > 0 ? (
205
+ <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
206
+ {passenger.baggageAllowance.map((bag, bagIndex) => (
207
+ <span key={bagIndex} className="badge badge-info" style={{ fontSize: '11px' }}>
208
+ {bag.quantity}x {bag.type.replace('_', ' ')}
209
+ </span>
210
+ ))}
211
+ </div>
212
+ ) : (
213
+ <div style={{ fontSize: '11px', color: isDark ? '#94A3B8' : '#64748B' }}>
214
+ No baggage info
215
+ </div>
216
+ )}
217
+ </div>
218
+ ))}
219
+ </div>
220
+
221
+ {/* Fare Conditions */}
222
+ {data.conditions && (
223
+ <div className="card">
224
+ <h3 style={{ margin: '0 0 12px 0', fontSize: '14px', fontWeight: 600, display: 'flex', alignItems: 'center', gap: '6px' }}>
225
+ <span>📋</span>
226
+ <span>Fare Conditions</span>
227
+ </h3>
228
+ <div style={{ display: 'grid', gap: '8px' }}>
229
+ <div className={data.conditions.refundBeforeDeparture?.allowed ? 'badge-success' : 'badge-warning'} style={{
230
+ padding: '12px',
231
+ borderRadius: '8px'
232
+ }}>
233
+ <div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '4px' }}>
234
+ {data.conditions.refundBeforeDeparture?.allowed ? '✓' : '✗'} Refund Before Departure
235
+ </div>
236
+ {data.conditions.refundBeforeDeparture?.penaltyAmount && (
237
+ <div style={{ fontSize: '10px', opacity: 0.8 }}>
238
+ Penalty: {data.conditions.refundBeforeDeparture.penaltyCurrency} {data.conditions.refundBeforeDeparture.penaltyAmount}
239
+ </div>
240
+ )}
241
+ </div>
242
+
243
+ <div className={data.conditions.changeBeforeDeparture?.allowed ? 'badge-success' : 'badge-warning'} style={{
244
+ padding: '12px',
245
+ borderRadius: '8px'
246
+ }}>
247
+ <div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '4px' }}>
248
+ {data.conditions.changeBeforeDeparture?.allowed ? '✓' : '✗'} Changes Before Departure
249
+ </div>
250
+ {data.conditions.changeBeforeDeparture?.penaltyAmount && (
251
+ <div style={{ fontSize: '10px', opacity: 0.8 }}>
252
+ Penalty: {data.conditions.changeBeforeDeparture.penaltyCurrency} {data.conditions.changeBeforeDeparture.penaltyAmount}
253
+ </div>
254
+ )}
255
+ </div>
256
+ </div>
257
+ </div>
258
+ )}
259
+ </div>
260
+ );
261
+ }
@@ -0,0 +1,378 @@
1
+ 'use client';
2
+
3
+ import { useWidgetSDK, useTheme } from 'nitrostack/widgets';
4
+
5
+ /**
6
+ * Flight Search Results Widget
7
+ *
8
+ * Modern, compact display of flight search results with Nitrocloud branding.
9
+ */
10
+
11
+ interface FlightSegment {
12
+ origin: string;
13
+ destination: string;
14
+ departureTime: string;
15
+ arrivalTime: string;
16
+ duration: string;
17
+ stops: number;
18
+ airline: string;
19
+ flightNumber: string;
20
+ }
21
+
22
+ interface FlightOffer {
23
+ id: string;
24
+ totalAmount: string;
25
+ totalCurrency: string;
26
+ outbound: FlightSegment;
27
+ return?: FlightSegment;
28
+ fareType: string;
29
+ refundable: boolean;
30
+ changeable: boolean;
31
+ }
32
+
33
+ interface FlightSearchData {
34
+ searchParams: {
35
+ origin: string;
36
+ destination: string;
37
+ departureDate: string;
38
+ returnDate?: string;
39
+ passengers: {
40
+ adults: number;
41
+ children: number;
42
+ infants: number;
43
+ };
44
+ cabinClass: string;
45
+ };
46
+ totalOffers: number;
47
+ offers: FlightOffer[];
48
+ }
49
+
50
+ export default function FlightSearchResults() {
51
+ const { getToolOutput, callTool } = useWidgetSDK();
52
+ const theme = useTheme();
53
+ const data = getToolOutput<FlightSearchData>();
54
+ const isDark = theme === 'dark';
55
+
56
+ const handleFlightClick = async (offerId: string) => {
57
+ try {
58
+ await callTool('get_flight_details', { offerId });
59
+ } catch (error) {
60
+ console.error('Failed to get flight details:', error);
61
+ }
62
+ };
63
+
64
+ const formatDuration = (duration: string) => {
65
+ const match = duration.match(/PT(\d+H)?(\d+M)?/);
66
+ if (!match) return duration;
67
+ const hours = match[1] ? parseInt(match[1]) : 0;
68
+ const minutes = match[2] ? parseInt(match[2]) : 0;
69
+ return `${hours}h ${minutes}m`;
70
+ };
71
+
72
+ const formatTime = (isoString: string) => {
73
+ return new Date(isoString).toLocaleTimeString('en-US', {
74
+ hour: '2-digit',
75
+ minute: '2-digit',
76
+ hour12: false
77
+ });
78
+ };
79
+
80
+ const formatDate = (dateString: string) => {
81
+ return new Date(dateString).toLocaleDateString('en-US', {
82
+ month: 'short',
83
+ day: 'numeric'
84
+ });
85
+ };
86
+
87
+ const getAirlineInitials = (name: string) => {
88
+ return name.split(' ').map(w => w[0]).join('').substring(0, 2).toUpperCase();
89
+ };
90
+
91
+ if (!data?.searchParams) {
92
+ return (
93
+ <div style={{
94
+ padding: '24px',
95
+ textAlign: 'center',
96
+ color: isDark ? '#F8FAFC' : '#020617'
97
+ }}>
98
+ <div style={{ fontSize: '48px', marginBottom: '12px' }}>⚠️</div>
99
+ <div style={{ fontSize: '16px', fontWeight: 600, marginBottom: '8px' }}>
100
+ Invalid Data
101
+ </div>
102
+ <div style={{ fontSize: '14px', color: isDark ? '#94A3B8' : '#64748B' }}>
103
+ Flight search data is missing
104
+ </div>
105
+ </div>
106
+ );
107
+ }
108
+
109
+ const totalPassengers = (data.searchParams.passengers?.adults || 0) +
110
+ (data.searchParams.passengers?.children || 0) +
111
+ (data.searchParams.passengers?.infants || 0);
112
+
113
+ const FlightSegment = ({ segment, label }: { segment: FlightSegment; label: string }) => (
114
+ <div style={{
115
+ background: isDark ? '#0F172A' : '#F8FAFC',
116
+ borderRadius: '8px',
117
+ padding: '12px',
118
+ marginBottom: '8px'
119
+ }}>
120
+ <div style={{
121
+ fontSize: '12px',
122
+ fontWeight: 600,
123
+ color: '#3B9FFF',
124
+ marginBottom: '8px',
125
+ display: 'flex',
126
+ alignItems: 'center',
127
+ gap: '6px'
128
+ }}>
129
+ <span>{label === 'Outbound' ? '✈️' : '🔄'}</span>
130
+ <span>{label}</span>
131
+ </div>
132
+
133
+ <div style={{
134
+ display: 'grid',
135
+ gridTemplateColumns: '1fr auto 1fr',
136
+ gap: '12px',
137
+ alignItems: 'center'
138
+ }}>
139
+ <div style={{ textAlign: 'left' }}>
140
+ <div style={{ fontSize: '18px', fontWeight: 700, color: isDark ? '#F8FAFC' : '#020617' }}>
141
+ {formatTime(segment.departureTime)}
142
+ </div>
143
+ <div style={{ fontSize: '12px', color: isDark ? '#94A3B8' : '#64748B', marginTop: '2px' }}>
144
+ {segment.origin}
145
+ </div>
146
+ </div>
147
+
148
+ <div style={{
149
+ textAlign: 'center',
150
+ padding: '8px',
151
+ background: isDark ? '#1E293B' : 'white',
152
+ borderRadius: '6px',
153
+ minWidth: '100px'
154
+ }}>
155
+ <div style={{ fontSize: '12px', fontWeight: 600, color: isDark ? '#F8FAFC' : '#020617' }}>
156
+ {formatDuration(segment.duration)}
157
+ </div>
158
+ <div style={{
159
+ height: '2px',
160
+ background: 'linear-gradient(90deg, #3B9FFF 0%, #2563EB 100%)',
161
+ margin: '6px 0',
162
+ position: 'relative'
163
+ }}>
164
+ {segment.stops > 0 && (
165
+ <div style={{
166
+ width: '6px',
167
+ height: '6px',
168
+ background: '#3B9FFF',
169
+ borderRadius: '50%',
170
+ position: 'absolute',
171
+ top: '50%',
172
+ left: '50%',
173
+ transform: 'translate(-50%, -50%)'
174
+ }} />
175
+ )}
176
+ </div>
177
+ <div style={{ fontSize: '10px', color: isDark ? '#94A3B8' : '#64748B' }}>
178
+ {segment.stops === 0 ? 'Direct' : `${segment.stops} stop${segment.stops > 1 ? 's' : ''}`}
179
+ </div>
180
+ </div>
181
+
182
+ <div style={{ textAlign: 'right' }}>
183
+ <div style={{ fontSize: '18px', fontWeight: 700, color: isDark ? '#F8FAFC' : '#020617' }}>
184
+ {formatTime(segment.arrivalTime)}
185
+ </div>
186
+ <div style={{ fontSize: '12px', color: isDark ? '#94A3B8' : '#64748B', marginTop: '2px' }}>
187
+ {segment.destination}
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ );
193
+
194
+ return (
195
+ <div className={isDark ? 'dark' : ''} style={{
196
+ padding: '16px',
197
+ background: isDark ? '#020617' : '#FFFFFF',
198
+ color: isDark ? '#F8FAFC' : '#020617'
199
+ }}>
200
+ {/* Header */}
201
+ <div style={{
202
+ background: isDark ? '#0F172A' : '#F8FAFC',
203
+ borderRadius: '12px',
204
+ padding: '16px',
205
+ marginBottom: '16px',
206
+ border: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`
207
+ }}>
208
+ <div style={{
209
+ display: 'flex',
210
+ alignItems: 'center',
211
+ justifyContent: 'space-between',
212
+ flexWrap: 'wrap',
213
+ gap: '12px',
214
+ marginBottom: '12px'
215
+ }}>
216
+ <div style={{
217
+ fontSize: '18px',
218
+ fontWeight: 700,
219
+ display: 'flex',
220
+ alignItems: 'center',
221
+ gap: '8px'
222
+ }}>
223
+ <span>{data.searchParams.origin}</span>
224
+ <span style={{ color: '#3B9FFF' }}>✈️</span>
225
+ <span>{data.searchParams.destination}</span>
226
+ </div>
227
+
228
+ <div className="badge badge-info">
229
+ {data.totalOffers} flights
230
+ </div>
231
+ </div>
232
+
233
+ <div style={{
234
+ display: 'flex',
235
+ gap: '16px',
236
+ flexWrap: 'wrap',
237
+ fontSize: '12px',
238
+ color: isDark ? '#94A3B8' : '#64748B'
239
+ }}>
240
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
241
+ <span>📅</span>
242
+ <span>
243
+ {formatDate(data.searchParams.departureDate)}
244
+ {data.searchParams.returnDate && ` - ${formatDate(data.searchParams.returnDate)}`}
245
+ </span>
246
+ </div>
247
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
248
+ <span>👥</span>
249
+ <span>{totalPassengers} pax</span>
250
+ </div>
251
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
252
+ <span>💺</span>
253
+ <span>{(data.searchParams.cabinClass || 'economy').replace('_', ' ')}</span>
254
+ </div>
255
+ </div>
256
+ </div>
257
+
258
+ {/* Flight Offers */}
259
+ {data.offers.length > 0 ? (
260
+ <div style={{
261
+ display: 'flex',
262
+ gap: '12px',
263
+ overflowX: 'auto',
264
+ paddingBottom: '12px',
265
+ scrollbarWidth: 'thin',
266
+ scrollbarColor: isDark ? '#334155 #0F172A' : '#CBD5E1 #F1F5F9'
267
+ }}>
268
+ {data.offers.map((offer) => (
269
+ <div key={offer.id} style={{
270
+ minWidth: '320px',
271
+ maxWidth: '320px',
272
+ background: isDark ? '#1a1a1a' : '#ffffff',
273
+ border: `1px solid ${isDark ? '#333' : '#e5e7eb'}`,
274
+ borderRadius: '12px',
275
+ padding: '16px',
276
+ boxShadow: isDark ? '0 2px 8px rgba(0,0,0,0.3)' : '0 2px 8px rgba(0,0,0,0.1)',
277
+ transition: 'all 0.2s ease',
278
+ cursor: 'pointer'
279
+ }}
280
+ onClick={() => handleFlightClick(offer.id)}
281
+ onMouseEnter={(e) => {
282
+ e.currentTarget.style.transform = 'translateY(-2px)';
283
+ e.currentTarget.style.boxShadow = isDark
284
+ ? '0 4px 12px rgba(59, 159, 255, 0.2)'
285
+ : '0 4px 12px rgba(59, 159, 255, 0.15)';
286
+ }}
287
+ onMouseLeave={(e) => {
288
+ e.currentTarget.style.transform = 'translateY(0)';
289
+ e.currentTarget.style.boxShadow = isDark
290
+ ? '0 2px 8px rgba(0,0,0,0.3)'
291
+ : '0 2px 8px rgba(0,0,0,0.1)';
292
+ }}>
293
+ {/* Offer Header */}
294
+ <div style={{
295
+ display: 'flex',
296
+ justifyContent: 'space-between',
297
+ alignItems: 'center',
298
+ marginBottom: '12px',
299
+ paddingBottom: '12px',
300
+ borderBottom: `1px solid ${isDark ? '#334155' : '#E2E8F0'}`
301
+ }}>
302
+ <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
303
+ <div style={{
304
+ width: '40px',
305
+ height: '40px',
306
+ background: 'linear-gradient(135deg, #3B9FFF 0%, #2563EB 100%)',
307
+ borderRadius: '8px',
308
+ display: 'flex',
309
+ alignItems: 'center',
310
+ justifyContent: 'center',
311
+ color: 'white',
312
+ fontWeight: 700,
313
+ fontSize: '14px'
314
+ }}>
315
+ {getAirlineInitials(offer.outbound.airline)}
316
+ </div>
317
+ <div>
318
+ <div style={{ fontSize: '14px', fontWeight: 600 }}>
319
+ {offer.outbound.airline}
320
+ </div>
321
+ <div style={{ fontSize: '11px', color: isDark ? '#94A3B8' : '#64748B' }}>
322
+ {offer.outbound.flightNumber}
323
+ </div>
324
+ </div>
325
+ </div>
326
+
327
+ <div style={{ textAlign: 'right' }}>
328
+ <div style={{
329
+ color: 'var(--primary)',
330
+ fontSize: '24px',
331
+ fontWeight: 700
332
+ }}>
333
+ {offer.totalCurrency} {parseFloat(offer.totalAmount).toFixed(0)}
334
+ </div>
335
+ <div style={{ fontSize: '10px', color: isDark ? '#94A3B8' : '#64748B' }}>
336
+ Total
337
+ </div>
338
+ </div>
339
+ </div>
340
+
341
+ {/* Flight Segments */}
342
+ <FlightSegment segment={offer.outbound} label="Outbound" />
343
+ {offer.return && <FlightSegment segment={offer.return} label="Return" />}
344
+
345
+ {/* Badges */}
346
+ <div style={{
347
+ display: 'flex',
348
+ gap: '6px',
349
+ flexWrap: 'wrap',
350
+ marginTop: '12px'
351
+ }}>
352
+ <span className={offer.refundable ? 'badge badge-success' : 'badge badge-warning'}>
353
+ {offer.refundable ? '✓ Refundable' : '✗ Non-refundable'}
354
+ </span>
355
+ {offer.changeable && (
356
+ <span className="badge badge-success">✓ Changeable</span>
357
+ )}
358
+ <span className="badge badge-info">{offer.fareType}</span>
359
+ </div>
360
+ </div>
361
+ ))}
362
+ </div>
363
+ ) : (
364
+ <div style={{
365
+ background: isDark ? '#0F172A' : '#F8FAFC',
366
+ borderRadius: '12px',
367
+ padding: '32px',
368
+ textAlign: 'center'
369
+ }}>
370
+ <div style={{ fontSize: '48px', marginBottom: '12px' }}>✈️</div>
371
+ <div style={{ fontSize: '14px', color: isDark ? '#94A3B8' : '#64748B' }}>
372
+ No flights found. Try adjusting your search.
373
+ </div>
374
+ </div>
375
+ )}
376
+ </div>
377
+ );
378
+ }