@open-mercato/core 0.7.1-develop.7203.1.9396450b78 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (416) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build.mjs +11 -0
  3. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +23 -0
  4. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  5. package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
  6. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  7. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +2 -1
  8. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  9. package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -2
  10. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  11. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +2 -1
  12. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  13. package/dist/modules/customers/lib/detailHelpers.js +4 -0
  14. package/dist/modules/customers/lib/detailHelpers.js.map +2 -2
  15. package/dist/modules/customers/lib/todoCompatibility.js +5 -0
  16. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  17. package/dist/modules/design_system/backend/design-system/page.meta.js +3 -2
  18. package/dist/modules/design_system/backend/design-system/page.meta.js.map +2 -2
  19. package/dist/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
  20. package/dist/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
  21. package/dist/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
  22. package/dist/modules/design_system/gallery/assets/avatar-photo.png +0 -0
  23. package/dist/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
  24. package/dist/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
  25. package/dist/modules/design_system/gallery/assets/crypto-ada.png +0 -0
  26. package/dist/modules/design_system/gallery/assets/crypto-avax.png +0 -0
  27. package/dist/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
  28. package/dist/modules/design_system/gallery/assets/crypto-brand.png +0 -0
  29. package/dist/modules/design_system/gallery/assets/crypto-btc.png +0 -0
  30. package/dist/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
  31. package/dist/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
  32. package/dist/modules/design_system/gallery/assets/crypto-dot.png +0 -0
  33. package/dist/modules/design_system/gallery/assets/crypto-eth.png +0 -0
  34. package/dist/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
  35. package/dist/modules/design_system/gallery/assets/crypto-history.svg +3 -0
  36. package/dist/modules/design_system/gallery/assets/crypto-link.svg +3 -0
  37. package/dist/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
  38. package/dist/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
  39. package/dist/modules/design_system/gallery/assets/crypto-more.svg +3 -0
  40. package/dist/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
  41. package/dist/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
  42. package/dist/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
  43. package/dist/modules/design_system/gallery/assets/crypto-sol.png +0 -0
  44. package/dist/modules/design_system/gallery/assets/crypto-star.svg +3 -0
  45. package/dist/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
  46. package/dist/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
  47. package/dist/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
  48. package/dist/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
  49. package/dist/modules/design_system/gallery/assets/crypto-trx.png +0 -0
  50. package/dist/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
  51. package/dist/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
  52. package/dist/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
  53. package/dist/modules/design_system/gallery/assets/crypto-wei.png +0 -0
  54. package/dist/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
  55. package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js +67 -0
  56. package/dist/modules/design_system/gallery/assets/cryptocurrency-artwork.js.map +7 -0
  57. package/dist/modules/design_system/gallery/assets/github-logo.png +0 -0
  58. package/dist/modules/design_system/gallery/assets/images.d.js +1 -0
  59. package/dist/modules/design_system/gallery/assets/images.d.js.map +7 -0
  60. package/dist/modules/design_system/gallery/assets/input-european-union.svg +18 -0
  61. package/dist/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
  62. package/dist/modules/design_system/gallery/assets/marketing-artwork.js +83 -0
  63. package/dist/modules/design_system/gallery/assets/marketing-artwork.js.map +7 -0
  64. package/dist/modules/design_system/gallery/assets/menu-apex.svg +18 -0
  65. package/dist/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
  66. package/dist/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
  67. package/dist/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
  68. package/dist/modules/design_system/gallery/assets/select-google.png +0 -0
  69. package/dist/modules/design_system/gallery/assets/select-poland.png +0 -0
  70. package/dist/modules/design_system/gallery/assets/selection-artwork.js +5 -0
  71. package/dist/modules/design_system/gallery/assets/selection-artwork.js.map +7 -0
  72. package/dist/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
  73. package/dist/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
  74. package/dist/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
  75. package/dist/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
  76. package/dist/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
  77. package/dist/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
  78. package/dist/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
  79. package/dist/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
  80. package/dist/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
  81. package/dist/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
  82. package/dist/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
  83. package/dist/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
  84. package/dist/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
  85. package/dist/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
  86. package/dist/modules/design_system/gallery/assets/style-studio-product.png +0 -0
  87. package/dist/modules/design_system/gallery/assets/upload-images.js +8 -0
  88. package/dist/modules/design_system/gallery/assets/upload-images.js.map +7 -0
  89. package/dist/modules/design_system/gallery/components/BrandStylePreview.js +74 -0
  90. package/dist/modules/design_system/gallery/components/BrandStylePreview.js.map +7 -0
  91. package/dist/modules/design_system/gallery/components/CodeSnippet.js +31 -31
  92. package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +2 -2
  93. package/dist/modules/design_system/gallery/components/ColorPlayground.js +408 -0
  94. package/dist/modules/design_system/gallery/components/ColorPlayground.js.map +7 -0
  95. package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js +166 -0
  96. package/dist/modules/design_system/gallery/components/ColorStudioCompositions.js.map +7 -0
  97. package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js +182 -0
  98. package/dist/modules/design_system/gallery/components/ColorStudioSpecimen.js.map +7 -0
  99. package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js +80 -0
  100. package/dist/modules/design_system/gallery/components/CompactEntrySpecimen.js.map +7 -0
  101. package/dist/modules/design_system/gallery/components/ComponentIndex.js +42 -0
  102. package/dist/modules/design_system/gallery/components/ComponentIndex.js.map +7 -0
  103. package/dist/modules/design_system/gallery/components/DesignSystemHome.js +69 -0
  104. package/dist/modules/design_system/gallery/components/DesignSystemHome.js.map +7 -0
  105. package/dist/modules/design_system/gallery/components/EntryCard.js +47 -41
  106. package/dist/modules/design_system/gallery/components/EntryCard.js.map +2 -2
  107. package/dist/modules/design_system/gallery/components/EntryHeader.js +68 -0
  108. package/dist/modules/design_system/gallery/components/EntryHeader.js.map +7 -0
  109. package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js +19 -0
  110. package/dist/modules/design_system/gallery/components/FamilyEntryPreview.js.map +7 -0
  111. package/dist/modules/design_system/gallery/components/FoundationGuide.js +46 -0
  112. package/dist/modules/design_system/gallery/components/FoundationGuide.js.map +7 -0
  113. package/dist/modules/design_system/gallery/components/GalleryLink.js +21 -0
  114. package/dist/modules/design_system/gallery/components/GalleryLink.js.map +7 -0
  115. package/dist/modules/design_system/gallery/components/GalleryNavigation.js +82 -0
  116. package/dist/modules/design_system/gallery/components/GalleryNavigation.js.map +7 -0
  117. package/dist/modules/design_system/gallery/components/GalleryShell.js +147 -67
  118. package/dist/modules/design_system/gallery/components/GalleryShell.js.map +3 -3
  119. package/dist/modules/design_system/gallery/components/IconsBrowser.js +33 -0
  120. package/dist/modules/design_system/gallery/components/IconsBrowser.js.map +7 -0
  121. package/dist/modules/design_system/gallery/components/SourceLibrary.js +93 -0
  122. package/dist/modules/design_system/gallery/components/SourceLibrary.js.map +7 -0
  123. package/dist/modules/design_system/gallery/components/StyleAgents.js +139 -0
  124. package/dist/modules/design_system/gallery/components/StyleAgents.js.map +7 -0
  125. package/dist/modules/design_system/gallery/components/ThemeTokenReference.js +101 -0
  126. package/dist/modules/design_system/gallery/components/ThemeTokenReference.js.map +7 -0
  127. package/dist/modules/design_system/gallery/components/VariantPreview.js +2 -2
  128. package/dist/modules/design_system/gallery/components/VariantPreview.js.map +2 -2
  129. package/dist/modules/design_system/gallery/components/colorPreview.js +58 -0
  130. package/dist/modules/design_system/gallery/components/colorPreview.js.map +7 -0
  131. package/dist/modules/design_system/gallery/components/colorStudio.js +218 -0
  132. package/dist/modules/design_system/gallery/components/colorStudio.js.map +7 -0
  133. package/dist/modules/design_system/gallery/components/styleAgentBranding.js +46 -0
  134. package/dist/modules/design_system/gallery/components/styleAgentBranding.js.map +7 -0
  135. package/dist/modules/design_system/gallery/components/themeTokens.js +32 -0
  136. package/dist/modules/design_system/gallery/components/themeTokens.js.map +7 -0
  137. package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js +194 -0
  138. package/dist/modules/design_system/gallery/demos/ai-product-code.generated.js.map +7 -0
  139. package/dist/modules/design_system/gallery/demos/ai-product.js +423 -0
  140. package/dist/modules/design_system/gallery/demos/ai-product.js.map +7 -0
  141. package/dist/modules/design_system/gallery/demos/buttons.js +289 -0
  142. package/dist/modules/design_system/gallery/demos/buttons.js.map +7 -0
  143. package/dist/modules/design_system/gallery/demos/cryptocurrency.js +1708 -0
  144. package/dist/modules/design_system/gallery/demos/cryptocurrency.js.map +7 -0
  145. package/dist/modules/design_system/gallery/demos/feeds-code.generated.js +92 -0
  146. package/dist/modules/design_system/gallery/demos/feeds-code.generated.js.map +7 -0
  147. package/dist/modules/design_system/gallery/demos/feeds.js +148 -0
  148. package/dist/modules/design_system/gallery/demos/feeds.js.map +7 -0
  149. package/dist/modules/design_system/gallery/demos/file-upload.js +72 -0
  150. package/dist/modules/design_system/gallery/demos/file-upload.js.map +7 -0
  151. package/dist/modules/design_system/gallery/demos/foundation-reference-data.js +4875 -0
  152. package/dist/modules/design_system/gallery/demos/foundation-reference-data.js.map +7 -0
  153. package/dist/modules/design_system/gallery/demos/foundation-references.js +188 -0
  154. package/dist/modules/design_system/gallery/demos/foundation-references.js.map +7 -0
  155. package/dist/modules/design_system/gallery/demos/key-components-code.generated.js +6 -0
  156. package/dist/modules/design_system/gallery/demos/key-components-code.generated.js.map +7 -0
  157. package/dist/modules/design_system/gallery/demos/key-components.js +127 -0
  158. package/dist/modules/design_system/gallery/demos/key-components.js.map +7 -0
  159. package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js +98 -0
  160. package/dist/modules/design_system/gallery/demos/marketing-controls-code.generated.js.map +7 -0
  161. package/dist/modules/design_system/gallery/demos/marketing-controls.js +134 -0
  162. package/dist/modules/design_system/gallery/demos/marketing-controls.js.map +7 -0
  163. package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js +195 -0
  164. package/dist/modules/design_system/gallery/demos/marketing-widgets-code.generated.js.map +7 -0
  165. package/dist/modules/design_system/gallery/demos/marketing-widgets.js +586 -0
  166. package/dist/modules/design_system/gallery/demos/marketing-widgets.js.map +7 -0
  167. package/dist/modules/design_system/gallery/demos/menu-fixtures.js +40 -0
  168. package/dist/modules/design_system/gallery/demos/menu-fixtures.js.map +7 -0
  169. package/dist/modules/design_system/gallery/demos/menu-sources.js +295 -0
  170. package/dist/modules/design_system/gallery/demos/menu-sources.js.map +7 -0
  171. package/dist/modules/design_system/gallery/demos/menus.js +150 -0
  172. package/dist/modules/design_system/gallery/demos/menus.js.map +7 -0
  173. package/dist/modules/design_system/gallery/demos/rating-reviews.js +53 -0
  174. package/dist/modules/design_system/gallery/demos/rating-reviews.js.map +7 -0
  175. package/dist/modules/design_system/gallery/demos/registered-icons.js +104 -0
  176. package/dist/modules/design_system/gallery/demos/registered-icons.js.map +7 -0
  177. package/dist/modules/design_system/gallery/demos/selection-cards.js +99 -0
  178. package/dist/modules/design_system/gallery/demos/selection-cards.js.map +7 -0
  179. package/dist/modules/design_system/gallery/demos/shell-code.generated.js +189 -0
  180. package/dist/modules/design_system/gallery/demos/shell-code.generated.js.map +7 -0
  181. package/dist/modules/design_system/gallery/demos/shell.js +330 -0
  182. package/dist/modules/design_system/gallery/demos/shell.js.map +7 -0
  183. package/dist/modules/design_system/gallery/demos/source-artwork.js +120 -0
  184. package/dist/modules/design_system/gallery/demos/source-artwork.js.map +7 -0
  185. package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js +125 -0
  186. package/dist/modules/design_system/gallery/demos/source-filters-code.generated.js.map +7 -0
  187. package/dist/modules/design_system/gallery/demos/source-filters.js +185 -0
  188. package/dist/modules/design_system/gallery/demos/source-filters.js.map +7 -0
  189. package/dist/modules/design_system/gallery/demos/source-icons.js +69 -0
  190. package/dist/modules/design_system/gallery/demos/source-icons.js.map +7 -0
  191. package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js +150 -0
  192. package/dist/modules/design_system/gallery/demos/table-cells-code.generated.js.map +7 -0
  193. package/dist/modules/design_system/gallery/demos/table-cells.js +181 -0
  194. package/dist/modules/design_system/gallery/demos/table-cells.js.map +7 -0
  195. package/dist/modules/design_system/gallery/demos/text-inputs.js +113 -0
  196. package/dist/modules/design_system/gallery/demos/text-inputs.js.map +7 -0
  197. package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js +41 -0
  198. package/dist/modules/design_system/gallery/demos/time-picker-code.generated.js.map +7 -0
  199. package/dist/modules/design_system/gallery/demos/time-picker.js +41 -0
  200. package/dist/modules/design_system/gallery/demos/time-picker.js.map +7 -0
  201. package/dist/modules/design_system/gallery/entries/banners.js +123 -60
  202. package/dist/modules/design_system/gallery/entries/banners.js.map +2 -2
  203. package/dist/modules/design_system/gallery/entries/buttons-snippets.js +433 -0
  204. package/dist/modules/design_system/gallery/entries/buttons-snippets.js.map +7 -0
  205. package/dist/modules/design_system/gallery/entries/buttons.js +127 -148
  206. package/dist/modules/design_system/gallery/entries/buttons.js.map +2 -2
  207. package/dist/modules/design_system/gallery/entries/charts.js +532 -186
  208. package/dist/modules/design_system/gallery/entries/charts.js.map +2 -2
  209. package/dist/modules/design_system/gallery/entries/dates.js +191 -3
  210. package/dist/modules/design_system/gallery/entries/dates.js.map +2 -2
  211. package/dist/modules/design_system/gallery/entries/detail.js +118 -77
  212. package/dist/modules/design_system/gallery/entries/detail.js.map +2 -2
  213. package/dist/modules/design_system/gallery/entries/display.js +1449 -240
  214. package/dist/modules/design_system/gallery/entries/display.js.map +2 -2
  215. package/dist/modules/design_system/gallery/entries/feedback.js +1058 -132
  216. package/dist/modules/design_system/gallery/entries/feedback.js.map +2 -2
  217. package/dist/modules/design_system/gallery/entries/filters.js +231 -103
  218. package/dist/modules/design_system/gallery/entries/filters.js.map +2 -2
  219. package/dist/modules/design_system/gallery/entries/foundations.js +368 -57
  220. package/dist/modules/design_system/gallery/entries/foundations.js.map +2 -2
  221. package/dist/modules/design_system/gallery/entries/icons.js +29 -91
  222. package/dist/modules/design_system/gallery/entries/icons.js.map +2 -2
  223. package/dist/modules/design_system/gallery/entries/inputs-snippets.js +154 -0
  224. package/dist/modules/design_system/gallery/entries/inputs-snippets.js.map +7 -0
  225. package/dist/modules/design_system/gallery/entries/inputs.js +2043 -306
  226. package/dist/modules/design_system/gallery/entries/inputs.js.map +2 -2
  227. package/dist/modules/design_system/gallery/entries/library.js +20 -0
  228. package/dist/modules/design_system/gallery/entries/library.js.map +7 -0
  229. package/dist/modules/design_system/gallery/entries/messages.js +121 -103
  230. package/dist/modules/design_system/gallery/entries/messages.js.map +2 -2
  231. package/dist/modules/design_system/gallery/entries/navigation.js +676 -103
  232. package/dist/modules/design_system/gallery/entries/navigation.js.map +2 -2
  233. package/dist/modules/design_system/gallery/entries/notifications.js +131 -78
  234. package/dist/modules/design_system/gallery/entries/notifications.js.map +2 -2
  235. package/dist/modules/design_system/gallery/entries/overlays.js +611 -203
  236. package/dist/modules/design_system/gallery/entries/overlays.js.map +2 -2
  237. package/dist/modules/design_system/gallery/entries/scaffolding.js +1175 -120
  238. package/dist/modules/design_system/gallery/entries/scaffolding.js.map +2 -2
  239. package/dist/modules/design_system/gallery/entries/schedule.js +79 -54
  240. package/dist/modules/design_system/gallery/entries/schedule.js.map +2 -2
  241. package/dist/modules/design_system/gallery/presentation.js +108 -0
  242. package/dist/modules/design_system/gallery/presentation.js.map +7 -0
  243. package/dist/modules/design_system/gallery/registry.js +8 -5
  244. package/dist/modules/design_system/gallery/registry.js.map +2 -2
  245. package/dist/modules/design_system/gallery/source-library.generated.js +5 -0
  246. package/dist/modules/design_system/gallery/source-library.generated.js.map +7 -0
  247. package/dist/modules/design_system/i18n/de.json +7167 -0
  248. package/dist/modules/design_system/i18n/en.json +7167 -0
  249. package/dist/modules/design_system/i18n/es.json +7167 -0
  250. package/dist/modules/design_system/i18n/ko.json +7167 -0
  251. package/dist/modules/design_system/i18n/pl.json +7167 -0
  252. package/dist/modules/push_notifications/lib/queue.js +1 -31
  253. package/dist/modules/push_notifications/lib/queue.js.map +2 -2
  254. package/jest.config.cjs +1 -0
  255. package/jest.mocks/image.js +1 -0
  256. package/package.json +8 -9
  257. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +50 -0
  258. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
  259. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +2 -1
  260. package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -2
  261. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +2 -1
  262. package/src/modules/customers/lib/detailHelpers.ts +4 -0
  263. package/src/modules/customers/lib/todoCompatibility.ts +10 -0
  264. package/src/modules/design_system/backend/design-system/page.meta.ts +3 -2
  265. package/src/modules/design_system/gallery/assets/README.md +30 -0
  266. package/src/modules/design_system/gallery/assets/ai-prompt-image.png +0 -0
  267. package/src/modules/design_system/gallery/assets/avatar-illustration.png +0 -0
  268. package/src/modules/design_system/gallery/assets/avatar-memoji.png +0 -0
  269. package/src/modules/design_system/gallery/assets/avatar-photo.png +0 -0
  270. package/src/modules/design_system/gallery/assets/blog-photo-0.png +0 -0
  271. package/src/modules/design_system/gallery/assets/contact-support-portrait.png +0 -0
  272. package/src/modules/design_system/gallery/assets/crypto-ada.png +0 -0
  273. package/src/modules/design_system/gallery/assets/crypto-avax.png +0 -0
  274. package/src/modules/design_system/gallery/assets/crypto-bnb.png +0 -0
  275. package/src/modules/design_system/gallery/assets/crypto-brand.png +0 -0
  276. package/src/modules/design_system/gallery/assets/crypto-btc.png +0 -0
  277. package/src/modules/design_system/gallery/assets/crypto-clock.svg +3 -0
  278. package/src/modules/design_system/gallery/assets/crypto-crown.svg +3 -0
  279. package/src/modules/design_system/gallery/assets/crypto-dot.png +0 -0
  280. package/src/modules/design_system/gallery/assets/crypto-eth.png +0 -0
  281. package/src/modules/design_system/gallery/assets/crypto-hbar.png +0 -0
  282. package/src/modules/design_system/gallery/assets/crypto-history.svg +3 -0
  283. package/src/modules/design_system/gallery/assets/crypto-link.svg +3 -0
  284. package/src/modules/design_system/gallery/assets/crypto-menu.svg +3 -0
  285. package/src/modules/design_system/gallery/assets/crypto-metamask.png +0 -0
  286. package/src/modules/design_system/gallery/assets/crypto-more.svg +3 -0
  287. package/src/modules/design_system/gallery/assets/crypto-phantom.png +0 -0
  288. package/src/modules/design_system/gallery/assets/crypto-rewards.svg +3 -0
  289. package/src/modules/design_system/gallery/assets/crypto-sad-face.png +0 -0
  290. package/src/modules/design_system/gallery/assets/crypto-sol.png +0 -0
  291. package/src/modules/design_system/gallery/assets/crypto-star.svg +3 -0
  292. package/src/modules/design_system/gallery/assets/crypto-status-cancelled.svg +3 -0
  293. package/src/modules/design_system/gallery/assets/crypto-status-pending.svg +3 -0
  294. package/src/modules/design_system/gallery/assets/crypto-status-success.svg +3 -0
  295. package/src/modules/design_system/gallery/assets/crypto-status-warning.svg +3 -0
  296. package/src/modules/design_system/gallery/assets/crypto-trx.png +0 -0
  297. package/src/modules/design_system/gallery/assets/crypto-usdc.png +0 -0
  298. package/src/modules/design_system/gallery/assets/crypto-usdt.png +0 -0
  299. package/src/modules/design_system/gallery/assets/crypto-walletconnect.png +0 -0
  300. package/src/modules/design_system/gallery/assets/crypto-wei.png +0 -0
  301. package/src/modules/design_system/gallery/assets/crypto-xrp.png +0 -0
  302. package/src/modules/design_system/gallery/assets/cryptocurrency-artwork.ts +63 -0
  303. package/src/modules/design_system/gallery/assets/github-logo.png +0 -0
  304. package/src/modules/design_system/gallery/assets/images.d.ts +9 -0
  305. package/src/modules/design_system/gallery/assets/input-european-union.svg +18 -0
  306. package/src/modules/design_system/gallery/assets/landing-teams-01.png +0 -0
  307. package/src/modules/design_system/gallery/assets/marketing-artwork.ts +79 -0
  308. package/src/modules/design_system/gallery/assets/menu-apex.svg +18 -0
  309. package/src/modules/design_system/gallery/assets/menu-mastercard.svg +7 -0
  310. package/src/modules/design_system/gallery/assets/menu-spotify.svg +4 -0
  311. package/src/modules/design_system/gallery/assets/menu-united-states.svg +16 -0
  312. package/src/modules/design_system/gallery/assets/select-google.png +0 -0
  313. package/src/modules/design_system/gallery/assets/select-poland.png +0 -0
  314. package/src/modules/design_system/gallery/assets/selection-artwork.ts +1 -0
  315. package/src/modules/design_system/gallery/assets/social-apple-filled.svg +4 -0
  316. package/src/modules/design_system/gallery/assets/social-apple-stroke.svg +4 -0
  317. package/src/modules/design_system/gallery/assets/social-dropbox-filled.svg +5 -0
  318. package/src/modules/design_system/gallery/assets/social-dropbox-stroke.svg +5 -0
  319. package/src/modules/design_system/gallery/assets/social-facebook-filled.svg +4 -0
  320. package/src/modules/design_system/gallery/assets/social-facebook-stroke.svg +10 -0
  321. package/src/modules/design_system/gallery/assets/social-github-filled.svg +4 -0
  322. package/src/modules/design_system/gallery/assets/social-github-stroke.svg +4 -0
  323. package/src/modules/design_system/gallery/assets/social-google-filled.svg +7 -0
  324. package/src/modules/design_system/gallery/assets/social-google-stroke.svg +7 -0
  325. package/src/modules/design_system/gallery/assets/social-linkedin-filled.svg +4 -0
  326. package/src/modules/design_system/gallery/assets/social-linkedin-stroke.svg +4 -0
  327. package/src/modules/design_system/gallery/assets/social-x-filled.svg +4 -0
  328. package/src/modules/design_system/gallery/assets/social-x-stroke.svg +4 -0
  329. package/src/modules/design_system/gallery/assets/style-studio-product.png +0 -0
  330. package/src/modules/design_system/gallery/assets/upload-images.ts +5 -0
  331. package/src/modules/design_system/gallery/components/BrandStylePreview.tsx +94 -0
  332. package/src/modules/design_system/gallery/components/CodeSnippet.tsx +34 -34
  333. package/src/modules/design_system/gallery/components/ColorPlayground.tsx +324 -0
  334. package/src/modules/design_system/gallery/components/ColorStudioCompositions.tsx +96 -0
  335. package/src/modules/design_system/gallery/components/ColorStudioSpecimen.tsx +164 -0
  336. package/src/modules/design_system/gallery/components/CompactEntrySpecimen.tsx +65 -0
  337. package/src/modules/design_system/gallery/components/ComponentIndex.tsx +47 -0
  338. package/src/modules/design_system/gallery/components/DesignSystemHome.tsx +66 -0
  339. package/src/modules/design_system/gallery/components/EntryCard.tsx +52 -37
  340. package/src/modules/design_system/gallery/components/EntryHeader.tsx +80 -0
  341. package/src/modules/design_system/gallery/components/FamilyEntryPreview.tsx +22 -0
  342. package/src/modules/design_system/gallery/components/FoundationGuide.tsx +43 -0
  343. package/src/modules/design_system/gallery/components/GalleryLink.tsx +19 -0
  344. package/src/modules/design_system/gallery/components/GalleryNavigation.tsx +86 -0
  345. package/src/modules/design_system/gallery/components/GalleryShell.tsx +149 -63
  346. package/src/modules/design_system/gallery/components/IconsBrowser.tsx +40 -0
  347. package/src/modules/design_system/gallery/components/SourceLibrary.tsx +90 -0
  348. package/src/modules/design_system/gallery/components/StyleAgents.tsx +115 -0
  349. package/src/modules/design_system/gallery/components/ThemeTokenReference.tsx +76 -0
  350. package/src/modules/design_system/gallery/components/VariantPreview.tsx +2 -2
  351. package/src/modules/design_system/gallery/components/colorPreview.ts +63 -0
  352. package/src/modules/design_system/gallery/components/colorStudio.ts +260 -0
  353. package/src/modules/design_system/gallery/components/styleAgentBranding.ts +42 -0
  354. package/src/modules/design_system/gallery/components/themeTokens.ts +26 -0
  355. package/src/modules/design_system/gallery/demos/ai-product-code.generated.ts +3 -0
  356. package/src/modules/design_system/gallery/demos/ai-product.tsx +188 -0
  357. package/src/modules/design_system/gallery/demos/buttons.tsx +331 -0
  358. package/src/modules/design_system/gallery/demos/cryptocurrency.tsx +1943 -0
  359. package/src/modules/design_system/gallery/demos/feeds-code.generated.ts +4 -0
  360. package/src/modules/design_system/gallery/demos/feeds.tsx +82 -0
  361. package/src/modules/design_system/gallery/demos/file-upload.tsx +61 -0
  362. package/src/modules/design_system/gallery/demos/foundation-reference-data.ts +4872 -0
  363. package/src/modules/design_system/gallery/demos/foundation-references.tsx +140 -0
  364. package/src/modules/design_system/gallery/demos/key-components-code.generated.ts +3 -0
  365. package/src/modules/design_system/gallery/demos/key-components.tsx +106 -0
  366. package/src/modules/design_system/gallery/demos/marketing-controls-code.generated.ts +3 -0
  367. package/src/modules/design_system/gallery/demos/marketing-controls.tsx +92 -0
  368. package/src/modules/design_system/gallery/demos/marketing-widgets-code.generated.ts +3 -0
  369. package/src/modules/design_system/gallery/demos/marketing-widgets.tsx +189 -0
  370. package/src/modules/design_system/gallery/demos/menu-fixtures.tsx +48 -0
  371. package/src/modules/design_system/gallery/demos/menu-sources.ts +5 -0
  372. package/src/modules/design_system/gallery/demos/menus.tsx +122 -0
  373. package/src/modules/design_system/gallery/demos/rating-reviews.tsx +44 -0
  374. package/src/modules/design_system/gallery/demos/registered-icons.tsx +117 -0
  375. package/src/modules/design_system/gallery/demos/selection-cards.tsx +97 -0
  376. package/src/modules/design_system/gallery/demos/shell-code.generated.ts +3 -0
  377. package/src/modules/design_system/gallery/demos/shell.tsx +183 -0
  378. package/src/modules/design_system/gallery/demos/source-artwork.tsx +88 -0
  379. package/src/modules/design_system/gallery/demos/source-filters-code.generated.ts +3 -0
  380. package/src/modules/design_system/gallery/demos/source-filters.tsx +119 -0
  381. package/src/modules/design_system/gallery/demos/source-icons.tsx +62 -0
  382. package/src/modules/design_system/gallery/demos/table-cells-code.generated.ts +3 -0
  383. package/src/modules/design_system/gallery/demos/table-cells.tsx +144 -0
  384. package/src/modules/design_system/gallery/demos/text-inputs.tsx +90 -0
  385. package/src/modules/design_system/gallery/demos/time-picker-code.generated.ts +5 -0
  386. package/src/modules/design_system/gallery/demos/time-picker.tsx +31 -0
  387. package/src/modules/design_system/gallery/entries/banners.tsx +143 -94
  388. package/src/modules/design_system/gallery/entries/buttons-snippets.ts +432 -0
  389. package/src/modules/design_system/gallery/entries/buttons.tsx +125 -196
  390. package/src/modules/design_system/gallery/entries/charts.tsx +545 -233
  391. package/src/modules/design_system/gallery/entries/dates.tsx +171 -3
  392. package/src/modules/design_system/gallery/entries/detail.tsx +142 -115
  393. package/src/modules/design_system/gallery/entries/display.tsx +1691 -319
  394. package/src/modules/design_system/gallery/entries/feedback.tsx +1082 -169
  395. package/src/modules/design_system/gallery/entries/filters.tsx +238 -143
  396. package/src/modules/design_system/gallery/entries/foundations.tsx +404 -61
  397. package/src/modules/design_system/gallery/entries/icons.tsx +31 -117
  398. package/src/modules/design_system/gallery/entries/inputs-snippets.ts +156 -0
  399. package/src/modules/design_system/gallery/entries/inputs.tsx +2146 -503
  400. package/src/modules/design_system/gallery/entries/library.tsx +16 -0
  401. package/src/modules/design_system/gallery/entries/messages.tsx +114 -106
  402. package/src/modules/design_system/gallery/entries/navigation.tsx +782 -192
  403. package/src/modules/design_system/gallery/entries/notifications.tsx +145 -120
  404. package/src/modules/design_system/gallery/entries/overlays.tsx +696 -280
  405. package/src/modules/design_system/gallery/entries/scaffolding.tsx +1192 -158
  406. package/src/modules/design_system/gallery/entries/schedule.tsx +72 -58
  407. package/src/modules/design_system/gallery/presentation.ts +106 -0
  408. package/src/modules/design_system/gallery/registry.ts +8 -6
  409. package/src/modules/design_system/gallery/source-library.generated.ts +2 -0
  410. package/src/modules/design_system/i18n/de.json +7112 -3
  411. package/src/modules/design_system/i18n/en.json +7112 -3
  412. package/src/modules/design_system/i18n/es.json +7112 -3
  413. package/src/modules/design_system/i18n/ko.json +7112 -3
  414. package/src/modules/design_system/i18n/pl.json +7112 -3
  415. package/src/modules/push_notifications/AGENTS.md +9 -5
  416. package/src/modules/push_notifications/lib/queue.ts +15 -44
