@pylonsync/create-pylon 0.3.279 → 0.3.281

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 (38) hide show
  1. package/bin/create-pylon.js +6 -13
  2. package/package.json +1 -1
  3. package/templates/backend/chat/apps/api/schema.ts +1 -1
  4. package/templates/default/app/auth-form.tsx +36 -13
  5. package/templates/default/app/dashboard/billing/page.tsx +6 -6
  6. package/templates/default/app/dashboard/dashboard-client.tsx +4 -4
  7. package/templates/default/app/dashboard/members/page.tsx +5 -3
  8. package/templates/default/app/dashboard/page.tsx +9 -6
  9. package/templates/default/app/dashboard/projects/page.tsx +5 -3
  10. package/templates/default/app/dashboard/provision-workspace.tsx +74 -0
  11. package/templates/default/app/dashboard/settings/page.tsx +5 -3
  12. package/templates/default/app/layout.tsx +1 -1
  13. package/templates/default/components/dashboard-shell.tsx +1 -0
  14. package/templates/b2b/AGENTS.md +0 -61
  15. package/templates/b2b/README.md +0 -62
  16. package/templates/b2b/app/auth-form.tsx +0 -142
  17. package/templates/b2b/app/dashboard/dashboard-client.tsx +0 -192
  18. package/templates/b2b/app/dashboard/page.tsx +0 -63
  19. package/templates/b2b/app/error.tsx +0 -43
  20. package/templates/b2b/app/globals.css +0 -139
  21. package/templates/b2b/app/layout.tsx +0 -71
  22. package/templates/b2b/app/login/page.tsx +0 -47
  23. package/templates/b2b/app/not-found.tsx +0 -29
  24. package/templates/b2b/app/page.tsx +0 -114
  25. package/templates/b2b/app/robots.ts +0 -12
  26. package/templates/b2b/app/signup/page.tsx +0 -44
  27. package/templates/b2b/app/sitemap.ts +0 -27
  28. package/templates/b2b/app.ts +0 -179
  29. package/templates/b2b/components/ui/button.tsx +0 -56
  30. package/templates/b2b/components/ui/card.tsx +0 -90
  31. package/templates/b2b/components.json +0 -20
  32. package/templates/b2b/functions/_keep.ts +0 -13
  33. package/templates/b2b/gitignore +0 -10
  34. package/templates/b2b/lib/utils.ts +0 -10
  35. package/templates/b2b/package.json +0 -33
  36. package/templates/b2b/tsconfig.json +0 -18
  37. package/templates/default/app/onboarding/onboarding-client.tsx +0 -261
  38. package/templates/default/app/onboarding/page.tsx +0 -29
