@pylonsync/create-pylon 0.19.1 → 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
@@ -10,9 +10,14 @@ import { DataTable, type ColumnDef } from "@/components/data-table";
10
10
  import { EmptyState } from "@/components/empty-state";
11
11
  import { SummaryBar } from "@/components/summary-bar";
12
12
  import { StatusBadge } from "@/components/status-badge";
13
+ import { FilterTabs } from "@/components/filter-tabs";
14
+ import { Avatar } from "@/components/avatar";
13
15
  import {
16
+ INVOICE_TABS,
14
17
  daysOverdue,
15
18
  displayStatus,
19
+ inInvoiceTab,
20
+ invoiceTabCounts,
16
21
  money,
17
22
  totals,
18
23
  } from "@/lib/billing";
@@ -26,6 +31,7 @@ export function InvoicesView({
26
31
  }) {
27
32
  const router = useRouter();
28
33
  const [creating, setCreating] = useState(false);
34
+ const [tab, setTab] = useState("all");
29
35
 
30
36
  async function create() {
31
37
  if (creating) return;
@@ -66,42 +72,65 @@ export function InvoicesView({
66
72
  }, [creating]);
67
73
 
68
74
  return (
69
- <RequireAuth title="Invoices" description="Your team shares one set of books. Anyone with an account sees it.">
75
+ <RequireAuth>
70
76
  <Workspace pathname="/">
71
77
  {(data) => {
78
+ const now = Date.now();
79
+ const view = new Map(
80
+ data.invoices.map((invoice) => {
81
+ const t = totals(invoice, data.items, data.payments);
82
+ return [invoice.id, { t, status: displayStatus(invoice, t.balanceCents, now) }] as const;
83
+ }),
84
+ );
85
+ const info = (row: InvoiceRow) => view.get(row.id)!;
86
+
72
87
  const columns: ColumnDef<InvoiceRow>[] = [
73
88
  {
74
89
  key: "number",
75
90
  header: "Invoice",
76
- cell: (row) => <span className="tabular font-medium">{row.number}</span>,
91
+ className: "w-[46%] md:w-[130px]",
92
+ cell: (row) => (
93
+ <span className="min-w-0">
94
+ <span className="tabular block truncate font-medium">{row.number}</span>
95
+ <span className="block truncate text-[12px] text-muted-foreground md:hidden">
96
+ {data.clientName(row.clientId) ?? "No client"}
97
+ </span>
98
+ </span>
99
+ ),
77
100
  },
78
101
  {
79
102
  key: "client",
80
103
  header: "Client",
81
- cell: (row) => (
82
- <span className="text-muted-foreground">
83
- {data.clientName(row.clientId) ?? "—"}
84
- </span>
85
- ),
104
+ hideBelow: "md",
105
+ cell: (row) => {
106
+ const name = data.clientName(row.clientId);
107
+ return name ? (
108
+ <span className="flex min-w-0 items-center gap-2.5">
109
+ <Avatar name={name} size="sm" shape="square" />
110
+ <span className="truncate">{name}</span>
111
+ </span>
112
+ ) : (
113
+ <span className="text-muted-foreground">—</span>
114
+ );
115
+ },
86
116
  },
87
117
  {
88
118
  key: "status",
89
119
  header: "Status",
90
- cell: (row) => {
91
- const t = totals(row, data.items, data.payments);
92
- return <StatusBadge status={displayStatus(row, t.balanceCents)} />;
93
- },
120
+ className: "w-[104px] md:w-[120px]",
121
+ cell: (row) => <StatusBadge status={info(row).status} />,
94
122
  },
95
123
  {
96
124
  key: "due",
97
125
  header: "Due",
126
+ hideBelow: "lg",
127
+ className: "w-[120px]",
98
128
  cell: (row) => {
99
- const t = totals(row, data.items, data.payments);
100
- const late = displayStatus(row, t.balanceCents) === "overdue";
101
- const days = daysOverdue(row);
129
+ const late = info(row).status === "overdue";
130
+ const days = daysOverdue(row, now);
102
131
  if (!row.dueDate) return <span className="text-muted-foreground">—</span>;
103
132
  return (
104
- <span className={late ? "text-destructive" : "text-muted-foreground"}>
133
+ <span className={late ? "font-medium text-destructive" : "text-muted-foreground"}>
105
134
  {late && days !== null
106
135
  ? `${days}d overdue`
107
136
  : new Date(row.dueDate).toLocaleDateString("en-US", {
@@ -116,37 +145,41 @@ export function InvoicesView({
116
145
  key: "total",
117
146
  header: "Total",
118
147
  numeric: true,
119
- cell: (row) => money(totals(row, data.items, data.payments).totalCents),
148
+ hideBelow: "sm",
149
+ className: "w-[120px]",
150
+ cell: (row) => money(info(row).t.totalCents),
120
151
  },
121
152
  {
122
153
  key: "balance",
123
154
  header: "Balance",
124
155
  numeric: true,
156
+ className: "w-[104px] md:w-[120px]",
125
157
  cell: (row) => {
126
- const t = totals(row, data.items, data.payments);
158
+ const balance = info(row).t.balanceCents;
127
159
  return (
128
- <span className={t.balanceCents > 0 ? "" : "text-muted-foreground"}>
129
- {money(t.balanceCents)}
160
+ <span className={balance > 0 ? "font-medium" : "text-muted-foreground"}>
161
+ {money(balance)}
130
162
  </span>
131
163
  );
132
164
  },
133
165
  },
134
166
  ];
135
167
 
168
+ const tabCounts = invoiceTabCounts(data.invoices, data.items, data.payments, now);
136
169
  // Newest first — an invoice list is read from the top, and the number
137
170
  // series already encodes the order.
138
- const rows = [...data.invoices].sort((a, b) =>
139
- b.number.localeCompare(a.number),
140
- );
171
+ const rows = data.invoices
172
+ .filter((invoice) => inInvoiceTab(tab, view.get(invoice.id)!.status))
173
+ .sort((a, b) => b.number.localeCompare(a.number));
141
174
 
142
175
  return (
143
176
  <>
144
- <PageHeader title="Invoices" count={rows.length}>
145
- <Button size="sm" onClick={() => void create()} disabled={creating}>
177
+ <PageHeader title="Invoices" count={data.navCounts["/"]}>
178
+ <Button size="sm" onClick={() => void create()} disabled={creating} title="New invoice (c)">
146
179
  <Plus />
147
180
  {creating ? "Creating…" : "New invoice"}
148
- <Kbd className="ml-1 border-primary-foreground/25 bg-primary-foreground/10 text-primary-foreground/70">
149
- c
181
+ <Kbd className="ml-0.5 hidden border-primary-foreground/25 bg-primary-foreground/10 text-primary-foreground/75 md:inline-flex">
182
+ C
150
183
  </Kbd>
151
184
  </Button>
152
185
  </PageHeader>
@@ -155,22 +188,38 @@ export function InvoicesView({
155
188
  invoices={data.invoices}
156
189
  items={data.items}
157
190
  payments={data.payments}
191
+ now={now}
192
+ />
193
+
194
+ <FilterTabs
195
+ label="Invoice status"
196
+ value={tab}
197
+ onChange={setTab}
198
+ tabs={INVOICE_TABS.map((t) => ({ id: t.id, label: t.label, count: tabCounts[t.id] }))}
158
199
  />
159
200
 
160
201
  <DataTable
161
202
  rows={rows}
162
203
  columns={columns}
204
+ loading={data.loading}
205
+ label="Invoices"
163
206
  onRowClick={(row) => router.push(`/invoices/${row.id}`)}
164
207
  empty={
165
208
  <EmptyState
166
209
  icon={<FileText />}
167
- title={data.loading ? "Loading…" : "No invoices yet"}
168
- description="Create one, add the billable lines, then send it. Totals and ageing are derived — there's nothing to keep in sync."
210
+ title={tab === "all" ? "No invoices yet" : "No invoices with this status"}
211
+ description={
212
+ tab === "all"
213
+ ? "Create one, add the billable lines, then send it. Totals and ageing are derived from the lines and payments."
214
+ : "Pick another tab to see the rest of your invoices."
215
+ }
169
216
  action={
170
- <Button size="sm" onClick={() => void create()}>
171
- <Plus />
172
- New invoice
173
- </Button>
217
+ tab === "all" ? (
218
+ <Button size="sm" onClick={() => void create()}>
219
+ <Plus />
220
+ New invoice
221
+ </Button>
222
+ ) : undefined
174
223
  }
175
224
  />
176
225
  }
@@ -18,12 +18,14 @@ export default function RootLayout({ children }: LayoutProps) {
18
18
  <html lang="en">
19
19
  <head>
20
20
  <meta charSet="utf-8" />
21
- <meta name="viewport" content="width=device-width, initial-scale=1" />
22
- <title>__APP_NAME__</title>
21
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
22
+ {/* No <title> here: each page's `metadata` sets "<Page> · <Brand>".
23
+ A title in the layout renders first and wins over the page's. */}
23
24
  {/* Tailwind is compiled by Pylon from app/globals.css and the
24
- stylesheet link is injected here automatically. Inter is declared in
25
- app.ts (fonts: [...]) and self-hosted by the build — @font-face, the
26
- preload link, and a size-adjusted fallback are injected too. */}
25
+ stylesheet link is injected here automatically. The typeface is
26
+ declared in app.ts (fonts: [...]) and self-hosted by the build;
27
+ @font-face, the preload link, and a size-adjusted fallback are
28
+ injected too. */}
27
29
  </head>
28
30
  <body className="h-full">{children}</body>
29
31
  </html>
@@ -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 { InvoicesView } from "./invoices-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Invoices",
7
+ title: `Invoices · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
10
- /**
11
- * `/` — the invoice list.
12
- *
13
- */
11
+ /** `/` — the invoice list. */
14
12
  export default function InvoicesPage({ searchParams }: PageProps) {
15
13
  return (
16
14
  <InvoicesView
17
- openNew={searchParams?.new === "invoice"}
15
+ openNew={searchParams?.new === "invoice"}
18
16
  />
19
17
  );
20
18
  }
@@ -3,7 +3,8 @@
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 { FileText, User } 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
10
  import type { Invoice, LineItem, Payment } from "@/lib/billing";
@@ -31,6 +32,11 @@ export interface WorkspaceData {
31
32
  items: LineItemRow[];
32
33
  payments: PaymentRow[];
33
34
  clientName: (id: string | null | undefined) => string | null;
35
+ /**
36
+ * The number beside each nav link, keyed by href. Page headers read the
37
+ * same map, so the sidebar and the page can't disagree.
38
+ */
39
+ navCounts: Record<string, number>;
34
40
  loading: boolean;
35
41
  }
36
42
 
@@ -66,8 +72,7 @@ export function Workspace({
66
72
 
67
73
  // Whoever is signed in, named from the synced User row rather than an
68
74
  // SSR prop — see the note on useAuth above.
69
- const signedInEmail =
70
- (users ?? []).find((user) => user.id === userId)?.email ?? "";
75
+ const me = (users ?? []).find((user) => user.id === userId);
71
76
 
72
77
  const [paletteOpen, setPaletteOpen] = useState(false);
73
78
  const seeded = useRef(false);
@@ -119,14 +124,14 @@ export function Workspace({
119
124
  () => [
120
125
  ...invoiceList.map((invoice) => ({
121
126
  id: `invoice:${invoice.id}`,
122
- type: "deal" as const,
127
+ type: "invoice",
123
128
  title: invoice.number,
124
129
  subtitle: clientById.get(invoice.clientId ?? "")?.name,
125
130
  href: `/invoices/${invoice.id}`,
126
131
  })),
127
132
  ...clientList.map((client) => ({
128
133
  id: `client:${client.id}`,
129
- type: "company" as const,
134
+ type: "client",
130
135
  title: client.name,
131
136
  subtitle: client.email ?? undefined,
132
137
  href: "/clients",
@@ -135,26 +140,29 @@ export function Workspace({
135
140
  [invoiceList, clientList, clientById],
136
141
  );
137
142
 
143
+ const navCounts = { "/": invoiceList.length, "/clients": clientList.length };
144
+
138
145
  const data: WorkspaceData = {
139
146
  clients: clientList,
140
147
  invoices: invoiceList,
141
148
  items: items ?? [],
142
149
  payments: payments ?? [],
143
150
  clientName: (id) => clientById.get(id ?? "")?.name ?? null,
151
+ navCounts,
144
152
  loading,
145
153
  };
146
154
 
147
155
  return (
148
- <div className="flex h-screen overflow-hidden">
149
- <Sidebar
150
- workspace="Invoices"
151
- email={signedInEmail}
152
- pathname={pathname}
153
- counts={{ "/": invoiceList.length, "/clients": clientList.length }}
154
- onOpenCommand={() => setPaletteOpen(true)}
155
- onSignOut={() => void signOut()}
156
- />
157
- <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)}
158
166
 
159
167
  <CommandPalette
160
168
  open={paletteOpen}
@@ -171,9 +179,11 @@ export function Workspace({
171
179
  run: () => router.push("/clients"),
172
180
  },
173
181
  ]}
182
+ placeholder="Search invoices and clients…"
183
+ icons={{ invoice: <FileText />, client: <User /> }}
174
184
  onClose={() => setPaletteOpen(false)}
175
185
  onSelect={(item) => router.push(item.href)}
176
186
  />
177
- </div>
187
+ </AppShell>
178
188
  );
179
189
  }
@@ -10,7 +10,7 @@ import {
10
10
  } from "@pylonsync/sdk";
11
11
 
12
12
  // ---------------------------------------------------------------------------
13
- // invoices — billing for a small business: clients, invoices with line items,
13
+ // Billfold (the invoices template) — billing for a small business: clients, invoices with line items,
14
14
  // and payments against them.
15
15
  //
16
16
  // The realtime hook: recording a payment updates the balance and flips the
@@ -159,7 +159,7 @@ const manifest = buildManifest({
159
159
  auth: auth(),
160
160
  fonts: [
161
161
  font({
162
- family: "Inter",
162
+ family: "Hanken Grotesk",
163
163
  variable: "--font-sans",
164
164
  weights: ["400", "500", "600", "700"],
165
165
  subsets: ["latin"],
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import React, { createContext, useContext, useEffect, useState } from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { BRAND } from "@/lib/brand";
6
+ import { Sidebar } from "@/components/sidebar";
7
+
8
+ interface ShellControls {
9
+ /** Opens the navigation drawer. Only reachable on narrow screens. */
10
+ openNav: () => void;
11
+ openCommand: () => void;
12
+ }
13
+
14
+ const ShellContext = createContext<ShellControls | null>(null);
15
+
16
+ /** The shell's controls, or null outside a shell (a component test). */
17
+ export function useShell(): ShellControls | null {
18
+ return useContext(ShellContext);
19
+ }
20
+
21
+ type SidebarProps = React.ComponentProps<typeof Sidebar>;
22
+
23
+ /**
24
+ * The page frame. From the `md` breakpoint up, the sidebar is a fixed
25
+ * 240px rail and the main column scrolls on its own. Below it, the rail
26
+ * is hidden and the same sidebar opens as a drawer from the menu button
27
+ * in each page header, so a phone gets the full width for content.
28
+ */
29
+ export function AppShell({
30
+ sidebar,
31
+ children,
32
+ }: {
33
+ sidebar: Omit<SidebarProps, "onNavigate" | "className">;
34
+ children: React.ReactNode;
35
+ }) {
36
+ const [navOpen, setNavOpen] = useState(false);
37
+ const { pathname, onOpenCommand } = sidebar;
38
+
39
+ // A route change closes the drawer, including one made from ⌘K.
40
+ useEffect(() => {
41
+ setNavOpen(false);
42
+ }, [pathname]);
43
+
44
+ // Widening past md hides the drawer, so close it rather than leave an
45
+ // invisible modal holding focus.
46
+ useEffect(() => {
47
+ const wide = window.matchMedia("(min-width: 768px)");
48
+ const onChange = () => {
49
+ if (wide.matches) setNavOpen(false);
50
+ };
51
+ wide.addEventListener("change", onChange);
52
+ return () => wide.removeEventListener("change", onChange);
53
+ }, []);
54
+
55
+ return (
56
+ <ShellContext.Provider
57
+ value={{ openNav: () => setNavOpen(true), openCommand: onOpenCommand }}
58
+ >
59
+ <div className="flex h-dvh overflow-hidden bg-background">
60
+ <aside className="hidden w-[240px] shrink-0 border-r border-border md:flex">
61
+ <Sidebar {...sidebar} />
62
+ </aside>
63
+
64
+ <DialogPrimitive.Root open={navOpen} onOpenChange={setNavOpen}>
65
+ <DialogPrimitive.Portal>
66
+ <DialogPrimitive.Overlay className="fixed inset-0 z-40 bg-black/40 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 md:hidden" />
67
+ <DialogPrimitive.Content
68
+ className={
69
+ "fixed inset-y-0 left-0 z-40 flex w-[min(300px,86vw)] border-r border-border shadow-xl outline-none md:hidden " +
70
+ "duration-200 data-[state=open]:animate-in data-[state=open]:slide-in-from-left " +
71
+ "data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left"
72
+ }
73
+ >
74
+ <DialogPrimitive.Title className="sr-only">{BRAND.name} navigation</DialogPrimitive.Title>
75
+ <DialogPrimitive.Description className="sr-only">
76
+ Links to every section, search, and your account.
77
+ </DialogPrimitive.Description>
78
+ <Sidebar
79
+ {...sidebar}
80
+ onNavigate={() => setNavOpen(false)}
81
+ onOpenCommand={() => {
82
+ setNavOpen(false);
83
+ onOpenCommand();
84
+ }}
85
+ />
86
+ </DialogPrimitive.Content>
87
+ </DialogPrimitive.Portal>
88
+ </DialogPrimitive.Root>
89
+
90
+ <main className="flex min-w-0 flex-1 flex-col">{children}</main>
91
+ </div>
92
+ </ShellContext.Provider>
93
+ );
94
+ }
@@ -21,8 +21,16 @@ import { Label } from "@/components/ui/label";
21
21
  * SSR cookie, which browsers withhold inside the builder's cross-site preview
22
22
  * iframe — that's the sign-in loop this avoids.
23
23
  */
24
- export function AuthForm() {
25
- const [mode, setMode] = useState<"login" | "signup">("login");
24
+ export type AuthMode = "login" | "signup";
25
+
26
+ export function AuthForm({
27
+ mode,
28
+ onModeChange,
29
+ }: {
30
+ mode: AuthMode;
31
+ onModeChange: (mode: AuthMode) => void;
32
+ }) {
33
+ const [name, setName] = useState("");
26
34
  const [email, setEmail] = useState("");
27
35
  const [password, setPassword] = useState("");
28
36
  const [error, setError] = useState<string | null>(null);
@@ -36,7 +44,11 @@ export function AuthForm() {
36
44
  const session =
37
45
  mode === "login"
38
46
  ? await passwordLogin({ email, password })
39
- : await passwordRegister({ email, password });
47
+ : await passwordRegister({
48
+ email,
49
+ password,
50
+ ...(name.trim() ? { displayName: name.trim() } : {}),
51
+ });
40
52
  // No navigation: persistSession notifies the sync engine, RequireAuth
41
53
  // re-renders, and the app appears in place. A full page load here would
42
54
  // depend on the SSR session cookie, which a cross-site iframe withholds.
@@ -50,6 +62,18 @@ export function AuthForm() {
50
62
  return (
51
63
  <div className="space-y-5">
52
64
  <form onSubmit={onSubmit} className="space-y-3.5">
65
+ {mode === "signup" ? (
66
+ <div className="space-y-1.5">
67
+ <Label htmlFor="name">Name</Label>
68
+ <Input
69
+ id="name"
70
+ value={name}
71
+ onChange={(event) => setName(event.target.value)}
72
+ autoComplete="name"
73
+ placeholder="Jordan Lee"
74
+ />
75
+ </div>
76
+ ) : null}
53
77
  <div className="space-y-1.5">
54
78
  <Label htmlFor="email">Email</Label>
55
79
  <Input
@@ -80,7 +104,13 @@ export function AuthForm() {
80
104
  </p>
81
105
  ) : null}
82
106
  <Button type="submit" disabled={pending} className="w-full">
83
- {pending ? "…" : mode === "login" ? "Sign in" : "Create account"}
107
+ {pending
108
+ ? mode === "login"
109
+ ? "Signing in…"
110
+ : "Creating account…"
111
+ : mode === "login"
112
+ ? "Sign in"
113
+ : "Create account"}
84
114
  </Button>
85
115
  </form>
86
116
 
@@ -89,7 +119,7 @@ export function AuthForm() {
89
119
  <button
90
120
  type="button"
91
121
  onClick={() => {
92
- setMode(mode === "login" ? "signup" : "login");
122
+ onModeChange(mode === "login" ? "signup" : "login");
93
123
  setError(null);
94
124
  }}
95
125
  className="font-medium text-foreground underline underline-offset-2"
@@ -17,10 +17,13 @@ const ACCENTS = [
17
17
  export function Avatar({
18
18
  name,
19
19
  size = "md",
20
+ shape = "round",
20
21
  className,
21
22
  }: {
22
23
  name: string | null | undefined;
23
24
  size?: "sm" | "md";
25
+ /** Square for an organisation, round for a person. */
26
+ shape?: "round" | "square";
24
27
  className?: string;
25
28
  }) {
26
29
  const label = initials(name);
@@ -28,7 +31,8 @@ export function Avatar({
28
31
  <span
29
32
  aria-hidden="true"
30
33
  className={cn(
31
- "inline-flex shrink-0 select-none items-center justify-center rounded-full font-medium",
34
+ "inline-flex shrink-0 select-none items-center justify-center font-semibold",
35
+ shape === "square" ? "rounded-md" : "rounded-full",
32
36
  size === "sm" ? "size-5 text-[9px]" : "size-6 text-[10px]",
33
37
  ACCENTS[accentIndex(name ?? "?", ACCENTS.length)],
34
38
  className,
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ /**
5
+ * The Billfold mark: an invoice with a folded corner and two lines, on the
6
+ * accent square. app/icon.svg draws the same shape for the favicon.
7
+ */
8
+ export function BrandMark({ className }: { className?: string }) {
9
+ return (
10
+ <span
11
+ aria-hidden="true"
12
+ className={cn(
13
+ "inline-flex size-6 shrink-0 items-center justify-center rounded-[7px] bg-primary text-primary-foreground",
14
+ "shadow-[inset_0_1px_0_rgb(255_255_255/0.18),0_1px_2px_rgb(0_0_0/0.12)]",
15
+ className,
16
+ )}
17
+ >
18
+ <svg viewBox="0 0 16 16" className="size-[64%]" fill="none">
19
+ <path d="M3 2.5A1.5 1.5 0 0 1 4.5 1h5l3.5 3.5v9A1.5 1.5 0 0 1 11.5 15h-7A1.5 1.5 0 0 1 3 13.5v-11Z" fill="currentColor" />
20
+ <path d="M9.5 1v2.5a1 1 0 0 0 1 1H13" fill="var(--primary)" opacity="0.35" />
21
+ <rect x="5.2" y="7.4" width="5.6" height="1.3" rx="0.65" fill="var(--primary)" />
22
+ <rect x="5.2" y="10" width="3.6" height="1.3" rx="0.65" fill="var(--primary)" />
23
+ </svg>
24
+ </span>
25
+ );
26
+ }