@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
@@ -8,16 +8,23 @@ import { PageHeader } from "@/components/page-header";
8
8
  import { DataTable, type ColumnDef } from "@/components/data-table";
9
9
  import { EmptyState } from "@/components/empty-state";
10
10
  import { RecordDialog } from "@/components/record-dialog";
11
+ import { FilterTabs } from "@/components/filter-tabs";
12
+ import { Meter } from "@/components/meter";
11
13
  import {
14
+ DEFAULT_PROJECT_TAB,
15
+ PROJECT_STATUSES,
16
+ PROJECT_TABS,
12
17
  budgetState,
13
18
  duration,
14
- isOpen,
19
+ inProjectTab,
15
20
  minutesForProject,
16
21
  parseDuration,
22
+ portfolio,
17
23
  progress,
18
24
  } from "@/lib/work";
25
+ import { SummaryBar } from "@/components/summary-bar";
19
26
  import { RequireAuth } from "@/components/require-auth";
20
- import { Workspace, type ProjectRow } from "./workspace";
27
+ import { Workspace, type ProjectRow, type TaskRow } from "./workspace";
21
28
 
22
29
  export function ProjectsView({
23
30
  openNew,
@@ -26,20 +33,47 @@ export function ProjectsView({
26
33
  }) {
27
34
  const router = useRouter();
28
35
  const [open, setOpen] = useState(Boolean(openNew));
36
+ const [tab, setTab] = useState(DEFAULT_PROJECT_TAB);
29
37
 
30
38
  return (
31
- <RequireAuth title="Projects" description="Your team shares one set of projects. Anyone with an account sees it.">
39
+ <RequireAuth>
32
40
  <Workspace pathname="/">
33
41
  {(data) => {
42
+ const tasksByProject = new Map<string, TaskRow[]>();
43
+ for (const task of data.tasks) {
44
+ const list = tasksByProject.get(task.projectId) ?? [];
45
+ list.push(task);
46
+ tasksByProject.set(task.projectId, list);
47
+ }
48
+ const logged = new Map(
49
+ data.projects.map((p) => [p.id, minutesForProject(p.id, data.entries)] as const),
50
+ );
51
+
34
52
  const columns: ColumnDef<ProjectRow>[] = [
35
53
  {
36
54
  key: "name",
37
55
  header: "Project",
38
- cell: (row) => <span className="truncate font-medium">{row.name}</span>,
56
+ className: "w-[58%] md:w-[28%]",
57
+ cell: (row) => (
58
+ <span className="min-w-0">
59
+ <span className="flex items-center gap-2">
60
+ <span className="truncate font-medium">{row.name}</span>
61
+ {row.status !== "active" && tab === "all" ? (
62
+ <span className="shrink-0 rounded bg-foreground/[0.06] px-1.5 text-[11px] font-medium text-muted-foreground">
63
+ {PROJECT_STATUSES.find((s) => s.id === row.status)?.label ?? row.status}
64
+ </span>
65
+ ) : null}
66
+ </span>
67
+ <span className="block truncate text-[12px] text-muted-foreground md:hidden">
68
+ {data.clientName(row.clientId) ?? "No client"}
69
+ </span>
70
+ </span>
71
+ ),
39
72
  },
40
73
  {
41
74
  key: "client",
42
75
  header: "Client",
76
+ hideBelow: "md",
43
77
  cell: (row) => (
44
78
  <span className="text-muted-foreground">
45
79
  {data.clientName(row.clientId) ?? "—"}
@@ -47,57 +81,53 @@ export function ProjectsView({
47
81
  ),
48
82
  },
49
83
  {
50
- key: "tasks",
51
- header: "Tasks",
84
+ key: "progress",
85
+ header: "Progress",
86
+ className: "w-[42%] md:w-[190px]",
52
87
  cell: (row) => {
53
- const tasks = data.tasks.filter((t) => t.projectId === row.id);
54
- const p = progress(tasks);
88
+ const p = progress(tasksByProject.get(row.id) ?? []);
55
89
  return (
56
- <span className="tabular text-muted-foreground">
57
- {p.done}/{p.total}
58
- </span>
90
+ <Meter
91
+ ratio={p.ratio ?? 0}
92
+ tone="primary"
93
+ label={`${p.done}/${p.total}`}
94
+ title={`${p.done} of ${p.total} tasks done`}
95
+ />
59
96
  );
60
97
  },
61
98
  },
62
- {
63
- key: "open",
64
- header: "Open",
65
- numeric: true,
66
- cell: (row) =>
67
- data.tasks.filter((t) => t.projectId === row.id && isOpen(t)).length,
68
- },
69
99
  {
70
100
  key: "time",
71
- header: "Logged",
72
- numeric: true,
101
+ header: "Budget used",
102
+ hideBelow: "lg",
103
+ className: "w-[220px]",
73
104
  cell: (row) => {
74
- const logged = minutesForProject(row.id, data.entries);
75
- const state = budgetState(row.budgetMinutes, logged);
76
- return (
77
- <span
78
- className={
79
- state === "over"
80
- ? "text-destructive"
81
- : state === "near"
82
- ? "text-stage-proposal"
83
- : ""
84
- }
105
+ const minutes = logged.get(row.id) ?? 0;
106
+ const state = budgetState(row.budgetMinutes, minutes);
107
+ const budget = Number(row.budgetMinutes) || 0;
108
+ return state === "none" ? (
109
+ <span className="text-muted-foreground">{duration(minutes)} logged</span>
110
+ ) : (
111
+ <Meter
112
+ ratio={minutes / budget}
113
+ tone={state === "over" ? "danger" : state === "near" ? "warn" : "neutral"}
114
+ label={`${duration(minutes)} / ${duration(budget)}`}
85
115
  title={
86
- row.budgetMinutes
87
- ? `Budget ${duration(row.budgetMinutes)}`
88
- : undefined
116
+ state === "over"
117
+ ? `Over budget by ${duration(minutes - budget)}`
118
+ : `${duration(budget - minutes)} left`
89
119
  }
90
- >
91
- {duration(logged)}
92
- </span>
120
+ />
93
121
  );
94
122
  },
95
123
  },
96
124
  {
97
125
  key: "due",
98
126
  header: "Due",
127
+ hideBelow: "sm",
128
+ className: "w-[90px]",
99
129
  cell: (row) => (
100
- <span className="text-muted-foreground">
130
+ <span className="tabular text-muted-foreground">
101
131
  {row.dueDate
102
132
  ? new Date(row.dueDate).toLocaleDateString("en-US", {
103
133
  month: "short",
@@ -109,36 +139,59 @@ export function ProjectsView({
109
139
  },
110
140
  ];
111
141
 
112
- // Active first, then by name — a finished project shouldn\'t sit above
113
- // the work in flight.
114
- const rows = [...data.projects].sort((a, b) => {
115
- const rank = (p: ProjectRow) => (p.status === "active" ? 0 : p.status === "paused" ? 1 : 2);
116
- return rank(a) - rank(b) || a.name.localeCompare(b.name);
117
- });
142
+ // Soonest due first, then by name. A project without a date sorts last.
143
+ const rows = data.projects
144
+ .filter((project) => inProjectTab(tab, project))
145
+ .sort(
146
+ (a, b) =>
147
+ (a.dueDate ?? "9999").localeCompare(b.dueDate ?? "9999") ||
148
+ a.name.localeCompare(b.name),
149
+ );
118
150
 
119
151
  return (
120
152
  <>
121
- <PageHeader title="Projects" count={rows.length}>
153
+ <PageHeader title="Projects" count={data.navCounts["/"]}>
122
154
  <Button size="sm" onClick={() => setOpen(true)}>
123
155
  <Plus />
124
- New project
156
+ <span className="max-sm:sr-only">New project</span>
125
157
  </Button>
126
158
  </PageHeader>
127
159
 
160
+ <SummaryBar portfolio={portfolio(data.projects, data.entries)} />
161
+
162
+ <FilterTabs
163
+ label="Project status"
164
+ value={tab}
165
+ onChange={setTab}
166
+ tabs={PROJECT_TABS.map((t) => ({
167
+ id: t.id,
168
+ label: t.label,
169
+ count: data.projectCounts[t.id],
170
+ }))}
171
+ />
172
+
128
173
  <DataTable
129
174
  rows={rows}
130
175
  columns={columns}
176
+ loading={data.loading}
177
+ label="Projects"
131
178
  onRowClick={(row) => router.push(`/projects/${row.id}`)}
132
179
  empty={
133
180
  <EmptyState
134
181
  icon={<FolderKanban />}
135
- title={data.loading ? "Loading…" : "No projects yet"}
136
- description="A project holds a task board and the time logged against it. Create one to get started."
182
+ title={data.projects.length === 0 ? "No projects yet" : "No projects with this status"}
183
+ description={
184
+ data.projects.length === 0
185
+ ? "A project holds a task board and the time logged against it. Create one to get started."
186
+ : "Pick another tab to see the rest."
187
+ }
137
188
  action={
138
- <Button size="sm" onClick={() => setOpen(true)}>
139
- <Plus />
140
- New project
141
- </Button>
189
+ data.projects.length === 0 ? (
190
+ <Button size="sm" onClick={() => setOpen(true)}>
191
+ <Plus />
192
+ New project
193
+ </Button>
194
+ ) : undefined
142
195
  }
143
196
  />
144
197
  }
@@ -3,10 +3,18 @@
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 { FolderKanban, SquareCheck } 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 { minutesByTask, type Project, type Task, type TimeEntry } from "@/lib/work";
10
+ import {
11
+ DEFAULT_PROJECT_TAB,
12
+ minutesByTask,
13
+ projectTabCounts,
14
+ type Project,
15
+ type Task,
16
+ type TimeEntry,
17
+ } from "@/lib/work";
10
18
 
11
19
  export interface ClientRow {
12
20
  id: string;
@@ -33,6 +41,13 @@ export interface WorkspaceData {
33
41
  clientName: (id: string | null | undefined) => string | null;
34
42
  memberName: (id: string | null | undefined) => string | null;
35
43
  members: UserRow[];
44
+ /** Project count per list tab; see PROJECT_TABS. */
45
+ projectCounts: Record<string, number>;
46
+ /**
47
+ * The number beside each nav link, keyed by href. Page headers and tabs
48
+ * read the same counts, so the sidebar and the page can't disagree.
49
+ */
50
+ navCounts: Record<string, number>;
36
51
  loading: boolean;
37
52
  }
38
53
 
@@ -67,8 +82,7 @@ export function Workspace({
67
82
 
68
83
  // Whoever is signed in, named from the synced User row rather than an
69
84
  // SSR prop — see the note on useAuth above.
70
- const signedInEmail =
71
- (users ?? []).find((user) => user.id === userId)?.email ?? "";
85
+ const me = (users ?? []).find((user) => user.id === userId);
72
86
 
73
87
  const [paletteOpen, setPaletteOpen] = useState(false);
74
88
  const seeded = useRef(false);
@@ -126,14 +140,14 @@ export function Workspace({
126
140
  () => [
127
141
  ...projectList.map((project) => ({
128
142
  id: `project:${project.id}`,
129
- type: "company" as const,
143
+ type: "project",
130
144
  title: project.name,
131
145
  subtitle: clientById.get(project.clientId ?? "")?.name,
132
146
  href: `/projects/${project.id}`,
133
147
  })),
134
148
  ...taskList.map((task) => ({
135
149
  id: `task:${task.id}`,
136
- type: "deal" as const,
150
+ type: "task",
137
151
  title: task.title,
138
152
  href: `/projects/${task.projectId}`,
139
153
  })),
@@ -141,6 +155,12 @@ export function Workspace({
141
155
  [projectList, taskList, clientById],
142
156
  );
143
157
 
158
+ const projectCounts = projectTabCounts(projectList);
159
+ const navCounts = {
160
+ "/": projectCounts[DEFAULT_PROJECT_TAB],
161
+ "/clients": clientList.length,
162
+ };
163
+
144
164
  const data: WorkspaceData = {
145
165
  clients: clientList,
146
166
  projects: projectList,
@@ -153,23 +173,22 @@ export function Workspace({
153
173
  return user?.displayName || user?.email || null;
154
174
  },
155
175
  members: users ?? [],
176
+ projectCounts,
177
+ navCounts,
156
178
  loading,
157
179
  };
158
180
 
159
181
  return (
160
- <div className="flex h-screen overflow-hidden">
161
- <Sidebar
162
- workspace="Projects"
163
- email={signedInEmail}
164
- pathname={pathname}
165
- counts={{
166
- "/": projectList.filter((p) => p.status === "active").length,
167
- "/clients": clientList.length,
168
- }}
169
- onOpenCommand={() => setPaletteOpen(true)}
170
- onSignOut={() => void signOut()}
171
- />
172
- <main className="flex min-w-0 flex-1 flex-col">{children(data)}</main>
182
+ <AppShell
183
+ sidebar={{
184
+ user: { name: me?.displayName ?? "", email: me?.email ?? "" },
185
+ pathname,
186
+ counts: navCounts,
187
+ onOpenCommand: () => setPaletteOpen(true),
188
+ onSignOut: () => void signOut(),
189
+ }}
190
+ >
191
+ {children(data)}
173
192
 
174
193
  <CommandPalette
175
194
  open={paletteOpen}
@@ -186,9 +205,11 @@ export function Workspace({
186
205
  run: () => router.push("/clients"),
187
206
  },
188
207
  ]}
208
+ placeholder="Search projects and tasks…"
209
+ icons={{ project: <FolderKanban />, task: <SquareCheck /> }}
189
210
  onClose={() => setPaletteOpen(false)}
190
211
  onSelect={(item) => router.push(item.href)}
191
212
  />
192
- </div>
213
+ </AppShell>
193
214
  );
194
215
  }
@@ -10,7 +10,7 @@ import {
10
10
  } from "@pylonsync/sdk";
11
11
 
12
12
  // ---------------------------------------------------------------------------
13
- // projects — client project delivery for a small team: projects, a task board,
13
+ // Workbench (the projects template) — client project delivery for a small team: projects, a task board,
14
14
  // and time logged against the work.
15
15
  //
16
16
  // TIME IS A LEDGER. A task\'s logged hours are the sum of its TimeEntry rows,
@@ -176,7 +176,7 @@ const manifest = buildManifest({
176
176
  auth: auth(),
177
177
  fonts: [
178
178
  font({
179
- family: "Inter",
179
+ family: "Manrope",
180
180
  variable: "--font-sans",
181
181
  weights: ["400", "500", "600", "700"],
182
182
  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,
@@ -2,17 +2,17 @@ import React from "react";
2
2
  import { TASK_STATUSES } from "@/lib/work";
3
3
 
4
4
  /**
5
- * The board\'s shape while the replica hydrates. Showing the real columns keeps
5
+ * The board's shape while the replica hydrates. Showing the real columns keeps
6
6
  * the beat before the first row lands from reading as "this project has no
7
7
  * tasks", and nothing jumps when the data arrives.
8
8
  */
9
9
  export function BoardSkeleton() {
10
10
  return (
11
- <div className="flex h-full gap-3 overflow-hidden p-4" aria-hidden="true">
11
+ <div className="flex h-full gap-3 overflow-hidden p-4 md:px-5" aria-hidden="true">
12
12
  {TASK_STATUSES.map((status, column) => (
13
13
  <section
14
14
  key={status.id}
15
- className="flex w-[268px] shrink-0 flex-col rounded-xl border border-border bg-surface-1/50"
15
+ className="flex w-[84vw] shrink-0 flex-col rounded-xl border border-border/70 bg-surface-1 md:w-auto md:min-w-[220px] md:max-w-[360px] md:flex-1"
16
16
  >
17
17
  <header className="flex items-center gap-2 px-3 py-2.5">
18
18
  <span className="text-[12px] font-medium text-muted-foreground">
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ /**
5
+ * The Workbench mark: three board columns of falling height, on the accent
6
+ * 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-[62%]" fill="currentColor">
19
+ <rect x="1.5" y="1.5" width="3.6" height="13" rx="1.2" />
20
+ <rect x="6.2" y="1.5" width="3.6" height="9.5" rx="1.2" />
21
+ <rect x="10.9" y="1.5" width="3.6" height="6" rx="1.2" />
22
+ </svg>
23
+ </span>
24
+ );
25
+ }