create-githat-app 1.0.14 → 1.0.16

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 (85) hide show
  1. package/dist/cli.js +72 -18
  2. package/package.json +1 -1
  3. package/templates/agent/app/(auth)/sign-in/page.tsx.hbs +9 -0
  4. package/templates/agent/app/(auth)/sign-up/page.tsx.hbs +9 -0
  5. package/templates/agent/app/admin/agent/page.tsx.hbs +127 -0
  6. package/templates/agent/app/globals.css.hbs +87 -0
  7. package/templates/agent/app/layout.tsx.hbs +41 -0
  8. package/templates/agent/app/page.tsx.hbs +100 -0
  9. package/templates/agent/next.config.ts.hbs +7 -0
  10. package/templates/agent/postcss.config.mjs.hbs +14 -0
  11. package/templates/agent/proxy.ts.hbs +10 -0
  12. package/templates/agent/tsconfig.json.hbs +21 -0
  13. package/templates/classroom/app/(auth)/sign-in/page.tsx.hbs +9 -0
  14. package/templates/classroom/app/(auth)/sign-up/page.tsx.hbs +9 -0
  15. package/templates/classroom/app/globals.css.hbs +87 -0
  16. package/templates/classroom/app/layout.tsx.hbs +41 -0
  17. package/templates/classroom/app/page.tsx.hbs +103 -0
  18. package/templates/classroom/app/projects/[id]/feedback/page.tsx.hbs +159 -0
  19. package/templates/classroom/app/projects/[id]/present/page.tsx.hbs +113 -0
  20. package/templates/classroom/next.config.ts.hbs +7 -0
  21. package/templates/classroom/postcss.config.mjs.hbs +14 -0
  22. package/templates/classroom/proxy.ts.hbs +10 -0
  23. package/templates/classroom/tsconfig.json.hbs +21 -0
  24. package/templates/content/app/(auth)/sign-in/page.tsx.hbs +9 -0
  25. package/templates/content/app/(auth)/sign-up/page.tsx.hbs +9 -0
  26. package/templates/content/app/globals.css.hbs +87 -0
  27. package/templates/content/app/layout.tsx.hbs +41 -0
  28. package/templates/content/app/newsletter/page.tsx.hbs +90 -0
  29. package/templates/content/app/page.tsx.hbs +105 -0
  30. package/templates/content/app/posts/[slug]/page.tsx.hbs +119 -0
  31. package/templates/content/next.config.ts.hbs +7 -0
  32. package/templates/content/postcss.config.mjs.hbs +14 -0
  33. package/templates/content/proxy.ts.hbs +10 -0
  34. package/templates/content/tsconfig.json.hbs +21 -0
  35. package/templates/dashboard/app/(auth)/sign-in/page.tsx.hbs +9 -0
  36. package/templates/dashboard/app/(auth)/sign-up/page.tsx.hbs +9 -0
  37. package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +68 -0
  38. package/templates/dashboard/app/admin/page.tsx.hbs +59 -0
  39. package/templates/dashboard/app/globals.css.hbs +87 -0
  40. package/templates/dashboard/app/layout.tsx.hbs +41 -0
  41. package/templates/dashboard/app/page.tsx.hbs +57 -0
  42. package/templates/dashboard/next.config.ts.hbs +7 -0
  43. package/templates/dashboard/postcss.config.mjs.hbs +14 -0
  44. package/templates/dashboard/proxy.ts.hbs +10 -0
  45. package/templates/dashboard/src/lib/db.ts.hbs +39 -0
  46. package/templates/dashboard/tsconfig.json.hbs +21 -0
  47. package/templates/marketplace/CULTURE.md +74 -0
  48. package/templates/marketplace/app/(auth)/sign-in/page.tsx.hbs +9 -0
  49. package/templates/marketplace/app/(auth)/sign-up/page.tsx.hbs +9 -0
  50. package/templates/marketplace/app/(shop)/[slug]/p/[productId]/page.tsx.hbs +99 -0
  51. package/templates/marketplace/app/(shop)/[slug]/page.tsx.hbs +90 -0
  52. package/templates/marketplace/app/admin/page.tsx.hbs +95 -0
  53. package/templates/marketplace/app/cart/page.tsx.hbs +157 -0
  54. package/templates/marketplace/app/globals.css.hbs +87 -0
  55. package/templates/marketplace/app/layout.tsx.hbs +77 -0
  56. package/templates/marketplace/app/page.tsx.hbs +178 -0
  57. package/templates/marketplace/app/sell/page.tsx.hbs +78 -0
  58. package/templates/marketplace/next.config.ts.hbs +7 -0
  59. package/templates/marketplace/postcss.config.mjs.hbs +14 -0
  60. package/templates/marketplace/proxy.ts.hbs +10 -0
  61. package/templates/marketplace/src/lib/anon-session.ts.hbs +117 -0
  62. package/templates/marketplace/src/lib/categories.ts.hbs +35 -0
  63. package/templates/marketplace/tsconfig.json.hbs +21 -0
  64. package/templates/portfolio/app/(auth)/sign-in/page.tsx.hbs +9 -0
  65. package/templates/portfolio/app/(auth)/sign-up/page.tsx.hbs +9 -0
  66. package/templates/portfolio/app/globals.css.hbs +87 -0
  67. package/templates/portfolio/app/layout.tsx.hbs +41 -0
  68. package/templates/portfolio/app/page.tsx.hbs +86 -0
  69. package/templates/portfolio/next.config.ts.hbs +7 -0
  70. package/templates/portfolio/postcss.config.mjs.hbs +14 -0
  71. package/templates/portfolio/proxy.ts.hbs +10 -0
  72. package/templates/portfolio/tsconfig.json.hbs +21 -0
  73. package/templates/saas/app/(auth)/sign-in/page.tsx.hbs +9 -0
  74. package/templates/saas/app/(auth)/sign-up/page.tsx.hbs +9 -0
  75. package/templates/saas/app/admin/billing/page.tsx.hbs +145 -0
  76. package/templates/saas/app/admin/page.tsx.hbs +106 -0
  77. package/templates/saas/app/admin/team/page.tsx.hbs +134 -0
  78. package/templates/saas/app/globals.css.hbs +87 -0
  79. package/templates/saas/app/layout.tsx.hbs +41 -0
  80. package/templates/saas/app/page.tsx.hbs +108 -0
  81. package/templates/saas/app/pricing/page.tsx.hbs +131 -0
  82. package/templates/saas/next.config.ts.hbs +7 -0
  83. package/templates/saas/postcss.config.mjs.hbs +14 -0
  84. package/templates/saas/proxy.ts.hbs +10 -0
  85. package/templates/saas/tsconfig.json.hbs +21 -0
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { useAuth } from '@githat/nextjs';
5
+
6
+ /**
7
+ * Admin dashboard — auth-gated.
8
+ *
9
+ * Three tiles a B2B operator looks at on Monday morning:
10
+ * 1. Active members on the org
11
+ * 2. Subscription status (trial / active / past-due / canceled)
12
+ * 3. Usage against plan limits (seats, storage, API calls)
13
+ *
14
+ * Tile data is hardcoded in the starter — wire to your data layer.
15
+ * Real org info comes from useGitHat().org once the user has joined
16
+ * or created an org.
17
+ */
18
+ export default function AdminPage() {
19
+ const { isSignedIn, user, isLoading } = useAuth();
20
+
21
+ if (isLoading) return <Loading />;
22
+ if (!isSignedIn) return <SignInPrompt />;
23
+
24
+ return (
25
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
26
+ <div style=\{{ maxWidth: '64rem', margin: '0 auto', padding: 'var(--space-8) var(--space-4)' }}>
27
+ <header style=\{{ marginBottom: 'var(--space-8)' }}>
28
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-2)' }}>
29
+ Welcome back{user?.name ? `, ${user.name}` : ''}
30
+ </h1>
31
+ <p style=\{{ color: 'var(--fg-muted)' }}>
32
+ Your org dashboard. Wire each tile to real data.
33
+ </p>
34
+ </header>
35
+
36
+ <div style=\{{
37
+ display: 'grid',
38
+ gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
39
+ gap: 'var(--space-4)',
40
+ marginBottom: 'var(--space-8)',
41
+ }}>
42
+ <Tile label="Members" value="—" hint="Active org members" />
43
+ <Tile label="Plan" value="Trial" hint="14 days remaining" />
44
+ <Tile label="Usage" value="—" hint="API calls this month" />
45
+ </div>
46
+
47
+ <nav style=\{{
48
+ display: 'grid',
49
+ gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
50
+ gap: 'var(--space-3)',
51
+ }}>
52
+ <AdminLink href="/admin/team" title="Team" hint="Invite members, set roles" />
53
+ <AdminLink href="/admin/billing" title="Billing" hint="Subscription, invoices, payment method" />
54
+ <AdminLink href="/admin/settings" title="Settings" hint="Org name, custom domain, branding" />
55
+ </nav>
56
+ </div>
57
+ </div>
58
+ );
59
+ }
60
+
61
+ function Tile({ label, value, hint }: { label: string; value: string; hint: string }) {
62
+ return (
63
+ <div style=\{{
64
+ padding: 'var(--space-5)',
65
+ borderRadius: 'var(--radius-md, 0.5rem)',
66
+ border: '1px solid var(--border)',
67
+ background: 'var(--surface)',
68
+ }}>
69
+ <div style=\{{ fontSize: '0.875rem', color: 'var(--fg-muted)', marginBottom: 'var(--space-1)' }}>{label}</div>
70
+ <div style=\{{ fontSize: '2rem', fontWeight: 600 }}>{value}</div>
71
+ <div style=\{{ fontSize: '0.75rem', color: 'var(--fg-subtle)', marginTop: 'var(--space-1)' }}>{hint}</div>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ function AdminLink({ href, title, hint }: { href: string; title: string; hint: string }) {
77
+ return (
78
+ <Link href={href} style=\{{
79
+ display: 'block',
80
+ padding: 'var(--space-5)',
81
+ borderRadius: 'var(--radius-md, 0.5rem)',
82
+ border: '1px solid var(--border)',
83
+ background: 'var(--surface)',
84
+ color: 'var(--fg)',
85
+ textDecoration: 'none',
86
+ }}>
87
+ <div style=\{{ fontWeight: 600, marginBottom: 'var(--space-1)' }}>{title} →</div>
88
+ <div style=\{{ fontSize: '0.75rem', color: 'var(--fg-muted)' }}>{hint}</div>
89
+ </Link>
90
+ );
91
+ }
92
+
93
+ function Loading() {
94
+ return <div style=\{{ display: 'flex', minHeight: 'calc(100vh - 64px)', alignItems: 'center', justifyContent: 'center', color: 'var(--fg-muted)' }}>Loading…</div>;
95
+ }
96
+
97
+ function SignInPrompt() {
98
+ return (
99
+ <div style=\{{ display: 'flex', minHeight: 'calc(100vh - 64px)', alignItems: 'center', justifyContent: 'center' }}>
100
+ <div style=\{{ textAlign: 'center' }}>
101
+ <h2 style=\{{ marginBottom: 'var(--space-3)' }}>Sign in to continue</h2>
102
+ <Link href="/sign-in" style=\{{ color: 'var(--primary)' }}>Sign in →</Link>
103
+ </div>
104
+ </div>
105
+ );
106
+ }
@@ -0,0 +1,134 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import Link from 'next/link';
5
+ import { useAuth } from '@githat/nextjs';
6
+
7
+ /**
8
+ * Team management — invite members, change roles.
9
+ *
10
+ * GitHat's API has the primitives (POST /org/{id}/invite, PATCH
11
+ * /org/{id}/members/{userId}/role). The starter renders the form
12
+ * shell; wire it to your /api/team route or directly to GitHat's
13
+ * REST endpoints depending on your architecture.
14
+ */
15
+ export default function TeamPage() {
16
+ const { isSignedIn, isLoading } = useAuth();
17
+ const [email, setEmail] = useState('');
18
+ const [role, setRole] = useState<'admin' | 'member'>('member');
19
+
20
+ if (isLoading) return <div style=\{{ padding: 'var(--space-8)', color: 'var(--fg-muted)' }}>Loading…</div>;
21
+ if (!isSignedIn) {
22
+ return (
23
+ <div style=\{{ padding: 'var(--space-8)', textAlign: 'center' }}>
24
+ <p>Sign in to manage your team.</p>
25
+ <Link href="/sign-in" style=\{{ color: 'var(--primary)' }}>Sign in →</Link>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ const sampleMembers = [
31
+ { id: '1', email: 'you@example.com', role: 'owner', name: 'You' },
32
+ { id: '2', email: 'jane@example.com', role: 'admin', name: 'Jane Cooper' },
33
+ { id: '3', email: 'kai@example.com', role: 'member', name: 'Kai Lee' },
34
+ ];
35
+
36
+ return (
37
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
38
+ <div style=\{{ maxWidth: '48rem', margin: '0 auto', padding: 'var(--space-8) var(--space-4)' }}>
39
+ <Link href="/admin" style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem', display: 'inline-block', marginBottom: 'var(--space-4)' }}>
40
+ ← Dashboard
41
+ </Link>
42
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-6)' }}>
43
+ Team
44
+ </h1>
45
+
46
+ <section style=\{{
47
+ padding: 'var(--space-5)',
48
+ borderRadius: 'var(--radius-md, 0.5rem)',
49
+ border: '1px solid var(--border)',
50
+ background: 'var(--surface)',
51
+ marginBottom: 'var(--space-6)',
52
+ }}>
53
+ <h2 style=\{{ fontSize: '1.125rem', marginBottom: 'var(--space-3)' }}>Invite member</h2>
54
+ <form
55
+ onSubmit={(e) => { e.preventDefault(); /* TODO: POST /api/team/invite { email, role } */ }}
56
+ style=\{{ display: 'flex', gap: 'var(--space-2)', flexWrap: 'wrap' }}
57
+ >
58
+ <input
59
+ type="email"
60
+ required
61
+ value={email}
62
+ onChange={(e) => setEmail(e.target.value)}
63
+ placeholder="teammate@example.com"
64
+ style=\{{
65
+ flex: '1 1 200px',
66
+ padding: 'var(--space-3)',
67
+ borderRadius: 'var(--radius-md, 0.5rem)',
68
+ border: '1px solid var(--border)',
69
+ background: 'var(--bg)',
70
+ color: 'var(--fg)',
71
+ }}
72
+ />
73
+ <select
74
+ value={role}
75
+ onChange={(e) => setRole(e.target.value as 'admin' | 'member')}
76
+ style=\{{
77
+ padding: 'var(--space-3)',
78
+ borderRadius: 'var(--radius-md, 0.5rem)',
79
+ border: '1px solid var(--border)',
80
+ background: 'var(--bg)',
81
+ color: 'var(--fg)',
82
+ }}
83
+ >
84
+ <option value="member">Member</option>
85
+ <option value="admin">Admin</option>
86
+ </select>
87
+ <button type="submit" style=\{{
88
+ padding: 'var(--space-3) var(--space-5)',
89
+ borderRadius: 'var(--radius-md, 0.5rem)',
90
+ border: 'none',
91
+ background: 'var(--primary)',
92
+ color: 'var(--bg)',
93
+ fontWeight: 600,
94
+ cursor: 'pointer',
95
+ }}>
96
+ Send invite
97
+ </button>
98
+ </form>
99
+ </section>
100
+
101
+ <h2 style=\{{ fontSize: '1.125rem', marginBottom: 'var(--space-3)' }}>Members</h2>
102
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0 }}>
103
+ {sampleMembers.map((m) => (
104
+ <li key={m.id} style=\{{
105
+ display: 'flex',
106
+ alignItems: 'center',
107
+ justifyContent: 'space-between',
108
+ padding: 'var(--space-3) var(--space-4)',
109
+ borderRadius: 'var(--radius-md, 0.5rem)',
110
+ border: '1px solid var(--border)',
111
+ background: 'var(--surface)',
112
+ marginBottom: 'var(--space-2)',
113
+ }}>
114
+ <div>
115
+ <div style=\{{ fontWeight: 600 }}>{m.name}</div>
116
+ <div style=\{{ fontSize: '0.75rem', color: 'var(--fg-muted)' }}>{m.email}</div>
117
+ </div>
118
+ <span style=\{{
119
+ padding: 'var(--space-1) var(--space-3)',
120
+ borderRadius: 'var(--radius-full, 9999px)',
121
+ background: m.role === 'owner' ? 'var(--primary)' : 'var(--surface-sub)',
122
+ color: m.role === 'owner' ? 'var(--bg)' : 'var(--fg)',
123
+ fontSize: '0.75rem',
124
+ fontWeight: 600,
125
+ }}>
126
+ {m.role}
127
+ </span>
128
+ </li>
129
+ ))}
130
+ </ul>
131
+ </div>
132
+ </div>
133
+ );
134
+ }
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Tailwind v4 — required because @githat/nextjs/styles is processed
3
+ * through @tailwindcss/postcss. Plain doesn't ship utility classes,
4
+ * but the import is needed for the auth pages to render styled.
5
+ */
6
+ @import "tailwindcss";
7
+
8
+ /*
9
+ * Plain template — self-contained globals.
10
+ *
11
+ * Defines the minimum CSS variables a GitHat app uses for layout and
12
+ * the auth-page styling that ships with @githat/nextjs/styles.
13
+ * Override these in your own files when you want a real theme.
14
+ *
15
+ * Light theme by default; flip --bg/--fg for dark.
16
+ */
17
+
18
+ :root {
19
+ /* Surface */
20
+ --bg: #ffffff;
21
+ --surface: #fafafa;
22
+ --surface-sub: #f4f4f5;
23
+
24
+ /* Borders */
25
+ --border: #e5e7eb;
26
+
27
+ /* Foreground */
28
+ --fg: #0a0a0a;
29
+ --fg-muted: #525252;
30
+ --fg-subtle: #737373;
31
+
32
+ /* Brand — change these two to re-skin the whole auth flow */
33
+ --primary: #6366f1;
34
+ --accent: #f59e0b;
35
+
36
+ /* Semantic */
37
+ --success: #16a34a;
38
+ --warn: #d97706;
39
+ --danger: #dc2626;
40
+
41
+ /* Spacing — used by @githat/nextjs/styles */
42
+ --space-1: 0.25rem;
43
+ --space-2: 0.5rem;
44
+ --space-3: 0.75rem;
45
+ --space-4: 1rem;
46
+ --space-6: 1.5rem;
47
+ --space-8: 2rem;
48
+
49
+ /* Radius */
50
+ --radius: 0.5rem;
51
+ --radius-md: 0.5rem;
52
+ --radius-lg: 0.75rem;
53
+
54
+ /* Fonts */
55
+ --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
56
+ --font-wordmark: 'Instrument Serif', Georgia, serif;
57
+ }
58
+
59
+ @media (prefers-color-scheme: dark) {
60
+ :root {
61
+ --bg: #0a0a0a;
62
+ --surface: #18181b;
63
+ --surface-sub: #27272a;
64
+ --border: #3f3f46;
65
+ --fg: #fafafa;
66
+ --fg-muted: #a1a1aa;
67
+ --fg-subtle: #71717a;
68
+ }
69
+ }
70
+
71
+ * {
72
+ box-sizing: border-box;
73
+ margin: 0;
74
+ padding: 0;
75
+ }
76
+
77
+ body {
78
+ font-family: var(--font-sans);
79
+ background: var(--bg);
80
+ color: var(--fg);
81
+ line-height: 1.5;
82
+ }
83
+
84
+ a {
85
+ color: inherit;
86
+ text-decoration: none;
87
+ }
@@ -0,0 +1,41 @@
1
+ import { GitHatProvider } from '@githat/nextjs';
2
+ import '@githat/nextjs/styles';
3
+ import './globals.css';
4
+
5
+ export const metadata = {
6
+ title: '{{businessName}}',
7
+ description: '{{description}}',
8
+ };
9
+
10
+ export default function RootLayout({ children }{{#if typescript}}: { children: React.ReactNode }{{/if}}) {
11
+ return (
12
+ <html lang="en">
13
+ <body>
14
+ {/*
15
+ Plain template: no @githat/ui dep, no Wordmark. The
16
+ full-kit (`nextjs`) template uses @githat/ui for the
17
+ shared design system; the plain scaffold is the smallest
18
+ working app, so we avoid extra deps.
19
+ */}
20
+ <GitHatProvider config=\{{
21
+ publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
22
+ signInUrl: '/sign-in',
23
+ signUpUrl: '/sign-up',
24
+ afterSignInUrl: '/',
25
+ afterSignOutUrl: '/',
26
+ }}>
27
+ <header style=\{{
28
+ padding: '1rem 1.5rem',
29
+ borderBottom: '1px solid var(--border, #e5e7eb)',
30
+ fontFamily: 'system-ui, -apple-system, sans-serif',
31
+ }}>
32
+ <a href="/" style=\{{ textDecoration: 'none', color: 'inherit', fontWeight: 600 }}>
33
+ {{businessName}}
34
+ </a>
35
+ </header>
36
+ <main>{children}</main>
37
+ </GitHatProvider>
38
+ </body>
39
+ </html>
40
+ );
41
+ }
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { SignUpButton, useAuth } from '@githat/nextjs';
5
+
6
+ /**
7
+ * SaaS landing page.
8
+ *
9
+ * Replaces Clerk + Stripe + Vercel + a starter kit. Demonstrates:
10
+ * - GitHat orgs & RBAC out of the box (owner / admin / member)
11
+ * - Sebastn subscription billing wired to /admin/billing
12
+ * - One-stack deploy (this same project deploys to GitHat hosting)
13
+ *
14
+ * The homepage is the conversion surface; the signed-in product
15
+ * lives under /admin.
16
+ */
17
+ export default function Home() {
18
+ const { isSignedIn } = useAuth();
19
+
20
+ return (
21
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)' }}>
22
+ <section style=\{{
23
+ padding: 'var(--space-12) var(--space-4)',
24
+ textAlign: 'center',
25
+ maxWidth: '48rem',
26
+ margin: '0 auto',
27
+ }}>
28
+ <h1 style=\{{
29
+ fontFamily: 'var(--font-wordmark, Georgia, serif)',
30
+ fontSize: 'clamp(2rem, 5vw, 3rem)',
31
+ lineHeight: 1.1,
32
+ marginBottom: 'var(--space-3)',
33
+ }}>
34
+ {{businessName}}
35
+ </h1>
36
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '1.25rem', marginBottom: 'var(--space-6)' }}>
37
+ The fastest way to ship a B2B product. Auth, teams, RBAC, and
38
+ subscription billing — wired up before you write a line.
39
+ </p>
40
+ <div style=\{{ display: 'flex', gap: 'var(--space-3)', justifyContent: 'center', flexWrap: 'wrap' }}>
41
+ {!isSignedIn ? (
42
+ <>
43
+ <SignUpButton />
44
+ <Link href="/pricing" style=\{{ alignSelf: 'center', color: 'var(--primary)' }}>
45
+ See pricing →
46
+ </Link>
47
+ </>
48
+ ) : (
49
+ <Link href="/admin" style=\{{
50
+ padding: 'var(--space-3) var(--space-6)',
51
+ borderRadius: 'var(--radius-md, 0.5rem)',
52
+ background: 'var(--primary)',
53
+ color: 'var(--bg)',
54
+ fontWeight: 600,
55
+ textDecoration: 'none',
56
+ }}>
57
+ Open dashboard →
58
+ </Link>
59
+ )}
60
+ </div>
61
+ </section>
62
+
63
+ <section style=\{{ padding: 'var(--space-8) var(--space-4)', background: 'var(--surface-sub)' }}>
64
+ <div style=\{{
65
+ maxWidth: '64rem',
66
+ margin: '0 auto',
67
+ display: 'grid',
68
+ gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
69
+ gap: 'var(--space-6)',
70
+ }}>
71
+ <Feature emoji="🏢" title="Orgs & teams" body="Create organisations, invite teammates, assign roles. RBAC built in (owner, admin, member)." />
72
+ <Feature emoji="💳" title="Subscriptions" body="Sebastn-powered billing — Stripe Checkout under the hood. Trials, plan changes, dunning, invoices, all handled." />
73
+ <Feature emoji="🚀" title="Ship in a weekend" body="Auth + payments + hosting are this one stack. No Vercel + Clerk + Stripe + a billing service to glue together." />
74
+ </div>
75
+ </section>
76
+
77
+ <section style=\{{ padding: 'var(--space-12) var(--space-4)', textAlign: 'center', maxWidth: '48rem', margin: '0 auto' }}>
78
+ <h2 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '1.75rem', marginBottom: 'var(--space-3)' }}>
79
+ Simple pricing
80
+ </h2>
81
+ <p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-6)' }}>
82
+ Free tier for the team trying us out. Paid tiers for the team that's growing.
83
+ Three plans, no hidden fees.
84
+ </p>
85
+ <Link href="/pricing" style=\{{
86
+ display: 'inline-block',
87
+ padding: 'var(--space-3) var(--space-6)',
88
+ borderRadius: 'var(--radius-md, 0.5rem)',
89
+ border: '1px solid var(--border)',
90
+ color: 'var(--fg)',
91
+ textDecoration: 'none',
92
+ }}>
93
+ Compare plans →
94
+ </Link>
95
+ </section>
96
+ </div>
97
+ );
98
+ }
99
+
100
+ function Feature({ emoji, title, body }: { emoji: string; title: string; body: string }) {
101
+ return (
102
+ <div>
103
+ <div style=\{{ fontSize: '2rem', marginBottom: 'var(--space-2)' }} aria-hidden>{emoji}</div>
104
+ <h3 style=\{{ fontWeight: 600, marginBottom: 'var(--space-2)' }}>{title}</h3>
105
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem' }}>{body}</p>
106
+ </div>
107
+ );
108
+ }
@@ -0,0 +1,131 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { SignUpButton } from '@githat/nextjs';
5
+
6
+ /**
7
+ * Pricing page — three tiers, monthly + annual toggle.
8
+ *
9
+ * Real apps wire each "Choose plan" button to a Sebastn checkout
10
+ * URL (or call /api/billing/checkout against your backend, which
11
+ * in turn calls Sebastn). The starter ships hardcoded prices so
12
+ * the page renders before any billing is wired.
13
+ *
14
+ * Tier IDs (sebastn_price_id) belong in your env, not in source —
15
+ * see /admin/billing for how the runtime page reads them.
16
+ */
17
+
18
+ const TIERS = [
19
+ {
20
+ name: 'Starter',
21
+ price: 0,
22
+ blurb: 'For teams trying us out.',
23
+ features: ['1 org', '5 members', 'Email support', '1 GB storage'],
24
+ cta: 'Sign up free',
25
+ href: '/sign-up',
26
+ highlight: false,
27
+ },
28
+ {
29
+ name: 'Team',
30
+ price: 29,
31
+ blurb: 'For growing teams that need more.',
32
+ features: ['Unlimited orgs', '50 members', 'Priority support', '50 GB storage', 'API access'],
33
+ cta: 'Start 14-day trial',
34
+ href: '/sign-up?plan=team',
35
+ highlight: true,
36
+ },
37
+ {
38
+ name: 'Business',
39
+ price: 99,
40
+ blurb: 'For teams that need SSO and audit logs.',
41
+ features: ['Everything in Team', 'SSO (SAML)', 'Audit log', 'SLA support', 'Custom contract'],
42
+ cta: 'Contact sales',
43
+ href: '/contact',
44
+ highlight: false,
45
+ },
46
+ ];
47
+
48
+ export default function PricingPage() {
49
+ return (
50
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)', padding: 'var(--space-12) var(--space-4)' }}>
51
+ <div style=\{{ maxWidth: '64rem', margin: '0 auto' }}>
52
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2.5rem', textAlign: 'center', marginBottom: 'var(--space-3)' }}>
53
+ Pricing
54
+ </h1>
55
+ <p style=\{{ color: 'var(--fg-muted)', textAlign: 'center', marginBottom: 'var(--space-8)' }}>
56
+ Free tier is real free. Upgrade when your team outgrows it.
57
+ </p>
58
+
59
+ <div style=\{{
60
+ display: 'grid',
61
+ gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))',
62
+ gap: 'var(--space-4)',
63
+ }}>
64
+ {TIERS.map((tier) => (
65
+ <div key={tier.name} style=\{{
66
+ padding: 'var(--space-6)',
67
+ borderRadius: 'var(--radius-md, 0.5rem)',
68
+ border: tier.highlight ? '2px solid var(--primary)' : '1px solid var(--border)',
69
+ background: 'var(--surface)',
70
+ display: 'flex',
71
+ flexDirection: 'column',
72
+ }}>
73
+ {tier.highlight && (
74
+ <div style=\{{
75
+ alignSelf: 'flex-start',
76
+ padding: 'var(--space-1) var(--space-2)',
77
+ borderRadius: 'var(--radius-sm, 0.25rem)',
78
+ background: 'var(--primary)',
79
+ color: 'var(--bg)',
80
+ fontSize: '0.75rem',
81
+ fontWeight: 600,
82
+ marginBottom: 'var(--space-3)',
83
+ }}>
84
+ MOST POPULAR
85
+ </div>
86
+ )}
87
+ <h2 style=\{{ fontSize: '1.25rem', fontWeight: 600, marginBottom: 'var(--space-2)' }}>
88
+ {tier.name}
89
+ </h2>
90
+ <div style=\{{ marginBottom: 'var(--space-3)' }}>
91
+ <span style=\{{ fontSize: '2.5rem', fontWeight: 700 }}>
92
+ ${tier.price}
93
+ </span>
94
+ {tier.price > 0 && (
95
+ <span style=\{{ fontSize: '0.875rem', color: 'var(--fg-muted)' }}>/mo</span>
96
+ )}
97
+ </div>
98
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem', marginBottom: 'var(--space-4)' }}>
99
+ {tier.blurb}
100
+ </p>
101
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, marginBottom: 'var(--space-4)', flex: 1 }}>
102
+ {tier.features.map((f) => (
103
+ <li key={f} style=\{{ padding: 'var(--space-1) 0', fontSize: '0.875rem' }}>
104
+ ✓ {f}
105
+ </li>
106
+ ))}
107
+ </ul>
108
+ {tier.name === 'Starter' ? (
109
+ <SignUpButton />
110
+ ) : (
111
+ <Link href={tier.href} style=\{{
112
+ display: 'inline-block',
113
+ padding: 'var(--space-3) var(--space-4)',
114
+ borderRadius: 'var(--radius-md, 0.5rem)',
115
+ background: tier.highlight ? 'var(--primary)' : 'var(--surface-sub)',
116
+ color: tier.highlight ? 'var(--bg)' : 'var(--fg)',
117
+ textDecoration: 'none',
118
+ fontWeight: 600,
119
+ textAlign: 'center',
120
+ border: tier.highlight ? 'none' : '1px solid var(--border)',
121
+ }}>
122
+ {tier.cta}
123
+ </Link>
124
+ )}
125
+ </div>
126
+ ))}
127
+ </div>
128
+ </div>
129
+ </div>
130
+ );
131
+ }
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from 'next';
2
+
3
+ const nextConfig: NextConfig = {
4
+ output: 'standalone',
5
+ };
6
+
7
+ export default nextConfig;
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Plain template — Tailwind v4 PostCSS plugin is required even though
3
+ * the plain scaffold doesn't use Tailwind utility classes. The auth
4
+ * page CSS shipped by `@githat/nextjs/styles` is processed through
5
+ * @tailwindcss/postcss at build time. Drop this config and the
6
+ * auth pages render unstyled.
7
+ */
8
+ const config = {
9
+ plugins: {
10
+ '@tailwindcss/postcss': {},
11
+ },
12
+ };
13
+
14
+ export default config;
@@ -0,0 +1,10 @@
1
+ import { authProxy } from '@githat/nextjs/proxy';
2
+
3
+ export const proxy = authProxy({
4
+ publicRoutes: ['/', '/sign-in', '/sign-up'{{#if includeForgotPassword}}, '/forgot-password', '/reset-password'{{/if}}{{#if includeEmailVerification}}, '/verify-email'{{/if}}],
5
+ signInUrl: '/sign-in',
6
+ });
7
+
8
+ export const config = {
9
+ matcher: ['/((?!_next|api|.*\\..*).*)'],
10
+ };
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [{ "name": "next" }],
17
+ "paths": { "@/*": ["./*"] }
18
+ },
19
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
20
+ "exclude": ["node_modules"]
21
+ }