themelia-react-ui 2.0.3 → 2.0.4

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 (421) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +65 -28
  3. package/dist/{accordion-Cjb6sx0G.js → accordion-96o0pN62.js} +5 -5
  4. package/dist/{accordion-Cjb6sx0G.js.map → accordion-96o0pN62.js.map} +1 -1
  5. package/dist/action-menu-CHgs8vmj.js +281 -0
  6. package/dist/action-menu-CHgs8vmj.js.map +1 -0
  7. package/dist/{affordance-geometry.generated-tF36bREk.js → affordance-geometry.generated-B8yGOr8-.js} +7 -1
  8. package/dist/affordance-geometry.generated-B8yGOr8-.js.map +1 -0
  9. package/dist/{ai-artifact-DvTwDVyL.js → ai-artifact-BZUGwBq6.js} +277 -276
  10. package/dist/ai-artifact-BZUGwBq6.js.map +1 -0
  11. package/dist/{app-shell-C4FhvCqF.js → app-shell-D63ATkIK.js} +84 -83
  12. package/dist/app-shell-D63ATkIK.js.map +1 -0
  13. package/dist/{attachment-BxDfL2mw.js → attachment-DkoqHPPl.js} +7 -7
  14. package/dist/{attachment-BxDfL2mw.js.map → attachment-DkoqHPPl.js.map} +1 -1
  15. package/dist/{auth-BGua8ouh.js → auth-D2FVZ1MH.js} +10 -10
  16. package/dist/{auth-BGua8ouh.js.map → auth-D2FVZ1MH.js.map} +1 -1
  17. package/dist/{avatar-DAPlDvhJ.js → avatar-DWO_HHhB.js} +5 -5
  18. package/dist/{avatar-DAPlDvhJ.js.map → avatar-DWO_HHhB.js.map} +1 -1
  19. package/dist/{badge-HpFqoqSY.js → badge-DErMA7ul.js} +4 -4
  20. package/dist/{badge-HpFqoqSY.js.map → badge-DErMA7ul.js.map} +1 -1
  21. package/dist/base/accordion.js +1 -1
  22. package/dist/base/action-menu.js +1 -1
  23. package/dist/base/attachment.js +1 -1
  24. package/dist/base/avatar.js +1 -1
  25. package/dist/base/badge.js +1 -1
  26. package/dist/base/batch-action-bar.js +1 -1
  27. package/dist/base/buttons.js +1 -1
  28. package/dist/base/cards.js +2 -2
  29. package/dist/base/carousel.js +3 -3
  30. package/dist/base/cascader.js +13 -13
  31. package/dist/base/chart.js +1 -1
  32. package/dist/base/choice-inputs.js +2 -2
  33. package/dist/base/code-block.js +1 -1
  34. package/dist/base/combobox.js +1 -1
  35. package/dist/base/command.js +1 -1
  36. package/dist/base/copyable.js +1 -1
  37. package/dist/base/date-pickers.js +2 -2
  38. package/dist/base/display.js +6 -6
  39. package/dist/base/feedback.js +3 -3
  40. package/dist/base/forms-numeric.js +1 -1
  41. package/dist/base/forms.js +3 -3
  42. package/dist/base/inline-edit.js +10 -10
  43. package/dist/base/item.js +1 -1
  44. package/dist/base/media-gallery.js +1 -1
  45. package/dist/base/metadata-list.js +1 -1
  46. package/dist/base/navigation.js +6 -6
  47. package/dist/base/popover-menu.js +1 -1
  48. package/dist/base/popover.js +1 -1
  49. package/dist/base/qr-code.js +1 -1
  50. package/dist/base/repeaters.js +9 -9
  51. package/dist/base/sheet.js +1 -1
  52. package/dist/base/sidebar.js +1 -1
  53. package/dist/base/signature-pad.js +6 -6
  54. package/dist/base/skeleton.js +1 -1
  55. package/dist/base/spinner.js +4 -4
  56. package/dist/base/structure.js +1 -1
  57. package/dist/base/table.js +1 -1
  58. package/dist/base/text-inputs.js +2 -2
  59. package/dist/base/toaster.js +3 -3
  60. package/dist/base/tree.js +1 -1
  61. package/dist/base/upload-queue.js +1 -1
  62. package/dist/base/upload.js +3 -3
  63. package/dist/base/value-inputs.js +1 -1
  64. package/dist/{batch-action-bar-DnUJ21NQ.js → batch-action-bar-CdfIkCsV.js} +4 -4
  65. package/dist/{batch-action-bar-DnUJ21NQ.js.map → batch-action-bar-CdfIkCsV.js.map} +1 -1
  66. package/dist/blocks/admin.js +20 -20
  67. package/dist/blocks/admin.js.map +1 -1
  68. package/dist/blocks/ai.js +15 -15
  69. package/dist/blocks/analytics/usage-card.js +1 -1
  70. package/dist/blocks/analytics.js +17 -17
  71. package/dist/blocks/analytics.js.map +1 -1
  72. package/dist/blocks/collaboration.js +1 -1
  73. package/dist/blocks/commerce/receipt.js +1 -1
  74. package/dist/blocks/commerce.js +570 -567
  75. package/dist/blocks/commerce.js.map +1 -1
  76. package/dist/blocks/data-display.js +2 -2
  77. package/dist/blocks/onboarding.js +5 -5
  78. package/dist/blocks/planning.js +11 -11
  79. package/dist/blocks/timelines.js +1 -1
  80. package/dist/{breadcrumbs-C-oqs1sQ.js → breadcrumbs-B-WFKecD.js} +4 -4
  81. package/dist/{breadcrumbs-C-oqs1sQ.js.map → breadcrumbs-B-WFKecD.js.map} +1 -1
  82. package/dist/{button-DVzspB_G.js → button-DkmK-z8E.js} +2 -2
  83. package/dist/{button-DVzspB_G.js.map → button-DkmK-z8E.js.map} +1 -1
  84. package/dist/{buttons-DPZekJFu.js → buttons-BbfzgczK.js} +8 -8
  85. package/dist/{buttons-DPZekJFu.js.map → buttons-BbfzgczK.js.map} +1 -1
  86. package/dist/{cards-BaBZR-AU.js → cards-4JTWqvVe.js} +10 -10
  87. package/dist/cards-4JTWqvVe.js.map +1 -0
  88. package/dist/{chart-ZOYyK2xG.js → chart-C19flDQb.js} +4 -4
  89. package/dist/{chart-ZOYyK2xG.js.map → chart-C19flDQb.js.map} +1 -1
  90. package/dist/{checkbox-AWIhNhmo.js → checkbox-CzYUTPeJ.js} +9 -5
  91. package/dist/checkbox-CzYUTPeJ.js.map +1 -0
  92. package/dist/{choice-inputs-CNHvARCJ.js → choice-inputs-JLtij2y4.js} +182 -176
  93. package/dist/choice-inputs-JLtij2y4.js.map +1 -0
  94. package/dist/{code-block-DeneBJR_.js → code-block-CGNaUtAE.js} +7 -7
  95. package/dist/{code-block-DeneBJR_.js.map → code-block-CGNaUtAE.js.map} +1 -1
  96. package/dist/{collapsible-C-_l4hpy.js → collapsible-Do4G0zcC.js} +3 -3
  97. package/dist/{collapsible-C-_l4hpy.js.map → collapsible-Do4G0zcC.js.map} +1 -1
  98. package/dist/{collection-content-By56pLog.js → collection-content-C0e8SsTm.js} +3 -3
  99. package/dist/{collection-content-By56pLog.js.map → collection-content-C0e8SsTm.js.map} +1 -1
  100. package/dist/{combobox-Dxo2fAYQ.js → combobox-CBJDs6w_.js} +12 -12
  101. package/dist/{combobox-Dxo2fAYQ.js.map → combobox-CBJDs6w_.js.map} +1 -1
  102. package/dist/{combobox-DaHESuiL.js → combobox-hbhSS18O.js} +7 -7
  103. package/dist/{combobox-DaHESuiL.js.map → combobox-hbhSS18O.js.map} +1 -1
  104. package/dist/{command-C25VMRII.js → command-D8NrBj-b.js} +7 -7
  105. package/dist/{command-C25VMRII.js.map → command-D8NrBj-b.js.map} +1 -1
  106. package/dist/{comments-DD0DUPwJ.js → comments-Di4w0H_S.js} +383 -382
  107. package/dist/comments-Di4w0H_S.js.map +1 -0
  108. package/dist/components/base/action-menu/action-menu.d.ts +2 -0
  109. package/dist/components/base/cards/card.recipes.d.ts +12 -5
  110. package/dist/components/base/cards/smart-card.types.d.ts +12 -7
  111. package/dist/components/base/choice-inputs/checkbox.d.ts +7 -2
  112. package/dist/components/base/choice-inputs/switch-card.d.ts +7 -0
  113. package/dist/components/base/choice-inputs/toggle-field.d.ts +7 -0
  114. package/dist/components/base/date-pickers/datepicker.types.d.ts +7 -0
  115. package/dist/components/base/forms/controlled-form-field.d.ts +6 -6
  116. package/dist/components/base/forms/form-field.d.ts +5 -3
  117. package/dist/components/base/forms/index.d.ts +1 -0
  118. package/dist/components/base/forms/server-errors.d.ts +16 -0
  119. package/dist/components/base/forms/workflow.d.ts +5 -2
  120. package/dist/components/base/metadata-list/metadata-list.d.ts +3 -1
  121. package/dist/components/base/navigation/page-header/page-header.d.ts +4 -2
  122. package/dist/components/base/structure/composition.recipes.d.ts +17 -0
  123. package/dist/components/base/text-inputs/hooks/use-form-field-state.d.ts +7 -2
  124. package/dist/components/base/text-inputs/input.d.ts +4 -2
  125. package/dist/components/base/text-inputs/textarea.d.ts +4 -2
  126. package/dist/components/base/value-inputs/date-time-input.d.ts +2 -1
  127. package/dist/components/base/value-inputs/slider.d.ts +5 -1
  128. package/dist/components/base/value-inputs/tags-input.d.ts +5 -1
  129. package/dist/components/typography/text.styles.d.ts +26 -1
  130. package/dist/composition.recipes-COdiFhxB.js +19 -0
  131. package/dist/composition.recipes-COdiFhxB.js.map +1 -0
  132. package/dist/containers-CRmHf7GP.js +104 -0
  133. package/dist/containers-CRmHf7GP.js.map +1 -0
  134. package/dist/{copyable-ClBuk_UK.js → copyable-CnGCZdX4.js} +4 -4
  135. package/dist/{copyable-ClBuk_UK.js.map → copyable-CnGCZdX4.js.map} +1 -1
  136. package/dist/{data-display-DT_khPl_.js → data-display-D4oujtx0.js} +239 -238
  137. package/dist/data-display-D4oujtx0.js.map +1 -0
  138. package/dist/{date-DCrsDTZc.js → date-Q7jnXxpk.js} +5 -5
  139. package/dist/{date-DCrsDTZc.js.map → date-Q7jnXxpk.js.map} +1 -1
  140. package/dist/{date-picker-B2K3ucWn.js → date-picker-zOt1oNss.js} +102 -90
  141. package/dist/date-picker-zOt1oNss.js.map +1 -0
  142. package/dist/date-pickers-cj8IXbDC.js +179 -0
  143. package/dist/date-pickers-cj8IXbDC.js.map +1 -0
  144. package/dist/{display-hAc1HjpI.js → display-BB4BL_-K.js} +8 -8
  145. package/dist/{display-hAc1HjpI.js.map → display-BB4BL_-K.js.map} +1 -1
  146. package/dist/{dropdown-menu-Cl4X7L5S.js → dropdown-menu-CjY8Vt7W.js} +8 -8
  147. package/dist/{dropdown-menu-Cl4X7L5S.js.map → dropdown-menu-CjY8Vt7W.js.map} +1 -1
  148. package/dist/features/activities.js +11 -11
  149. package/dist/features/ai-chat.js +9 -9
  150. package/dist/features/appointment-calendar.js +222 -220
  151. package/dist/features/appointment-calendar.js.map +1 -1
  152. package/dist/features/async-preview.js +8 -8
  153. package/dist/features/combobox.js +1 -1
  154. package/dist/features/comments.js +1 -1
  155. package/dist/features/data-view.js +10 -10
  156. package/dist/features/email-composer.js +275 -274
  157. package/dist/features/email-composer.js.map +1 -1
  158. package/dist/features/event-calendar.js +199 -198
  159. package/dist/features/event-calendar.js.map +1 -1
  160. package/dist/features/filters.js +1 -1
  161. package/dist/features/global-search.js +12 -12
  162. package/dist/features/kanban.js +4 -4
  163. package/dist/features/map.js +7 -7
  164. package/dist/features/media-library.js +623 -622
  165. package/dist/features/media-library.js.map +1 -1
  166. package/dist/features/mentions.js +1 -1
  167. package/dist/features/overlays.js +1 -1
  168. package/dist/features/products.js +973 -969
  169. package/dist/features/products.js.map +1 -1
  170. package/dist/features/questionnaire.js +12 -12
  171. package/dist/features/resource-assignment.js +8 -8
  172. package/dist/features/resource.js +113 -112
  173. package/dist/features/resource.js.map +1 -1
  174. package/dist/features/rich-text-editor.js +2 -2
  175. package/dist/features/schema-form.js +254 -253
  176. package/dist/features/schema-form.js.map +1 -1
  177. package/dist/features/suggestions.js +3 -3
  178. package/dist/features/sync.js +3 -3
  179. package/dist/features/table.js +1 -1
  180. package/dist/features/theme-tweaker.js +19 -19
  181. package/dist/features/theme-tweaker.js.map +1 -1
  182. package/dist/{feedback-BYIhtitP.js → feedback-DZNL5Lg1.js} +9 -9
  183. package/dist/{feedback-BYIhtitP.js.map → feedback-DZNL5Lg1.js.map} +1 -1
  184. package/dist/{field-affordance-recipes-DZhnIL_Y.js → field-affordance-recipes-DjwGx8eM.js} +2 -2
  185. package/dist/{field-affordance-recipes-DZhnIL_Y.js.map → field-affordance-recipes-DjwGx8eM.js.map} +1 -1
  186. package/dist/{filters-DJVAveHA.js → filters-BcT-uoFy.js} +19 -19
  187. package/dist/{filters-DJVAveHA.js.map → filters-BcT-uoFy.js.map} +1 -1
  188. package/dist/{form-field-CwH9y77i.js → form-field-CsgRmdit.js} +14 -8
  189. package/dist/form-field-CsgRmdit.js.map +1 -0
  190. package/dist/{form-sizing-BdP_Mf5p.js → form-sizing-NXgjkmBG.js} +3 -3
  191. package/dist/{form-sizing-BdP_Mf5p.js.map → form-sizing-NXgjkmBG.js.map} +1 -1
  192. package/dist/{forms-B9G_xwzi.js → forms-BV5m5aQF.js} +83 -65
  193. package/dist/forms-BV5m5aQF.js.map +1 -0
  194. package/dist/{forms-numeric-B_jraJaQ.js → forms-numeric-Cd33AKbD.js} +288 -288
  195. package/dist/forms-numeric-Cd33AKbD.js.map +1 -0
  196. package/dist/{header-BSfIEWbI.js → header-BA697l4a.js} +14 -14
  197. package/dist/{header-BSfIEWbI.js.map → header-BA697l4a.js.map} +1 -1
  198. package/dist/hooks/useFormErrors.d.ts +1 -1
  199. package/dist/{hooks-ClH9thn4.js → hooks-BQ-L7Uu2.js} +2 -2
  200. package/dist/{hooks-ClH9thn4.js.map → hooks-BQ-L7Uu2.js.map} +1 -1
  201. package/dist/{icon-badge-DYKIwvOV.js → icon-badge-COWbCCWu.js} +3 -3
  202. package/dist/{icon-badge-DYKIwvOV.js.map → icon-badge-COWbCCWu.js.map} +1 -1
  203. package/dist/index.js +2 -2
  204. package/dist/input-Cdw4k0fb.js +289 -0
  205. package/dist/input-Cdw4k0fb.js.map +1 -0
  206. package/dist/{input-CZw8iU99.js → input-DOPL5_QE.js} +3 -3
  207. package/dist/{input-CZw8iU99.js.map → input-DOPL5_QE.js.map} +1 -1
  208. package/dist/item-BvZ2DR_x.js +277 -0
  209. package/dist/item-BvZ2DR_x.js.map +1 -0
  210. package/dist/layout/app-shell.js +1 -1
  211. package/dist/layout/auth.js +1 -1
  212. package/dist/layout/containers.js +1 -1
  213. package/dist/layout/header.js +1 -1
  214. package/dist/layout/hooks.js +1 -1
  215. package/dist/layout/navigation.js +1 -1
  216. package/dist/layout/page.js +1 -1
  217. package/dist/layout/settings.js +1 -1
  218. package/dist/layout/sidebar.js +2 -2
  219. package/dist/layout/workspace.js +1 -1
  220. package/dist/layout.js +10 -10
  221. package/dist/lib/affordance-geometry.generated.d.ts +6 -0
  222. package/dist/lib/ui-provider/types.d.ts +1 -1
  223. package/dist/lib/utils.js +1 -1
  224. package/dist/{media-gallery-3zIVLMWH.js → media-gallery-CSl5W1Fy.js} +6 -6
  225. package/dist/{media-gallery-3zIVLMWH.js.map → media-gallery-CSl5W1Fy.js.map} +1 -1
  226. package/dist/{mentions-CGlEJ15c.js → mentions-RKOQaaIB.js} +8 -8
  227. package/dist/{mentions-CGlEJ15c.js.map → mentions-RKOQaaIB.js.map} +1 -1
  228. package/dist/{message-DZ_ruyUV.js → message-INU2xsxu.js} +4 -4
  229. package/dist/{message-DZ_ruyUV.js.map → message-INU2xsxu.js.map} +1 -1
  230. package/dist/metadata-list-qVV7AVxa.js +443 -0
  231. package/dist/metadata-list-qVV7AVxa.js.map +1 -0
  232. package/dist/{navigation-BzhHI3lo.js → navigation-BQNcg1jT.js} +8 -8
  233. package/dist/{navigation-BzhHI3lo.js.map → navigation-BQNcg1jT.js.map} +1 -1
  234. package/dist/{navigation-Bnotn4AQ.js → navigation-DLF3E1IC.js} +7 -7
  235. package/dist/{navigation-Bnotn4AQ.js.map → navigation-DLF3E1IC.js.map} +1 -1
  236. package/dist/{navigation-row-recipes-BFLCk_Aq.js → navigation-row-recipes-WZEs-hYo.js} +2 -2
  237. package/dist/{navigation-row-recipes-BFLCk_Aq.js.map → navigation-row-recipes-WZEs-hYo.js.map} +1 -1
  238. package/dist/{number-DbV1Dija.js → number-E_OiIfJz.js} +5 -5
  239. package/dist/{number-DbV1Dija.js.map → number-E_OiIfJz.js.map} +1 -1
  240. package/dist/{overflow-tab-bar-CWY7Nogn.js → overflow-tab-bar-CrbqMteP.js} +107 -105
  241. package/dist/overflow-tab-bar-CrbqMteP.js.map +1 -0
  242. package/dist/{overlays-tMkF-VAN.js → overlays-BlKynmde.js} +228 -227
  243. package/dist/overlays-BlKynmde.js.map +1 -0
  244. package/dist/page-CIwNUtGr.js +227 -0
  245. package/dist/page-CIwNUtGr.js.map +1 -0
  246. package/dist/page-header-commands-DFMlNYzN.js +128 -0
  247. package/dist/page-header-commands-DFMlNYzN.js.map +1 -0
  248. package/dist/{pagination-BL25hu2s.js → pagination-Ch8X_47u.js} +5 -5
  249. package/dist/{pagination-BL25hu2s.js.map → pagination-Ch8X_47u.js.map} +1 -1
  250. package/dist/{popover-menu-Cn9rUkdc.js → popover-menu-BYaCdFhf.js} +6 -6
  251. package/dist/{popover-menu-Cn9rUkdc.js.map → popover-menu-BYaCdFhf.js.map} +1 -1
  252. package/dist/{popover-gnTOhbFM.js → popover-uwzX2vo7.js} +4 -4
  253. package/dist/{popover-gnTOhbFM.js.map → popover-uwzX2vo7.js.map} +1 -1
  254. package/dist/{primitives-BFK08r9M.js → primitives-Du64iDbb.js} +6 -6
  255. package/dist/{primitives-BFK08r9M.js.map → primitives-Du64iDbb.js.map} +1 -1
  256. package/dist/primitives.js +4 -4
  257. package/dist/{progress-B1VnCT0K.js → progress-BvF2oCdV.js} +5 -5
  258. package/dist/{progress-B1VnCT0K.js.map → progress-BvF2oCdV.js.map} +1 -1
  259. package/dist/{receipt-BVSOH3Xo.js → receipt-D789Jwyy.js} +6 -6
  260. package/dist/{receipt-BVSOH3Xo.js.map → receipt-D789Jwyy.js.map} +1 -1
  261. package/dist/{rich-text-GOUyKQ-s.js → rich-text-CLJEf76s.js} +4 -4
  262. package/dist/{rich-text-GOUyKQ-s.js.map → rich-text-CLJEf76s.js.map} +1 -1
  263. package/dist/{rich-text-editor-cRLC-eBK.js → rich-text-editor-Cm1dpUpQ.js} +10 -10
  264. package/dist/{rich-text-editor-cRLC-eBK.js.map → rich-text-editor-Cm1dpUpQ.js.map} +1 -1
  265. package/dist/{rich-text-editor-toolbar-DfXg0-VC.js → rich-text-editor-toolbar-L__e4x47.js} +4 -4
  266. package/dist/{rich-text-editor-toolbar-DfXg0-VC.js.map → rich-text-editor-toolbar-L__e4x47.js.map} +1 -1
  267. package/dist/{scroll-area-DcI-M1UT.js → scroll-area-La5RXsEk.js} +2 -2
  268. package/dist/{scroll-area-DcI-M1UT.js.map → scroll-area-La5RXsEk.js.map} +1 -1
  269. package/dist/{separator-Bom7NsGO.js → separator-DAEV0w65.js} +2 -2
  270. package/dist/{separator-Bom7NsGO.js.map → separator-DAEV0w65.js.map} +1 -1
  271. package/dist/{separator-BDV-Y648.js → separator-GNfPG-90.js} +3 -3
  272. package/dist/{separator-BDV-Y648.js.map → separator-GNfPG-90.js.map} +1 -1
  273. package/dist/services/actions.js +1 -1
  274. package/dist/settings-BA2iboic.js +174 -0
  275. package/dist/settings-BA2iboic.js.map +1 -0
  276. package/dist/{sheet-ijlsjq-n.js → sheet-DoAxTb2J.js} +7 -7
  277. package/dist/{sheet-ijlsjq-n.js.map → sheet-DoAxTb2J.js.map} +1 -1
  278. package/dist/{sidebar-DkTO4VTO.js → sidebar-ByqKR_W5.js} +13 -13
  279. package/dist/{sidebar-DkTO4VTO.js.map → sidebar-ByqKR_W5.js.map} +1 -1
  280. package/dist/{sidebar-Db6gkh3U.js → sidebar-D2iSnGJ7.js} +11 -11
  281. package/dist/{sidebar-Db6gkh3U.js.map → sidebar-D2iSnGJ7.js.map} +1 -1
  282. package/dist/{skeleton-Byjnt7qr.js → skeleton-CUj8O5Bd.js} +3 -3
  283. package/dist/{skeleton-Byjnt7qr.js.map → skeleton-CUj8O5Bd.js.map} +1 -1
  284. package/dist/smart-card-CqkzQsK-.js +614 -0
  285. package/dist/smart-card-CqkzQsK-.js.map +1 -0
  286. package/dist/{structure-BmisBK-3.js → structure-C0V0_Fyf.js} +3 -3
  287. package/dist/{structure-BmisBK-3.js.map → structure-C0V0_Fyf.js.map} +1 -1
  288. package/dist/{structure.recipes-EPz60cU8.js → structure.recipes-BB4-n58V.js} +2 -2
  289. package/dist/{structure.recipes-EPz60cU8.js.map → structure.recipes-BB4-n58V.js.map} +1 -1
  290. package/dist/style.css +1 -1
  291. package/dist/{table-DsfzoUia.js → table-CpvnzJn9.js} +3 -3
  292. package/dist/{table-DsfzoUia.js.map → table-CpvnzJn9.js.map} +1 -1
  293. package/dist/{table-BweFy_P9.js → table-DH7rozUv.js} +19 -19
  294. package/dist/{table-BweFy_P9.js.map → table-DH7rozUv.js.map} +1 -1
  295. package/dist/{tabs-CDocW5D1.js → tabs-eqVsJZWO.js} +7 -7
  296. package/dist/{tabs-CDocW5D1.js.map → tabs-eqVsJZWO.js.map} +1 -1
  297. package/dist/{text-inputs-f9Ia-aCi.js → text-inputs-DXItiR0O.js} +101 -98
  298. package/dist/text-inputs-DXItiR0O.js.map +1 -0
  299. package/dist/{text.styles-g4He6UIW.js → text.styles-B64My8RZ.js} +25 -10
  300. package/dist/text.styles-B64My8RZ.js.map +1 -0
  301. package/dist/theme.css +152 -112
  302. package/dist/{timelines-CwM_9IhD.js → timelines-DmtztAY_.js} +10 -10
  303. package/dist/{timelines-CwM_9IhD.js.map → timelines-DmtztAY_.js.map} +1 -1
  304. package/dist/{tooltip-yiRJUFjp.js → tooltip-B4hjKtoV.js} +3 -3
  305. package/dist/{tooltip-yiRJUFjp.js.map → tooltip-B4hjKtoV.js.map} +1 -1
  306. package/dist/{tree-WnKQzwbq.js → tree-CV11PURP.js} +8 -8
  307. package/dist/{tree-WnKQzwbq.js.map → tree-CV11PURP.js.map} +1 -1
  308. package/dist/typography/rich-text.js +1 -1
  309. package/dist/typography-CSyJrh28.js +117 -0
  310. package/dist/typography-CSyJrh28.js.map +1 -0
  311. package/dist/typography.js +1 -1
  312. package/dist/{ui-provider-C_U0Xma4.js → ui-provider-CQCuCBcy.js} +3 -3
  313. package/dist/ui-provider-CQCuCBcy.js.map +1 -0
  314. package/dist/ui-provider.js +1 -1
  315. package/dist/{upload-CXyxmQBP.js → upload-C9SA0X-8.js} +11 -11
  316. package/dist/{upload-CXyxmQBP.js.map → upload-C9SA0X-8.js.map} +1 -1
  317. package/dist/{upload-queue-Boscubn9.js → upload-queue-DhQJiSRc.js} +9 -9
  318. package/dist/{upload-queue-Boscubn9.js.map → upload-queue-DhQJiSRc.js.map} +1 -1
  319. package/dist/{usage-card-BhO5eqkX.js → usage-card-DVTRDmGz.js} +134 -133
  320. package/dist/usage-card-DVTRDmGz.js.map +1 -0
  321. package/dist/{utils-BRhFDwuU.js → utils-CF3c95Io.js} +68 -5
  322. package/dist/utils-CF3c95Io.js.map +1 -0
  323. package/dist/value-inputs-C0o70p-U.js +980 -0
  324. package/dist/value-inputs-C0o70p-U.js.map +1 -0
  325. package/dist/{value-typography-BP8OG6h5.js → value-typography-ClupF0Gz.js} +2 -2
  326. package/dist/{value-typography-BP8OG6h5.js.map → value-typography-ClupF0Gz.js.map} +1 -1
  327. package/dist/workspace-9EFVXUyk.js +410 -0
  328. package/dist/workspace-9EFVXUyk.js.map +1 -0
  329. package/docs/consumer-skill/SKILL.md +14 -129
  330. package/docs/consumer-skill/examples/app-layout.tsx +22 -12
  331. package/docs/consumer-skill/examples/authentication.tsx +77 -0
  332. package/docs/consumer-skill/examples/edit.tsx +30 -38
  333. package/docs/consumer-skill/examples/manifest.json +51 -1
  334. package/docs/consumer-skill/examples/rhf-bindings.tsx +86 -0
  335. package/docs/consumer-skill/examples/rhf-edit-page.tsx +141 -0
  336. package/docs/consumer-skill/examples/rhf-overlay-form.tsx +92 -0
  337. package/docs/consumer-skill/examples/rhf-transport.tsx +28 -0
  338. package/docs/consumer-skill/references/agents-md.md +17 -7
  339. package/docs/consumer-skill/references/authentication.md +42 -0
  340. package/docs/consumer-skill/references/component-contracts.json +319824 -0
  341. package/docs/consumer-skill/references/composition-mapping.md +92 -0
  342. package/docs/consumer-skill/references/composition.md +43 -19
  343. package/docs/consumer-skill/references/design-contract.md +32 -0
  344. package/docs/consumer-skill/references/discovery.md +84 -7
  345. package/docs/consumer-skill/references/installation.md +40 -36
  346. package/docs/consumer-skill/references/integration.md +6 -2
  347. package/docs/consumer-skill/references/public-api.json +1519 -65
  348. package/docs/consumer-skill/references/review.md +9 -4
  349. package/docs/consumer-skill/references/rhf-workflows.md +86 -0
  350. package/docs/consumer-skill/references/screen-archetypes.md +22 -0
  351. package/docs/consumer-skill/references/theming.md +8 -9
  352. package/docs/consumer-skill/references/tokens.json +12 -3
  353. package/docs/consumer-skill/references/workflows.md +2 -1
  354. package/docs/consumer-skill/scripts/composition-schema.d.mts +2 -0
  355. package/docs/consumer-skill/scripts/composition-schema.mjs +32 -0
  356. package/docs/consumer-skill/scripts/composition.mjs +198 -0
  357. package/docs/consumer-skill/scripts/discovery.mjs +42 -10
  358. package/docs/consumer-skill/scripts/inspect-component.mjs +11 -0
  359. package/docs/consumer-skill/scripts/inspect-tokens.mjs +11 -0
  360. package/docs/consumer-skill/scripts/inspection.mjs +138 -0
  361. package/docs/consumer-skill/scripts/map-composition.mjs +12 -0
  362. package/docs/consumer-skill/scripts/setup-skills.mjs +76 -0
  363. package/docs/consumer-skills/themelia-components/SKILL.md +30 -0
  364. package/docs/consumer-skills/themelia-composition/SKILL.md +35 -0
  365. package/docs/consumer-skills/themelia-forms/SKILL.md +12 -0
  366. package/docs/consumer-skills/themelia-foundation/SKILL.md +14 -0
  367. package/docs/consumer-skills/themelia-tokenization/SKILL.md +35 -0
  368. package/package.json +4 -2
  369. package/templates/README.md +39 -0
  370. package/templates/admin-shell-template.tsx +382 -0
  371. package/templates/analytics-operations-template.tsx +376 -0
  372. package/templates/customers-template.tsx +509 -0
  373. package/templates/index.ts +29 -0
  374. package/templates/orders-template.tsx +1102 -0
  375. package/templates/products-template.tsx +834 -0
  376. package/templates/user-permissions-template.tsx +450 -0
  377. package/dist/action-menu-CllU52Mm.js +0 -280
  378. package/dist/action-menu-CllU52Mm.js.map +0 -1
  379. package/dist/affordance-geometry.generated-tF36bREk.js.map +0 -1
  380. package/dist/ai-artifact-DvTwDVyL.js.map +0 -1
  381. package/dist/app-shell-C4FhvCqF.js.map +0 -1
  382. package/dist/cards-BaBZR-AU.js.map +0 -1
  383. package/dist/checkbox-AWIhNhmo.js.map +0 -1
  384. package/dist/choice-inputs-CNHvARCJ.js.map +0 -1
  385. package/dist/comments-DD0DUPwJ.js.map +0 -1
  386. package/dist/containers-Mu1LMGV1.js +0 -103
  387. package/dist/containers-Mu1LMGV1.js.map +0 -1
  388. package/dist/data-display-DT_khPl_.js.map +0 -1
  389. package/dist/date-picker-B2K3ucWn.js.map +0 -1
  390. package/dist/date-pickers-Ds8gLaoP.js +0 -175
  391. package/dist/date-pickers-Ds8gLaoP.js.map +0 -1
  392. package/dist/form-field-CwH9y77i.js.map +0 -1
  393. package/dist/forms-B9G_xwzi.js.map +0 -1
  394. package/dist/forms-numeric-B_jraJaQ.js.map +0 -1
  395. package/dist/input-DQdEd0YD.js +0 -263
  396. package/dist/input-DQdEd0YD.js.map +0 -1
  397. package/dist/item-K5_ghvze.js +0 -275
  398. package/dist/item-K5_ghvze.js.map +0 -1
  399. package/dist/metadata-list-8N16ZMW_.js +0 -405
  400. package/dist/metadata-list-8N16ZMW_.js.map +0 -1
  401. package/dist/overflow-tab-bar-CWY7Nogn.js.map +0 -1
  402. package/dist/overlays-tMkF-VAN.js.map +0 -1
  403. package/dist/page-CL9YeH_V.js +0 -224
  404. package/dist/page-CL9YeH_V.js.map +0 -1
  405. package/dist/page-header-commands-DOb6WsfD.js +0 -126
  406. package/dist/page-header-commands-DOb6WsfD.js.map +0 -1
  407. package/dist/settings-ybnjVwbd.js +0 -171
  408. package/dist/settings-ybnjVwbd.js.map +0 -1
  409. package/dist/smart-card-DiC5aac3.js +0 -604
  410. package/dist/smart-card-DiC5aac3.js.map +0 -1
  411. package/dist/text-inputs-f9Ia-aCi.js.map +0 -1
  412. package/dist/text.styles-g4He6UIW.js.map +0 -1
  413. package/dist/typography-UTnveOEy.js +0 -117
  414. package/dist/typography-UTnveOEy.js.map +0 -1
  415. package/dist/ui-provider-C_U0Xma4.js.map +0 -1
  416. package/dist/usage-card-BhO5eqkX.js.map +0 -1
  417. package/dist/utils-BRhFDwuU.js.map +0 -1
  418. package/dist/value-inputs-TaX4OjNk.js +0 -963
  419. package/dist/value-inputs-TaX4OjNk.js.map +0 -1
  420. package/dist/workspace-B-OEm3Kf.js +0 -408
  421. package/dist/workspace-B-OEm3Kf.js.map +0 -1
