@pylonsync/create-pylon 0.19.0 → 0.20.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 (675) hide show
  1. package/bin/create-pylon.js +30 -7
  2. package/package.json +1 -1
  3. package/templates/agency/.env.example +7 -0
  4. package/templates/agency/README.md +28 -3
  5. package/templates/agency/app/(marketing)/contact-form.tsx +20 -18
  6. package/templates/agency/app/(marketing)/page.tsx +3 -3
  7. package/templates/agency/app/(marketing)/work/page.tsx +3 -3
  8. package/templates/agency/app/auth-form.tsx +4 -4
  9. package/templates/agency/app/dashboard/dashboard-client.tsx +110 -64
  10. package/templates/agency/app/dashboard/page.tsx +14 -16
  11. package/templates/agency/app/icon.svg +3 -0
  12. package/templates/agency/app/login/page.tsx +12 -14
  13. package/templates/agency/components/marketing.tsx +12 -5
  14. package/templates/agency/functions/seedStudioBackoffice.ts +42 -11
  15. package/templates/agency/lib/agency.ts +22 -0
  16. package/templates/agency/lib/demo.ts +118 -0
  17. package/templates/agency/lib/site.config.ts +19 -13
  18. package/templates/agency/public/favicon.ico +0 -0
  19. package/templates/agency/tests/agency.test.ts +57 -0
  20. package/templates/ai-chat/.env.example +13 -27
  21. package/templates/ai-chat/README.md +53 -60
  22. package/templates/ai-chat/app/(chat)/c/[id]/page.tsx +19 -0
  23. package/templates/ai-chat/app/(chat)/chat-client.tsx +657 -622
  24. package/templates/ai-chat/app/(chat)/layout.tsx +6 -43
  25. package/templates/ai-chat/app/(chat)/not-found.tsx +6 -10
  26. package/templates/ai-chat/app/(chat)/page.tsx +7 -11
  27. package/templates/ai-chat/app/auth-form.tsx +45 -38
  28. package/templates/ai-chat/app/error.tsx +9 -12
  29. package/templates/ai-chat/app/globals.css +464 -102
  30. package/templates/ai-chat/app/layout.tsx +11 -19
  31. package/templates/ai-chat/app/login/page.tsx +39 -24
  32. package/templates/ai-chat/app.ts +118 -43
  33. package/templates/ai-chat/components/chat/composer.tsx +193 -0
  34. package/templates/ai-chat/components/chat/empty-state.tsx +65 -0
  35. package/templates/ai-chat/components/chat/icons.tsx +198 -0
  36. package/templates/ai-chat/components/chat/markdown.tsx +164 -0
  37. package/templates/ai-chat/components/chat/message.tsx +183 -0
  38. package/templates/ai-chat/components/chat/setup.tsx +71 -0
  39. package/templates/ai-chat/components/chat/sidebar.tsx +288 -0
  40. package/templates/ai-chat/functions/_beginTurn.ts +145 -0
  41. package/templates/ai-chat/functions/_finishTurn.ts +55 -0
  42. package/templates/ai-chat/functions/assistantStatus.ts +13 -0
  43. package/templates/ai-chat/functions/claimGuestHistory.ts +39 -0
  44. package/templates/ai-chat/functions/createConversation.ts +28 -0
  45. package/templates/ai-chat/functions/deleteConversation.ts +15 -20
  46. package/templates/ai-chat/functions/prepareGuestClaim.ts +32 -0
  47. package/templates/ai-chat/functions/renameConversation.ts +19 -0
  48. package/templates/ai-chat/functions/respond.ts +88 -0
  49. package/templates/ai-chat/functions/seedExamples.ts +46 -0
  50. package/templates/ai-chat/functions/stopResponse.ts +30 -0
  51. package/templates/ai-chat/lib/brand-icon.ts +7 -0
  52. package/templates/ai-chat/lib/chat.ts +277 -0
  53. package/templates/ai-chat/lib/claim.ts +15 -0
  54. package/templates/ai-chat/lib/examples.ts +179 -0
  55. package/templates/ai-chat/lib/highlight.ts +107 -0
  56. package/templates/ai-chat/lib/markdown.ts +317 -0
  57. package/templates/ai-chat/lib/provider.ts +38 -0
  58. package/templates/ai-chat/lib/site.config.ts +55 -39
  59. package/templates/ai-chat/tests/chat.test.ts +174 -0
  60. package/templates/ai-chat/tests/components.test.tsx +57 -0
  61. package/templates/ai-chat/tests/markdown.test.ts +88 -0
  62. package/templates/ai-chat/tests/provider.test.ts +32 -0
  63. package/templates/backend/consumer/apps/api/app/layout.tsx +33 -0
  64. package/templates/backend/consumer/apps/api/app/page.tsx +54 -0
  65. package/templates/backend/consumer/apps/api/app.ts +166 -0
  66. package/templates/backend/consumer/apps/api/functions/addComment.ts +24 -0
  67. package/templates/backend/consumer/apps/api/functions/createPost.ts +19 -36
  68. package/templates/backend/consumer/apps/api/functions/deletePost.ts +15 -13
  69. package/templates/backend/consumer/apps/api/functions/myProfile.ts +5 -8
  70. package/templates/backend/consumer/apps/api/functions/seedDemo.ts +95 -0
  71. package/templates/backend/consumer/apps/api/functions/setFollow.ts +36 -0
  72. package/templates/backend/consumer/apps/api/functions/setLike.ts +36 -0
  73. package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +43 -37
  74. package/templates/backend/consumer/apps/api/lib/seed.ts +89 -0
  75. package/templates/backend/consumer/apps/api/lib/social.ts +50 -0
  76. package/templates/backend/consumer/apps/api/package.json +8 -2
  77. package/templates/backend/consumer/apps/api/public/images/posts/arch.jpg +0 -0
  78. package/templates/backend/consumer/apps/api/public/images/posts/coffee.jpg +0 -0
  79. package/templates/backend/consumer/apps/api/public/images/posts/dog.jpg +0 -0
  80. package/templates/backend/consumer/apps/api/public/images/posts/lisbon.jpg +0 -0
  81. package/templates/backend/consumer/apps/api/public/images/posts/ramen.jpg +0 -0
  82. package/templates/backend/consumer/apps/api/public/images/posts/run.jpg +0 -0
  83. package/templates/backend/consumer/apps/api/public/images/posts/sunset.jpg +0 -0
  84. package/templates/backend/consumer/apps/api/tsconfig.json +12 -2
  85. package/templates/chat/README.md +43 -21
  86. package/templates/chat/app/chat-client.tsx +495 -85
  87. package/templates/chat/app/error.tsx +1 -1
  88. package/templates/chat/app/globals.css +170 -54
  89. package/templates/chat/app/layout.tsx +12 -9
  90. package/templates/chat/app/not-found.tsx +4 -2
  91. package/templates/chat/app/page.tsx +9 -19
  92. package/templates/chat/app.ts +96 -17
  93. package/templates/chat/components/chat/avatar.tsx +45 -0
  94. package/templates/chat/components/chat/composer.tsx +110 -0
  95. package/templates/chat/components/chat/message-list.tsx +285 -0
  96. package/templates/chat/components/chat/sidebar.tsx +190 -0
  97. package/templates/chat/functions/joinChat.ts +83 -0
  98. package/templates/chat/functions/renameMember.ts +25 -0
  99. package/templates/chat/lib/chat.ts +188 -0
  100. package/templates/chat/lib/seed.ts +78 -0
  101. package/templates/chat/tests/chat.test.ts +159 -0
  102. package/templates/chat/tests/composer.test.tsx +54 -0
  103. package/templates/consumer/README.md +3 -3
  104. package/templates/consumer/app/globals.css +10 -3
  105. package/templates/consumer/app/icon.svg +1 -0
  106. package/templates/consumer/components/social/avatar.tsx +25 -11
  107. package/templates/consumer/functions/seedFeed.ts +3 -3
  108. package/templates/consumer/lib/seed.ts +43 -85
  109. package/templates/consumer/public/favicon.ico +0 -0
  110. package/templates/consumer/public/images/posts/arch.jpg +0 -0
  111. package/templates/consumer/public/images/posts/coffee.jpg +0 -0
  112. package/templates/consumer/public/images/posts/dog.jpg +0 -0
  113. package/templates/consumer/public/images/posts/lisbon.jpg +0 -0
  114. package/templates/consumer/public/images/posts/ramen.jpg +0 -0
  115. package/templates/consumer/public/images/posts/run.jpg +0 -0
  116. package/templates/consumer/public/images/posts/sunset.jpg +0 -0
  117. package/templates/consumer/tests/seed.test.ts +39 -0
  118. package/templates/creator/.env.example +7 -0
  119. package/templates/creator/README.md +24 -5
  120. package/templates/creator/app/(marketing)/newsletter-signup.tsx +33 -12
  121. package/templates/creator/app/auth-form.tsx +7 -7
  122. package/templates/creator/app/dashboard/dashboard-client.tsx +137 -100
  123. package/templates/creator/app/dashboard/page.tsx +18 -18
  124. package/templates/creator/app/icon.svg +2 -0
  125. package/templates/creator/app/login/page.tsx +14 -14
  126. package/templates/creator/app.ts +4 -4
  127. package/templates/creator/functions/seedDemo.ts +32 -0
  128. package/templates/creator/functions/subscribe.ts +4 -15
  129. package/templates/creator/lib/demo.ts +94 -0
  130. package/templates/creator/lib/site.config.ts +15 -10
  131. package/templates/creator/lib/stats.ts +35 -0
  132. package/templates/creator/public/favicon.ico +0 -0
  133. package/templates/creator/tests/counter.test.ts +92 -0
  134. package/templates/creator/tests/live-count.test.tsx +45 -0
  135. package/templates/crm/README.md +22 -1
  136. package/templates/crm/app/apple-icon.png +0 -0
  137. package/templates/crm/app/companies/companies-view.tsx +32 -14
  138. package/templates/crm/app/companies/page.tsx +2 -1
  139. package/templates/crm/app/contacts/contacts-view.tsx +34 -10
  140. package/templates/crm/app/contacts/page.tsx +2 -1
  141. package/templates/crm/app/deals/[id]/deal-view.tsx +137 -98
  142. package/templates/crm/app/deals/[id]/page.tsx +2 -1
  143. package/templates/crm/app/globals.css +29 -14
  144. package/templates/crm/app/icon.svg +1 -0
  145. package/templates/crm/app/layout.tsx +7 -5
  146. package/templates/crm/app/page.tsx +2 -1
  147. package/templates/crm/app/pipeline-view.tsx +5 -5
  148. package/templates/crm/app/workspace.tsx +29 -17
  149. package/templates/crm/app.ts +5 -5
  150. package/templates/crm/components/activity-timeline.tsx +14 -4
  151. package/templates/crm/components/app-shell.tsx +94 -0
  152. package/templates/crm/components/auth-form.tsx +35 -5
  153. package/templates/crm/components/avatar.tsx +5 -1
  154. package/templates/crm/components/board-skeleton.tsx +5 -5
  155. package/templates/crm/components/brand-mark.tsx +25 -0
  156. package/templates/crm/components/command-palette.tsx +17 -17
  157. package/templates/crm/components/data-table.tsx +97 -36
  158. package/templates/crm/components/deal-card.tsx +23 -27
  159. package/templates/crm/components/filter-tabs.tsx +101 -0
  160. package/templates/crm/components/metrics-bar.tsx +6 -6
  161. package/templates/crm/components/nav.tsx +15 -0
  162. package/templates/crm/components/page-header.tsx +43 -7
  163. package/templates/crm/components/pipeline-board.tsx +16 -10
  164. package/templates/crm/components/require-auth.tsx +24 -26
  165. package/templates/crm/components/sidebar.tsx +54 -49
  166. package/templates/crm/components/stage-badge.tsx +4 -2
  167. package/templates/crm/components/ui/button.tsx +5 -4
  168. package/templates/crm/components/ui/dialog.tsx +1 -1
  169. package/templates/crm/components/ui/input.tsx +1 -1
  170. package/templates/crm/components/ui/select.tsx +1 -1
  171. package/templates/crm/components/ui/textarea.tsx +1 -1
  172. package/templates/crm/functions/seedWorkspace.ts +32 -17
  173. package/templates/crm/lib/brand.ts +9 -0
  174. package/templates/crm/lib/demo-team.ts +51 -0
  175. package/templates/crm/lib/pipeline.ts +37 -0
  176. package/templates/crm/lib/search.ts +4 -3
  177. package/templates/crm/lib/seed.ts +53 -8
  178. package/templates/crm/public/favicon.ico +0 -0
  179. package/templates/crm/tests/components.test.tsx +5 -5
  180. package/templates/crm/tests/demo-team.test.ts +18 -0
  181. package/templates/crm/tests/pipeline.test.ts +59 -3
  182. package/templates/directory/.env.example +7 -0
  183. package/templates/directory/README.md +22 -3
  184. package/templates/directory/app/(marketing)/directory-browse.tsx +30 -12
  185. package/templates/directory/app/(marketing)/page.tsx +6 -4
  186. package/templates/directory/app/auth-form.tsx +4 -4
  187. package/templates/directory/app/dashboard/dashboard-client.tsx +177 -90
  188. package/templates/directory/app/dashboard/page.tsx +16 -14
  189. package/templates/directory/app/icon.svg +4 -0
  190. package/templates/directory/app/login/page.tsx +16 -15
  191. package/templates/directory/functions/seedDemo.ts +43 -0
  192. package/templates/directory/lib/demo.ts +145 -0
  193. package/templates/directory/lib/directory.ts +27 -0
  194. package/templates/directory/lib/site.config.ts +21 -16
  195. package/templates/directory/public/favicon.ico +0 -0
  196. package/templates/directory/tests/directory.test.ts +46 -0
  197. package/templates/expo/consumer/apps/expo/App.tsx +360 -288
  198. package/templates/expo/consumer/apps/expo/app.json +9 -1
  199. package/templates/expo/consumer/apps/expo/package.json +2 -1
  200. package/templates/helpdesk/README.md +21 -0
  201. package/templates/helpdesk/app/apple-icon.png +0 -0
  202. package/templates/helpdesk/app/customers/customers-view.tsx +23 -7
  203. package/templates/helpdesk/app/customers/page.tsx +2 -1
  204. package/templates/helpdesk/app/globals.css +29 -14
  205. package/templates/helpdesk/app/icon.svg +1 -0
  206. package/templates/helpdesk/app/inbox-view.tsx +66 -47
  207. package/templates/helpdesk/app/layout.tsx +7 -5
  208. package/templates/helpdesk/app/page.tsx +4 -7
  209. package/templates/helpdesk/app/tickets/[id]/page.tsx +2 -1
  210. package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +134 -97
  211. package/templates/helpdesk/app/workspace.tsx +33 -22
  212. package/templates/helpdesk/app.ts +2 -2
  213. package/templates/helpdesk/components/app-shell.tsx +94 -0
  214. package/templates/helpdesk/components/auth-form.tsx +35 -5
  215. package/templates/helpdesk/components/avatar.tsx +5 -1
  216. package/templates/helpdesk/components/brand-mark.tsx +28 -0
  217. package/templates/helpdesk/components/command-palette.tsx +17 -17
  218. package/templates/helpdesk/components/data-table.tsx +97 -36
  219. package/templates/helpdesk/components/filter-tabs.tsx +101 -0
  220. package/templates/helpdesk/components/nav.tsx +14 -0
  221. package/templates/helpdesk/components/page-header.tsx +43 -7
  222. package/templates/helpdesk/components/priority-badge.tsx +49 -6
  223. package/templates/helpdesk/components/require-auth.tsx +24 -26
  224. package/templates/helpdesk/components/sidebar.tsx +54 -48
  225. package/templates/helpdesk/components/sla-indicator.tsx +5 -8
  226. package/templates/helpdesk/components/thread.tsx +7 -7
  227. package/templates/helpdesk/components/ticket-list.tsx +79 -19
  228. package/templates/helpdesk/components/ui/button.tsx +5 -4
  229. package/templates/helpdesk/components/ui/dialog.tsx +1 -1
  230. package/templates/helpdesk/components/ui/input.tsx +1 -1
  231. package/templates/helpdesk/components/ui/select.tsx +1 -1
  232. package/templates/helpdesk/components/ui/textarea.tsx +1 -1
  233. package/templates/helpdesk/functions/seedWorkspace.ts +25 -15
  234. package/templates/helpdesk/lib/brand.ts +9 -0
  235. package/templates/helpdesk/lib/demo-team.ts +51 -0
  236. package/templates/helpdesk/lib/search.ts +4 -3
  237. package/templates/helpdesk/lib/seed.ts +43 -2
  238. package/templates/helpdesk/lib/tickets.ts +68 -20
  239. package/templates/helpdesk/public/favicon.ico +0 -0
  240. package/templates/helpdesk/tests/components.test.tsx +10 -0
  241. package/templates/helpdesk/tests/demo-team.test.ts +18 -0
  242. package/templates/helpdesk/tests/tickets.test.ts +70 -0
  243. package/templates/imessage/.env.example +49 -0
  244. package/templates/imessage/AGENTS.md +153 -0
  245. package/templates/imessage/CLAUDE.md +7 -0
  246. package/templates/imessage/README.md +187 -0
  247. package/templates/imessage/app/console.tsx +382 -0
  248. package/templates/imessage/app/contacts/page.tsx +13 -0
  249. package/templates/imessage/app/error.tsx +16 -0
  250. package/templates/imessage/app/gate.tsx +31 -0
  251. package/templates/imessage/app/globals.css +183 -0
  252. package/templates/imessage/app/layout.tsx +17 -0
  253. package/templates/imessage/app/login/login-form.tsx +115 -0
  254. package/templates/imessage/app/login/page.tsx +30 -0
  255. package/templates/imessage/app/not-found.tsx +13 -0
  256. package/templates/imessage/app/page.tsx +14 -0
  257. package/templates/imessage/app/robots.ts +6 -0
  258. package/templates/imessage/app/setup/page.tsx +13 -0
  259. package/templates/imessage/app/sign-out.tsx +20 -0
  260. package/templates/imessage/app.ts +299 -0
  261. package/templates/imessage/bunfig +4 -0
  262. package/templates/imessage/components/avatar.tsx +46 -0
  263. package/templates/imessage/components/contact-panel.tsx +263 -0
  264. package/templates/imessage/components/contacts-view.tsx +172 -0
  265. package/templates/imessage/components/conversation-list.tsx +129 -0
  266. package/templates/imessage/components/nav-rail.tsx +67 -0
  267. package/templates/imessage/components/setup-view.tsx +408 -0
  268. package/templates/imessage/components/thread.tsx +229 -0
  269. package/templates/imessage/components/ui/badge.tsx +37 -0
  270. package/templates/imessage/components/ui/button.tsx +56 -0
  271. package/templates/imessage/components/ui/card.tsx +90 -0
  272. package/templates/imessage/components/ui/dialog.tsx +110 -0
  273. package/templates/imessage/components/ui/input.tsx +30 -0
  274. package/templates/imessage/components/ui/label.tsx +21 -0
  275. package/templates/imessage/components/ui/select.tsx +39 -0
  276. package/templates/imessage/components/ui/table.tsx +124 -0
  277. package/templates/imessage/components/ui/textarea.tsx +24 -0
  278. package/templates/imessage/components.json +20 -0
  279. package/templates/imessage/functions/answerNow.ts +34 -0
  280. package/templates/imessage/functions/assistant.ts +96 -0
  281. package/templates/imessage/functions/beginTurn.ts +101 -0
  282. package/templates/imessage/functions/cancelReminder.ts +13 -0
  283. package/templates/imessage/functions/claimOutbound.ts +44 -0
  284. package/templates/imessage/functions/clearDemo.ts +18 -0
  285. package/templates/imessage/functions/deleteNote.ts +11 -0
  286. package/templates/imessage/functions/deliverOutbound.ts +55 -0
  287. package/templates/imessage/functions/finishTurn.ts +84 -0
  288. package/templates/imessage/functions/fireReminder.ts +42 -0
  289. package/templates/imessage/functions/ingestInbound.ts +22 -0
  290. package/templates/imessage/functions/ownerAccount.ts +13 -0
  291. package/templates/imessage/functions/processTurn.ts +74 -0
  292. package/templates/imessage/functions/relayApply.ts +102 -0
  293. package/templates/imessage/functions/relaySync.ts +63 -0
  294. package/templates/imessage/functions/releaseOutbound.ts +18 -0
  295. package/templates/imessage/functions/resolveTurn.ts +25 -0
  296. package/templates/imessage/functions/saveContact.ts +27 -0
  297. package/templates/imessage/functions/seedDemo.ts +70 -0
  298. package/templates/imessage/functions/sendOwnerMessage.ts +28 -0
  299. package/templates/imessage/functions/sendblueWebhook.ts +49 -0
  300. package/templates/imessage/functions/setContactStatus.ts +17 -0
  301. package/templates/imessage/functions/settleOutbound.ts +35 -0
  302. package/templates/imessage/functions/setupStatus.ts +33 -0
  303. package/templates/imessage/functions/sweep.ts +58 -0
  304. package/templates/imessage/functions/toolCancelReminder.ts +17 -0
  305. package/templates/imessage/functions/toolForgetNote.ts +15 -0
  306. package/templates/imessage/functions/toolListNotes.ts +16 -0
  307. package/templates/imessage/functions/toolListReminders.ts +17 -0
  308. package/templates/imessage/functions/toolSaveNote.ts +22 -0
  309. package/templates/imessage/functions/toolSetReminder.ts +55 -0
  310. package/templates/imessage/functions/updateSettings.ts +48 -0
  311. package/templates/imessage/gitignore +11 -0
  312. package/templates/imessage/lib/agent-call.ts +251 -0
  313. package/templates/imessage/lib/assistant.ts +74 -0
  314. package/templates/imessage/lib/chunk.ts +135 -0
  315. package/templates/imessage/lib/demo.ts +68 -0
  316. package/templates/imessage/lib/format.ts +170 -0
  317. package/templates/imessage/lib/gate.ts +104 -0
  318. package/templates/imessage/lib/handles.ts +67 -0
  319. package/templates/imessage/lib/provider.ts +38 -0
  320. package/templates/imessage/lib/relay-protocol.ts +95 -0
  321. package/templates/imessage/lib/reminders.ts +41 -0
  322. package/templates/imessage/lib/secrets.ts +59 -0
  323. package/templates/imessage/lib/sendblue.ts +173 -0
  324. package/templates/imessage/lib/store.ts +407 -0
  325. package/templates/imessage/lib/transport.ts +135 -0
  326. package/templates/imessage/lib/utils.ts +10 -0
  327. package/templates/imessage/package.json +40 -0
  328. package/templates/imessage/relay/.env.example +14 -0
  329. package/templates/imessage/relay/applescript.ts +70 -0
  330. package/templates/imessage/relay/chatdb.ts +165 -0
  331. package/templates/imessage/relay/client.ts +41 -0
  332. package/templates/imessage/relay/main.ts +137 -0
  333. package/templates/imessage/relay/state.ts +35 -0
  334. package/templates/imessage/tests/agent-call.test.ts +112 -0
  335. package/templates/imessage/tests/applescript.test.ts +88 -0
  336. package/templates/imessage/tests/chatdb.test.ts +139 -0
  337. package/templates/imessage/tests/chunk.test.ts +61 -0
  338. package/templates/imessage/tests/fake-ctx.ts +88 -0
  339. package/templates/imessage/tests/ingest.test.ts +141 -0
  340. package/templates/imessage/tests/lib.test.ts +99 -0
  341. package/templates/imessage/tests/review-fixes.test.ts +138 -0
  342. package/templates/imessage/tests/secrets.test.ts +36 -0
  343. package/templates/imessage/tests/sendblue.test.ts +143 -0
  344. package/templates/imessage/tests/setup.ts +5 -0
  345. package/templates/imessage/tsconfig.json +18 -0
  346. package/templates/inventory/README.md +21 -0
  347. package/templates/inventory/app/apple-icon.png +0 -0
  348. package/templates/inventory/app/globals.css +29 -14
  349. package/templates/inventory/app/icon.svg +1 -0
  350. package/templates/inventory/app/layout.tsx +7 -5
  351. package/templates/inventory/app/movements/movements-view.tsx +32 -10
  352. package/templates/inventory/app/movements/page.tsx +2 -1
  353. package/templates/inventory/app/page.tsx +4 -6
  354. package/templates/inventory/app/products/[id]/page.tsx +2 -1
  355. package/templates/inventory/app/products/[id]/product-view.tsx +55 -25
  356. package/templates/inventory/app/products-view.tsx +74 -33
  357. package/templates/inventory/app/workspace.tsx +34 -16
  358. package/templates/inventory/app.ts +10 -2
  359. package/templates/inventory/components/app-shell.tsx +94 -0
  360. package/templates/inventory/components/auth-form.tsx +35 -5
  361. package/templates/inventory/components/avatar.tsx +5 -1
  362. package/templates/inventory/components/brand-mark.tsx +25 -0
  363. package/templates/inventory/components/category-tile.tsx +49 -0
  364. package/templates/inventory/components/command-palette.tsx +17 -17
  365. package/templates/inventory/components/data-table.tsx +97 -36
  366. package/templates/inventory/components/filter-tabs.tsx +101 -0
  367. package/templates/inventory/components/nav.tsx +14 -0
  368. package/templates/inventory/components/page-header.tsx +43 -7
  369. package/templates/inventory/components/require-auth.tsx +24 -26
  370. package/templates/inventory/components/sidebar.tsx +54 -48
  371. package/templates/inventory/components/stock-level.tsx +30 -14
  372. package/templates/inventory/components/summary-bar.tsx +8 -15
  373. package/templates/inventory/components/ui/button.tsx +5 -4
  374. package/templates/inventory/components/ui/dialog.tsx +1 -1
  375. package/templates/inventory/components/ui/input.tsx +1 -1
  376. package/templates/inventory/components/ui/select.tsx +1 -1
  377. package/templates/inventory/components/ui/textarea.tsx +1 -1
  378. package/templates/inventory/functions/seedWorkspace.ts +18 -8
  379. package/templates/inventory/lib/brand.ts +9 -0
  380. package/templates/inventory/lib/demo-team.ts +51 -0
  381. package/templates/inventory/lib/search.ts +4 -3
  382. package/templates/inventory/lib/seed.ts +29 -2
  383. package/templates/inventory/lib/stock.ts +32 -0
  384. package/templates/inventory/public/favicon.ico +0 -0
  385. package/templates/inventory/tests/demo-team.test.ts +18 -0
  386. package/templates/inventory/tests/stock.test.ts +29 -0
  387. package/templates/invoices/README.md +16 -0
  388. package/templates/invoices/app/apple-icon.png +0 -0
  389. package/templates/invoices/app/clients/clients-view.tsx +18 -6
  390. package/templates/invoices/app/clients/page.tsx +2 -1
  391. package/templates/invoices/app/globals.css +29 -14
  392. package/templates/invoices/app/icon.svg +1 -0
  393. package/templates/invoices/app/invoices/[id]/invoice-view.tsx +72 -48
  394. package/templates/invoices/app/invoices/[id]/page.tsx +2 -1
  395. package/templates/invoices/app/invoices-view.tsx +81 -32
  396. package/templates/invoices/app/layout.tsx +7 -5
  397. package/templates/invoices/app/page.tsx +4 -6
  398. package/templates/invoices/app/workspace.tsx +26 -16
  399. package/templates/invoices/app.ts +2 -2
  400. package/templates/invoices/components/app-shell.tsx +94 -0
  401. package/templates/invoices/components/auth-form.tsx +35 -5
  402. package/templates/invoices/components/avatar.tsx +5 -1
  403. package/templates/invoices/components/brand-mark.tsx +26 -0
  404. package/templates/invoices/components/command-palette.tsx +17 -17
  405. package/templates/invoices/components/data-table.tsx +97 -36
  406. package/templates/invoices/components/filter-tabs.tsx +101 -0
  407. package/templates/invoices/components/line-items.tsx +19 -14
  408. package/templates/invoices/components/nav.tsx +14 -0
  409. package/templates/invoices/components/page-header.tsx +43 -7
  410. package/templates/invoices/components/require-auth.tsx +24 -26
  411. package/templates/invoices/components/sidebar.tsx +54 -48
  412. package/templates/invoices/components/status-badge.tsx +7 -7
  413. package/templates/invoices/components/summary-bar.tsx +5 -5
  414. package/templates/invoices/components/ui/button.tsx +5 -4
  415. package/templates/invoices/components/ui/dialog.tsx +1 -1
  416. package/templates/invoices/components/ui/input.tsx +1 -1
  417. package/templates/invoices/components/ui/select.tsx +1 -1
  418. package/templates/invoices/components/ui/textarea.tsx +1 -1
  419. package/templates/invoices/functions/recordPayment.ts +3 -0
  420. package/templates/invoices/lib/billing.ts +40 -0
  421. package/templates/invoices/lib/brand.ts +9 -0
  422. package/templates/invoices/lib/search.ts +4 -3
  423. package/templates/invoices/public/favicon.ico +0 -0
  424. package/templates/invoices/tests/billing.test.ts +27 -0
  425. package/templates/ios/consumer/apps/ios/Package.swift +4 -3
  426. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ActivityView.swift +107 -0
  427. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AppModel.swift +171 -0
  428. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AuthView.swift +182 -0
  429. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/CommentsView.swift +136 -0
  430. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ComposeView.swift +152 -0
  431. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ExploreView.swift +117 -0
  432. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +175 -159
  433. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/MainTabView.swift +54 -0
  434. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Media.swift +271 -0
  435. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +101 -4
  436. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/PostCard.swift +245 -0
  437. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileEditorView.swift +208 -0
  438. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileView.swift +183 -0
  439. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +25 -31
  440. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/SocialStore.swift +285 -0
  441. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/StoryViewer.swift +155 -0
  442. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Theme.swift +82 -0
  443. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +4 -47
  444. package/templates/ios/consumer/apps/ios/project.yml +13 -9
  445. package/templates/local-service/.env.example +7 -0
  446. package/templates/local-service/README.md +26 -2
  447. package/templates/local-service/app/(marketing)/booking-widget.tsx +48 -9
  448. package/templates/local-service/app/(marketing)/page.tsx +66 -42
  449. package/templates/local-service/app/auth-form.tsx +8 -8
  450. package/templates/local-service/app/dashboard/dashboard-client.tsx +119 -92
  451. package/templates/local-service/app/dashboard/page.tsx +14 -14
  452. package/templates/local-service/app/icon.svg +2 -0
  453. package/templates/local-service/app/login/page.tsx +15 -15
  454. package/templates/local-service/functions/seedDemo.ts +49 -0
  455. package/templates/local-service/lib/demo.ts +135 -0
  456. package/templates/local-service/lib/hours.ts +44 -0
  457. package/templates/local-service/lib/site.config.ts +8 -3
  458. package/templates/local-service/lib/slots.ts +3 -0
  459. package/templates/local-service/public/favicon.ico +0 -0
  460. package/templates/local-service/tests/booking.test.ts +104 -0
  461. package/templates/marketplace/README.md +23 -15
  462. package/templates/marketplace/app/globals.css +172 -114
  463. package/templates/marketplace/app/layout.tsx +75 -30
  464. package/templates/marketplace/app/listing/[id]/page.tsx +210 -123
  465. package/templates/marketplace/app/me/page.tsx +4 -4
  466. package/templates/marketplace/app/page.tsx +221 -298
  467. package/templates/marketplace/app/sell/page.tsx +8 -11
  468. package/templates/marketplace/app.ts +32 -7
  469. package/templates/marketplace/client/ActivityFeed.tsx +311 -0
  470. package/templates/marketplace/client/AuthNav.tsx +24 -16
  471. package/templates/marketplace/client/ListingCard.tsx +118 -0
  472. package/templates/marketplace/client/LiveGrid.tsx +92 -0
  473. package/templates/marketplace/client/LiveListingStatus.tsx +32 -24
  474. package/templates/marketplace/client/LoginCard.tsx +114 -136
  475. package/templates/marketplace/client/MarketProvider.tsx +3 -5
  476. package/templates/marketplace/client/MyMarket.tsx +371 -185
  477. package/templates/marketplace/client/OfferPanel.tsx +421 -287
  478. package/templates/marketplace/client/SellForm.tsx +271 -192
  479. package/templates/marketplace/client/ThemeToggle.tsx +1 -0
  480. package/templates/marketplace/client/WatchButton.tsx +16 -16
  481. package/templates/marketplace/client/market.ts +61 -14
  482. package/templates/marketplace/functions/seedMarket.ts +143 -36
  483. package/templates/marketplace/functions/seedOffers.ts +101 -0
  484. package/templates/marketplace/lib/catalog.ts +72 -1
  485. package/templates/marketplace/public/images/listings/ceramic-lamp-640.webp +0 -0
  486. package/templates/marketplace/public/images/listings/ceramic-lamp.webp +0 -0
  487. package/templates/marketplace/public/images/listings/duffel-640.webp +0 -0
  488. package/templates/marketplace/public/images/listings/duffel.webp +0 -0
  489. package/templates/marketplace/public/images/listings/film-camera-640.webp +0 -0
  490. package/templates/marketplace/public/images/listings/film-camera.webp +0 -0
  491. package/templates/marketplace/public/images/listings/lounge-chair-640.webp +0 -0
  492. package/templates/marketplace/public/images/listings/lounge-chair.webp +0 -0
  493. package/templates/marketplace/public/images/listings/road-bike-640.webp +0 -0
  494. package/templates/marketplace/public/images/listings/road-bike.webp +0 -0
  495. package/templates/marketplace/public/images/listings/slipper-chair-640.webp +0 -0
  496. package/templates/marketplace/public/images/listings/slipper-chair.webp +0 -0
  497. package/templates/marketplace/public/images/listings/turntable-640.webp +0 -0
  498. package/templates/marketplace/public/images/listings/turntable.webp +0 -0
  499. package/templates/marketplace/public/images/listings/wardrobe-640.webp +0 -0
  500. package/templates/marketplace/public/images/listings/wardrobe.webp +0 -0
  501. package/templates/marketplace/public/images/listings/watch-640.webp +0 -0
  502. package/templates/marketplace/public/images/listings/watch.webp +0 -0
  503. package/templates/marketplace/public/images/listings/weekender-bag-640.webp +0 -0
  504. package/templates/marketplace/public/images/listings/weekender-bag.webp +0 -0
  505. package/templates/marketplace/tests/example.test.ts +64 -2
  506. package/templates/marketplace/tests/listing-card.test.tsx +61 -0
  507. package/templates/projects/README.md +24 -3
  508. package/templates/projects/app/apple-icon.png +0 -0
  509. package/templates/projects/app/clients/clients-view.tsx +20 -6
  510. package/templates/projects/app/clients/page.tsx +2 -1
  511. package/templates/projects/app/globals.css +29 -14
  512. package/templates/projects/app/icon.svg +1 -0
  513. package/templates/projects/app/layout.tsx +7 -5
  514. package/templates/projects/app/page.tsx +3 -5
  515. package/templates/projects/app/projects/[id]/page.tsx +2 -1
  516. package/templates/projects/app/projects/[id]/project-view.tsx +45 -30
  517. package/templates/projects/app/projects-view.tsx +105 -52
  518. package/templates/projects/app/workspace.tsx +41 -20
  519. package/templates/projects/app.ts +2 -2
  520. package/templates/projects/components/app-shell.tsx +94 -0
  521. package/templates/projects/components/auth-form.tsx +35 -5
  522. package/templates/projects/components/avatar.tsx +5 -1
  523. package/templates/projects/components/board-skeleton.tsx +3 -3
  524. package/templates/projects/components/brand-mark.tsx +25 -0
  525. package/templates/projects/components/command-palette.tsx +17 -17
  526. package/templates/projects/components/data-table.tsx +97 -36
  527. package/templates/projects/components/filter-tabs.tsx +101 -0
  528. package/templates/projects/components/meter.tsx +48 -0
  529. package/templates/projects/components/nav.tsx +14 -0
  530. package/templates/projects/components/page-header.tsx +43 -7
  531. package/templates/projects/components/require-auth.tsx +24 -26
  532. package/templates/projects/components/sidebar.tsx +54 -48
  533. package/templates/projects/components/summary-bar.tsx +49 -0
  534. package/templates/projects/components/task-board.tsx +10 -9
  535. package/templates/projects/components/time-dialog.tsx +29 -5
  536. package/templates/projects/components/ui/button.tsx +5 -4
  537. package/templates/projects/components/ui/dialog.tsx +1 -1
  538. package/templates/projects/components/ui/input.tsx +1 -1
  539. package/templates/projects/components/ui/select.tsx +1 -1
  540. package/templates/projects/components/ui/textarea.tsx +1 -1
  541. package/templates/projects/functions/seedWorkspace.ts +26 -12
  542. package/templates/projects/lib/brand.ts +9 -0
  543. package/templates/projects/lib/demo-team.ts +51 -0
  544. package/templates/projects/lib/search.ts +4 -3
  545. package/templates/projects/lib/seed.ts +35 -1
  546. package/templates/projects/lib/work.ts +63 -0
  547. package/templates/projects/public/favicon.ico +0 -0
  548. package/templates/projects/tests/demo-team.test.ts +18 -0
  549. package/templates/projects/tests/work.test.ts +65 -0
  550. package/templates/restaurant/.env.example +7 -0
  551. package/templates/restaurant/README.md +29 -3
  552. package/templates/restaurant/app/(marketing)/layout.tsx +4 -2
  553. package/templates/restaurant/app/(marketing)/page.tsx +47 -23
  554. package/templates/restaurant/app/(marketing)/reservation-widget.tsx +37 -7
  555. package/templates/restaurant/app/auth-form.tsx +8 -8
  556. package/templates/restaurant/app/dashboard/dashboard-client.tsx +241 -109
  557. package/templates/restaurant/app/dashboard/page.tsx +14 -14
  558. package/templates/restaurant/app/icon.svg +4 -0
  559. package/templates/restaurant/app/login/page.tsx +15 -15
  560. package/templates/restaurant/functions/seedDemo.ts +44 -0
  561. package/templates/restaurant/lib/demo.ts +144 -0
  562. package/templates/restaurant/lib/hours.ts +45 -0
  563. package/templates/restaurant/lib/site.config.ts +3 -3
  564. package/templates/restaurant/public/favicon.ico +0 -0
  565. package/templates/restaurant/tests/reservations.test.ts +67 -0
  566. package/templates/saas/README.md +54 -35
  567. package/templates/saas/app/(auth)/auth-form.tsx +25 -23
  568. package/templates/saas/app/(auth)/forgot-password/page.tsx +1 -1
  569. package/templates/saas/app/(auth)/layout.tsx +27 -33
  570. package/templates/saas/app/(auth)/login/page.tsx +1 -1
  571. package/templates/saas/app/(auth)/reset-password/page.tsx +1 -1
  572. package/templates/saas/app/(auth)/signup/page.tsx +1 -1
  573. package/templates/saas/app/(marketing)/layout.tsx +127 -253
  574. package/templates/saas/app/(marketing)/page.tsx +133 -372
  575. package/templates/saas/app/(marketing)/pricing/page.tsx +21 -19
  576. package/templates/saas/app/(marketing)/products/[slug]/page.tsx +40 -95
  577. package/templates/saas/app/dashboard/board-client.tsx +416 -0
  578. package/templates/saas/app/dashboard/dashboard-client.tsx +30 -615
  579. package/templates/saas/app/dashboard/layout.tsx +27 -13
  580. package/templates/saas/app/dashboard/overview-client.tsx +332 -0
  581. package/templates/saas/app/dashboard/page.tsx +29 -35
  582. package/templates/saas/app/dashboard/projects/[id]/page.tsx +29 -0
  583. package/templates/saas/app/dashboard/projects/page.tsx +11 -7
  584. package/templates/saas/app/dashboard/projects-client.tsx +398 -0
  585. package/templates/saas/app/globals.css +5 -4
  586. package/templates/saas/app/icon.svg +1 -0
  587. package/templates/saas/app/onboarding/onboarding-client.tsx +93 -26
  588. package/templates/saas/app/onboarding/page.tsx +6 -2
  589. package/templates/saas/app.ts +68 -13
  590. package/templates/saas/components/dashboard-shell.tsx +212 -132
  591. package/templates/saas/components/marketing.tsx +137 -274
  592. package/templates/saas/components/pricing-table.tsx +12 -11
  593. package/templates/saas/components/setup-checklist.tsx +31 -29
  594. package/templates/saas/components/task-ui.tsx +199 -0
  595. package/templates/saas/functions/createProject.ts +7 -13
  596. package/templates/saas/functions/createTask.ts +67 -0
  597. package/templates/saas/functions/deleteProject.ts +17 -0
  598. package/templates/saas/functions/deleteTask.ts +14 -0
  599. package/templates/saas/functions/seedWorkspace.ts +46 -0
  600. package/templates/saas/functions/setProjectStatus.ts +41 -0
  601. package/templates/saas/functions/updateProfile.ts +13 -0
  602. package/templates/saas/functions/updateProject.ts +4 -5
  603. package/templates/saas/functions/updateTask.ts +88 -0
  604. package/templates/saas/lib/members.ts +53 -0
  605. package/templates/saas/lib/names.ts +53 -0
  606. package/templates/saas/lib/plans.ts +11 -0
  607. package/templates/saas/lib/sample-workspace.ts +117 -0
  608. package/templates/saas/lib/site.config.ts +200 -463
  609. package/templates/saas/lib/tasks.ts +101 -0
  610. package/templates/saas/lib/use-live.ts +25 -0
  611. package/templates/saas/public/favicon.ico +0 -0
  612. package/templates/saas/public/screenshots/board.png +0 -0
  613. package/templates/saas/public/screenshots/members.png +0 -0
  614. package/templates/saas/public/screenshots/overview.png +0 -0
  615. package/templates/saas/public/screenshots/projects.png +0 -0
  616. package/templates/saas/scripts/capture-screenshots.mjs +156 -0
  617. package/templates/saas/tests/names.test.ts +35 -0
  618. package/templates/saas/tests/plans.test.ts +12 -0
  619. package/templates/saas/tests/sample-workspace.test.ts +46 -0
  620. package/templates/saas/tests/tasks.test.ts +81 -0
  621. package/templates/shop/README.md +25 -13
  622. package/templates/shop/app/(marketing)/layout.tsx +94 -67
  623. package/templates/shop/app/(marketing)/not-found.tsx +13 -12
  624. package/templates/shop/app/(marketing)/page.tsx +120 -59
  625. package/templates/shop/app/(marketing)/shop-client.tsx +364 -237
  626. package/templates/shop/app/(marketing)/success/page.tsx +75 -22
  627. package/templates/shop/app/globals.css +94 -1
  628. package/templates/shop/app/layout.tsx +4 -4
  629. package/templates/shop/app/login/page.tsx +4 -4
  630. package/templates/shop/app.ts +18 -6
  631. package/templates/shop/components/cart-button.tsx +33 -0
  632. package/templates/shop/components/marketing.tsx +17 -87
  633. package/templates/shop/components/shelf-count.tsx +36 -0
  634. package/templates/shop/functions/checkout.ts +17 -1
  635. package/templates/shop/lib/cart-store.ts +57 -0
  636. package/templates/shop/lib/shelf-store.ts +35 -0
  637. package/templates/shop/lib/shop.ts +27 -0
  638. package/templates/shop/lib/site.config.ts +108 -98
  639. package/templates/shop/public/images/hero.jpg +0 -0
  640. package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
  641. package/templates/shop/public/images/products/fig-leaf.jpg +0 -0
  642. package/templates/shop/public/images/products/linen.jpg +0 -0
  643. package/templates/shop/public/images/products/sea-salt.jpg +0 -0
  644. package/templates/shop/public/images/products/tobacco-oak.jpg +0 -0
  645. package/templates/shop/tests/shop.test.ts +64 -0
  646. package/templates/waitlist/.env.example +7 -0
  647. package/templates/waitlist/README.md +29 -13
  648. package/templates/waitlist/app/(marketing)/page.tsx +9 -7
  649. package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +64 -31
  650. package/templates/waitlist/app/auth-form.tsx +8 -8
  651. package/templates/waitlist/app/dashboard/dashboard-client.tsx +132 -95
  652. package/templates/waitlist/app/dashboard/page.tsx +17 -16
  653. package/templates/waitlist/app/icon.svg +2 -0
  654. package/templates/waitlist/app/login/page.tsx +14 -14
  655. package/templates/waitlist/app.ts +6 -6
  656. package/templates/waitlist/functions/joinWaitlist.ts +6 -18
  657. package/templates/waitlist/functions/seedDemo.ts +32 -0
  658. package/templates/waitlist/lib/demo.ts +94 -0
  659. package/templates/waitlist/lib/launch.ts +19 -0
  660. package/templates/waitlist/lib/site.config.ts +14 -9
  661. package/templates/waitlist/lib/stats.ts +35 -0
  662. package/templates/waitlist/public/favicon.ico +0 -0
  663. package/templates/waitlist/tests/counter.test.ts +107 -0
  664. package/templates/waitlist/tests/live-count.test.tsx +48 -0
  665. package/templates/ai-chat/tests/example.test.ts +0 -12
  666. package/templates/backend/consumer/apps/api/functions/feed.ts +0 -57
  667. package/templates/backend/consumer/apps/api/functions/profilePosts.ts +0 -17
  668. package/templates/backend/consumer/apps/api/functions/toggleLike.ts +0 -48
  669. package/templates/backend/consumer/apps/api/schema.ts +0 -143
  670. package/templates/chat/functions/_keep.ts +0 -13
  671. package/templates/chat/tests/example.test.ts +0 -12
  672. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +0 -68
  673. package/templates/marketplace/client/LiveTicker.tsx +0 -116
  674. package/templates/marketplace/client/ScrollToListingsLink.tsx +0 -29
  675. package/templates/marketplace/public/images/marketplace-hero.webp +0 -0
