@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.
- package/bin/create-pylon.js +30 -7
- package/package.json +1 -1
- package/templates/agency/.env.example +7 -0
- 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/waitlist/.env.example +7 -0
- 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
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import React, { useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { db, callFn } from "@pylonsync/react";
|
|
5
5
|
import { useAuth } from "@pylonsync/client";
|
|
6
|
+
import { ArrowUpRight, Check, Eye, EyeOff, LogOut, Plus, Star, Trash2, X } from "lucide-react";
|
|
6
7
|
import {
|
|
8
|
+
bookingWindow,
|
|
7
9
|
money,
|
|
8
10
|
parseLineItems,
|
|
9
11
|
lineItemsTotal,
|
|
@@ -46,9 +48,11 @@ export function AgencyDashboard({ userEmail }: { userEmail: string }) {
|
|
|
46
48
|
setAuthorized(false);
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
|
|
51
|
+
// In `pylon dev` an empty back-office gets demo leads, clients, and
|
|
52
|
+
// invoices before the tabs load (functions/seedStudioBackoffice.ts). On
|
|
53
|
+
// a deploy the call returns without writing.
|
|
54
|
+
await callFn("seedStudioBackoffice", {}).catch(() => {});
|
|
55
|
+
if (!cancelled) setAuthorized(true);
|
|
52
56
|
} catch (e) {
|
|
53
57
|
if (!cancelled) setError(e instanceof Error ? e.message : String(e));
|
|
54
58
|
}
|
|
@@ -72,17 +76,17 @@ export function AgencyDashboard({ userEmail }: { userEmail: string }) {
|
|
|
72
76
|
];
|
|
73
77
|
|
|
74
78
|
return (
|
|
75
|
-
<div className="space-y-
|
|
76
|
-
<nav className="flex gap-1 border-b border-zinc-200">
|
|
79
|
+
<div className="space-y-8">
|
|
80
|
+
<nav className="flex gap-1 overflow-x-auto border-b border-zinc-200">
|
|
77
81
|
{tabs.map((t) => (
|
|
78
82
|
<button
|
|
79
83
|
key={t.id}
|
|
80
84
|
type="button"
|
|
81
85
|
onClick={() => setTab(t.id)}
|
|
82
86
|
className={
|
|
83
|
-
"-mb-px border-b-2 px-3.5 py-2.5 text-[
|
|
87
|
+
"-mb-px shrink-0 border-b-2 px-3.5 py-2.5 text-[14px] font-medium transition-colors " +
|
|
84
88
|
(tab === t.id
|
|
85
|
-
? "border-
|
|
89
|
+
? "border-ink text-ink"
|
|
86
90
|
: "border-transparent text-zinc-500 hover:text-zinc-800")
|
|
87
91
|
}
|
|
88
92
|
>
|
|
@@ -148,10 +152,10 @@ function PipelinePanel() {
|
|
|
148
152
|
|
|
149
153
|
return (
|
|
150
154
|
<div className="space-y-6">
|
|
151
|
-
<PanelHead title="Pipeline" sub="
|
|
155
|
+
<PanelHead title="Pipeline" sub="Leads appear here as they are sent. Booking one lowers the open-slot count on your site." live />
|
|
152
156
|
|
|
153
|
-
<div className="grid gap-
|
|
154
|
-
<Stat label="Open slots" value={String(cap?.openSlots ?? 0)} hint={cap?.label} />
|
|
157
|
+
<div className="grid gap-px overflow-hidden rounded-sm border border-zinc-200 bg-zinc-200 sm:grid-cols-3">
|
|
158
|
+
<Stat label="Open slots" value={String(cap?.openSlots ?? 0)} hint={bookingWindow(cap?.label, Date.now())} />
|
|
155
159
|
<Stat label="New leads" value={String(newCount)} />
|
|
156
160
|
<Stat label="Booked" value={String(bookedCount)} />
|
|
157
161
|
</div>
|
|
@@ -160,7 +164,7 @@ function PipelinePanel() {
|
|
|
160
164
|
|
|
161
165
|
<Card title="Inquiries" count={active.length}>
|
|
162
166
|
{inquiries.length === 0 ? (
|
|
163
|
-
<Empty>No inquiries yet
|
|
167
|
+
<Empty>No inquiries yet. They appear here as soon as someone sends the contact form.</Empty>
|
|
164
168
|
) : (
|
|
165
169
|
<ul className="divide-y divide-zinc-100">
|
|
166
170
|
{inquiries.map((i) => (
|
|
@@ -187,9 +191,10 @@ function PipelinePanel() {
|
|
|
187
191
|
type="button"
|
|
188
192
|
disabled={busyId === i.id}
|
|
189
193
|
onClick={() => act(i.id, "bookInquiry")}
|
|
190
|
-
className="rounded-
|
|
194
|
+
className="inline-flex items-center gap-1.5 rounded-full bg-ink px-3.5 py-1.5 text-[12.5px] font-medium text-white transition-opacity hover:opacity-85 disabled:opacity-50"
|
|
191
195
|
>
|
|
192
|
-
|
|
196
|
+
<Check aria-hidden className="size-3.5" />
|
|
197
|
+
Book
|
|
193
198
|
</button>
|
|
194
199
|
) : null}
|
|
195
200
|
{i.status !== "declined" ? (
|
|
@@ -197,7 +202,7 @@ function PipelinePanel() {
|
|
|
197
202
|
type="button"
|
|
198
203
|
disabled={busyId === i.id}
|
|
199
204
|
onClick={() => act(i.id, "declineInquiry")}
|
|
200
|
-
className="rounded-
|
|
205
|
+
className="rounded-full border border-zinc-300 px-3.5 py-1.5 text-[12.5px] font-medium text-zinc-600 transition-colors hover:border-zinc-500 hover:text-ink disabled:opacity-50"
|
|
201
206
|
>
|
|
202
207
|
{i.status === "booked" ? "Release" : "Decline"}
|
|
203
208
|
</button>
|
|
@@ -237,17 +242,20 @@ function CapacityCard({ cap }: { cap?: CapacityRow }) {
|
|
|
237
242
|
}
|
|
238
243
|
|
|
239
244
|
return (
|
|
240
|
-
<form onSubmit={save} className="rounded-
|
|
241
|
-
<
|
|
242
|
-
<p className="mt-
|
|
245
|
+
<form onSubmit={save} className="rounded-sm border border-zinc-200 bg-white p-5">
|
|
246
|
+
<h2 className="font-display text-[1.5rem] leading-none text-ink">Availability</h2>
|
|
247
|
+
<p className="mt-2 text-[13px] text-zinc-500">
|
|
248
|
+
Your site shows this as “N project slots open”. Leave the window empty to use the next
|
|
249
|
+
quarter a project could start in.
|
|
250
|
+
</p>
|
|
243
251
|
<div className="mt-4 flex flex-wrap items-end gap-3">
|
|
244
252
|
<label className="block">
|
|
245
253
|
<span className="mb-1 block text-[12px] font-medium text-zinc-600">Booking window</span>
|
|
246
254
|
<input
|
|
247
255
|
value={label}
|
|
248
256
|
onChange={(e) => { setLabel(e.target.value); setSaved(false); }}
|
|
249
|
-
placeholder="
|
|
250
|
-
className="h-9 w-
|
|
257
|
+
placeholder={bookingWindow("", Date.now())}
|
|
258
|
+
className="h-9 w-44 rounded-sm border border-zinc-300 px-3 text-[14px] outline-none focus:border-brand focus:ring-2 focus:ring-brand/20"
|
|
251
259
|
/>
|
|
252
260
|
</label>
|
|
253
261
|
<label className="block">
|
|
@@ -257,16 +265,22 @@ function CapacityCard({ cap }: { cap?: CapacityRow }) {
|
|
|
257
265
|
min={0}
|
|
258
266
|
value={slots}
|
|
259
267
|
onChange={(e) => { setSlots(e.target.value); setSaved(false); }}
|
|
260
|
-
className="h-9 w-24 rounded-
|
|
268
|
+
className="h-9 w-24 rounded-sm border border-zinc-300 px-3 text-[14px] tabular-nums outline-none focus:border-brand focus:ring-2 focus:ring-brand/20"
|
|
261
269
|
/>
|
|
262
270
|
</label>
|
|
263
271
|
<button
|
|
264
272
|
type="submit"
|
|
265
273
|
disabled={saving}
|
|
266
|
-
className="h-9 rounded-
|
|
274
|
+
className="inline-flex h-9 items-center gap-1.5 rounded-full bg-ink px-4 text-[13px] font-medium text-white transition-opacity hover:opacity-85 disabled:opacity-50"
|
|
267
275
|
>
|
|
268
|
-
{saving ? "Saving…" :
|
|
276
|
+
{saving ? "Saving…" : "Save"}
|
|
269
277
|
</button>
|
|
278
|
+
{saved ? (
|
|
279
|
+
<span role="status" className="inline-flex items-center gap-1 text-[13px] text-green-700">
|
|
280
|
+
<Check aria-hidden className="size-3.5" />
|
|
281
|
+
Saved
|
|
282
|
+
</span>
|
|
283
|
+
) : null}
|
|
270
284
|
</div>
|
|
271
285
|
</form>
|
|
272
286
|
);
|
|
@@ -310,13 +324,13 @@ function WorkPanel() {
|
|
|
310
324
|
<div className="space-y-6">
|
|
311
325
|
<PanelHead
|
|
312
326
|
title="Work"
|
|
313
|
-
sub={`Your portfolio. ${selectedCount} featured on the homepage.
|
|
327
|
+
sub={`Your portfolio. ${selectedCount} featured on the homepage. The star features a project; the eye publishes it.`}
|
|
314
328
|
action={<NewButton onClick={() => setEditing("new")}>New project</NewButton>}
|
|
315
329
|
/>
|
|
316
330
|
|
|
317
331
|
<Card title="Projects" count={projects.length}>
|
|
318
332
|
{ordered.length === 0 ? (
|
|
319
|
-
<Empty>No projects yet
|
|
333
|
+
<Empty>No projects yet. Add your first case study.</Empty>
|
|
320
334
|
) : (
|
|
321
335
|
<ul className="divide-y divide-zinc-100">
|
|
322
336
|
{ordered.map((p) => (
|
|
@@ -343,7 +357,7 @@ function WorkPanel() {
|
|
|
343
357
|
title={p.selected ? "Featured on homepage" : "Feature on homepage"}
|
|
344
358
|
onClick={() => flag(p.id, { selected: !p.selected })}
|
|
345
359
|
>
|
|
346
|
-
{p.selected ? "
|
|
360
|
+
<Star aria-hidden className="size-4" fill={p.selected ? "currentColor" : "none"} />
|
|
347
361
|
</IconToggle>
|
|
348
362
|
<IconToggle
|
|
349
363
|
on={p.published}
|
|
@@ -351,16 +365,17 @@ function WorkPanel() {
|
|
|
351
365
|
title={p.published ? "Published" : "Draft (hidden)"}
|
|
352
366
|
onClick={() => flag(p.id, { published: !p.published })}
|
|
353
367
|
>
|
|
354
|
-
{p.published ? "
|
|
368
|
+
{p.published ? <Eye aria-hidden className="size-4" /> : <EyeOff aria-hidden className="size-4" />}
|
|
355
369
|
</IconToggle>
|
|
356
370
|
<a
|
|
357
371
|
href={`/work/${p.slug}`}
|
|
358
372
|
target="_blank"
|
|
359
373
|
rel="noreferrer"
|
|
360
|
-
className="rounded-md
|
|
374
|
+
className="rounded-md p-1.5 text-zinc-500 hover:bg-zinc-100 hover:text-zinc-800"
|
|
361
375
|
title="View case study"
|
|
376
|
+
aria-label={`View the ${p.title} case study`}
|
|
362
377
|
>
|
|
363
|
-
|
|
378
|
+
<ArrowUpRight aria-hidden className="size-4" />
|
|
364
379
|
</a>
|
|
365
380
|
<button
|
|
366
381
|
type="button"
|
|
@@ -373,10 +388,11 @@ function WorkPanel() {
|
|
|
373
388
|
type="button"
|
|
374
389
|
disabled={busyId === p.id}
|
|
375
390
|
onClick={() => remove(p)}
|
|
376
|
-
className="rounded-md
|
|
391
|
+
className="rounded-md p-1.5 text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
|
|
377
392
|
title="Delete"
|
|
393
|
+
aria-label="Delete"
|
|
378
394
|
>
|
|
379
|
-
|
|
395
|
+
<Trash2 aria-hidden className="size-4" />
|
|
380
396
|
</button>
|
|
381
397
|
</div>
|
|
382
398
|
</li>
|
|
@@ -445,7 +461,7 @@ function ProjectForm({
|
|
|
445
461
|
});
|
|
446
462
|
onClose();
|
|
447
463
|
} catch (e) {
|
|
448
|
-
setErr(e instanceof Error ? e.message : "
|
|
464
|
+
setErr(e instanceof Error ? e.message : "Could not save. Try again.");
|
|
449
465
|
setSaving(false);
|
|
450
466
|
}
|
|
451
467
|
}
|
|
@@ -514,7 +530,7 @@ function ClientsPanel() {
|
|
|
514
530
|
setNote(null);
|
|
515
531
|
try {
|
|
516
532
|
const r = await callFn<{ ok: boolean; invoices: number }>("deleteClient", { id: c.id });
|
|
517
|
-
if (!r.ok) setNote(`${c.name} has ${r.invoices} invoice${r.invoices === 1 ? "" : "s"}
|
|
533
|
+
if (!r.ok) setNote(`${c.name} has ${r.invoices} invoice${r.invoices === 1 ? "" : "s"}. Delete those first.`);
|
|
518
534
|
else await load();
|
|
519
535
|
} finally {
|
|
520
536
|
setBusyId(null);
|
|
@@ -527,7 +543,7 @@ function ClientsPanel() {
|
|
|
527
543
|
<div className="space-y-6">
|
|
528
544
|
<PanelHead
|
|
529
545
|
title="Clients"
|
|
530
|
-
sub="
|
|
546
|
+
sub="Contacts and their status. Only you can see this tab; nothing here reaches the public site."
|
|
531
547
|
action={<NewButton onClick={() => setEditing("new")}>New client</NewButton>}
|
|
532
548
|
/>
|
|
533
549
|
{note ? (
|
|
@@ -536,7 +552,7 @@ function ClientsPanel() {
|
|
|
536
552
|
|
|
537
553
|
<Card title="Contacts" count={clients.length}>
|
|
538
554
|
{clients.length === 0 ? (
|
|
539
|
-
<Empty>No clients yet
|
|
555
|
+
<Empty>No clients yet. Add your first contact.</Empty>
|
|
540
556
|
) : (
|
|
541
557
|
<ul className="divide-y divide-zinc-100">
|
|
542
558
|
{clients.map((c) => (
|
|
@@ -560,10 +576,11 @@ function ClientsPanel() {
|
|
|
560
576
|
type="button"
|
|
561
577
|
disabled={busyId === c.id}
|
|
562
578
|
onClick={() => remove(c)}
|
|
563
|
-
className="rounded-md
|
|
579
|
+
className="rounded-md p-1.5 text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
|
|
564
580
|
title="Delete"
|
|
581
|
+
aria-label="Delete"
|
|
565
582
|
>
|
|
566
|
-
|
|
583
|
+
<Trash2 aria-hidden className="size-4" />
|
|
567
584
|
</button>
|
|
568
585
|
</div>
|
|
569
586
|
</li>
|
|
@@ -617,7 +634,7 @@ function ClientForm({
|
|
|
617
634
|
await onSaved();
|
|
618
635
|
onClose();
|
|
619
636
|
} catch (e) {
|
|
620
|
-
setErr(e instanceof Error ? e.message : "
|
|
637
|
+
setErr(e instanceof Error ? e.message : "Could not save. Try again.");
|
|
621
638
|
setSaving(false);
|
|
622
639
|
}
|
|
623
640
|
}
|
|
@@ -713,11 +730,11 @@ function InvoicesPanel() {
|
|
|
713
730
|
<div className="space-y-6">
|
|
714
731
|
<PanelHead
|
|
715
732
|
title="Invoices"
|
|
716
|
-
sub="
|
|
733
|
+
sub="View an invoice, export it to PDF, and track what is paid and outstanding. Only you can see this tab."
|
|
717
734
|
action={<NewButton onClick={() => setEditing("new")}>New invoice</NewButton>}
|
|
718
735
|
/>
|
|
719
736
|
|
|
720
|
-
<div className="grid gap-
|
|
737
|
+
<div className="grid gap-px overflow-hidden rounded-sm border border-zinc-200 bg-zinc-200 sm:grid-cols-3">
|
|
721
738
|
<Stat label="Paid" value={money(totals.paid)} />
|
|
722
739
|
<Stat label="Outstanding" value={money(totals.outstanding)} />
|
|
723
740
|
<Stat label="Invoices" value={String(invoices.length)} />
|
|
@@ -770,10 +787,11 @@ function InvoicesPanel() {
|
|
|
770
787
|
type="button"
|
|
771
788
|
disabled={busyId === inv.id}
|
|
772
789
|
onClick={() => remove(inv)}
|
|
773
|
-
className="rounded-md
|
|
790
|
+
className="rounded-md p-1.5 text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-50"
|
|
774
791
|
title="Delete"
|
|
792
|
+
aria-label="Delete"
|
|
775
793
|
>
|
|
776
|
-
|
|
794
|
+
<Trash2 aria-hidden className="size-4" />
|
|
777
795
|
</button>
|
|
778
796
|
</div>
|
|
779
797
|
</li>
|
|
@@ -894,7 +912,7 @@ function InvoiceView({
|
|
|
894
912
|
>
|
|
895
913
|
{downloading ? "Preparing…" : "Download PDF"}
|
|
896
914
|
</button>
|
|
897
|
-
<button type="button" onClick={onClose} className="rounded-md
|
|
915
|
+
<button type="button" onClick={onClose} aria-label="Close" className="rounded-md p-1.5 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700"><X aria-hidden className="size-4" /></button>
|
|
898
916
|
</div>
|
|
899
917
|
</div>
|
|
900
918
|
|
|
@@ -1072,7 +1090,7 @@ function InvoiceForm({
|
|
|
1072
1090
|
await onSaved();
|
|
1073
1091
|
onClose();
|
|
1074
1092
|
} catch (e) {
|
|
1075
|
-
setErr(e instanceof Error ? e.message : "
|
|
1093
|
+
setErr(e instanceof Error ? e.message : "Could not save. Try again.");
|
|
1076
1094
|
setSaving(false);
|
|
1077
1095
|
}
|
|
1078
1096
|
}
|
|
@@ -1083,7 +1101,7 @@ function InvoiceForm({
|
|
|
1083
1101
|
<Modal title={initial ? "Edit invoice" : "New invoice"} onClose={onClose} onSubmit={save} saving={saving} error={err}>
|
|
1084
1102
|
{noClients ? (
|
|
1085
1103
|
<p className="rounded-lg bg-amber-50 px-3 py-2 text-[13px] text-amber-800">
|
|
1086
|
-
Add a client first
|
|
1104
|
+
Add a client first. An invoice needs someone to bill.
|
|
1087
1105
|
</p>
|
|
1088
1106
|
) : null}
|
|
1089
1107
|
<div className="grid gap-3 sm:grid-cols-2">
|
|
@@ -1143,8 +1161,9 @@ function InvoiceForm({
|
|
|
1143
1161
|
disabled={items.length === 1}
|
|
1144
1162
|
className="shrink-0 rounded-md px-2 py-1 text-[13px] text-zinc-400 hover:bg-red-50 hover:text-red-600 disabled:opacity-30"
|
|
1145
1163
|
title="Remove line"
|
|
1164
|
+
aria-label="Remove line"
|
|
1146
1165
|
>
|
|
1147
|
-
|
|
1166
|
+
<X aria-hidden className="size-4" />
|
|
1148
1167
|
</button>
|
|
1149
1168
|
</div>
|
|
1150
1169
|
))}
|
|
@@ -1159,7 +1178,7 @@ function InvoiceForm({
|
|
|
1159
1178
|
</div>
|
|
1160
1179
|
</div>
|
|
1161
1180
|
|
|
1162
|
-
<Field label="Project" hint="Optional
|
|
1181
|
+
<Field label="Project" hint="Optional. Links the invoice to a case study">
|
|
1163
1182
|
<select value={f.projectId} onChange={(e) => set("projectId", e.target.value)} className={inputCls}>
|
|
1164
1183
|
<option value="">None</option>
|
|
1165
1184
|
{projects.map((p) => (
|
|
@@ -1192,14 +1211,33 @@ function InvoiceForm({
|
|
|
1192
1211
|
|
|
1193
1212
|
/* ============================== PRIMITIVES ============================= */
|
|
1194
1213
|
|
|
1195
|
-
function PanelHead({
|
|
1214
|
+
function PanelHead({
|
|
1215
|
+
title,
|
|
1216
|
+
sub,
|
|
1217
|
+
action,
|
|
1218
|
+
live,
|
|
1219
|
+
}: {
|
|
1220
|
+
title: string;
|
|
1221
|
+
sub: string;
|
|
1222
|
+
action?: React.ReactNode;
|
|
1223
|
+
live?: boolean;
|
|
1224
|
+
}) {
|
|
1196
1225
|
return (
|
|
1197
|
-
<div className="flex flex-wrap items-
|
|
1226
|
+
<div className="flex flex-wrap items-end justify-between gap-4">
|
|
1198
1227
|
<div>
|
|
1199
|
-
<h1 className="text-
|
|
1200
|
-
<p className="mt-
|
|
1228
|
+
<h1 className="font-display text-[2.75rem] leading-none tracking-[-0.015em] text-ink">{title}</h1>
|
|
1229
|
+
<p className="mt-3 max-w-xl text-[14px] leading-relaxed text-zinc-600">{sub}</p>
|
|
1201
1230
|
</div>
|
|
1202
1231
|
{action}
|
|
1232
|
+
{live ? (
|
|
1233
|
+
<span className="inline-flex items-center gap-2 text-[13px] text-zinc-600">
|
|
1234
|
+
<span className="relative flex size-2">
|
|
1235
|
+
<span className="absolute inline-flex size-2 animate-ping rounded-full bg-green-500/60" />
|
|
1236
|
+
<span className="relative inline-flex size-2 rounded-full bg-green-600" />
|
|
1237
|
+
</span>
|
|
1238
|
+
Live
|
|
1239
|
+
</span>
|
|
1240
|
+
) : null}
|
|
1203
1241
|
</div>
|
|
1204
1242
|
);
|
|
1205
1243
|
}
|
|
@@ -1209,18 +1247,20 @@ function NewButton({ onClick, children }: { onClick: () => void; children: React
|
|
|
1209
1247
|
<button
|
|
1210
1248
|
type="button"
|
|
1211
1249
|
onClick={onClick}
|
|
1212
|
-
className="shrink-0 rounded-
|
|
1250
|
+
className="inline-flex shrink-0 items-center gap-1.5 rounded-full bg-ink px-4 py-2 text-[13px] font-medium text-white transition-opacity hover:opacity-85"
|
|
1213
1251
|
>
|
|
1214
|
-
|
|
1252
|
+
<Plus aria-hidden className="size-3.5" />
|
|
1253
|
+
{children}
|
|
1215
1254
|
</button>
|
|
1216
1255
|
);
|
|
1217
1256
|
}
|
|
1218
1257
|
|
|
1219
1258
|
function Card({ title, count, children }: { title: string; count: number; children: React.ReactNode }) {
|
|
1220
1259
|
return (
|
|
1221
|
-
<div className="rounded-
|
|
1222
|
-
<div className="border-b border-zinc-100 px-4 py-3
|
|
1223
|
-
|
|
1260
|
+
<div className="rounded-sm border border-zinc-200 bg-white">
|
|
1261
|
+
<div className="flex items-baseline gap-2 border-b border-zinc-100 px-4 py-3.5">
|
|
1262
|
+
<h2 className="font-display text-[1.375rem] leading-none text-ink">{title}</h2>
|
|
1263
|
+
<span className="text-[13px] tabular-nums text-zinc-400">{count}</span>
|
|
1224
1264
|
</div>
|
|
1225
1265
|
{children}
|
|
1226
1266
|
</div>
|
|
@@ -1233,9 +1273,9 @@ function Empty({ children }: { children: React.ReactNode }) {
|
|
|
1233
1273
|
|
|
1234
1274
|
function Stat({ label, value, hint }: { label: string; value: string; hint?: string }) {
|
|
1235
1275
|
return (
|
|
1236
|
-
<div className="
|
|
1237
|
-
<div className="text-[11px] font-medium uppercase tracking-wide text-zinc-
|
|
1238
|
-
<div className="mt-
|
|
1276
|
+
<div className="bg-white p-5">
|
|
1277
|
+
<div className="text-[11px] font-medium uppercase tracking-wide text-zinc-500">{label}</div>
|
|
1278
|
+
<div className="font-display mt-2 text-[2.75rem] leading-none tabular-nums text-ink">{value}</div>
|
|
1239
1279
|
{hint ? <div className="mt-0.5 text-[12px] text-zinc-400">{hint}</div> : null}
|
|
1240
1280
|
</div>
|
|
1241
1281
|
);
|
|
@@ -1284,10 +1324,12 @@ function IconToggle({
|
|
|
1284
1324
|
type="button"
|
|
1285
1325
|
disabled={disabled}
|
|
1286
1326
|
title={title}
|
|
1327
|
+
aria-label={title}
|
|
1328
|
+
aria-pressed={on}
|
|
1287
1329
|
onClick={onClick}
|
|
1288
1330
|
className={
|
|
1289
|
-
"rounded-md
|
|
1290
|
-
(on ? "text-
|
|
1331
|
+
"rounded-md p-1.5 leading-none transition-colors disabled:opacity-50 " +
|
|
1332
|
+
(on ? "text-ink hover:bg-zinc-100" : "text-zinc-300 hover:bg-zinc-100 hover:text-zinc-500")
|
|
1291
1333
|
}
|
|
1292
1334
|
>
|
|
1293
1335
|
{children}
|
|
@@ -1312,7 +1354,7 @@ function Switch({ label, checked, onChange }: { label: string; checked: boolean;
|
|
|
1312
1354
|
}
|
|
1313
1355
|
|
|
1314
1356
|
// A simple centered modal with a sticky footer. Submits on the footer button or
|
|
1315
|
-
// Enter; closes on the backdrop, the
|
|
1357
|
+
// Enter; closes on the backdrop, the close button, or Escape.
|
|
1316
1358
|
function Modal({
|
|
1317
1359
|
title,
|
|
1318
1360
|
onClose,
|
|
@@ -1343,7 +1385,7 @@ function Modal({
|
|
|
1343
1385
|
>
|
|
1344
1386
|
<div className="flex items-center justify-between border-b border-zinc-100 px-5 py-3.5">
|
|
1345
1387
|
<h2 className="text-[15px] font-semibold text-zinc-900">{title}</h2>
|
|
1346
|
-
<button type="button" onClick={onClose} className="rounded-md
|
|
1388
|
+
<button type="button" onClick={onClose} aria-label="Close" className="rounded-md p-1.5 text-zinc-400 hover:bg-zinc-100 hover:text-zinc-700"><X aria-hidden className="size-4" /></button>
|
|
1347
1389
|
</div>
|
|
1348
1390
|
<div className="max-h-[70vh] space-y-3.5 overflow-y-auto px-5 py-4">{children}</div>
|
|
1349
1391
|
<div className="flex items-center justify-between gap-3 border-t border-zinc-100 px-5 py-3.5">
|
|
@@ -1406,7 +1448,10 @@ export function UserMenu({ email }: { email: string }) {
|
|
|
1406
1448
|
}
|
|
1407
1449
|
return (
|
|
1408
1450
|
<details className="group relative">
|
|
1409
|
-
<summary
|
|
1451
|
+
<summary
|
|
1452
|
+
aria-label="Account"
|
|
1453
|
+
className="flex size-8 cursor-pointer select-none list-none items-center justify-center rounded-full bg-ink text-[12px] font-semibold text-white marker:hidden [&::-webkit-details-marker]:hidden"
|
|
1454
|
+
>
|
|
1410
1455
|
{initial}
|
|
1411
1456
|
</summary>
|
|
1412
1457
|
<div className="absolute right-0 top-full z-40 mt-2 w-56 overflow-hidden rounded-xl border border-zinc-200 bg-white py-1 shadow-[0_16px_48px_-16px_rgba(0,0,0,0.25)]">
|
|
@@ -1416,8 +1461,9 @@ export function UserMenu({ email }: { email: string }) {
|
|
|
1416
1461
|
<button
|
|
1417
1462
|
type="button"
|
|
1418
1463
|
onClick={onSignOut}
|
|
1419
|
-
className="flex w-full items-center px-3 py-2 text-left text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
|
|
1464
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
|
|
1420
1465
|
>
|
|
1466
|
+
<LogOut aria-hidden className="size-3.5" />
|
|
1421
1467
|
Sign out
|
|
1422
1468
|
</button>
|
|
1423
1469
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { use } from "react";
|
|
2
2
|
import { Link, type Metadata, type PageProps } from "@pylonsync/react";
|
|
3
|
+
import { ArrowUpRight } from "lucide-react";
|
|
3
4
|
import { siteConfig } from "@/lib/site.config";
|
|
4
5
|
import { UserMenu, AgencyDashboard } from "./dashboard-client";
|
|
5
6
|
|
|
@@ -35,35 +36,32 @@ export default function DashboardPage({ auth, response, serverData }: PageProps)
|
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
// Dashboard chrome
|
|
39
|
-
// site, and the account menu (a client
|
|
39
|
+
// Dashboard chrome in the site's own look: white page, the studio name in the
|
|
40
|
+
// display serif, a link back to the site, and the account menu (a client
|
|
41
|
+
// island for sign-out).
|
|
40
42
|
function Shell({ email, children }: { email: string; children: React.ReactNode }) {
|
|
41
43
|
const { brand } = siteConfig;
|
|
42
44
|
return (
|
|
43
|
-
<div className="flex min-h-screen flex-col bg-white text-
|
|
45
|
+
<div className="flex min-h-screen flex-col bg-white text-ink">
|
|
44
46
|
<header className="border-b border-zinc-200">
|
|
45
|
-
<div className="mx-auto flex h-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<span className="text-[15px] font-semibold tracking-tight">
|
|
51
|
-
{brand.name} <span className="text-zinc-400">/ studio</span>
|
|
52
|
-
</span>
|
|
53
|
-
</div>
|
|
54
|
-
<div className="flex items-center gap-4">
|
|
47
|
+
<div className="mx-auto flex h-16 w-full max-w-5xl items-center justify-between px-6">
|
|
48
|
+
<Link href="/" className="font-display text-[1.5rem] leading-none text-ink">
|
|
49
|
+
{brand.name}
|
|
50
|
+
</Link>
|
|
51
|
+
<div className="flex items-center gap-5">
|
|
55
52
|
<Link
|
|
56
53
|
href="/"
|
|
57
|
-
className="text-[
|
|
54
|
+
className="hidden items-center gap-1 text-[14px] text-zinc-600 transition-colors hover:text-ink sm:inline-flex"
|
|
58
55
|
>
|
|
59
|
-
View site
|
|
56
|
+
View site
|
|
57
|
+
<ArrowUpRight aria-hidden className="size-3.5" />
|
|
60
58
|
</Link>
|
|
61
59
|
<UserMenu email={email} />
|
|
62
60
|
</div>
|
|
63
61
|
</div>
|
|
64
62
|
</header>
|
|
65
63
|
<main className="flex-1">
|
|
66
|
-
<div className="mx-auto max-w-
|
|
64
|
+
<div className="mx-auto w-full max-w-5xl px-6 py-8 sm:py-10">{children}</div>
|
|
67
65
|
</main>
|
|
68
66
|
</div>
|
|
69
67
|
);
|
|
@@ -18,21 +18,19 @@ export default function LoginPage({ auth, response }: PageProps) {
|
|
|
18
18
|
if (auth.user_id && !auth.user_id.startsWith("guest_")) response.redirect("/dashboard");
|
|
19
19
|
const { brand } = siteConfig;
|
|
20
20
|
return (
|
|
21
|
-
<div className="flex min-h-screen
|
|
22
|
-
<div className="w-full max-w-
|
|
23
|
-
<Link href="/" className="
|
|
24
|
-
|
|
25
|
-
{brand.letter}
|
|
26
|
-
</span>
|
|
21
|
+
<div className="flex min-h-screen flex-col bg-white text-ink">
|
|
22
|
+
<div className="mx-auto flex h-16 w-full max-w-5xl items-center px-6">
|
|
23
|
+
<Link href="/" className="font-display text-[1.5rem] leading-none">
|
|
24
|
+
{brand.name}
|
|
27
25
|
</Link>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
</div>
|
|
27
|
+
<div className="flex flex-1 items-center justify-center px-6 pb-24">
|
|
28
|
+
<div className="w-full max-w-[380px]">
|
|
29
|
+
<h1 className="font-display text-[2.75rem] leading-none tracking-[-0.015em]">Studio sign in</h1>
|
|
30
|
+
<p className="mt-3 text-[15px] text-zinc-600">Leads, work, clients, and invoices.</p>
|
|
31
|
+
<div className="mt-8">
|
|
32
|
+
<AuthForm />
|
|
33
|
+
</div>
|
|
36
34
|
</div>
|
|
37
35
|
</div>
|
|
38
36
|
</div>
|
|
@@ -41,11 +41,16 @@ export function SectionTitle({ title, body }: { title: string; body?: string })
|
|
|
41
41
|
// A linked portfolio entry: the 4:3 cover, then the title, client label, and
|
|
42
42
|
// a one-line summary in small type. Used on the homepage work grid and the
|
|
43
43
|
// full /work index, so both stay in lockstep.
|
|
44
|
-
|
|
44
|
+
// `wide` spans both grid columns with a wider cover (see isWideCard).
|
|
45
|
+
export function ProjectCard({ p, wide = false }: { p: ProjectView; wide?: boolean }) {
|
|
45
46
|
return (
|
|
46
|
-
<Link href={`/work/${p.slug}`} className="group block">
|
|
47
|
+
<Link href={`/work/${p.slug}`} className={"group block" + (wide ? " sm:col-span-2" : "")}>
|
|
47
48
|
{/* Case-study cover: public/images/work/<slug>.jpg. */}
|
|
48
|
-
<ImagePlaceholder
|
|
49
|
+
<ImagePlaceholder
|
|
50
|
+
shape={wide ? "wide" : "landscape"}
|
|
51
|
+
title={`${p.title} product shot`}
|
|
52
|
+
src={`/images/work/${p.slug}.jpg`}
|
|
53
|
+
/>
|
|
49
54
|
<div className="mt-4 flex items-baseline justify-between gap-4">
|
|
50
55
|
<h3 className="font-display text-[1.5rem] leading-none text-ink transition-colors group-hover:text-brand">
|
|
51
56
|
{p.title}
|
|
@@ -73,7 +78,7 @@ export function ImagePlaceholder({
|
|
|
73
78
|
src,
|
|
74
79
|
className = "",
|
|
75
80
|
}: {
|
|
76
|
-
shape?: "landscape" | "portrait" | "square" | "circle";
|
|
81
|
+
shape?: "landscape" | "wide" | "portrait" | "square" | "circle";
|
|
77
82
|
title: string;
|
|
78
83
|
hint?: string;
|
|
79
84
|
/** A real image. The template ships one under public/images; swap it for yours. */
|
|
@@ -85,7 +90,9 @@ export function ImagePlaceholder({
|
|
|
85
90
|
? "aspect-[3/4]"
|
|
86
91
|
: shape === "square" || shape === "circle"
|
|
87
92
|
? "aspect-square"
|
|
88
|
-
: "
|
|
93
|
+
: shape === "wide"
|
|
94
|
+
? "aspect-[4/3] sm:aspect-[2/1]"
|
|
95
|
+
: "aspect-[4/3]";
|
|
89
96
|
const radius = shape === "circle" ? "rounded-full" : "rounded-sm";
|
|
90
97
|
if (src) {
|
|
91
98
|
return (
|