@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
@@ -1,12 +1,19 @@
1
1
  import { expect, test } from "bun:test";
2
- import { browseListings } from "../lib/catalog";
3
- import { makeSlug, type Listing } from "../client/market";
2
+ import {
3
+ browseListings,
4
+ listingSrcSet,
5
+ pendingOfferCounts,
6
+ percentOfAsk,
7
+ suggestedOffers,
8
+ } from "../lib/catalog";
9
+ import { makeSlug, type Listing, type Offer } from "../client/market";
4
10
 
5
11
  const listings: Listing[] = [
6
12
  {
7
13
  id: "1",
8
14
  sellerId: "seller-1",
9
15
  sellerName: "maple-fox",
16
+ location: "Brooklyn, NY",
10
17
  title: "Walnut lounge chair",
11
18
  slug: "walnut-lounge-chair-1",
12
19
  description: "Restored frame with new upholstery.",
@@ -78,3 +85,58 @@ test("listing slugs remain readable and unique", () => {
78
85
  "vintage-technics-sl-1200-mk2-a1f3",
79
86
  );
80
87
  });
88
+
89
+ test("browse searches the seller's location", () => {
90
+ expect(
91
+ browseListings(listings, { query: "brooklyn" }).map((listing) => listing.id),
92
+ ).toEqual(["1"]);
93
+ });
94
+
95
+ test("pending offer counts ignore answered offers", () => {
96
+ const offer = (id: string, listingId: string, status: Offer["status"]): Offer => ({
97
+ id,
98
+ listingId,
99
+ listingTitle: "",
100
+ sellerId: "s",
101
+ buyerId: "b",
102
+ buyerName: "B",
103
+ amount: 10,
104
+ status,
105
+ createdAt: "2026-01-01T00:00:00.000Z",
106
+ });
107
+ const counts = pendingOfferCounts([
108
+ offer("a", "1", "pending"),
109
+ offer("b", "1", "pending"),
110
+ offer("c", "1", "declined"),
111
+ offer("d", "2", "accepted"),
112
+ ]);
113
+ expect(counts.get("1")).toBe(2);
114
+ expect(counts.has("2")).toBe(false);
115
+ });
116
+
117
+ test("suggested offers step below the asking price", () => {
118
+ expect(suggestedOffers(680)).toEqual([650, 610, 580]);
119
+ expect(suggestedOffers(95)).toEqual([90, 86, 81]);
120
+ expect(suggestedOffers(1)).toEqual([]);
121
+ expect(suggestedOffers(0)).toEqual([]);
122
+ for (const price of [5, 49, 120, 240, 1150]) {
123
+ for (const amount of suggestedOffers(price)) {
124
+ expect(amount).toBeGreaterThan(0);
125
+ expect(amount).toBeLessThan(price);
126
+ }
127
+ }
128
+ });
129
+
130
+ test("offer percentage of the asking price", () => {
131
+ expect(percentOfAsk(620, 680)).toBe(91);
132
+ expect(percentOfAsk(10, 0)).toBe(0);
133
+ });
134
+
135
+ test("bundled photos get a responsive srcset; others do not", () => {
136
+ expect(listingSrcSet("/images/listings/watch.webp")).toBe(
137
+ "/images/listings/watch-640.webp 640w, /images/listings/watch.webp 1120w",
138
+ );
139
+ expect(listingSrcSet("/api/files/abc")).toBeUndefined();
140
+ expect(listingSrcSet("https://example.com/a.webp")).toBeUndefined();
141
+ expect(listingSrcSet(undefined)).toBeUndefined();
142
+ });
@@ -0,0 +1,61 @@
1
+ import { afterEach, expect, mock, test } from "bun:test";
2
+ import React from "react";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import type { Listing } from "../client/market";
5
+
6
+ mock.module("@pylonsync/react", () => ({
7
+ Link: ({ href, children, seed: _seed, ...rest }: Record<string, unknown>) => (
8
+ <a href={href as string} {...rest}>
9
+ {children as React.ReactNode}
10
+ </a>
11
+ ),
12
+ db: { useQuery: () => ({ data: [], loading: false }) },
13
+ init: () => {},
14
+ configureClient: () => {},
15
+ callFn: async () => ({}),
16
+ setSessionToken: async () => {},
17
+ storageKey: (key: string) => key,
18
+ }));
19
+ const { ListingCard } = await import("../client/ListingCard");
20
+
21
+ afterEach(cleanup);
22
+
23
+ const listing: Listing = {
24
+ id: "1",
25
+ sellerId: "s1",
26
+ sellerName: "Nora Lind",
27
+ location: "Brooklyn, NY",
28
+ title: "Danish teak lounge chair",
29
+ slug: "danish-teak-lounge-chair-a1f3",
30
+ description: "",
31
+ price: 680,
32
+ category: "furniture",
33
+ condition: "good",
34
+ status: "active",
35
+ imageUrl: "/images/listings/lounge-chair.webp",
36
+ seed: "a1f3",
37
+ createdAt: new Date().toISOString(),
38
+ };
39
+
40
+ test("card shows price, title, location, and the live offer count", () => {
41
+ render(<ListingCard listing={listing} pendingOffers={2} />);
42
+ expect(screen.getByText("$680")).toBeDefined();
43
+ expect(screen.getByText("Danish teak lounge chair")).toBeDefined();
44
+ expect(screen.getByText("Brooklyn, NY · Good")).toBeDefined();
45
+ expect(screen.getByText("2 offers")).toBeDefined();
46
+ const link = screen.getByRole("link");
47
+ expect(link.getAttribute("href")).toBe("/listing/danish-teak-lounge-chair-a1f3");
48
+ });
49
+
50
+ test("a sold card shows Sold instead of the offer count", () => {
51
+ render(
52
+ <ListingCard listing={{ ...listing, id: "2", status: "sold" }} pendingOffers={3} />,
53
+ );
54
+ expect(screen.getAllByText("Sold").length).toBe(1);
55
+ expect(screen.queryByText("3 offers")).toBeNull();
56
+ });
57
+
58
+ test("a listing that arrived live is marked Just listed", () => {
59
+ render(<ListingCard listing={{ ...listing, id: "3" }} fresh />);
60
+ expect(screen.getByText("Just listed")).toBeDefined();
61
+ });
@@ -10,11 +10,16 @@ API, auth, and realtime sync from one process on one port.
10
10
  __RUN_DEV__
11
11
  ```
12
12
 
13
- Open http://localhost:4321 and create an account. The first sign-in seeds two
14
- live projects — one comfortably inside its budget, one over it — so both states
15
- are visible rather than theoretical. Delete `functions/seedWorkspace.ts` and
13
+ Open http://localhost:4321 and create an account. The first sign-in seeds ten
14
+ projects across active, paused, and complete, with two active ones over budget,
15
+ so every state is visible on first load. Delete `functions/seedWorkspace.ts` and
16
16
  `lib/seed.ts` once you have real work.
17
17
 
18
+ The tasks and timesheets are spread across you and four demo teammates. Each is a User
19
+ row with a `@demo.invalid` address and no password, so outside dev mode nobody
20
+ can sign in as one (see `lib/demo-team.ts`). Delete those users along with the
21
+ seed.
22
+
18
23
  ## What's interesting here
19
24
 
20
25
  **Time is a ledger, not a total.** A task\'s logged hours are the sum of its
@@ -46,6 +51,22 @@ Rejecting "1h30" because it isn\'t "90" is the kind of friction that stops time
46
51
  being logged at all, which costs far more than a forgiving parser. Values over a
47
52
  day are refused — a typo on a timesheet becomes a typo on an invoice.
48
53
 
54
+ ## Brand and layout
55
+
56
+ The product ships as **Workbench**. The name lives in `lib/brand.ts`, and the
57
+ sidebar, sign-in screen, page titles, and phone drawer all read it. The mark is
58
+ `components/brand-mark.tsx`; `app/icon.svg`, `app/apple-icon.png`, and
59
+ `public/favicon.ico` draw the same shape for browsers and home screens. The
60
+ accent colour is `--brand` at the top of `app/globals.css`, and the typeface
61
+ (Manrope) is set in `fonts` in `app.ts`.
62
+
63
+ The frame is `components/app-shell.tsx`. From 768px up the sidebar is a fixed
64
+ rail; below that it opens as a drawer from the menu button in each page header,
65
+ tables drop their lower-priority columns (`hideBelow` in
66
+ `components/data-table.tsx`), and boards scroll one column at a time. The nav
67
+ links are in `components/nav.tsx`. Every nav count comes from `navCounts` in
68
+ `app/workspace.tsx`, and page headers and tabs read the same numbers.
69
+
49
70
  ## Layout
50
71
 
51
72
  ```
@@ -17,7 +17,7 @@ export function ClientsView() {
17
17
  const [open, setOpen] = useState(false);
18
18
 
19
19
  return (
20
- <RequireAuth title="Projects" description="Your team shares one set of projects. Anyone with an account sees it.">
20
+ <RequireAuth>
21
21
  <Workspace pathname="/clients">
22
22
  {(data) => {
23
23
  const forClient = (clientId: string) =>
@@ -27,16 +27,23 @@ export function ClientsView() {
27
27
  {
28
28
  key: "name",
29
29
  header: "Client",
30
+ className: "w-[60%] md:w-[30%]",
30
31
  cell: (row) => (
31
- <span className="flex items-center gap-2">
32
- <Avatar name={row.name} size="sm" />
33
- <span className="truncate font-medium">{row.name}</span>
32
+ <span className="flex min-w-0 items-center gap-2.5">
33
+ <Avatar name={row.name} shape="square" />
34
+ <span className="min-w-0">
35
+ <span className="block truncate font-medium">{row.name}</span>
36
+ <span className="block truncate text-[12px] text-muted-foreground md:hidden">
37
+ {row.email ?? ""}
38
+ </span>
39
+ </span>
34
40
  </span>
35
41
  ),
36
42
  },
37
43
  {
38
44
  key: "email",
39
45
  header: "Email",
46
+ hideBelow: "md",
40
47
  cell: (row) => (
41
48
  <span className="text-muted-foreground">{row.email ?? "—"}</span>
42
49
  ),
@@ -44,12 +51,16 @@ export function ClientsView() {
44
51
  {
45
52
  key: "projects",
46
53
  header: "Projects",
54
+ hideBelow: "sm",
55
+ className: "w-[96px]",
47
56
  numeric: true,
48
57
  cell: (row) => forClient(row.id).length,
49
58
  },
50
59
  {
51
60
  key: "logged",
52
61
  header: "Logged",
62
+ hideBelow: "lg",
63
+ className: "w-[110px]",
53
64
  numeric: true,
54
65
  cell: (row) =>
55
66
  duration(
@@ -62,6 +73,7 @@ export function ClientsView() {
62
73
  {
63
74
  key: "billable",
64
75
  header: "Billable",
76
+ className: "w-[120px] md:w-[140px]",
65
77
  numeric: true,
66
78
  cell: (row) =>
67
79
  money(
@@ -82,16 +94,18 @@ export function ClientsView() {
82
94
 
83
95
  return (
84
96
  <>
85
- <PageHeader title="Clients" count={rows.length}>
97
+ <PageHeader title="Clients" count={data.navCounts["/clients"]}>
86
98
  <Button size="sm" onClick={() => setOpen(true)}>
87
99
  <Plus />
88
- New client
100
+ <span className="max-sm:sr-only">New client</span>
89
101
  </Button>
90
102
  </PageHeader>
91
103
 
92
104
  <DataTable
93
105
  rows={rows}
94
106
  columns={columns}
107
+ loading={data.loading}
108
+ label="Clients"
95
109
  empty={
96
110
  <EmptyState
97
111
  icon={<Users />}
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import type { Metadata, PageProps } from "@pylonsync/react";
3
+ import { BRAND } from "@/lib/brand";
3
4
  import { ClientsView } from "./clients-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Clients",
7
+ title: `Clients · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
@@ -9,15 +9,21 @@
9
9
  /* ---------------------------------------------------------------------------
10
10
  Design tokens.
11
11
 
12
- Tuned for a dense internal tool rather than a marketing page: a near-neutral
13
- surface ramp, hairline borders instead of shadows, and one accent used
14
- sparingly so status colour still means something. Dark is the primary theme —
15
- `app/layout.tsx` sets `class="dark"` — and light is a real, tested
16
- alternative, not an afterthought.
12
+ Tuned for a dense internal tool: a near-neutral surface ramp, hairline
13
+ borders instead of shadows, and one accent used sparingly so status colour
14
+ still means something. Light is the default; `.dark` holds a complete dark
15
+ set (add `class="dark"` to <html> in app/layout.tsx to switch).
17
16
 
18
- These are the same variable names shadcn/ui generates against, so
17
+ These are the variable names shadcn/ui generates against, so
19
18
  `npx shadcn@latest add <component>` drops in already themed.
20
19
  --------------------------------------------------------------------------- */
20
+ :root {
21
+ /* The brand accent: primary buttons, the brand mark, focus rings, and the
22
+ active nav icon. Change these two lines to re-colour the product. */
23
+ --brand: oklch(0.57 0.17 42);
24
+ --brand-dark: oklch(0.68 0.16 45);
25
+ }
26
+
21
27
  :root {
22
28
  --radius: 0.5rem;
23
29
 
@@ -28,7 +34,7 @@
28
34
  --popover: oklch(1 0 0);
29
35
  --popover-foreground: var(--foreground);
30
36
 
31
- --primary: oklch(0.55 0.204 274.5);
37
+ --primary: var(--brand);
32
38
  --primary-foreground: oklch(1 0 0);
33
39
  --secondary: oklch(0.968 0.002 286.4);
34
40
  --secondary-foreground: oklch(0.27 0.006 286);
@@ -40,7 +46,7 @@
40
46
 
41
47
  --border: oklch(0.918 0.004 286.3);
42
48
  --input: oklch(0.918 0.004 286.3);
43
- --ring: oklch(0.55 0.204 274.5);
49
+ --ring: var(--brand);
44
50
 
45
51
  /* Surfaces above the page: sidebar, popovers, hover rows. */
46
52
  --surface-1: oklch(0.985 0.001 286.4);
@@ -51,6 +57,7 @@
51
57
  --stage-lead: oklch(0.62 0.03 264);
52
58
  --stage-qualified: oklch(0.6 0.13 240);
53
59
  --stage-proposal: oklch(0.68 0.15 78);
60
+ --stage-negotiation: oklch(0.64 0.16 45);
54
61
  --stage-won: oklch(0.6 0.15 155);
55
62
  --stage-lost: oklch(0.6 0.11 25);
56
63
  }
@@ -63,7 +70,7 @@
63
70
  --popover: oklch(0.216 0.006 285.9);
64
71
  --popover-foreground: var(--foreground);
65
72
 
66
- --primary: oklch(0.62 0.19 274.5);
73
+ --primary: var(--brand-dark);
67
74
  --primary-foreground: oklch(1 0 0);
68
75
  --secondary: oklch(0.25 0.006 286);
69
76
  --secondary-foreground: oklch(0.93 0.002 286.3);
@@ -75,7 +82,7 @@
75
82
 
76
83
  --border: oklch(1 0 0 / 9%);
77
84
  --input: oklch(1 0 0 / 12%);
78
- --ring: oklch(0.62 0.19 274.5);
85
+ --ring: var(--brand-dark);
79
86
 
80
87
  --surface-1: oklch(0.186 0.004 285.9);
81
88
  --surface-2: oklch(0.216 0.006 285.9);
@@ -83,6 +90,7 @@
83
90
  --stage-lead: oklch(0.68 0.03 264);
84
91
  --stage-qualified: oklch(0.68 0.13 240);
85
92
  --stage-proposal: oklch(0.75 0.14 78);
93
+ --stage-negotiation: oklch(0.72 0.15 45);
86
94
  --stage-won: oklch(0.7 0.15 155);
87
95
  --stage-lost: oklch(0.68 0.14 25);
88
96
  }
@@ -118,6 +126,7 @@
118
126
  --color-stage-lead: var(--stage-lead);
119
127
  --color-stage-qualified: var(--stage-qualified);
120
128
  --color-stage-proposal: var(--stage-proposal);
129
+ --color-stage-negotiation: var(--stage-negotiation);
121
130
  --color-stage-won: var(--stage-won);
122
131
  --color-stage-lost: var(--stage-lost);
123
132
  }
@@ -143,15 +152,21 @@
143
152
  height: 100%;
144
153
  background-color: var(--color-background);
145
154
  color: var(--color-foreground);
146
- /* 13px is the density these screens are designed at; the slight negative
147
- tracking is what keeps Inter from looking loose at that size. */
148
- font-size: 13px;
155
+ /* 14px on phones, 13px from the md breakpoint up: the density these
156
+ screens are designed at on a desktop. */
157
+ font-size: 14px;
149
158
  line-height: 1.5;
150
- letter-spacing: -0.006em;
159
+ letter-spacing: -0.003em;
151
160
  -webkit-font-smoothing: antialiased;
152
161
  text-rendering: optimizeLegibility;
153
162
  }
154
163
 
164
+ @media (min-width: 768px) {
165
+ body {
166
+ font-size: 13px;
167
+ }
168
+ }
169
+
155
170
  button {
156
171
  cursor: pointer;
157
172
  }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#c54a05"/><g fill="#fff"><rect x="6" y="6" width="5.6" height="20" rx="1.8"/><rect x="13.2" y="6" width="5.6" height="14.6" rx="1.8"/><rect x="20.4" y="6" width="5.6" height="9.2" rx="1.8"/></g></svg>
@@ -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,16 +1,14 @@
1
1
  import React from "react";
2
2
  import type { Metadata, PageProps } from "@pylonsync/react";
3
+ import { BRAND } from "@/lib/brand";
3
4
  import { ProjectsView } from "./projects-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Projects",
7
+ title: `Projects · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
10
- /**
11
- * `/` — the project list.
12
- *
13
- */
11
+ /** `/` — the project list. */
14
12
  export default function ProjectsPage({ searchParams }: PageProps) {
15
13
  return (
16
14
  <ProjectsView openNew={searchParams?.new === "project"} />
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import type { Metadata, PageProps } from "@pylonsync/react";
3
+ import { BRAND } from "@/lib/brand";
3
4
  import { ProjectView } from "./project-view";
4
5
 
5
6
  export const metadata: Metadata = {
6
- title: "Project",
7
+ title: `Project · ${BRAND.name}`,
7
8
  robots: "noindex",
8
9
  };
9
10
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { useState } from "react";
4
4
  import { Link, callFn, db, useRouter } from "@pylonsync/react";
5
- import { ArrowLeft, Clock, Plus } from "lucide-react";
5
+ import { ChevronRight, Clock, Plus } from "lucide-react";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import { Input } from "@/components/ui/input";
8
8
  import { Select } from "@/components/ui/select";
@@ -11,8 +11,9 @@ import { EmptyState } from "@/components/empty-state";
11
11
  import { TaskBoard } from "@/components/task-board";
12
12
  import { BoardSkeleton } from "@/components/board-skeleton";
13
13
  import { BudgetBar } from "@/components/budget-bar";
14
+ import { Meter } from "@/components/meter";
14
15
  import { TimeDialog } from "@/components/time-dialog";
15
- import { PROJECT_STATUSES, duration, minutesForProject, progress } from "@/lib/work";
16
+ import { PROJECT_STATUSES, minutesForProject, progress } from "@/lib/work";
16
17
  import { RequireAuth } from "@/components/require-auth";
17
18
  import { Workspace } from "../../workspace";
18
19
 
@@ -26,7 +27,7 @@ export function ProjectView({
26
27
  const [timeFor, setTimeFor] = useState<{ id: string; title: string } | null>(null);
27
28
 
28
29
  return (
29
- <RequireAuth title="Projects" description="Your team shares one set of projects. Anyone with an account sees it.">
30
+ <RequireAuth>
30
31
  <Workspace pathname="/">
31
32
  {(data) => {
32
33
  const project = data.projects.find((p) => p.id === projectId);
@@ -36,7 +37,7 @@ export function ProjectView({
36
37
  <>
37
38
  <PageHeader title="Project" />
38
39
  <EmptyState
39
- title={data.loading ? "Loading…" : "Project not found"}
40
+ title={data.loading ? "Loading project…" : "Project not found"}
40
41
  description={
41
42
  data.loading ? undefined : "It may have been deleted, or the link is wrong."
42
43
  }
@@ -74,15 +75,23 @@ export function ProjectView({
74
75
 
75
76
  return (
76
77
  <>
77
- <PageHeader title={project.name}>
78
- <span className="tabular text-[12px] text-muted-foreground">
79
- {p.done}/{p.total} done · {duration(logged)}
80
- </span>
81
- <div className="w-28">
78
+ <PageHeader
79
+ title={project.name}
80
+ leading={
81
+ <Link
82
+ href="/"
83
+ className="hidden items-center gap-1 text-[13px] text-muted-foreground transition-colors hover:text-foreground md:inline-flex"
84
+ >
85
+ Projects
86
+ <ChevronRight className="size-3.5" />
87
+ </Link>
88
+ }
89
+ >
90
+ <div className="w-28 md:w-32">
82
91
  <Select
83
92
  aria-label="Project status"
84
93
  value={project.status}
85
- className="h-7 text-[12px]"
94
+ className="h-8 md:h-7.5"
86
95
  onChange={(event) =>
87
96
  void db.update("Project", project.id, {
88
97
  status: event.target.value,
@@ -98,34 +107,36 @@ export function ProjectView({
98
107
  </div>
99
108
  </PageHeader>
100
109
 
101
- <div className="flex items-center gap-6 border-b border-border px-4 py-3">
102
- <Link
103
- href="/"
104
- className="inline-flex shrink-0 items-center gap-1.5 text-[12px] text-muted-foreground transition-colors hover:text-foreground"
105
- >
106
- <ArrowLeft className="size-3.5" />
107
- Projects
108
- </Link>
109
- <span className="shrink-0 text-[12px] text-muted-foreground">
110
- {data.clientName(project.clientId) ?? "No client"}
111
- </span>
112
- <div className="min-w-0 flex-1 max-w-md">
113
- <BudgetBar
114
- budgetMinutes={project.budgetMinutes}
115
- loggedMinutes={logged}
116
- hourlyRateCents={project.hourlyRateCents}
110
+ <div className="grid shrink-0 gap-x-8 gap-y-3 border-b border-border px-4 py-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] md:items-center md:px-5">
111
+ <div className="min-w-0">
112
+ <p className="truncate text-[12.5px] text-muted-foreground">
113
+ {data.clientName(project.clientId) ?? "No client"}
114
+ {project.dueDate
115
+ ? ` · due ${new Date(project.dueDate).toLocaleDateString("en-US", { month: "short", day: "numeric" })}`
116
+ : ""}
117
+ </p>
118
+ <Meter
119
+ className="mt-1.5"
120
+ ratio={p.ratio ?? 0}
121
+ label={`${p.done}/${p.total} tasks done`}
117
122
  />
118
123
  </div>
119
- <form onSubmit={addTask} className="ml-auto flex shrink-0 items-center gap-2">
124
+ <BudgetBar
125
+ budgetMinutes={project.budgetMinutes}
126
+ loggedMinutes={logged}
127
+ hourlyRateCents={project.hourlyRateCents}
128
+ />
129
+ <form onSubmit={addTask} className="flex items-center gap-2">
120
130
  <Input
121
131
  aria-label="New task"
122
132
  placeholder="Add a task…"
123
133
  value={newTask}
124
134
  onChange={(event) => setNewTask(event.target.value)}
125
- className="h-7 w-48 text-[12px]"
135
+ className="h-9 flex-1 md:h-8 md:w-52"
126
136
  />
127
- <Button type="submit" size="sm" className="h-7" disabled={!newTask.trim()}>
137
+ <Button type="submit" size="sm" className="h-9 md:h-8" disabled={!newTask.trim()}>
128
138
  <Plus />
139
+ <span className="sr-only">Add task</span>
129
140
  </Button>
130
141
  </form>
131
142
  </div>
@@ -146,7 +157,7 @@ export function ProjectView({
146
157
  minutesFor={(taskId) => data.taskMinutes.get(taskId) ?? 0}
147
158
  onMove={(taskId, status) => {
148
159
  // Through the mutation: the new position is computed
149
- // server-side so two simultaneous drags don\'t collide.
160
+ // server-side so two simultaneous drags don't collide.
150
161
  void callFn("moveTask", { taskId, status });
151
162
  }}
152
163
  onOpen={(taskId) => {
@@ -160,6 +171,10 @@ export function ProjectView({
160
171
  <TimeDialog
161
172
  open={timeFor !== null}
162
173
  taskTitle={timeFor?.title ?? ""}
174
+ status={tasks.find((t) => t.id === timeFor?.id)?.status}
175
+ onMove={(status) => {
176
+ if (timeFor) void callFn("moveTask", { taskId: timeFor.id, status });
177
+ }}
163
178
  onOpenChange={(open) => {
164
179
  if (!open) setTimeFor(null);
165
180
  }}