@vendure-platform/create 0.1.0

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 (531) hide show
  1. package/README.md +70 -0
  2. package/dist/assets/demo-data/branding/logo.svg +4 -0
  3. package/dist/assets/demo-data/images/absorbent-pad.webp +0 -0
  4. package/dist/assets/demo-data/images/adjustable-wrench.webp +0 -0
  5. package/dist/assets/demo-data/images/anchor-bolt.webp +0 -0
  6. package/dist/assets/demo-data/images/angle-grinder.webp +0 -0
  7. package/dist/assets/demo-data/images/barrier-tape.webp +0 -0
  8. package/dist/assets/demo-data/images/bench-grinder.webp +0 -0
  9. package/dist/assets/demo-data/images/bracket.webp +0 -0
  10. package/dist/assets/demo-data/images/broom.webp +0 -0
  11. package/dist/assets/demo-data/images/bucket.webp +0 -0
  12. package/dist/assets/demo-data/images/cable-gland.webp +0 -0
  13. package/dist/assets/demo-data/images/cable-tie.webp +0 -0
  14. package/dist/assets/demo-data/images/cable-tray.webp +0 -0
  15. package/dist/assets/demo-data/images/cable.webp +0 -0
  16. package/dist/assets/demo-data/images/chimney-base-kit.webp +0 -0
  17. package/dist/assets/demo-data/images/chimney-cowl-bird-guard.webp +0 -0
  18. package/dist/assets/demo-data/images/chimney-cowl-open.webp +0 -0
  19. package/dist/assets/demo-data/images/chimney-storm-collar.webp +0 -0
  20. package/dist/assets/demo-data/images/chimney-wall-bracket.webp +0 -0
  21. package/dist/assets/demo-data/images/circular-saw.webp +0 -0
  22. package/dist/assets/demo-data/images/cleaning-spray.webp +0 -0
  23. package/dist/assets/demo-data/images/conduit.webp +0 -0
  24. package/dist/assets/demo-data/images/cordless-drill.webp +0 -0
  25. package/dist/assets/demo-data/images/drill-bits.webp +0 -0
  26. package/dist/assets/demo-data/images/dust-mask.webp +0 -0
  27. package/dist/assets/demo-data/images/ear-defenders.webp +0 -0
  28. package/dist/assets/demo-data/images/ear-plugs.webp +0 -0
  29. package/dist/assets/demo-data/images/extension-lead.webp +0 -0
  30. package/dist/assets/demo-data/images/eye-wash-station.webp +0 -0
  31. package/dist/assets/demo-data/images/face-shield.webp +0 -0
  32. package/dist/assets/demo-data/images/file-set.webp +0 -0
  33. package/dist/assets/demo-data/images/first-aid-kit.webp +0 -0
  34. package/dist/assets/demo-data/images/floor-sign.webp +0 -0
  35. package/dist/assets/demo-data/images/flue-bend-45.webp +0 -0
  36. package/dist/assets/demo-data/images/flue-bend-90.webp +0 -0
  37. package/dist/assets/demo-data/images/flue-section-1000mm.webp +0 -0
  38. package/dist/assets/demo-data/images/flue-section-250mm.webp +0 -0
  39. package/dist/assets/demo-data/images/flue-section-500mm.webp +0 -0
  40. package/dist/assets/demo-data/images/fuse.webp +0 -0
  41. package/dist/assets/demo-data/images/glove-dispenser.webp +0 -0
  42. package/dist/assets/demo-data/images/grinding-disc.webp +0 -0
  43. package/dist/assets/demo-data/images/hacksaw.webp +0 -0
  44. package/dist/assets/demo-data/images/hammer.webp +0 -0
  45. package/dist/assets/demo-data/images/hand-sanitiser.webp +0 -0
  46. package/dist/assets/demo-data/images/head-torch.webp +0 -0
  47. package/dist/assets/demo-data/images/heat-gun.webp +0 -0
  48. package/dist/assets/demo-data/images/hex-bolt.webp +0 -0
  49. package/dist/assets/demo-data/images/hex-key-set.webp +0 -0
  50. package/dist/assets/demo-data/images/hex-nut.webp +0 -0
  51. package/dist/assets/demo-data/images/hi-vis-jacket.webp +0 -0
  52. package/dist/assets/demo-data/images/hi-vis-vest.webp +0 -0
  53. package/dist/assets/demo-data/images/hose-clamp.webp +0 -0
  54. package/dist/assets/demo-data/images/impact-driver.webp +0 -0
  55. package/dist/assets/demo-data/images/jigsaw.webp +0 -0
  56. package/dist/assets/demo-data/images/junction-box.webp +0 -0
  57. package/dist/assets/demo-data/images/knee-pads.webp +0 -0
  58. package/dist/assets/demo-data/images/machine-screw.webp +0 -0
  59. package/dist/assets/demo-data/images/made-to-order-configurable-chimney.webp +0 -0
  60. package/dist/assets/demo-data/images/mallet.webp +0 -0
  61. package/dist/assets/demo-data/images/mcb.webp +0 -0
  62. package/dist/assets/demo-data/images/mop.webp +0 -0
  63. package/dist/assets/demo-data/images/paper-towels.webp +0 -0
  64. package/dist/assets/demo-data/images/pipe-wrench.webp +0 -0
  65. package/dist/assets/demo-data/images/pliers-set.webp +0 -0
  66. package/dist/assets/demo-data/images/pry-bar.webp +0 -0
  67. package/dist/assets/demo-data/images/rcd.webp +0 -0
  68. package/dist/assets/demo-data/images/reciprocating-saw.webp +0 -0
  69. package/dist/assets/demo-data/images/rivet.webp +0 -0
  70. package/dist/assets/demo-data/images/roof-flashing-high-pitch.webp +0 -0
  71. package/dist/assets/demo-data/images/roof-flashing-low-pitch.webp +0 -0
  72. package/dist/assets/demo-data/images/safety-boots.webp +0 -0
  73. package/dist/assets/demo-data/images/safety-glasses.webp +0 -0
  74. package/dist/assets/demo-data/images/safety-harness.webp +0 -0
  75. package/dist/assets/demo-data/images/safety-helmet.webp +0 -0
  76. package/dist/assets/demo-data/images/safety-sign.webp +0 -0
  77. package/dist/assets/demo-data/images/sanding-disc.webp +0 -0
  78. package/dist/assets/demo-data/images/saw-blade.webp +0 -0
  79. package/dist/assets/demo-data/images/screwdriver-set.webp +0 -0
  80. package/dist/assets/demo-data/images/screwdriver.webp +0 -0
  81. package/dist/assets/demo-data/images/self-tapping-screw.webp +0 -0
  82. package/dist/assets/demo-data/images/socket-set.webp +0 -0
  83. package/dist/assets/demo-data/images/socket.webp +0 -0
  84. package/dist/assets/demo-data/images/spanner-set.webp +0 -0
  85. package/dist/assets/demo-data/images/spanner.webp +0 -0
  86. package/dist/assets/demo-data/images/spill-kit.webp +0 -0
  87. package/dist/assets/demo-data/images/spirit-level.webp +0 -0
  88. package/dist/assets/demo-data/images/switch.webp +0 -0
  89. package/dist/assets/demo-data/images/tape-measure.webp +0 -0
  90. package/dist/assets/demo-data/images/terminal-block.webp +0 -0
  91. package/dist/assets/demo-data/images/threaded-rod.webp +0 -0
  92. package/dist/assets/demo-data/images/torque-wrench.webp +0 -0
  93. package/dist/assets/demo-data/images/utility-knife.webp +0 -0
  94. package/dist/assets/demo-data/images/wall-pass-sleeve.webp +0 -0
  95. package/dist/assets/demo-data/images/wall-plug.webp +0 -0
  96. package/dist/assets/demo-data/images/washer.webp +0 -0
  97. package/dist/assets/demo-data/images/waste-bin.webp +0 -0
  98. package/dist/assets/demo-data/images/wire-connector.webp +0 -0
  99. package/dist/assets/demo-data/images/wire-cutters.webp +0 -0
  100. package/dist/assets/demo-data/images/wood-screw.webp +0 -0
  101. package/dist/assets/demo-data/images/work-gloves.webp +0 -0
  102. package/dist/assets/demo-data/images/work-light.webp +0 -0
  103. package/dist/assets/demo-data/initial-data.json +667 -0
  104. package/dist/assets/demo-data/products.csv +779 -0
  105. package/dist/assets/storefront/.env.example +14 -0
  106. package/dist/assets/storefront/.env.local +1 -0
  107. package/dist/assets/storefront/components.json +22 -0
  108. package/dist/assets/storefront/eslint.config.mjs +23 -0
  109. package/dist/assets/storefront/graphql.config.yml +2 -0
  110. package/dist/assets/storefront/messages/de.json +1196 -0
  111. package/dist/assets/storefront/messages/en.json +1196 -0
  112. package/dist/assets/storefront/next-env.d.ts +6 -0
  113. package/dist/assets/storefront/next.config.ts +73 -0
  114. package/dist/assets/storefront/package.json +77 -0
  115. package/dist/assets/storefront/postcss.config.mjs +5 -0
  116. package/dist/assets/storefront/public/next.svg +22 -0
  117. package/dist/assets/storefront/public/scenarios/b2b-wholesale/hero/hero-bg.webp +0 -0
  118. package/dist/assets/storefront/public/scenarios/b2b-wholesale/logo.svg +4 -0
  119. package/dist/assets/storefront/public/scenarios/default/logo.svg +4 -0
  120. package/dist/assets/storefront/public/scenarios/kaminpro/hero/hero-bg.webp +0 -0
  121. package/dist/assets/storefront/public/scenarios/kaminpro/logo.svg +7 -0
  122. package/dist/assets/storefront/public/vendure.svg +4 -0
  123. package/dist/assets/storefront/src/app/[locale]/(catalog)/catalog/page.tsx +65 -0
  124. package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/loading.tsx +24 -0
  125. package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/page.tsx +194 -0
  126. package/dist/assets/storefront/src/app/[locale]/(catalog)/layout.tsx +9 -0
  127. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/actions.ts +139 -0
  128. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/loading.tsx +46 -0
  129. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/page.tsx +330 -0
  130. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/actions.ts +11 -0
  131. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/loading.tsx +20 -0
  132. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/page.tsx +97 -0
  133. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-analytics-tracker.tsx +46 -0
  134. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-results.tsx +53 -0
  135. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-term.tsx +26 -0
  136. package/dist/assets/storefront/src/app/[locale]/(scenario)/[...path]/page.tsx +31 -0
  137. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/accept-invitation-client.tsx +56 -0
  138. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/actions.ts +32 -0
  139. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/page.tsx +85 -0
  140. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/sign-out-and-retry.tsx +26 -0
  141. package/dist/assets/storefront/src/app/[locale]/account/addresses/actions.ts +125 -0
  142. package/dist/assets/storefront/src/app/[locale]/account/addresses/address-form.tsx +180 -0
  143. package/dist/assets/storefront/src/app/[locale]/account/addresses/addresses-client.tsx +292 -0
  144. package/dist/assets/storefront/src/app/[locale]/account/addresses/loading.tsx +48 -0
  145. package/dist/assets/storefront/src/app/[locale]/account/addresses/page.tsx +54 -0
  146. package/dist/assets/storefront/src/app/[locale]/account/approvals/actions.ts +28 -0
  147. package/dist/assets/storefront/src/app/[locale]/account/approvals/page.tsx +340 -0
  148. package/dist/assets/storefront/src/app/[locale]/account/company/actions.ts +33 -0
  149. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/actions.ts +83 -0
  150. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/address-form.tsx +267 -0
  151. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/addresses-client.tsx +253 -0
  152. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/page.tsx +75 -0
  153. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/actions.ts +70 -0
  154. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-unit-form.tsx +153 -0
  155. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-units-client.tsx +210 -0
  156. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/page.tsx +66 -0
  157. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/types.ts +8 -0
  158. package/dist/assets/storefront/src/app/[locale]/account/company/edit-company-form.tsx +57 -0
  159. package/dist/assets/storefront/src/app/[locale]/account/company/page.tsx +89 -0
  160. package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/page.tsx +65 -0
  161. package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/payment-methods-client.tsx +55 -0
  162. package/dist/assets/storefront/src/app/[locale]/account/company/roles/page.tsx +66 -0
  163. package/dist/assets/storefront/src/app/[locale]/account/company/roles/roles-client.tsx +93 -0
  164. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/actions.ts +51 -0
  165. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/page.tsx +57 -0
  166. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/tax-ids-client.tsx +326 -0
  167. package/dist/assets/storefront/src/app/[locale]/account/company/users/actions.ts +74 -0
  168. package/dist/assets/storefront/src/app/[locale]/account/company/users/edit-user-form.tsx +157 -0
  169. package/dist/assets/storefront/src/app/[locale]/account/company/users/invite-user-form.tsx +133 -0
  170. package/dist/assets/storefront/src/app/[locale]/account/company/users/page.tsx +104 -0
  171. package/dist/assets/storefront/src/app/[locale]/account/company/users/users-client.tsx +266 -0
  172. package/dist/assets/storefront/src/app/[locale]/account/error.tsx +24 -0
  173. package/dist/assets/storefront/src/app/[locale]/account/layout.tsx +50 -0
  174. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/page.tsx +463 -0
  175. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-actions.ts +160 -0
  176. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-dialog.tsx +386 -0
  177. package/dist/assets/storefront/src/app/[locale]/account/orders/loading.tsx +45 -0
  178. package/dist/assets/storefront/src/app/[locale]/account/orders/page.tsx +309 -0
  179. package/dist/assets/storefront/src/app/[locale]/account/profile/actions.ts +106 -0
  180. package/dist/assets/storefront/src/app/[locale]/account/profile/change-password-form.tsx +82 -0
  181. package/dist/assets/storefront/src/app/[locale]/account/profile/edit-email-form.tsx +88 -0
  182. package/dist/assets/storefront/src/app/[locale]/account/profile/edit-profile-form.tsx +80 -0
  183. package/dist/assets/storefront/src/app/[locale]/account/profile/loading.tsx +54 -0
  184. package/dist/assets/storefront/src/app/[locale]/account/profile/page.tsx +44 -0
  185. package/dist/assets/storefront/src/app/[locale]/account/quick-order/actions.ts +103 -0
  186. package/dist/assets/storefront/src/app/[locale]/account/quick-order/file-upload-tab.tsx +148 -0
  187. package/dist/assets/storefront/src/app/[locale]/account/quick-order/manual-entry-tab.tsx +112 -0
  188. package/dist/assets/storefront/src/app/[locale]/account/quick-order/page.tsx +26 -0
  189. package/dist/assets/storefront/src/app/[locale]/account/quick-order/preview-table.tsx +170 -0
  190. package/dist/assets/storefront/src/app/[locale]/account/quick-order/quick-order-form.tsx +243 -0
  191. package/dist/assets/storefront/src/app/[locale]/account/quick-order/save-to-list-dialog.tsx +153 -0
  192. package/dist/assets/storefront/src/app/[locale]/account/quick-order/sku-autocomplete.tsx +118 -0
  193. package/dist/assets/storefront/src/app/[locale]/account/quick-order/text-input-tab.tsx +40 -0
  194. package/dist/assets/storefront/src/app/[locale]/account/quotes/[id]/page.tsx +260 -0
  195. package/dist/assets/storefront/src/app/[locale]/account/quotes/actions.ts +253 -0
  196. package/dist/assets/storefront/src/app/[locale]/account/quotes/new/new-quote-form.tsx +28 -0
  197. package/dist/assets/storefront/src/app/[locale]/account/quotes/new/page.tsx +51 -0
  198. package/dist/assets/storefront/src/app/[locale]/account/quotes/page.tsx +248 -0
  199. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/loading.tsx +44 -0
  200. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/page.tsx +51 -0
  201. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/shopping-list-detail-client.tsx +562 -0
  202. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/actions.ts +235 -0
  203. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/create-shopping-list-form.tsx +86 -0
  204. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/loading.tsx +48 -0
  205. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/page.tsx +176 -0
  206. package/dist/assets/storefront/src/app/[locale]/account/store-credits/actions.ts +31 -0
  207. package/dist/assets/storefront/src/app/[locale]/account/store-credits/claim-form.tsx +41 -0
  208. package/dist/assets/storefront/src/app/[locale]/account/store-credits/page.tsx +190 -0
  209. package/dist/assets/storefront/src/app/[locale]/account/store-credits/reveal-code.tsx +38 -0
  210. package/dist/assets/storefront/src/app/[locale]/account/verify-email/page.tsx +117 -0
  211. package/dist/assets/storefront/src/app/[locale]/cart/actions.ts +128 -0
  212. package/dist/assets/storefront/src/app/[locale]/cart/cart-items.tsx +228 -0
  213. package/dist/assets/storefront/src/app/[locale]/cart/cart-line-actions.tsx +55 -0
  214. package/dist/assets/storefront/src/app/[locale]/cart/cart-quantity-control.tsx +158 -0
  215. package/dist/assets/storefront/src/app/[locale]/cart/cart-remove-button.tsx +36 -0
  216. package/dist/assets/storefront/src/app/[locale]/cart/cart.tsx +151 -0
  217. package/dist/assets/storefront/src/app/[locale]/cart/code-input.tsx +49 -0
  218. package/dist/assets/storefront/src/app/[locale]/cart/loading.tsx +10 -0
  219. package/dist/assets/storefront/src/app/[locale]/cart/order-summary.tsx +178 -0
  220. package/dist/assets/storefront/src/app/[locale]/cart/page.tsx +33 -0
  221. package/dist/assets/storefront/src/app/[locale]/cart/promotion-code.tsx +142 -0
  222. package/dist/assets/storefront/src/app/[locale]/cart/reapproval-confirm.tsx +76 -0
  223. package/dist/assets/storefront/src/app/[locale]/cart/request-quote-button.tsx +80 -0
  224. package/dist/assets/storefront/src/app/[locale]/checkout/actions.ts +192 -0
  225. package/dist/assets/storefront/src/app/[locale]/checkout/cancel-quote-checkout-button.tsx +30 -0
  226. package/dist/assets/storefront/src/app/[locale]/checkout/checkout-flow.tsx +214 -0
  227. package/dist/assets/storefront/src/app/[locale]/checkout/checkout-provider.tsx +181 -0
  228. package/dist/assets/storefront/src/app/[locale]/checkout/loading.tsx +90 -0
  229. package/dist/assets/storefront/src/app/[locale]/checkout/order-summary.tsx +160 -0
  230. package/dist/assets/storefront/src/app/[locale]/checkout/page.tsx +154 -0
  231. package/dist/assets/storefront/src/app/[locale]/checkout/steps/delivery-step.tsx +107 -0
  232. package/dist/assets/storefront/src/app/[locale]/checkout/steps/payment-step.tsx +110 -0
  233. package/dist/assets/storefront/src/app/[locale]/checkout/steps/review-step.tsx +180 -0
  234. package/dist/assets/storefront/src/app/[locale]/checkout/steps/shipping-address-step.tsx +518 -0
  235. package/dist/assets/storefront/src/app/[locale]/checkout/types.ts +7 -0
  236. package/dist/assets/storefront/src/app/[locale]/error.tsx +28 -0
  237. package/dist/assets/storefront/src/app/[locale]/forgot-password/actions.ts +28 -0
  238. package/dist/assets/storefront/src/app/[locale]/forgot-password/forgot-password-form.tsx +128 -0
  239. package/dist/assets/storefront/src/app/[locale]/forgot-password/page.tsx +24 -0
  240. package/dist/assets/storefront/src/app/[locale]/globals.css +72 -0
  241. package/dist/assets/storefront/src/app/[locale]/layout.tsx +202 -0
  242. package/dist/assets/storefront/src/app/[locale]/not-found.tsx +26 -0
  243. package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx +296 -0
  244. package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/page.tsx +18 -0
  245. package/dist/assets/storefront/src/app/[locale]/page.tsx +125 -0
  246. package/dist/assets/storefront/src/app/[locale]/register/actions.ts +50 -0
  247. package/dist/assets/storefront/src/app/[locale]/register/page.tsx +82 -0
  248. package/dist/assets/storefront/src/app/[locale]/register/registration-form.tsx +237 -0
  249. package/dist/assets/storefront/src/app/[locale]/register-company/actions.ts +40 -0
  250. package/dist/assets/storefront/src/app/[locale]/register-company/page.tsx +43 -0
  251. package/dist/assets/storefront/src/app/[locale]/register-company/registration-form.tsx +48 -0
  252. package/dist/assets/storefront/src/app/[locale]/reset-password/actions.ts +40 -0
  253. package/dist/assets/storefront/src/app/[locale]/reset-password/page.tsx +32 -0
  254. package/dist/assets/storefront/src/app/[locale]/reset-password/reset-password-form.tsx +96 -0
  255. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/add-to-cart-button.tsx +31 -0
  256. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/loading.tsx +29 -0
  257. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/page.tsx +157 -0
  258. package/dist/assets/storefront/src/app/[locale]/sign-in/actions.ts +76 -0
  259. package/dist/assets/storefront/src/app/[locale]/sign-in/login-form.tsx +177 -0
  260. package/dist/assets/storefront/src/app/[locale]/sign-in/page.tsx +81 -0
  261. package/dist/assets/storefront/src/app/[locale]/verify/actions.ts +41 -0
  262. package/dist/assets/storefront/src/app/[locale]/verify/page.tsx +40 -0
  263. package/dist/assets/storefront/src/app/[locale]/verify/verify-content.tsx +127 -0
  264. package/dist/assets/storefront/src/app/[locale]/verify-pending/page.tsx +67 -0
  265. package/dist/assets/storefront/src/app/api/clear-company-context/route.ts +26 -0
  266. package/dist/assets/storefront/src/app/api/draft/disable/route.ts +12 -0
  267. package/dist/assets/storefront/src/app/api/draft/route.ts +17 -0
  268. package/dist/assets/storefront/src/app/api/revalidate/route.ts +110 -0
  269. package/dist/assets/storefront/src/app/api/search-analytics/route.ts +16 -0
  270. package/dist/assets/storefront/src/app/favicon.ico +0 -0
  271. package/dist/assets/storefront/src/components/commerce/account-nav-items.ts +56 -0
  272. package/dist/assets/storefront/src/components/commerce/account-sidebar.tsx +112 -0
  273. package/dist/assets/storefront/src/components/commerce/active-filters.tsx +107 -0
  274. package/dist/assets/storefront/src/components/commerce/approval-decision-form.tsx +70 -0
  275. package/dist/assets/storefront/src/components/commerce/approval-status-badge.tsx +37 -0
  276. package/dist/assets/storefront/src/components/commerce/business-unit-switcher.tsx +56 -0
  277. package/dist/assets/storefront/src/components/commerce/catalog-search-bar.tsx +83 -0
  278. package/dist/assets/storefront/src/components/commerce/category-product-section.tsx +69 -0
  279. package/dist/assets/storefront/src/components/commerce/category-sidebar.tsx +133 -0
  280. package/dist/assets/storefront/src/components/commerce/collection-hero-banner.tsx +36 -0
  281. package/dist/assets/storefront/src/components/commerce/company-state-badge.tsx +32 -0
  282. package/dist/assets/storefront/src/components/commerce/company-switcher.tsx +58 -0
  283. package/dist/assets/storefront/src/components/commerce/configurator-form.tsx +606 -0
  284. package/dist/assets/storefront/src/components/commerce/facet-filters.tsx +286 -0
  285. package/dist/assets/storefront/src/components/commerce/gift-card-pdp.tsx +43 -0
  286. package/dist/assets/storefront/src/components/commerce/gift-card-product-info.tsx +137 -0
  287. package/dist/assets/storefront/src/components/commerce/highlighted-text.tsx +30 -0
  288. package/dist/assets/storefront/src/components/commerce/instant-search-dropdown.tsx +377 -0
  289. package/dist/assets/storefront/src/components/commerce/order-status-badge.tsx +41 -0
  290. package/dist/assets/storefront/src/components/commerce/packaging-unit-selector.tsx +146 -0
  291. package/dist/assets/storefront/src/components/commerce/permission-gate.tsx +20 -0
  292. package/dist/assets/storefront/src/components/commerce/price-range-filter.tsx +111 -0
  293. package/dist/assets/storefront/src/components/commerce/price-tiers.tsx +98 -0
  294. package/dist/assets/storefront/src/components/commerce/price.tsx +29 -0
  295. package/dist/assets/storefront/src/components/commerce/product-card-list.tsx +121 -0
  296. package/dist/assets/storefront/src/components/commerce/product-card.tsx +89 -0
  297. package/dist/assets/storefront/src/components/commerce/product-carousel.tsx +42 -0
  298. package/dist/assets/storefront/src/components/commerce/product-grid.tsx +54 -0
  299. package/dist/assets/storefront/src/components/commerce/product-image-carousel.tsx +127 -0
  300. package/dist/assets/storefront/src/components/commerce/product-info.tsx +525 -0
  301. package/dist/assets/storefront/src/components/commerce/product-list-expansion.tsx +393 -0
  302. package/dist/assets/storefront/src/components/commerce/product-list-view.tsx +34 -0
  303. package/dist/assets/storefront/src/components/commerce/product-sidebar.tsx +175 -0
  304. package/dist/assets/storefront/src/components/commerce/product-view-switcher.tsx +107 -0
  305. package/dist/assets/storefront/src/components/commerce/quote-action-buttons.tsx +235 -0
  306. package/dist/assets/storefront/src/components/commerce/quote-activity-timeline.tsx +320 -0
  307. package/dist/assets/storefront/src/components/commerce/quote-comment-form.tsx +52 -0
  308. package/dist/assets/storefront/src/components/commerce/quote-csv-export-button.tsx +97 -0
  309. package/dist/assets/storefront/src/components/commerce/quote-product-search.tsx +139 -0
  310. package/dist/assets/storefront/src/components/commerce/quote-request-form.tsx +252 -0
  311. package/dist/assets/storefront/src/components/commerce/quote-status-badge.tsx +41 -0
  312. package/dist/assets/storefront/src/components/commerce/quote-validity-indicator.tsx +33 -0
  313. package/dist/assets/storefront/src/components/commerce/quote-version-comparison.tsx +88 -0
  314. package/dist/assets/storefront/src/components/commerce/related-products.tsx +56 -0
  315. package/dist/assets/storefront/src/components/commerce/shop-by-category.tsx +59 -0
  316. package/dist/assets/storefront/src/components/commerce/shopping-list-toggle.tsx +158 -0
  317. package/dist/assets/storefront/src/components/commerce/sort-dropdown.tsx +51 -0
  318. package/dist/assets/storefront/src/components/commerce/store-credit-summary.tsx +45 -0
  319. package/dist/assets/storefront/src/components/commerce/subcollection-tiles.tsx +56 -0
  320. package/dist/assets/storefront/src/components/layout/auth-page-shell.tsx +15 -0
  321. package/dist/assets/storefront/src/components/layout/draft-mode-action.ts +8 -0
  322. package/dist/assets/storefront/src/components/layout/draft-mode-indicator.tsx +54 -0
  323. package/dist/assets/storefront/src/components/layout/draft-mode-link.tsx +29 -0
  324. package/dist/assets/storefront/src/components/layout/footer.tsx +168 -0
  325. package/dist/assets/storefront/src/components/layout/navbar/actions.ts +33 -0
  326. package/dist/assets/storefront/src/components/layout/navbar/cart-icon.tsx +50 -0
  327. package/dist/assets/storefront/src/components/layout/navbar/channel-picker-wrapper.tsx +19 -0
  328. package/dist/assets/storefront/src/components/layout/navbar/channel-picker.tsx +53 -0
  329. package/dist/assets/storefront/src/components/layout/navbar/currency-picker-wrapper.tsx +21 -0
  330. package/dist/assets/storefront/src/components/layout/navbar/currency-picker.tsx +52 -0
  331. package/dist/assets/storefront/src/components/layout/navbar/language-picker.tsx +51 -0
  332. package/dist/assets/storefront/src/components/layout/navbar/login-button.tsx +34 -0
  333. package/dist/assets/storefront/src/components/layout/navbar/mobile-nav.tsx +74 -0
  334. package/dist/assets/storefront/src/components/layout/navbar/nav-menu-items.tsx +51 -0
  335. package/dist/assets/storefront/src/components/layout/navbar/navbar-cart.tsx +5 -0
  336. package/dist/assets/storefront/src/components/layout/navbar/navbar-collections.tsx +291 -0
  337. package/dist/assets/storefront/src/components/layout/navbar/navbar-link.tsx +28 -0
  338. package/dist/assets/storefront/src/components/layout/navbar/navbar-user.tsx +147 -0
  339. package/dist/assets/storefront/src/components/layout/navbar/theme-switcher.tsx +39 -0
  340. package/dist/assets/storefront/src/components/layout/navbar.tsx +117 -0
  341. package/dist/assets/storefront/src/components/layout/search-input.tsx +71 -0
  342. package/dist/assets/storefront/src/components/providers/awaiting-approval-banner.tsx +58 -0
  343. package/dist/assets/storefront/src/components/providers/cart-count-server.tsx +31 -0
  344. package/dist/assets/storefront/src/components/providers/company-context-guard.tsx +47 -0
  345. package/dist/assets/storefront/src/components/providers/company-permissions-server.tsx +14 -0
  346. package/dist/assets/storefront/src/components/providers/theme-provider.tsx +16 -0
  347. package/dist/assets/storefront/src/components/shared/breadcrumbs.tsx +39 -0
  348. package/dist/assets/storefront/src/components/shared/catalog-sidebar-skeleton.tsx +21 -0
  349. package/dist/assets/storefront/src/components/shared/country-select.tsx +83 -0
  350. package/dist/assets/storefront/src/components/shared/error-state.tsx +16 -0
  351. package/dist/assets/storefront/src/components/shared/pagination.tsx +119 -0
  352. package/dist/assets/storefront/src/components/shared/prefetch-link.tsx +36 -0
  353. package/dist/assets/storefront/src/components/shared/product-grid-skeleton.tsx +26 -0
  354. package/dist/assets/storefront/src/components/shared/scenario-paused.tsx +80 -0
  355. package/dist/assets/storefront/src/components/shared/skeletons/cart-skeleton.tsx +48 -0
  356. package/dist/assets/storefront/src/components/shared/skeletons/navbar-user-skeleton.tsx +9 -0
  357. package/dist/assets/storefront/src/components/shared/skeletons/search-input-skeleton.tsx +7 -0
  358. package/dist/assets/storefront/src/components/shared/skeletons/search-results-skeleton.tsx +17 -0
  359. package/dist/assets/storefront/src/components/ui/accordion.tsx +66 -0
  360. package/dist/assets/storefront/src/components/ui/alert-dialog.tsx +157 -0
  361. package/dist/assets/storefront/src/components/ui/alert.tsx +66 -0
  362. package/dist/assets/storefront/src/components/ui/aspect-ratio.tsx +11 -0
  363. package/dist/assets/storefront/src/components/ui/avatar.tsx +53 -0
  364. package/dist/assets/storefront/src/components/ui/badge.tsx +46 -0
  365. package/dist/assets/storefront/src/components/ui/breadcrumb.tsx +109 -0
  366. package/dist/assets/storefront/src/components/ui/button.tsx +58 -0
  367. package/dist/assets/storefront/src/components/ui/calendar.tsx +213 -0
  368. package/dist/assets/storefront/src/components/ui/card.tsx +92 -0
  369. package/dist/assets/storefront/src/components/ui/carousel.tsx +241 -0
  370. package/dist/assets/storefront/src/components/ui/chart.tsx +357 -0
  371. package/dist/assets/storefront/src/components/ui/checkbox.tsx +32 -0
  372. package/dist/assets/storefront/src/components/ui/collapsible.tsx +33 -0
  373. package/dist/assets/storefront/src/components/ui/command.tsx +184 -0
  374. package/dist/assets/storefront/src/components/ui/context-menu.tsx +252 -0
  375. package/dist/assets/storefront/src/components/ui/dialog.tsx +143 -0
  376. package/dist/assets/storefront/src/components/ui/drawer.tsx +135 -0
  377. package/dist/assets/storefront/src/components/ui/dropdown-menu.tsx +257 -0
  378. package/dist/assets/storefront/src/components/ui/empty-state.tsx +37 -0
  379. package/dist/assets/storefront/src/components/ui/field.tsx +244 -0
  380. package/dist/assets/storefront/src/components/ui/form.tsx +167 -0
  381. package/dist/assets/storefront/src/components/ui/hover-card.tsx +44 -0
  382. package/dist/assets/storefront/src/components/ui/input-otp.tsx +77 -0
  383. package/dist/assets/storefront/src/components/ui/input.tsx +21 -0
  384. package/dist/assets/storefront/src/components/ui/kbd-shortcut.tsx +20 -0
  385. package/dist/assets/storefront/src/components/ui/label.tsx +24 -0
  386. package/dist/assets/storefront/src/components/ui/menubar.tsx +276 -0
  387. package/dist/assets/storefront/src/components/ui/navigation-menu.tsx +168 -0
  388. package/dist/assets/storefront/src/components/ui/pagination.tsx +127 -0
  389. package/dist/assets/storefront/src/components/ui/popover.tsx +48 -0
  390. package/dist/assets/storefront/src/components/ui/progress.tsx +31 -0
  391. package/dist/assets/storefront/src/components/ui/radio-group.tsx +45 -0
  392. package/dist/assets/storefront/src/components/ui/resizable.tsx +56 -0
  393. package/dist/assets/storefront/src/components/ui/scroll-area.tsx +58 -0
  394. package/dist/assets/storefront/src/components/ui/select.tsx +185 -0
  395. package/dist/assets/storefront/src/components/ui/separator.tsx +28 -0
  396. package/dist/assets/storefront/src/components/ui/sheet.tsx +139 -0
  397. package/dist/assets/storefront/src/components/ui/sidebar.tsx +728 -0
  398. package/dist/assets/storefront/src/components/ui/skeleton.tsx +13 -0
  399. package/dist/assets/storefront/src/components/ui/slider.tsx +63 -0
  400. package/dist/assets/storefront/src/components/ui/sonner.tsx +25 -0
  401. package/dist/assets/storefront/src/components/ui/switch.tsx +31 -0
  402. package/dist/assets/storefront/src/components/ui/table.tsx +116 -0
  403. package/dist/assets/storefront/src/components/ui/tabs.tsx +66 -0
  404. package/dist/assets/storefront/src/components/ui/textarea.tsx +18 -0
  405. package/dist/assets/storefront/src/components/ui/toggle-group.tsx +73 -0
  406. package/dist/assets/storefront/src/components/ui/toggle.tsx +47 -0
  407. package/dist/assets/storefront/src/components/ui/tooltip.tsx +61 -0
  408. package/dist/assets/storefront/src/config/branding.ts +4 -0
  409. package/dist/assets/storefront/src/config/scenario-meta.ts +46 -0
  410. package/dist/assets/storefront/src/config/scenario.ts +313 -0
  411. package/dist/assets/storefront/src/contexts/auth-context.tsx +35 -0
  412. package/dist/assets/storefront/src/graphql.ts +14 -0
  413. package/dist/assets/storefront/src/hooks/use-mobile.ts +19 -0
  414. package/dist/assets/storefront/src/i18n/locale-utils.ts +33 -0
  415. package/dist/assets/storefront/src/i18n/locales.ts +18 -0
  416. package/dist/assets/storefront/src/i18n/navigation.ts +35 -0
  417. package/dist/assets/storefront/src/i18n/request.ts +19 -0
  418. package/dist/assets/storefront/src/i18n/routing.ts +19 -0
  419. package/dist/assets/storefront/src/i18n/server.ts +16 -0
  420. package/dist/assets/storefront/src/lib/actions/switch-channel.ts +32 -0
  421. package/dist/assets/storefront/src/lib/actions/switch-currency.ts +25 -0
  422. package/dist/assets/storefront/src/lib/auth.ts +18 -0
  423. package/dist/assets/storefront/src/lib/backend-health.ts +38 -0
  424. package/dist/assets/storefront/src/lib/cart-count-context.tsx +59 -0
  425. package/dist/assets/storefront/src/lib/cart-guard-utils.ts +10 -0
  426. package/dist/assets/storefront/src/lib/cart-guard.ts +104 -0
  427. package/dist/assets/storefront/src/lib/channel-server.ts +28 -0
  428. package/dist/assets/storefront/src/lib/channel.ts +2 -0
  429. package/dist/assets/storefront/src/lib/company-context.ts +35 -0
  430. package/dist/assets/storefront/src/lib/company-permissions-context.tsx +40 -0
  431. package/dist/assets/storefront/src/lib/currency-server.ts +20 -0
  432. package/dist/assets/storefront/src/lib/currency.ts +2 -0
  433. package/dist/assets/storefront/src/lib/format.ts +48 -0
  434. package/dist/assets/storefront/src/lib/metadata.ts +85 -0
  435. package/dist/assets/storefront/src/lib/quantity.test.js +20 -0
  436. package/dist/assets/storefront/src/lib/quantity.ts +20 -0
  437. package/dist/assets/storefront/src/lib/search-bar-context.tsx +45 -0
  438. package/dist/assets/storefront/src/lib/search-helpers.ts +69 -0
  439. package/dist/assets/storefront/src/lib/use-recent-searches.ts +55 -0
  440. package/dist/assets/storefront/src/lib/utils.ts +6 -0
  441. package/dist/assets/storefront/src/lib/vendure/actions.ts +96 -0
  442. package/dist/assets/storefront/src/lib/vendure/api.ts +164 -0
  443. package/dist/assets/storefront/src/lib/vendure/approval-utils.ts +21 -0
  444. package/dist/assets/storefront/src/lib/vendure/cached.ts +71 -0
  445. package/dist/assets/storefront/src/lib/vendure/fragments.ts +213 -0
  446. package/dist/assets/storefront/src/lib/vendure/instant-search-action.ts +104 -0
  447. package/dist/assets/storefront/src/lib/vendure/mutations.ts +1136 -0
  448. package/dist/assets/storefront/src/lib/vendure/queries.ts +1406 -0
  449. package/dist/assets/storefront/src/middleware.ts +11 -0
  450. package/dist/assets/storefront/src/scenarios/b2b-wholesale/branding.ts +180 -0
  451. package/dist/assets/storefront/src/scenarios/b2b-wholesale/hero.tsx +122 -0
  452. package/dist/assets/storefront/src/scenarios/b2b-wholesale/index.ts +21 -0
  453. package/dist/assets/storefront/src/scenarios/b2b-wholesale/meta.ts +3 -0
  454. package/dist/assets/storefront/src/scenarios/default/branding.ts +148 -0
  455. package/dist/assets/storefront/src/scenarios/default/hero.tsx +107 -0
  456. package/dist/assets/storefront/src/scenarios/default/index.ts +12 -0
  457. package/dist/assets/storefront/src/scenarios/default/meta.ts +3 -0
  458. package/dist/assets/storefront/src/scenarios/kaminpro/auth-gate.tsx +36 -0
  459. package/dist/assets/storefront/src/scenarios/kaminpro/branding.ts +238 -0
  460. package/dist/assets/storefront/src/scenarios/kaminpro/cart-items.tsx +359 -0
  461. package/dist/assets/storefront/src/scenarios/kaminpro/hero.tsx +123 -0
  462. package/dist/assets/storefront/src/scenarios/kaminpro/index.ts +50 -0
  463. package/dist/assets/storefront/src/scenarios/kaminpro/meta.ts +9 -0
  464. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/banners.tsx +21 -0
  465. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/bom-basket.tsx +221 -0
  466. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/btn.tsx +38 -0
  467. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/chip.tsx +35 -0
  468. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/config-header.tsx +72 -0
  469. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/field-row.tsx +126 -0
  470. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/info-dot.tsx +38 -0
  471. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/login-notice.tsx +81 -0
  472. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/module-panel.tsx +144 -0
  473. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/quote-dialog.tsx +110 -0
  474. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/schematic.tsx +674 -0
  475. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/components/why-panel.tsx +66 -0
  476. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/configurator-pdp.tsx +43 -0
  477. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/configurator-shell.tsx +320 -0
  478. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/lib/data.ts +150 -0
  479. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/lib/types.ts +30 -0
  480. package/dist/assets/storefront/src/scenarios/kaminpro/pdp/styles.css +104 -0
  481. package/dist/assets/storefront/src/scenarios/kaminpro/shop-by-system.tsx +97 -0
  482. package/dist/assets/storefront/src/scenarios/kaminpro/system-finder/decision-tree.ts +261 -0
  483. package/dist/assets/storefront/src/scenarios/kaminpro/system-finder/page.tsx +21 -0
  484. package/dist/assets/storefront/src/scenarios/kaminpro/system-finder/wizard.tsx +600 -0
  485. package/dist/assets/storefront/src/schema.graphql.placeholder +5 -0
  486. package/dist/assets/storefront/tsconfig.json +37 -0
  487. package/dist/assets/templates/CLAUDE.md.hbs +63 -0
  488. package/dist/assets/templates/README.md.hbs +53 -0
  489. package/dist/assets/templates/custom-populate.ts.hbs +28 -0
  490. package/dist/assets/templates/docker-compose.yml.hbs +29 -0
  491. package/dist/assets/templates/env.hbs +44 -0
  492. package/dist/assets/templates/environment.d.ts.hbs +32 -0
  493. package/dist/assets/templates/gitignore.hbs +7 -0
  494. package/dist/assets/templates/index.ts.hbs +16 -0
  495. package/dist/assets/templates/npmrc.hbs +1 -0
  496. package/dist/assets/templates/populate.ts.hbs +27 -0
  497. package/dist/assets/templates/root-package.json.hbs +17 -0
  498. package/dist/assets/templates/server-package.json.hbs +31 -0
  499. package/dist/assets/templates/tsconfig.dashboard.json.hbs +17 -0
  500. package/dist/assets/templates/tsconfig.root.json.hbs +16 -0
  501. package/dist/assets/templates/tsconfig.server.json.hbs +20 -0
  502. package/dist/assets/templates/vendure-config.ts.hbs +80 -0
  503. package/dist/assets/templates/vite.config.mts.hbs +27 -0
  504. package/dist/bin/run.d.ts +2 -0
  505. package/dist/bin/run.js +79 -0
  506. package/dist/bin/run.js.map +1 -0
  507. package/dist/cli.d.ts +6 -0
  508. package/dist/cli.js +154 -0
  509. package/dist/cli.js.map +1 -0
  510. package/dist/git-init.d.ts +4 -0
  511. package/dist/git-init.js +60 -0
  512. package/dist/git-init.js.map +1 -0
  513. package/dist/index.d.ts +4 -0
  514. package/dist/index.js +13 -0
  515. package/dist/index.js.map +1 -0
  516. package/dist/plugin-registry.d.ts +10 -0
  517. package/dist/plugin-registry.js +485 -0
  518. package/dist/plugin-registry.js.map +1 -0
  519. package/dist/scaffolder.d.ts +10 -0
  520. package/dist/scaffolder.js +218 -0
  521. package/dist/scaffolder.js.map +1 -0
  522. package/dist/storefront-copier.d.ts +6 -0
  523. package/dist/storefront-copier.js +68 -0
  524. package/dist/storefront-copier.js.map +1 -0
  525. package/dist/template-renderer.d.ts +30 -0
  526. package/dist/template-renderer.js +177 -0
  527. package/dist/template-renderer.js.map +1 -0
  528. package/dist/types.d.ts +60 -0
  529. package/dist/types.js +3 -0
  530. package/dist/types.js.map +1 -0
  531. package/package.json +48 -0
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef, useCallback } from 'react';
4
+ import { useSearchParams } from 'next/navigation';
5
+ import { useTranslations } from 'next-intl';
6
+ import { useRouter } from '@/i18n/navigation';
7
+ import { Search } from 'lucide-react';
8
+ import { Input } from '@/components/ui/input';
9
+ import { useSearchBar } from '@/lib/search-bar-context';
10
+ import { KbdShortcut } from '@/components/ui/kbd-shortcut';
11
+ import { InstantSearchDropdown } from '@/components/commerce/instant-search-dropdown';
12
+
13
+ export function SearchInput() {
14
+ const t = useTranslations('Navigation');
15
+ const router = useRouter();
16
+ const searchParams = useSearchParams();
17
+ const [searchValue, setSearchValue] = useState(searchParams.get('q') || '');
18
+ const inputRef = useRef<HTMLInputElement | null>(null);
19
+ const { hasInlineSearch, searchInputRef } = useSearchBar();
20
+
21
+ const setRef = useCallback((el: HTMLInputElement | null) => {
22
+ inputRef.current = el;
23
+ }, []);
24
+
25
+ useEffect(() => {
26
+ if (!hasInlineSearch && inputRef.current) {
27
+ searchInputRef.current = inputRef.current;
28
+ }
29
+ }, [hasInlineSearch, searchInputRef]);
30
+
31
+ useEffect(() => {
32
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- sync external URL state
33
+ setSearchValue(searchParams.get('q') || '');
34
+ }, [searchParams]);
35
+
36
+ const handleSubmit = useCallback((term: string) => {
37
+ if (!term.trim()) return;
38
+ router.push(`/search?q=${encodeURIComponent(term.trim())}`);
39
+ }, [router]);
40
+
41
+ const handleFormSubmit = (e: React.FormEvent) => {
42
+ e.preventDefault();
43
+ handleSubmit(searchValue);
44
+ };
45
+
46
+ return (
47
+ <div className={hasInlineSearch ? 'hidden' : ''}>
48
+ <InstantSearchDropdown
49
+ inputRef={inputRef}
50
+ searchValue={searchValue}
51
+ onSearchValueChange={setSearchValue}
52
+ onSubmit={handleSubmit}
53
+ dropdownClassName="w-[32rem] right-0 left-auto"
54
+ >
55
+ <form onSubmit={handleFormSubmit} className="relative">
56
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
57
+ <Input
58
+ ref={setRef}
59
+ type="search"
60
+ placeholder={t('searchByNameSkuKeyword')}
61
+ className="pl-9 pr-16 w-64"
62
+ value={searchValue}
63
+ onChange={(e) => setSearchValue(e.target.value)}
64
+ autoComplete="off"
65
+ />
66
+ <KbdShortcut className="absolute right-3 top-1/2 -translate-y-1/2" />
67
+ </form>
68
+ </InstantSearchDropdown>
69
+ </div>
70
+ );
71
+ }
@@ -0,0 +1,58 @@
1
+ import {cacheLife, cacheTag} from 'next/cache';
2
+ import {ClipboardCheck, ArrowRight} from 'lucide-react';
3
+ import {getTranslations} from 'next-intl/server';
4
+ import {Link} from '@/i18n/navigation';
5
+ import {client, getVendureContext} from '@/lib/vendure/api';
6
+ import type {VendureContext} from '@/lib/vendure/api';
7
+ import {GetActiveOrderQuery} from '@/lib/vendure/queries';
8
+
9
+ async function getAwaitingApprovalOrderCode(ctx: VendureContext): Promise<string | null> {
10
+ 'use cache: private';
11
+ cacheLife('minutes');
12
+ cacheTag('cart');
13
+ cacheTag('active-order');
14
+
15
+ const [result, error] = await client.query(GetActiveOrderQuery, undefined, ctx);
16
+ if (error) return null;
17
+ const order = result.data.activeOrder;
18
+ if (!order || order.state !== 'AwaitingApproval') return null;
19
+ return order.code;
20
+ }
21
+
22
+ export async function AwaitingApprovalBanner() {
23
+ const ctx = await getVendureContext();
24
+ const code = await getAwaitingApprovalOrderCode(ctx);
25
+ if (!code) return null;
26
+ const t = await getTranslations('Cart');
27
+ return (
28
+ <>
29
+ {/* Push page content down so it clears the banner. The banner is
30
+ ~44px tall on >=sm screens and wraps to ~68px on <640px. The
31
+ page-top-offset = navbar + banner sum is computed in globals.css. */}
32
+ <style>{`:root{--banner-height:44px}@media (max-width:639px){:root{--banner-height:68px}}`}</style>
33
+ <div className="bg-amber-500/15 border-b border-amber-500/30 text-foreground">
34
+ <Link
35
+ href={`/account/approvals?order=${code}`}
36
+ className="block hover:bg-amber-500/25 transition-colors"
37
+ >
38
+ <div className="container mx-auto px-4 py-2 flex items-center justify-between gap-4 flex-wrap">
39
+ <div className="flex items-center gap-3 min-w-0">
40
+ <ClipboardCheck className="h-5 w-5 flex-shrink-0 text-amber-600 dark:text-amber-400"/>
41
+ <p className="text-sm">
42
+ <span className="font-semibold">{t('orderPendingApproval')}:</span>{' '}
43
+ <code className="font-mono font-semibold px-1.5 py-0.5 rounded bg-amber-500/20">
44
+ {code}
45
+ </code>{' '}
46
+ <span className="text-muted-foreground">{t('awaitingApprovalBannerBody')}</span>
47
+ </p>
48
+ </div>
49
+ <span className="text-sm font-semibold inline-flex items-center gap-1.5 flex-shrink-0 text-amber-600 dark:text-amber-400">
50
+ {t('viewRequest')}
51
+ <ArrowRight className="h-4 w-4"/>
52
+ </span>
53
+ </div>
54
+ </Link>
55
+ </div>
56
+ </>
57
+ );
58
+ }
@@ -0,0 +1,31 @@
1
+ import {cacheLife, cacheTag} from 'next/cache';
2
+ import {CartStatusSync} from '@/lib/cart-count-context';
3
+ import {client, getVendureContext} from '@/lib/vendure/api';
4
+ import type {VendureContext} from '@/lib/vendure/api';
5
+ import {GetActiveOrderQuery} from '@/lib/vendure/queries';
6
+
7
+ async function getCartStatus(ctx: VendureContext): Promise<{count: number; awaitingApprovalOrderCode: string | null}> {
8
+ 'use cache: private';
9
+ cacheLife('minutes');
10
+ cacheTag('cart');
11
+ cacheTag('active-order');
12
+
13
+ const [result, error] = await client.query(GetActiveOrderQuery, undefined, ctx);
14
+ if (error) return {count: 0, awaitingApprovalOrderCode: null};
15
+
16
+ const order = result.data.activeOrder;
17
+ if (!order) return {count: 0, awaitingApprovalOrderCode: null};
18
+ if (order.state === 'AwaitingApproval') {
19
+ return {count: 0, awaitingApprovalOrderCode: order.code};
20
+ }
21
+ // Only count lines when the order is in AddingItems (editable cart).
22
+ if (order.state !== 'AddingItems') return {count: 0, awaitingApprovalOrderCode: null};
23
+
24
+ return {count: order.lines.length, awaitingApprovalOrderCode: null};
25
+ }
26
+
27
+ export async function CartCountServer() {
28
+ const ctx = await getVendureContext();
29
+ const {count, awaitingApprovalOrderCode} = await getCartStatus(ctx);
30
+ return <CartStatusSync count={count} awaitingApprovalOrderCode={awaitingApprovalOrderCode} />;
31
+ }
@@ -0,0 +1,47 @@
1
+ import {cookies} from 'next/headers';
2
+ import {getMyCompanies} from '@/lib/vendure/actions';
3
+ import {redirect} from 'next/navigation';
4
+
5
+ const COMPANY_ID_COOKIE = 'vendure-company-id';
6
+
7
+ /**
8
+ * Server component that detects and clears stale company context cookies.
9
+ *
10
+ * ## Why this exists
11
+ *
12
+ * The storefront stores the active company ID in a `vendure-company-id` cookie.
13
+ * This cookie persists across server restarts and database repopulations. After a
14
+ * repopulate, entity IDs change but the browser still sends the old cookie value.
15
+ * The Vendure API then can't resolve the company context, causing:
16
+ * - Company addresses not loading at checkout
17
+ * - Company section disappearing from the account sidebar
18
+ * - ForbiddenError on company-scoped queries
19
+ *
20
+ * ## How it works
21
+ *
22
+ * On every page load, this component reads the `vendure-company-id` cookie and
23
+ * checks whether it matches one of the user's actual company memberships.
24
+ * If the cookie is stale, it redirects to `/api/clear-company-context` (a Route
25
+ * Handler that can modify cookies) which clears the cookie and redirects back.
26
+ *
27
+ * ## Performance
28
+ *
29
+ * `getMyCompanies()` is wrapped in React `cache()`, so if other components on the
30
+ * same page also call it, no duplicate API request is made. The redirect only
31
+ * happens once (on the first request with a stale cookie).
32
+ */
33
+ export async function CompanyContextGuard() {
34
+ const cookieStore = await cookies();
35
+ const companyId = cookieStore.get(COMPANY_ID_COOKIE)?.value;
36
+ if (!companyId) return null;
37
+
38
+ const companies = await getMyCompanies();
39
+ const isValid = companies.some(c => c.id === companyId);
40
+ if (!isValid) {
41
+ // Can't modify cookies in a Server Component, so redirect to a Route Handler
42
+ // that clears the stale cookie and sends the user back.
43
+ await redirect('/api/clear-company-context');
44
+ }
45
+
46
+ return null;
47
+ }
@@ -0,0 +1,14 @@
1
+ import { getCompanyAccount } from '@/lib/vendure/actions';
2
+ import { CompanyPermissionsProvider } from '@/lib/company-permissions-context';
3
+ import type { ReactNode } from 'react';
4
+
5
+ export async function CompanyPermissionsServer({ children }: { children: ReactNode }) {
6
+ const account = await getCompanyAccount();
7
+ const permissions = account?.permissions ?? null;
8
+
9
+ return (
10
+ <CompanyPermissionsProvider permissions={permissions}>
11
+ {children}
12
+ </CompanyPermissionsProvider>
13
+ );
14
+ }
@@ -0,0 +1,16 @@
1
+ "use client";
2
+
3
+ import {ThemeProvider as NextThemesProvider} from "next-themes";
4
+
5
+ export function ThemeProvider({children}: {children: React.ReactNode}) {
6
+ return (
7
+ <NextThemesProvider
8
+ attribute="class"
9
+ defaultTheme="dark"
10
+ enableSystem={false}
11
+ disableTransitionOnChange
12
+ >
13
+ {children}
14
+ </NextThemesProvider>
15
+ );
16
+ }
@@ -0,0 +1,39 @@
1
+ import { PrefetchLink } from '@/components/shared/prefetch-link';
2
+ import { ChevronRight } from 'lucide-react';
3
+ import { getTranslations } from 'next-intl/server';
4
+
5
+ interface BreadcrumbItem {
6
+ name: string;
7
+ href?: string;
8
+ }
9
+
10
+ interface BreadcrumbsProps {
11
+ items?: BreadcrumbItem[];
12
+ }
13
+
14
+ export async function Breadcrumbs({ items = [] }: BreadcrumbsProps) {
15
+ const t = await getTranslations('Navigation');
16
+ return (
17
+ <nav aria-label="Breadcrumb" className="pt-4 pb-10 flex items-center">
18
+ <ol className="flex items-center gap-2 text-lg text-muted-foreground">
19
+ <li>
20
+ <PrefetchLink href="/search" className="hover:text-foreground transition-colors">
21
+ {t('catalog')}
22
+ </PrefetchLink>
23
+ </li>
24
+ {items.map((item, i) => (
25
+ <li key={i} className="flex items-center gap-1.5">
26
+ <ChevronRight className="h-5 w-5" />
27
+ {item.href ? (
28
+ <PrefetchLink href={item.href} className="hover:text-foreground transition-colors">
29
+ {item.name}
30
+ </PrefetchLink>
31
+ ) : (
32
+ <span className="text-foreground font-medium">{item.name}</span>
33
+ )}
34
+ </li>
35
+ ))}
36
+ </ol>
37
+ </nav>
38
+ );
39
+ }
@@ -0,0 +1,21 @@
1
+ const WIDTHS = ['75%', '60%', '85%'];
2
+
3
+ export function CatalogSidebarSkeleton() {
4
+ return (
5
+ <div className="space-y-4">
6
+ <div className="h-5 w-16 bg-muted rounded" />
7
+ <div className="space-y-3">
8
+ {Array.from({ length: 4 }).map((_, i) => (
9
+ <div key={i} className="space-y-2">
10
+ <div className="h-4 w-24 bg-muted rounded" />
11
+ <div className="space-y-1.5 pl-1">
12
+ {Array.from({ length: 3 }).map((_, j) => (
13
+ <div key={j} className="h-4 bg-muted rounded" style={{ width: WIDTHS[j] }} />
14
+ ))}
15
+ </div>
16
+ </div>
17
+ ))}
18
+ </div>
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { Check, ChevronsUpDown } from 'lucide-react';
5
+ import { useTranslations } from 'next-intl';
6
+ import { cn } from '@/lib/utils';
7
+ import { Button } from '@/components/ui/button';
8
+ import {
9
+ Command,
10
+ CommandEmpty,
11
+ CommandGroup,
12
+ CommandInput,
13
+ CommandItem,
14
+ CommandList,
15
+ } from '@/components/ui/command';
16
+ import {
17
+ Popover,
18
+ PopoverContent,
19
+ PopoverTrigger,
20
+ } from '@/components/ui/popover';
21
+
22
+ interface Country {
23
+ code: string;
24
+ name: string;
25
+ }
26
+
27
+ interface CountrySelectProps {
28
+ countries: Country[];
29
+ value?: string;
30
+ onValueChange: (value: string) => void;
31
+ disabled?: boolean;
32
+ }
33
+
34
+ export function CountrySelect({ countries, value, onValueChange, disabled }: CountrySelectProps) {
35
+ const t = useTranslations('Common');
36
+ const [open, setOpen] = React.useState(false);
37
+ const selectedCountry = countries.find((country) => country.code === value);
38
+
39
+ return (
40
+ <Popover open={open} onOpenChange={setOpen}>
41
+ <PopoverTrigger asChild>
42
+ <Button
43
+ variant="outline"
44
+ role="combobox"
45
+ aria-expanded={open}
46
+ className="w-full justify-between"
47
+ disabled={disabled}
48
+ >
49
+ {selectedCountry ? selectedCountry.name : t('selectCountry')}
50
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
51
+ </Button>
52
+ </PopoverTrigger>
53
+ <PopoverContent className="w-full p-0" align="start">
54
+ <Command>
55
+ <CommandInput placeholder={t('searchCountry')} />
56
+ <CommandList>
57
+ <CommandEmpty>{t('noCountryFound')}</CommandEmpty>
58
+ <CommandGroup>
59
+ {countries.map((country) => (
60
+ <CommandItem
61
+ key={country.code}
62
+ value={country.name}
63
+ onSelect={() => {
64
+ onValueChange(country.code);
65
+ setOpen(false);
66
+ }}
67
+ >
68
+ <Check
69
+ className={cn(
70
+ 'mr-2 h-4 w-4',
71
+ value === country.code ? 'opacity-100' : 'opacity-0'
72
+ )}
73
+ />
74
+ {country.name}
75
+ </CommandItem>
76
+ ))}
77
+ </CommandGroup>
78
+ </CommandList>
79
+ </Command>
80
+ </PopoverContent>
81
+ </Popover>
82
+ );
83
+ }
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+
3
+ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
4
+ import { AlertCircle } from 'lucide-react';
5
+ import { useTranslations } from 'next-intl';
6
+
7
+ export function ErrorState({ message }: { message: string }) {
8
+ const t = useTranslations('Common');
9
+ return (
10
+ <Alert variant="destructive">
11
+ <AlertCircle className="h-4 w-4" />
12
+ <AlertTitle>{t('somethingWentWrong')}</AlertTitle>
13
+ <AlertDescription>{message}</AlertDescription>
14
+ </Alert>
15
+ );
16
+ }
@@ -0,0 +1,119 @@
1
+ 'use client';
2
+
3
+ import {useSearchParams} from 'next/navigation';
4
+ import {usePathname} from '@/i18n/navigation';
5
+ import {ChevronLeft, ChevronRight} from 'lucide-react';
6
+ import {Button} from '@/components/ui/button';
7
+ import {Link} from "@/i18n/navigation";
8
+
9
+ interface PaginationProps {
10
+ currentPage: number;
11
+ totalPages: number;
12
+ }
13
+
14
+ export function Pagination({currentPage, totalPages}: PaginationProps) {
15
+ const pathname = usePathname();
16
+ const searchParams = useSearchParams();
17
+
18
+ const createPageUrl = (page: number) => {
19
+ const params = new URLSearchParams(searchParams);
20
+ params.set('page', page.toString());
21
+ return `${pathname}?${params.toString()}`;
22
+ };
23
+
24
+ const getPageNumbers = () => {
25
+ const delta = 2;
26
+ const range = [];
27
+ const rangeWithDots = [];
28
+
29
+ for (let i = 1; i <= totalPages; i++) {
30
+ if (
31
+ i === 1 ||
32
+ i === totalPages ||
33
+ (i >= currentPage - delta && i <= currentPage + delta)
34
+ ) {
35
+ range.push(i);
36
+ }
37
+ }
38
+
39
+ let prev = 0;
40
+ for (const i of range) {
41
+ if (prev && i - prev > 1) {
42
+ rangeWithDots.push('...');
43
+ }
44
+ rangeWithDots.push(i);
45
+ prev = i;
46
+ }
47
+
48
+ return rangeWithDots;
49
+ };
50
+
51
+ const pages = getPageNumbers();
52
+
53
+ return (
54
+ <nav className="flex items-center justify-center gap-2">
55
+ <Button
56
+ variant="outline"
57
+ size="icon"
58
+ asChild
59
+ disabled={currentPage === 1}
60
+ >
61
+ {currentPage === 1 ? (
62
+ <span className="cursor-not-allowed">
63
+ <ChevronLeft className="h-4 w-4"/>
64
+ </span>
65
+ ) : (
66
+ <Link href={createPageUrl(currentPage - 1)} prefetch={false}>
67
+ <ChevronLeft className="h-4 w-4"/>
68
+ </Link>
69
+ )}
70
+ </Button>
71
+
72
+ {pages.map((page, index) => {
73
+ if (page === '...') {
74
+ return (
75
+ <span key={`dots-${index}`} className="px-2 text-muted-foreground">
76
+ ...
77
+ </span>
78
+ );
79
+ }
80
+
81
+ const pageNum = page as number;
82
+ const isActive = pageNum === currentPage;
83
+
84
+ return (
85
+ <Button
86
+ key={pageNum}
87
+ variant={isActive ? 'default' : 'outline'}
88
+ size="icon"
89
+ asChild={!isActive}
90
+ disabled={isActive}
91
+ >
92
+ {isActive ? (
93
+ <span>{pageNum}</span>
94
+ ) : (
95
+ <Link href={createPageUrl(pageNum)} prefetch={false}>{pageNum}</Link>
96
+ )}
97
+ </Button>
98
+ );
99
+ })}
100
+
101
+ <Button
102
+ variant="outline"
103
+ size="icon"
104
+ asChild
105
+ disabled={currentPage === totalPages}
106
+ >
107
+ {currentPage === totalPages ? (
108
+ <span className="cursor-not-allowed">
109
+ <ChevronRight className="h-4 w-4"/>
110
+ </span>
111
+ ) : (
112
+ <Link href={createPageUrl(currentPage + 1)} prefetch={false}>
113
+ <ChevronRight className="h-4 w-4"/>
114
+ </Link>
115
+ )}
116
+ </Button>
117
+ </nav>
118
+ );
119
+ }
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+
3
+ import {Link} from '@/i18n/navigation';
4
+ import { useState, type ComponentProps } from 'react';
5
+
6
+ type PrefetchLinkProps = ComponentProps<typeof Link>;
7
+
8
+ /**
9
+ * Link that defers prefetching until hover/focus, then does a FULL prefetch
10
+ * (including dynamic 'use cache' data). This avoids the skeleton flash on
11
+ * navigation while not flooding the server with viewport-triggered requests.
12
+ *
13
+ * - false: no prefetch until user shows intent
14
+ * - true (on hover): full route prefetch including cached data
15
+ *
16
+ * See: https://nextjs.org/docs/app/api-reference/components/link#prefetch
17
+ */
18
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
+ export function PrefetchLink({ prefetch, onMouseEnter, onFocus, ...props }: PrefetchLinkProps) {
20
+ const [active, setActive] = useState(false);
21
+
22
+ return (
23
+ <Link
24
+ {...props}
25
+ prefetch={active ? true : false}
26
+ onMouseEnter={(e) => {
27
+ setActive(true);
28
+ onMouseEnter?.(e);
29
+ }}
30
+ onFocus={(e) => {
31
+ setActive(true);
32
+ onFocus?.(e);
33
+ }}
34
+ />
35
+ );
36
+ }
@@ -0,0 +1,26 @@
1
+ export function ProductGridSkeleton() {
2
+ return (
3
+ <div className="space-y-6">
4
+ <div className="flex items-center justify-between">
5
+ <div className="h-5 w-32 bg-muted rounded" />
6
+ <div className="flex items-center gap-2">
7
+ <div className="h-8 w-16 bg-muted rounded" />
8
+ <div className="h-8 w-24 bg-muted rounded" />
9
+ </div>
10
+ </div>
11
+
12
+ <div className="grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-3 gap-4">
13
+ {Array.from({ length: 12 }).map((_, i) => (
14
+ <div key={i} className="bg-card rounded-lg overflow-hidden border border-border">
15
+ <div className="aspect-[4/3] bg-muted" />
16
+ <div className="p-3 space-y-1.5">
17
+ <div className="h-4 bg-muted rounded w-3/4" />
18
+ <div className="h-3 bg-muted rounded w-1/3" />
19
+ <div className="h-5 bg-muted rounded w-1/2" />
20
+ </div>
21
+ </div>
22
+ ))}
23
+ </div>
24
+ </div>
25
+ );
26
+ }
@@ -0,0 +1,80 @@
1
+ import Image from 'next/image';
2
+ import {getTranslations} from 'next-intl/server';
3
+ import {Button} from '@/components/ui/button';
4
+ import {getBranding} from '@/config/branding';
5
+ import {isBackendReachable} from '@/lib/backend-health';
6
+
7
+ const CONTACT_URL = 'https://vendure.io/contact/';
8
+
9
+ /**
10
+ * Suspense-wrappable server component that renders nothing when the Vendure
11
+ * backend is reachable, or a full-screen "Demo Paused" overlay when it isn't.
12
+ *
13
+ * Must be used inside a <Suspense> boundary — it awaits isBackendReachable(),
14
+ * which would otherwise block layout prerender under cacheComponents.
15
+ */
16
+ export async function BackendHealthGate({locale}: {locale: string}) {
17
+ const reachable = await isBackendReachable();
18
+ if (reachable) return null;
19
+ return <ScenarioPausedOverlay locale={locale} />;
20
+ }
21
+
22
+ async function ScenarioPausedOverlay({locale}: {locale: string}) {
23
+ const t = await getTranslations({locale, namespace: 'ScenarioPaused'});
24
+ const {logo, logoInverted, siteName} = getBranding();
25
+ const displayLogo = logoInverted ?? logo;
26
+
27
+ return (
28
+ <div className="fixed inset-0 z-[9999] flex flex-col items-center justify-center px-4 py-12 bg-background overflow-auto">
29
+ <div className="max-w-lg w-full text-center space-y-8">
30
+ <div className="flex justify-center">
31
+ <Image
32
+ src={displayLogo.src}
33
+ alt={displayLogo.alt}
34
+ width={displayLogo.width}
35
+ height={displayLogo.height}
36
+ className="h-16 w-auto"
37
+ priority
38
+ />
39
+ </div>
40
+
41
+ <div className="inline-flex items-center gap-2 rounded-full border border-border bg-muted/60 px-4 py-1.5 text-xs font-medium text-muted-foreground">
42
+ <span className="relative flex h-2 w-2">
43
+ <span className="absolute inline-flex h-full w-full rounded-full bg-primary/40 animate-ping" />
44
+ <span className="relative inline-flex h-2 w-2 rounded-full bg-primary" />
45
+ </span>
46
+ {t('badge')}
47
+ </div>
48
+
49
+ <div className="space-y-4">
50
+ <h1 className="text-3xl sm:text-4xl font-bold tracking-tight text-foreground">
51
+ {t('title', {siteName})}
52
+ </h1>
53
+ <p className="text-base text-muted-foreground leading-relaxed">
54
+ {t('description')}
55
+ </p>
56
+ </div>
57
+
58
+ <div className="flex flex-col sm:flex-row gap-3 justify-center pt-2">
59
+ <Button asChild size="lg">
60
+ <a href={CONTACT_URL} target="_blank" rel="noopener noreferrer">
61
+ {t('contactCta')}
62
+ </a>
63
+ </Button>
64
+ <Button asChild size="lg" variant="outline">
65
+ <a href="https://vendure.io" target="_blank" rel="noopener noreferrer">
66
+ {t('learnMore')}
67
+ </a>
68
+ </Button>
69
+ </div>
70
+
71
+ <p className="text-xs text-muted-foreground pt-4">
72
+ {t('poweredBy')}{' '}
73
+ <a href="https://vendure.io" target="_blank" rel="noopener noreferrer" className="underline hover:text-foreground transition-colors">
74
+ Vendure
75
+ </a>
76
+ </p>
77
+ </div>
78
+ </div>
79
+ );
80
+ }