@pylonsync/create-pylon 0.19.1 → 0.21.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.
- package/bin/create-pylon.js +30 -7
- package/package.json +1 -1
- package/templates/_root/AGENTS.md +1 -1
- package/templates/agency/.env.example +7 -0
- package/templates/agency/AGENTS.md +1 -1
- package/templates/agency/README.md +28 -3
- package/templates/agency/app/(marketing)/contact-form.tsx +20 -18
- package/templates/agency/app/(marketing)/page.tsx +3 -3
- package/templates/agency/app/(marketing)/work/page.tsx +3 -3
- package/templates/agency/app/auth-form.tsx +4 -4
- package/templates/agency/app/dashboard/dashboard-client.tsx +110 -64
- package/templates/agency/app/dashboard/page.tsx +14 -16
- package/templates/agency/app/icon.svg +3 -0
- package/templates/agency/app/login/page.tsx +12 -14
- package/templates/agency/components/marketing.tsx +12 -5
- package/templates/agency/functions/seedStudioBackoffice.ts +42 -11
- package/templates/agency/lib/agency.ts +22 -0
- package/templates/agency/lib/demo.ts +118 -0
- package/templates/agency/lib/site.config.ts +19 -13
- package/templates/agency/public/favicon.ico +0 -0
- package/templates/agency/tests/agency.test.ts +57 -0
- package/templates/ai-chat/.env.example +13 -27
- package/templates/ai-chat/AGENTS.md +1 -1
- package/templates/ai-chat/README.md +53 -60
- package/templates/ai-chat/app/(chat)/c/[id]/page.tsx +19 -0
- package/templates/ai-chat/app/(chat)/chat-client.tsx +657 -622
- package/templates/ai-chat/app/(chat)/layout.tsx +6 -43
- package/templates/ai-chat/app/(chat)/not-found.tsx +6 -10
- package/templates/ai-chat/app/(chat)/page.tsx +7 -11
- package/templates/ai-chat/app/auth-form.tsx +45 -38
- package/templates/ai-chat/app/error.tsx +9 -12
- package/templates/ai-chat/app/globals.css +464 -102
- package/templates/ai-chat/app/layout.tsx +11 -19
- package/templates/ai-chat/app/login/page.tsx +39 -24
- package/templates/ai-chat/app.ts +118 -43
- package/templates/ai-chat/components/chat/composer.tsx +193 -0
- package/templates/ai-chat/components/chat/empty-state.tsx +65 -0
- package/templates/ai-chat/components/chat/icons.tsx +198 -0
- package/templates/ai-chat/components/chat/markdown.tsx +164 -0
- package/templates/ai-chat/components/chat/message.tsx +183 -0
- package/templates/ai-chat/components/chat/setup.tsx +71 -0
- package/templates/ai-chat/components/chat/sidebar.tsx +288 -0
- package/templates/ai-chat/functions/_beginTurn.ts +145 -0
- package/templates/ai-chat/functions/_finishTurn.ts +55 -0
- package/templates/ai-chat/functions/assistantStatus.ts +13 -0
- package/templates/ai-chat/functions/claimGuestHistory.ts +39 -0
- package/templates/ai-chat/functions/createConversation.ts +28 -0
- package/templates/ai-chat/functions/deleteConversation.ts +15 -20
- package/templates/ai-chat/functions/prepareGuestClaim.ts +32 -0
- package/templates/ai-chat/functions/renameConversation.ts +19 -0
- package/templates/ai-chat/functions/respond.ts +88 -0
- package/templates/ai-chat/functions/seedExamples.ts +46 -0
- package/templates/ai-chat/functions/stopResponse.ts +30 -0
- package/templates/ai-chat/lib/brand-icon.ts +7 -0
- package/templates/ai-chat/lib/chat.ts +277 -0
- package/templates/ai-chat/lib/claim.ts +15 -0
- package/templates/ai-chat/lib/examples.ts +179 -0
- package/templates/ai-chat/lib/highlight.ts +107 -0
- package/templates/ai-chat/lib/markdown.ts +317 -0
- package/templates/ai-chat/lib/provider.ts +38 -0
- package/templates/ai-chat/lib/site.config.ts +55 -39
- package/templates/ai-chat/tests/chat.test.ts +174 -0
- package/templates/ai-chat/tests/components.test.tsx +57 -0
- package/templates/ai-chat/tests/markdown.test.ts +88 -0
- package/templates/ai-chat/tests/provider.test.ts +32 -0
- package/templates/ai-studio/AGENTS.md +1 -1
- package/templates/backend/consumer/apps/api/app/layout.tsx +33 -0
- package/templates/backend/consumer/apps/api/app/page.tsx +54 -0
- package/templates/backend/consumer/apps/api/app.ts +166 -0
- package/templates/backend/consumer/apps/api/functions/addComment.ts +24 -0
- package/templates/backend/consumer/apps/api/functions/createPost.ts +19 -36
- package/templates/backend/consumer/apps/api/functions/deletePost.ts +15 -13
- package/templates/backend/consumer/apps/api/functions/myProfile.ts +5 -8
- package/templates/backend/consumer/apps/api/functions/seedDemo.ts +95 -0
- package/templates/backend/consumer/apps/api/functions/setFollow.ts +36 -0
- package/templates/backend/consumer/apps/api/functions/setLike.ts +36 -0
- package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +43 -37
- package/templates/backend/consumer/apps/api/lib/seed.ts +89 -0
- package/templates/backend/consumer/apps/api/lib/social.ts +50 -0
- package/templates/backend/consumer/apps/api/package.json +8 -2
- package/templates/backend/consumer/apps/api/public/images/posts/arch.jpg +0 -0
- package/templates/backend/consumer/apps/api/public/images/posts/coffee.jpg +0 -0
- package/templates/backend/consumer/apps/api/public/images/posts/dog.jpg +0 -0
- package/templates/backend/consumer/apps/api/public/images/posts/lisbon.jpg +0 -0
- package/templates/backend/consumer/apps/api/public/images/posts/ramen.jpg +0 -0
- package/templates/backend/consumer/apps/api/public/images/posts/run.jpg +0 -0
- package/templates/backend/consumer/apps/api/public/images/posts/sunset.jpg +0 -0
- package/templates/backend/consumer/apps/api/tsconfig.json +12 -2
- package/templates/barebones/AGENTS.md +1 -1
- package/templates/chat/AGENTS.md +1 -1
- package/templates/chat/README.md +43 -21
- package/templates/chat/app/chat-client.tsx +495 -85
- package/templates/chat/app/error.tsx +1 -1
- package/templates/chat/app/globals.css +170 -54
- package/templates/chat/app/layout.tsx +12 -9
- package/templates/chat/app/not-found.tsx +4 -2
- package/templates/chat/app/page.tsx +9 -19
- package/templates/chat/app.ts +96 -17
- package/templates/chat/components/chat/avatar.tsx +45 -0
- package/templates/chat/components/chat/composer.tsx +110 -0
- package/templates/chat/components/chat/message-list.tsx +285 -0
- package/templates/chat/components/chat/sidebar.tsx +190 -0
- package/templates/chat/functions/joinChat.ts +83 -0
- package/templates/chat/functions/renameMember.ts +25 -0
- package/templates/chat/lib/chat.ts +188 -0
- package/templates/chat/lib/seed.ts +78 -0
- package/templates/chat/tests/chat.test.ts +159 -0
- package/templates/chat/tests/composer.test.tsx +54 -0
- package/templates/consumer/AGENTS.md +1 -1
- package/templates/consumer/README.md +3 -3
- package/templates/consumer/app/globals.css +10 -3
- package/templates/consumer/app/icon.svg +1 -0
- package/templates/consumer/components/social/avatar.tsx +25 -11
- package/templates/consumer/functions/seedFeed.ts +3 -3
- package/templates/consumer/lib/seed.ts +43 -85
- package/templates/consumer/public/favicon.ico +0 -0
- package/templates/consumer/public/images/posts/arch.jpg +0 -0
- package/templates/consumer/public/images/posts/coffee.jpg +0 -0
- package/templates/consumer/public/images/posts/dog.jpg +0 -0
- package/templates/consumer/public/images/posts/lisbon.jpg +0 -0
- package/templates/consumer/public/images/posts/ramen.jpg +0 -0
- package/templates/consumer/public/images/posts/run.jpg +0 -0
- package/templates/consumer/public/images/posts/sunset.jpg +0 -0
- package/templates/consumer/tests/seed.test.ts +39 -0
- package/templates/creator/.env.example +7 -0
- package/templates/creator/AGENTS.md +1 -1
- package/templates/creator/README.md +24 -5
- package/templates/creator/app/(marketing)/newsletter-signup.tsx +33 -12
- package/templates/creator/app/auth-form.tsx +7 -7
- package/templates/creator/app/dashboard/dashboard-client.tsx +137 -100
- package/templates/creator/app/dashboard/page.tsx +18 -18
- package/templates/creator/app/icon.svg +2 -0
- package/templates/creator/app/login/page.tsx +14 -14
- package/templates/creator/app.ts +4 -4
- package/templates/creator/functions/seedDemo.ts +32 -0
- package/templates/creator/functions/subscribe.ts +4 -15
- package/templates/creator/lib/demo.ts +94 -0
- package/templates/creator/lib/site.config.ts +15 -10
- package/templates/creator/lib/stats.ts +35 -0
- package/templates/creator/public/favicon.ico +0 -0
- package/templates/creator/tests/counter.test.ts +92 -0
- package/templates/creator/tests/live-count.test.tsx +45 -0
- package/templates/crm/AGENTS.md +1 -1
- package/templates/crm/README.md +22 -1
- package/templates/crm/app/apple-icon.png +0 -0
- package/templates/crm/app/companies/companies-view.tsx +32 -14
- package/templates/crm/app/companies/page.tsx +2 -1
- package/templates/crm/app/contacts/contacts-view.tsx +34 -10
- package/templates/crm/app/contacts/page.tsx +2 -1
- package/templates/crm/app/deals/[id]/deal-view.tsx +137 -98
- package/templates/crm/app/deals/[id]/page.tsx +2 -1
- package/templates/crm/app/globals.css +29 -14
- package/templates/crm/app/icon.svg +1 -0
- package/templates/crm/app/layout.tsx +7 -5
- package/templates/crm/app/page.tsx +2 -1
- package/templates/crm/app/pipeline-view.tsx +5 -5
- package/templates/crm/app/workspace.tsx +29 -17
- package/templates/crm/app.ts +5 -5
- package/templates/crm/components/activity-timeline.tsx +14 -4
- package/templates/crm/components/app-shell.tsx +94 -0
- package/templates/crm/components/auth-form.tsx +35 -5
- package/templates/crm/components/avatar.tsx +5 -1
- package/templates/crm/components/board-skeleton.tsx +5 -5
- package/templates/crm/components/brand-mark.tsx +25 -0
- package/templates/crm/components/command-palette.tsx +17 -17
- package/templates/crm/components/data-table.tsx +97 -36
- package/templates/crm/components/deal-card.tsx +23 -27
- package/templates/crm/components/filter-tabs.tsx +101 -0
- package/templates/crm/components/metrics-bar.tsx +6 -6
- package/templates/crm/components/nav.tsx +15 -0
- package/templates/crm/components/page-header.tsx +43 -7
- package/templates/crm/components/pipeline-board.tsx +16 -10
- package/templates/crm/components/require-auth.tsx +24 -26
- package/templates/crm/components/sidebar.tsx +54 -49
- package/templates/crm/components/stage-badge.tsx +4 -2
- package/templates/crm/components/ui/button.tsx +5 -4
- package/templates/crm/components/ui/dialog.tsx +1 -1
- package/templates/crm/components/ui/input.tsx +1 -1
- package/templates/crm/components/ui/select.tsx +1 -1
- package/templates/crm/components/ui/textarea.tsx +1 -1
- package/templates/crm/functions/seedWorkspace.ts +32 -17
- package/templates/crm/lib/brand.ts +9 -0
- package/templates/crm/lib/demo-team.ts +51 -0
- package/templates/crm/lib/pipeline.ts +37 -0
- package/templates/crm/lib/search.ts +4 -3
- package/templates/crm/lib/seed.ts +53 -8
- package/templates/crm/public/favicon.ico +0 -0
- package/templates/crm/tests/components.test.tsx +5 -5
- package/templates/crm/tests/demo-team.test.ts +18 -0
- package/templates/crm/tests/pipeline.test.ts +59 -3
- package/templates/directory/.env.example +7 -0
- package/templates/directory/AGENTS.md +1 -1
- package/templates/directory/README.md +22 -3
- package/templates/directory/app/(marketing)/directory-browse.tsx +30 -12
- package/templates/directory/app/(marketing)/page.tsx +6 -4
- package/templates/directory/app/auth-form.tsx +4 -4
- package/templates/directory/app/dashboard/dashboard-client.tsx +177 -90
- package/templates/directory/app/dashboard/page.tsx +16 -14
- package/templates/directory/app/icon.svg +4 -0
- package/templates/directory/app/login/page.tsx +16 -15
- package/templates/directory/functions/seedDemo.ts +43 -0
- package/templates/directory/lib/demo.ts +145 -0
- package/templates/directory/lib/directory.ts +27 -0
- package/templates/directory/lib/site.config.ts +21 -16
- package/templates/directory/public/favicon.ico +0 -0
- package/templates/directory/tests/directory.test.ts +46 -0
- package/templates/expo/consumer/apps/expo/App.tsx +360 -288
- package/templates/expo/consumer/apps/expo/app.json +9 -1
- package/templates/expo/consumer/apps/expo/package.json +2 -1
- package/templates/helpdesk/AGENTS.md +1 -1
- package/templates/helpdesk/README.md +21 -0
- package/templates/helpdesk/app/apple-icon.png +0 -0
- package/templates/helpdesk/app/customers/customers-view.tsx +23 -7
- package/templates/helpdesk/app/customers/page.tsx +2 -1
- package/templates/helpdesk/app/globals.css +29 -14
- package/templates/helpdesk/app/icon.svg +1 -0
- package/templates/helpdesk/app/inbox-view.tsx +66 -47
- package/templates/helpdesk/app/layout.tsx +7 -5
- package/templates/helpdesk/app/page.tsx +4 -7
- package/templates/helpdesk/app/tickets/[id]/page.tsx +2 -1
- package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +134 -97
- package/templates/helpdesk/app/workspace.tsx +33 -22
- package/templates/helpdesk/app.ts +2 -2
- package/templates/helpdesk/components/app-shell.tsx +94 -0
- package/templates/helpdesk/components/auth-form.tsx +35 -5
- package/templates/helpdesk/components/avatar.tsx +5 -1
- package/templates/helpdesk/components/brand-mark.tsx +28 -0
- package/templates/helpdesk/components/command-palette.tsx +17 -17
- package/templates/helpdesk/components/data-table.tsx +97 -36
- package/templates/helpdesk/components/filter-tabs.tsx +101 -0
- package/templates/helpdesk/components/nav.tsx +14 -0
- package/templates/helpdesk/components/page-header.tsx +43 -7
- package/templates/helpdesk/components/priority-badge.tsx +49 -6
- package/templates/helpdesk/components/require-auth.tsx +24 -26
- package/templates/helpdesk/components/sidebar.tsx +54 -48
- package/templates/helpdesk/components/sla-indicator.tsx +5 -8
- package/templates/helpdesk/components/thread.tsx +7 -7
- package/templates/helpdesk/components/ticket-list.tsx +79 -19
- package/templates/helpdesk/components/ui/button.tsx +5 -4
- package/templates/helpdesk/components/ui/dialog.tsx +1 -1
- package/templates/helpdesk/components/ui/input.tsx +1 -1
- package/templates/helpdesk/components/ui/select.tsx +1 -1
- package/templates/helpdesk/components/ui/textarea.tsx +1 -1
- package/templates/helpdesk/functions/seedWorkspace.ts +25 -15
- package/templates/helpdesk/lib/brand.ts +9 -0
- package/templates/helpdesk/lib/demo-team.ts +51 -0
- package/templates/helpdesk/lib/search.ts +4 -3
- package/templates/helpdesk/lib/seed.ts +43 -2
- package/templates/helpdesk/lib/tickets.ts +68 -20
- package/templates/helpdesk/public/favicon.ico +0 -0
- package/templates/helpdesk/tests/components.test.tsx +10 -0
- package/templates/helpdesk/tests/demo-team.test.ts +18 -0
- package/templates/helpdesk/tests/tickets.test.ts +70 -0
- package/templates/imessage/.env.example +49 -0
- package/templates/imessage/AGENTS.md +153 -0
- package/templates/imessage/CLAUDE.md +7 -0
- package/templates/imessage/README.md +187 -0
- package/templates/imessage/app/console.tsx +382 -0
- package/templates/imessage/app/contacts/page.tsx +13 -0
- package/templates/imessage/app/error.tsx +16 -0
- package/templates/imessage/app/gate.tsx +31 -0
- package/templates/imessage/app/globals.css +183 -0
- package/templates/imessage/app/layout.tsx +17 -0
- package/templates/imessage/app/login/login-form.tsx +115 -0
- package/templates/imessage/app/login/page.tsx +30 -0
- package/templates/imessage/app/not-found.tsx +13 -0
- package/templates/imessage/app/page.tsx +14 -0
- package/templates/imessage/app/robots.ts +6 -0
- package/templates/imessage/app/setup/page.tsx +13 -0
- package/templates/imessage/app/sign-out.tsx +20 -0
- package/templates/imessage/app.ts +299 -0
- package/templates/imessage/bunfig +4 -0
- package/templates/imessage/components/avatar.tsx +46 -0
- package/templates/imessage/components/contact-panel.tsx +263 -0
- package/templates/imessage/components/contacts-view.tsx +172 -0
- package/templates/imessage/components/conversation-list.tsx +129 -0
- package/templates/imessage/components/nav-rail.tsx +67 -0
- package/templates/imessage/components/setup-view.tsx +408 -0
- package/templates/imessage/components/thread.tsx +229 -0
- package/templates/imessage/components/ui/badge.tsx +37 -0
- package/templates/imessage/components/ui/button.tsx +56 -0
- package/templates/imessage/components/ui/card.tsx +90 -0
- package/templates/imessage/components/ui/dialog.tsx +110 -0
- package/templates/imessage/components/ui/input.tsx +30 -0
- package/templates/imessage/components/ui/label.tsx +21 -0
- package/templates/imessage/components/ui/select.tsx +39 -0
- package/templates/imessage/components/ui/table.tsx +124 -0
- package/templates/imessage/components/ui/textarea.tsx +24 -0
- package/templates/imessage/components.json +20 -0
- package/templates/imessage/functions/answerNow.ts +34 -0
- package/templates/imessage/functions/assistant.ts +96 -0
- package/templates/imessage/functions/beginTurn.ts +101 -0
- package/templates/imessage/functions/cancelReminder.ts +13 -0
- package/templates/imessage/functions/claimOutbound.ts +44 -0
- package/templates/imessage/functions/clearDemo.ts +18 -0
- package/templates/imessage/functions/deleteNote.ts +11 -0
- package/templates/imessage/functions/deliverOutbound.ts +55 -0
- package/templates/imessage/functions/finishTurn.ts +84 -0
- package/templates/imessage/functions/fireReminder.ts +42 -0
- package/templates/imessage/functions/ingestInbound.ts +22 -0
- package/templates/imessage/functions/ownerAccount.ts +13 -0
- package/templates/imessage/functions/processTurn.ts +74 -0
- package/templates/imessage/functions/relayApply.ts +102 -0
- package/templates/imessage/functions/relaySync.ts +63 -0
- package/templates/imessage/functions/releaseOutbound.ts +18 -0
- package/templates/imessage/functions/resolveTurn.ts +25 -0
- package/templates/imessage/functions/saveContact.ts +27 -0
- package/templates/imessage/functions/seedDemo.ts +70 -0
- package/templates/imessage/functions/sendOwnerMessage.ts +28 -0
- package/templates/imessage/functions/sendblueWebhook.ts +49 -0
- package/templates/imessage/functions/setContactStatus.ts +17 -0
- package/templates/imessage/functions/settleOutbound.ts +35 -0
- package/templates/imessage/functions/setupStatus.ts +33 -0
- package/templates/imessage/functions/sweep.ts +58 -0
- package/templates/imessage/functions/toolCancelReminder.ts +17 -0
- package/templates/imessage/functions/toolForgetNote.ts +15 -0
- package/templates/imessage/functions/toolListNotes.ts +16 -0
- package/templates/imessage/functions/toolListReminders.ts +17 -0
- package/templates/imessage/functions/toolSaveNote.ts +22 -0
- package/templates/imessage/functions/toolSetReminder.ts +55 -0
- package/templates/imessage/functions/updateSettings.ts +48 -0
- package/templates/imessage/gitignore +11 -0
- package/templates/imessage/lib/agent-call.ts +251 -0
- package/templates/imessage/lib/assistant.ts +74 -0
- package/templates/imessage/lib/chunk.ts +135 -0
- package/templates/imessage/lib/demo.ts +68 -0
- package/templates/imessage/lib/format.ts +170 -0
- package/templates/imessage/lib/gate.ts +104 -0
- package/templates/imessage/lib/handles.ts +67 -0
- package/templates/imessage/lib/provider.ts +38 -0
- package/templates/imessage/lib/relay-protocol.ts +95 -0
- package/templates/imessage/lib/reminders.ts +41 -0
- package/templates/imessage/lib/secrets.ts +59 -0
- package/templates/imessage/lib/sendblue.ts +173 -0
- package/templates/imessage/lib/store.ts +407 -0
- package/templates/imessage/lib/transport.ts +135 -0
- package/templates/imessage/lib/utils.ts +10 -0
- package/templates/imessage/package.json +40 -0
- package/templates/imessage/relay/.env.example +14 -0
- package/templates/imessage/relay/applescript.ts +70 -0
- package/templates/imessage/relay/chatdb.ts +165 -0
- package/templates/imessage/relay/client.ts +41 -0
- package/templates/imessage/relay/main.ts +137 -0
- package/templates/imessage/relay/state.ts +35 -0
- package/templates/imessage/tests/agent-call.test.ts +112 -0
- package/templates/imessage/tests/applescript.test.ts +88 -0
- package/templates/imessage/tests/chatdb.test.ts +139 -0
- package/templates/imessage/tests/chunk.test.ts +61 -0
- package/templates/imessage/tests/fake-ctx.ts +88 -0
- package/templates/imessage/tests/ingest.test.ts +141 -0
- package/templates/imessage/tests/lib.test.ts +99 -0
- package/templates/imessage/tests/review-fixes.test.ts +138 -0
- package/templates/imessage/tests/secrets.test.ts +36 -0
- package/templates/imessage/tests/sendblue.test.ts +143 -0
- package/templates/imessage/tests/setup.ts +5 -0
- package/templates/imessage/tsconfig.json +18 -0
- package/templates/inventory/AGENTS.md +1 -1
- package/templates/inventory/README.md +21 -0
- package/templates/inventory/app/apple-icon.png +0 -0
- package/templates/inventory/app/globals.css +29 -14
- package/templates/inventory/app/icon.svg +1 -0
- package/templates/inventory/app/layout.tsx +7 -5
- package/templates/inventory/app/movements/movements-view.tsx +32 -10
- package/templates/inventory/app/movements/page.tsx +2 -1
- package/templates/inventory/app/page.tsx +4 -6
- package/templates/inventory/app/products/[id]/page.tsx +2 -1
- package/templates/inventory/app/products/[id]/product-view.tsx +55 -25
- package/templates/inventory/app/products-view.tsx +74 -33
- package/templates/inventory/app/workspace.tsx +34 -16
- package/templates/inventory/app.ts +10 -2
- package/templates/inventory/components/app-shell.tsx +94 -0
- package/templates/inventory/components/auth-form.tsx +35 -5
- package/templates/inventory/components/avatar.tsx +5 -1
- package/templates/inventory/components/brand-mark.tsx +25 -0
- package/templates/inventory/components/category-tile.tsx +49 -0
- package/templates/inventory/components/command-palette.tsx +17 -17
- package/templates/inventory/components/data-table.tsx +97 -36
- package/templates/inventory/components/filter-tabs.tsx +101 -0
- package/templates/inventory/components/nav.tsx +14 -0
- package/templates/inventory/components/page-header.tsx +43 -7
- package/templates/inventory/components/require-auth.tsx +24 -26
- package/templates/inventory/components/sidebar.tsx +54 -48
- package/templates/inventory/components/stock-level.tsx +30 -14
- package/templates/inventory/components/summary-bar.tsx +8 -15
- package/templates/inventory/components/ui/button.tsx +5 -4
- package/templates/inventory/components/ui/dialog.tsx +1 -1
- package/templates/inventory/components/ui/input.tsx +1 -1
- package/templates/inventory/components/ui/select.tsx +1 -1
- package/templates/inventory/components/ui/textarea.tsx +1 -1
- package/templates/inventory/functions/seedWorkspace.ts +18 -8
- package/templates/inventory/lib/brand.ts +9 -0
- package/templates/inventory/lib/demo-team.ts +51 -0
- package/templates/inventory/lib/search.ts +4 -3
- package/templates/inventory/lib/seed.ts +29 -2
- package/templates/inventory/lib/stock.ts +32 -0
- package/templates/inventory/public/favicon.ico +0 -0
- package/templates/inventory/tests/demo-team.test.ts +18 -0
- package/templates/inventory/tests/stock.test.ts +29 -0
- package/templates/invoices/AGENTS.md +1 -1
- package/templates/invoices/README.md +16 -0
- package/templates/invoices/app/apple-icon.png +0 -0
- package/templates/invoices/app/clients/clients-view.tsx +18 -6
- package/templates/invoices/app/clients/page.tsx +2 -1
- package/templates/invoices/app/globals.css +29 -14
- package/templates/invoices/app/icon.svg +1 -0
- package/templates/invoices/app/invoices/[id]/invoice-view.tsx +72 -48
- package/templates/invoices/app/invoices/[id]/page.tsx +2 -1
- package/templates/invoices/app/invoices-view.tsx +81 -32
- package/templates/invoices/app/layout.tsx +7 -5
- package/templates/invoices/app/page.tsx +4 -6
- package/templates/invoices/app/workspace.tsx +26 -16
- package/templates/invoices/app.ts +2 -2
- package/templates/invoices/components/app-shell.tsx +94 -0
- package/templates/invoices/components/auth-form.tsx +35 -5
- package/templates/invoices/components/avatar.tsx +5 -1
- package/templates/invoices/components/brand-mark.tsx +26 -0
- package/templates/invoices/components/command-palette.tsx +17 -17
- package/templates/invoices/components/data-table.tsx +97 -36
- package/templates/invoices/components/filter-tabs.tsx +101 -0
- package/templates/invoices/components/line-items.tsx +19 -14
- package/templates/invoices/components/nav.tsx +14 -0
- package/templates/invoices/components/page-header.tsx +43 -7
- package/templates/invoices/components/require-auth.tsx +24 -26
- package/templates/invoices/components/sidebar.tsx +54 -48
- package/templates/invoices/components/status-badge.tsx +7 -7
- package/templates/invoices/components/summary-bar.tsx +5 -5
- package/templates/invoices/components/ui/button.tsx +5 -4
- package/templates/invoices/components/ui/dialog.tsx +1 -1
- package/templates/invoices/components/ui/input.tsx +1 -1
- package/templates/invoices/components/ui/select.tsx +1 -1
- package/templates/invoices/components/ui/textarea.tsx +1 -1
- package/templates/invoices/functions/recordPayment.ts +3 -0
- package/templates/invoices/lib/billing.ts +40 -0
- package/templates/invoices/lib/brand.ts +9 -0
- package/templates/invoices/lib/search.ts +4 -3
- package/templates/invoices/public/favicon.ico +0 -0
- package/templates/invoices/tests/billing.test.ts +27 -0
- package/templates/ios/consumer/apps/ios/Package.swift +4 -3
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ActivityView.swift +107 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AppModel.swift +171 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/AuthView.swift +182 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/CommentsView.swift +136 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ComposeView.swift +152 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ExploreView.swift +117 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +175 -159
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/MainTabView.swift +54 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Media.swift +271 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +101 -4
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/PostCard.swift +245 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileEditorView.swift +208 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileView.swift +183 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +25 -31
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/SocialStore.swift +285 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/StoryViewer.swift +155 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Theme.swift +82 -0
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +4 -47
- package/templates/ios/consumer/apps/ios/project.yml +13 -9
- package/templates/local-service/.env.example +7 -0
- package/templates/local-service/AGENTS.md +1 -1
- package/templates/local-service/README.md +26 -2
- package/templates/local-service/app/(marketing)/booking-widget.tsx +48 -9
- package/templates/local-service/app/(marketing)/page.tsx +66 -42
- package/templates/local-service/app/auth-form.tsx +8 -8
- package/templates/local-service/app/dashboard/dashboard-client.tsx +119 -92
- package/templates/local-service/app/dashboard/page.tsx +14 -14
- package/templates/local-service/app/icon.svg +2 -0
- package/templates/local-service/app/login/page.tsx +15 -15
- package/templates/local-service/functions/seedDemo.ts +49 -0
- package/templates/local-service/lib/demo.ts +135 -0
- package/templates/local-service/lib/hours.ts +44 -0
- package/templates/local-service/lib/site.config.ts +8 -3
- package/templates/local-service/lib/slots.ts +3 -0
- package/templates/local-service/public/favicon.ico +0 -0
- package/templates/local-service/tests/booking.test.ts +104 -0
- package/templates/marketplace/AGENTS.md +1 -1
- package/templates/marketplace/README.md +23 -15
- package/templates/marketplace/app/globals.css +172 -114
- package/templates/marketplace/app/layout.tsx +75 -30
- package/templates/marketplace/app/listing/[id]/page.tsx +210 -123
- package/templates/marketplace/app/me/page.tsx +4 -4
- package/templates/marketplace/app/page.tsx +221 -298
- package/templates/marketplace/app/sell/page.tsx +8 -11
- package/templates/marketplace/app.ts +32 -7
- package/templates/marketplace/client/ActivityFeed.tsx +311 -0
- package/templates/marketplace/client/AuthNav.tsx +24 -16
- package/templates/marketplace/client/ListingCard.tsx +118 -0
- package/templates/marketplace/client/LiveGrid.tsx +92 -0
- package/templates/marketplace/client/LiveListingStatus.tsx +32 -24
- package/templates/marketplace/client/LoginCard.tsx +114 -136
- package/templates/marketplace/client/MarketProvider.tsx +3 -5
- package/templates/marketplace/client/MyMarket.tsx +371 -185
- package/templates/marketplace/client/OfferPanel.tsx +421 -287
- package/templates/marketplace/client/SellForm.tsx +271 -192
- package/templates/marketplace/client/ThemeToggle.tsx +1 -0
- package/templates/marketplace/client/WatchButton.tsx +16 -16
- package/templates/marketplace/client/market.ts +61 -14
- package/templates/marketplace/functions/seedMarket.ts +143 -36
- package/templates/marketplace/functions/seedOffers.ts +101 -0
- package/templates/marketplace/lib/catalog.ts +72 -1
- package/templates/marketplace/public/images/listings/ceramic-lamp-640.webp +0 -0
- package/templates/marketplace/public/images/listings/ceramic-lamp.webp +0 -0
- package/templates/marketplace/public/images/listings/duffel-640.webp +0 -0
- package/templates/marketplace/public/images/listings/duffel.webp +0 -0
- package/templates/marketplace/public/images/listings/film-camera-640.webp +0 -0
- package/templates/marketplace/public/images/listings/film-camera.webp +0 -0
- package/templates/marketplace/public/images/listings/lounge-chair-640.webp +0 -0
- package/templates/marketplace/public/images/listings/lounge-chair.webp +0 -0
- package/templates/marketplace/public/images/listings/road-bike-640.webp +0 -0
- package/templates/marketplace/public/images/listings/road-bike.webp +0 -0
- package/templates/marketplace/public/images/listings/slipper-chair-640.webp +0 -0
- package/templates/marketplace/public/images/listings/slipper-chair.webp +0 -0
- package/templates/marketplace/public/images/listings/turntable-640.webp +0 -0
- package/templates/marketplace/public/images/listings/turntable.webp +0 -0
- package/templates/marketplace/public/images/listings/wardrobe-640.webp +0 -0
- package/templates/marketplace/public/images/listings/wardrobe.webp +0 -0
- package/templates/marketplace/public/images/listings/watch-640.webp +0 -0
- package/templates/marketplace/public/images/listings/watch.webp +0 -0
- package/templates/marketplace/public/images/listings/weekender-bag-640.webp +0 -0
- package/templates/marketplace/public/images/listings/weekender-bag.webp +0 -0
- package/templates/marketplace/tests/example.test.ts +64 -2
- package/templates/marketplace/tests/listing-card.test.tsx +61 -0
- package/templates/projects/AGENTS.md +1 -1
- package/templates/projects/README.md +24 -3
- package/templates/projects/app/apple-icon.png +0 -0
- package/templates/projects/app/clients/clients-view.tsx +20 -6
- package/templates/projects/app/clients/page.tsx +2 -1
- package/templates/projects/app/globals.css +29 -14
- package/templates/projects/app/icon.svg +1 -0
- package/templates/projects/app/layout.tsx +7 -5
- package/templates/projects/app/page.tsx +3 -5
- package/templates/projects/app/projects/[id]/page.tsx +2 -1
- package/templates/projects/app/projects/[id]/project-view.tsx +45 -30
- package/templates/projects/app/projects-view.tsx +105 -52
- package/templates/projects/app/workspace.tsx +41 -20
- package/templates/projects/app.ts +2 -2
- package/templates/projects/components/app-shell.tsx +94 -0
- package/templates/projects/components/auth-form.tsx +35 -5
- package/templates/projects/components/avatar.tsx +5 -1
- package/templates/projects/components/board-skeleton.tsx +3 -3
- package/templates/projects/components/brand-mark.tsx +25 -0
- package/templates/projects/components/command-palette.tsx +17 -17
- package/templates/projects/components/data-table.tsx +97 -36
- package/templates/projects/components/filter-tabs.tsx +101 -0
- package/templates/projects/components/meter.tsx +48 -0
- package/templates/projects/components/nav.tsx +14 -0
- package/templates/projects/components/page-header.tsx +43 -7
- package/templates/projects/components/require-auth.tsx +24 -26
- package/templates/projects/components/sidebar.tsx +54 -48
- package/templates/projects/components/summary-bar.tsx +49 -0
- package/templates/projects/components/task-board.tsx +10 -9
- package/templates/projects/components/time-dialog.tsx +29 -5
- package/templates/projects/components/ui/button.tsx +5 -4
- package/templates/projects/components/ui/dialog.tsx +1 -1
- package/templates/projects/components/ui/input.tsx +1 -1
- package/templates/projects/components/ui/select.tsx +1 -1
- package/templates/projects/components/ui/textarea.tsx +1 -1
- package/templates/projects/functions/seedWorkspace.ts +26 -12
- package/templates/projects/lib/brand.ts +9 -0
- package/templates/projects/lib/demo-team.ts +51 -0
- package/templates/projects/lib/search.ts +4 -3
- package/templates/projects/lib/seed.ts +35 -1
- package/templates/projects/lib/work.ts +63 -0
- package/templates/projects/public/favicon.ico +0 -0
- package/templates/projects/tests/demo-team.test.ts +18 -0
- package/templates/projects/tests/work.test.ts +65 -0
- package/templates/restaurant/.env.example +7 -0
- package/templates/restaurant/AGENTS.md +1 -1
- package/templates/restaurant/README.md +29 -3
- package/templates/restaurant/app/(marketing)/layout.tsx +4 -2
- package/templates/restaurant/app/(marketing)/page.tsx +47 -23
- package/templates/restaurant/app/(marketing)/reservation-widget.tsx +37 -7
- package/templates/restaurant/app/auth-form.tsx +8 -8
- package/templates/restaurant/app/dashboard/dashboard-client.tsx +241 -109
- package/templates/restaurant/app/dashboard/page.tsx +14 -14
- package/templates/restaurant/app/icon.svg +4 -0
- package/templates/restaurant/app/login/page.tsx +15 -15
- package/templates/restaurant/functions/seedDemo.ts +44 -0
- package/templates/restaurant/lib/demo.ts +144 -0
- package/templates/restaurant/lib/hours.ts +45 -0
- package/templates/restaurant/lib/site.config.ts +3 -3
- package/templates/restaurant/public/favicon.ico +0 -0
- package/templates/restaurant/tests/reservations.test.ts +67 -0
- package/templates/saas/AGENTS.md +1 -1
- package/templates/saas/README.md +54 -35
- package/templates/saas/app/(auth)/auth-form.tsx +25 -23
- package/templates/saas/app/(auth)/forgot-password/page.tsx +1 -1
- package/templates/saas/app/(auth)/layout.tsx +27 -33
- package/templates/saas/app/(auth)/login/page.tsx +1 -1
- package/templates/saas/app/(auth)/reset-password/page.tsx +1 -1
- package/templates/saas/app/(auth)/signup/page.tsx +1 -1
- package/templates/saas/app/(marketing)/layout.tsx +127 -253
- package/templates/saas/app/(marketing)/page.tsx +133 -372
- package/templates/saas/app/(marketing)/pricing/page.tsx +21 -19
- package/templates/saas/app/(marketing)/products/[slug]/page.tsx +40 -95
- package/templates/saas/app/dashboard/board-client.tsx +416 -0
- package/templates/saas/app/dashboard/dashboard-client.tsx +30 -615
- package/templates/saas/app/dashboard/layout.tsx +27 -13
- package/templates/saas/app/dashboard/overview-client.tsx +332 -0
- package/templates/saas/app/dashboard/page.tsx +29 -35
- package/templates/saas/app/dashboard/projects/[id]/page.tsx +29 -0
- package/templates/saas/app/dashboard/projects/page.tsx +11 -7
- package/templates/saas/app/dashboard/projects-client.tsx +398 -0
- package/templates/saas/app/globals.css +5 -4
- package/templates/saas/app/icon.svg +1 -0
- package/templates/saas/app/onboarding/onboarding-client.tsx +93 -26
- package/templates/saas/app/onboarding/page.tsx +6 -2
- package/templates/saas/app.ts +68 -13
- package/templates/saas/components/dashboard-shell.tsx +212 -132
- package/templates/saas/components/marketing.tsx +137 -274
- package/templates/saas/components/pricing-table.tsx +12 -11
- package/templates/saas/components/setup-checklist.tsx +31 -29
- package/templates/saas/components/task-ui.tsx +199 -0
- package/templates/saas/functions/createProject.ts +7 -13
- package/templates/saas/functions/createTask.ts +67 -0
- package/templates/saas/functions/deleteProject.ts +17 -0
- package/templates/saas/functions/deleteTask.ts +14 -0
- package/templates/saas/functions/seedWorkspace.ts +46 -0
- package/templates/saas/functions/setProjectStatus.ts +41 -0
- package/templates/saas/functions/updateProfile.ts +13 -0
- package/templates/saas/functions/updateProject.ts +4 -5
- package/templates/saas/functions/updateTask.ts +88 -0
- package/templates/saas/lib/members.ts +53 -0
- package/templates/saas/lib/names.ts +53 -0
- package/templates/saas/lib/plans.ts +11 -0
- package/templates/saas/lib/sample-workspace.ts +117 -0
- package/templates/saas/lib/site.config.ts +200 -463
- package/templates/saas/lib/tasks.ts +101 -0
- package/templates/saas/lib/use-live.ts +25 -0
- package/templates/saas/public/favicon.ico +0 -0
- package/templates/saas/public/screenshots/board.png +0 -0
- package/templates/saas/public/screenshots/members.png +0 -0
- package/templates/saas/public/screenshots/overview.png +0 -0
- package/templates/saas/public/screenshots/projects.png +0 -0
- package/templates/saas/scripts/capture-screenshots.mjs +156 -0
- package/templates/saas/tests/names.test.ts +35 -0
- package/templates/saas/tests/plans.test.ts +12 -0
- package/templates/saas/tests/sample-workspace.test.ts +46 -0
- package/templates/saas/tests/tasks.test.ts +81 -0
- package/templates/shop/AGENTS.md +1 -1
- package/templates/shop/README.md +25 -13
- package/templates/shop/app/(marketing)/layout.tsx +94 -67
- package/templates/shop/app/(marketing)/not-found.tsx +13 -12
- package/templates/shop/app/(marketing)/page.tsx +120 -59
- package/templates/shop/app/(marketing)/shop-client.tsx +364 -237
- package/templates/shop/app/(marketing)/success/page.tsx +75 -22
- package/templates/shop/app/globals.css +94 -1
- package/templates/shop/app/layout.tsx +4 -4
- package/templates/shop/app/login/page.tsx +4 -4
- package/templates/shop/app.ts +18 -6
- package/templates/shop/components/cart-button.tsx +33 -0
- package/templates/shop/components/marketing.tsx +17 -87
- package/templates/shop/components/shelf-count.tsx +36 -0
- package/templates/shop/functions/checkout.ts +17 -1
- package/templates/shop/lib/cart-store.ts +57 -0
- package/templates/shop/lib/shelf-store.ts +35 -0
- package/templates/shop/lib/shop.ts +27 -0
- package/templates/shop/lib/site.config.ts +108 -98
- package/templates/shop/public/images/hero.jpg +0 -0
- package/templates/shop/public/images/products/cedar-smoke.jpg +0 -0
- package/templates/shop/public/images/products/fig-leaf.jpg +0 -0
- package/templates/shop/public/images/products/linen.jpg +0 -0
- package/templates/shop/public/images/products/sea-salt.jpg +0 -0
- package/templates/shop/public/images/products/tobacco-oak.jpg +0 -0
- package/templates/shop/tests/shop.test.ts +64 -0
- package/templates/todo/AGENTS.md +1 -1
- package/templates/waitlist/.env.example +7 -0
- package/templates/waitlist/AGENTS.md +1 -1
- package/templates/waitlist/README.md +29 -13
- package/templates/waitlist/app/(marketing)/page.tsx +9 -7
- package/templates/waitlist/app/(marketing)/waitlist-hero.tsx +64 -31
- package/templates/waitlist/app/auth-form.tsx +8 -8
- package/templates/waitlist/app/dashboard/dashboard-client.tsx +132 -95
- package/templates/waitlist/app/dashboard/page.tsx +17 -16
- package/templates/waitlist/app/icon.svg +2 -0
- package/templates/waitlist/app/login/page.tsx +14 -14
- package/templates/waitlist/app.ts +6 -6
- package/templates/waitlist/functions/joinWaitlist.ts +6 -18
- package/templates/waitlist/functions/seedDemo.ts +32 -0
- package/templates/waitlist/lib/demo.ts +94 -0
- package/templates/waitlist/lib/launch.ts +19 -0
- package/templates/waitlist/lib/site.config.ts +14 -9
- package/templates/waitlist/lib/stats.ts +35 -0
- package/templates/waitlist/public/favicon.ico +0 -0
- package/templates/waitlist/tests/counter.test.ts +107 -0
- package/templates/waitlist/tests/live-count.test.tsx +48 -0
- package/templates/ai-chat/tests/example.test.ts +0 -12
- package/templates/backend/consumer/apps/api/functions/feed.ts +0 -57
- package/templates/backend/consumer/apps/api/functions/profilePosts.ts +0 -17
- package/templates/backend/consumer/apps/api/functions/toggleLike.ts +0 -48
- package/templates/backend/consumer/apps/api/schema.ts +0 -143
- package/templates/chat/functions/_keep.ts +0 -13
- package/templates/chat/tests/example.test.ts +0 -12
- package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +0 -68
- package/templates/marketplace/client/LiveTicker.tsx +0 -116
- package/templates/marketplace/client/ScrollToListingsLink.tsx +0 -29
- package/templates/marketplace/public/images/marketplace-hero.webp +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// Client-side search powering the ⌘K palette.
|
|
2
2
|
//
|
|
3
|
-
// A small team's
|
|
3
|
+
// A small team's workspace fits in the synced replica, so search runs locally against
|
|
4
4
|
// data already in memory: results appear as you type, with no request per
|
|
5
5
|
// keystroke. Swap in `db.useSearch` (Pylon's FTS) when a workspace outgrows
|
|
6
6
|
// that — the component boundary doesn't change, only this file.
|
|
7
7
|
|
|
8
8
|
export interface SearchItem {
|
|
9
9
|
id: string;
|
|
10
|
-
|
|
10
|
+
/** The record kind, such as "deal". The palette picks its icon from this. */
|
|
11
|
+
type: string;
|
|
11
12
|
title: string;
|
|
12
|
-
/**
|
|
13
|
+
/** Shown on the right of the row: the company for a contact, say. */
|
|
13
14
|
subtitle?: string;
|
|
14
15
|
href: string;
|
|
15
16
|
/** Extra text matched but not displayed — an email, say. */
|
|
@@ -198,9 +198,35 @@ export const SEED_MOVEMENTS: SeedMovement[] = (() => {
|
|
|
198
198
|
return SEED_PRODUCTS.flatMap((p) => ledgerFor(p, purchaseOrder));
|
|
199
199
|
})();
|
|
200
200
|
|
|
201
|
+
/**
|
|
202
|
+
* The demo stock team. Each becomes a User row with no
|
|
203
|
+
* password (see lib/demo-team.ts); they exist so the ledger shows who moved stock.
|
|
204
|
+
*/
|
|
205
|
+
export interface SeedTeammate {
|
|
206
|
+
name: string;
|
|
207
|
+
email: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const SEED_TEAM: SeedTeammate[] = [
|
|
211
|
+
{ name: "Maya Castillo", email: "maya.castillo@demo.invalid" },
|
|
212
|
+
{ name: "Jordan Ellis", email: "jordan.ellis@demo.invalid" },
|
|
213
|
+
{ name: "Aisha Karim", email: "aisha.karim@demo.invalid" },
|
|
214
|
+
{ name: "Noah Brandt", email: "noah.brandt@demo.invalid" },
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Who recorded the seeded movement at `index`, as a position in
|
|
219
|
+
* `[you, ...SEED_TEAM]`. Stepping by 3 through a team of 5 spreads the ledger
|
|
220
|
+
* across everyone.
|
|
221
|
+
*/
|
|
222
|
+
export function seedActor(index: number, teamSize: number = SEED_TEAM.length + 1): number {
|
|
223
|
+
return (index * 3 + 1) % teamSize;
|
|
224
|
+
}
|
|
225
|
+
|
|
201
226
|
export interface ShapedSeed {
|
|
202
227
|
products: Array<{ key: string; row: Record<string, unknown> }>;
|
|
203
|
-
|
|
228
|
+
/** `actor` is a position in `[you, ...SEED_TEAM]`; see seedActor. */
|
|
229
|
+
movements: Array<{ product: string; actor: number; row: Record<string, unknown> }>;
|
|
204
230
|
}
|
|
205
231
|
|
|
206
232
|
export function shapeSeed(now: number = Date.now()): ShapedSeed {
|
|
@@ -220,8 +246,9 @@ export function shapeSeed(now: number = Date.now()): ShapedSeed {
|
|
|
220
246
|
createdAt: daysAgo(120 - index),
|
|
221
247
|
},
|
|
222
248
|
})),
|
|
223
|
-
movements: SEED_MOVEMENTS.map((m) => ({
|
|
249
|
+
movements: SEED_MOVEMENTS.map((m, index) => ({
|
|
224
250
|
product: m.product,
|
|
251
|
+
actor: seedActor(index),
|
|
225
252
|
row: {
|
|
226
253
|
delta: m.delta,
|
|
227
254
|
reason: m.reason,
|
|
@@ -119,6 +119,38 @@ export function stockState(quantity: number, reorderPoint?: number | null): Stoc
|
|
|
119
119
|
return "ok";
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
export interface StockTab {
|
|
123
|
+
id: string;
|
|
124
|
+
label: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The product list's tabs, left to right. */
|
|
128
|
+
export const STOCK_TABS: StockTab[] = [
|
|
129
|
+
{ id: "all", label: "All" },
|
|
130
|
+
{ id: "reorder", label: "Needs reorder" },
|
|
131
|
+
{ id: "out", label: "Out of stock" },
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
/** Whether an active product at `state` belongs on a tab. */
|
|
135
|
+
export function inStockTab(tabId: string, state: StockState): boolean {
|
|
136
|
+
if (tabId === "reorder") return state !== "ok";
|
|
137
|
+
if (tabId === "out") return state === "out";
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* How many active products each tab lists. The sidebar badge, the tab counts,
|
|
143
|
+
* and the summary bar all read `summarize`, and this agrees with it by
|
|
144
|
+
* construction: all = skuCount, reorder = low + out, out = outCount.
|
|
145
|
+
*/
|
|
146
|
+
export function stockTabCounts(summary: Summary): Record<string, number> {
|
|
147
|
+
return {
|
|
148
|
+
all: summary.skuCount,
|
|
149
|
+
reorder: summary.lowCount + summary.outCount,
|
|
150
|
+
out: summary.outCount,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
122
154
|
export interface Summary {
|
|
123
155
|
skuCount: number;
|
|
124
156
|
unitCount: number;
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { demoEmail, isDemoRow } from "../lib/demo-team";
|
|
3
|
+
|
|
4
|
+
describe("demo teammates", () => {
|
|
5
|
+
test("the first address is the person's own, then numbered aliases", () => {
|
|
6
|
+
expect(demoEmail("maya.castillo@demo.invalid", 0)).toBe("maya.castillo@demo.invalid");
|
|
7
|
+
expect(demoEmail("maya.castillo@demo.invalid", 1)).toBe("maya.castillo+2@demo.invalid");
|
|
8
|
+
expect(demoEmail("maya.castillo@demo.invalid", 2)).toBe("maya.castillo+3@demo.invalid");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("a registered account is never reused as a demo teammate", () => {
|
|
12
|
+
// Someone who registered a demo address first must not be handed the
|
|
13
|
+
// seeded records.
|
|
14
|
+
expect(isDemoRow({ email: "x@demo.invalid", passwordHash: "argon2id$..." })).toBe(false);
|
|
15
|
+
expect(isDemoRow({ email: "x@demo.invalid", emailVerified: "2026-07-01T00:00:00Z" })).toBe(false);
|
|
16
|
+
expect(isDemoRow({ email: "x@demo.invalid", displayName: "Maya Castillo" })).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
REASONS,
|
|
4
|
+
STOCK_TABS,
|
|
5
|
+
inStockTab,
|
|
4
6
|
isValidReason,
|
|
5
7
|
money,
|
|
6
8
|
needsReorder,
|
|
@@ -11,6 +13,7 @@ import {
|
|
|
11
13
|
reasonAllows,
|
|
12
14
|
signedQuantity,
|
|
13
15
|
stockState,
|
|
16
|
+
stockTabCounts,
|
|
14
17
|
summarize,
|
|
15
18
|
type Movement,
|
|
16
19
|
type Product,
|
|
@@ -253,3 +256,29 @@ describe("parsing", () => {
|
|
|
253
256
|
expect(parseCount("")).toBeNull();
|
|
254
257
|
});
|
|
255
258
|
});
|
|
259
|
+
|
|
260
|
+
describe("stock tabs", () => {
|
|
261
|
+
const products = [
|
|
262
|
+
product({ id: "a", reorderPoint: 5 }),
|
|
263
|
+
product({ id: "b", reorderPoint: 5 }),
|
|
264
|
+
product({ id: "c", reorderPoint: 5 }),
|
|
265
|
+
product({ id: "d", reorderPoint: 5, archived: true }),
|
|
266
|
+
];
|
|
267
|
+
const ledger = [
|
|
268
|
+
move({ id: "1", productId: "a", delta: 20 }),
|
|
269
|
+
move({ id: "2", productId: "b", delta: 3 }),
|
|
270
|
+
move({ id: "3", productId: "d", delta: 1 }),
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
test("each tab's count is exactly the rows it lists", () => {
|
|
274
|
+
const counts = stockTabCounts(summarize(products, ledger));
|
|
275
|
+
const levels = onHandByProduct(ledger);
|
|
276
|
+
for (const tab of STOCK_TABS) {
|
|
277
|
+
const listed = products.filter(
|
|
278
|
+
(p) => !p.archived && inStockTab(tab.id, stockState(levels.get(p.id) ?? 0, p.reorderPoint)),
|
|
279
|
+
);
|
|
280
|
+
expect(counts[tab.id]).toBe(listed.length);
|
|
281
|
+
}
|
|
282
|
+
expect(counts).toEqual({ all: 3, reorder: 2, out: 1 });
|
|
283
|
+
});
|
|
284
|
+
});
|
|
@@ -62,7 +62,7 @@ this pass before the user has to ask.
|
|
|
62
62
|
- **`serverData` (SSR) is READ-ONLY.** No write methods; the runtime rejects write frames (`SSR_WRITE_FORBIDDEN`). Mutations belong in actions/functions, never in a page render.
|
|
63
63
|
- **`response.*` / `response.redirect()` / `response.notFound()` must fire in the synchronous shell render**, before any `await` / `<Suspense>`. The HTTP head commits when the shell is ready; status/headers/cookies set from a suspended subtree are lost, and `redirect`/`notFound` thrown below a Suspense boundary are swallowed.
|
|
64
64
|
- **`ctx.llm`, `ctx.rooms`, and `ctx.connections` are on mutation + action only, NOT query** (reactive purity). `action` has no direct `ctx.db`; use `ctx.runQuery` / `ctx.runMutation`.
|
|
65
|
-
- **`ctx.llm.stream(request, onEvent)` streams tokens as they generate** and still resolves with the full response, so `stop_reason === "tool_use"` drives an agent tool loop. Events are `text_delta` / `tool_use_start` / `tool_input_delta` / `done`. Same auth + model-allowlist gating as `ctx.llm.complete`.
|
|
65
|
+
- **`ctx.llm.stream(request, onEvent)` streams tokens as they generate** and still resolves with the full response, so `stop_reason === "tool_use"` drives an agent tool loop. Events are `text_delta` / `tool_use_start` / `tool_input_delta` / `done`. Same auth + model-allowlist gating as `ctx.llm.complete`. The call timeout is an idle timeout (`PYLON_FN_CALL_TIMEOUT`, 30s default; Stack0 Cloud 300s): each stream event resets it, and the total run is capped at 10× the timeout. Set `timeout: <secs>` on the def when a step (a slow tool call) can be silent longer than that.
|
|
66
66
|
- **`ctx.stream.write(text)` streams to the caller — and every fn stream is RESUMABLE**: the server buffers frames under a stream id (`streamFn`'s `onStreamId`), so a dropped connection or closed tab catches up via `resumeStream(id)`, including the final result after the handler finished. `ctx.rooms.broadcast(room, topic, data)` fans out to every CURRENTLY-CONNECTED subscriber (second device, second tab) but does not replay missed messages — use the stream id for anything that must survive a gap. Broadcast resolves `{ delivered: false }` when the room has no members — a no-op, not an error. Clients receive with `useRoomMessages(room, cb)` from `@pylonsync/react`; `useRoom` is the send side.
|
|
67
67
|
- **It's `db.useQueryOne`, not `useOne`.** Validators and field types have aliases: `v.bool`/`v.boolean`, `v.float`/`v.number`.
|
|
68
68
|
- **Use the supported file and scheduling APIs.** Files go through `<FileUpload>` and `/api/files/*`; an upload is readable by its uploader only, so pass `visibility: "public"` to `uploadFile` or `<FileUpload>` for files every visitor sees; there is no `ctx.files`. One-shot work uses `ctx.scheduler.runAfter`, `runAt`, or `cancel`; there is no `defineWorkflow` or `defineJob`. Recurring work uses `cron("0 * * * *", "fnName")` in `buildManifest({ crons: [...] })`, imported from `@pylonsync/sdk`. Make the target function `internal: true`. It runs with anonymous auth, but its own `ctx.db.*` calls are server-side and bypass policies. Use `ctx.auth.elevate({ admin: true, reason: "..." })`, with a mandatory reason, only when chaining another internal function through `ctx.scheduler`.
|
|
@@ -42,6 +42,22 @@ one from the existing rows: a counter in a synced replica is a race, and gaps in
|
|
|
42
42
|
an invoice series get asked about by accountants. `number` is unique in the
|
|
43
43
|
schema, so a genuine collision fails loudly.
|
|
44
44
|
|
|
45
|
+
## Brand and layout
|
|
46
|
+
|
|
47
|
+
The product ships as **Billfold**. The name lives in `lib/brand.ts`, and the
|
|
48
|
+
sidebar, sign-in screen, page titles, and phone drawer all read it. The mark is
|
|
49
|
+
`components/brand-mark.tsx`; `app/icon.svg`, `app/apple-icon.png`, and
|
|
50
|
+
`public/favicon.ico` draw the same shape for browsers and home screens. The
|
|
51
|
+
accent colour is `--brand` at the top of `app/globals.css`, and the typeface
|
|
52
|
+
(Hanken Grotesk) is set in `fonts` in `app.ts`.
|
|
53
|
+
|
|
54
|
+
The frame is `components/app-shell.tsx`. From 768px up the sidebar is a fixed
|
|
55
|
+
rail; below that it opens as a drawer from the menu button in each page header,
|
|
56
|
+
tables drop their lower-priority columns (`hideBelow` in
|
|
57
|
+
`components/data-table.tsx`), and boards scroll one column at a time. The nav
|
|
58
|
+
links are in `components/nav.tsx`. Every nav count comes from `navCounts` in
|
|
59
|
+
`app/workspace.tsx`, and page headers and tabs read the same numbers.
|
|
60
|
+
|
|
45
61
|
## Layout
|
|
46
62
|
|
|
47
63
|
```
|
|
Binary file
|
|
@@ -17,7 +17,7 @@ export function ClientsView() {
|
|
|
17
17
|
const [open, setOpen] = useState(false);
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<RequireAuth
|
|
20
|
+
<RequireAuth>
|
|
21
21
|
<Workspace pathname="/clients">
|
|
22
22
|
{(data) => {
|
|
23
23
|
// Outstanding per client, derived rather than stored — one less thing
|
|
@@ -35,16 +35,23 @@ export function ClientsView() {
|
|
|
35
35
|
{
|
|
36
36
|
key: "name",
|
|
37
37
|
header: "Client",
|
|
38
|
+
className: "w-[60%] md:w-[34%]",
|
|
38
39
|
cell: (row) => (
|
|
39
|
-
<span className="flex items-center gap-2">
|
|
40
|
-
<Avatar name={row.name}
|
|
41
|
-
<span className="
|
|
40
|
+
<span className="flex min-w-0 items-center gap-2.5">
|
|
41
|
+
<Avatar name={row.name} shape="square" />
|
|
42
|
+
<span className="min-w-0">
|
|
43
|
+
<span className="block truncate font-medium">{row.name}</span>
|
|
44
|
+
<span className="block truncate text-[12px] text-muted-foreground md:hidden">
|
|
45
|
+
{row.email ?? ""}
|
|
46
|
+
</span>
|
|
47
|
+
</span>
|
|
42
48
|
</span>
|
|
43
49
|
),
|
|
44
50
|
},
|
|
45
51
|
{
|
|
46
52
|
key: "email",
|
|
47
53
|
header: "Email",
|
|
54
|
+
hideBelow: "md",
|
|
48
55
|
cell: (row) =>
|
|
49
56
|
row.email ? (
|
|
50
57
|
<a
|
|
@@ -62,6 +69,8 @@ export function ClientsView() {
|
|
|
62
69
|
key: "invoices",
|
|
63
70
|
header: "Invoices",
|
|
64
71
|
numeric: true,
|
|
72
|
+
hideBelow: "sm",
|
|
73
|
+
className: "w-[100px]",
|
|
65
74
|
cell: (row) =>
|
|
66
75
|
data.invoices.filter((invoice) => invoice.clientId === row.id).length,
|
|
67
76
|
},
|
|
@@ -69,6 +78,7 @@ export function ClientsView() {
|
|
|
69
78
|
key: "outstanding",
|
|
70
79
|
header: "Outstanding",
|
|
71
80
|
numeric: true,
|
|
81
|
+
className: "w-[120px] md:w-[140px]",
|
|
72
82
|
cell: (row) => {
|
|
73
83
|
const value = outstanding(row.id);
|
|
74
84
|
return (
|
|
@@ -84,16 +94,18 @@ export function ClientsView() {
|
|
|
84
94
|
|
|
85
95
|
return (
|
|
86
96
|
<>
|
|
87
|
-
<PageHeader title="Clients" count={
|
|
97
|
+
<PageHeader title="Clients" count={data.navCounts["/clients"]}>
|
|
88
98
|
<Button size="sm" onClick={() => setOpen(true)}>
|
|
89
99
|
<Plus />
|
|
90
|
-
New client
|
|
100
|
+
<span className="max-sm:sr-only">New client</span>
|
|
91
101
|
</Button>
|
|
92
102
|
</PageHeader>
|
|
93
103
|
|
|
94
104
|
<DataTable
|
|
95
105
|
rows={rows}
|
|
96
106
|
columns={columns}
|
|
107
|
+
loading={data.loading}
|
|
108
|
+
label="Clients"
|
|
97
109
|
empty={
|
|
98
110
|
<EmptyState
|
|
99
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:
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
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.47 0.15 255);
|
|
24
|
+
--brand-dark: oklch(0.64 0.14 255);
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
/*
|
|
147
|
-
|
|
148
|
-
font-size:
|
|
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.
|
|
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="#0859ac"/><path d="M9 6.5A2 2 0 0 1 11 4.5h7.6l5.4 5.4v15.6a2 2 0 0 1-2 2H11a2 2 0 0 1-2-2v-19Z" fill="#fff"/><rect x="12" y="15" width="8.5" height="2.2" rx="1.1" fill="#0859ac"/><rect x="12" y="19.4" width="5.5" height="2.2" rx="1.1" fill="#0859ac"/></svg>
|
|
@@ -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 {
|
|
5
|
+
import { Banknote, ChevronRight, Trash2 } from "lucide-react";
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
7
|
import { Select } from "@/components/ui/select";
|
|
8
8
|
import { PageHeader } from "@/components/page-header";
|
|
@@ -25,7 +25,7 @@ export function InvoiceView({
|
|
|
25
25
|
const [payOpen, setPayOpen] = useState(false);
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<RequireAuth
|
|
28
|
+
<RequireAuth>
|
|
29
29
|
<Workspace pathname="/">
|
|
30
30
|
{(data) => {
|
|
31
31
|
const invoice = data.invoices.find((i) => i.id === invoiceId);
|
|
@@ -35,7 +35,7 @@ export function InvoiceView({
|
|
|
35
35
|
<>
|
|
36
36
|
<PageHeader title="Invoice" />
|
|
37
37
|
<EmptyState
|
|
38
|
-
title={data.loading ? "Loading…" : "Invoice not found"}
|
|
38
|
+
title={data.loading ? "Loading invoice…" : "Invoice not found"}
|
|
39
39
|
description={
|
|
40
40
|
data.loading ? undefined : "It may have been deleted, or the link is wrong."
|
|
41
41
|
}
|
|
@@ -64,36 +64,32 @@ export function InvoiceView({
|
|
|
64
64
|
|
|
65
65
|
return (
|
|
66
66
|
<>
|
|
67
|
-
<PageHeader
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
className="h-7 text-[12px]"
|
|
74
|
-
onChange={(event) =>
|
|
75
|
-
void callFn("setInvoiceStatus", {
|
|
76
|
-
invoiceId: invoice.id,
|
|
77
|
-
status: event.target.value,
|
|
78
|
-
})
|
|
79
|
-
}
|
|
67
|
+
<PageHeader
|
|
68
|
+
title={invoice.number}
|
|
69
|
+
leading={
|
|
70
|
+
<Link
|
|
71
|
+
href="/"
|
|
72
|
+
className="hidden items-center gap-1 text-[13px] text-muted-foreground transition-colors hover:text-foreground md:inline-flex"
|
|
80
73
|
>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{
|
|
74
|
+
Invoices
|
|
75
|
+
<ChevronRight className="size-3.5" />
|
|
76
|
+
</Link>
|
|
77
|
+
}
|
|
78
|
+
>
|
|
79
|
+
{/* Offered on a sent invoice with a balance. A draft hasn't been
|
|
80
|
+
issued yet, and a void or paid one has nothing to collect. */}
|
|
81
|
+
{invoice.status === "sent" && t.balanceCents > 0 ? (
|
|
89
82
|
<Button size="sm" onClick={() => setPayOpen(true)}>
|
|
90
|
-
|
|
83
|
+
<Banknote />
|
|
84
|
+
<span className="max-sm:sr-only">Record payment</span>
|
|
91
85
|
</Button>
|
|
92
86
|
) : null}
|
|
93
87
|
{editable ? (
|
|
94
88
|
<Button
|
|
95
89
|
size="sm"
|
|
96
90
|
variant="ghost"
|
|
91
|
+
aria-label="Delete draft"
|
|
92
|
+
title="Delete draft"
|
|
97
93
|
onClick={async () => {
|
|
98
94
|
await db.delete("Invoice", invoice.id);
|
|
99
95
|
router.push("/");
|
|
@@ -106,21 +102,59 @@ export function InvoiceView({
|
|
|
106
102
|
</PageHeader>
|
|
107
103
|
|
|
108
104
|
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
109
|
-
<div className="mx-auto flex max-w-3xl flex-col gap-6 px-
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
105
|
+
<div className="mx-auto flex max-w-3xl flex-col gap-6 px-4 py-5 md:px-8 md:py-8">
|
|
106
|
+
<div className="flex flex-wrap items-end justify-between gap-3">
|
|
107
|
+
<div>
|
|
108
|
+
<div className="flex items-center gap-2">
|
|
109
|
+
<h2 className="tabular text-[22px] font-semibold tracking-[-0.02em] md:text-[26px]">
|
|
110
|
+
{invoice.number}
|
|
111
|
+
</h2>
|
|
112
|
+
<StatusBadge status={shown} />
|
|
113
|
+
</div>
|
|
114
|
+
<p className="mt-1 text-[13px] text-muted-foreground">
|
|
115
|
+
{client?.name ?? "No client yet"}
|
|
116
|
+
</p>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="text-right">
|
|
119
|
+
<p className="text-[12px] text-muted-foreground">
|
|
120
|
+
{invoice.status === "draft" ? "Total" : "Balance due"}
|
|
121
|
+
</p>
|
|
122
|
+
<p
|
|
123
|
+
className={
|
|
124
|
+
"tabular text-[22px] font-semibold tracking-[-0.02em] md:text-[26px]" +
|
|
125
|
+
(shown === "overdue" ? " text-destructive" : "")
|
|
126
|
+
}
|
|
127
|
+
>
|
|
128
|
+
{money(invoice.status === "draft" ? t.totalCents : t.balanceCents)}
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
117
132
|
|
|
118
|
-
<dl className="grid grid-cols-2 gap-x-6 gap-y-4 rounded-
|
|
133
|
+
<dl className="grid grid-cols-2 gap-x-6 gap-y-4 rounded-xl border border-border bg-surface-1 p-4 sm:grid-cols-4">
|
|
134
|
+
<Field label="Status">
|
|
135
|
+
<Select
|
|
136
|
+
aria-label="Status"
|
|
137
|
+
value={invoice.status}
|
|
138
|
+
className="h-8 bg-background md:h-7.5"
|
|
139
|
+
onChange={(event) =>
|
|
140
|
+
void callFn("setInvoiceStatus", {
|
|
141
|
+
invoiceId: invoice.id,
|
|
142
|
+
status: event.target.value,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
>
|
|
146
|
+
{STATUSES.map((s) => (
|
|
147
|
+
<option key={s.id} value={s.id}>
|
|
148
|
+
{s.label}
|
|
149
|
+
</option>
|
|
150
|
+
))}
|
|
151
|
+
</Select>
|
|
152
|
+
</Field>
|
|
119
153
|
<Field label="Client">
|
|
120
154
|
<Select
|
|
121
155
|
aria-label="Client"
|
|
122
156
|
value={invoice.clientId ?? ""}
|
|
123
|
-
className="h-
|
|
157
|
+
className="h-8 bg-background md:h-7.5"
|
|
124
158
|
onChange={(event) =>
|
|
125
159
|
void db.update("Invoice", invoice.id, {
|
|
126
160
|
clientId: event.target.value || null,
|
|
@@ -153,16 +187,6 @@ export function InvoiceView({
|
|
|
153
187
|
})
|
|
154
188
|
: "—"}
|
|
155
189
|
</Field>
|
|
156
|
-
<Field label="Balance">
|
|
157
|
-
<span
|
|
158
|
-
className={
|
|
159
|
-
"tabular font-medium" +
|
|
160
|
-
(t.balanceCents > 0 ? " text-destructive" : "")
|
|
161
|
-
}
|
|
162
|
-
>
|
|
163
|
-
{money(t.balanceCents)}
|
|
164
|
-
</span>
|
|
165
|
-
</Field>
|
|
166
190
|
</dl>
|
|
167
191
|
|
|
168
192
|
{client?.address ? (
|
|
@@ -192,11 +216,11 @@ export function InvoiceView({
|
|
|
192
216
|
{paid.length > 0 ? (
|
|
193
217
|
<section>
|
|
194
218
|
<h2 className="mb-2 text-[13px] font-semibold">Payments</h2>
|
|
195
|
-
<ul className="
|
|
219
|
+
<ul className="divide-y divide-border/70 overflow-hidden rounded-xl border border-border">
|
|
196
220
|
{paid.map((payment) => (
|
|
197
221
|
<li
|
|
198
222
|
key={payment.id}
|
|
199
|
-
className="flex items-center gap-3
|
|
223
|
+
className="flex h-11 items-center gap-3 bg-card px-3.5 text-[13px]"
|
|
200
224
|
>
|
|
201
225
|
<span className="tabular font-medium">
|
|
202
226
|
{money(payment.amountCents)}
|
|
@@ -247,7 +271,7 @@ export function InvoiceView({
|
|
|
247
271
|
function Field({ label, children }: { label: string; children: React.ReactNode }) {
|
|
248
272
|
return (
|
|
249
273
|
<div className="min-w-0">
|
|
250
|
-
<dt className="text-[
|
|
274
|
+
<dt className="text-[11.5px] font-medium text-muted-foreground">{label}</dt>
|
|
251
275
|
<dd className="mt-1 truncate text-[13px]">{children}</dd>
|
|
252
276
|
</div>
|
|
253
277
|
);
|
|
@@ -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 { InvoiceView } from "./invoice-view";
|
|
4
5
|
|
|
5
6
|
export const metadata: Metadata = {
|
|
6
|
-
title:
|
|
7
|
+
title: `Invoice · ${BRAND.name}`,
|
|
7
8
|
robots: "noindex",
|
|
8
9
|
};
|
|
9
10
|
|