@pylonsync/create-pylon 0.3.268 → 0.3.270

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/bin/create-pylon.js +11 -9
  2. package/package.json +1 -1
  3. package/templates/b2b/app/layout.tsx +1 -1
  4. package/templates/b2b/app/page.tsx +2 -2
  5. package/templates/b2b/tsconfig.json +1 -1
  6. package/templates/barebones/app/page.tsx +1 -1
  7. package/templates/barebones/tsconfig.json +1 -1
  8. package/templates/chat/app/page.tsx +1 -1
  9. package/templates/chat/tsconfig.json +1 -1
  10. package/templates/consumer/app/page.tsx +1 -1
  11. package/templates/consumer/tsconfig.json +1 -1
  12. package/templates/default/.env.example +19 -0
  13. package/templates/default/README.md +85 -0
  14. package/templates/default/app/auth-form.tsx +218 -0
  15. package/templates/default/app/auth-shell.tsx +76 -0
  16. package/templates/default/app/company/[slug]/page.tsx +28 -0
  17. package/templates/default/app/compare/[slug]/page.tsx +27 -0
  18. package/templates/default/app/dashboard/billing/page.tsx +49 -0
  19. package/templates/default/app/dashboard/dashboard-client.tsx +832 -0
  20. package/templates/default/app/dashboard/members/page.tsx +37 -0
  21. package/templates/default/app/dashboard/page.tsx +64 -0
  22. package/templates/default/app/dashboard/projects/page.tsx +37 -0
  23. package/templates/default/app/dashboard/settings/page.tsx +45 -0
  24. package/templates/{ssr → default}/app/globals.css +14 -0
  25. package/templates/default/app/layout.tsx +466 -0
  26. package/templates/default/app/login/page.tsx +27 -0
  27. package/templates/default/app/onboarding/onboarding-client.tsx +261 -0
  28. package/templates/default/app/onboarding/page.tsx +29 -0
  29. package/templates/default/app/page.tsx +653 -0
  30. package/templates/default/app/products/[slug]/page.tsx +134 -0
  31. package/templates/default/app/resources/[slug]/page.tsx +28 -0
  32. package/templates/default/app/signup/page.tsx +24 -0
  33. package/templates/default/app/sitemap.ts +40 -0
  34. package/templates/default/app/solutions/[slug]/page.tsx +28 -0
  35. package/templates/default/app.ts +194 -0
  36. package/templates/default/components/dashboard-shell.tsx +150 -0
  37. package/templates/default/components/marketing.tsx +370 -0
  38. package/templates/default/functions/_pylonStripeFindActiveSubForReference.ts +3 -0
  39. package/templates/default/functions/_pylonStripeFindByCustomerId.ts +3 -0
  40. package/templates/default/functions/_pylonStripeGetCustomerHolder.ts +3 -0
  41. package/templates/default/functions/_pylonStripeListSubsForReference.ts +3 -0
  42. package/templates/default/functions/_pylonStripeOrgMembership.ts +3 -0
  43. package/templates/default/functions/_pylonStripeSetCustomerId.ts +3 -0
  44. package/templates/default/functions/_pylonStripeUpsertSubscription.ts +3 -0
  45. package/templates/default/functions/cancelSubscription.ts +3 -0
  46. package/templates/default/functions/createBillingPortalSession.ts +3 -0
  47. package/templates/default/functions/createCheckoutSession.ts +3 -0
  48. package/templates/default/functions/restoreSubscription.ts +3 -0
  49. package/templates/default/functions/stripeWebhook.ts +3 -0
  50. package/templates/default/lib/billing.ts +46 -0
  51. package/templates/default/lib/products.ts +122 -0
  52. package/templates/default/lib/site.ts +261 -0
  53. package/templates/{ssr → default}/package.json +2 -0
  54. package/templates/{ssr → default}/tsconfig.json +2 -2
  55. package/templates/todo/app/page.tsx +1 -1
  56. package/templates/todo/tsconfig.json +1 -1
  57. package/templates/ssr/README.md +0 -56
  58. package/templates/ssr/app/auth-form.tsx +0 -142
  59. package/templates/ssr/app/dashboard/dashboard-client.tsx +0 -116
  60. package/templates/ssr/app/dashboard/page.tsx +0 -70
  61. package/templates/ssr/app/layout.tsx +0 -71
  62. package/templates/ssr/app/login/page.tsx +0 -47
  63. package/templates/ssr/app/page.tsx +0 -114
  64. package/templates/ssr/app/signup/page.tsx +0 -44
  65. package/templates/ssr/app/sitemap.ts +0 -27
  66. package/templates/ssr/app.ts +0 -94
  67. package/templates/ssr/functions/_keep.ts +0 -13
  68. /package/templates/{ssr → default}/AGENTS.md +0 -0
  69. /package/templates/{ssr → default}/app/error.tsx +0 -0
  70. /package/templates/{ssr → default}/app/not-found.tsx +0 -0
  71. /package/templates/{ssr → default}/app/robots.ts +0 -0
  72. /package/templates/{ssr → default}/components/ui/button.tsx +0 -0
  73. /package/templates/{ssr → default}/components/ui/card.tsx +0 -0
  74. /package/templates/{ssr → default}/components.json +0 -0
  75. /package/templates/{ssr → default}/gitignore +0 -0
  76. /package/templates/{ssr → default}/lib/utils.ts +0 -0