@@ -5,6 +5,7 @@ import { ArrowLeftRight } from "lucide-react";
5
5
  import { PageHeader } from "@/components/page-header";
6
6
  import { DataTable, type ColumnDef } from "@/components/data-table";
7
7
  import { EmptyState } from "@/components/empty-state";
8
+ import { Avatar } from "@/components/avatar";
8
9
  import { relativeTime } from "@/lib/format";
9
10
  import { reasonById, signedQuantity } from "@/lib/stock";
10
11
  import { RequireAuth } from "@/components/require-auth";
@@ -18,13 +19,14 @@ import { Workspace, type MovementRow } from "../workspace";
18
19
  */
19
20
  export function MovementsView() {
20
21
  return (
21
- <RequireAuth title="Inventory" description="Your team shares one stock ledger. Anyone with an account sees it.">
22
+ <RequireAuth>
22
23
  <Workspace pathname="/movements">
23
24
  {(data) => {
24
25
  const columns: ColumnDef<MovementRow>[] = [
25
26
  {
26
27
  key: "when",
27
28
  header: "When",
29
+ className: "w-[84px] md:w-[100px]",
28
30
  cell: (row) => (
29
31
  <span className="text-muted-foreground">{relativeTime(row.createdAt)}</span>
30
32
  ),
@@ -32,15 +34,23 @@ export function MovementsView() {
32
34
  {
33
35
  key: "product",
34
36
  header: "Product",
37
+ className: "md:w-[30%]",
35
38
  cell: (row) => (
36
- <span className="truncate font-medium">
37
- {data.productName(row.productId) ?? "—"}
39
+ <span className="min-w-0">
40
+ <span className="block truncate font-medium">
41
+ {data.productName(row.productId) ?? "—"}
42
+ </span>
43
+ <span className="block truncate text-[12px] text-muted-foreground md:hidden">
44
+ {reasonById(row.reason)?.label ?? row.reason}
45
+ </span>
38
46
  </span>
39
47
  ),
40
48
  },
41
49
  {
42
50
  key: "reason",
43
51
  header: "Reason",
52
+ hideBelow: "md",
53
+ className: "w-[130px]",
44
54
  cell: (row) => (
45
55
  <span className="text-muted-foreground">
46
56
  {reasonById(row.reason)?.label ?? row.reason}
@@ -51,6 +61,7 @@ export function MovementsView() {
51
61
  key: "delta",
52
62
  header: "Change",
53
63
  numeric: true,
64
+ className: "w-[80px] md:w-[90px]",
54
65
  cell: (row) => (
55
66
  <span
56
67
  className={
@@ -64,6 +75,7 @@ export function MovementsView() {
64
75
  {
65
76
  key: "note",
66
77
  header: "Note",
78
+ hideBelow: "lg",
67
79
  cell: (row) => (
68
80
  <span className="text-muted-foreground">{row.note ?? "—"}</span>
69
81
  ),
@@ -71,11 +83,19 @@ export function MovementsView() {
71
83
  {
72
84
  key: "who",
73
85
  header: "By",
74
- cell: (row) => (
75
- <span className="text-muted-foreground">
76
- {data.actorName(row.actorId) ?? "—"}
77
- </span>
78
- ),
86
+ hideBelow: "sm",
87
+ className: "w-[170px]",
88
+ cell: (row) => {
89
+ const name = data.actorName(row.actorId);
90
+ return name ? (
91
+ <span className="flex min-w-0 items-center gap-2">
92
+ <Avatar name={name} size="sm" />
93
+ <span className="truncate text-muted-foreground">{name}</span>
94
+ </span>
95
+ ) : (
96
+ <span className="text-muted-foreground">—</span>
97
+ );
98
+ },
79
99
  },
80
100
  ];
81
101
 
@@ -85,14 +105,16 @@ export function MovementsView() {
85
105
 
86
106
  return (
87
107
  <>
88
- <PageHeader title="Movements" count={rows.length} />
108
+ <PageHeader title="Movements" count={data.navCounts["/movements"]} />
89
109
  <DataTable
90
110
  rows={rows}
91
111
  columns={columns}
112
+ loading={data.loading}
113
+ label="Stock movements"
92
114
  empty={
93
115
  <EmptyState
94
116
  icon={<ArrowLeftRight />}
95
- title={data.loading ? "Loading…" : "No movements yet"}
117
+ title="No movements yet"
96
118
  description="Every change to stock is recorded here, permanently. On-hand is the sum of these rows."
97
119
  />
98
120
  }
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import type { Metadata, PageProps } from "@pylonsync/react";
3
+ import { BRAND } from "@/lib/brand";
3
4
  import { MovementsView } from "./movements-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Movements",
7
+ title: `Movements · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
@@ -1,20 +1,18 @@
1
1
  import React from "react";
2
2
  import type { Metadata, PageProps } from "@pylonsync/react";
3
+ import { BRAND } from "@/lib/brand";
3
4
  import { ProductsView } from "./products-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Products",
7
+ title: `Products · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
10
- /**
11
- * `/` — the stock list.
12
- *
13
- */
11
+ /** `/` — the stock list. */
14
12
  export default function ProductsPage({ searchParams }: PageProps) {
15
13
  return (
16
14
  <ProductsView
17
- openNew={searchParams?.new === "product"}
15
+ openNew={searchParams?.new === "product"}
18
16
  initialFilter={searchParams?.filter}
19
17
  />
20
18
  );
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import type { Metadata, PageProps } from "@pylonsync/react";
3
+ import { BRAND } from "@/lib/brand";
3
4
  import { ProductView } from "./product-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Product",
7
+ title: `Product · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
@@ -2,11 +2,13 @@
2
2
 
3
3
  import React, { useState } from "react";
4
4
  import { Link, callFn, useRouter } from "@pylonsync/react";
5
- import { ArrowLeft, ArrowLeftRight } from "lucide-react";
5
+ import { ArrowLeftRight, ChevronRight } from "lucide-react";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import { PageHeader } from "@/components/page-header";
8
8
  import { EmptyState } from "@/components/empty-state";
9
9
  import { StockLevel } from "@/components/stock-level";
10
+ import { CategoryTile } from "@/components/category-tile";
11
+ import { Avatar } from "@/components/avatar";
10
12
  import { MovementDialog } from "@/components/movement-dialog";
11
13
  import { relativeTime } from "@/lib/format";
12
14
  import { money, reasonById, signedQuantity } from "@/lib/stock";
@@ -21,7 +23,7 @@ export function ProductView({
21
23
  const [moveOpen, setMoveOpen] = useState(false);
22
24
 
23
25
  return (
24
- <RequireAuth title="Inventory" description="Your team shares one stock ledger. Anyone with an account sees it.">
26
+ <RequireAuth>
25
27
  <Workspace pathname="/">
26
28
  {(data) => {
27
29
  const product = data.products.find((p) => p.id === productId);
@@ -31,7 +33,7 @@ export function ProductView({
31
33
  <>
32
34
  <PageHeader title="Product" />
33
35
  <EmptyState
34
- title={data.loading ? "Loading…" : "Product not found"}
36
+ title={data.loading ? "Loading product…" : "Product not found"}
35
37
  description={
36
38
  data.loading ? undefined : "It may have been deleted, or the link is wrong."
37
39
  }
@@ -63,31 +65,48 @@ export function ProductView({
63
65
 
64
66
  return (
65
67
  <>
66
- <PageHeader title={product.name}>
68
+ <PageHeader
69
+ title={product.name}
70
+ leading={
71
+ <Link
72
+ href="/"
73
+ className="hidden items-center gap-1 text-[13px] text-muted-foreground transition-colors hover:text-foreground md:inline-flex"
74
+ >
75
+ Products
76
+ <ChevronRight className="size-3.5" />
77
+ </Link>
78
+ }
79
+ >
67
80
  <Button size="sm" onClick={() => setMoveOpen(true)}>
68
81
  <ArrowLeftRight />
69
- Record movement
82
+ <span className="max-sm:sr-only">Record movement</span>
70
83
  </Button>
71
84
  </PageHeader>
72
85
 
73
86
  <div className="min-h-0 flex-1 overflow-y-auto">
74
- <div className="mx-auto flex max-w-3xl flex-col gap-6 px-6 py-6">
75
- <Link
76
- href="/"
77
- className="inline-flex w-fit items-center gap-1.5 text-[12px] text-muted-foreground transition-colors hover:text-foreground"
78
- >
79
- <ArrowLeft className="size-3.5" />
80
- Products
81
- </Link>
87
+ <div className="mx-auto flex max-w-3xl flex-col gap-6 px-4 py-5 md:px-8 md:py-8">
88
+ <div className="flex items-center gap-3.5">
89
+ <CategoryTile category={product.category} className="size-11 rounded-lg [&_svg]:size-5" />
90
+ <div className="min-w-0">
91
+ <h2 className="truncate text-[20px] font-semibold tracking-[-0.015em] md:text-[22px]">
92
+ {product.name}
93
+ </h2>
94
+ <p className="mt-0.5 text-[12.5px] text-muted-foreground">
95
+ <span className="font-mono">{product.sku}</span>
96
+ {product.category ? ` · ${product.category}` : ""}
97
+ </p>
98
+ </div>
99
+ </div>
82
100
 
83
- <dl className="grid grid-cols-2 gap-x-6 gap-y-4 rounded-lg border border-border bg-card p-4 sm:grid-cols-4">
84
- <Field label="SKU">
85
- <span className="tabular">{product.sku}</span>
101
+ <dl className="grid grid-cols-2 gap-x-6 gap-y-4 rounded-xl border border-border bg-surface-1 p-4 sm:grid-cols-4">
102
+ <Field label="Reorder at">
103
+ <span className="tabular">{product.reorderPoint || "—"}</span>
86
104
  </Field>
87
105
  <Field label="On hand">
88
106
  <StockLevel
89
107
  quantity={quantity}
90
108
  reorderPoint={product.reorderPoint}
109
+ className="justify-start [&>span:last-child]:min-w-0 [&>span:last-child]:text-left"
91
110
  />
92
111
  </Field>
93
112
  <Field label="Unit cost">{money(product.unitCostCents)}</Field>
@@ -99,19 +118,19 @@ export function ProductView({
99
118
  <section>
100
119
  <h2 className="mb-2 text-[13px] font-semibold">History</h2>
101
120
  <p className="mb-3 text-[12px] text-muted-foreground">
102
- On hand is the sum of these movements. Nothing here is ever
103
- edited — a correction is another movement.
121
+ On hand is the sum of these movements. Movements are never
122
+ edited; a correction is another movement.
104
123
  </p>
105
124
  {withBalance.length === 0 ? (
106
125
  <p className="py-6 text-center text-[12px] text-muted-foreground">
107
126
  No movements yet.
108
127
  </p>
109
128
  ) : (
110
- <ol className="space-y-1.5">
129
+ <ol className="divide-y divide-border/70 overflow-hidden rounded-xl border border-border">
111
130
  {withBalance.map(({ movement, after }) => (
112
131
  <li
113
132
  key={movement.id}
114
- className="flex items-center gap-3 rounded-md border border-border bg-card px-3 py-2 text-[12px]"
133
+ className="flex h-11 items-center gap-3 bg-card px-3.5 text-[13px]"
115
134
  >
116
135
  <span
117
136
  className={
@@ -125,15 +144,26 @@ export function ProductView({
125
144
  {reasonById(movement.reason)?.label ?? movement.reason}
126
145
  </span>
127
146
  {movement.note ? (
128
- <span className="truncate text-muted-foreground">
147
+ <span className="hidden truncate text-muted-foreground sm:inline">
129
148
  {movement.note}
130
149
  </span>
131
150
  ) : null}
132
- <span className="tabular ml-auto shrink-0 text-muted-foreground">
151
+ <span className="ml-auto hidden shrink-0 items-center gap-1.5 text-[12.5px] text-muted-foreground md:flex">
152
+ {data.actorName(movement.actorId) ? (
153
+ <>
154
+ <Avatar name={data.actorName(movement.actorId)} size="sm" />
155
+ {data.actorName(movement.actorId)}
156
+ </>
157
+ ) : null}
158
+ </span>
159
+ <span
160
+ className="tabular ml-auto w-14 shrink-0 text-right text-muted-foreground md:ml-0"
161
+ title="On hand after this movement"
162
+ >
133
163
  → {after}
134
164
  </span>
135
165
  <time
136
- className="w-20 shrink-0 text-right text-muted-foreground"
166
+ className="w-16 shrink-0 text-right text-muted-foreground"
137
167
  dateTime={movement.createdAt ?? undefined}
138
168
  >
139
169
  {relativeTime(movement.createdAt)}
@@ -172,8 +202,8 @@ export function ProductView({
172
202
  function Field({ label, children }: { label: string; children: React.ReactNode }) {
173
203
  return (
174
204
  <div className="min-w-0">
175
- <dt className="text-[11px] text-muted-foreground">{label}</dt>
176
- <dd className="mt-1 truncate text-[13px]">{children}</dd>
205
+ <dt className="text-[11.5px] font-medium text-muted-foreground">{label}</dt>
206
+ <dd className="mt-1 truncate text-[14px] font-medium">{children}</dd>
177
207
  </div>
178
208
  );
179
209
  }
@@ -12,7 +12,17 @@ import { RecordDialog } from "@/components/record-dialog";
12
12
  import { SummaryBar } from "@/components/summary-bar";
13
13
  import { StockLevel } from "@/components/stock-level";
14
14
  import { MovementDialog } from "@/components/movement-dialog";
15
- import { money, parseAmount, parseCount, stockState } from "@/lib/stock";
15
+ import { FilterTabs } from "@/components/filter-tabs";
16
+ import { CategoryTile } from "@/components/category-tile";
17
+ import {
18
+ STOCK_TABS,
19
+ inStockTab,
20
+ money,
21
+ parseAmount,
22
+ parseCount,
23
+ stockState,
24
+ stockTabCounts,
25
+ } from "@/lib/stock";
16
26
  import { RequireAuth } from "@/components/require-auth";
17
27
  import { Workspace, type ProductRow } from "./workspace";
18
28
 
@@ -26,7 +36,13 @@ export function ProductsView({
26
36
  const router = useRouter();
27
37
  const [newOpen, setNewOpen] = useState(Boolean(openNew));
28
38
  const [moveOpen, setMoveOpen] = useState(false);
29
- const [reorderOnly, setReorderOnly] = useState(initialFilter === "reorder");
39
+ const [tab, setTab] = useState(initialFilter === "reorder" ? "reorder" : "all");
40
+
41
+ // ⌘K "Needs reorder" navigates here with ?filter=reorder while the list may
42
+ // already be mounted.
43
+ useEffect(() => {
44
+ if (initialFilter === "reorder") setTab("reorder");
45
+ }, [initialFilter]);
30
46
 
31
47
  // "m" records a movement — the thing you do twenty times a day. Ignored
32
48
  // while typing.
@@ -52,25 +68,39 @@ export function ProductsView({
52
68
  }, []);
53
69
 
54
70
  return (
55
- <RequireAuth title="Inventory" description="Your team shares one stock ledger. Anyone with an account sees it.">
71
+ <RequireAuth>
56
72
  <Workspace pathname="/">
57
73
  {(data) => {
58
74
  const level = (id: string) => data.levels.get(id) ?? 0;
59
75
 
60
76
  const columns: ColumnDef<ProductRow>[] = [
61
- {
62
- key: "sku",
63
- header: "SKU",
64
- cell: (row) => <span className="tabular text-muted-foreground">{row.sku}</span>,
65
- },
66
77
  {
67
78
  key: "name",
68
79
  header: "Product",
69
- cell: (row) => <span className="truncate font-medium">{row.name}</span>,
80
+ className: "w-[60%] md:w-[34%]",
81
+ cell: (row) => (
82
+ <span className="flex min-w-0 items-center gap-3">
83
+ <CategoryTile category={row.category} />
84
+ <span className="min-w-0">
85
+ <span className="block truncate font-medium">{row.name}</span>
86
+ <span className="block truncate font-mono text-[11.5px] text-muted-foreground md:hidden">
87
+ {row.sku}
88
+ </span>
89
+ </span>
90
+ </span>
91
+ ),
92
+ },
93
+ {
94
+ key: "sku",
95
+ header: "SKU",
96
+ hideBelow: "md",
97
+ className: "w-[110px]",
98
+ cell: (row) => <span className="font-mono text-[12px] text-muted-foreground">{row.sku}</span>,
70
99
  },
71
100
  {
72
101
  key: "category",
73
102
  header: "Category",
103
+ hideBelow: "lg",
74
104
  cell: (row) => (
75
105
  <span className="text-muted-foreground">{row.category ?? "—"}</span>
76
106
  ),
@@ -79,6 +109,7 @@ export function ProductsView({
79
109
  key: "onhand",
80
110
  header: "On hand",
81
111
  numeric: true,
112
+ className: "w-[120px] md:w-[170px]",
82
113
  cell: (row) => (
83
114
  <StockLevel quantity={level(row.id)} reorderPoint={row.reorderPoint} />
84
115
  ),
@@ -87,6 +118,8 @@ export function ProductsView({
87
118
  key: "reorder",
88
119
  header: "Reorder at",
89
120
  numeric: true,
121
+ hideBelow: "lg",
122
+ className: "w-[100px]",
90
123
  cell: (row) => (
91
124
  <span className="text-muted-foreground">{row.reorderPoint || "—"}</span>
92
125
  ),
@@ -95,69 +128,77 @@ export function ProductsView({
95
128
  key: "value",
96
129
  header: "Value",
97
130
  numeric: true,
131
+ hideBelow: "sm",
132
+ className: "w-[110px]",
98
133
  cell: (row) =>
99
134
  money(Math.max(0, level(row.id)) * (Number(row.unitCostCents) || 0)),
100
135
  },
101
136
  ];
102
137
 
138
+ const tabCounts = stockTabCounts(data.summary);
103
139
  const rows = data.products
104
140
  .filter((product) => !product.archived)
105
141
  .filter((product) =>
106
- reorderOnly
107
- ? stockState(level(product.id), product.reorderPoint) !== "ok"
108
- : true,
142
+ inStockTab(tab, stockState(level(product.id), product.reorderPoint)),
109
143
  )
110
144
  .sort((a, b) => a.sku.localeCompare(b.sku));
111
145
 
112
146
  return (
113
147
  <>
114
- <PageHeader title="Products" count={rows.length}>
148
+ <PageHeader title="Products" count={data.navCounts["/"]}>
115
149
  <Button
116
150
  size="sm"
117
- variant={reorderOnly ? "default" : "secondary"}
118
- onClick={() => setReorderOnly((on) => !on)}
151
+ variant="outline"
152
+ onClick={() => setMoveOpen(true)}
153
+ title="Record a movement (m)"
119
154
  >
120
- Needs reorder
121
- </Button>
122
- <Button size="sm" variant="secondary" onClick={() => setMoveOpen(true)}>
123
155
  <ArrowLeftRight />
124
- Movement
125
- <Kbd className="ml-1">m</Kbd>
156
+ <span className="max-sm:sr-only">Movement</span>
157
+ <Kbd className="ml-0.5 hidden md:inline-flex">M</Kbd>
126
158
  </Button>
127
- <Button size="sm" onClick={() => setNewOpen(true)}>
159
+ <Button size="sm" onClick={() => setNewOpen(true)} title="New product (c)">
128
160
  <Plus />
129
- New product
161
+ <span className="max-sm:sr-only">New product</span>
130
162
  </Button>
131
163
  </PageHeader>
132
164
 
133
- <SummaryBar products={data.products} movements={data.movements} />
165
+ <SummaryBar summary={data.summary} />
166
+
167
+ <FilterTabs
168
+ label="Stock level"
169
+ value={tab}
170
+ onChange={setTab}
171
+ tabs={STOCK_TABS.map((t) => ({ id: t.id, label: t.label, count: tabCounts[t.id] }))}
172
+ />
134
173
 
135
174
  <DataTable
136
175
  rows={rows}
137
176
  columns={columns}
177
+ loading={data.loading}
178
+ label="Products"
138
179
  onRowClick={(row) => router.push(`/products/${row.id}`)}
139
180
  empty={
140
181
  <EmptyState
141
182
  icon={<Boxes />}
142
183
  title={
143
- data.loading
144
- ? "Loading…"
145
- : reorderOnly
146
- ? "Nothing needs reordering"
147
- : "No products yet"
184
+ tab === "all"
185
+ ? "No products yet"
186
+ : tab === "out"
187
+ ? "Nothing is out of stock"
188
+ : "Nothing needs reordering"
148
189
  }
149
190
  description={
150
- reorderOnly
151
- ? "Every line is above its reorder point."
152
- : "Add a product, then record what you receive. On-hand is the sum of those movements — there is no quantity to keep in sync."
191
+ tab === "all"
192
+ ? "Add a product, then record what you receive. On-hand is the sum of those movements, so there is no quantity to keep in sync."
193
+ : "Every active product is above its reorder point."
153
194
  }
154
195
  action={
155
- reorderOnly ? undefined : (
196
+ tab === "all" ? (
156
197
  <Button size="sm" onClick={() => setNewOpen(true)}>
157
198
  <Plus />
158
199
  New product
159
200
  </Button>
160
- )
201
+ ) : undefined
161
202
  }
162
203
  />
163
204
  }
@@ -3,10 +3,17 @@
3
3
  import React, { useEffect, useMemo, useRef, useState } from "react";
4
4
  import { callFn, db, useRouter } from "@pylonsync/react";
5
5
  import { useAuth } from "@pylonsync/client";
6
- import { Sidebar } from "@/components/sidebar";
6
+ import { Package } from "lucide-react";
7
+ import { AppShell } from "@/components/app-shell";
7
8
  import { CommandPalette } from "@/components/command-palette";
8
9
  import type { SearchItem } from "@/lib/search";
9
- import { onHandByProduct, summarize, type Movement, type Product } from "@/lib/stock";
10
+ import {
11
+ onHandByProduct,
12
+ summarize,
13
+ type Movement,
14
+ type Product,
15
+ type Summary,
16
+ } from "@/lib/stock";
10
17
 
11
18
  export type ProductRow = Product;
12
19
  export type MovementRow = Movement;
@@ -23,6 +30,13 @@ export interface WorkspaceData {
23
30
  levels: Map<string, number>;
24
31
  productName: (id: string | null | undefined) => string | null;
25
32
  actorName: (id: string | null | undefined) => string | null;
33
+ /** Totals from the ledger: SKUs, units, value, low and out counts. */
34
+ summary: Summary;
35
+ /**
36
+ * The number beside each nav link, keyed by href. Page headers and tabs
37
+ * read the same summary, so the sidebar and the page can't disagree.
38
+ */
39
+ navCounts: Record<string, number>;
26
40
  loading: boolean;
27
41
  }
28
42
 
@@ -56,8 +70,7 @@ export function Workspace({
56
70
 
57
71
  // Whoever is signed in, named from the synced User row rather than an
58
72
  // SSR prop — see the note on useAuth above.
59
- const signedInEmail =
60
- (users ?? []).find((user) => user.id === userId)?.email ?? "";
73
+ const me = (users ?? []).find((user) => user.id === userId);
61
74
 
62
75
  const [paletteOpen, setPaletteOpen] = useState(false);
63
76
  const seeded = useRef(false);
@@ -113,7 +126,7 @@ export function Workspace({
113
126
  () =>
114
127
  productList.map((product) => ({
115
128
  id: `product:${product.id}`,
116
- type: "company" as const,
129
+ type: "product",
117
130
  title: product.name,
118
131
  subtitle: product.sku,
119
132
  href: `/products/${product.id}`,
@@ -123,6 +136,7 @@ export function Workspace({
123
136
  );
124
137
 
125
138
  const summary = summarize(productList, movementList);
139
+ const navCounts = { "/": summary.skuCount, "/movements": movementList.length };
126
140
 
127
141
  const data: WorkspaceData = {
128
142
  products: productList,
@@ -133,20 +147,22 @@ export function Workspace({
133
147
  const user = userById.get(id ?? "");
134
148
  return user?.displayName || user?.email || null;
135
149
  },
150
+ summary,
151
+ navCounts,
136
152
  loading,
137
153
  };
138
154
 
139
155
  return (
140
- <div className="flex h-screen overflow-hidden">
141
- <Sidebar
142
- workspace="Inventory"
143
- email={signedInEmail}
144
- pathname={pathname}
145
- counts={{ "/": summary.skuCount }}
146
- onOpenCommand={() => setPaletteOpen(true)}
147
- onSignOut={() => void signOut()}
148
- />
149
- <main className="flex min-w-0 flex-1 flex-col">{children(data)}</main>
156
+ <AppShell
157
+ sidebar={{
158
+ user: { name: me?.displayName ?? "", email: me?.email ?? "" },
159
+ pathname,
160
+ counts: navCounts,
161
+ onOpenCommand: () => setPaletteOpen(true),
162
+ onSignOut: () => void signOut(),
163
+ }}
164
+ >
165
+ {children(data)}
150
166
 
151
167
  <CommandPalette
152
168
  open={paletteOpen}
@@ -168,9 +184,11 @@ export function Workspace({
168
184
  run: () => router.push("/movements"),
169
185
  },
170
186
  ]}
187
+ placeholder="Search products by name, SKU, or category…"
188
+ icons={{ product: <Package /> }}
171
189
  onClose={() => setPaletteOpen(false)}
172
190
  onSelect={(item) => router.push(item.href)}
173
191
  />
174
- </div>
192
+ </AppShell>
175
193
  );
176
194
  }
@@ -10,7 +10,7 @@ import {
10
10
  } from "@pylonsync/sdk";
11
11
 
12
12
  // ---------------------------------------------------------------------------
13
- // inventory — stock control for a small business: products, an append-only
13
+ // Stockroom (the inventory template) — stock control for a small business: products, an append-only
14
14
  // ledger of movements, and the levels derived from it.
15
15
  //
16
16
  // THE CENTRAL DECISION: there is no `quantity` column. On-hand is the SUM of a
@@ -144,13 +144,21 @@ const manifest = buildManifest({
144
144
  auth: auth(),
145
145
  fonts: [
146
146
  font({
147
- family: "Inter",
147
+ family: "IBM Plex Sans",
148
148
  variable: "--font-sans",
149
149
  weights: ["400", "500", "600", "700"],
150
150
  subsets: ["latin"],
151
151
  display: "swap",
152
152
  preload: true,
153
153
  }),
154
+ // SKUs and quantities in the ledger read best in a fixed-width face.
155
+ font({
156
+ family: "IBM Plex Mono",
157
+ variable: "--font-mono",
158
+ weights: ["400", "500"],
159
+ subsets: ["latin"],
160
+ display: "swap",
161
+ }),
154
162
  ],
155
163
  routes: await discoverAppRoutes(),
156
164
  });