@@ -1,12 +1,20 @@
1
+ import { Banner } from '@open-mercato/ui/primitives/banner'
1
2
  import * as React from 'react'
3
+ import { NotificationFeedExample } from '../demos/feeds'
4
+ import { notificationFeedExampleCode } from '../demos/feeds-code.generated'
2
5
  import { Inbox, RefreshCw, Search, Settings, UserPlus } from 'lucide-react'
3
6
  import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
4
7
  import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
8
+ import { EmptyStateIllustration } from '@open-mercato/ui/primitives/empty-state-illustration'
5
9
  import { Skeleton } from '@open-mercato/ui/primitives/skeleton'
6
10
  import { CircularProgress, Progress } from '@open-mercato/ui/primitives/progress'
7
11
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
12
+ import { RatingCellDemo, RatingReviewDemo } from '../demos/rating-reviews'
8
13
  import { Rating } from '@open-mercato/ui/primitives/rating'
9
- import { StepIndicator, type StepIndicatorStep } from '@open-mercato/ui/primitives/step-indicator'
14
+ import { RatingBar, type RatingBarProps } from '@open-mercato/ui/primitives/rating-bar'
15
+ import { Textarea } from '@open-mercato/ui/primitives/textarea'
16
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
17
+ import { StepIndicator, StepperDots, type StepIndicatorStep } from '@open-mercato/ui/primitives/step-indicator'
10
18
  import { Notification } from '@open-mercato/ui/primitives/notification'
