@pylonsync/create-pylon 0.3.269 → 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.
- package/bin/create-pylon.js +11 -9
- package/package.json +1 -1
- package/templates/b2b/app/layout.tsx +1 -1
- package/templates/b2b/app/page.tsx +2 -2
- package/templates/b2b/tsconfig.json +1 -1
- package/templates/barebones/app/page.tsx +1 -1
- package/templates/barebones/tsconfig.json +1 -1
- package/templates/chat/app/page.tsx +1 -1
- package/templates/chat/tsconfig.json +1 -1
- package/templates/consumer/app/page.tsx +1 -1
- package/templates/consumer/tsconfig.json +1 -1
- package/templates/default/.env.example +19 -0
- package/templates/{ssr → default}/README.md +20 -6
- package/templates/default/app/auth-form.tsx +218 -0
- package/templates/default/app/auth-shell.tsx +76 -0
- package/templates/default/app/company/[slug]/page.tsx +28 -0
- package/templates/default/app/compare/[slug]/page.tsx +27 -0
- package/templates/default/app/dashboard/billing/page.tsx +49 -0
- package/templates/default/app/dashboard/dashboard-client.tsx +832 -0
- package/templates/default/app/dashboard/members/page.tsx +37 -0
- package/templates/default/app/dashboard/page.tsx +64 -0
- package/templates/default/app/dashboard/projects/page.tsx +37 -0
- package/templates/default/app/dashboard/settings/page.tsx +45 -0
- package/templates/{ssr → default}/app/globals.css +14 -0
- package/templates/default/app/layout.tsx +466 -0
- package/templates/default/app/login/page.tsx +27 -0
- package/templates/default/app/onboarding/onboarding-client.tsx +261 -0
- package/templates/default/app/onboarding/page.tsx +29 -0
- package/templates/default/app/page.tsx +653 -0
- package/templates/default/app/products/[slug]/page.tsx +134 -0
- package/templates/default/app/resources/[slug]/page.tsx +28 -0
- package/templates/default/app/signup/page.tsx +24 -0
- package/templates/default/app/sitemap.ts +40 -0
- package/templates/default/app/solutions/[slug]/page.tsx +28 -0
- package/templates/{ssr → default}/app.ts +17 -2
- package/templates/default/components/dashboard-shell.tsx +150 -0
- package/templates/default/components/marketing.tsx +370 -0
- package/templates/default/functions/_pylonStripeFindActiveSubForReference.ts +3 -0
- package/templates/default/functions/_pylonStripeFindByCustomerId.ts +3 -0
- package/templates/default/functions/_pylonStripeGetCustomerHolder.ts +3 -0
- package/templates/default/functions/_pylonStripeListSubsForReference.ts +3 -0
- package/templates/default/functions/_pylonStripeOrgMembership.ts +3 -0
- package/templates/default/functions/_pylonStripeSetCustomerId.ts +3 -0
- package/templates/default/functions/_pylonStripeUpsertSubscription.ts +3 -0
- package/templates/default/functions/cancelSubscription.ts +3 -0
- package/templates/default/functions/createBillingPortalSession.ts +3 -0
- package/templates/default/functions/createCheckoutSession.ts +3 -0
- package/templates/default/functions/restoreSubscription.ts +3 -0
- package/templates/default/functions/stripeWebhook.ts +3 -0
- package/templates/default/lib/billing.ts +46 -0
- package/templates/default/lib/products.ts +122 -0
- package/templates/default/lib/site.ts +261 -0
- package/templates/{ssr → default}/package.json +2 -0
- package/templates/{ssr → default}/tsconfig.json +2 -2
- package/templates/todo/app/page.tsx +1 -1
- package/templates/todo/tsconfig.json +1 -1
- package/templates/ssr/app/auth-form.tsx +0 -142
- package/templates/ssr/app/dashboard/dashboard-client.tsx +0 -192
- package/templates/ssr/app/dashboard/page.tsx +0 -26
- package/templates/ssr/app/layout.tsx +0 -78
- package/templates/ssr/app/login/page.tsx +0 -47
- package/templates/ssr/app/page.tsx +0 -212
- package/templates/ssr/app/signup/page.tsx +0 -44
- package/templates/ssr/app/sitemap.ts +0 -27
- package/templates/ssr/functions/_keep.ts +0 -13
- /package/templates/{ssr → default}/AGENTS.md +0 -0
- /package/templates/{ssr → default}/app/error.tsx +0 -0
- /package/templates/{ssr → default}/app/not-found.tsx +0 -0
- /package/templates/{ssr → default}/app/robots.ts +0 -0
- /package/templates/{ssr → default}/components/ui/button.tsx +0 -0
- /package/templates/{ssr → default}/components/ui/card.tsx +0 -0
- /package/templates/{ssr → default}/components.json +0 -0
- /package/templates/{ssr → default}/gitignore +0 -0
- /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;
|