@@ -0,0 +1,37 @@
1
+ import React, { use } from "react";
2
+ import { type Metadata, type PageProps } from "@pylonsync/react";
3
+ import { DashboardShell } from "@/components/dashboard-shell";
4
+ import { Members } from "../dashboard-client";
5
+
6
+ export const metadata: Metadata = {
7
+ title: "Members — Acme",
8
+ robots: "noindex",
9
+ };
10
+
11
+ // `/dashboard/members` — the active org's roster + invites. The roster (with
12
+ // real emails) is loaded client-side from the framework's org-members endpoint;
13
+ // invites are gated to owners/admins both here and on the server.
14
+ export default function MembersPage({ auth, response, serverData }: PageProps) {
15
+ if (!auth.user_id) {
16
+ response.redirect("/login");
17
+ return null;
18
+ }
19
+ const me = use(serverData.get<{ email?: string }>("User", auth.user_id));
20
+ const org = auth.tenant_id
21
+ ? use(serverData.get<{ name?: string }>("Org", auth.tenant_id))
22
+ : null;
23
+ return (
24
+ <DashboardShell
25
+ active="members"
26
+ title="Members"
27
+ userEmail={me?.email ?? ""}
28
+ orgName={org?.name}
29
+ >
30
+ <Members
31
+ tenantId={auth.tenant_id}
32
+ currentUserId={auth.user_id}
33
+ role={auth.roles?.[0] ?? ""}
34
+ />
35
+ </DashboardShell>
36
+ );
37
+ }
@@ -0,0 +1,64 @@
1
+ import React, { use } from "react";
2
+ import { type Metadata, type PageProps } from "@pylonsync/react";
3
+ import { DashboardShell } from "@/components/dashboard-shell";
4
+ import {
5
+ Overview,
6
+ type Project,
7
+ type OrgMemberRow,
8
+ type Subscription,
9
+ } from "./dashboard-client";
10
+
11
+ export const metadata: Metadata = {
12
+ title: "Dashboard — Acme",
13
+ robots: "noindex",
14
+ };
15
+
16
+ // `app/dashboard/page.tsx` → `/dashboard`. Server-side auth gate, then the
17
+ // active org (`auth.tenant_id`) + this org's rows are read during the render
18
+ // via `serverData` + React 19 `use()` — resolved server-side and replayed on
19
+ // hydration, so the dashboard paints with real data on the first byte (no
20
+ // client fetch, no empty-state flash). The marketing nav/footer are suppressed
21
+ // for /dashboard in the root layout; the shell is the only chrome here.
22
+ export default function DashboardPage({
23
+ auth,
24
+ response,
25
+ serverData,
26
+ }: PageProps) {
27
+ if (!auth.user_id) {
28
+ response.redirect("/login");
29
+ return null;
30
+ }
31
+ const me = use(serverData.get<{ email?: string }>("User", auth.user_id));
32
+ const org = auth.tenant_id
33
+ ? use(serverData.get<{ name?: string }>("Org", auth.tenant_id))
34
+ : null;
35
+ const projects = use(serverData.list<Project>("Project"));
36
+ const members = use(serverData.list<OrgMemberRow>("OrgMember"));
37
+ // The OrgMember read policy returns this user's memberships across every org,
38
+ // so scope the count to the active workspace.
39
+ const memberCount = members.filter((m) => m.orgId === auth.tenant_id).length;
40
+ // Active-plan badge from the workspace's Stripe subscription (Free until one
41
+ // exists). Scoped to the active tenant by the plugin's read policy.
42
+ const subs = auth.tenant_id
43
+ ? use(serverData.list<Subscription>("StripeSubscription"))
44
+ : [];
45
+ const active = subs.find((s) =>
46
+ ["active", "trialing", "past_due"].includes(s.status),
47
+ );
48
+ const plan = active ? active.plan : "free";
49
+ return (
50
+ <DashboardShell
51
+ active="overview"
52
+ title="Overview"
53
+ userEmail={me?.email ?? ""}
54
+ orgName={org?.name}
55
+ >
56
+ <Overview
57
+ tenantId={auth.tenant_id}
58
+ projects={projects}
59
+ memberCount={memberCount}
60
+ plan={plan}
61
+ />
62
+ </DashboardShell>
63
+ );
64
+ }
@@ -0,0 +1,37 @@
1
+ import React, { use } from "react";
2
+ import { type Metadata, type PageProps } from "@pylonsync/react";
3
+ import { DashboardShell } from "@/components/dashboard-shell";
4
+ import { Projects, type Project } from "../dashboard-client";
5
+
6
+ export const metadata: Metadata = {
7
+ title: "Projects — Acme",
8
+ robots: "noindex",
9
+ };
10
+
11
+ // `/dashboard/projects` — this org's projects, server-resolved via `use()` then
12
+ // kept live + optimistic by `db` on the client.
13
+ export default function ProjectsPage({
14
+ auth,
15
+ response,
16
+ serverData,
17
+ }: PageProps) {
18
+ if (!auth.user_id) {
19
+ response.redirect("/login");
20
+ return null;
21
+ }
22
+ const me = use(serverData.get<{ email?: string }>("User", auth.user_id));
23
+ const org = auth.tenant_id
24
+ ? use(serverData.get<{ name?: string }>("Org", auth.tenant_id))
25
+ : null;
26
+ const projects = use(serverData.list<Project>("Project"));
27
+ return (
28
+ <DashboardShell
29
+ active="projects"
30
+ title="Projects"
31
+ userEmail={me?.email ?? ""}
32
+ orgName={org?.name}
33
+ >
34
+ <Projects tenantId={auth.tenant_id} initial={projects} />
35
+ </DashboardShell>
36
+ );
37
+ }
@@ -0,0 +1,45 @@
1
+ import React, { use } from "react";
2
+ import { type Metadata, type PageProps } from "@pylonsync/react";
3
+ import { DashboardShell } from "@/components/dashboard-shell";
4
+ import {
5
+ Settings,
6
+ type OrgInfo,
7
+ type OrgMemberRow,
8
+ } from "../dashboard-client";
9
+
10
+ export const metadata: Metadata = {
11
+ title: "Settings — Acme",
12
+ robots: "noindex",
13
+ };
14
+
15
+ // `/dashboard/settings` — workspace settings: rename (owners/admins) and delete
16
+ // (owners). The active org + member count are resolved server-side so the page
17
+ // paints with real values on the first byte.
18
+ export default function SettingsPage({ auth, response, serverData }: PageProps) {
19
+ if (!auth.user_id) {
20
+ response.redirect("/login");
21
+ return null;
22
+ }
23
+ const me = use(serverData.get<{ email?: string }>("User", auth.user_id));
24
+ const org = auth.tenant_id
25
+ ? use(serverData.get<OrgInfo>("Org", auth.tenant_id))
26
+ : null;
27
+ const members = use(serverData.list<OrgMemberRow>("OrgMember"));
28
+ const memberCount = members.filter(
29
+ (m) => m.orgId === auth.tenant_id,
30
+ ).length;
31
+ return (
32
+ <DashboardShell
33
+ active="settings"
34
+ title="Settings"
35
+ userEmail={me?.email ?? ""}
36
+ orgName={org?.name}
37
+ >
38
+ <Settings
39
+ org={org}
40
+ role={auth.roles?.[0] ?? ""}
41
+ memberCount={memberCount}
42
+ />
43
+ </DashboardShell>
44
+ );
45
+ }
@@ -6,6 +6,10 @@
6
6
  markup elsewhere. */