@@ -0,0 +1,834 @@
1
+ import { useMemo, useState } from 'react';
2
+
3
+ import { Badge } from 'themelia-react-ui/base/badge';
4
+ import { Card } from 'themelia-react-ui/base/cards';
5
+ import { Select, ToggleField } from 'themelia-react-ui/base/choice-inputs';
6
+ import { FormField, FormSection } from 'themelia-react-ui/base/forms';
7
+ import { Grid, Stack } from 'themelia-react-ui/base/structure';
8
+ import { Input, SlugField, Textarea } from 'themelia-react-ui/base/text-inputs';
9
+ import { TagsInput } from 'themelia-react-ui/base/value-inputs';
10
+ import { SeoListingCard } from 'themelia-react-ui/blocks/commerce';
11
+ import { MetadataList } from 'themelia-react-ui/base/metadata-list';
12
+ import { ActionDialog, ActionDrawer, useOverlayVisibility } from 'themelia-react-ui/features/overlays';
13
+ import {
14
+ ProductVariantDetails,
15
+ ProductVariantEditor,
16
+ ProductVariantsManager,
17
+ type ProductChoice,
18
+ type ProductOptionEditDraft,
19
+ type ProductOptionGroup,
20
+ type ProductVariantEditableField,
21
+ type ProductVariantEditorValues,
22
+ type ProductVariantOptionField,
23
+ type ProductVariantRow,
24
+ type ProductVariantSummary,
25
+ } from 'themelia-react-ui/features/products';
26
+ import { TwoColumnLayout } from 'themelia-react-ui/layout/containers';
27
+ import { Page } from 'themelia-react-ui/layout/page';
28
+ import { useMoneyFormatter } from 'themelia-react-ui/primitives';
29
+ import { DisplayLabel, Text } from 'themelia-react-ui/typography';
30
+
31
+ const INITIAL_OPTION_GROUPS: ProductOptionGroup[] = [
32
+ {
33
+ id: 'color',
34
+ name: 'Color',
35
+ description: 'Yarn-dyed core colorways.',
36
+ usedForVariants: true,
37
+ values: [
38
+ { id: 'heather-gray', label: 'Heather Gray' },
39
+ { id: 'midnight-navy', label: 'Midnight Navy' },
40
+ ],
41
+ },
42
+ {
43
+ id: 'size',
44
+ name: 'Size',
45
+ description: 'Standard unisex knitwear sizing.',
46
+ usedForVariants: true,
47
+ values: [
48
+ { id: 'small', label: 'Small' },
49
+ { id: 'medium', label: 'Medium' },
50
+ { id: 'large', label: 'Large' },
51
+ ],
52
+ },
53
+ ];
54
+
55
+ const INITIAL_VARIANTS: ProductVariantRow[] = [
56
+ {
57
+ id: 'mwc-gry-s',
58
+ name: 'Heather Gray / Small',
59
+ sku: 'MWC-GRY-S',
60
+ price: '145.00',
61
+ inventory: '28',
62
+ status: 'Active',
63
+ statusTone: 'success',
64
+ optionValueIds: { color: 'heather-gray', size: 'small' },
65
+ },
66
+ {
67
+ id: 'mwc-gry-m',
68
+ name: 'Heather Gray / Medium',
69
+ sku: 'MWC-GRY-M',
70
+ price: '145.00',
71
+ inventory: '42',
72
+ status: 'Active',
73
+ statusTone: 'success',
74
+ optionValueIds: { color: 'heather-gray', size: 'medium' },
75
+ },
76
+ {
77
+ id: 'mwc-gry-l',
78
+ name: 'Heather Gray / Large',
79
+ sku: 'MWC-GRY-L',
80
+ price: '145.00',
81
+ inventory: '31',
82
+ status: 'Active',
83
+ statusTone: 'success',
84
+ optionValueIds: { color: 'heather-gray', size: 'large' },
85
+ },
86
+ {
87
+ id: 'mwc-nvy-s',
88
+ name: 'Midnight Navy / Small',
89
+ sku: 'MWC-NVY-S',
90
+ price: '145.00',
91
+ inventory: '24',
92
+ status: 'Active',
93
+ statusTone: 'success',
94
+ optionValueIds: { color: 'midnight-navy', size: 'small' },
95
+ },
96
+ {
97
+ id: 'mwc-nvy-m',
98
+ name: 'Midnight Navy / Medium',
99
+ sku: 'MWC-NVY-M',
100
+ price: '145.00',
101
+ inventory: '37',
102
+ status: 'Active',
103
+ statusTone: 'success',
104
+ optionValueIds: { color: 'midnight-navy', size: 'medium' },
105
+ },
106
+ {
107
+ id: 'mwc-nvy-l',
108
+ name: 'Midnight Navy / Large',
109
+ sku: 'MWC-NVY-L',
110
+ price: '145.00',
111
+ inventory: '6',
112
+ status: 'Low stock',
113
+ statusTone: 'warning',
114
+ optionValueIds: { color: 'midnight-navy', size: 'large' },
115
+ },
116
+ ];
117
+
118
+ const PRODUCT_STATUS_OPTIONS = [
119
+ { value: 'Active', label: 'Active' },
120
+ { value: 'Draft', label: 'Draft' },
121
+ { value: 'Archived', label: 'Archived' },
122
+ ];
123
+
124
+ const PRODUCT_CATEGORY_OPTIONS = [
125
+ { value: 'sweaters', label: 'Apparel & Accessories > Clothing > Sweaters' },
126
+ { value: 'shirts', label: 'Apparel & Accessories > Clothing > Shirts & Tops' },
127
+ { value: 'outerwear', label: 'Apparel & Accessories > Clothing > Outerwear' },
128
+ ];
129
+
130
+ const COUNTRY_OPTIONS = [
131
+ { value: 'PT', label: 'Portugal' },
132
+ { value: 'IT', label: 'Italy' },
133
+ { value: 'US', label: 'United States' },
134
+ ];
135
+
136
+ const VARIANT_STATUS_CHOICES: ProductChoice[] = [
137
+ { value: 'Active', label: 'Active' },
138
+ { value: 'Low stock', label: 'Low stock' },
139
+ { value: 'Draft', label: 'Draft' },
140
+ ];
141
+
142
+ function updateVariantField(
143
+ rows: ProductVariantRow[],
144
+ variantId: string,
145
+ field: ProductVariantEditableField,
146
+ value: string,
147
+ ) {
148
+ return rows.map((row) => (row.id === variantId ? { ...row, [field]: value } : row));
149
+ }
150
+
151
+ export function ProductsAdminTemplate() {
152
+ // Sample records are priced in USD; bind that once for every text amount.
153
+ const formatMoney = useMoneyFormatter({ currency: 'USD' });
154
+ const [productTitle, setProductTitle] = useState('Merino Wool Crewneck Sweater');
155
+ const [productDescription, setProductDescription] = useState(
156
+ 'Knitted in Braga, Portugal from 100% extra-fine Italian merino wool. Designed with a classic regular fit, saddle shoulders, and 1x1 ribbed collar, cuffs, and hem. Naturally breathable and temperature-regulating for three-season layering.',
157
+ );
158
+ const [productStatus, setProductStatus] = useState<'Active' | 'Draft' | 'Archived'>('Active');
159
+ const [savedRevision, setSavedRevision] = useState<number | null>(null);
160
+
161
+ // Shipping & customs state
162
+ const [isPhysicalProduct, setIsPhysicalProduct] = useState(true);
163
+ const [weightKg, setWeightKg] = useState('0.42');
164
+ const [countryOfOrigin, setCountryOfOrigin] = useState('PT');
165
+ const [hsCode, setHsCode] = useState('6110.11.00');
166
+
167
+ // Publishing, organization, collections & tags state
168
+ const [onlineStoreChannel, setOnlineStoreChannel] = useState(true);
169
+ const [posChannel, setPosChannel] = useState(true);
170
+ const [category, setCategory] = useState('sweaters');
171
+ const [productType, setProductType] = useState('Knitwear');
172
+ const [vendor, setVendor] = useState('Oak & Loom');
173
+ const [inAutumnCollection, setInAutumnCollection] = useState(true);
174
+ const [inMensKnitwear, setInMensKnitwear] = useState(true);
175
+ const [inMerinoEssentials, setInMerinoEssentials] = useState(false);
176
+ const [tags, setTags] = useState<string[]>([
177
+ 'merino-wool',
178
+ 'crewneck',
179
+ 'italian-yarn',
180
+ 'portugal',
181
+ ]);
182
+
183
+ // Variants state (owns per-SKU price, compare-at price, cost, barcode, and stock)
184
+ const [optionState, setOptionState] = useState<ProductOptionGroup[]>(INITIAL_OPTION_GROUPS);
185
+ const [variantState, setVariantState] = useState<ProductVariantRow[]>(INITIAL_VARIANTS);
186
+ const [collapsedOptionIds, setCollapsedOptionIds] = useState<string[]>([]);
187
+ const [editingOptionId, setEditingOptionId] = useState<string | null>(null);
188
+ const [selectedVariantIds, setSelectedVariantIds] = useState<string[]>(['mwc-gry-m']);
189
+
190
+ const [editingVariantId, setEditingVariantId] = useState<string>('mwc-gry-m');
191
+ const [variantCompareAtPrice, setVariantCompareAtPrice] = useState('165.00');
192
+ const [variantCostPerItem, setVariantCostPerItem] = useState('52.00');
193
+ const [variantBarcode, setVariantBarcode] = useState('084291004218');
194
+ const [variantDraft, setVariantDraft] = useState<ProductVariantEditorValues>({
195
+ name: 'Heather Gray / Medium',
196
+ description: '100% Extra-Fine Merino Wool',
197
+ sku: 'MWC-GRY-M',
198
+ price: '145.00',
199
+ inventory: '42',
200
+ channels: 'Online Store, SoHo POS',
201
+ status: 'Active',
202
+ options: {
203
+ color: 'Heather Gray',
204
+ size: 'Medium',
205
+ },
206
+ });
207
+
208
+ // SEO state
209
+ const [seoTitle, setSeoTitle] = useState(
210
+ 'Merino Wool Crewneck Sweater — Heather Gray & Navy | Oak & Loom',
211
+ );
212
+ const [seoSlug] = useState('merino-wool-crewneck-sweater');
213
+ const [seoDescription, setSeoDescription] = useState(
214
+ 'Knitted in Portugal from 100% extra-fine Italian merino wool. Soft, breathable 7-gauge crewneck sweater built for everyday layering. Free US shipping & 30-day returns.',
215
+ );
216
+
217
+ const [seoDraft, setSeoDraft] = useState({ title: seoTitle, description: seoDescription });
218
+ const [savedVariantDetails, setSavedVariantDetails] = useState<Record<string, { compareAtPrice: string; costPerItem: string; barcode: string }>>({});
219
+ const variantDrawer = useOverlayVisibility();
220
+ const seoDialog = useOverlayVisibility();
221
+ const previewDialog = useOverlayVisibility();
222
+
223
+ const productSnapshot = JSON.stringify({
224
+ productTitle, productDescription, productStatus, isPhysicalProduct, weightKg,
225
+ countryOfOrigin, hsCode, onlineStoreChannel, posChannel, category, productType,
226
+ vendor, inAutumnCollection, inMensKnitwear, inMerinoEssentials, tags,
227
+ optionState, variantState, savedVariantDetails, seoTitle, seoDescription, seoSlug,
228
+ });
229
+ const [savedProductSnapshot, setSavedProductSnapshot] = useState(productSnapshot);
230
+ const productDirty = productSnapshot !== savedProductSnapshot;
231
+
232
+ const totalInventory = useMemo(
233
+ () =>
234
+ variantState.reduce(
235
+ (sum, row) => sum + (Number.parseInt(String(row.inventory ?? '0'), 10) || 0),
236
+ 0,
237
+ ),
238
+ [variantState],
239
+ );
240
+
241
+ const priceRangeLabel = useMemo(() => {
242
+ const prices = variantState
243
+ .map((row) => Number.parseFloat(String(row.price ?? '0')))
244
+ .filter((n) => Number.isFinite(n) && n > 0);
245
+ if (prices.length === 0) return formatMoney(0);
246
+ const min = Math.min(...prices);
247
+ const max = Math.max(...prices);
248
+ return min === max ? formatMoney(min) : `${formatMoney(min)} – ${formatMoney(max)}`;
249
+ }, [variantState, formatMoney]);
250
+
251
+ const variantSummaries: ProductVariantSummary[] = useMemo(
252
+ () =>
253
+ variantState.map((row) => ({
254
+ id: row.id,
255
+ name: row.name,
256
+ description: '100% Extra-Fine Merino Wool',
257
+ options: Object.values(row.optionValueIds ?? {}),
258
+ sku: row.sku,
259
+ price: formatMoney(String(row.price ?? '145.00')),
260
+ inventory: String(row.inventory ?? '0'),
261
+ channels: 'Online Store, POS',
262
+ updatedAt: 'Today',
263
+ status: row.status,
264
+ statusTone: row.statusTone,
265
+ })),
266
+ [variantState, formatMoney],
267
+ );
268
+
269
+ const variantOptionFields: ProductVariantOptionField[] = useMemo(
270
+ () =>
271
+ optionState.map((group) => ({
272
+ id: group.id,
273
+ label: String(group.name),
274
+ value: String(variantDraft.options?.[group.id] ?? group.values?.[0]?.label ?? ''),
275
+ choices: (group.values ?? []).map((v) => ({
276
+ value: String(v.label),
277
+ label: String(v.label),
278
+ })),
279
+ })),
280
+ [optionState, variantDraft.options],
281
+ );
282
+
283
+ const openVariantEditor = (row: ProductVariantRow) => {
284
+ setEditingVariantId(row.id);
285
+ const detail = savedVariantDetails[row.id];
286
+ setVariantCompareAtPrice(detail?.compareAtPrice ?? '165.00');
287
+ setVariantCostPerItem(detail?.costPerItem ?? '52.00');
288
+ setVariantBarcode(detail?.barcode ?? '084291004218');
289
+ const colorId = row.optionValueIds?.color ?? 'heather-gray';
290
+ const sizeId = row.optionValueIds?.size ?? 'medium';
291
+ const colorLabel = String(
292
+ optionState
293
+ .find((g) => g.id === 'color')
294
+ ?.values?.find((v) => v.id === colorId)?.label ?? 'Heather Gray',
295
+ );
296
+ const sizeLabel = String(
297
+ optionState
298
+ .find((g) => g.id === 'size')
299
+ ?.values?.find((v) => v.id === sizeId)?.label ?? 'Medium',
300
+ );
301
+
302
+ setVariantDraft({
303
+ name: String(row.name ?? ''),
304
+ description: '100% Extra-Fine Merino Wool',
305
+ sku: String(row.sku ?? ''),
306
+ price: String(row.price ?? '145.00'),
307
+ inventory: String(row.inventory ?? '0'),
308
+ channels: 'Online Store, SoHo POS',
309
+ status: String(row.status ?? 'Active'),
310
+ options: {
311
+ color: colorLabel,
312
+ size: sizeLabel,
313
+ },
314
+ });
315
+ variantDrawer.show();
316
+ };
317
+
318
+ const duplicateVariant = (variant: ProductVariantRow) => {
319
+ const copy = { ...variant, id: crypto.randomUUID(), sku: `${String(variant.sku ?? 'MWC')}-COPY` };
320
+ setVariantState((current) => [...current, copy]);
321
+ return copy;
322
+ };
323
+
324
+ const handleSaveVariantDraft = () => {
325
+ setSavedVariantDetails((current) => ({ ...current, [editingVariantId]: { compareAtPrice: variantCompareAtPrice, costPerItem: variantCostPerItem, barcode: variantBarcode } }));
326
+ const optionValueIds = Object.fromEntries(optionState.map((group) => [group.id, group.values?.find((value) => String(value.label) === variantDraft.options?.[group.id])?.id]).filter((entry): entry is [string, string] => typeof entry[1] === 'string'));
327
+ setVariantState((current) =>
328
+ current.map((row) =>
329
+ row.id === editingVariantId
330
+ ? {
331
+ ...row,
332
+ optionValueIds,
333
+ name: variantDraft.name || row.name,
334
+ sku: variantDraft.sku || row.sku,
335
+ price: variantDraft.price || row.price,
336
+ inventory: variantDraft.inventory || row.inventory,
337
+ status: variantDraft.status || row.status,
338
+ statusTone:
339
+ variantDraft.status === 'Active'
340
+ ? 'success'
341
+ : variantDraft.status === 'Low stock'
342
+ ? 'warning'
343
+ : 'secondary',
344
+ }
345
+ : row,
346
+ ),
347
+ );
348
+ variantDrawer.hide();
349
+ };
350
+
351
+ const activeVariantSummary = useMemo<ProductVariantSummary | undefined>(
352
+ () => variantSummaries.find((item) => item.id === editingVariantId) ?? variantSummaries[0],
353
+ [variantSummaries, editingVariantId],
354
+ );
355
+
356
+ const statusBadgeTone =
357
+ productStatus === 'Active'
358
+ ? 'success'
359
+ : productStatus === 'Draft'
360
+ ? 'info'
361
+ : 'secondary';
362
+
363
+ return (
364
+ <Page
365
+ header={{
366
+ title: productTitle,
367
+ description:
368
+ productDirty ? 'Unsaved product changes' : savedRevision !== null
369
+ ? `Saved revision #${savedRevision} · Updated just now`
370
+ : `Men’s Knitwear · ${variantState.length} variants (${totalInventory} in stock)`,
371
+ titleBadges: [{ label: productStatus, tone: statusBadgeTone }],
372
+ primaryAction: {
373
+ label: 'Save product',
374
+ disabled: !productDirty,
375
+ onClick: () => { setSavedProductSnapshot(productSnapshot); setSavedRevision((rev) => (rev ?? 0) + 1); },
376
+ },
377
+ secondaryActions: [{ label: 'Preview', onClick: previewDialog.show }],
378
+ }}
379
+ >
380
+ <TwoColumnLayout
381
+ stickyAside
382
+ main={
383
+ <Stack gap="2xl">
384
+ {/* 1. Product details */}
385
+ <Card title="Product details">
386
+ <Stack gap="lg">
387
+ <FormField label="Title" required>
388
+ <Input
389
+ aria-label="Title"
390
+ value={productTitle}
391
+ onChange={(event) => setProductTitle(event.target.value)}
392
+ />
393
+ </FormField>
394
+
395
+ <FormField
396
+ label="Description"
397
+ hint="Displayed on the storefront under fabric, yarn origin, and fit details."
398
+ >
399
+ <Textarea
400
+ aria-label="Description"
401
+ rows={5}
402
+ value={productDescription}
403
+ onChange={(event) => setProductDescription(event.target.value)}
404
+ />
405
+ </FormField>
406
+
407
+ <FormField label="URL handle">
408
+ <SlugField
409
+ aria-label="URL handle"
410
+ startAddon="/products/"
411
+ value={seoSlug}
412
+ />
413
+ </FormField>
414
+ </Stack>
415
+ </Card>
416
+
417
+ {/* 2. Options & Variants (owns SKU pricing and stock per variant) */}
418
+ <ProductVariantsManager
419
+ surface="card"
420
+ optionGroups={optionState}
421
+ variants={variantState}
422
+ optionCombinationDisplay="tags"
423
+ cellDisplay={{ price: 'field', inventory: 'field' }}
424
+ visibleColumns={['variant', 'price', 'inventory']}
425
+ collapsedOptionIds={collapsedOptionIds}
426
+ onCollapsedOptionIdsChange={setCollapsedOptionIds}
427
+ editingOptionId={editingOptionId}
428
+ onEditingOptionIdChange={setEditingOptionId}
429
+ selectedVariantIds={selectedVariantIds}
430
+ onSelectedVariantIdsChange={setSelectedVariantIds}
431
+ onEditOption={(option) => setEditingOptionId(option.id)}
432
+ onCancelEditingOption={() => setEditingOptionId(null)}
433
+ onSaveEditingOption={(option, draft: ProductOptionEditDraft) => {
434
+ setOptionState((current) =>
435
+ current.map((item) =>
436
+ item.id === option.id
437
+ ? {
438
+ ...item,
439
+ name: draft.name.trim() || item.name,
440
+ values: draft.values.map((val, idx) => ({
441
+ ...val,
442
+ id: val.id || `${item.id}-v-${idx + 1}`,
443
+ label: String(val.label).trim() || `Option ${idx + 1}`,
444
+ })),
445
+ }
446
+ : item,
447
+ ),
448
+ );
449
+ setEditingOptionId(null);
450
+ }}
451
+ onCreateVariant={() => {
452
+ const nextIndex = variantState.length + 1;
453
+ setVariantState((current) => [
454
+ ...current,
455
+ {
456
+ id: crypto.randomUUID(),
457
+ name: 'Heather Gray / Large',
458
+ sku: `MWC-GRY-L-${nextIndex}`,
459
+ price: '145.00',
460
+ inventory: '20',
461
+ status: 'Active',
462
+ statusTone: 'success',
463
+ optionValueIds: { color: 'heather-gray', size: 'large' },
464
+ },
465
+ ]);
466
+ }}
467
+ onVariantFieldChange={(variant, field, value) => {
468
+ setVariantState((current) =>
469
+ updateVariantField(current, variant.id, field, value),
470
+ );
471
+ }}
472
+ onVariantFieldBlur={(variant, field, value) => {
473
+ setVariantState((current) =>
474
+ updateVariantField(current, variant.id, field, value.trim()),
475
+ );
476
+ }}
477
+ onSelectVariant={openVariantEditor}
478
+ onEditVariant={openVariantEditor}
479
+ onDuplicateVariant={duplicateVariant}
480
+ onDeleteVariant={(variant) => {
481
+ setVariantState((current) =>
482
+ current.filter((item) => item.id !== variant.id),
483
+ );
484
+ }}
485
+ strings={{
486
+ title: 'Variants',
487
+ description:
488
+ 'Each variant manages its own SKU, retail price, compare-at price, barcode, and warehouse inventory.',
489
+ }}
490
+ />
491
+
492
+ {/* 3. Shipping & customs */}
493
+ <Card
494
+ title="Shipping & customs"
495
+ description="Default package weight and customs tariff classification inherited by physical variants."
496
+ >
497
+ <Stack gap="lg">
498
+ <ToggleField
499
+ kind="checkbox"
500
+ value={isPhysicalProduct}
501
+ onValueChange={(checked) => setIsPhysicalProduct(Boolean(checked))}
502
+ label="This is a physical product requiring shipping"
503
+ />
504
+
505
+ {isPhysicalProduct && (
506
+ <Grid columns={{ base: 1, sm: 3 }} gap="lg">
507
+ <FormField label="Package weight">
508
+ <Input
509
+ aria-label="Weight"
510
+ endAddon="kg"
511
+ value={weightKg}
512
+ onChange={(event) => setWeightKg(event.target.value)}
513
+ />
514
+ </FormField>
515
+ <FormField label="Country/Region of origin">
516
+ <Select
517
+ aria-label="Country/Region of origin"
518
+ options={COUNTRY_OPTIONS}
519
+ value={countryOfOrigin}
520
+ onValueChange={(value) => setCountryOfOrigin(String(value))}
521
+ />
522
+ </FormField>
523
+ <FormField label="HS tariff code">
524
+ <Input
525
+ aria-label="HS code"
526
+ value={hsCode}
527
+ onChange={(event) => setHsCode(event.target.value)}
528
+ />
529
+ </FormField>
530
+ </Grid>
531
+ )}
532
+ </Stack>
533
+ </Card>
534
+
535
+ {/* 4. Search Engine Listing */}
536
+ <SeoListingCard
537
+ surface="card"
538
+ siteName="Oak & Loom"
539
+ baseUrl="https://oakandloom.com"
540
+ pathSegments={['collections', 'knitwear', 'products']}
541
+ permalink={seoSlug}
542
+ title={seoTitle}
543
+ description={seoDescription}
544
+ price={priceRangeLabel}
545
+ keyword="merino wool crewneck sweater"
546
+ onEdit={() => { setSeoDraft({ title: seoTitle, description: seoDescription }); seoDialog.show(); }}
547
+ />
548
+ </Stack>
549
+ }
550
+ aside={
551
+ <Stack gap="2xl">
552
+ {/* 1. Status Card */}
553
+ <Card title="Status">
554
+ <Stack gap="lg">
555
+ <Select
556
+ aria-label="Product status"
557
+ options={PRODUCT_STATUS_OPTIONS}
558
+ value={productStatus}
559
+ onValueChange={(value) =>
560
+ setProductStatus(value as 'Active' | 'Draft' | 'Archived')
561
+ }
562
+ />
563
+ <MetadataList
564
+ columns={1}
565
+ items={[
566
+ {
567
+ id: 'variant-count',
568
+ label: 'Variants',
569
+ value: `${variantState.length} SKUs`,
570
+ },
571
+ {
572
+ id: 'total-stock',
573
+ label: 'Total inventory',
574
+ value: `${totalInventory} units across 2 locations`,
575
+ },
576
+ {
577
+ id: 'price-range',
578
+ label: 'Variant price',
579
+ value: priceRangeLabel,
580
+ },
581
+ ]}
582
+ />
583
+ </Stack>
584
+ </Card>
585
+
586
+ {/* 2. Publishing Card */}
587
+ <Card title="Publishing">
588
+ <Stack gap="2xl">
589
+ <Stack gap="xs">
590
+ <DisplayLabel>Sales channels</DisplayLabel>
591
+ <ToggleField
592
+ kind="checkbox"
593
+ value={onlineStoreChannel}
594
+ onValueChange={(checked) => setOnlineStoreChannel(Boolean(checked))}
595
+ label="Online Store"
596
+ />
597
+ <ToggleField
598
+ kind="checkbox"
599
+ value={posChannel}
600
+ onValueChange={(checked) => setPosChannel(Boolean(checked))}
601
+ label="Point of Sale (SoHo Flagship)"
602
+ />
603
+ </Stack>
604
+
605
+ <MetadataList
606
+ columns={1}
607
+ items={[
608
+ {
609
+ id: 'markets',
610
+ label: 'Markets',
611
+ value: 'United States (USD) & EU (EUR)',
612
+ },
613
+ ]}
614
+ />
615
+ </Stack>
616
+ </Card>
617
+
618
+ {/* 3. Product organization Card (Category, Type, Vendor) */}
619
+ <Card title="Organization">
620
+ <Stack gap="lg">
621
+ <FormField label="Category">
622
+ <Select
623
+ aria-label="Category"
624
+ options={PRODUCT_CATEGORY_OPTIONS}
625
+ value={category}
626
+ onValueChange={(value) => setCategory(String(value))}
627
+ />
628
+ </FormField>
629
+
630
+ <FormField label="Product type">
631
+ <Input
632
+ aria-label="Product type"
633
+ value={productType}
634
+ onChange={(event) => setProductType(event.target.value)}
635
+ />
636
+ </FormField>
637
+
638
+ <FormField label="Vendor">
639
+ <Input
640
+ aria-label="Vendor"
641
+ value={vendor}
642
+ onChange={(event) => setVendor(event.target.value)}
643
+ />
644
+ </FormField>
645
+ </Stack>
646
+ </Card>
647
+
648
+ {/* 4. Collections Card */}
649
+ <Card
650
+ title="Collections"
651
+ description="Assign this product to manual and automated storefront collections."
652
+ >
653
+ <Stack gap="sm">
654
+ <ToggleField
655
+ kind="checkbox"
656
+ value={inAutumnCollection}
657
+ onValueChange={(checked) => setInAutumnCollection(Boolean(checked))}
658
+ label="Autumn / Winter Knitwear"
659
+ />
660
+ <ToggleField
661
+ kind="checkbox"
662
+ value={inMensKnitwear}
663
+ onValueChange={(checked) => setInMensKnitwear(Boolean(checked))}
664
+ label="Men’s Core Essentials"
665
+ />
666
+ <ToggleField
667
+ kind="checkbox"
668
+ value={inMerinoEssentials}
669
+ onValueChange={(checked) => setInMerinoEssentials(Boolean(checked))}
670
+ label="Italian Merino Capsule"
671
+ />
672
+ </Stack>
673
+ </Card>
674
+
675
+ {/* 5. Tags Card */}
676
+ <Card
677
+ title="Tags"
678
+ description="Used for storefront filtering and automated collection rules."
679
+ >
680
+ <FormField label="Product tags">
681
+ <TagsInput
682
+ aria-label="Tags"
683
+ value={tags}
684
+ onValueChange={setTags}
685
+ placeholder="Add tag…"
686
+ />
687
+ </FormField>
688
+ </Card>
689
+ </Stack>
690
+ }
691
+ />
692
+
693
+ <ActionDrawer
694
+ {...variantDrawer.overlayProps}
695
+ title={`Edit variant · ${variantDraft.name}`}
696
+ description="Manage variant-level pricing, cost margin, SKU barcode, and warehouse inventory."
697
+ width="lg"
698
+ showFooter={false}
699
+ >
700
+ <Stack gap="xl">
701
+ <ProductVariantDetails
702
+ variant={activeVariantSummary}
703
+ metadata={[
704
+ {
705
+ id: 'sku',
706
+ label: 'SKU',
707
+ value: { kind: 'mono', value: variantDraft.sku || 'MWC-GRY-M' },
708
+ },
709
+ {
710
+ id: 'barcode',
711
+ label: 'Barcode (GTIN)',
712
+ value: { kind: 'mono', value: variantBarcode },
713
+ },
714
+ {
715
+ id: 'compare-at',
716
+ label: 'Compare-at price',
717
+ value: formatMoney(variantCompareAtPrice),
718
+ },
719
+ {
720
+ id: 'unit-cost',
721
+ label: 'Unit cost',
722
+ value: formatMoney(variantCostPerItem),
723
+ },
724
+ {
725
+ id: 'brooklyn-stock',
726
+ label: 'Brooklyn Warehouse',
727
+ value: `${variantDraft.inventory ?? '42'} available`,
728
+ },
729
+ ]}
730
+ onDuplicateVariant={() => { const source = variantState.find((row) => row.id === editingVariantId); if (source) openVariantEditor(duplicateVariant(source)); }}
731
+ />
732
+ <FormSection title="Variant pricing & identifiers">
733
+ <Grid columns={{ base: 1, sm: 3 }} gap="lg">
734
+ <FormField label="Compare-at price">
735
+ <Input
736
+ aria-label="Compare-at price"
737
+ startAddon="$"
738
+ value={variantCompareAtPrice}
739
+ onChange={(event) => setVariantCompareAtPrice(event.target.value)}
740
+ />
741
+ </FormField>
742
+ <FormField label="Cost per item">
743
+ <Input
744
+ aria-label="Cost per item"
745
+ startAddon="$"
746
+ value={variantCostPerItem}
747
+ onChange={(event) => setVariantCostPerItem(event.target.value)}
748
+ />
749
+ </FormField>
750
+ <FormField label="Barcode (UPC / GTIN)">
751
+ <Input
752
+ aria-label="Barcode"
753
+ value={variantBarcode}
754
+ onChange={(event) => setVariantBarcode(event.target.value)}
755
+ />
756
+ </FormField>
757
+ </Grid>
758
+ </FormSection>
759
+ <ProductVariantEditor
760
+ value={variantDraft}
761
+ statusOptions={VARIANT_STATUS_CHOICES}
762
+ optionFields={variantOptionFields}
763
+ onValueChange={setVariantDraft}
764
+ onCancel={variantDrawer.hide}
765
+ onSubmit={handleSaveVariantDraft}
766
+ />
767
+ </Stack>
768
+ </ActionDrawer>
769
+
770
+ <ActionDialog
771
+ {...seoDialog.overlayProps}
772
+ title="Edit search engine listing"
773
+ description="Customize the page title and meta description shown in search results."
774
+ strings={{ confirm: 'Save SEO settings', cancel: 'Cancel' }}
775
+ onConfirm={() => { setSeoTitle(seoDraft.title); setSeoDescription(seoDraft.description); seoDialog.hide(); }}
776
+ >
777
+ <FormSection title="Search engine metadata">
778
+ <FormField label="Page title" required>
779
+ <Input
780
+ aria-label="Page title"
781
+ value={seoDraft.title}
782
+ onChange={(event) => setSeoDraft((draft) => ({ ...draft, title: event.target.value }))}
783
+ />
784
+ </FormField>
785
+ <FormField label="Meta description">
786
+ <Textarea
787
+ aria-label="Meta description"
788
+ rows={4}
789
+ value={seoDraft.description}
790
+ onChange={(event) => setSeoDraft((draft) => ({ ...draft, description: event.target.value }))}
791
+ />
792
+ </FormField>
793
+ </FormSection>
794
+ </ActionDialog>
795
+
796
+ <ActionDialog
797
+ {...previewDialog.overlayProps}
798
+ title="Storefront product preview"
799
+ description="Preview how this product appears on the Online Store."
800
+ strings={{ confirm: 'Done', cancel: 'Close' }}
801
+ onConfirm={previewDialog.hide}
802
+ >
803
+ <Stack gap="md">
804
+ <Stack direction="horizontal" justify="between" align="center" wrap>
805
+ <Text size="lg" weight="semibold">
806
+ {productTitle}
807
+ </Text>
808
+ <Badge tone="success">{priceRangeLabel}</Badge>
809
+ </Stack>
810
+ <Text size="sm" type="secondary">
811
+ {productDescription}
812
+ </Text>
813
+ <MetadataList
814
+ layout="rows"
815
+ density="compact"
816
+ items={[
817
+ {
818
+ id: 'vendor',
819
+ label: 'Vendor',
820
+ value: vendor,
821
+ },
822
+ {
823
+ id: 'variants',
824
+ label: 'Available variants',
825
+ value: `${variantState.length} options (${totalInventory} in stock)`,
826
+ },
827
+ ]}
828
+ />
829
+ </Stack>
830
+ </ActionDialog>
831
+ </Page>
832
+ );
833
+ }
834
+ export default ProductsAdminTemplate;