11
19
  import {
12
20
  NotificationFeed,
@@ -26,8 +34,87 @@ import type { GalleryEntry } from '../types'
26
34
  // (enforced by the registry-integrity test) and is always reviewed alongside
27
35
  // its sibling `render`.
28
36
 
37
+
38
+ const bannerStatuses = ['error', 'warning', 'success', 'information', 'feature'] as const
39
+ function BannerExamples({ appearance, minimal = false }: { appearance: 'filled' | 'light' | 'lighter' | 'stroke'; minimal?: boolean }) {
40
+ const t = useT()
41
+ const [dismissed, setDismissed] = React.useState<string[]>([])
42
+ const [details, setDetails] = React.useState(false)
43
+ return <div className="w-full space-y-4">
44
+ {bannerStatuses.filter(status => !dismissed.includes(status)).map(status => <Banner key={status} status={status} style={appearance} title={t('design_system.gallery.samples.banner.title')} description={minimal ? undefined : t('design_system.gallery.samples.banner.description')} showIcon={!minimal} onDismiss={minimal ? undefined : () => setDismissed(value => [...value, status])} action={minimal ? undefined : <Button variant="link" className="h-auto p-0 text-inherit underline" onClick={() => setDetails(value => !value)}>{t('design_system.gallery.samples.banner.details')}</Button>} />)}
45
+ {details ? <p className="text-sm text-muted-foreground">{t('design_system.gallery.samples.banner.detailsBody')}</p> : null}
46
+ {dismissed.length ? <Button variant="outline" size="sm" onClick={() => setDismissed([])}>{t('design_system.gallery.samples.banner.restore')}</Button> : null}
47
+ </div>
48
+ }
49
+ function bannerExampleCode(appearance: 'filled' | 'light' | 'lighter' | 'stroke', minimal = false) {
50
+ return `import * as React from 'react'
51
+ import { Banner } from '@open-mercato/ui/primitives/banner'
52
+ import { Button } from '@open-mercato/ui/primitives/button'
53
+
54
+ function Example() {
55
+ const [dismissed, setDismissed] = React.useState<string[]>([])
56
+ const [details, setDetails] = React.useState(false)
57
+ return <div className="w-full space-y-4">
58
+ {(['error', 'warning', 'success', 'information', 'feature'] as const).filter(status => !dismissed.includes(status)).map(status => (
59
+ <Banner key={status} status={status} style="${appearance}" title="Workspace update"${minimal ? ' showIcon={false}' : ' description="Your team can review the latest changes." onDismiss={() => setDismissed(value => [...value, status])} action={<Button variant="link" className="h-auto p-0 text-inherit underline" onClick={() => setDetails(value => !value)}>View details</Button>}'} />
60
+ ))}
61
+ {details ? <p className="text-sm text-muted-foreground">This example changes local preview state.</p> : null}
62
+ {dismissed.length ? <Button variant="outline" size="sm" onClick={() => setDismissed([])}>Restore banners</Button> : null}
63
+ </div>
64
+ }`
65
+ }
66
+ const bannerEntry: GalleryEntry = {
67
+ id: 'banner', title: 'Banner', importPath: '@open-mercato/ui/primitives/banner', figmaNodeId: '224:2249', docsAnchor: '#banner',
68
+ variants: [
69
+ { id: 'filled', title: '5 statuses / Filled', render: () => <BannerExamples appearance="filled" />, code: bannerExampleCode('filled') },
70
+ { id: 'light', title: '5 statuses / Light', render: () => <BannerExamples appearance="light" />, code: bannerExampleCode('light') },
71
+ { id: 'lighter', title: '5 statuses / Lighter', render: () => <BannerExamples appearance="lighter" />, code: bannerExampleCode('lighter') },
72
+ { id: 'stroke', title: '5 statuses / Stroke', render: () => <BannerExamples appearance="stroke" />, code: bannerExampleCode('stroke') },
73
+ { id: 'minimal', title: 'Title only', render: () => <BannerExamples appearance="lighter" minimal />, code: bannerExampleCode('lighter', true) },
74
+ ],
75
+ }
76
+
77
+ function AlertSourceExamples({ size }: { size: 'xs' | 'sm' | 'default' }) {
78
+ const t = useT()
79
+ const [hidden, setHidden] = React.useState<string[]>([])
80
+ const [details, setDetails] = React.useState(false)
81
+ return <div className="grid w-full gap-5 lg:grid-cols-2">{(['filled', 'light', 'lighter', 'stroke'] as const).map(appearance => <div key={appearance} className="space-y-3">
82
+ <p className="text-xs text-muted-foreground">{appearance}</p>
83
+ {bannerStatuses.filter(status => !hidden.includes(`${appearance}-${status}`)).map(status => <Alert className="max-w-[390px]" key={status} status={status} style={appearance} size={size} dismissible onDismiss={() => setHidden(value => [...value, `${appearance}-${status}`])} dismissAriaLabel={t('ui.banner.dismiss', 'Dismiss banner')} action={size === 'default' ? undefined : <Button variant="link" className={size === 'xs' ? 'h-auto p-0 text-xs leading-4 text-inherit underline' : 'h-auto p-0 text-sm leading-5 text-inherit underline'} onClick={() => setDetails(value => !value)}>{t('design_system.gallery.samples.banner.details')}</Button>}
84
+ footer={size === 'default' ? <><Button variant="link" className="h-auto p-0 text-inherit underline" onClick={() => setDetails(value => !value)}>{t('design_system.gallery.samples.banner.details')}</Button><span aria-hidden="true" className="opacity-50">∙</span><Button variant="link" className="h-auto p-0 text-inherit" onClick={() => setHidden(value => [...value, `${appearance}-${status}`])}>{t('ui.dialog.close.ariaLabel', 'Close')}</Button></> : undefined}>
85
+ {size === 'default' ? <><AlertTitle>{t('design_system.gallery.samples.banner.title')}</AlertTitle><AlertDescription>{t('design_system.gallery.samples.overlay.description')}</AlertDescription></> : t('design_system.gallery.samples.banner.title')}
86
+ </Alert>)}
87
+ </div>)}
88
+ {details ? <p role="status" className="text-sm text-muted-foreground">{t('design_system.gallery.samples.banner.detailsBody')}</p> : null}
89
+ {hidden.length ? <Button variant="outline" size="sm" onClick={() => setHidden([])}>{t('design_system.gallery.samples.feeds.restore')}</Button> : null}</div>
90
+ }
91
+
92
+ function alertSourceCode(size: 'xs' | 'sm' | 'default') {
93
+ return `import * as React from 'react'
94
+ import { Alert, AlertTitle, AlertDescription } from '@open-mercato/ui/primitives/alert'
95
+ import { Button } from '@open-mercato/ui/primitives/button'
96
+
97
+ function Example() {
98
+ const [hidden, setHidden] = React.useState<string[]>([])
99
+ const [details, setDetails] = React.useState(false)
100
+ const statuses = ['error', 'warning', 'success', 'information', 'feature'] as const
101
+ return <div className="grid w-full gap-5 lg:grid-cols-2">
102
+ {(['filled', 'light', 'lighter', 'stroke'] as const).map(appearance => <div key={appearance} className="space-y-3">
103
+ <p className="text-xs text-muted-foreground">{appearance}</p>
104
+ {statuses.filter(status => !hidden.includes(appearance + '-' + status)).map(status => <Alert className="max-w-[390px]" key={status} status={status} style={appearance} size="${size}" dismissible onDismiss={() => setHidden(value => [...value, appearance + '-' + status])}
105
+ ${size === 'default' ? 'footer' : 'action'}={<Button variant="link" className="h-auto p-0 ${size === 'xs' ? 'text-xs leading-4' : 'text-sm leading-5'} text-inherit underline" onClick={() => setDetails(value => !value)}>View details</Button>}>
106
+ ${size === 'default' ? '<AlertTitle>Workspace update</AlertTitle><AlertDescription>Choose how these changes apply to your workspace.</AlertDescription>' : 'Workspace update'}
107
+ </Alert>)}
108
+ </div>)}
109
+ {details ? <p role="status">This example changes local preview state.</p> : null}
110
+ {hidden.length ? <Button size="sm" variant="outline" onClick={() => setHidden([])}>Restore examples</Button> : null}
111
+ </div>
112
+ }`
113
+ }
114
+
29
115
  const alertEntry: GalleryEntry = {
30
116
  id: 'alert',
117
+ figmaNodeId: '169:2399',
31
118
  title: 'Alert',
32
119
  importPath: '@open-mercato/ui/primitives/alert',
33
120
  usage: {
@@ -38,18 +125,13 @@ const alertEntry: GalleryEntry = {
38
125
  dont: ['The legacy variant prop is deprecated — lint flags it.', 'Never build ad-hoc colored notice boxes; this is the primitive for them.'],
39
126
  },
40
127
  variants: [
128
+ { id: 'source-xs', title: '20 status/style combinations / 32px', render: () => <AlertSourceExamples size="xs" />, code: alertSourceCode('xs') },
129
+ { id: 'source-sm', title: '20 status/style combinations / 36px', render: () => <AlertSourceExamples size="sm" />, code: alertSourceCode('sm') },
130
+ { id: 'source-large', title: '20 status/style combinations / large', render: () => <AlertSourceExamples size="default" />, code: alertSourceCode('default') },
41
131
  {
42
132
  id: 'statuses-light',
43
133
  title: 'Statuses (light)',
44
- render: () => (
45
- <div className="flex w-full max-w-md flex-col gap-2">
46
- <Alert status="error" style="light">Payment failed — the card was declined.</Alert>
47
- <Alert status="warning" style="light">Inventory sync is running behind schedule.</Alert>
48
- <Alert status="success" style="light">Order #10231 was fulfilled.</Alert>
49
- <Alert status="information" style="light">Prices include VAT for EU customers.</Alert>
50
- <Alert status="feature" style="light">Bulk editing is now available in the catalog.</Alert>
51
- </div>
52
- ),
134
+ render: () => <AlertFeedbackStatusesLightSample />,
53
135
  code: `import { Alert } from '@open-mercato/ui/primitives/alert'
54
136
 
55
137
  <Alert status="error" style="light">Payment failed — the card was declined.</Alert>
@@ -61,14 +143,7 @@ const alertEntry: GalleryEntry = {
61
143
  {
62
144
  id: 'filled',
63
145
  title: 'filled',
64
- render: () => (
65
- <div className="w-full max-w-md">
66
- <Alert status="error" style="filled" size="default">
67
- <AlertTitle>Import failed</AlertTitle>
68
- <AlertDescription>14 rows were rejected — download the error report to review them.</AlertDescription>
69
- </Alert>
70
- </div>
71
- ),
146
+ render: () => <AlertFeedbackFilledSample />,
72
147
  code: `import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
73
148
 
74
149
  <Alert status="error" style="filled" size="default">
@@ -79,14 +154,7 @@ const alertEntry: GalleryEntry = {
79
154
  {
80
155
  id: 'stroke',
81
156
  title: 'stroke',
82
- render: () => (
83
- <div className="w-full max-w-md">
84
- <Alert status="success" style="stroke" size="default">
85
- <AlertTitle>Backup complete</AlertTitle>
86
- <AlertDescription>The nightly snapshot finished without warnings.</AlertDescription>
87
- </Alert>
88
- </div>
89
- ),
157
+ render: () => <AlertFeedbackStrokeSample />,
90
158
  code: `import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
91
159
 
92
160
  <Alert status="success" style="stroke" size="default">
@@ -97,16 +165,7 @@ const alertEntry: GalleryEntry = {
97
165
  {
98
166
  id: 'sizes',
99
167
  title: 'Sizes',
100
- render: () => (
101
- <div className="flex w-full max-w-md flex-col gap-2">
102
- <Alert status="information" size="xs">X-Small — inline form hint.</Alert>
103
- <Alert status="information" size="sm">Small — the default single-line alert.</Alert>
104
- <Alert status="information" size="default">
105
- <AlertTitle>Large</AlertTitle>
106
- <AlertDescription>Multi-line alert with a title and a description body.</AlertDescription>
107
- </Alert>
108
- </div>
109
- ),
168
+ render: () => <AlertFeedbackSizesSample />,
110
169
  code: `import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
111
170
 
112
171
  <Alert status="information" size="xs">X-Small — inline form hint.</Alert>
@@ -119,18 +178,7 @@ const alertEntry: GalleryEntry = {
119
178
  {
120
179
  id: 'dismissible-with-action',
121
180
  title: 'Dismissible with action',
122
- render: () => (
123
- <div className="w-full max-w-md">
124
- <Alert
125
- status="warning"
126
- style="lighter"
127
- dismissible
128
- action={<LinkButton variant="black" underline="always">Review</LinkButton>}
129
- >
130
- 3 products are missing tax categories.
131
- </Alert>
132
- </div>
133
- ),
181
+ render: () => <AlertFeedbackDismissibleWithActionSample />,
134
182
  code: `import { Alert } from '@open-mercato/ui/primitives/alert'
135
183
  import { LinkButton } from '@open-mercato/ui/primitives/link-button'
136
184
 
@@ -152,17 +200,27 @@ const emptyStateEntry: GalleryEntry = {
152
200
  title: 'EmptyState',
153
201
  importPath: '@open-mercato/ui/primitives/empty-state',
154
202
  variants: [
203
+ {
204
+ id: 'source-illustration', title: 'Open Mercato',
205
+ render: () => <IllustratedEmptyStateExample />,
206
+ code: `import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
207
+ import { EmptyStateIllustration } from '@open-mercato/ui/primitives/empty-state-illustration'
208
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
209
+
210
+ function RecordsEmptyState() {
211
+ const t = useT()
212
+ return <EmptyState
213
+ variant="subtle"
214
+ title={t('design_system.gallery.samples.emptyArtwork.records.title')}
215
+ description={t('design_system.gallery.samples.emptyArtwork.records.description')}
216
+ illustration={<EmptyStateIllustration kind="mercato-records" />}
217
+ />
218
+ }`,
219
+ },
155
220
  {
156
221
  id: 'default',
157
222
  title: 'default',
158
- render: () => (
159
- <div className="w-full max-w-md">
160
- <EmptyState
161
- title="No customers yet"
162
- description="Create your first customer to start tracking opportunities."
163
- />
164
- </div>
165
- ),
223
+ render: () => <EmptyStateFeedbackDefaultSample />,
166
224
  code: `import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
167
225
 
168
226
  <EmptyState
@@ -173,16 +231,7 @@ const emptyStateEntry: GalleryEntry = {
173
231
  {
174
232
  id: 'with-actions',
175
233
  title: 'With icon and actions',
176
- render: () => (
177
- <div className="w-full max-w-md">
178
- <EmptyState
179
- icon={<Inbox className="size-8" />}
180
- title="No orders found"
181
- description="Orders will appear here once your storefront starts selling."
182
- actions={<Button size="sm">Create order</Button>}
183
- />
184
- </div>
185
- ),
234
+ render: () => <EmptyStateFeedbackWithActionsSample />,
186
235
  code: `import { Inbox } from 'lucide-react'
187
236
  import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
188
237
  import { Button } from '@open-mercato/ui/primitives/button'
@@ -197,17 +246,7 @@ import { Button } from '@open-mercato/ui/primitives/button'
197
246
  {
198
247
  id: 'subtle',
199
248
  title: 'subtle',
200
- render: () => (
201
- <div className="w-full max-w-md">
202
- <EmptyState
203
- variant="subtle"
204
- size="sm"
205
- icon={<Search className="size-5" />}
206
- title="No results"
207
- description="Try a different search term or clear the filters."
208
- />
209
- </div>
210
- ),
249
+ render: () => <EmptyStateFeedbackSubtleSample />,
211
250
  code: `import { Search } from 'lucide-react'
212
251
  import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
213
252
 
@@ -222,6 +261,64 @@ import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
222
261
  ],
223
262
  }
224
263
 
264
+ function IllustratedEmptyStateExample() {
265
+ const t = useT()
266
+ return <EmptyState
267
+ variant="subtle"
268
+ title={t('design_system.gallery.samples.emptyArtwork.records.title')}
269
+ description={t('design_system.gallery.samples.emptyArtwork.records.description')}
270
+ illustration={<EmptyStateIllustration kind="mercato-records" />}
271
+ />
272
+ }
273
+
274
+ const emptyArtworkSubjects = ['records', 'search', 'files', 'messages'] as const
275
+
276
+ function EmptyIllustrationsExample() {
277
+ const t = useT()
278
+ return <div className="grid w-full grid-cols-1 gap-6 lg:grid-cols-2">
279
+ {emptyArtworkSubjects.map(subject => <EmptyState
280
+ key={subject}
281
+ variant="subtle"
282
+ className="min-w-0 border border-border bg-background"
283
+ title={t(`design_system.gallery.samples.emptyArtwork.${subject}.title`)}
284
+ description={t(`design_system.gallery.samples.emptyArtwork.${subject}.description`)}
285
+ illustration={<EmptyStateIllustration kind={`mercato-${subject}`} />}
286
+ />)}
287
+ </div>
288
+ }
289
+
290
+ const emptyStateIllustrationEntry: GalleryEntry = {
291
+ id: 'empty-state-illustration',
292
+ title: 'EmptyStateIllustration',
293
+ importPath: '@open-mercato/ui/primitives/empty-state-illustration',
294
+ variants: [
295
+ {
296
+ id: 'mercato',
297
+ title: 'Open Mercato',
298
+ render: () => <EmptyIllustrationsExample />,
299
+ code: `import { EmptyStateIllustration } from '@open-mercato/ui/primitives/empty-state-illustration'
300
+ import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
301
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
302
+
303
+ const subjects = ['records', 'search', 'files', 'messages'] as const
304
+
305
+ function EmptyStateCollection() {
306
+ const t = useT()
307
+ return <div className="grid w-full grid-cols-1 gap-6 lg:grid-cols-2">
308
+ {subjects.map(subject => <EmptyState
309
+ key={subject}
310
+ variant="subtle"
311
+ className="min-w-0 border border-border bg-background"
312
+ title={t(\`design_system.gallery.samples.emptyArtwork.\${subject}.title\`)}
313
+ description={t(\`design_system.gallery.samples.emptyArtwork.\${subject}.description\`)}
314
+ illustration={<EmptyStateIllustration kind={\`mercato-\${subject}\`} />}
315
+ />)}
316
+ </div>
317
+ }`,
318
+ },
319
+ ],
320
+ }
321
+
225
322
  const skeletonEntry: GalleryEntry = {
226
323
  id: 'skeleton',
227
324
  title: 'Skeleton',
@@ -280,9 +377,44 @@ const skeletonEntry: GalleryEntry = {
280
377
  ],
281
378
  }
282
379
 
380
+ function CircularSourceDemo() {
381
+ const t = useT()
382
+ return (
383
+ <div className="flex w-full flex-col gap-6">
384
+ {([48, 56, 64, 72, 80] as const).map((size) => (
385
+ <div key={size} className="flex flex-wrap items-center gap-4">
386
+ <span className="w-12 text-xs text-muted-foreground">{size}px</span>
387
+ {[0, 25, 50, 75, 100].map((value) => (
388
+ <CircularProgress key={value} value={value} size={size} showValue={size !== 48} ariaLabel={t('design_system.gallery.examples.navigation.progress')} />
389
+ ))}
390
+ </div>
391
+ ))}
392
+ <p className="text-sm text-muted-foreground">{t('design_system.gallery.examples.navigation.circularHint')}</p>
393
+ </div>
394
+ )
395
+ }
396
+
397
+ function ProgressLabelDemo() {
398
+ const t = useT()
399
+ const label = t('design_system.gallery.examples.controls.storage')
400
+ return <div className="flex w-full max-w-md flex-col gap-6">
401
+ <Progress value={80} size="md" label={label} aria-label={label} showValue description={t('design_system.gallery.examples.controls.storageHint')} />
402
+ <Progress value={80} size="md" label={label} aria-label={label} showValue />
403
+ <Progress value={80} size="md" aria-label={label} showValue valuePlacement="right" />
404
+ </div>
405
+ }
406
+
407
+ function ProgressEndpointDemo() {
408
+ const t = useT()
409
+ return <div className="flex w-full max-w-md flex-col gap-4">
410
+ {[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].map(value => <Progress key={value} value={value} size="md" showValue valuePlacement="right" aria-label={t('design_system.gallery.examples.controls.storage')} />)}
411
+ </div>
412
+ }
413
+
283
414
  const progressEntry: GalleryEntry = {
284
415
  id: 'progress',
285
416
  title: 'Progress',
417
+ figmaNodeId: '450:17821',
286
418
  importPath: '@open-mercato/ui/primitives/progress',
287
419
  variants: [
288
420
  {
@@ -300,16 +432,7 @@ const progressEntry: GalleryEntry = {
300
432
  {
301
433
  id: 'labelled',
302
434
  title: 'Labelled',
303
- render: () => (
304
- <div className="w-full max-w-md">
305
- <Progress
306
- value={80}
307
- label="Data storage"
308
- showValue
309
- description="Upgrade to unlock unlimited storage."
310
- />
311
- </div>
312
- ),
435
+ render: () => <ProgressFeedbackLabelledSample />,
313
436
  code: `import { Progress } from '@open-mercato/ui/primitives/progress'
314
437
 
315
438
  <Progress
@@ -359,6 +482,62 @@ const progressEntry: GalleryEntry = {
359
482
  <CircularProgress value={75} size="xs" />
360
483
  <CircularProgress value={100} tone="success" showValue />`,
361
484
  },
485
+ {
486
+ id: 'circular-source-sizes',
487
+ title: 'Circular source sizes · 25 states',
488
+ render: () => <CircularSourceDemo />,
489
+ code: `import { CircularProgress } from '@open-mercato/ui/primitives/progress'
490
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
491
+
492
+ const t = useT()
493
+
494
+ {([48, 56, 64, 72, 80] as const).map((size) => (
495
+ <div key={size} className="flex flex-wrap items-center gap-4">
496
+ <span>{size}px</span>
497
+ {[0, 25, 50, 75, 100].map((value) => (
498
+ <CircularProgress key={value} value={value} size={size} showValue={size !== 48} ariaLabel={t('design_system.gallery.examples.navigation.progress')} />
499
+ ))}
500
+ </div>
501
+ ))}`,
502
+ },
503
+ {
504
+ id: 'label-positions',
505
+ title: 'Label positions · top / description / right',
506
+ render: () => <ProgressLabelDemo />,
507
+ code: `import * as React from 'react'
508
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
509
+ import { Progress } from '@open-mercato/ui/primitives/progress'
510
+
511
+ function ProgressLabelDemo() {
512
+ const t = useT()
513
+ const label = t('design_system.gallery.examples.controls.storage')
514
+ return <div className="flex w-full max-w-md flex-col gap-6">
515
+ <Progress value={80} size="md" label={label} aria-label={label} showValue description={t('design_system.gallery.examples.controls.storageHint')} />
516
+ <Progress value={80} size="md" label={label} aria-label={label} showValue />
517
+ <Progress value={80} size="md" aria-label={label} showValue valuePlacement="right" />
518
+ </div>
519
+ }
520
+
521
+
522
+ <ProgressLabelDemo />`,
523
+ },
524
+ {
525
+ id: 'endpoints',
526
+ title: 'Linear values · 0 to 100',
527
+ render: () => <ProgressEndpointDemo />,
528
+ code: `import * as React from 'react'
529
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
530
+ import { Progress } from '@open-mercato/ui/primitives/progress'
531
+
532
+ function ProgressEndpointDemo() {
533
+ const t = useT()
534
+ return <div className="flex w-full max-w-md flex-col gap-4">
535
+ {[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].map(value => <Progress key={value} value={value} size="md" showValue valuePlacement="right" aria-label={t('design_system.gallery.examples.controls.storage')} />)}
536
+ </div>
537
+ }
538
+
539
+ <ProgressEndpointDemo />`,
540
+ },
362
541
  ],
363
542
  }
364
543
 
@@ -386,12 +565,7 @@ const spinnerEntry: GalleryEntry = {
386
565
  {
387
566
  id: 'inline-with-label',
388
567
  title: 'Inline with label',
389
- render: () => (
390
- <span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
391
- <Spinner size="sm" />
392
- Loading orders…
393
- </span>
394
- ),
568
+ render: () => <SpinnerFeedbackInlineWithLabelSample />,
395
569
  code: `import { Spinner } from '@open-mercato/ui/primitives/spinner'
396
570
 
397
571
  <span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
@@ -402,15 +576,17 @@ const spinnerEntry: GalleryEntry = {
402
576
  ],
403
577
  }
404
578
 
405
- function InteractiveRatingDemo() {
579
+ function InteractiveRatingDemo({ allowHalf = false }: { allowHalf?: boolean }) {
580
+ const t = useT()
406
581
  const [value, setValue] = React.useState(3)
407
- return <Rating value={value} onChange={setValue} aria-label="Your rating" />
582
+ return <Rating value={value} onChange={setValue} allowHalf={allowHalf} aria-label={t('design_system.gallery.samples.rating.label')} />
408
583
  }
409
584
 
410
585
  const ratingEntry: GalleryEntry = {
411
586
  id: 'rating',
412
587
  title: 'Rating',
413
588
  importPath: '@open-mercato/ui/primitives/rating',
589
+ figmaNodeId: '532:4340',
414
590
  variants: [
415
591
  {
416
592
  id: 'read-only',
@@ -433,10 +609,32 @@ const ratingEntry: GalleryEntry = {
433
609
  title: 'Interactive',
434
610
  render: () => <InteractiveRatingDemo />,
435
611
  code: `import { Rating } from '@open-mercato/ui/primitives/rating'
612
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
613
+
614
+ const t = useT()
615
+ const [value, setValue] = React.useState(3)
616
+
617
+ <Rating value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
618
+ },
619
+ {
620
+ id: 'half-heart',
621
+ title: 'Heart / half precision',
622
+ render: () => <Rating value={3.5} icon="heart" allowHalf />,
623
+ code: `import { Rating } from '@open-mercato/ui/primitives/rating'
624
+
625
+ <Rating value={3.5} icon="heart" allowHalf />`,
626
+ },
627
+ {
628
+ id: 'interactive-half',
629
+ title: 'Interactive / half precision',
630
+ render: () => <InteractiveRatingDemo allowHalf />,
631
+ code: `import { Rating } from '@open-mercato/ui/primitives/rating'
632
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
436
633
 
634
+ const t = useT()
437
635
  const [value, setValue] = React.useState(3)
438
636
 
439
- <Rating value={value} onChange={setValue} aria-label="Your rating" />`,
637
+ <Rating value={value} onChange={setValue} allowHalf aria-label={t('design_system.gallery.samples.rating.label')} />`,
440
638
  },
441
639
  {
442
640
  id: 'icons-and-sizes',
@@ -454,6 +652,450 @@ const [value, setValue] = React.useState(3)
454
652
  <Rating value={3} max={5} icon="circle" size="sm" />
455
653
  <Rating value={3} max={5} size="lg" />`,
456
654
  },
655
+ {
656
+ id: 'cell-star-default', title: 'Cell / star / default and live hover',
657
+ render: () => <RatingCellDemo icon="star" initialValue={0} />,
658
+ code: `import * as React from 'react'
659
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
660
+ import { Rating } from '@open-mercato/ui/primitives/rating'
661
+
662
+ function RatingCellExample() {
663
+ const t = useT()
664
+ const [value, setValue] = React.useState(0)
665
+ return <Rating appearance="cell" icon="star" value={value} onChange={setValue} max={1} aria-label={t('design_system.gallery.samples.rating.label')} />
666
+ }
667
+
668
+ <RatingCellExample />`,
669
+ },
670
+ {
671
+ id: 'cell-star-selected', title: 'Cell / star / selected and live hover',
672
+ render: () => <RatingCellDemo icon="star" initialValue={1} />,
673
+ code: `import * as React from 'react'
674
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
675
+ import { Rating } from '@open-mercato/ui/primitives/rating'
676
+
677
+ function RatingCellExample() {
678
+ const t = useT()
679
+ const [value, setValue] = React.useState(1)
680
+ return <Rating appearance="cell" icon="star" value={value} onChange={setValue} max={1} aria-label={t('design_system.gallery.samples.rating.label')} />
681
+ }
682
+
683
+ <RatingCellExample />`,
684
+ },
685
+ {
686
+ id: 'cell-heart-default', title: 'Cell / heart / default and live hover',
687
+ render: () => <RatingCellDemo icon="heart" initialValue={0} />,
688
+ code: `import * as React from 'react'
689
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
690
+ import { Rating } from '@open-mercato/ui/primitives/rating'
691
+
692
+ function RatingCellExample() {
693
+ const t = useT()
694
+ const [value, setValue] = React.useState(0)
695
+ return <Rating appearance="cell" icon="heart" value={value} onChange={setValue} max={1} aria-label={t('design_system.gallery.samples.rating.label')} />
696
+ }
697
+
698
+ <RatingCellExample />`,
699
+ },
700
+ {
701
+ id: 'cell-heart-selected', title: 'Cell / heart / selected and live hover',
702
+ render: () => <RatingCellDemo icon="heart" initialValue={1} />,
703
+ code: `import * as React from 'react'
704
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
705
+ import { Rating } from '@open-mercato/ui/primitives/rating'
706
+
707
+ function RatingCellExample() {
708
+ const t = useT()
709
+ const [value, setValue] = React.useState(1)
710
+ return <Rating appearance="cell" icon="heart" value={value} onChange={setValue} max={1} aria-label={t('design_system.gallery.samples.rating.label')} />
711
+ }
712
+
713
+ <RatingCellExample />`,
714
+ },
715
+ {
716
+ id: 'cells-interactive', title: 'Cells / keyboard selection',
717
+ render: () => <RatingCellDemo icon="star" initialValue={3} max={5} />,
718
+ code: `import * as React from 'react'
719
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
720
+ import { Rating } from '@open-mercato/ui/primitives/rating'
721
+
722
+ function RatingCellsExample() {
723
+ const t = useT()
724
+ const [value, setValue] = React.useState(3)
725
+ return <Rating appearance="cell" value={value} onChange={setValue} max={5} aria-label={t('design_system.gallery.samples.rating.label')} />
726
+ }
727
+
728
+ <RatingCellsExample />`,
729
+ },
730
+ {
731
+ id: 'cells-disabled', title: 'Cells / disabled',
732
+ render: () => <RatingCellDemo icon="star" initialValue={3} max={5} disabled />,
733
+ code: `import * as React from 'react'
734
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
735
+ import { Rating } from '@open-mercato/ui/primitives/rating'
736
+
737
+ function RatingCellsExample() {
738
+ const t = useT()
739
+ const [value, setValue] = React.useState(3)
740
+ return <Rating appearance="cell" value={value} onChange={setValue} max={5} disabled aria-label={t('design_system.gallery.samples.rating.label')} />
741
+ }
742
+
743
+ <RatingCellsExample />`,
744
+ },
745
+ {
746
+ id: 'review-star-vertical', title: 'Review / star / vertical',
747
+ render: () => <RatingReviewDemo icon="star" alignment="vertical" />,
748
+ code: `import * as React from 'react'
749
+ import { cn } from '@open-mercato/shared/lib/utils'
750
+ import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
751
+ import { Rating } from '@open-mercato/ui/primitives/rating'
752
+ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
753
+ import { Button } from '@open-mercato/ui/primitives/button'
754
+ import { ScrollArea } from '@open-mercato/ui/primitives/scroll-area'
755
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@open-mercato/ui/primitives/dialog'
756
+
757
+ function RatingReviewExample({ icon, alignment }: { icon: 'star' | 'heart'; alignment: 'vertical' | 'horizontal' }) {
758
+ const t = useT()
759
+ const locale = useLocale()
760
+ const [open, setOpen] = React.useState(false)
761
+ const value = new Intl.NumberFormat(locale).format(4.5)
762
+ const count = new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 1 }).format(5200)
763
+ const summary = t('design_system.gallery.samples.rating.summary', '{value} ∙ {count} Ratings', { value, count })
764
+ const reviews = t('design_system.gallery.samples.rating.reviews', '{count} reviews', { count: 18 })
765
+ return (
766
+ <Dialog open={open} onOpenChange={setOpen}>
767
+ <div data-slot="rating-review" data-alignment={alignment} className={cn('inline-flex max-w-full items-start gap-2', alignment === 'vertical' ? 'flex-col' : 'flex-wrap')}>
768
+ <Rating value={4.5} allowHalf icon={icon} />
769
+ <div className="flex flex-wrap items-start gap-1 text-sm leading-5">
770
+ <span>{summary}</span>
771
+ <LinkButton asChild variant="gray" underline="always"><DialogTrigger>{reviews}</DialogTrigger></LinkButton>
772
+ </div>
773
+ </div>
774
+ <DialogContent onKeyDownCapture={event => { if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) { event.preventDefault(); setOpen(false) } }}>
775
+ <DialogHeader><DialogTitle>{reviews}</DialogTitle><DialogDescription>{summary}</DialogDescription></DialogHeader>
776
+ <ScrollArea className="h-72" scrollbarSize="md">
777
+ <ol className="flex flex-col gap-3 pr-6">
778
+ {Array.from({ length: 18 }, (_, index) => <li key={index} className="flex items-center justify-between gap-3 text-sm"><span>{t('design_system.gallery.samples.rating.reviewNumber', 'Review {number}', { number: index + 1 })}</span><Rating value={index < 9 ? 5 : 4} icon={icon} /></li>)}
779
+ </ol>
780
+ </ScrollArea>
781
+ <DialogFooter><Button asChild variant="outline"><DialogClose>{t('common.close')}</DialogClose></Button></DialogFooter>
782
+ </DialogContent>
783
+ </Dialog>
784
+ )
785
+ }
786
+
787
+ <RatingReviewExample icon="star" alignment="vertical" />`,
788
+ },
789
+ {
790
+ id: 'review-star-horizontal', title: 'Review / star / horizontal',
791
+ render: () => <RatingReviewDemo icon="star" alignment="horizontal" />,
792
+ code: `import * as React from 'react'
793
+ import { cn } from '@open-mercato/shared/lib/utils'
794
+ import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
795
+ import { Rating } from '@open-mercato/ui/primitives/rating'
796
+ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
797
+ import { Button } from '@open-mercato/ui/primitives/button'
798
+ import { ScrollArea } from '@open-mercato/ui/primitives/scroll-area'
799
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@open-mercato/ui/primitives/dialog'
800
+
801
+ function RatingReviewExample({ icon, alignment }: { icon: 'star' | 'heart'; alignment: 'vertical' | 'horizontal' }) {
802
+ const t = useT()
803
+ const locale = useLocale()
804
+ const [open, setOpen] = React.useState(false)
805
+ const value = new Intl.NumberFormat(locale).format(4.5)
806
+ const count = new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 1 }).format(5200)
807
+ const summary = t('design_system.gallery.samples.rating.summary', '{value} ∙ {count} Ratings', { value, count })
808
+ const reviews = t('design_system.gallery.samples.rating.reviews', '{count} reviews', { count: 18 })
809
+ return (
810
+ <Dialog open={open} onOpenChange={setOpen}>
811
+ <div data-slot="rating-review" data-alignment={alignment} className={cn('inline-flex max-w-full items-start gap-2', alignment === 'vertical' ? 'flex-col' : 'flex-wrap')}>
812
+ <Rating value={4.5} allowHalf icon={icon} />
813
+ <div className="flex flex-wrap items-start gap-1 text-sm leading-5">
814
+ <span>{summary}</span>
815
+ <LinkButton asChild variant="gray" underline="always"><DialogTrigger>{reviews}</DialogTrigger></LinkButton>
816
+ </div>
817
+ </div>
818
+ <DialogContent onKeyDownCapture={event => { if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) { event.preventDefault(); setOpen(false) } }}>
819
+ <DialogHeader><DialogTitle>{reviews}</DialogTitle><DialogDescription>{summary}</DialogDescription></DialogHeader>
820
+ <ScrollArea className="h-72" scrollbarSize="md">
821
+ <ol className="flex flex-col gap-3 pr-6">
822
+ {Array.from({ length: 18 }, (_, index) => <li key={index} className="flex items-center justify-between gap-3 text-sm"><span>{t('design_system.gallery.samples.rating.reviewNumber', 'Review {number}', { number: index + 1 })}</span><Rating value={index < 9 ? 5 : 4} icon={icon} /></li>)}
823
+ </ol>
824
+ </ScrollArea>
825
+ <DialogFooter><Button asChild variant="outline"><DialogClose>{t('common.close')}</DialogClose></Button></DialogFooter>
826
+ </DialogContent>
827
+ </Dialog>
828
+ )
829
+ }
830
+
831
+ <RatingReviewExample icon="star" alignment="horizontal" />`,
832
+ },
833
+ {
834
+ id: 'review-heart-vertical', title: 'Review / heart / vertical',
835
+ render: () => <RatingReviewDemo icon="heart" alignment="vertical" />,
836
+ code: `import * as React from 'react'
837
+ import { cn } from '@open-mercato/shared/lib/utils'
838
+ import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
839
+ import { Rating } from '@open-mercato/ui/primitives/rating'
840
+ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
841
+ import { Button } from '@open-mercato/ui/primitives/button'
842
+ import { ScrollArea } from '@open-mercato/ui/primitives/scroll-area'
843
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@open-mercato/ui/primitives/dialog'
844
+
845
+ function RatingReviewExample({ icon, alignment }: { icon: 'star' | 'heart'; alignment: 'vertical' | 'horizontal' }) {
846
+ const t = useT()
847
+ const locale = useLocale()
848
+ const [open, setOpen] = React.useState(false)
849
+ const value = new Intl.NumberFormat(locale).format(4.5)
850
+ const count = new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 1 }).format(5200)
851
+ const summary = t('design_system.gallery.samples.rating.summary', '{value} ∙ {count} Ratings', { value, count })
852
+ const reviews = t('design_system.gallery.samples.rating.reviews', '{count} reviews', { count: 18 })
853
+ return (
854
+ <Dialog open={open} onOpenChange={setOpen}>
855
+ <div data-slot="rating-review" data-alignment={alignment} className={cn('inline-flex max-w-full items-start gap-2', alignment === 'vertical' ? 'flex-col' : 'flex-wrap')}>
856
+ <Rating value={4.5} allowHalf icon={icon} />
857
+ <div className="flex flex-wrap items-start gap-1 text-sm leading-5">
858
+ <span>{summary}</span>
859
+ <LinkButton asChild variant="gray" underline="always"><DialogTrigger>{reviews}</DialogTrigger></LinkButton>
860
+ </div>
861
+ </div>
862
+ <DialogContent onKeyDownCapture={event => { if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) { event.preventDefault(); setOpen(false) } }}>
863
+ <DialogHeader><DialogTitle>{reviews}</DialogTitle><DialogDescription>{summary}</DialogDescription></DialogHeader>
864
+ <ScrollArea className="h-72" scrollbarSize="md">
865
+ <ol className="flex flex-col gap-3 pr-6">
866
+ {Array.from({ length: 18 }, (_, index) => <li key={index} className="flex items-center justify-between gap-3 text-sm"><span>{t('design_system.gallery.samples.rating.reviewNumber', 'Review {number}', { number: index + 1 })}</span><Rating value={index < 9 ? 5 : 4} icon={icon} /></li>)}
867
+ </ol>
868
+ </ScrollArea>
869
+ <DialogFooter><Button asChild variant="outline"><DialogClose>{t('common.close')}</DialogClose></Button></DialogFooter>
870
+ </DialogContent>
871
+ </Dialog>
872
+ )
873
+ }
874
+
875
+ <RatingReviewExample icon="heart" alignment="vertical" />`,
876
+ },
877
+ {
878
+ id: 'review-heart-horizontal', title: 'Review / heart / horizontal',
879
+ render: () => <RatingReviewDemo icon="heart" alignment="horizontal" />,
880
+ code: `import * as React from 'react'
881
+ import { cn } from '@open-mercato/shared/lib/utils'
882
+ import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
883
+ import { Rating } from '@open-mercato/ui/primitives/rating'
884
+ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
885
+ import { Button } from '@open-mercato/ui/primitives/button'
886
+ import { ScrollArea } from '@open-mercato/ui/primitives/scroll-area'
887
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@open-mercato/ui/primitives/dialog'
888
+
889
+ function RatingReviewExample({ icon, alignment }: { icon: 'star' | 'heart'; alignment: 'vertical' | 'horizontal' }) {
890
+ const t = useT()
891
+ const locale = useLocale()
892
+ const [open, setOpen] = React.useState(false)
893
+ const value = new Intl.NumberFormat(locale).format(4.5)
894
+ const count = new Intl.NumberFormat(locale, { notation: 'compact', maximumFractionDigits: 1 }).format(5200)
895
+ const summary = t('design_system.gallery.samples.rating.summary', '{value} ∙ {count} Ratings', { value, count })
896
+ const reviews = t('design_system.gallery.samples.rating.reviews', '{count} reviews', { count: 18 })
897
+ return (
898
+ <Dialog open={open} onOpenChange={setOpen}>
899
+ <div data-slot="rating-review" data-alignment={alignment} className={cn('inline-flex max-w-full items-start gap-2', alignment === 'vertical' ? 'flex-col' : 'flex-wrap')}>
900
+ <Rating value={4.5} allowHalf icon={icon} />
901
+ <div className="flex flex-wrap items-start gap-1 text-sm leading-5">
902
+ <span>{summary}</span>
903
+ <LinkButton asChild variant="gray" underline="always"><DialogTrigger>{reviews}</DialogTrigger></LinkButton>
904
+ </div>
905
+ </div>
906
+ <DialogContent onKeyDownCapture={event => { if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) { event.preventDefault(); setOpen(false) } }}>
907
+ <DialogHeader><DialogTitle>{reviews}</DialogTitle><DialogDescription>{summary}</DialogDescription></DialogHeader>
908
+ <ScrollArea className="h-72" scrollbarSize="md">
909
+ <ol className="flex flex-col gap-3 pr-6">
910
+ {Array.from({ length: 18 }, (_, index) => <li key={index} className="flex items-center justify-between gap-3 text-sm"><span>{t('design_system.gallery.samples.rating.reviewNumber', 'Review {number}', { number: index + 1 })}</span><Rating value={index < 9 ? 5 : 4} icon={icon} /></li>)}
911
+ </ol>
912
+ </ScrollArea>
913
+ <DialogFooter><Button asChild variant="outline"><DialogClose>{t('common.close')}</DialogClose></Button></DialogFooter>
914
+ </DialogContent>
915
+ </Dialog>
916
+ )
917
+ }
918
+
919
+ <RatingReviewExample icon="heart" alignment="horizontal" />`,
920
+ },
921
+ ],
922
+ }
923
+
924
+ function RatingBarDemo({ variant = 'emoji', initialValue = 0, disabled = false, feedback = false }: { variant?: RatingBarProps['variant']; initialValue?: number; disabled?: boolean; feedback?: boolean }) {
925
+ const t = useT()
926
+ const [value, setValue] = React.useState(initialValue)
927
+ const bar = <RatingBar variant={variant} value={value} onChange={setValue} disabled={disabled} aria-label={t('design_system.gallery.samples.rating.label')} className={feedback ? 'w-full border-0 border-b' : undefined} />
928
+ return feedback ? (
929
+ <div className="w-80 max-w-full overflow-hidden rounded-xl border border-border bg-background focus-within:shadow-focus">
930
+ {bar}
931
+ <Textarea aria-label={t('design_system.gallery.samples.rating.feedback')} placeholder={t('design_system.gallery.samples.rating.feedback')} disabled={disabled} className="min-h-24 resize-none rounded-none border-0 p-3 shadow-none focus-visible:shadow-none" />
932
+ </div>
933
+ ) : bar
934
+ }
935
+
936
+ const ratingBarEntry: GalleryEntry = {
937
+ id: 'rating-bar',
938
+ title: 'RatingBar',
939
+ importPath: '@open-mercato/ui/primitives/rating-bar',
940
+ figmaNodeId: '535:4658',
941
+ usage: {
942
+ do: ['Use the five-part feedback scale with a translated group label.', 'The feedback-area composition joins the scale to a Textarea and keeps both controls independently accessible.'],
943
+ dont: ['Do not use this feedback scale for unrelated actions; use ButtonGroup for actions.'],
944
+ },
945
+ variants: [
946
+ {
947
+ id: 'emoji',
948
+ title: 'emoji / unselected',
949
+ render: () => <RatingBarDemo variant="emoji" />,
950
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
951
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
952
+
953
+ const t = useT()
954
+ const [value, setValue] = React.useState(0)
955
+
956
+ <RatingBar variant="emoji" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
957
+ },
958
+ {
959
+ id: 'emoji-selected',
960
+ title: 'emoji / selected',
961
+ render: () => <RatingBarDemo variant="emoji" initialValue={4} />,
962
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
963
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
964
+
965
+ const t = useT()
966
+ const [value, setValue] = React.useState(4)
967
+
968
+ <RatingBar variant="emoji" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
969
+ },
970
+ {
971
+ id: 'number',
972
+ title: 'number / unselected',
973
+ render: () => <RatingBarDemo variant="number" />,
974
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
975
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
976
+
977
+ const t = useT()
978
+ const [value, setValue] = React.useState(0)
979
+
980
+ <RatingBar variant="number" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
981
+ },
982
+ {
983
+ id: 'number-selected',
984
+ title: 'number / selected',
985
+ render: () => <RatingBarDemo variant="number" initialValue={4} />,
986
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
987
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
988
+
989
+ const t = useT()
990
+ const [value, setValue] = React.useState(4)
991
+
992
+ <RatingBar variant="number" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
993
+ },
994
+ {
995
+ id: 'star',
996
+ title: 'star / unselected',
997
+ render: () => <RatingBarDemo variant="star" />,
998
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
999
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1000
+
1001
+ const t = useT()
1002
+ const [value, setValue] = React.useState(0)
1003
+
1004
+ <RatingBar variant="star" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
1005
+ },
1006
+ {
1007
+ id: 'star-selected',
1008
+ title: 'star / selected',
1009
+ render: () => <RatingBarDemo variant="star" initialValue={4} />,
1010
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
1011
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1012
+
1013
+ const t = useT()
1014
+ const [value, setValue] = React.useState(4)
1015
+
1016
+ <RatingBar variant="star" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
1017
+ },
1018
+ {
1019
+ id: 'heart',
1020
+ title: 'heart / unselected',
1021
+ render: () => <RatingBarDemo variant="heart" />,
1022
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
1023
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1024
+
1025
+ const t = useT()
1026
+ const [value, setValue] = React.useState(0)
1027
+
1028
+ <RatingBar variant="heart" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
1029
+ },
1030
+ {
1031
+ id: 'heart-selected',
1032
+ title: 'heart / selected',
1033
+ render: () => <RatingBarDemo variant="heart" initialValue={4} />,
1034
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
1035
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1036
+
1037
+ const t = useT()
1038
+ const [value, setValue] = React.useState(4)
1039
+
1040
+ <RatingBar variant="heart" value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} />`,
1041
+ },
1042
+ {
1043
+ id: 'feedback-area',
1044
+ title: 'Feedback area / all types',
1045
+ render: () => (
1046
+ <div className="flex w-full flex-wrap gap-6">
1047
+ <RatingBarDemo variant="emoji" feedback />
1048
+ <RatingBarDemo variant="number" feedback />
1049
+ <RatingBarDemo variant="star" feedback />
1050
+ <RatingBarDemo variant="heart" feedback />
1051
+ </div>
1052
+ ),
1053
+ code: `import { RatingBar, type RatingBarProps } from '@open-mercato/ui/primitives/rating-bar'
1054
+ import { Textarea } from '@open-mercato/ui/primitives/textarea'
1055
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1056
+
1057
+ function RatingFeedback({ variant }: { variant: RatingBarProps['variant'] }) {
1058
+ const t = useT()
1059
+ const [value, setValue] = React.useState(0)
1060
+ return (
1061
+ <div className="w-80 max-w-full overflow-hidden rounded-xl border border-border bg-background focus-within:shadow-focus">
1062
+ <RatingBar variant={variant} value={value} onChange={setValue} aria-label={t('design_system.gallery.samples.rating.label')} className="w-full border-0 border-b" />
1063
+ <Textarea aria-label={t('design_system.gallery.samples.rating.feedback')} placeholder={t('design_system.gallery.samples.rating.feedback')} className="min-h-24 resize-none rounded-none border-0 p-3 shadow-none focus-visible:shadow-none" />
1064
+ </div>
1065
+ )
1066
+ }
1067
+
1068
+ <div className="flex w-full flex-wrap gap-6">
1069
+ <RatingFeedback variant="emoji" />
1070
+ <RatingFeedback variant="number" />
1071
+ <RatingFeedback variant="star" />
1072
+ <RatingFeedback variant="heart" />
1073
+ </div>`,
1074
+ },
1075
+ {
1076
+ id: 'disabled',
1077
+ title: 'Disabled / all types',
1078
+ render: () => (
1079
+ <div className="flex w-full flex-wrap gap-6">
1080
+ <RatingBarDemo variant="emoji" initialValue={4} disabled />
1081
+ <RatingBarDemo variant="number" initialValue={4} disabled />
1082
+ <RatingBarDemo variant="star" initialValue={4} disabled />
1083
+ <RatingBarDemo variant="heart" initialValue={4} disabled />
1084
+ </div>
1085
+ ),
1086
+ code: `import { RatingBar } from '@open-mercato/ui/primitives/rating-bar'
1087
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1088
+
1089
+ const t = useT()
1090
+ const [value, setValue] = React.useState(4)
1091
+
1092
+ <div className="flex w-full flex-wrap gap-6">
1093
+ <RatingBar variant="emoji" value={value} onChange={setValue} disabled aria-label={t('design_system.gallery.samples.rating.label')} />
1094
+ <RatingBar variant="number" value={value} onChange={setValue} disabled aria-label={t('design_system.gallery.samples.rating.label')} />
1095
+ <RatingBar variant="star" value={value} onChange={setValue} disabled aria-label={t('design_system.gallery.samples.rating.label')} />
1096
+ <RatingBar variant="heart" value={value} onChange={setValue} disabled aria-label={t('design_system.gallery.samples.rating.label')} />
1097
+ </div>`,
1098
+ },
457
1099
  ],
458
1100
  }
459
1101
 
@@ -475,9 +1117,59 @@ const errorSteps: StepIndicatorStep[] = [
475
1117
  { id: 'import', label: 'Import', status: 'pending' },
476
1118
  ]
477
1119
 
1120
+ function StepQuantityDemo({ orientation }: { orientation: 'horizontal' | 'vertical' }) {
1121
+ const t = useT()
1122
+ return (
1123
+ <div className={orientation === 'vertical' ? 'grid w-full gap-6 md:grid-cols-3' : 'flex w-full flex-col gap-6 overflow-x-auto'}>
1124
+ {[3, 4, 5].map((count) => (
1125
+ <StepIndicator key={count} showNumbers orientation={orientation} steps={Array.from({ length: count }, (_, index) => ({
1126
+ id: String(index),
1127
+ label: t('design_system.gallery.examples.navigation.step', { step: index + 1 }),
1128
+ status: index === 0 ? 'complete' : index === 1 ? 'current' : 'pending',
1129
+ }))} />
1130
+ ))}
1131
+ </div>
1132
+ )
1133
+ }
1134
+
1135
+ function StepInteractiveDemo() {
1136
+ const t = useT()
1137
+ const [activeStep, setActiveStep] = React.useState(1)
1138
+ const steps: StepIndicatorStep[] = Array.from({ length: 5 }, (_, index) => ({
1139
+ id: String(index),
1140
+ label: t('design_system.gallery.examples.navigation.step', { step: index + 1 }),
1141
+ status: index < activeStep ? 'complete' : index === activeStep ? 'current' : 'pending',
1142
+ }))
1143
+ return (
1144
+ <div className="flex w-full flex-col gap-4">
1145
+ <div className="overflow-x-auto p-1"><StepIndicator steps={steps} showNumbers onStepClick={(id) => setActiveStep(Number(id))} /></div>
1146
+ <StepperDots count={5} activeStep={activeStep} aria-label={t('design_system.gallery.examples.navigation.progress')} />
1147
+ <div className="flex gap-2">
1148
+ <Button variant="outline" size="sm" disabled={activeStep === 0} onClick={() => setActiveStep((value) => value - 1)}>{t('design_system.gallery.examples.navigation.previous')}</Button>
1149
+ <Button variant="outline" size="sm" disabled={activeStep === 4} onClick={() => setActiveStep((value) => value + 1)}>{t('design_system.gallery.examples.navigation.next')}</Button>
1150
+ </div>
1151
+ </div>
1152
+ )
1153
+ }
1154
+
1155
+ function StepperDotsDemo() {
1156
+ const t = useT()
1157
+ return (
1158
+ <div className="flex flex-wrap gap-8">
1159
+ {(['sm', 'xs'] as const).map((size) => (
1160
+ <div key={size} className="flex flex-col gap-4">
1161
+ <span className="text-xs text-muted-foreground">{size}</span>
1162
+ {[0, 1, 2].map((activeStep) => <StepperDots key={activeStep} count={3} activeStep={activeStep} size={size} aria-label={t('design_system.gallery.examples.navigation.progress')} />)}
1163
+ </div>
1164
+ ))}
1165
+ </div>
1166
+ )
1167
+ }
1168
+
478
1169
  const stepIndicatorEntry: GalleryEntry = {
479
1170
  id: 'step-indicator',
480
1171
  title: 'StepIndicator',
1172
+ figmaNodeId: '3507:28',
481
1173
  importPath: '@open-mercato/ui/primitives/step-indicator',
482
1174
  variants: [
483
1175
  {
@@ -526,6 +1218,96 @@ const steps: StepIndicatorStep[] = [
526
1218
 
527
1219
  <StepIndicator steps={steps} />`,
528
1220
  },
1221
+ {
1222
+ id: 'horizontal-quantities',
1223
+ title: 'horizontal · 3, 4, 5 steps',
1224
+ render: () => <StepQuantityDemo orientation="horizontal" />,
1225
+ code: `import { StepIndicator } from '@open-mercato/ui/primitives/step-indicator'
1226
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1227
+
1228
+ const t = useT()
1229
+
1230
+ {[3, 4, 5].map((count) => (
1231
+ <StepIndicator key={count} showNumbers orientation="horizontal" steps={Array.from({ length: count }, (_, index) => ({
1232
+ id: String(index),
1233
+ label: t('design_system.gallery.examples.navigation.step', { step: index + 1 }),
1234
+ status: index === 0 ? 'complete' : index === 1 ? 'current' : 'pending',
1235
+ }))} />
1236
+ ))}`,
1237
+ },
1238
+ {
1239
+ id: 'vertical-quantities',
1240
+ title: 'vertical · 3, 4, 5 steps',
1241
+ render: () => <StepQuantityDemo orientation="vertical" />,
1242
+ code: `import { StepIndicator } from '@open-mercato/ui/primitives/step-indicator'
1243
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1244
+
1245
+ const t = useT()
1246
+
1247
+ {[3, 4, 5].map((count) => (
1248
+ <StepIndicator key={count} showNumbers orientation="vertical" steps={Array.from({ length: count }, (_, index) => ({
1249
+ id: String(index),
1250
+ label: t('design_system.gallery.examples.navigation.step', { step: index + 1 }),
1251
+ status: index === 0 ? 'complete' : index === 1 ? 'current' : 'pending',
1252
+ }))} />
1253
+ ))}`,
1254
+ },
1255
+ {
1256
+ id: 'interactive',
1257
+ title: 'Interactive steps and dots',
1258
+ render: () => <StepInteractiveDemo />,
1259
+ code: `import * as React from 'react'
1260
+ import { StepIndicator, StepperDots, type StepIndicatorStep } from '@open-mercato/ui/primitives/step-indicator'
1261
+ import { Button } from '@open-mercato/ui/primitives/button'
1262
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1263
+
1264
+ function StepInteractiveDemo() {
1265
+ const t = useT()
1266
+ const [activeStep, setActiveStep] = React.useState(1)
1267
+ const steps: StepIndicatorStep[] = Array.from({ length: 5 }, (_, index) => ({
1268
+ id: String(index),
1269
+ label: t('design_system.gallery.examples.navigation.step', { step: index + 1 }),
1270
+ status: index < activeStep ? 'complete' : index === activeStep ? 'current' : 'pending',
1271
+ }))
1272
+ return (
1273
+ <div className="flex w-full flex-col gap-4">
1274
+ <div className="overflow-x-auto p-1"><StepIndicator steps={steps} showNumbers onStepClick={(id) => setActiveStep(Number(id))} /></div>
1275
+ <StepperDots count={5} activeStep={activeStep} aria-label={t('design_system.gallery.examples.navigation.progress')} />
1276
+ <div className="flex gap-2">
1277
+ <Button variant="outline" size="sm" disabled={activeStep === 0} onClick={() => setActiveStep((value) => value - 1)}>{t('design_system.gallery.examples.navigation.previous')}</Button>
1278
+ <Button variant="outline" size="sm" disabled={activeStep === 4} onClick={() => setActiveStep((value) => value + 1)}>{t('design_system.gallery.examples.navigation.next')}</Button>
1279
+ </div>
1280
+ </div>
1281
+ )
1282
+ }
1283
+
1284
+ <StepInteractiveDemo />`,
1285
+ },
1286
+ {
1287
+ id: 'dots',
1288
+ title: 'StepperDots · 6 states',
1289
+ render: () => <StepperDotsDemo />,
1290
+ code: `import * as React from 'react'
1291
+ import { StepIndicator, StepperDots, type StepIndicatorStep } from '@open-mercato/ui/primitives/step-indicator'
1292
+ import { Button } from '@open-mercato/ui/primitives/button'
1293
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
1294
+
1295
+ function StepperDotsDemo() {
1296
+ const t = useT()
1297
+ return (
1298
+ <div className="flex flex-wrap gap-8">
1299
+ {(['sm', 'xs'] as const).map((size) => (
1300
+ <div key={size} className="flex flex-col gap-4">
1301
+ <span className="text-xs text-muted-foreground">{size}</span>
1302
+ {[0, 1, 2].map((activeStep) => <StepperDots key={activeStep} count={3} activeStep={activeStep} size={size} aria-label={t('design_system.gallery.examples.navigation.progress')} />)}
1303
+ </div>
1304
+ ))}
1305
+ </div>
1306
+ )
1307
+ }
1308
+
1309
+ <StepperDotsDemo />`,
1310
+ },
529
1311
  ],
530
1312
  }
531
1313
 
@@ -537,15 +1319,7 @@ const notificationEntry: GalleryEntry = {
537
1319
  {
538
1320
  id: 'information',
539
1321
  title: 'Information',
540
- render: () => (
541
- <div className="w-full max-w-md">
542
- <Notification
543
- title="Scheduled maintenance"
544
- description="The admin panel will be read-only on Sunday between 02:00 and 04:00 UTC."
545
- timestamp="2 min ago"
546
- />
547
- </div>
548
- ),
1322
+ render: () => <NotificationFeedbackInformationSample />,
549
1323
  code: `import { Notification } from '@open-mercato/ui/primitives/notification'
550
1324
 
551
1325
  <Notification
@@ -558,22 +1332,7 @@ const notificationEntry: GalleryEntry = {
558
1332
  {
559
1333
  id: 'success-with-actions',
560
1334
  title: 'Success with actions',
561
- render: () => (
562
- <div className="w-full max-w-md">
563
- <Notification
564
- status="success"
565
- title="Export ready"
566
- description="Your product export (1,204 rows) finished successfully."
567
- timestamp="just now"
568
- actions={
569
- <>
570
- <LinkButton variant="black" underline="always">Download</LinkButton>
571
- <LinkButton variant="gray" underline="always">View log</LinkButton>
572
- </>
573
- }
574
- />
575
- </div>
576
- ),
1335
+ render: () => <NotificationFeedbackSuccessWithActionsSample />,
577
1336
  code: `import { Notification } from '@open-mercato/ui/primitives/notification'
578
1337
  import { LinkButton } from '@open-mercato/ui/primitives/link-button'
579
1338
 
@@ -593,17 +1352,7 @@ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
593
1352
  {
594
1353
  id: 'error',
595
1354
  title: 'Error',
596
- render: () => (
597
- <div className="w-full max-w-md">
598
- <Notification
599
- status="error"
600
- title="Webhook delivery failed"
601
- description="The endpoint responded with 503 after 3 retries."
602
- timestamp="5 min ago"
603
- actions={<LinkButton variant="error" underline="always">Retry now</LinkButton>}
604
- />
605
- </div>
606
- ),
1355
+ render: () => <NotificationFeedbackErrorSample />,
607
1356
  code: `import { Notification } from '@open-mercato/ui/primitives/notification'
608
1357
  import { LinkButton } from '@open-mercato/ui/primitives/link-button'
609
1358
 
@@ -620,49 +1369,17 @@ import { LinkButton } from '@open-mercato/ui/primitives/link-button'
620
1369
 
621
1370
  const notificationFeedEntry: GalleryEntry = {
622
1371
  id: 'notification-feed',
1372
+ figmaNodeId: '4308:731',
623
1373
  title: 'NotificationFeed',
624
1374
  importPath: '@open-mercato/ui/primitives/notification-feed',
625
1375
  variants: [
1376
+ { id: 'source-tabs-2', title: '4 row types / 2 tabs', render: () => <NotificationFeedExample tabs={2} />, code: notificationFeedExampleCode(2) },
1377
+ { id: 'source-tabs-3', title: '4 row types / 3 tabs', render: () => <NotificationFeedExample tabs={3} />, code: notificationFeedExampleCode(3) },
1378
+ { id: 'source-tabs-4', title: '4 row types / 4 tabs', render: () => <NotificationFeedExample tabs={4} />, code: notificationFeedExampleCode(4) },
626
1379
  {
627
1380
  id: 'default',
628
1381
  title: 'default',
629
- render: () => (
630
- <div className="w-full max-w-sm">
631
- <NotificationFeed>
632
- <NotificationFeedHeader title="Notifications">
633
- <IconButton size="sm" variant="ghost" aria-label="Notification settings">
634
- <Settings />
635
- </IconButton>
636
- </NotificationFeedHeader>
637
- <NotificationFeedList>
638
- <NotificationFeedItem
639
- icon={
640
- <NotificationFeedIconBadge tone="indigo">
641
- <UserPlus className="size-5" />
642
- </NotificationFeedIconBadge>
643
- }
644
- title="New lead generated"
645
- body="John Smith submitted the contact form."
646
- timestamp="10 minutes ago"
647
- unread
648
- />
649
- <NotificationFeedItem
650
- icon={
651
- <NotificationFeedIconBadge tone="success">
652
- <RefreshCw className="size-5" />
653
- </NotificationFeedIconBadge>
654
- }
655
- title="Catalog sync completed"
656
- body="312 products updated from the PIM."
657
- timestamp="1 hour ago"
658
- />
659
- </NotificationFeedList>
660
- <NotificationFeedFooter>
661
- <Button variant="outline" size="sm" className="w-full">Archive all</Button>
662
- </NotificationFeedFooter>
663
- </NotificationFeed>
664
- </div>
665
- ),
1382
+ render: () => <NotificationFeedFeedbackDefaultSample />,
666
1383
  code: `import { Settings, UserPlus } from 'lucide-react'
667
1384
  import {
668
1385
  NotificationFeed,
@@ -734,13 +1451,209 @@ import { NotificationFeedIconBadge } from '@open-mercato/ui/primitives/notificat
734
1451
  }
735
1452
 
736
1453
  export const entries: GalleryEntry[] = [
1454
+ bannerEntry,
737
1455
  alertEntry,
738
1456
  emptyStateEntry,
1457
+ emptyStateIllustrationEntry,
739
1458
  skeletonEntry,
740
1459
  progressEntry,
741
1460
  spinnerEntry,
742
1461
  ratingEntry,
1462
+ ratingBarEntry,
743
1463
  stepIndicatorEntry,
744
1464
  notificationEntry,
745
1465
  notificationFeedEntry,
746
1466
  ]
1467
+
1468
+ function AlertFeedbackStatusesLightSample() {
1469
+ const t = useT()
1470
+ return (<div className="flex w-full max-w-md flex-col gap-2">
1471
+ <Alert status="error" style="light">{t('design_system.gallery.sampleCopy.paymentFailedTheCardWasDeclined')}</Alert>
1472
+ <Alert status="warning" style="light">{t('design_system.gallery.sampleCopy.inventorySyncIsRunningBehindSchedule')}</Alert>
1473
+ <Alert status="success" style="light">{t('design_system.gallery.sampleCopy.order10231WasFulfilled')}</Alert>
1474
+ <Alert status="information" style="light">{t('design_system.gallery.sampleCopy.pricesIncludeVATForEUCustomers')}</Alert>
1475
+ <Alert status="feature" style="light">{t('design_system.gallery.sampleCopy.bulkEditingIsNowAvailableInTheCatalog')}</Alert>
1476
+ </div>)
1477
+ }
1478
+
1479
+ function AlertFeedbackFilledSample() {
1480
+ const t = useT()
1481
+ return (<div className="w-full max-w-md">
1482
+ <Alert status="error" style="filled" size="default">
1483
+ <AlertTitle>{t('design_system.gallery.sampleCopy.importFailed')}</AlertTitle>
1484
+ <AlertDescription>{t('design_system.gallery.sampleCopy.14RowsWereRejectedDownloadTheErrorReportToReview')}</AlertDescription>
1485
+ </Alert>
1486
+ </div>)
1487
+ }
1488
+
1489
+ function AlertFeedbackStrokeSample() {
1490
+ const t = useT()
1491
+ return (<div className="w-full max-w-md">
1492
+ <Alert status="success" style="stroke" size="default">
1493
+ <AlertTitle>{t('design_system.gallery.sampleCopy.backupComplete')}</AlertTitle>
1494
+ <AlertDescription>{t('design_system.gallery.sampleCopy.theNightlySnapshotFinishedWithoutWarnings')}</AlertDescription>
1495
+ </Alert>
1496
+ </div>)
1497
+ }
1498
+
1499
+ function AlertFeedbackSizesSample() {
1500
+ const t = useT()
1501
+ return (<div className="flex w-full max-w-md flex-col gap-2">
1502
+ <Alert status="information" size="xs">{t('design_system.gallery.sampleCopy.xsmallInlineFormHint')}</Alert>
1503
+ <Alert status="information" size="sm">{t('design_system.gallery.sampleCopy.smallTheDefaultSinglelineAlert')}</Alert>
1504
+ <Alert status="information" size="default">
1505
+ <AlertTitle>{t('design_system.gallery.sampleCopy.large')}</AlertTitle>
1506
+ <AlertDescription>{t('design_system.gallery.sampleCopy.multilineAlertWithATitleAndADescriptionBody')}</AlertDescription>
1507
+ </Alert>
1508
+ </div>)
1509
+ }
1510
+
1511
+ function AlertFeedbackDismissibleWithActionSample() {
1512
+ const t = useT()
1513
+ return (<div className="w-full max-w-md">
1514
+ <Alert
1515
+ status="warning"
1516
+ style="lighter"
1517
+ dismissible
1518
+ action={<LinkButton variant="black" underline="always">{t('design_system.gallery.sampleCopy.review')}</LinkButton>}
1519
+ >
1520
+ {t('design_system.gallery.sampleCopy.3ProductsAreMissingTaxCategories')}
1521
+ </Alert>
1522
+ </div>)
1523
+ }
1524
+
1525
+ function EmptyStateFeedbackDefaultSample() {
1526
+ const t = useT()
1527
+ return (<div className="w-full max-w-md">
1528
+ <EmptyState
1529
+ title={t('design_system.gallery.sampleCopy.noCustomersYet')}
1530
+ description={t('design_system.gallery.sampleCopy.createYourFirstCustomerToStartTrackingOpportunities')}
1531
+ />
1532
+ </div>)
1533
+ }
1534
+
1535
+ function EmptyStateFeedbackWithActionsSample() {
1536
+ const t = useT()
1537
+ return (<div className="w-full max-w-md">
1538
+ <EmptyState
1539
+ icon={<Inbox className="size-8" />}
1540
+ title={t('design_system.gallery.sampleCopy.noOrdersFound')}
1541
+ description={t('design_system.gallery.sampleCopy.ordersWillAppearHereOnceYourStorefrontStartsSelling')}
1542
+ actions={<Button size="sm">{t('design_system.gallery.sampleCopy.createOrder')}</Button>}
1543
+ />
1544
+ </div>)
1545
+ }
1546
+
1547
+ function EmptyStateFeedbackSubtleSample() {
1548
+ const t = useT()
1549
+ return (<div className="w-full max-w-md">
1550
+ <EmptyState
1551
+ variant="subtle"
1552
+ size="sm"
1553
+ icon={<Search className="size-5" />}
1554
+ title={t('design_system.gallery.sampleCopy.noResults')}
1555
+ description={t('design_system.gallery.sampleCopy.tryADifferentSearchTermOrClearTheFilters')}
1556
+ />
1557
+ </div>)
1558
+ }
1559
+
1560
+ function ProgressFeedbackLabelledSample() {
1561
+ const t = useT()
1562
+ return (<div className="w-full max-w-md">
1563
+ <Progress
1564
+ value={80}
1565
+ label={t('design_system.gallery.examples.controls.storage')}
1566
+ showValue
1567
+ description={t('design_system.gallery.examples.controls.storageHint')}
1568
+ />
1569
+ </div>)
1570
+ }
1571
+
1572
+ function SpinnerFeedbackInlineWithLabelSample() {
1573
+ const t = useT()
1574
+ return (<span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
1575
+ <Spinner size="sm" />
1576
+ {t('design_system.gallery.sampleCopy.loadingOrders')}
1577
+ </span>)
1578
+ }
1579
+
1580
+ function NotificationFeedbackInformationSample() {
1581
+ const t = useT()
1582
+ return (<div className="w-full max-w-md">
1583
+ <Notification
1584
+ title={t('design_system.gallery.samples.crypto.notificationMaintenance')}
1585
+ description={t('design_system.gallery.sampleCopy.theAdminPanelWillBeReadonlyOnSundayBetween0200')}
1586
+ timestamp={t('design_system.gallery.sampleCopy.2MinAgo')}
1587
+ />
1588
+ </div>)
1589
+ }
1590
+
1591
+ function NotificationFeedbackSuccessWithActionsSample() {
1592
+ const t = useT()
1593
+ return (<div className="w-full max-w-md">
1594
+ <Notification
1595
+ status="success"
1596
+ title={t('design_system.gallery.sampleCopy.exportReady')}
1597
+ description={t('design_system.gallery.sampleCopy.yourProductExport1204RowsFinishedSuccessfully')}
1598
+ timestamp={t('design_system.gallery.sampleCopy.justNow')}
1599
+ actions={
1600
+ <>
1601
+ <LinkButton variant="black" underline="always">{t('design_system.gallery.sampleCopy.download')}</LinkButton>
1602
+ <LinkButton variant="gray" underline="always">{t('design_system.gallery.sampleCopy.viewLog')}</LinkButton>
1603
+ </>
1604
+ }
1605
+ />
1606
+ </div>)
1607
+ }
1608
+
1609
+ function NotificationFeedbackErrorSample() {
1610
+ const t = useT()
1611
+ return (<div className="w-full max-w-md">
1612
+ <Notification
1613
+ status="error"
1614
+ title={t('design_system.gallery.sampleCopy.webhookDeliveryFailed')}
1615
+ description={t('design_system.gallery.sampleCopy.theEndpointRespondedWith503After3Retries')}
1616
+ timestamp={t('design_system.gallery.sampleCopy.5MinAgo')}
1617
+ actions={<LinkButton variant="error" underline="always">{t('design_system.gallery.sampleCopy.retryNow')}</LinkButton>}
1618
+ />
1619
+ </div>)
1620
+ }
1621
+
1622
+ function NotificationFeedFeedbackDefaultSample() {
1623
+ const t = useT()
1624
+ return (<div className="w-full max-w-sm">
1625
+ <NotificationFeed>
1626
+ <NotificationFeedHeader title={t('design_system.families.notifications')}>
1627
+ <IconButton size="sm" variant="ghost" aria-label={t('design_system.gallery.sampleCopy.notificationSettings')}>
1628
+ <Settings />
1629
+ </IconButton>
1630
+ </NotificationFeedHeader>
1631
+ <NotificationFeedList>
1632
+ <NotificationFeedItem
1633
+ icon={
1634
+ <NotificationFeedIconBadge tone="indigo">
1635
+ <UserPlus className="size-5" />
1636
+ </NotificationFeedIconBadge>
1637
+ }
1638
+ title={t('design_system.gallery.sampleCopy.newLeadGenerated')}
1639
+ body={t('design_system.gallery.sampleCopy.johnSmithSubmittedTheContactForm')}
1640
+ timestamp={t('design_system.gallery.sampleCopy.10MinutesAgo')}
1641
+ unread
1642
+ />
1643
+ <NotificationFeedItem
1644
+ icon={
1645
+ <NotificationFeedIconBadge tone="success">
1646
+ <RefreshCw className="size-5" />
1647
+ </NotificationFeedIconBadge>
1648
+ }
1649
+ title={t('design_system.gallery.sampleCopy.catalogSyncCompleted')}
1650
+ body={t('design_system.gallery.sampleCopy.312ProductsUpdatedFromThePIM')}
1651
+ timestamp={t('design_system.gallery.sampleCopy.1HourAgo')}
1652
+ />
1653
+ </NotificationFeedList>
1654
+ <NotificationFeedFooter>
1655
+ <Button variant="outline" size="sm" className="w-full">{t('design_system.gallery.samples.feeds.archive')}</Button>
1656
+ </NotificationFeedFooter>
1657
+ </NotificationFeed>
1658
+ </div>)
1659
+ }