@@ -1,261 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { db } from "@pylonsync/react";
5
- import {
6
- CreateOrganization,
7
- InviteMembers,
8
- useAuth,
9
- type OrgSummary,
10
- } from "@pylonsync/client";
11
- import { Button } from "@/components/ui/button";
12
-
13
- // First-run onboarding. Three steps, each built on a real @pylonsync/client
14
- // primitive so the flow does actual work end-to-end (no mock screens):
15
- // 1. Workspace → <CreateOrganization> (createOrg) + selectOrg to make it the
16
- // active tenant.
17
- // 2. Teammates → <InviteMembers> (POST /api/auth/orgs/:id/invites, gated to
18
- // admins by the framework — the creator is the admin).
19
- // 3. Project → db.insert("Project", { orgId }) — the same tenant-scoped,
20
- // optimistic write the dashboard uses.
21
- // The marketing nav/footer are suppressed for /onboarding in the root layout,
22
- // so this owns the whole screen. The client components theme off --pylon-* vars,
23
- // which we map to the template's zinc palette on the wrapper below so they blend.
24
- const STEPS = ["Workspace", "Invite", "Project"] as const;
25
-
26
- export function Onboarding({ email }: { email: string }) {
27
- const { selectOrg } = useAuth();
28
- const [step, setStep] = useState(0);
29
- const [org, setOrg] = useState<OrgSummary | null>(null);
30
-
31
- async function onOrgCreated(created: OrgSummary) {
32
- // CreateOrganization creates the org but doesn't switch into it — make it
33
- // the active tenant so the invite + project steps (and the dashboard after)
34
- // operate on it.
35
- await selectOrg(created.id);
36
- setOrg(created);
37
- setStep(1);
38
- }
39
-
40
- function finish() {
41
- // Full navigation so the SSR dashboard re-renders with the now-active tenant
42
- // resolved from the session cookie.
43
- window.location.assign("/dashboard");
44
- }
45
-
46
- return (
47
- <div
48
- className="flex min-h-screen flex-col bg-zinc-50"
49
- style={
50
- {
51
- "--pylon-ink": "#18181b",
52
- "--pylon-ink-2": "#52525b",
53
- "--pylon-ink-3": "#a1a1aa",
54
- "--pylon-paper": "#ffffff",
55
- "--pylon-paper-2": "#f4f4f5",
56
- "--pylon-rule": "#e4e4e7",
57
- } as React.CSSProperties
58
- }
59
- >
60
- <header className="flex h-14 items-center justify-between px-6">
61
- <a href="/" className="flex items-center gap-2">
62
- <span className="flex size-6 items-center justify-center rounded-[7px] bg-zinc-900 text-[13px] font-bold text-white">
63
- A
64
- </span>
65
- <span className="text-[15px] font-semibold tracking-tight text-zinc-900">
66
- Acme
67
- </span>
68
- </a>
69
- <span className="truncate text-[13px] text-zinc-500">{email}</span>
70
- </header>
71
-
72
- <main className="flex flex-1 items-center justify-center px-6 py-10">
73
- <div className="w-full max-w-md">
74
- <Stepper current={step} />
75
-
76
- <div className="mt-7 rounded-2xl border border-zinc-200 bg-white p-7 shadow-[0_24px_60px_-32px_rgba(0,0,0,0.25)]">
77
- {step === 0 && (
78
- <Step
79
- eyebrow="Step 1 of 3"
80
- title="Create your workspace"
81
- subtitle="A workspace is an isolated tenant — its projects and members are private to it. You can make more later."
82
- >
83
- <CreateOrganization title="" onCreated={onOrgCreated} />
84
- </Step>
85
- )}
86
-
87
- {step === 1 && org && (
88
- <Step
89
- eyebrow="Step 2 of 3"
90
- title="Invite your team"
91
- subtitle={`Add people to ${org.name}. They'll get an email invite — skip this if you're flying solo for now.`}
92
- >
93
- <InviteMembers orgId={org.id} hideMembers hidePending />
94
- <StepNav onBack={() => setStep(0)} onNext={() => setStep(2)} />
95
- </Step>
96
- )}
97
-
98
- {step === 2 && org && (
99
- <Step
100
- eyebrow="Step 3 of 3"
101
- title="Create your first project"
102
- subtitle="Projects hold your work. Add one to land in the dashboard with something already there."
103
- >
104
- <FirstProject orgId={org.id} onDone={finish} />
105
- </Step>
106
- )}
107
- </div>
108
-
109
- {step === 0 && (
110
- <p className="mt-5 text-center text-[13px] text-zinc-400">
111
- Wrong account?{" "}
112
- <a href="/login" className="text-zinc-600 underline">
113
- Switch
114
- </a>
115
- </p>
116
- )}
117
- </div>
118
- </main>
119
- </div>
120
- );
121
- }
122
-
123
- function Stepper({ current }: { current: number }) {
124
- return (
125
- <ol className="flex items-center gap-2">
126
- {STEPS.map((label, i) => {
127
- const state =
128
- i < current ? "done" : i === current ? "active" : "todo";
129
- return (
130
- <li key={label} className="flex flex-1 items-center gap-2">
131
- <span
132
- className={
133
- "flex size-6 shrink-0 items-center justify-center rounded-full text-[11px] font-semibold transition-colors " +
134
- (state === "active"
135
- ? "bg-zinc-900 text-white"
136
- : state === "done"
137
- ? "bg-brand text-white"
138
- : "bg-zinc-200 text-zinc-500")
139
- }
140
- >
141
- {state === "done" ? "✓" : i + 1}
142
- </span>
143
- <span
144
- className={
145
- "text-[12px] font-medium " +
146
- (state === "todo" ? "text-zinc-400" : "text-zinc-700")
147
- }
148
- >
149
- {label}
150
- </span>
151
- {i < STEPS.length - 1 && (
152
- <span className="ml-1 h-px flex-1 bg-zinc-200" />
153
- )}
154
- </li>
155
- );
156
- })}
157
- </ol>
158
- );
159
- }
160
-
161
- function Step({
162
- eyebrow,
163
- title,
164
- subtitle,
165
- children,
166
- }: {
167
- eyebrow: string;
168
- title: string;
169
- subtitle: string;
170
- children: React.ReactNode;
171
- }) {
172
- return (
173
- <div>
174
- <p className="text-[11px] font-semibold uppercase tracking-wide text-brand">
175
- {eyebrow}
176
- </p>
177
- <h1 className="mt-1.5 text-xl font-semibold tracking-tight text-zinc-900">
178
- {title}
179
- </h1>
180
- <p className="mt-1.5 text-[13.5px] leading-relaxed text-zinc-500">
181
- {subtitle}
182
- </p>
183
- <div className="mt-5">{children}</div>
184
- </div>
185
- );
186
- }
187
-
188
- function StepNav({
189
- onBack,
190
- onNext,
191
- }: {
192
- onBack: () => void;
193
- onNext: () => void;
194
- }) {
195
- return (
196
- <div className="mt-5 flex items-center justify-between">
197
- <button
198
- type="button"
199
- onClick={onBack}
200
- className="text-[13px] font-medium text-zinc-500 transition-colors hover:text-zinc-900"
201
- >
202
- ← Back
203
- </button>
204
- <Button type="button" size="sm" onClick={onNext}>
205
- Continue
206
- </Button>
207
- </div>
208
- );
209
- }
210
-
211
- function FirstProject({
212
- orgId,
213
- onDone,
214
- }: {
215
- orgId: string;
216
- onDone: () => void;
217
- }) {
218
- const [name, setName] = useState("");
219
- const [saving, setSaving] = useState(false);
220
-
221
- async function create() {
222
- const value = name.trim();
223
- if (!value) return;
224
- setSaving(true);
225
- await db.insert("Project", { orgId, name: value });
226
- onDone();
227
- }
228
-
229
- return (
230
- <div>
231
- <input
232
- value={name}
233
- onChange={(e) => setName(e.target.value)}
234
- onKeyDown={(e) => {
235
- if (e.key === "Enter") void create();
236
- }}
237
- placeholder="e.g. Website redesign"
238
- aria-label="Project name"
239
- autoFocus
240
- className="h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none transition placeholder:text-zinc-400 focus:border-zinc-900 focus:ring-2 focus:ring-zinc-900/10"
241
- />
242
- <div className="mt-5 flex items-center justify-between">
243
- <button
244
- type="button"
245
- onClick={onDone}
246
- className="text-[13px] font-medium text-zinc-500 transition-colors hover:text-zinc-900"
247
- >
248
- Skip for now
249
- </button>
250
- <Button
251
- type="button"
252
- size="sm"
253
- onClick={create}
254
- disabled={saving || !name.trim()}
255
- >
256
- {saving ? "…" : "Finish →"}
257
- </Button>
258
- </div>
259
- </div>
260
- );
261
- }
@@ -1,29 +0,0 @@
1
- import React, { use } from "react";
2
- import { type Metadata, type PageProps } from "@pylonsync/react";
3
- import { Onboarding } from "./onboarding-client";
4
-
5
- export const metadata: Metadata = {
6
- title: "Get started — Acme",
7
- robots: "noindex",
8
- };
9
-
10
- // `/onboarding` — first-run flow. Server-side gate: signed-out users go to
11
- // /login; users who already have an active workspace (auth.tenant_id) skip
12
- // straight to the dashboard, so this is only ever the brand-new-account screen.
13
- // The signed-in email is resolved server-side (serverData.get) and passed in.
14
- export default function OnboardingPage({
15
- auth,
16
- response,
17
- serverData,
18
- }: PageProps) {
19
- if (!auth.user_id) {
20
- response.redirect("/login");
21
- return null;
22
- }
23
- if (auth.tenant_id) {
24
- response.redirect("/dashboard");
25
- return null;
26
- }
27
- const me = use(serverData.get<{ email?: string }>("User", auth.user_id));
28
- return <Onboarding email={me?.email ?? ""} />;
29
- }