@wix/agent-skills 1.14.0 → 1.15.1

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 (268) hide show
  1. package/README.md +12 -1
  2. package/package.json +1 -1
  3. package/skills/README.md +23 -0
  4. package/skills/wix-app/SKILL.md +10 -2
  5. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
  6. package/skills/wix-app/references/CODE_QUALITY.md +1 -0
  7. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
  9. package/skills/wix-app/references/DATA_COLLECTION.md +5 -3
  10. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +25 -9
  11. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  12. package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
  13. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  31. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  32. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  33. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
  34. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  35. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
  36. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  37. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  38. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
  39. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +23 -105
  40. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  41. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  42. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +18 -16
  43. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  44. package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
  45. package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
  46. package/skills/wix-app/references/stores/INVENTORY.md +37 -0
  47. package/skills/wix-app/references/stores/QUERY.md +87 -0
  48. package/skills/wix-app/references/stores/WRITE.md +55 -0
  49. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  50. package/skills/wix-auth/SKILL.md +12 -0
  51. package/skills/wix-auth/references/device-flow.md +140 -0
  52. package/skills/wix-docs/SKILL.md +188 -0
  53. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  54. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  55. package/skills/wix-headless/SKILL.md +89 -130
  56. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  57. package/skills/wix-headless/entry/skill.md +76 -0
  58. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  59. package/skills/wix-headless/references/CAPABILITIES.md +86 -0
  60. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  61. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  62. package/skills/wix-headless/references/FEEDBACK.md +129 -0
  63. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  64. package/skills/wix-headless/references/SDK_HANDOFF.md +99 -0
  65. package/skills/wix-headless/references/SEED.md +45 -187
  66. package/skills/wix-headless/references/SETUP.md +32 -103
  67. package/skills/wix-headless/references/astro.md +98 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  72. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  73. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  74. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  75. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  76. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  77. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  78. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  79. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  80. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +213 -0
  81. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  83. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +232 -0
  84. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  85. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  86. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  87. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +192 -0
  88. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +155 -0
  89. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  90. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
  91. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +197 -0
  92. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +136 -0
  93. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  94. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  95. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  96. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  97. package/skills/wix-headless/references/managed/DEPLOYMENT.md +79 -0
  98. package/skills/wix-headless/references/non-astro.md +59 -0
  99. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  100. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +15 -0
  101. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  102. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +71 -0
  103. package/skills/wix-manage/SKILL.md +29 -3
  104. package/skills/wix-manage/references/analytics/query-site-analytics.md +298 -0
  105. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
  106. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  107. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
  108. package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
  109. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +299 -0
  110. package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
  111. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
  112. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +517 -0
  113. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +218 -0
  114. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  115. package/skills/wix-manage/references/sites/site-import.md +270 -0
  116. package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
  117. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +57 -0
  118. package/skills/wix-vibe-headless/SKILL.md +142 -0
  119. package/skills/wix-vibe-headless/platforms/README.md +22 -0
  120. package/skills/wix-vibe-headless/platforms/base44.md +142 -0
  121. package/skills/wix-vibe-headless/platforms/generic.md +87 -0
  122. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  123. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  124. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +157 -0
  125. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  126. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +217 -0
  127. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  128. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  129. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  130. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +136 -0
  131. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  132. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  133. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  134. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  135. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +223 -0
  136. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  137. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  138. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  139. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  140. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  141. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  142. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  143. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +164 -0
  144. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  145. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +146 -0
  146. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  147. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  148. package/skills/wix-headless/references/BUILD.md +0 -105
  149. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  150. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  151. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  152. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  153. package/skills/wix-headless/references/PLAN-create.md +0 -17
  154. package/skills/wix-headless/references/PLAN.md +0 -92
  155. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  156. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  157. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  158. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  159. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  160. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  161. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  162. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  163. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  164. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  165. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  166. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  167. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  168. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  169. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  170. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  171. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  172. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  173. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  174. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  175. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  176. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  177. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  178. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  179. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  180. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  181. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  182. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  183. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  184. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  185. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  186. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  187. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  188. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  189. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  190. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  191. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  192. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  193. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  194. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  195. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  196. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  197. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  198. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  199. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  200. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  201. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  202. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  203. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  204. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  205. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  206. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  207. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  208. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  209. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  210. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  211. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  212. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  213. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  214. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  215. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  216. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  217. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  218. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  219. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  220. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  221. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  222. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  223. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  224. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  225. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  226. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  227. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  228. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  229. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  230. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  231. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  232. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  233. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  234. package/skills/wix-headless/references/seed-recipes.md +0 -49
  235. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  236. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  237. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  238. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  239. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  240. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  241. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  242. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  243. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  244. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  245. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  246. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  247. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  248. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  249. package/skills/wix-headless/references/verticals/blog.md +0 -31
  250. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  251. package/skills/wix-headless/references/verticals/cms.md +0 -30
  252. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  253. package/skills/wix-headless/references/verticals/forms.md +0 -26
  254. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  255. package/skills/wix-headless/references/verticals/stores.md +0 -33
  256. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  257. package/skills/wix-headless/scripts/compose.mjs +0 -520
  258. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  259. package/skills/wix-headless/scripts/flatten.sh +0 -61
  260. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  261. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  262. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  263. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  264. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  265. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  266. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  267. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  268. package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
