@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
@@ -0,0 +1,144 @@
1
+ // Demo reservations for local development.
2
+ //
3
+ // `pylon dev` fills an empty reservation book with fictional tables from the
4
+ // past week and the next two weeks, so the calendar shows fewer tables left
5
+ // (and some full seatings) and the owner dashboard has a book to run. Both
6
+ // read the same rows: every active demo Reservation gets its ReservationSlot
7
+ // marker, exactly like createReservation writes.
8
+ //
9
+ // A deploy does not seed: `demoDataEnabled` is true only when PYLON_DEMO_DATA is
10
+ // on, or when it is unset and the process runs under `pylon dev`. Set PYLON_DEMO_DATA=0 to
11
+ // turn it off in development too.
12
+ //
13
+ // Pure data + pure shaping, so functions/seedDemo.ts stays a thin wrapper and
14
+ // this file is testable without a server.
15
+
16
+ import { localDateKey, seatingsForDay, weekdayOf } from "./slots";
17
+ import type { RestaurantConfig } from "./site.config";
18
+
19
+ const ON = new Set(["1", "true", "yes", "on"]);
20
+ const OFF = new Set(["0", "false", "no", "off"]);
21
+
22
+ /**
23
+ * True when demo rows may be written. An explicit PYLON_DEMO_DATA wins.
24
+ * Otherwise it needs both PYLON_DEV_MODE on and PYLON_DEV_WATCH_DIR set. Only
25
+ * `pylon dev` sets PYLON_DEV_WATCH_DIR; `pylon start` and the Docker image do
26
+ * not, and the image turns PYLON_DEV_MODE on by default, so dev mode alone is
27
+ * not proof of a local machine.
28
+ */
29
+ export function demoDataEnabled(env: Record<string, string | undefined>): boolean {
30
+ const explicit = env.PYLON_DEMO_DATA?.trim().toLowerCase();
31
+ if (explicit && ON.has(explicit)) return true;
32
+ if (explicit && OFF.has(explicit)) return false;
33
+ const dev = env.PYLON_DEV_MODE?.trim().toLowerCase();
34
+ const devMode = dev === "1" || dev === "true";
35
+ return devMode && Boolean(env.PYLON_DEV_WATCH_DIR?.trim());
36
+ }
37
+
38
+ const GUESTS = [
39
+ "Ana Whitaker", "James Osei", "Priya Raman", "Tom Alvarez", "Claire Dubois", "Marcus Iyer",
40
+ "Leah Goldberg", "Noah Fischer", "Sofia Marin", "Daniel Park", "Grace Mensah", "Henry Walsh",
41
+ "Isabel Cruz", "Owen Reilly", "Nadia Haddad", "Ethan Brooks", "Maya Tanaka", "Luca Romano",
42
+ "Zoe Bennett", "Samir Khan", "Ruth Castillo", "Felix Wagner", "Hana Sato", "Diego Vega",
43
+ ];
44
+ const NOTES = [
45
+ "Anniversary dinner",
46
+ "One guest is vegetarian",
47
+ "Window table if possible",
48
+ "Celebrating a birthday",
49
+ "Shellfish allergy",
50
+ "Arriving from the theater, may run 10 minutes late",
51
+ ];
52
+
53
+ /** Small deterministic PRNG (mulberry32) so the demo set is stable per seed. */
54
+ function rng(seed: number): () => number {
55
+ let a = seed >>> 0;
56
+ return () => {
57
+ a = (a + 0x6d2b79f5) >>> 0;
58
+ let t = a;
59
+ t = Math.imul(t ^ (t >>> 15), t | 1);
60
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
61
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
62
+ };
63
+ }
64
+
65
+ export interface DemoReservation {
66
+ startsAt: string;
67
+ partySize: number;
68
+ customerName: string;
69
+ customerEmail: string;
70
+ customerPhone: string | null;
71
+ notes: string | null;
72
+ status: "pending" | "confirmed" | "cancelled";
73
+ createdAt: string;
74
+ }
75
+
76
+ type Cfg = Pick<RestaurantConfig, "reservations">;
77
+
78
+ /**
79
+ * Reservations on open days from 7 days ago to 13 days ahead, on the seating
80
+ * grid. Active reservations never exceed `tablesPerSlot` at one seating.
81
+ * Weekend evenings around 7–8 PM fill up; early and late seatings stay light.
82
+ */
83
+ export function demoReservations(nowMs: number, cfg: Cfg, seed = 5): DemoReservation[] {
84
+ const r = cfg.reservations;
85
+ const rand = rng(seed);
86
+ const out: DemoReservation[] = [];
87
+
88
+ for (let offset = -7; offset <= 13; offset++) {
89
+ const day = localDateKey(offset, nowMs);
90
+ const dow = weekdayOf(day);
91
+ const hours = r.hours[dow];
92
+ if (!hours) continue;
93
+ const seatings = seatingsForDay({
94
+ dayISODate: day,
95
+ open: hours.open,
96
+ close: hours.close,
97
+ slotMinutes: r.slotMinutes,
98
+ leadTimeHours: 0,
99
+ nowMs: 0,
100
+ });
101
+ const weekend = dow === 5 || dow === 6;
102
+ // Nearer dates are more booked than dates two weeks out.
103
+ const horizon = offset <= 3 ? 1 : offset <= 8 ? 0.7 : 0.4;
104
+ for (const seat of seatings) {
105
+ const hour = new Date(seat.startsAt).getHours();
106
+ const peak = hour >= 19 && hour < 21 ? 1 : hour >= 18 && hour < 22 ? 0.6 : 0.3;
107
+ const expected = r.tablesPerSlot * peak * horizon * (weekend ? 0.95 : 0.5);
108
+ let tables = Math.min(r.tablesPerSlot, Math.round(expected + (rand() - 0.4) * 2));
109
+ if (tables < 0) tables = 0;
110
+ const start = Date.parse(seat.startsAt);
111
+ let active = 0;
112
+ for (let t = 0; t < tables + 1 && active < r.tablesPerSlot; t++) {
113
+ // The extra pass may add a cancelled row, which frees its table.
114
+ const extra = t === tables;
115
+ if (extra && rand() > 0.15) break;
116
+ const past = start <= nowMs;
117
+ const roll = rand();
118
+ const status: DemoReservation["status"] = extra
119
+ ? "cancelled"
120
+ : past
121
+ ? "confirmed"
122
+ : roll < 0.86
123
+ ? "confirmed"
124
+ : "pending";
125
+ if (status !== "cancelled") active++;
126
+ const name = GUESTS[Math.floor(rand() * GUESTS.length)];
127
+ const [first, last] = name.toLowerCase().split(" ");
128
+ const party = rand() < 0.55 ? 2 : Math.min(r.maxPartySize, 3 + Math.floor(rand() * 4));
129
+ const bookedAt = Math.min(nowMs - 3_600_000, start - (1 + Math.floor(rand() * 10)) * 86_400_000);
130
+ out.push({
131
+ startsAt: seat.startsAt,
132
+ partySize: party,
133
+ customerName: name,
134
+ customerEmail: `${first}.${last}@example.com`,
135
+ customerPhone: rand() < 0.75 ? `(214) 555-01${String(Math.floor(rand() * 100)).padStart(2, "0")}` : null,
136
+ notes: rand() < 0.18 ? NOTES[Math.floor(rand() * NOTES.length)] : null,
137
+ status,
138
+ createdAt: new Date(bookedAt).toISOString(),
139
+ });
140
+ }
141
+ }
142
+ }
143
+ return out;
144
+ }
@@ -0,0 +1,45 @@
1
+ // Weekly hours as display rows, from `siteConfig.reservations.hours` (the same
2
+ // hours the reservation calendar uses), so the page cannot disagree with the
3
+ // calendar. Consecutive days with the same hours share a row:
4
+ // "Wed–Thu 5 PM–9:30 PM".
5
+
6
+ import type { DayHours } from "./site.config";
7
+
8
+ const DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
9
+ // Rows run Monday to Sunday, the way a door sign lists them.
10
+ const ORDER = [1, 2, 3, 4, 5, 6, 0];
11
+
12
+ export interface HoursRow {
13
+ days: string;
14
+ time: string; // "5 PM–10 PM" or "Closed"
15
+ closed: boolean;
16
+ }
17
+
18
+ /** "09:00" → "9 AM", "18:30" → "6:30 PM". */
19
+ export function clock(hhmm: string): string {
20
+ const [h, m] = hhmm.split(":").map(Number);
21
+ const suffix = h >= 12 ? "PM" : "AM";
22
+ const h12 = h % 12 === 0 ? 12 : h % 12;
23
+ return m ? `${h12}:${String(m).padStart(2, "0")} ${suffix}` : `${h12} ${suffix}`;
24
+ }
25
+
26
+ export function hoursRows(hours: Record<number, DayHours>): HoursRow[] {
27
+ const rows: { first: number; last: number; time: string }[] = [];
28
+ for (const d of ORDER) {
29
+ const h = hours[d];
30
+ const time = h ? `${clock(h.open)}–${clock(h.close)}` : "Closed";
31
+ const prev = rows[rows.length - 1];
32
+ if (prev && prev.time === time) prev.last = d;
33
+ else rows.push({ first: d, last: d, time });
34
+ }
35
+ return rows.map((r) => ({
36
+ days: r.first === r.last ? DAY[r.first] : `${DAY[r.first]}–${DAY[r.last]}`,
37
+ time: r.time,
38
+ closed: r.time === "Closed",
39
+ }));
40
+ }
41
+
42
+ /** A Google Maps directions link for a street address. Needs no API key. */
43
+ export function directionsUrl(address: string): string {
44
+ return `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(address)}`;
45
+ }
@@ -57,8 +57,9 @@ export type RestaurantConfig = BaseConfig & {
57
57
  location: {
58
58
  headline: string;
59
59
  address: string;
60
+ // A Google Maps embed URL (Share → Embed a map → the iframe's src). When
61
+ // empty, the page shows the week's service hours beside the address.
60
62
  mapEmbedUrl?: string;
61
- hoursText: string;
62
63
  phone: string;
63
64
  email: string;
64
65
  };
@@ -99,7 +100,7 @@ export const siteConfig: RestaurantConfig = {
99
100
  "Seasonal small plates, wood-fired mains, and a short natural-wine list. The reservation calendar shows current table availability.",
100
101
  ctaLabel: "Reserve a table",
101
102
  quickFacts: {
102
- hours: "Wed–Sun, 5–10",
103
+ hours: "Wed–Sun from 5 PM",
103
104
  area: "Bishop Arts, Dallas",
104
105
  phone: "(214) 555-0172",
105
106
  },
@@ -194,7 +195,6 @@ export const siteConfig: RestaurantConfig = {
194
195
  headline: "Find us in Bishop Arts.",
195
196
  address: "412 N Bishop Ave, Dallas, TX 75208",
196
197
  mapEmbedUrl: "",
197
- hoursText: "Wed–Thu 5–9:30 · Fri–Sat 5–10 · Sun 5–9 · Mon–Tue closed",
198
198
  phone: "(214) 555-0172",
199
199
  email: "hello@cedarandvine.example",
200
200
  },
@@ -0,0 +1,67 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { demoDataEnabled, demoReservations } from "../lib/demo";
3
+ import { clock, directionsUrl, hoursRows } from "../lib/hours";
4
+ import { siteConfig } from "../lib/site.config";
5
+ import { weekdayOf } from "../lib/slots";
6
+
7
+ describe("hoursRows", () => {
8
+ test("groups days with the same service hours", () => {
9
+ expect(hoursRows(siteConfig.reservations.hours)).toEqual([
10
+ { days: "Mon–Tue", time: "Closed", closed: true },
11
+ { days: "Wed–Thu", time: "5 PM–9:30 PM", closed: false },
12
+ { days: "Fri–Sat", time: "5 PM–10 PM", closed: false },
13
+ { days: "Sun", time: "5 PM–9 PM", closed: false },
14
+ ]);
15
+ });
16
+ test("clock and directions link", () => {
17
+ expect(clock("21:30")).toBe("9:30 PM");
18
+ expect(directionsUrl("412 N Bishop Ave")).toContain("destination=412%20N%20Bishop%20Ave");
19
+ });
20
+ });
21
+
22
+ describe("demo reservations", () => {
23
+ test("only under `pylon dev` unless PYLON_DEMO_DATA says otherwise", () => {
24
+ expect(demoDataEnabled({ PYLON_DEV_MODE: "1", PYLON_DEV_WATCH_DIR: "/src/app" })).toBe(true);
25
+ // The Docker base image turns dev mode on by default; without `pylon dev` it must not seed.
26
+ expect(demoDataEnabled({ PYLON_DEV_MODE: "true" })).toBe(false);
27
+ expect(demoDataEnabled({ PYLON_DEV_MODE: "false" })).toBe(false);
28
+ expect(demoDataEnabled({})).toBe(false);
29
+ expect(demoDataEnabled({ PYLON_DEV_MODE: "true", PYLON_DEV_WATCH_DIR: "/src/app", PYLON_DEMO_DATA: "off" })).toBe(false);
30
+ });
31
+
32
+ const now = new Date(2026, 8, 26, 14, 0).getTime();
33
+ const rows = demoReservations(now, siteConfig);
34
+ const cfg = siteConfig.reservations;
35
+
36
+ test("never books more active tables than a seating has", () => {
37
+ const perSeat = new Map<string, number>();
38
+ for (const r of rows) {
39
+ if (r.status === "cancelled") continue;
40
+ perSeat.set(r.startsAt, (perSeat.get(r.startsAt) ?? 0) + 1);
41
+ }
42
+ expect(Math.max(...perSeat.values())).toBeLessThanOrEqual(cfg.tablesPerSlot);
43
+ // Some weekend seatings fill up, so the calendar shows "Full".
44
+ expect([...perSeat.values()].some((n) => n === cfg.tablesPerSlot)).toBe(true);
45
+ });
46
+
47
+ test("only on open days, within the seating window, with valid parties", () => {
48
+ for (const r of rows) {
49
+ const d = new Date(r.startsAt);
50
+ const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
51
+ const hours = cfg.hours[weekdayOf(key)];
52
+ expect(hours).not.toBeNull();
53
+ const hhmm = `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
54
+ expect(hhmm >= hours!.open && hhmm <= hours!.close).toBe(true);
55
+ expect(r.partySize).toBeGreaterThanOrEqual(1);
56
+ expect(r.partySize).toBeLessThanOrEqual(cfg.maxPartySize);
57
+ expect(Date.parse(r.createdAt)).toBeLessThan(now);
58
+ if (r.customerPhone) expect(r.customerPhone).toMatch(/^\(214\) 555-01\d{2}$/);
59
+ }
60
+ });
61
+
62
+ test("past tables are confirmed; upcoming ones include some to confirm", () => {
63
+ const past = rows.filter((r) => Date.parse(r.startsAt) <= now && r.status !== "cancelled");
64
+ expect(past.every((r) => r.status === "confirmed")).toBe(true);
65
+ expect(rows.some((r) => Date.parse(r.startsAt) > now && r.status === "pending")).toBe(true);
66
+ });
67
+ });
@@ -1,10 +1,11 @@
1
1
  # __APP_NAME__
2
2
 
3
3
  A full-stack, multi-tenant SaaS starter on [Pylon](https://pylonsync.com),
4
- branded as the fictional product **Acme**. It includes a server-rendered
5
- marketing site, first-run onboarding, email/password and Google auth,
6
- organizations with members and roles, tenant-scoped projects, and per-workspace
7
- Stripe billing. Pylon serves it from one process.
4
+ branded as the fictional product **Acme**: project and task tracking for small
5
+ teams. It includes a server-rendered marketing site, first-run onboarding with
6
+ sample projects, email/password and Google auth, organizations with members
7
+ and roles, tenant-scoped projects and task boards that update live, and
8
+ per-workspace Stripe billing. Pylon serves it from one process.
8
9
 
9
10
  ## Develop
10
11
 
@@ -12,34 +13,38 @@ Stripe billing. Pylon serves it from one process.
12
13
  __RUN_DEV__
13
14
  ```
14
15
 
15
- Open http://localhost:4321 to load the Acme landing page. Sign up and create an
16
- organization to enter a workspace with tenant-scoped projects and a members
17
- panel. Create a second organization and switch between them; each one's data
18
- remains private. Editing a file under `app/` reloads the page.
16
+ Open http://localhost:4321 to load the Acme landing page. Sign up, name a
17
+ workspace, and pick **Start with sample projects** to land on a dashboard
18
+ with two projects and 16 tasks. Open a project board in two tabs and change a
19
+ task's status in one: the other updates within a second. Create a second
20
+ workspace and switch between them; each one's data stays private. Editing a
21
+ file under `app/` reloads the page.
19
22
 
20
23
  ## Layout
21
24
 
22
25
  ```
23
- app.ts User + Org/OrgMember/OrgInvite + Project + Stripe billing manifest
24
- app/page.tsx "/" — the server-rendered Acme landing page (auth-aware)
25
- app/layout.tsx marketing nav + footer (rebrand "Acme")
26
- app/{products,solutions,resources,company,compare}/[slug]/ data-driven marketing pages
27
- app/login,signup/ email/password, email code, Google; forgot/reset password
28
- app/onboarding/ first-run wizard: workspace → invite → first project → plan (trial)
29
- app/pricing/ monthly/annual plan table from lib/plans.ts
30
- app/dashboard/ "/dashboard" — authed; overview + getting-started checklist, projects, members, billing, settings
31
- app/dashboard/dashboard-client.tsx the workspace client island
32
- app/{error,not-found}.tsx hydrated error + 404 boundaries
33
- app/{robots,sitemap}.ts /robots.txt + /sitemap.xml (enumerates every public page)
34
- functions/ createProject (free-plan cap), onboarding + profile mutations, Stripe handlers
35
- lib/ plans.ts (prices, cap, trial), billing.ts (@pylonsync/stripe), site.config.ts (copy)
36
- app/globals.css Tailwind v4 + shadcn tokens (compiled by Pylon)
37
- components/ui/ shadcn primitives (Button, Card)
26
+ app.ts User + Org/OrgMember/OrgInvite + Project + Task + Stripe billing manifest
27
+ app/(marketing)/page.tsx "/": the landing page (hero, product sections, quote, pricing, FAQ)
28
+ app/(marketing)/layout.tsx marketing nav + footer
29
+ app/(marketing)/{products,solutions,resources,company,compare}/[slug]/ data-driven pages
30
+ app/(auth)/ /login, /signup, forgot/reset password
31
+ app/onboarding/ first-run wizard: workspace → invite → first project or samples → plan
32
+ app/dashboard/ overview, projects, projects/[id] (task board), members, billing, settings
33
+ app/icon.svg, public/favicon.ico the app icon
34
+ app/{robots,sitemap,llms}.ts /robots.txt, /sitemap.xml, /llms.txt
35
+ functions/ project + task mutations, seedWorkspace, onboarding, profile, Stripe handlers
36
+ lib/site.config.ts every piece of marketing copy, the products, and the colors
37
+ lib/plans.ts prices, the free project cap, the trial length
38
+ lib/tasks.ts task statuses, priorities, validation, ordering
39
+ lib/sample-workspace.ts the sample projects and tasks seedWorkspace adds
40
+ public/screenshots/ dashboard captures the landing page shows
41
+ scripts/capture-screenshots.mjs regenerates public/screenshots from the running app
42
+ components/ dashboard shell, task board pieces, marketing pieces, shadcn primitives
38
43
  ```
39
44
 
40
45
  ## How it works
41
46
 
42
- **The landing page** (`app/page.tsx`) is server-rendered React — view source and
47
+ **The landing page** (`app/(marketing)/page.tsx`) is server-rendered React — view source and
43
48
  the copy + SEO `<head>` are in the HTML, so it's fully indexable. It reads the
44
49
  session during the render, so the call-to-action is "Get started" for visitors
45
50
  and "Open dashboard" once you're signed in — no flash, no client fetch.
@@ -51,25 +56,39 @@ visitors with a real 3xx before any HTML (works with JS off).
51
56
  **Multi-tenancy** is a framework primitive. Declaring `Org` / `OrgMember` /
52
57
  `OrgInvite` lights up `/api/auth/orgs/*` + `/api/auth/select-org`, driven by
53
58
  `<OrganizationSwitcher>` from `@pylonsync/client`. Your data lives in
54
- tenant-scoped entities (`Project`), gated by policy:
59
+ tenant-scoped entities (`Project`, `Task`), gated by policy:
55
60
 
56
61
  ```ts
57
62
  allowRead: "auth.tenantId == data.orgId"
58
- allowInsert: "auth.tenantId == data.orgId"
59
63
  ```
60
64
 
61
- So `db.useQuery("Project")` returns only your **active org's** projects — switch
62
- orgs and the list changes, and a client literally cannot read or write another
63
- tenant's rows. `db.useQuery` is live; `db.insert` is optimistic.
65
+ So `db.useQuery("Project")` returns only your **active org's** projects, and
66
+ switching orgs changes the list. `Task` uses the same read policy.
67
+
68
+ Writes to projects and tasks go through server functions (`createProject`,
69
+ `setProjectStatus`, `deleteProject`, `createTask`, `updateTask`,
70
+ `deleteTask`). Each one loads the row, takes the workspace from the row, and
71
+ calls `ctx.requireMember` for that workspace. The entity policies deny direct
72
+ client writes, and `orgId` is `.readonly()`, so a client cannot move a row into
73
+ another workspace or skip the free plan's project cap.
64
74
 
65
75
  ## Make it yours
66
76
 
67
- - **Rebrand:** replace "Acme" in `app/page.tsx` + `app/layout.tsx`.
68
- - **Edit the marketing copy:** the product / solution / compare pages read from
69
- `lib/products.ts` + `lib/site.ts` — edit one entry and the nav dropdown, the
70
- footer, and the `[slug]` page all follow (they can't drift).
71
- - **Add tenant data:** new `entity()` with an `orgId` + the same two policy
72
- lines — a new tenant-scoped table, typed client and REST/realtime API included.
77
+ - **Rebrand and edit the copy:** everything the marketing site says, including
78
+ the products, the quote, the FAQ, and the colors, lives in
79
+ `lib/site.config.ts`. Edit one entry and the landing page, the nav, the
80
+ footer, and the `[slug]` pages all follow. The quote and the "vs" pages are
81
+ placeholders; replace them or delete them.
82
+ - **Replace the sample data:** edit `lib/sample-workspace.ts`, or remove the
83
+ "Start with sample projects" option in `app/onboarding/onboarding-client.tsx`.
84
+ - **Refresh the screenshots:** after you change the dashboard, run
85
+ `pylon dev`, then `node scripts/capture-screenshots.mjs` (it needs
86
+ Playwright: `npm i -D playwright && npx playwright install chromium`). Use a
87
+ fresh development database so the captures show only the sample workspace.
88
+ - **Add tenant data:** a new `entity()` with an `orgId: field.id("Org").readonly()`
89
+ and the same read policy gives you a tenant-scoped table with a typed client
90
+ and a REST and realtime API. Route its writes through functions that call
91
+ `ctx.requireMember`, as `functions/createTask.ts` does.
73
92
  - **Add a route:** drop `app/about/page.tsx` and visit `/about`.
74
93
  - **Change prices or the free cap:** edit `lib/plans.ts`. The pricing page, the
75
94
  Billing tab, and the server-side cap in `functions/createProject.ts` all read it.
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { useEffect, useState } from "react";
4
4
  import { Link } from "@pylonsync/react";
5
+ import { Lock, Mail, UserRound } from "lucide-react";
5
6
  import {
6
7
  passwordLogin,
7
8
  passwordRegister,
@@ -19,6 +20,7 @@ import {
19
20
  // engine's calls). Sign-up then lands on /onboarding, which creates the
20
21
  // workspace; sign-in lands on /dashboard.
21
22
  export function AuthForm({ mode }: { mode: "login" | "signup" }) {
23
+ const [name, setName] = useState("");
22
24
  const [email, setEmail] = useState("");
23
25
  const [password, setPassword] = useState("");
24
26
  const [code, setCode] = useState("");
@@ -72,7 +74,11 @@ export function AuthForm({ mode }: { mode: "login" | "signup" }) {
72
74
  const session =
73
75
  mode === "login"
74
76
  ? await passwordLogin({ email, password })
75
- : await passwordRegister({ email, password });
77
+ : await passwordRegister({
78
+ email,
79
+ password,
80
+ ...(name.trim() ? { displayName: name.trim() } : {}),
81
+ });
76
82
  persistSession(session);
77
83
  window.location.assign(destination);
78
84
  } catch (err) {
@@ -84,10 +90,23 @@ export function AuthForm({ mode }: { mode: "login" | "signup" }) {
84
90
  return (
85
91
  <div className="space-y-5">
86
92
  <form onSubmit={onSubmit} className="space-y-4">
93
+ {mode === "signup" && method === "password" ? (
94
+ <IconField
95
+ label="Full name"
96
+ type="text"
97
+ icon={<UserRound className="size-[15px]" strokeWidth={1.75} />}
98
+ value={name}
99
+ onChange={setName}
100
+ required
101
+ autoComplete="name"
102
+ maxLength={60}
103
+ placeholder="Dana Reyes"
104
+ />
105
+ ) : null}
87
106
  <IconField
88
107
  label="Email"
89
108
  type="email"
90
- icon={<MailIcon />}
109
+ icon={<Mail className="size-[15px]" strokeWidth={1.75} />}
91
110
  value={email}
92
111
  onChange={setEmail}
93
112
  required
@@ -99,7 +118,7 @@ export function AuthForm({ mode }: { mode: "login" | "signup" }) {
99
118
  <IconField
100
119
  label="Password"
101
120
  type="password"
102
- icon={<LockIcon />}
121
+ icon={<Lock className="size-[15px]" strokeWidth={1.75} />}
103
122
  value={password}
104
123
  onChange={setPassword}
105
124
  required
@@ -120,7 +139,7 @@ export function AuthForm({ mode }: { mode: "login" | "signup" }) {
120
139
  <IconField
121
140
  label="6-digit code"
122
141
  type="text"
123
- icon={<LockIcon />}
142
+ icon={<Lock className="size-[15px]" strokeWidth={1.75} />}
124
143
  value={code}
125
144
  onChange={(v) => setCode(v.replace(/\D/g, "").slice(0, 6))}
126
145
  required
@@ -258,7 +277,7 @@ function IconField({
258
277
  <input
259
278
  value={value}
260
279
  onChange={(e) => onChange(e.target.value)}
261
- className="h-10 w-full rounded-lg border border-zinc-300 bg-white pl-9 pr-3 text-sm text-zinc-900 outline-none transition placeholder:text-zinc-400 focus:border-zinc-900 focus:ring-2 focus:ring-zinc-900/10 disabled:bg-zinc-50 disabled:text-zinc-500"
280
+ className="h-10 w-full rounded-lg border border-zinc-200 bg-white pl-9 pr-3 text-sm text-zinc-900 shadow-[0_1px_1px_rgba(0,0,0,0.03)] outline-none transition placeholder:text-zinc-400 focus:border-zinc-400 focus:ring-4 focus:ring-zinc-900/5 disabled:bg-zinc-50 disabled:text-zinc-500"
262
281
  {...rest}
263
282
  />
264
283
  </span>
@@ -267,24 +286,6 @@ function IconField({
267
286
  );
268
287
  }
269
288
 
270
- function MailIcon() {
271
- return (
272
- <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
273
- <rect x="3" y="5" width="18" height="14" rx="2" />
274
- <path d="m3 7 9 6 9-6" />
275
- </svg>
276
- );
277
- }
278
-
279
- function LockIcon() {
280
- return (
281
- <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
282
- <rect x="4" y="11" width="16" height="10" rx="2" />
283
- <path d="M8 11V7a4 4 0 0 1 8 0v4" />
284
- </svg>
285
- );
286
- }
287
-
288
289
  function GitHubIcon() {
289
290
  return (
290
291
  <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
@@ -310,6 +311,7 @@ function messageFor(err: unknown): string {
310
311
  case "INVALID_CREDENTIALS":
311
312
  return "Wrong email or password.";
312
313
  case "USER_EXISTS":
314
+ case "EMAIL_TAKEN":
313
315
  return "That email is already in use — sign in instead.";
314
316
  case "WEAK_PASSWORD":
315
317
  return "Pick a longer password — at least 10 characters.";
@@ -10,7 +10,7 @@ export const metadata: Metadata = {
10
10
  export default function ForgotPasswordPage() {
11
11
  return (
12
12
  <>
13
- <h1 className="mt-5 text-[22px] font-semibold tracking-tight text-zinc-900">Reset your password</h1>
13
+ <h1 className="mt-10 text-[26px] font-semibold tracking-[-0.025em] text-zinc-950">Reset your password</h1>
14
14
  <p className="mt-1 text-[13px] text-zinc-500">Enter your email and we will send a reset link.</p>
15
15
  <div className="mt-6">
16
16
  <ForgotPasswordForm />
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import { Link, type PageAuth, type SsrResponse } from "@pylonsync/react";
3
+ import { siteConfig } from "@/lib/site.config";
3
4
 
4
5
  // `(auth)` route group → the shared split-screen frame for /login and /signup:
5
- // the form card on the left, a brand/testimonial panel on the right (hidden on
6
- // small screens). Each page supplies what differs — heading, switch link, and
6
+ // the form on the left, a product screenshot on the right (hidden on small
7
+ // screens). Each page supplies what differs — heading, switch link, and
7
8
  // the form — as `children`. The group segment adds no URL prefix, and because
8
9
  // these routes sit outside `(marketing)`, none of the site nav/footer renders
9
10
  // here.
@@ -22,46 +23,39 @@ export default function AuthLayout({ children, auth, response }: LayoutProps) {
22
23
  return null;
23
24
  }
24
25
  return (
25
- <div className="grid min-h-screen bg-white lg:grid-cols-2">
26
+ <div className="grid min-h-screen bg-white lg:grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)]">
26
27
  {/* Form side */}
27
- <div className="flex items-center justify-center px-6 py-12">
28
- <div className="w-full max-w-[400px] rounded-2xl border border-zinc-200/70 p-8">
29
- <Link href="/" className="inline-flex">
30
- <span className="flex size-9 items-center justify-center rounded-xl bg-zinc-900 text-base font-bold text-white">
31
- A
28
+ <div className="flex items-center justify-center px-4 py-12 sm:px-6">
29
+ <div className="w-full max-w-[380px]">
30
+ <Link href="/" className="inline-flex items-center gap-2">
31
+ <span className="flex size-8 items-center justify-center rounded-[9px] bg-zinc-950 text-[15px] font-bold text-white">
32
+ {siteConfig.brand.letter}
32
33
  </span>
34
+ <span className="text-[16px] font-semibold tracking-tight text-zinc-950">{siteConfig.brand.name}</span>
33
35
  </Link>
34
36
  {children}
35
37
  </div>
36
38
  </div>
37
39
 
38
- {/* Brand / testimonial side */}
39
- <div className="relative hidden flex-col justify-center bg-zinc-50 px-14 lg:flex">
40
- <div className="max-w-md">
41
- <div className="font-serif text-5xl leading-none text-zinc-300">
42
- &ldquo;
43
- </div>
44
- <blockquote className="mt-2 text-[1.6rem] font-medium leading-snug tracking-tight text-zinc-900">
45
- Acme gives our team one view of the work, from planning through
46
- launch.
47
- </blockquote>
48
- <div className="mt-8 flex items-center gap-3">
49
- <span className="flex size-10 items-center justify-center rounded-full bg-zinc-200 text-[13px] font-semibold text-zinc-500">
50
- MC
51
- </span>
52
- <div className="leading-tight">
53
- <div className="text-sm font-semibold text-zinc-900">
54
- Maya Chen
55
- </div>
56
- <div className="text-[13px] text-zinc-500">
57
- Head of Product, Northwind
58
- </div>
59
- </div>
40
+ {/* Product side: a capture of this app's own task board, cropped so it
41
+ runs off the right edge. */}
42
+ <div className="relative hidden overflow-hidden bg-zinc-50 lg:block">
43
+ <div
44
+ aria-hidden
45
+ className="absolute inset-0 bg-[radial-gradient(70%_60%_at_30%_20%,var(--brand-soft),transparent_70%)]"
46
+ />
47
+ <div className="relative flex h-full flex-col justify-center py-16 pl-14">
48
+ <p className="max-w-md text-[1.5rem] font-semibold leading-snug tracking-[-0.02em] text-zinc-950">
49
+ {siteConfig.hero.headline}
50
+ </p>
51
+ <div className="mt-10 w-[900px] rounded-l-[20px] bg-zinc-950/[0.035] py-2 pl-2 ring-1 ring-zinc-950/[0.06]">
52
+ <img
53
+ src="/screenshots/board.png"
54
+ alt="A project board in Acme"
55
+ className="block w-full rounded-l-[14px] shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_24px_48px_-24px_rgba(24,24,60,0.25)]"
56
+ />
60
57
  </div>
61
58
  </div>
62
- <p className="absolute bottom-8 left-14 text-[13px] text-zinc-400">
63
- Projects, docs, and automation. All in one place.
64
- </p>
65
59
  </div>
66
60
  </div>
67
61
  );
@@ -13,7 +13,7 @@ export const metadata: Metadata = {
13
13
  export default function LoginPage() {
14
14
  return (
15
15
  <>
16
- <h1 className="mt-5 text-[22px] font-semibold tracking-tight text-zinc-900">
16
+ <h1 className="mt-10 text-[26px] font-semibold tracking-[-0.025em] text-zinc-950">
17
17
  Welcome back
18
18
  </h1>
19
19
  <p className="mt-1 text-[13px] text-zinc-500">
@@ -14,7 +14,7 @@ export default function ResetPasswordPage({ searchParams }: PageProps) {
14
14
  const token = Array.isArray(raw) ? raw[0] ?? "" : raw ?? "";
15
15
  return (
16
16
  <>
17
- <h1 className="mt-5 text-[22px] font-semibold tracking-tight text-zinc-900">Choose a new password</h1>
17
+ <h1 className="mt-10 text-[26px] font-semibold tracking-[-0.025em] text-zinc-950">Choose a new password</h1>
18
18
  <p className="mt-1 text-[13px] text-zinc-500">You will be signed in on every device after this.</p>
19
19
  <div className="mt-6">
20
20
  <ResetPasswordForm token={token} />