@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
package/templates/saas/app.ts
CHANGED
|
@@ -111,7 +111,8 @@ const OrgInvite = entity(
|
|
|
111
111
|
const Project = entity(
|
|
112
112
|
"Project",
|
|
113
113
|
{
|
|
114
|
-
|
|
114
|
+
// `.readonly()`: set on insert, rejected on any client update.
|
|
115
|
+
orgId: field.id("Org").readonly(),
|
|
115
116
|
name: field.string(),
|
|
116
117
|
description: field.string().optional(),
|
|
117
118
|
// "active" | "archived" — archived projects stay in the workspace but drop
|
|
@@ -122,6 +123,38 @@ const Project = entity(
|
|
|
122
123
|
{ indexes: [{ name: "by_org", fields: ["orgId"], unique: false }] },
|
|
123
124
|
);
|
|
124
125
|
|
|
126
|
+
// A unit of work inside a project. `orgId` is copied from the project when
|
|
127
|
+
// the task is created, so the same one-line tenant policy scopes it.
|
|
128
|
+
// Statuses and priorities are listed in lib/tasks.ts. `assigneeName` is a
|
|
129
|
+
// copy of the assignee's display name, so the board renders names on the
|
|
130
|
+
// first server paint (the User policy only lets people read their own row);
|
|
131
|
+
// updateProfile refreshes it when a person renames themselves.
|
|
132
|
+
const Task = entity(
|
|
133
|
+
"Task",
|
|
134
|
+
{
|
|
135
|
+
orgId: field.id("Org").readonly(),
|
|
136
|
+
projectId: field.id("Project").readonly(),
|
|
137
|
+
title: field.string(),
|
|
138
|
+
status: field.string().default("todo"),
|
|
139
|
+
priority: field.string().default("none"),
|
|
140
|
+
assigneeId: field.id("User").optional(),
|
|
141
|
+
assigneeName: field.string().optional(),
|
|
142
|
+
// Calendar day, `YYYY-MM-DD`.
|
|
143
|
+
dueDate: field.string().optional(),
|
|
144
|
+
// Stamped by updateTask when the status moves to done; cleared when it
|
|
145
|
+
// moves back.
|
|
146
|
+
completedAt: field.datetime().optional(),
|
|
147
|
+
createdAt: field.datetime().defaultNow(),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
indexes: [
|
|
151
|
+
{ name: "by_org", fields: ["orgId"], unique: false },
|
|
152
|
+
{ name: "by_project", fields: ["projectId"], unique: false },
|
|
153
|
+
{ name: "by_assignee", fields: ["assigneeId"], unique: false },
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
125
158
|
// User rows: read your own; the auth subsystem owns writes.
|
|
126
159
|
const userPolicy = policy({
|
|
127
160
|
name: "user_self",
|
|
@@ -161,17 +194,31 @@ const orgInvitePolicy = policy({
|
|
|
161
194
|
allowDelete: "false",
|
|
162
195
|
});
|
|
163
196
|
|
|
164
|
-
// Projects are
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
197
|
+
// Projects and tasks are readable in your ACTIVE tenant only. Every write
|
|
198
|
+
// goes through a server function (functions/*Project.ts, *Task.ts):
|
|
199
|
+
// - createProject and setProjectStatus enforce the free plan's project cap
|
|
200
|
+
// (lib/plans.ts), so a client cannot skip the paywall.
|
|
201
|
+
// - An update policy is checked against the row as it is BEFORE the write,
|
|
202
|
+
// so `auth.tenantId == data.orgId` alone would let a client PATCH `orgId`
|
|
203
|
+
// and move a row into another workspace. `orgId` is `.readonly()`, and
|
|
204
|
+
// the functions never take `orgId` from the caller; they re-check
|
|
205
|
+
// membership against the row's own workspace.
|
|
206
|
+
// - deleteProject removes the project's tasks with it.
|
|
168
207
|
const projectPolicy = policy({
|
|
169
208
|
name: "project_tenant",
|
|
170
209
|
entity: "Project",
|
|
171
210
|
allowRead: "auth.tenantId == data.orgId",
|
|
172
211
|
allowInsert: "false",
|
|
173
|
-
allowUpdate: "
|
|
174
|
-
allowDelete: "
|
|
212
|
+
allowUpdate: "false",
|
|
213
|
+
allowDelete: "false",
|
|
214
|
+
});
|
|
215
|
+
const taskPolicy = policy({
|
|
216
|
+
name: "task_tenant",
|
|
217
|
+
entity: "Task",
|
|
218
|
+
allowRead: "auth.tenantId == data.orgId",
|
|
219
|
+
allowInsert: "false",
|
|
220
|
+
allowUpdate: "false",
|
|
221
|
+
allowDelete: "false",
|
|
175
222
|
});
|
|
176
223
|
|
|
177
224
|
// Every non-internal function in functions/ becomes a manifest entry —
|
|
@@ -182,7 +229,7 @@ const fns = await discoverFunctions();
|
|
|
182
229
|
const manifest = buildManifest({
|
|
183
230
|
name: "__APP_NAME__",
|
|
184
231
|
version: "0.1.0",
|
|
185
|
-
entities: [User, Org, OrgMember, OrgInvite, Project, ...billing.manifest.entities],
|
|
232
|
+
entities: [User, Org, OrgMember, OrgInvite, Project, Task, ...billing.manifest.entities],
|
|
186
233
|
queries: fns.queries,
|
|
187
234
|
// The billing actions (createCheckoutSession / createBillingPortalSession /
|
|
188
235
|
// cancelSubscription / restoreSubscription / stripeWebhook) are re-exported
|
|
@@ -199,25 +246,33 @@ const manifest = buildManifest({
|
|
|
199
246
|
orgMemberPolicy,
|
|
200
247
|
orgInvitePolicy,
|
|
201
248
|
projectPolicy,
|
|
249
|
+
taskPolicy,
|
|
202
250
|
...billing.manifest.policies,
|
|
203
251
|
],
|
|
204
252
|
// Email/password is on by default against the User entity. The org entities
|
|
205
253
|
// above are named with the framework defaults (Org / OrgMember / OrgInvite),
|
|
206
254
|
// so `/api/auth/orgs/*` + `/api/auth/select-org` work with no extra config.
|
|
207
255
|
auth: auth(),
|
|
208
|
-
// Self-hosted
|
|
209
|
-
// same-origin
|
|
210
|
-
//
|
|
211
|
-
//
|
|
256
|
+
// Self-hosted Geist + Geist Mono: the build fetches the woff2, serves it
|
|
257
|
+
// same-origin, preloads it, and synthesizes a size-adjusted fallback face so
|
|
258
|
+
// there's no layout shift. globals.css reads them via `var(--font-sans, …)`
|
|
259
|
+
// and `var(--font-geist-mono, …)`; layout.tsx carries no font <link>.
|
|
212
260
|
fonts: [
|
|
213
261
|
font({
|
|
214
|
-
family: "
|
|
262
|
+
family: "Geist",
|
|
215
263
|
variable: "--font-sans",
|
|
216
264
|
weights: ["400", "500", "600", "700"],
|
|
217
265
|
subsets: ["latin"],
|
|
218
266
|
display: "swap",
|
|
219
267
|
preload: true,
|
|
220
268
|
}),
|
|
269
|
+
font({
|
|
270
|
+
family: "Geist Mono",
|
|
271
|
+
variable: "--font-geist-mono",
|
|
272
|
+
weights: ["400", "500"],
|
|
273
|
+
subsets: ["latin"],
|
|
274
|
+
display: "swap",
|
|
275
|
+
}),
|
|
221
276
|
],
|
|
222
277
|
routes: await discoverAppRoutes(),
|
|
223
278
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React from "react";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
4
|
import { Link, useRouter } from "@pylonsync/react";
|
|
5
5
|
import { useAuth, OrganizationSwitcher } from "@pylonsync/client";
|
|
6
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
6
7
|
import {
|
|
7
|
-
|
|
8
|
+
LayoutGrid,
|
|
8
9
|
FolderKanban,
|
|
9
10
|
Users,
|
|
10
11
|
CreditCard,
|
|
@@ -12,207 +13,286 @@ import {
|
|
|
12
13
|
LogOut,
|
|
13
14
|
ExternalLink,
|
|
14
15
|
ChevronsUpDown,
|
|
16
|
+
ChevronRight,
|
|
17
|
+
Menu,
|
|
18
|
+
X,
|
|
15
19
|
type LucideIcon,
|
|
16
20
|
} from "lucide-react";
|
|
21
|
+
import { Avatar, ProjectMark } from "@/components/task-ui";
|
|
22
|
+
import { useLiveRows } from "@/lib/use-live";
|
|
23
|
+
import { siteConfig } from "@/lib/site.config";
|
|
17
24
|
|
|
18
25
|
export type NavKey = "overview" | "projects" | "members" | "billing" | "settings";
|
|
19
26
|
|
|
20
|
-
// Exported so `app/dashboard/layout.tsx` can derive the active item
|
|
21
|
-
// title from the request URL
|
|
22
|
-
// layout
|
|
23
|
-
// ungrouped items render first, then each named group under an eyebrow label.
|
|
27
|
+
// Exported so `app/dashboard/layout.tsx` can derive the active item and the
|
|
28
|
+
// page title from the request URL. One table drives both the sidebar and the
|
|
29
|
+
// layout. `group: "workspace"` items render in the lower block.
|
|
24
30
|
export const NAV: {
|
|
25
31
|
key: NavKey;
|
|
26
32
|
label: string;
|
|
27
33
|
href: string;
|
|
28
34
|
Icon: LucideIcon;
|
|
29
|
-
group?:
|
|
35
|
+
group?: "workspace";
|
|
30
36
|
}[] = [
|
|
31
|
-
{ key: "overview", label: "Overview", href: "/dashboard", Icon:
|
|
37
|
+
{ key: "overview", label: "Overview", href: "/dashboard", Icon: LayoutGrid },
|
|
32
38
|
{ key: "projects", label: "Projects", href: "/dashboard/projects", Icon: FolderKanban },
|
|
33
|
-
{ key: "members", label: "Members", href: "/dashboard/members", Icon: Users, group: "
|
|
34
|
-
{ key: "billing", label: "Billing", href: "/dashboard/billing", Icon: CreditCard, group: "
|
|
35
|
-
{ key: "settings", label: "Settings", href: "/dashboard/settings", Icon: SettingsIcon, group: "
|
|
39
|
+
{ key: "members", label: "Members", href: "/dashboard/members", Icon: Users, group: "workspace" },
|
|
40
|
+
{ key: "billing", label: "Billing", href: "/dashboard/billing", Icon: CreditCard, group: "workspace" },
|
|
41
|
+
{ key: "settings", label: "Settings", href: "/dashboard/settings", Icon: SettingsIcon, group: "workspace" },
|
|
36
42
|
];
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
export interface ShellProject {
|
|
45
|
+
id: string;
|
|
46
|
+
orgId: string;
|
|
47
|
+
name: string;
|
|
48
|
+
status?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Dashboard chrome. From md up: a sidebar on a grey frame with the page in a
|
|
52
|
+
// raised white panel. Below md: a top bar whose menu button opens the same
|
|
53
|
+
// sidebar as a drawer. Rendered by `app/dashboard/layout.tsx`, which resolves
|
|
54
|
+
// every prop on the server, so the first paint has the real names.
|
|
44
55
|
export function DashboardShell({
|
|
45
56
|
active,
|
|
46
57
|
title,
|
|
58
|
+
parent,
|
|
59
|
+
activeProjectId,
|
|
60
|
+
tenantId,
|
|
61
|
+
userId,
|
|
62
|
+
userName,
|
|
47
63
|
userEmail,
|
|
48
64
|
orgName,
|
|
65
|
+
projects,
|
|
49
66
|
children,
|
|
50
67
|
}: {
|
|
51
68
|
active: NavKey;
|
|
52
69
|
title: string;
|
|
70
|
+
/** Breadcrumb parent, e.g. Projects on a project's board. */
|
|
71
|
+
parent?: { label: string; href: string };
|
|
72
|
+
activeProjectId?: string;
|
|
73
|
+
tenantId: string;
|
|
74
|
+
userId: string;
|
|
75
|
+
userName: string;
|
|
53
76
|
userEmail: string;
|
|
54
|
-
// Active org name, resolved on the server, so the workspace switcher renders
|
|
55
|
-
// the real name on first paint instead of flashing in after hydration.
|
|
56
77
|
orgName?: string;
|
|
78
|
+
projects: ShellProject[];
|
|
57
79
|
children: React.ReactNode;
|
|
58
80
|
}) {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
81
|
+
const [drawer, setDrawer] = useState(false);
|
|
82
|
+
const sidebar = (
|
|
83
|
+
<Sidebar
|
|
84
|
+
active={active}
|
|
85
|
+
activeProjectId={activeProjectId}
|
|
86
|
+
tenantId={tenantId}
|
|
87
|
+
userId={userId}
|
|
88
|
+
userName={userName}
|
|
89
|
+
userEmail={userEmail}
|
|
90
|
+
orgName={orgName}
|
|
91
|
+
projects={projects}
|
|
92
|
+
onNavigate={() => setDrawer(false)}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
62
95
|
return (
|
|
63
|
-
<div className="
|
|
64
|
-
<aside className="
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
<div className="min-h-screen bg-[#f4f4f5] text-zinc-900 md:flex">
|
|
97
|
+
<aside className="sticky top-0 hidden h-screen w-[244px] shrink-0 md:block">{sidebar}</aside>
|
|
98
|
+
|
|
99
|
+
{/* Mobile top bar + drawer. */}
|
|
100
|
+
<DialogPrimitive.Root open={drawer} onOpenChange={setDrawer}>
|
|
101
|
+
<header className="sticky top-0 z-30 flex h-14 items-center gap-2 border-b border-zinc-200/80 bg-white/90 px-3 backdrop-blur md:hidden">
|
|
102
|
+
<DialogPrimitive.Trigger
|
|
103
|
+
aria-label="Open navigation"
|
|
104
|
+
className="flex size-9 items-center justify-center rounded-lg text-zinc-600 transition-colors hover:bg-zinc-100"
|
|
105
|
+
>
|
|
106
|
+
<Menu className="size-[18px]" strokeWidth={1.75} />
|
|
107
|
+
</DialogPrimitive.Trigger>
|
|
108
|
+
<div className="min-w-0 flex-1 truncate text-[14px] font-medium">{title}</div>
|
|
109
|
+
<Avatar id={userId} name={userName} />
|
|
110
|
+
</header>
|
|
111
|
+
<DialogPrimitive.Portal>
|
|
112
|
+
<DialogPrimitive.Overlay className="fixed inset-0 z-40 bg-zinc-950/30 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 md:hidden" />
|
|
113
|
+
<DialogPrimitive.Content className="fixed inset-y-0 left-0 z-50 w-[280px] max-w-[85vw] bg-[#f4f4f5] shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_24px_64px_-12px_rgba(0,0,0,0.3)] data-[state=open]:animate-in data-[state=open]:slide-in-from-left data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left md:hidden">
|
|
114
|
+
<DialogPrimitive.Title className="sr-only">Navigation</DialogPrimitive.Title>
|
|
115
|
+
<DialogPrimitive.Description className="sr-only">
|
|
116
|
+
Pages and projects in this workspace
|
|
117
|
+
</DialogPrimitive.Description>
|
|
118
|
+
<DialogPrimitive.Close
|
|
119
|
+
aria-label="Close navigation"
|
|
120
|
+
className="absolute right-2.5 top-3 z-10 flex size-8 items-center justify-center rounded-lg text-zinc-500 hover:bg-zinc-200/60"
|
|
121
|
+
>
|
|
122
|
+
<X className="size-4" />
|
|
123
|
+
</DialogPrimitive.Close>
|
|
124
|
+
{sidebar}
|
|
125
|
+
</DialogPrimitive.Content>
|
|
126
|
+
</DialogPrimitive.Portal>
|
|
127
|
+
</DialogPrimitive.Root>
|
|
128
|
+
|
|
129
|
+
<div className="min-w-0 flex-1 md:py-2 md:pr-2">
|
|
130
|
+
<div className="flex min-h-screen flex-col bg-white md:min-h-[calc(100vh-1rem)] md:rounded-xl md:shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_1px_2px_rgba(0,0,0,0.04)]">
|
|
131
|
+
<div className="hidden h-12 shrink-0 items-center gap-1.5 border-b border-zinc-100 px-6 text-[13px] md:flex">
|
|
132
|
+
{parent ? (
|
|
133
|
+
<>
|
|
134
|
+
<Link href={parent.href} className="text-zinc-500 transition-colors hover:text-zinc-900">
|
|
135
|
+
{parent.label}
|
|
136
|
+
</Link>
|
|
137
|
+
<ChevronRight className="size-3.5 text-zinc-300" />
|
|
138
|
+
</>
|
|
139
|
+
) : null}
|
|
140
|
+
<span className="truncate font-medium text-zinc-900">{title}</span>
|
|
141
|
+
</div>
|
|
142
|
+
<main className="min-w-0 flex-1 px-4 py-6 sm:px-6 md:px-8 md:py-8">{children}</main>
|
|
72
143
|
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function Sidebar({
|
|
150
|
+
active,
|
|
151
|
+
activeProjectId,
|
|
152
|
+
tenantId,
|
|
153
|
+
userId,
|
|
154
|
+
userName,
|
|
155
|
+
userEmail,
|
|
156
|
+
orgName,
|
|
157
|
+
projects,
|
|
158
|
+
onNavigate,
|
|
159
|
+
}: {
|
|
160
|
+
active: NavKey;
|
|
161
|
+
activeProjectId?: string;
|
|
162
|
+
tenantId: string;
|
|
163
|
+
userId: string;
|
|
164
|
+
userName: string;
|
|
165
|
+
userEmail: string;
|
|
166
|
+
orgName?: string;
|
|
167
|
+
projects: ShellProject[];
|
|
168
|
+
onNavigate: () => void;
|
|
169
|
+
}) {
|
|
170
|
+
const router = useRouter();
|
|
171
|
+
const live = useLiveRows<ShellProject>("Project", projects, tenantId)
|
|
172
|
+
.filter((p) => (p.status ?? "active") !== "archived")
|
|
173
|
+
.sort((a, b) => a.name.localeCompare(b.name, "en"));
|
|
174
|
+
const main = NAV.filter((n) => !n.group);
|
|
175
|
+
const workspace = NAV.filter((n) => n.group === "workspace");
|
|
176
|
+
return (
|
|
177
|
+
<div className="flex h-full flex-col px-3 pb-3">
|
|
178
|
+
<div className="flex h-14 shrink-0 items-center gap-2 px-2">
|
|
179
|
+
<Link href="/" className="flex items-center gap-2" onClick={onNavigate}>
|
|
180
|
+
<span className="flex size-[22px] items-center justify-center rounded-[6px] bg-zinc-900 text-[12px] font-bold text-white">
|
|
181
|
+
{siteConfig.brand.letter}
|
|
182
|
+
</span>
|
|
183
|
+
<span className="text-[14px] font-semibold tracking-tight">{siteConfig.brand.name}</span>
|
|
184
|
+
</Link>
|
|
185
|
+
</div>
|
|
73
186
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
187
|
+
{/* Switching workspaces re-renders the dashboard for the new tenant.
|
|
188
|
+
router.push re-fetches the SSR page without a full reload. */}
|
|
189
|
+
<div className="[&>div]:w-full [&_button[aria-haspopup]]:w-full [&_button[aria-haspopup]]:justify-start [&_button[aria-haspopup]]:border-zinc-200/80 [&_button[aria-haspopup]]:bg-white [&_button[aria-haspopup]]:px-2 [&_button[aria-haspopup]]:py-1.5 [&_button[aria-haspopup]]:text-[13px] [&_button[aria-haspopup]]:shadow-[0_1px_1px_rgba(0,0,0,0.03)] [&_button[aria-haspopup]>span:nth-child(2)]:flex-1 [&_button[aria-haspopup]>span:nth-child(2)]:text-left">
|
|
190
|
+
<OrganizationSwitcher
|
|
191
|
+
hidePersonal
|
|
192
|
+
initialActiveName={orgName}
|
|
193
|
+
onSwitched={() => {
|
|
194
|
+
onNavigate();
|
|
195
|
+
router.push("/dashboard");
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<nav className="mt-4 flex-1 overflow-y-auto">
|
|
201
|
+
<div className="space-y-px">
|
|
202
|
+
{main.map((n) => (
|
|
203
|
+
<NavItem key={n.key} item={n} isActive={active === n.key && !activeProjectId} onNavigate={onNavigate} />
|
|
204
|
+
))}
|
|
84
205
|
</div>
|
|
85
206
|
|
|
86
|
-
|
|
87
|
-
<div className="
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
207
|
+
{live.length > 0 ? (
|
|
208
|
+
<div className="mt-5">
|
|
209
|
+
<div className="px-2 pb-1 text-[12px] font-medium text-zinc-400">Active projects</div>
|
|
210
|
+
<div className="space-y-px">
|
|
211
|
+
{live.map((p) => (
|
|
212
|
+
<Link
|
|
213
|
+
key={p.id}
|
|
214
|
+
href={`/dashboard/projects/${p.id}`}
|
|
215
|
+
onClick={onNavigate}
|
|
216
|
+
className={
|
|
217
|
+
"flex items-center gap-2 rounded-md px-2 py-[5px] text-[13px] transition-colors " +
|
|
218
|
+
(activeProjectId === p.id
|
|
219
|
+
? "bg-white font-medium text-zinc-900 shadow-[0_0_0_1px_rgba(0,0,0,0.05),0_1px_2px_rgba(0,0,0,0.05)]"
|
|
220
|
+
: "text-zinc-600 hover:bg-zinc-200/50 hover:text-zinc-900")
|
|
221
|
+
}
|
|
222
|
+
>
|
|
223
|
+
<ProjectMark id={p.id} name={p.name} className="size-[18px] text-[8.5px]" />
|
|
224
|
+
<span className="truncate">{p.name}</span>
|
|
225
|
+
</Link>
|
|
226
|
+
))}
|
|
102
227
|
</div>
|
|
103
|
-
|
|
104
|
-
|
|
228
|
+
</div>
|
|
229
|
+
) : null}
|
|
105
230
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
231
|
+
<div className="mt-5">
|
|
232
|
+
<div className="px-2 pb-1 text-[12px] font-medium text-zinc-400">Workspace</div>
|
|
233
|
+
<div className="space-y-px">
|
|
234
|
+
{workspace.map((n) => (
|
|
235
|
+
<NavItem key={n.key} item={n} isActive={active === n.key} onNavigate={onNavigate} />
|
|
236
|
+
))}
|
|
237
|
+
</div>
|
|
111
238
|
</div>
|
|
112
|
-
</
|
|
239
|
+
</nav>
|
|
113
240
|
|
|
114
|
-
<
|
|
115
|
-
<header className="flex h-14 shrink-0 items-center justify-between border-b border-zinc-200/70 px-6">
|
|
116
|
-
<h1 className="text-[15px] font-semibold">{title}</h1>
|
|
117
|
-
{/* The sidebar (and its account card) is hidden below md, so the
|
|
118
|
-
top bar carries the account menu on small screens only. */}
|
|
119
|
-
<div className="md:hidden">
|
|
120
|
-
<UserMenu email={userEmail} direction="down" />
|
|
121
|
-
</div>
|
|
122
|
-
</header>
|
|
123
|
-
<main className="flex-1 p-6">
|
|
124
|
-
<div className="mx-auto max-w-4xl">{children}</div>
|
|
125
|
-
</main>
|
|
126
|
-
</div>
|
|
241
|
+
<UserMenu userId={userId} name={userName} email={userEmail} />
|
|
127
242
|
</div>
|
|
128
243
|
);
|
|
129
244
|
}
|
|
130
245
|
|
|
131
|
-
// A single sidebar row. The active row reads as a raised white card — layered
|
|
132
|
-
// translucent shadows instead of a hard border, so it sits naturally on the
|
|
133
|
-
// zinc-50 column. Inactive rows tint on hover only.
|
|
134
246
|
function NavItem({
|
|
135
247
|
item,
|
|
136
248
|
isActive,
|
|
249
|
+
onNavigate,
|
|
137
250
|
}: {
|
|
138
251
|
item: (typeof NAV)[number];
|
|
139
252
|
isActive: boolean;
|
|
253
|
+
onNavigate: () => void;
|
|
140
254
|
}) {
|
|
141
255
|
return (
|
|
142
256
|
<Link
|
|
143
257
|
href={item.href}
|
|
258
|
+
onClick={onNavigate}
|
|
144
259
|
className={
|
|
145
|
-
"flex items-center gap-2.5 rounded-
|
|
260
|
+
"flex items-center gap-2.5 rounded-md px-2 py-[6px] text-[13px] transition-colors " +
|
|
146
261
|
(isActive
|
|
147
|
-
? "bg-white text-zinc-900 shadow-[
|
|
262
|
+
? "bg-white font-medium text-zinc-900 shadow-[0_0_0_1px_rgba(0,0,0,0.05),0_1px_2px_rgba(0,0,0,0.05)]"
|
|
148
263
|
: "text-zinc-600 hover:bg-zinc-200/50 hover:text-zinc-900")
|
|
149
264
|
}
|
|
150
265
|
>
|
|
151
|
-
<item.Icon
|
|
152
|
-
className={"size-[17px] " + (isActive ? "text-zinc-700" : "text-zinc-400")}
|
|
153
|
-
strokeWidth={2}
|
|
154
|
-
/>
|
|
266
|
+
<item.Icon className={"size-4 " + (isActive ? "text-zinc-800" : "text-zinc-400")} strokeWidth={1.75} />
|
|
155
267
|
{item.label}
|
|
156
268
|
</Link>
|
|
157
269
|
);
|
|
158
270
|
}
|
|
159
271
|
|
|
160
|
-
//
|
|
161
|
-
// on click with no extra state
|
|
162
|
-
|
|
163
|
-
// avatar with a downward menu.
|
|
164
|
-
function UserMenu({
|
|
165
|
-
email,
|
|
166
|
-
direction,
|
|
167
|
-
}: {
|
|
168
|
-
email: string;
|
|
169
|
-
direction: "up" | "down";
|
|
170
|
-
}) {
|
|
272
|
+
// Account card pinned to the bottom of the sidebar. Native <details>, so it
|
|
273
|
+
// opens on click with no extra state; the menu opens upward.
|
|
274
|
+
function UserMenu({ userId, name, email }: { userId: string; name: string; email: string }) {
|
|
171
275
|
const { signOut } = useAuth();
|
|
172
|
-
const initial = (email.trim()[0] || "?").toUpperCase();
|
|
173
276
|
async function onSignOut() {
|
|
174
277
|
await signOut();
|
|
175
278
|
window.location.assign("/");
|
|
176
279
|
}
|
|
177
|
-
const up = direction === "up";
|
|
178
280
|
return (
|
|
179
|
-
<details className="
|
|
180
|
-
<summary
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
: "flex size-9 items-center justify-center")
|
|
186
|
-
}
|
|
187
|
-
>
|
|
188
|
-
<span className="flex size-7 shrink-0 items-center justify-center rounded-full bg-zinc-900 text-[11px] font-semibold text-white">
|
|
189
|
-
{initial}
|
|
281
|
+
<details className="relative">
|
|
282
|
+
<summary className="flex cursor-pointer select-none list-none items-center gap-2.5 rounded-lg px-2 py-2 transition-colors marker:hidden hover:bg-zinc-200/50 [&::-webkit-details-marker]:hidden">
|
|
283
|
+
<Avatar id={userId} name={name} />
|
|
284
|
+
<span className="min-w-0 flex-1 leading-tight">
|
|
285
|
+
<span className="block truncate text-[13px] font-medium text-zinc-800">{name}</span>
|
|
286
|
+
<span className="block truncate text-[11.5px] text-zinc-500">{email}</span>
|
|
190
287
|
</span>
|
|
191
|
-
{
|
|
192
|
-
<>
|
|
193
|
-
<span className="min-w-0 flex-1 truncate text-[13px] font-medium text-zinc-700">
|
|
194
|
-
{email || "Signed in"}
|
|
195
|
-
</span>
|
|
196
|
-
<ChevronsUpDown className="size-3.5 shrink-0 text-zinc-400" strokeWidth={2} />
|
|
197
|
-
</>
|
|
198
|
-
) : null}
|
|
288
|
+
<ChevronsUpDown className="size-3.5 shrink-0 text-zinc-400" strokeWidth={1.75} />
|
|
199
289
|
</summary>
|
|
200
|
-
<div
|
|
201
|
-
className={
|
|
202
|
-
"absolute z-40 w-56 overflow-hidden rounded-xl bg-white p-1 shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_16px_48px_-16px_rgba(0,0,0,0.25)] " +
|
|
203
|
-
(up ? "bottom-full left-0 mb-2" : "right-0 top-full mt-2")
|
|
204
|
-
}
|
|
205
|
-
>
|
|
206
|
-
<div className="border-b border-zinc-100 px-2.5 py-2">
|
|
207
|
-
<div className="truncate text-[13px] font-medium text-zinc-900">
|
|
208
|
-
{email || "Signed in"}
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
290
|
+
<div className="absolute bottom-full left-0 z-40 mb-2 w-full min-w-52 overflow-hidden rounded-xl bg-white p-1 shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_16px_48px_-16px_rgba(0,0,0,0.25)]">
|
|
211
291
|
<a
|
|
212
292
|
href="/"
|
|
213
293
|
className="flex items-center gap-2 rounded-lg px-2.5 py-2 text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
|
|
214
294
|
>
|
|
215
|
-
<ExternalLink className="size-4 text-zinc-400" strokeWidth={
|
|
295
|
+
<ExternalLink className="size-4 text-zinc-400" strokeWidth={1.75} />
|
|
216
296
|
View site
|
|
217
297
|
</a>
|
|
218
298
|
<button
|
|
@@ -220,7 +300,7 @@ function UserMenu({
|
|
|
220
300
|
onClick={onSignOut}
|
|
221
301
|
className="flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
|
|
222
302
|
>
|
|
223
|
-
<LogOut className="size-4 text-zinc-400" strokeWidth={
|
|
303
|
+
<LogOut className="size-4 text-zinc-400" strokeWidth={1.75} />
|
|
224
304
|
Sign out
|
|
225
305
|
</button>
|
|
226
306
|
</div>
|