@@ -1,132 +0,0 @@
1
- ---
2
- // ── Composer skeleton: src/layouts/Layout.astro ──────────────────────────────
3
- // Fully replaces the scaffold's bare stub. The <script> below and the View
4
- // Transitions wiring are LITERAL fixed bulk — do not re-author. Composer's only
5
- // substitutions (see compose.mjs):
6
- // {{components-css-imports}} — one `import '../styles/components-<pack>.css';`
7
- // per pack in "Packs with components" (omit entirely when none).
8
- // {{fonts.googleHref}} — the Google Fonts <link> href built from designTokens
9
- // .fonts (display + body families). Omit the <link> if fonts are system.
10
- // {{brand.name}} — default <title>.
11
- import '../styles/global.css';
12
- {{components-css-imports}}
13
- import { ClientRouter } from 'astro:transitions';
14
- import Navigation from '../components/Navigation.astro';
15
- import Footer from '../components/Footer.astro';
16
-
17
- interface Props {
18
- title?: string;
19
- description?: string;
20
- hasSeoTags?: boolean;
21
- }
22
- const { title, description, hasSeoTags = false } = Astro.props;
23
- ---
24
- <!doctype html>
25
- <html lang="en">
26
- <head>
27
- <meta charset="utf-8" />
28
- <meta name="viewport" content="width=device-width, initial-scale=1" />
29
- <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E" />
30
- <link rel="preconnect" href="https://fonts.googleapis.com" />
31
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
32
- <link rel="stylesheet" href="{{fonts.googleHref}}" />
33
- {!hasSeoTags && (
34
- <>
35
- <title>{title ? `${title} · {{brand.name}}` : "{{brand.name}}"}</title>
36
- {description && <meta name="description" content={description} />}
37
- </>
38
- )}
39
- <slot name="head" />
40
- <ClientRouter />
41
- </head>
42
- <body>
43
- <div class="nav-progress" data-nav-progress aria-hidden="true"></div>
44
- <Navigation />
45
- <main><slot /></main>
46
- <Footer />
47
- <script>
48
- // Persisted rail aria-current sync + top loading bar + rail-anchored
49
- // scroll across listing-to-listing navigation. Stores pack relies on
50
- // all three; do not split into separate scripts.
51
- function syncCategoryRail() {
52
- const path = window.location.pathname;
53
- const activeSlug = path.startsWith("/category/")
54
- ? path.slice("/category/".length).replace(/\/$/, "")
55
- : path === "/products" || path === "/products/"
56
- ? ""
57
- : null;
58
- if (activeSlug === null) return;
59
- const pills = document.querySelectorAll<HTMLAnchorElement>(
60
- "[data-category-rail] .category-pill",
61
- );
62
- for (const pill of pills) {
63
- const slug = pill.dataset.categorySlug ?? "";
64
- if (slug === activeSlug) pill.setAttribute("aria-current", "page");
65
- else pill.removeAttribute("aria-current");
66
- }
67
- }
68
- // Eager click feedback — flip aria-current before the network call.
69
- document.addEventListener("click", (event) => {
70
- const target = event.target as HTMLElement | null;
71
- const pill = target?.closest<HTMLAnchorElement>(
72
- "[data-category-rail] .category-pill",
73
- );
74
- if (!pill) return;
75
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
76
- const pills = document.querySelectorAll<HTMLAnchorElement>(
77
- "[data-category-rail] .category-pill",
78
- );
79
- for (const p of pills) p.removeAttribute("aria-current");
80
- pill.setAttribute("aria-current", "page");
81
- }, true);
82
- const progress = document.querySelector<HTMLElement>("[data-nav-progress]");
83
- function startProgress() {
84
- if (!progress) return;
85
- progress.dataset.state = "loading";
86
- document.body.dataset.navigating = "true";
87
- }
88
- function endProgress() {
89
- if (!progress) return;
90
- progress.dataset.state = "done";
91
- delete document.body.dataset.navigating;
92
- window.setTimeout(() => {
93
- if (progress.dataset.state === "done") delete progress.dataset.state;
94
- }, 320);
95
- }
96
- // Anchor the persisted rail across listing-to-listing navigation. The
97
- // page-header above the rail differs between /products and /category/[slug]
98
- // (breadcrumbs, lede, optional image), so naive scroll preservation reads
99
- // as a jump. Capture rail.getBoundingClientRect().top before the swap and
100
- // re-equalize after.
101
- const isListing = (p: string) =>
102
- p === "/products" || p === "/products/" || p.startsWith("/category/");
103
- let railAnchorTop: number | null = null;
104
- document.addEventListener("astro:before-preparation", (event: any) => {
105
- const from = window.location.pathname;
106
- const to = event?.to?.pathname ?? from;
107
- const rail = document.querySelector<HTMLElement>("[data-category-rail]");
108
- railAnchorTop =
109
- rail && isListing(from) && isListing(to)
110
- ? rail.getBoundingClientRect().top
111
- : null;
112
- startProgress();
113
- });
114
- document.addEventListener("astro:after-swap", () => {
115
- syncCategoryRail();
116
- if (railAnchorTop !== null) {
117
- const rail = document.querySelector<HTMLElement>("[data-category-rail]");
118
- if (rail) {
119
- const delta = rail.getBoundingClientRect().top - railAnchorTop;
120
- if (Math.abs(delta) > 0.5) {
121
- window.scrollBy({ top: delta, left: 0, behavior: "instant" as ScrollBehavior });
122
- }
123
- }
124
- }
125
- railAnchorTop = null;
126
- endProgress();
127
- });
128
- document.addEventListener("astro:page-load", endProgress);
129
- syncCategoryRail();
130
- </script>
131
- </body>
132
- </html>
@@ -1,41 +0,0 @@
1
- // ── Composer skeleton: astro.config.mjs ───────────────────────────────────────
2
- // UNLIKE the other skeletons, this one is a MERGE TARGET, not a clobber. The
3
- // scaffold's generated astro.config.mjs varies (integrations args, image.domains,
4
- // vite.server settings differ run to run). Composer must NOT overwrite it with
5
- // this file — it applies exactly two mutations to the scaffold's own config and
6
- // preserves everything else (see compose.mjs):
7
- //
8
- // (1) register the Tailwind v4 Vite plugin → vite.plugins: [tailwindcss()]
9
- // (2) fix the bare `process.env` reference → globalThis guard (TS-safe)
10
- //
11
- // The shape below is the canonical post-mutation result for a typical scaffold —
12
- // use it as the reference for what those two edits should produce.
13
- // @ts-check
14
- import { defineConfig } from "astro/config";
15
- import wix from "@wix/astro";
16
- import wixPages from "@wix/astro-pages";
17
- import react from "@astrojs/react";
18
- import cloudProviderFetchAdapter from "@wix/cloud-provider-fetch-adapter";
19
- import tailwindcss from "@tailwindcss/vite"; // (1) added by Composer
20
-
21
- // (2) `process.env` guarded so strict `tsc --noEmit` does not fail with
22
- // "Cannot find name 'process'" without an @types/node dependency.
23
- const isBuild =
24
- (/** @type {any} */ (globalThis)).process?.env?.NODE_ENV === "production";
25
-
26
- // https://astro.build/config
27
- export default defineConfig({
28
- integrations: [wix(), wixPages(), react()],
29
- security: { checkOrigin: false },
30
- ...(isBuild && { adapter: cloudProviderFetchAdapter({}) }),
31
-
32
- image: {
33
- domains: ["static.wixstatic.com"],
34
- },
35
-
36
- vite: {
37
- plugins: [tailwindcss()], // (1) merge into any existing vite config
38
- },
39
-
40
- output: "server",
41
- });
@@ -1,374 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
2
- import { availabilityTimeSlots, eventTimeSlots } from "@wix/bookings";
3
- import type { SelectedSlot } from "./bookingDriver";
4
- import { STAFF_MEMBER_RESOURCE_TYPE_ID } from "./bookingDriver";
5
-
6
- // AvailabilityCalendar.tsx — client:only="react" island. A week calendar: a
7
- // 7-day strip with week navigation, then the picked day's time slots. This is
8
- // the usable shape — a flat list of every slot with time-only labels leaves the
9
- // visitor unable to tell which day a slot is on.
10
- //
11
- // SDK calls run ambiently (the @wix/astro visitor client), like the ecom
12
- // CartView island — no createClient/OAuthStrategy here. On an own/own-build SPA
13
- // acquire a visitor client (OAuthStrategy) and call the same functions on it.
14
- //
15
- // Branches on serviceType:
16
- // APPOINTMENT → availabilityTimeSlots.listAvailabilityTimeSlots() (serviceId is
17
- // a single GUID string; slots carry scheduleId).
18
- // CLASS → eventTimeSlots.listEventTimeSlots() (serviceIds is an array;
19
- // slots carry eventInfo.eventId and NO scheduleId).
20
- //
21
- // LOCATION SCOPING (multi-location): listAvailabilityTimeSlots returns one slot
22
- // PER LOCATION per time — "if locations is not specified, returns time slots for
23
- // all locations where the service is available". A service offered at >1 location
24
- // therefore yields DUPLICATE same-time rows unless the call is scoped to a single
25
- // location. So availability is ALWAYS scoped to one business location: the picker
26
- // below defaults to the carried/first location and the call always passes a
27
- // single-element `locations` filter. (One location per availability call — the
28
- // same single-location guarantee the location-first SoT gets from its selector.)
29
- //
30
- // STAFF: a single slot carries multiple staff in availableResources, so staff is
31
- // a per-slot dropdown — it never multiplies rows. Only location multiplies rows.
32
- // Re-export SelectedSlot from bookingDriver so the form/driver share one shape.
33
- export type { SelectedSlot } from "./bookingDriver";
34
-
35
- // A staff member as carried on the service (service.staffMemberDetails.staffMembers[]),
36
- // fetched via conditionalFields:["STAFF_MEMBER_DETAILS"]. NOTE: the id field is
37
- // `staffMemberId` which — despite the name — IS the resource id (it matches the
38
- // service's staffMemberIds and the Staff Members API resourceId). That id is what
39
- // filters availability and books the resource.
40
- export interface StaffMemberOption {
41
- staffMemberId?: string;
42
- name?: string;
43
- }
44
-
45
- // A business location the service is offered at (derived from service.locations[]).
46
- export interface LocationOption {
47
- _id: string;
48
- name?: string;
49
- }
50
-
51
- interface Props {
52
- serviceId: string;
53
- serviceName: string;
54
- serviceType: "APPOINTMENT" | "CLASS";
55
- staffMembers?: StaffMemberOption[];
56
- locations?: LocationOption[]; // business locations the service offers
57
- locationId?: string; // the location chosen on the catalog (the default scope), if any
58
- onSlotSelected: (slot: SelectedSlot) => void;
59
- }
60
-
61
- interface FetchOpts {
62
- resourceId?: string; // a staff resource id to filter by (omit for "any staff")
63
- locationId?: string; // a business location id to scope to (omit only when the service has none)
64
- }
65
-
66
- const DAY_MS = 86_400_000;
67
- const HORIZON_WEEKS = 13; // how far "check next availability" probes forward
68
- const ANY_STAFF = ""; // the "any staff member" sentinel for the select value
69
-
70
- const pad = (n: number) => String(n).padStart(2, "0");
71
- // Local date string YYYY-MM-DDThh:mm:ss (no Z) — availability expects local time.
72
- const localDateString = (d: Date) =>
73
- `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
74
- const tz = () => Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
75
- const dateKey = (iso: string) => iso.slice(0, 10); // group slots by calendar day
76
- const timeLabel = (iso: string) =>
77
- new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
78
-
79
- const startOfDay = (d: Date) => { const n = new Date(d); n.setHours(0, 0, 0, 0); return n; };
80
- // Monday-start week containing `d`.
81
- const mondayOf = (d: Date) => {
82
- const n = startOfDay(d);
83
- const day = (n.getDay() + 6) % 7; // 0 = Monday
84
- n.setDate(n.getDate() - day);
85
- return n;
86
- };
87
-
88
- // Fetch one week of availability and group the slots by calendar day.
89
- const fetchWeek = async (
90
- serviceId: string,
91
- serviceType: "APPOINTMENT" | "CLASS",
92
- weekStart: Date,
93
- opts: FetchOpts = {},
94
- ): Promise<Record<string, SelectedSlot[]>> => {
95
- const from = startOfDay(weekStart);
96
- const to = new Date(from.getTime() + 7 * DAY_MS);
97
- const timeZone = tz();
98
- let raw: any[] = [];
99
-
100
- if (serviceType === "CLASS") {
101
- // CLASS filters via eventFilter: staff by resources.id ($hasSome), location by
102
- // location.id (a bare array, NOT a $hasSome operator).
103
- const eventFilter: Record<string, any> = {};
104
- if (opts.resourceId) eventFilter["resources.id"] = { $hasSome: [opts.resourceId] };
105
- if (opts.locationId) eventFilter["location.id"] = [opts.locationId];
106
- const result = await eventTimeSlots.listEventTimeSlots({
107
- serviceIds: [serviceId], // CLASS API takes an array
108
- fromLocalDate: localDateString(from),
109
- toLocalDate: localDateString(to),
110
- timeZone,
111
- includeNonBookable: false,
112
- cursorPaging: { limit: 100 },
113
- ...(Object.keys(eventFilter).length ? { eventFilter } : {}),
114
- });
115
- raw = (result.timeSlots ?? []).map((s: any) => ({
116
- serviceType: "CLASS" as const,
117
- serviceId,
118
- localStartDate: s.localStartDate,
119
- localEndDate: s.localEndDate,
120
- timezone: result.timeZone ?? timeZone,
121
- eventId: s.eventInfo?.eventId, // session id — no scheduleId on event slots
122
- }));
123
- } else {
124
- // APPOINTMENT filters staff via resourceTypes (resourceIds), location via locations[].
125
- // Scoping to a single location collapses the per-location duplicate rows.
126
- const result = await availabilityTimeSlots.listAvailabilityTimeSlots({
127
- serviceId, // single GUID string — NOT an array
128
- fromLocalDate: localDateString(from),
129
- toLocalDate: localDateString(to),
130
- timeZone,
131
- bookable: true,
132
- cursorPaging: { limit: 100 },
133
- ...(opts.resourceId
134
- ? {
135
- resourceTypes: [
136
- { resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID, resourceIds: [opts.resourceId] },
137
- ],
138
- includeResourceTypeIds: [STAFF_MEMBER_RESOURCE_TYPE_ID],
139
- }
140
- : {}),
141
- ...(opts.locationId
142
- ? { locations: [{ _id: opts.locationId, locationType: "BUSINESS" as const }] }
143
- : {}),
144
- });
145
- raw = (result.timeSlots ?? []).map((s: any) => ({
146
- serviceType: "APPOINTMENT" as const,
147
- serviceId,
148
- localStartDate: s.localStartDate,
149
- localEndDate: s.localEndDate,
150
- timezone: result.timeZone ?? timeZone,
151
- scheduleId: s.scheduleId,
152
- locationId: s.location?._id, // the id field is _id, not .id
153
- locationType: s.location?.locationType,
154
- }));
155
- }
156
-
157
- const byDay: Record<string, SelectedSlot[]> = {};
158
- for (const slot of raw) {
159
- if (!slot.localStartDate) continue;
160
- (byDay[dateKey(slot.localStartDate)] ||= []).push(slot);
161
- }
162
- return byDay;
163
- };
164
-
165
- export default function AvailabilityCalendar({
166
- serviceId,
167
- serviceName,
168
- serviceType,
169
- staffMembers,
170
- locations,
171
- locationId,
172
- onSlotSelected,
173
- }: Props) {
174
- const thisMonday = useMemo(() => mondayOf(new Date()), []);
175
- const [weekStart, setWeekStart] = useState<Date>(thisMonday);
176
- const [byDay, setByDay] = useState<Record<string, SelectedSlot[]>>({});
177
- const [selectedDay, setSelectedDay] = useState<string | null>(null);
178
- const [selectedKey, setSelectedKey] = useState<string | null>(null);
179
- const [status, setStatus] = useState<"loading" | "ready" | "error" | "searching">("loading");
180
- // The chosen staff resource id ("" = any staff member → ANY_RESOURCE fallback).
181
- const [selectedResource, setSelectedResource] = useState<string>(ANY_STAFF);
182
-
183
- // Staff options keyed by resource id; picker shown only when >1 staff.
184
- const staffOptions = useMemo(
185
- () =>
186
- (staffMembers ?? [])
187
- .map((s) => ({ id: s.staffMemberId ?? "", name: s.name ?? "Staff member" }))
188
- .filter((s) => s.id),
189
- [staffMembers],
190
- );
191
- const showStaffPicker = staffOptions.length > 1;
192
-
193
- // Business-location options. Availability is always scoped to ONE of these (the
194
- // carried location if it's one of them, else the first), so multi-location
195
- // services don't return duplicate per-location rows. Picker shown only when >1.
196
- const locationOptions = useMemo(
197
- () => (locations ?? []).filter((l) => l._id),
198
- [locations],
199
- );
200
- const [selectedLocation, setSelectedLocation] = useState<string>(() => {
201
- if (locationId && locationOptions.some((l) => l._id === locationId)) return locationId;
202
- return locationOptions[0]?._id ?? "";
203
- });
204
- const showLocationPicker = locationOptions.length > 1;
205
-
206
- const days = useMemo(
207
- () => Array.from({ length: 7 }, (_, i) => new Date(weekStart.getTime() + i * DAY_MS)),
208
- [weekStart],
209
- );
210
- const atFirstWeek = weekStart.getTime() <= thisMonday.getTime();
211
-
212
- const fetchOpts = useCallback(
213
- (): FetchOpts => ({
214
- resourceId: selectedResource || undefined,
215
- locationId: selectedLocation || undefined,
216
- }),
217
- [selectedResource, selectedLocation],
218
- );
219
-
220
- const load = useCallback(async (start: Date) => {
221
- setStatus("loading"); setSelectedKey(null);
222
- try {
223
- const grouped = await fetchWeek(serviceId, serviceType, start, fetchOpts());
224
- setByDay(grouped);
225
- const firstDayWithSlots = Object.keys(grouped).sort()[0] ?? null;
226
- setSelectedDay(firstDayWithSlots);
227
- setStatus("ready");
228
- } catch (err) {
229
- console.error("[availability] list failed:", err);
230
- setStatus("error");
231
- }
232
- }, [serviceId, serviceType, fetchOpts]);
233
-
234
- // Reload whenever the week OR the staff/location filter changes.
235
- useEffect(() => { void load(weekStart); }, [weekStart, load]);
236
-
237
- const shiftWeek = (deltaWeeks: number) =>
238
- setWeekStart((prev) => startOfDay(new Date(prev.getTime() + deltaWeeks * 7 * DAY_MS)));
239
-
240
- // Probe forward week-by-week for the next week that has any slots.
241
- const checkNextAvailability = useCallback(async () => {
242
- setStatus("searching");
243
- let cursor = new Date(weekStart.getTime() + 7 * DAY_MS);
244
- for (let i = 0; i < HORIZON_WEEKS; i++) {
245
- try {
246
- const grouped = await fetchWeek(serviceId, serviceType, cursor, fetchOpts());
247
- if (Object.keys(grouped).length > 0) {
248
- setWeekStart(cursor); // triggers load() via the effect
249
- return;
250
- }
251
- } catch { /* keep probing */ }
252
- cursor = new Date(cursor.getTime() + 7 * DAY_MS);
253
- }
254
- setStatus("ready"); // nothing found within the horizon
255
- }, [serviceId, serviceType, weekStart, fetchOpts]);
256
-
257
- const handleSelect = (s: SelectedSlot) => {
258
- const id = s.serviceType === "CLASS" ? s.eventId : s.scheduleId;
259
- if (!s.localStartDate || !id) return;
260
- setSelectedKey(`${s.localStartDate}|${id}`);
261
- // When a specific staff was chosen, record it on the slot so the booking books
262
- // that resource; otherwise the driver emits the ANY_RESOURCE fallback.
263
- const chosen = selectedResource
264
- ? staffOptions.find((o) => o.id === selectedResource)
265
- : null;
266
- onSlotSelected(chosen ? { ...s, resource: { _id: chosen.id, name: chosen.name } } : s);
267
- };
268
-
269
- const weekLabel = `${days[0].toLocaleDateString([], { month: "short", day: "numeric" })} – ${days[6].toLocaleDateString([], { month: "short", day: "numeric" })}`;
270
- const daySlots = selectedDay ? byDay[selectedDay] ?? [] : [];
271
- const weekIsEmpty = status === "ready" && Object.keys(byDay).length === 0;
272
-
273
- return (
274
- <div className="availability-calendar">
275
- {(showLocationPicker || showStaffPicker) && (
276
- <div className="availability-filters">
277
- {showLocationPicker && (
278
- <div className="availability-staff">
279
- <label className="availability-staff-label" htmlFor="location-select">Location</label>
280
- <select
281
- id="location-select"
282
- className="availability-staff-select"
283
- value={selectedLocation}
284
- onChange={(e) => { setSelectedLocation(e.target.value); setSelectedKey(null); }}
285
- >
286
- {locationOptions.map((o) => (
287
- <option key={o._id} value={o._id}>{o.name ?? "Location"}</option>
288
- ))}
289
- </select>
290
- </div>
291
- )}
292
- {showStaffPicker && (
293
- <div className="availability-staff">
294
- <label className="availability-staff-label" htmlFor="staff-select">Staff member</label>
295
- <select
296
- id="staff-select"
297
- className="availability-staff-select"
298
- value={selectedResource}
299
- onChange={(e) => { setSelectedResource(e.target.value); setSelectedKey(null); }}
300
- >
301
- <option value={ANY_STAFF}>Any staff member</option>
302
- {staffOptions.map((o) => (
303
- <option key={o.id} value={o.id}>{o.name}</option>
304
- ))}
305
- </select>
306
- </div>
307
- )}
308
- </div>
309
- )}
310
-
311
- <div className="availability-week-nav">
312
- <button type="button" className="availability-nav-btn" onClick={() => shiftWeek(-1)} disabled={atFirstWeek} aria-label="Previous week">← Prev week</button>
313
- <span className="availability-week-label">{weekLabel}</span>
314
- <button type="button" className="availability-nav-btn" onClick={() => shiftWeek(1)} aria-label="Next week">Next week →</button>
315
- </div>
316
-
317
- <div className="availability-day-strip" role="group" aria-label="Pick a day">
318
- {days.map((d) => {
319
- const key = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
320
- const has = (byDay[key]?.length ?? 0) > 0;
321
- const isSelected = key === selectedDay;
322
- return (
323
- <button
324
- key={key}
325
- type="button"
326
- className={["availability-day", isSelected ? "availability-day--selected" : "", has ? "availability-day--has-slots" : "availability-day--empty"].filter(Boolean).join(" ")}
327
- disabled={!has}
328
- aria-pressed={isSelected}
329
- onClick={() => { setSelectedDay(key); setSelectedKey(null); }}
330
- >
331
- <span className="availability-day-name">{d.toLocaleDateString([], { weekday: "short" })}</span>
332
- <span className="availability-day-num">{d.getDate()}</span>
333
- </button>
334
- );
335
- })}
336
- </div>
337
-
338
- {status === "loading" && <p className="availability-loading">Checking availability…</p>}
339
- {status === "searching" && <p className="availability-loading">Searching for the next available time…</p>}
340
- {status === "error" && (
341
- <>
342
- <p className="availability-error">Could not load availability — please try again.</p>
343
- <button type="button" className="availability-nav-btn" onClick={() => void load(weekStart)}>Retry</button>
344
- </>
345
- )}
346
- {weekIsEmpty && (
347
- <div className="availability-empty">
348
- <p>No availability this week.</p>
349
- <button type="button" className="availability-nav-btn" onClick={() => void checkNextAvailability()}>Check next availability</button>
350
- </div>
351
- )}
352
- {status === "ready" && daySlots.length > 0 && (
353
- <div className="availability-slots" role="group" aria-label={`Available times for ${serviceName}`}>
354
- {daySlots.map((s) => {
355
- const id = s.serviceType === "CLASS" ? s.eventId : s.scheduleId;
356
- const key = `${s.localStartDate}|${id}`;
357
- const isSelected = key === selectedKey;
358
- return (
359
- <button
360
- key={key}
361
- type="button"
362
- className={["time-slot", isSelected ? "time-slot--selected" : "time-slot--available"].join(" ")}
363
- aria-pressed={isSelected}
364
- onClick={() => handleSelect(s)}
365
- >
366
- <span className="time-slot-time">{timeLabel(s.localStartDate)}</span>
367
- </button>
368
- );
369
- })}
370
- </div>
371
- )}
372
- </div>
373
- );
374
- }