@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
|
@@ -6,7 +6,7 @@ import { EnsureGuest } from "@pylonsync/client";
|
|
|
6
6
|
import { ArrowUpRight, ChevronUp } from "lucide-react";
|
|
7
7
|
import { siteConfig } from "@/lib/site.config";
|
|
8
8
|
import { LINK } from "@/components/marketing";
|
|
9
|
-
import { parseTags, type ListingRow } from "@/lib/directory";
|
|
9
|
+
import { monogram, parseTags, type ListingRow } from "@/lib/directory";
|
|
10
10
|
|
|
11
11
|
// The browse island: search box, category rail, and the list.
|
|
12
12
|
// `db.useSearch("Listing", …)` is a LIVE faceted full-text search: it re-runs
|
|
@@ -89,18 +89,18 @@ function BrowseInner() {
|
|
|
89
89
|
className="h-10 w-full border border-zinc-300 bg-white px-3 font-mono text-[13px] text-zinc-900 outline-none placeholder:text-zinc-400 focus:border-brand"
|
|
90
90
|
/>
|
|
91
91
|
|
|
92
|
-
<div className="mt-6 grid gap-
|
|
92
|
+
<div className="mt-6 grid grid-cols-[minmax(0,1fr)] gap-6 lg:grid-cols-[180px_minmax(0,1fr)] lg:gap-8">
|
|
93
93
|
{/* Category rail. Live counts per category come from the search. On
|
|
94
|
-
narrow screens the same links
|
|
95
|
-
<aside>
|
|
94
|
+
narrow screens the same links wrap as a row of chips. */}
|
|
95
|
+
<aside className="min-w-0">
|
|
96
96
|
<p className="border-b border-zinc-200 pb-2 text-[12px] font-medium uppercase tracking-wide text-zinc-500">
|
|
97
97
|
Category
|
|
98
98
|
</p>
|
|
99
|
-
<ul className="mt-
|
|
99
|
+
<ul className="mt-3 flex flex-wrap gap-2 lg:mt-2 lg:flex-col lg:gap-0">{facetLinks}</ul>
|
|
100
100
|
</aside>
|
|
101
101
|
|
|
102
102
|
{/* Results */}
|
|
103
|
-
<div>
|
|
103
|
+
<div className="min-w-0">
|
|
104
104
|
<div className="flex items-center justify-between border-b border-zinc-200 pb-2 text-[13px] text-zinc-500">
|
|
105
105
|
<span className={MONO_SMALL}>
|
|
106
106
|
{search.total} {search.total === 1 ? "tool" : "tools"}
|
|
@@ -163,14 +163,16 @@ function FacetLink({
|
|
|
163
163
|
onClick: () => void;
|
|
164
164
|
}) {
|
|
165
165
|
return (
|
|
166
|
-
<li
|
|
166
|
+
<li>
|
|
167
167
|
<button
|
|
168
168
|
type="button"
|
|
169
169
|
onClick={onClick}
|
|
170
170
|
aria-pressed={active}
|
|
171
171
|
className={
|
|
172
|
-
"flex w-full items-baseline gap-2
|
|
173
|
-
(active
|
|
172
|
+
"flex w-full items-baseline gap-2 border px-2.5 py-1 text-left text-[13px] lg:justify-between lg:border-0 lg:px-0 lg:py-0.5 lg:text-[14px] " +
|
|
173
|
+
(active
|
|
174
|
+
? "border-zinc-900 font-medium text-zinc-900"
|
|
175
|
+
: "border-zinc-200 text-brand hover:border-zinc-300 lg:hover:underline")
|
|
174
176
|
}
|
|
175
177
|
>
|
|
176
178
|
<span>{label}</span>
|
|
@@ -183,8 +185,9 @@ function FacetLink({
|
|
|
183
185
|
function ListingRowItem({ listing }: { listing: ListingRow }) {
|
|
184
186
|
const tags = parseTags(listing.tags);
|
|
185
187
|
return (
|
|
186
|
-
<li className="grid grid-cols-[
|
|
188
|
+
<li className="grid grid-cols-[44px_36px_minmax(0,1fr)_20px] items-start gap-3 border-b border-zinc-200 py-3">
|
|
187
189
|
<VoteButton listingId={listing.id} votes={listing.votes} />
|
|
190
|
+
<ToolMark name={listing.name} />
|
|
188
191
|
<div className="min-w-0">
|
|
189
192
|
<div className="flex flex-wrap items-baseline gap-x-2">
|
|
190
193
|
<a href={listing.url} target="_blank" rel="noopener noreferrer" className="text-[14px] font-semibold text-zinc-900 hover:text-brand">
|
|
@@ -211,6 +214,20 @@ function ListingRowItem({ listing }: { listing: ListingRow }) {
|
|
|
211
214
|
);
|
|
212
215
|
}
|
|
213
216
|
|
|
217
|
+
// A tool's monogram mark (see monogram in lib/directory.ts).
|
|
218
|
+
function ToolMark({ name }: { name: string }) {
|
|
219
|
+
const m = monogram(name);
|
|
220
|
+
return (
|
|
221
|
+
<span
|
|
222
|
+
aria-hidden
|
|
223
|
+
className="flex size-9 items-center justify-center rounded-md font-mono text-[13px] font-semibold tracking-tight"
|
|
224
|
+
style={{ backgroundColor: m.bg, color: m.fg }}
|
|
225
|
+
>
|
|
226
|
+
{m.letters}
|
|
227
|
+
</span>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
214
231
|
// The upvote control. Clicking calls the public `upvote` mutation; the count
|
|
215
232
|
// you see comes from the LIVE search, so it ticks up here and in every other
|
|
216
233
|
// open tab. localStorage remembers your votes so the arrow stays "voted".
|
|
@@ -267,8 +284,9 @@ function BrowseSkeleton({ bare }: { bare?: boolean }) {
|
|
|
267
284
|
const list = (
|
|
268
285
|
<ul>
|
|
269
286
|
{Array.from({ length: 6 }).map((_, i) => (
|
|
270
|
-
<li key={i} className="grid grid-cols-[
|
|
287
|
+
<li key={i} className="grid grid-cols-[44px_36px_minmax(0,1fr)_20px] gap-3 border-b border-zinc-200 py-3">
|
|
271
288
|
<div className="mx-auto h-8 w-6 animate-pulse bg-zinc-100" />
|
|
289
|
+
<div className="size-9 animate-pulse rounded-md bg-zinc-100" />
|
|
272
290
|
<div className="space-y-2 py-0.5">
|
|
273
291
|
<div className="h-3.5 w-1/3 animate-pulse bg-zinc-100" />
|
|
274
292
|
<div className="h-3 w-2/3 animate-pulse bg-zinc-100" />
|
|
@@ -281,7 +299,7 @@ function BrowseSkeleton({ bare }: { bare?: boolean }) {
|
|
|
281
299
|
return (
|
|
282
300
|
<div>
|
|
283
301
|
<div className="h-10 w-full animate-pulse border border-zinc-200 bg-zinc-50" />
|
|
284
|
-
<div className="mt-6 grid gap-8 lg:grid-cols-[
|
|
302
|
+
<div className="mt-6 grid grid-cols-[minmax(0,1fr)] gap-8 lg:grid-cols-[180px_minmax(0,1fr)]">
|
|
285
303
|
<div className="hidden h-40 animate-pulse bg-zinc-50 lg:block" />
|
|
286
304
|
{list}
|
|
287
305
|
</div>
|
|
@@ -16,11 +16,13 @@ export const metadata: Metadata = {
|
|
|
16
16
|
// Listing table. Copy comes from siteConfig; the listings seed on first visit.
|
|
17
17
|
// Doesn't read `auth`, so the public page stays cacheable.
|
|
18
18
|
export default function LandingPage() {
|
|
19
|
-
const {
|
|
19
|
+
const { intro } = siteConfig;
|
|
20
20
|
return (
|
|
21
|
-
<div className={`${WRAP} pb-20 pt-
|
|
22
|
-
<h1 className="
|
|
23
|
-
|
|
21
|
+
<div className={`${WRAP} pb-20 pt-10 sm:pt-14`}>
|
|
22
|
+
<h1 className="text-balance text-[1.75rem] font-semibold leading-tight tracking-[-0.02em] text-zinc-900 sm:text-[2.25rem]">
|
|
23
|
+
{intro.headline}
|
|
24
|
+
</h1>
|
|
25
|
+
<p className="mt-2 max-w-2xl text-[15px] leading-relaxed text-zinc-600">{intro.description}</p>
|
|
24
26
|
<div id="browse" className="mt-5">
|
|
25
27
|
<DirectoryBrowse />
|
|
26
28
|
</div>
|
|
@@ -59,7 +59,7 @@ export function AuthForm() {
|
|
|
59
59
|
required
|
|
60
60
|
autoComplete="email"
|
|
61
61
|
placeholder="you@yourbusiness.com"
|
|
62
|
-
className="h-10 w-full
|
|
62
|
+
className="h-10 w-full border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none transition placeholder:text-zinc-400 focus:border-brand"
|
|
63
63
|
/>
|
|
64
64
|
</label>
|
|
65
65
|
<label className="block">
|
|
@@ -71,18 +71,18 @@ export function AuthForm() {
|
|
|
71
71
|
required
|
|
72
72
|
autoComplete={mode === "login" ? "current-password" : "new-password"}
|
|
73
73
|
placeholder={mode === "login" ? "Your password" : "At least 10 characters"}
|
|
74
|
-
className="h-10 w-full
|
|
74
|
+
className="h-10 w-full border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none transition placeholder:text-zinc-400 focus:border-brand"
|
|
75
75
|
/>
|
|
76
76
|
</label>
|
|
77
77
|
{error ? (
|
|
78
|
-
<p className="
|
|
78
|
+
<p className="border border-red-200 bg-red-50 px-3 py-2 text-[13px] leading-snug text-red-700">
|
|
79
79
|
{error}
|
|
80
80
|
</p>
|
|
81
81
|
) : null}
|
|
82
82
|
<button
|
|
83
83
|
type="submit"
|
|
84
84
|
disabled={pending}
|
|
85
|
-
className="inline-flex h-10 w-full items-center justify-center
|
|
85
|
+
className="inline-flex h-10 w-full items-center justify-center bg-zinc-900 text-sm font-medium text-white transition-colors hover:bg-zinc-700 disabled:opacity-60"
|
|
86
86
|
>
|
|
87
87
|
{pending ? "…" : mode === "login" ? "Sign in" : "Create account"}
|
|
88
88
|
</button>
|
|
@@ -3,13 +3,27 @@
|
|
|
3
3
|
import React, { useEffect, useState } from "react";
|
|
4
4
|
import { db, callFn } from "@pylonsync/react";
|
|
5
5
|
import { useAuth } from "@pylonsync/client";
|
|
6
|
-
import {
|
|
6
|
+
import { ArrowUpRight, Check, LogOut, X } from "lucide-react";
|
|
7
|
+
import {
|
|
8
|
+
monogram,
|
|
9
|
+
parseTags,
|
|
10
|
+
type SubmissionRow,
|
|
11
|
+
type OwnerSubmissionsResult,
|
|
12
|
+
} from "@/lib/directory";
|
|
7
13
|
|
|
8
14
|
// The curator's moderation queue. Liveness rides the public Listing table:
|
|
9
15
|
// `db.useQuery("Listing")` re-renders when listings change (approving a
|
|
10
16
|
// submission creates one), and that same signal re-fetches the owner-gated
|
|
11
|
-
// `submissionsForOwner
|
|
17
|
+
// `submissionsForOwner`, so the queue stays current without a reload, while
|
|
12
18
|
// submitter PII only ever travels through the gated call.
|
|
19
|
+
type Filter = "new" | "approved" | "rejected";
|
|
20
|
+
|
|
21
|
+
const FILTERS: { id: Filter; label: string }[] = [
|
|
22
|
+
{ id: "new", label: "Pending" },
|
|
23
|
+
{ id: "approved", label: "Approved" },
|
|
24
|
+
{ id: "rejected", label: "Rejected" },
|
|
25
|
+
];
|
|
26
|
+
|
|
13
27
|
export function DirectoryDashboard({ userEmail }: { userEmail: string }) {
|
|
14
28
|
const { data: listings } = db.useQuery<{ id: string }>("Listing");
|
|
15
29
|
const liveCount = listings.length;
|
|
@@ -18,6 +32,8 @@ export function DirectoryDashboard({ userEmail }: { userEmail: string }) {
|
|
|
18
32
|
const [denied, setDenied] = useState(false);
|
|
19
33
|
const [error, setError] = useState<string | null>(null);
|
|
20
34
|
const [busyId, setBusyId] = useState<string | null>(null);
|
|
35
|
+
const [filter, setFilter] = useState<Filter>("new");
|
|
36
|
+
const [seedDone, setSeedDone] = useState(false);
|
|
21
37
|
|
|
22
38
|
async function load() {
|
|
23
39
|
try {
|
|
@@ -33,10 +49,20 @@ export function DirectoryDashboard({ userEmail }: { userEmail: string }) {
|
|
|
33
49
|
}
|
|
34
50
|
}
|
|
35
51
|
|
|
52
|
+
// In `pylon dev` an empty queue gets demo submissions (functions/seedDemo.ts)
|
|
53
|
+
// before the first load. On a deploy, or for a non-owner, the call writes
|
|
54
|
+
// nothing.
|
|
36
55
|
useEffect(() => {
|
|
56
|
+
void callFn("seedDemo", {})
|
|
57
|
+
.catch(() => {})
|
|
58
|
+
.finally(() => setSeedDone(true));
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!seedDone) return;
|
|
37
63
|
void load();
|
|
38
64
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
|
-
}, [liveCount]);
|
|
65
|
+
}, [liveCount, seedDone]);
|
|
40
66
|
|
|
41
67
|
async function act(id: string, fn: "approveSubmission" | "rejectSubmission") {
|
|
42
68
|
setBusyId(id);
|
|
@@ -50,113 +76,170 @@ export function DirectoryDashboard({ userEmail }: { userEmail: string }) {
|
|
|
50
76
|
|
|
51
77
|
if (denied) return <OwnerOnly email={userEmail} />;
|
|
52
78
|
if (error) {
|
|
53
|
-
return <div className="
|
|
79
|
+
return <div className="border border-red-200 bg-red-50 px-5 py-4 text-sm text-red-700">{error}</div>;
|
|
54
80
|
}
|
|
55
81
|
if (!submissions) return <Skeleton />;
|
|
56
82
|
|
|
57
|
-
const
|
|
58
|
-
const
|
|
83
|
+
const counts: Record<Filter, number> = { new: 0, approved: 0, rejected: 0 };
|
|
84
|
+
for (const s of submissions) {
|
|
85
|
+
if (s.status === "new" || s.status === "approved" || s.status === "rejected") counts[s.status]++;
|
|
86
|
+
}
|
|
87
|
+
const shown = submissions.filter((s) => s.status === filter);
|
|
59
88
|
|
|
60
89
|
return (
|
|
61
90
|
<div className="space-y-8">
|
|
62
91
|
<div>
|
|
63
|
-
<h1 className="text-
|
|
64
|
-
|
|
65
|
-
|
|
92
|
+
<h1 className="text-[1.75rem] font-semibold leading-tight tracking-[-0.02em] text-zinc-900 sm:text-[2rem]">
|
|
93
|
+
Review queue
|
|
94
|
+
</h1>
|
|
95
|
+
<p className="mt-2 max-w-xl text-[14px] leading-relaxed text-zinc-600">
|
|
96
|
+
New submissions land here. Approving one publishes it to the directory right away.
|
|
66
97
|
</p>
|
|
67
98
|
</div>
|
|
68
99
|
|
|
69
|
-
<
|
|
70
|
-
<Stat label="Pending" value={
|
|
71
|
-
<Stat label="Approved" value={
|
|
72
|
-
<Stat label="Live listings" value={
|
|
73
|
-
</
|
|
100
|
+
<dl className="grid grid-cols-3 divide-x divide-zinc-200 border-y border-zinc-200">
|
|
101
|
+
<Stat label="Pending" value={counts.new} accent={counts.new > 0} />
|
|
102
|
+
<Stat label="Approved" value={counts.approved} />
|
|
103
|
+
<Stat label="Live listings" value={liveCount} />
|
|
104
|
+
</dl>
|
|
74
105
|
|
|
75
|
-
<
|
|
76
|
-
<div className="
|
|
77
|
-
|
|
106
|
+
<section>
|
|
107
|
+
<div className="flex items-center gap-4 border-b border-zinc-200">
|
|
108
|
+
{FILTERS.map((f) => (
|
|
109
|
+
<button
|
|
110
|
+
key={f.id}
|
|
111
|
+
type="button"
|
|
112
|
+
onClick={() => setFilter(f.id)}
|
|
113
|
+
aria-pressed={filter === f.id}
|
|
114
|
+
className={
|
|
115
|
+
"-mb-px border-b-2 pb-2 text-[14px] transition-colors " +
|
|
116
|
+
(filter === f.id
|
|
117
|
+
? "border-zinc-900 font-medium text-zinc-900"
|
|
118
|
+
: "border-transparent text-zinc-500 hover:text-zinc-900")
|
|
119
|
+
}
|
|
120
|
+
>
|
|
121
|
+
{f.label} <span className="font-mono text-[12px] text-zinc-400">{counts[f.id]}</span>
|
|
122
|
+
</button>
|
|
123
|
+
))}
|
|
78
124
|
</div>
|
|
79
|
-
|
|
80
|
-
|
|
125
|
+
|
|
126
|
+
{shown.length === 0 ? (
|
|
127
|
+
<p className="py-12 text-center text-[14px] text-zinc-500">
|
|
128
|
+
{filter === "new"
|
|
129
|
+
? "Nothing to review. New submissions appear here as they arrive."
|
|
130
|
+
: `No ${filter} submissions.`}
|
|
131
|
+
</p>
|
|
81
132
|
) : (
|
|
82
|
-
<ul
|
|
83
|
-
{
|
|
84
|
-
<
|
|
85
|
-
<div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-2">
|
|
86
|
-
<div className="min-w-0 flex-1">
|
|
87
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
88
|
-
<a href={s.url} target="_blank" rel="noopener noreferrer" className="text-[14px] font-medium text-zinc-900 hover:text-brand">
|
|
89
|
-
{s.name}
|
|
90
|
-
</a>
|
|
91
|
-
<span className="rounded-full bg-zinc-100 px-2 py-0.5 text-[11px] font-medium text-zinc-500">{s.category}</span>
|
|
92
|
-
<StatusBadge status={s.status} />
|
|
93
|
-
</div>
|
|
94
|
-
{s.tagline ? <p className="mt-1 text-[13.5px] text-zinc-600">{s.tagline}</p> : null}
|
|
95
|
-
<div className="mt-1 truncate text-[12px] text-zinc-400">
|
|
96
|
-
{s.submitterName} · {s.submitterEmail}
|
|
97
|
-
{parseTags(s.tags).length ? ` · ${parseTags(s.tags).map((t) => "#" + t).join(" ")}` : ""}
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
{s.status === "new" ? (
|
|
101
|
-
<div className="flex items-center gap-2">
|
|
102
|
-
<button
|
|
103
|
-
type="button"
|
|
104
|
-
disabled={busyId === s.id}
|
|
105
|
-
onClick={() => act(s.id, "approveSubmission")}
|
|
106
|
-
className="rounded-md bg-brand px-3 py-1.5 text-[12.5px] font-medium text-white transition-opacity hover:opacity-90 disabled:opacity-50"
|
|
107
|
-
>
|
|
108
|
-
{busyId === s.id ? "…" : "Approve"}
|
|
109
|
-
</button>
|
|
110
|
-
<button
|
|
111
|
-
type="button"
|
|
112
|
-
disabled={busyId === s.id}
|
|
113
|
-
onClick={() => act(s.id, "rejectSubmission")}
|
|
114
|
-
className="rounded-md border border-zinc-300 px-3 py-1.5 text-[12.5px] font-medium text-zinc-600 transition-colors hover:border-red-300 hover:text-red-600 disabled:opacity-50"
|
|
115
|
-
>
|
|
116
|
-
Reject
|
|
117
|
-
</button>
|
|
118
|
-
</div>
|
|
119
|
-
) : null}
|
|
120
|
-
</div>
|
|
121
|
-
</li>
|
|
133
|
+
<ul>
|
|
134
|
+
{shown.map((s) => (
|
|
135
|
+
<QueueItem key={s.id} s={s} busy={busyId === s.id} onAct={act} />
|
|
122
136
|
))}
|
|
123
137
|
</ul>
|
|
124
138
|
)}
|
|
125
|
-
</
|
|
139
|
+
</section>
|
|
126
140
|
</div>
|
|
127
141
|
);
|
|
128
142
|
}
|
|
129
143
|
|
|
130
|
-
function
|
|
144
|
+
function QueueItem({
|
|
145
|
+
s,
|
|
146
|
+
busy,
|
|
147
|
+
onAct,
|
|
148
|
+
}: {
|
|
149
|
+
s: SubmissionRow;
|
|
150
|
+
busy: boolean;
|
|
151
|
+
onAct: (id: string, fn: "approveSubmission" | "rejectSubmission") => void;
|
|
152
|
+
}) {
|
|
153
|
+
const m = monogram(s.name);
|
|
154
|
+
const tags = parseTags(s.tags);
|
|
131
155
|
return (
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
<li className="grid grid-cols-[36px_minmax(0,1fr)] gap-x-3 gap-y-3 border-b border-zinc-200 py-4 sm:grid-cols-[36px_minmax(0,1fr)_auto]">
|
|
157
|
+
<span
|
|
158
|
+
aria-hidden
|
|
159
|
+
className="flex size-9 items-center justify-center rounded-md font-mono text-[13px] font-semibold"
|
|
160
|
+
style={{ backgroundColor: m.bg, color: m.fg }}
|
|
161
|
+
>
|
|
162
|
+
{m.letters}
|
|
163
|
+
</span>
|
|
164
|
+
<div className="min-w-0">
|
|
165
|
+
<div className="flex flex-wrap items-baseline gap-x-2">
|
|
166
|
+
<a
|
|
167
|
+
href={s.url}
|
|
168
|
+
target="_blank"
|
|
169
|
+
rel="noopener noreferrer"
|
|
170
|
+
className="inline-flex items-center gap-1 text-[15px] font-semibold text-zinc-900 hover:text-brand"
|
|
171
|
+
>
|
|
172
|
+
{s.name}
|
|
173
|
+
<ArrowUpRight aria-hidden className="size-3.5 text-zinc-400" />
|
|
174
|
+
</a>
|
|
175
|
+
<span className="font-mono text-[12px] text-zinc-400">{s.category}</span>
|
|
176
|
+
</div>
|
|
177
|
+
<p className="mt-0.5 text-[14px] leading-snug text-zinc-700">{s.tagline}</p>
|
|
178
|
+
{s.description ? (
|
|
179
|
+
<p className="mt-1 max-w-2xl text-[13px] leading-relaxed text-zinc-500">{s.description}</p>
|
|
180
|
+
) : null}
|
|
181
|
+
<p className="mt-1.5 truncate font-mono text-[12px] text-zinc-400">
|
|
182
|
+
{s.submitterName} · {s.submitterEmail} · {ago(s.createdAt)}
|
|
183
|
+
{tags.length ? ` · ${tags.join(", ")}` : ""}
|
|
184
|
+
</p>
|
|
185
|
+
</div>
|
|
186
|
+
{s.status === "new" ? (
|
|
187
|
+
<div className="col-start-2 flex items-center gap-2 sm:col-start-3 sm:self-start">
|
|
188
|
+
<button
|
|
189
|
+
type="button"
|
|
190
|
+
disabled={busy}
|
|
191
|
+
onClick={() => onAct(s.id, "approveSubmission")}
|
|
192
|
+
className="inline-flex h-8 items-center gap-1.5 bg-brand px-3 text-[13px] font-medium text-white transition-opacity hover:opacity-90 disabled:opacity-50"
|
|
193
|
+
>
|
|
194
|
+
<Check aria-hidden className="size-3.5" />
|
|
195
|
+
Approve
|
|
196
|
+
</button>
|
|
197
|
+
<button
|
|
198
|
+
type="button"
|
|
199
|
+
disabled={busy}
|
|
200
|
+
onClick={() => onAct(s.id, "rejectSubmission")}
|
|
201
|
+
className="inline-flex h-8 items-center gap-1.5 border border-zinc-300 px-3 text-[13px] font-medium text-zinc-600 transition-colors hover:border-zinc-500 hover:text-zinc-900 disabled:opacity-50"
|
|
202
|
+
>
|
|
203
|
+
<X aria-hidden className="size-3.5" />
|
|
204
|
+
Reject
|
|
205
|
+
</button>
|
|
206
|
+
</div>
|
|
207
|
+
) : null}
|
|
208
|
+
</li>
|
|
136
209
|
);
|
|
137
210
|
}
|
|
138
211
|
|
|
139
|
-
function
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
212
|
+
function ago(iso: string): string {
|
|
213
|
+
const mins = Math.max(0, Math.round((Date.now() - Date.parse(iso)) / 60_000));
|
|
214
|
+
if (mins < 60) return `${mins}m ago`;
|
|
215
|
+
const hours = Math.round(mins / 60);
|
|
216
|
+
if (hours < 48) return `${hours}h ago`;
|
|
217
|
+
return `${Math.round(hours / 24)}d ago`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function Stat({ label, value, accent }: { label: string; value: number; accent?: boolean }) {
|
|
221
|
+
return (
|
|
222
|
+
<div className="px-4 py-4 first:pl-0">
|
|
223
|
+
<dt className="text-[12px] font-medium uppercase tracking-wide text-zinc-500">{label}</dt>
|
|
224
|
+
<dd className={"mt-1 font-mono text-[1.75rem] leading-none tabular-nums " + (accent ? "text-brand" : "text-zinc-900")}>
|
|
225
|
+
{value}
|
|
226
|
+
</dd>
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
148
229
|
}
|
|
149
230
|
|
|
150
231
|
function OwnerOnly({ email }: { email: string }) {
|
|
151
232
|
return (
|
|
152
|
-
<div className="
|
|
233
|
+
<div className="border border-dashed border-zinc-300 px-6 py-12 text-center">
|
|
153
234
|
<h1 className="text-lg font-semibold">This dashboard is owner-only</h1>
|
|
154
235
|
<p className="mx-auto mt-2 max-w-md text-sm text-zinc-500">
|
|
155
|
-
You
|
|
156
|
-
Only the
|
|
157
|
-
<code className="
|
|
158
|
-
|
|
159
|
-
|
|
236
|
+
You are signed in as <span className="font-medium text-zinc-700">{email || "this account"}</span>.
|
|
237
|
+
Only the directory owner can review submissions. Set{" "}
|
|
238
|
+
<code className="bg-zinc-100 px-1.5 py-0.5 font-mono text-[12px]">
|
|
239
|
+
PYLON_OWNER_EMAIL={email || "you@yourdirectory.com"}
|
|
240
|
+
</code>{" "}
|
|
241
|
+
in <code className="bg-zinc-100 px-1.5 py-0.5 font-mono text-[12px]">.env</code>, restart, and
|
|
242
|
+
reload. Or sign in with the owner account.
|
|
160
243
|
</p>
|
|
161
244
|
</div>
|
|
162
245
|
);
|
|
@@ -171,18 +254,22 @@ export function UserMenu({ email }: { email: string }) {
|
|
|
171
254
|
}
|
|
172
255
|
return (
|
|
173
256
|
<details className="group relative">
|
|
174
|
-
<summary
|
|
257
|
+
<summary
|
|
258
|
+
aria-label="Account"
|
|
259
|
+
className="flex size-7 cursor-pointer select-none list-none items-center justify-center bg-zinc-900 font-mono text-[12px] font-semibold text-white marker:hidden [&::-webkit-details-marker]:hidden"
|
|
260
|
+
>
|
|
175
261
|
{initial}
|
|
176
262
|
</summary>
|
|
177
|
-
<div className="absolute right-0 top-full z-40 mt-2 w-
|
|
178
|
-
<div className="border-b border-zinc-100 px-3 py-2">
|
|
179
|
-
|
|
263
|
+
<div className="absolute right-0 top-full z-40 mt-2 w-60 border border-zinc-200 bg-white py-1 shadow-[0_16px_40px_-20px_rgba(0,0,0,0.3)]">
|
|
264
|
+
<div className="truncate border-b border-zinc-100 px-3 py-2 font-mono text-[12px] text-zinc-700">
|
|
265
|
+
{email || "Signed in"}
|
|
180
266
|
</div>
|
|
181
267
|
<button
|
|
182
268
|
type="button"
|
|
183
269
|
onClick={onSignOut}
|
|
184
|
-
className="flex w-full items-center px-3 py-2 text-left text-[13px] text-zinc-700 transition-colors hover:bg-zinc-50"
|
|
270
|
+
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"
|
|
185
271
|
>
|
|
272
|
+
<LogOut aria-hidden className="size-3.5" />
|
|
186
273
|
Sign out
|
|
187
274
|
</button>
|
|
188
275
|
</div>
|
|
@@ -193,13 +280,13 @@ export function UserMenu({ email }: { email: string }) {
|
|
|
193
280
|
function Skeleton() {
|
|
194
281
|
return (
|
|
195
282
|
<div className="space-y-8">
|
|
196
|
-
<div className="h-
|
|
197
|
-
<div className="
|
|
198
|
-
|
|
199
|
-
|
|
283
|
+
<div className="h-8 w-48 animate-pulse bg-zinc-100" />
|
|
284
|
+
<div className="h-20 animate-pulse bg-zinc-50" />
|
|
285
|
+
<div className="space-y-3">
|
|
286
|
+
{[0, 1, 2, 3].map((i) => (
|
|
287
|
+
<div key={i} className="h-16 animate-pulse bg-zinc-50" />
|
|
200
288
|
))}
|
|
201
289
|
</div>
|
|
202
|
-
<div className="h-48 animate-pulse rounded-xl bg-zinc-100" />
|
|
203
290
|
</div>
|
|
204
291
|
);
|
|
205
292
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
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";
|
|
5
|
+
import { WRAP } from "@/components/marketing";
|
|
4
6
|
import { UserMenu, DirectoryDashboard } from "./dashboard-client";
|
|
5
7
|
|
|
6
8
|
export const metadata: Metadata = {
|
|
@@ -35,35 +37,35 @@ export default function DashboardPage({ auth, response, serverData }: PageProps)
|
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
// Dashboard chrome
|
|
39
|
-
// site, and the account menu (a client
|
|
40
|
+
// Dashboard chrome in the site's own look: the same slim white nav with the
|
|
41
|
+
// directory name, a link back to the site, and the account menu (a client
|
|
42
|
+
// island for sign-out).
|
|
40
43
|
function Shell({ email, children }: { email: string; children: React.ReactNode }) {
|
|
41
44
|
const { brand } = siteConfig;
|
|
42
45
|
return (
|
|
43
46
|
<div className="flex min-h-screen flex-col bg-white text-zinc-900">
|
|
44
47
|
<header className="border-b border-zinc-200">
|
|
45
|
-
<div className=
|
|
46
|
-
<div className="flex items-
|
|
47
|
-
<
|
|
48
|
-
{brand.
|
|
49
|
-
</
|
|
50
|
-
<span className="text-[
|
|
51
|
-
{brand.name} <span className="text-zinc-400">/ curator</span>
|
|
52
|
-
</span>
|
|
48
|
+
<div className={`${WRAP} flex h-12 items-center justify-between`}>
|
|
49
|
+
<div className="flex items-baseline gap-2.5">
|
|
50
|
+
<Link href="/" className="text-[15px] font-semibold text-zinc-900">
|
|
51
|
+
{brand.name}
|
|
52
|
+
</Link>
|
|
53
|
+
<span className="font-mono text-[12px] text-zinc-400">curator</span>
|
|
53
54
|
</div>
|
|
54
|
-
<div className="flex items-center gap-
|
|
55
|
+
<div className="flex items-center gap-5">
|
|
55
56
|
<Link
|
|
56
57
|
href="/"
|
|
57
|
-
className="text-[
|
|
58
|
+
className="hidden items-center gap-1 text-[14px] text-zinc-600 hover:text-zinc-900 sm:inline-flex"
|
|
58
59
|
>
|
|
59
|
-
View site
|
|
60
|
+
View site
|
|
61
|
+
<ArrowUpRight aria-hidden className="size-3.5" />
|
|
60
62
|
</Link>
|
|
61
63
|
<UserMenu email={email} />
|
|
62
64
|
</div>
|
|
63
65
|
</div>
|
|
64
66
|
</header>
|
|
65
67
|
<main className="flex-1">
|
|
66
|
-
<div className=
|
|
68
|
+
<div className={`${WRAP} py-10`}>{children}</div>
|
|
67
69
|
</main>
|
|
68
70
|
</div>
|
|
69
71
|
);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#1d4ed8"/>
|
|
2
|
+
<rect x="14" y="14" width="36" height="9" rx="2" fill="#ffffff"/>
|
|
3
|
+
<rect x="14" y="27.5" width="28" height="9" rx="2" fill="#ffffff" opacity="0.8"/>
|
|
4
|
+
<rect x="14" y="41" width="20" height="9" rx="2" fill="#ffffff" opacity="0.6"/></svg>
|
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { Link, type Metadata, type PageProps } from "@pylonsync/react";
|
|
3
3
|
import { AuthForm } from "../auth-form";
|
|
4
4
|
import { siteConfig } from "@/lib/site.config";
|
|
5
|
+
import { WRAP } from "@/components/marketing";
|
|
5
6
|
|
|
6
7
|
export const metadata: Metadata = {
|
|
7
8
|
title: `Sign in — ${siteConfig.brand.name}`,
|
|
@@ -18,21 +19,21 @@ export default function LoginPage({ auth, response }: PageProps) {
|
|
|
18
19
|
if (auth.user_id && !auth.user_id.startsWith("guest_")) response.redirect("/dashboard");
|
|
19
20
|
const { brand } = siteConfig;
|
|
20
21
|
return (
|
|
21
|
-
<div className="flex min-h-screen
|
|
22
|
-
<div className="
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
{brand.
|
|
26
|
-
</
|
|
27
|
-
</
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
<div className="flex min-h-screen flex-col bg-white text-zinc-900">
|
|
23
|
+
<div className="border-b border-zinc-200">
|
|
24
|
+
<div className={`${WRAP} flex h-12 items-center`}>
|
|
25
|
+
<Link href="/" className="text-[15px] font-semibold">
|
|
26
|
+
{brand.name}
|
|
27
|
+
</Link>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div className="flex flex-1 items-center justify-center px-5 py-16">
|
|
31
|
+
<div className="w-full max-w-[380px]">
|
|
32
|
+
<h1 className="text-[1.75rem] font-semibold tracking-[-0.02em]">Curator sign in</h1>
|
|
33
|
+
<p className="mt-2 text-[14px] text-zinc-600">Review submissions and publish them to {brand.name}.</p>
|
|
34
|
+
<div className="mt-8 border border-zinc-200 p-6">
|
|
35
|
+
<AuthForm />
|
|
36
|
+
</div>
|
|
36
37
|
</div>
|
|
37
38
|
</div>
|
|
38
39
|
</div>
|