7
7
  @source "../app/**/*.{tsx,ts,jsx,js}";
8
8
  @source "../components/**/*.{tsx,ts,jsx,js}";
9
+ @source "../lib/**/*.{tsx,ts,jsx,js}";
10
+ /* @pylonsync/client ships raw .tsx (OrganizationSwitcher, etc.) with Tailwind
11
+ classes — scan it too or its dropdowns/menus render unstyled. */
12
+ @source "../node_modules/@pylonsync/client/**/*.{tsx,ts,jsx,js}";
9
13
 
10
14
  @custom-variant dark (&:where(.dark, .dark *));
11
15
 
@@ -15,6 +19,11 @@
15
19
  on <html>. */
16
20
  :root {
17
21
  --radius: 0.625rem;
22
+ /* Brand accent (blue) + surfaces used by the marketing pages. Re-theme the
23
+ whole landing page by editing these three. */
24
+ --brand: #2563eb;
25
+ --brand-soft: #e8f0fe;
26
+ --paper: #fafafa;
18
27
  --background: oklch(1 0 0);
19
28
  --foreground: oklch(0.141 0.005 285.823);
20
29
  --card: oklch(1 0 0);
@@ -105,6 +114,9 @@
105
114
  --color-border: var(--border);
106
115
  --color-input: var(--input);
107
116
  --color-ring: var(--ring);
117
+ --color-brand: var(--brand);
118
+ --color-brand-soft: var(--brand-soft);
119
+ --color-paper: var(--paper);
108
120
  --color-chart-1: var(--chart-1);
109
121
  --color-chart-2: var(--chart-2);
110
122
  --color-chart-3: var(--chart-3);
@@ -132,6 +144,8 @@
132
144
  body {
133
145
  background-color: var(--color-background);
134
146
  color: var(--color-foreground);
147
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
148
+ -webkit-font-smoothing: antialiased;
135
149
  }
136
150
  button {
137
151
  cursor: pointer;