create-githat-app 1.2.3 → 1.4.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.
Files changed (157) hide show
  1. package/README.md +48 -18
  2. package/dist/cli.js +1161 -114
  3. package/package.json +34 -9
  4. package/templates/agent/app/(auth)/sign-in/page.tsx.hbs +9 -0
  5. package/templates/agent/app/(auth)/sign-up/page.tsx.hbs +9 -0
  6. package/templates/agent/app/admin/agent/page.tsx.hbs +127 -0
  7. package/templates/agent/app/globals.css.hbs +87 -0
  8. package/templates/agent/app/layout.tsx.hbs +41 -0
  9. package/templates/agent/app/page.tsx.hbs +100 -0
  10. package/templates/agent/next.config.ts.hbs +8 -0
  11. package/templates/agent/postcss.config.mjs.hbs +14 -0
  12. package/templates/agent/proxy.ts.hbs +10 -0
  13. package/templates/agent/tsconfig.json.hbs +21 -0
  14. package/templates/base/.env.example.hbs +2 -2
  15. package/templates/base/.env.local.example.hbs +20 -0
  16. package/templates/base/.env.local.hbs +13 -2
  17. package/templates/base/.github/CODEOWNERS.hbs +1 -0
  18. package/templates/base/.github/SECURITY.md +10 -0
  19. package/templates/base/.github/dependabot.yml +19 -0
  20. package/templates/base/.github/workflows/ci.yml.hbs +77 -0
  21. package/templates/base/.github/workflows/githat-policy.yml +51 -0
  22. package/templates/base/.gitignore.hbs +17 -2
  23. package/templates/base/README.md.hbs +31 -52
  24. package/templates/classroom/app/(auth)/sign-in/page.tsx.hbs +9 -0
  25. package/templates/classroom/app/(auth)/sign-up/page.tsx.hbs +9 -0
  26. package/templates/classroom/app/globals.css.hbs +87 -0
  27. package/templates/classroom/app/layout.tsx.hbs +41 -0
  28. package/templates/classroom/app/page.tsx.hbs +103 -0
  29. package/templates/classroom/app/projects/[id]/feedback/page.tsx.hbs +159 -0
  30. package/templates/classroom/app/projects/[id]/present/page.tsx.hbs +113 -0
  31. package/templates/classroom/next.config.ts.hbs +8 -0
  32. package/templates/classroom/postcss.config.mjs.hbs +14 -0
  33. package/templates/classroom/proxy.ts.hbs +10 -0
  34. package/templates/classroom/tsconfig.json.hbs +21 -0
  35. package/templates/content/app/(auth)/sign-in/page.tsx.hbs +9 -0
  36. package/templates/content/app/(auth)/sign-up/page.tsx.hbs +9 -0
  37. package/templates/content/app/globals.css.hbs +87 -0
  38. package/templates/content/app/layout.tsx.hbs +41 -0
  39. package/templates/content/app/newsletter/page.tsx.hbs +90 -0
  40. package/templates/content/app/page.tsx.hbs +105 -0
  41. package/templates/content/app/posts/[slug]/page.tsx.hbs +119 -0
  42. package/templates/content/next.config.ts.hbs +8 -0
  43. package/templates/content/postcss.config.mjs.hbs +14 -0
  44. package/templates/content/proxy.ts.hbs +10 -0
  45. package/templates/content/tsconfig.json.hbs +21 -0
  46. package/templates/dashboard/app/(auth)/sign-in/page.tsx.hbs +9 -0
  47. package/templates/dashboard/app/(auth)/sign-up/page.tsx.hbs +9 -0
  48. package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +68 -0
  49. package/templates/dashboard/app/admin/page.tsx.hbs +59 -0
  50. package/templates/dashboard/app/globals.css.hbs +87 -0
  51. package/templates/dashboard/app/layout.tsx.hbs +41 -0
  52. package/templates/dashboard/app/page.tsx.hbs +57 -0
  53. package/templates/dashboard/next.config.ts.hbs +8 -0
  54. package/templates/dashboard/postcss.config.mjs.hbs +14 -0
  55. package/templates/dashboard/proxy.ts.hbs +10 -0
  56. package/templates/dashboard/src/lib/db.ts.hbs +39 -0
  57. package/templates/dashboard/tsconfig.json.hbs +21 -0
  58. package/templates/fullstack/apps-api-express/.env.example.hbs +6 -0
  59. package/templates/fullstack/apps-api-express/.env.local.hbs +6 -0
  60. package/templates/fullstack/apps-api-express/package.json.hbs +24 -0
  61. package/templates/fullstack/apps-api-express/src/index.ts.hbs +41 -0
  62. package/templates/fullstack/apps-api-express/src/routes/health.ts.hbs +11 -0
  63. package/templates/fullstack/apps-api-express/src/routes/users.ts.hbs +43 -0
  64. package/templates/fullstack/apps-api-express/tsconfig.json.hbs +16 -0
  65. package/templates/fullstack/apps-api-fastify/.env.example.hbs +6 -0
  66. package/templates/fullstack/apps-api-fastify/.env.local.hbs +6 -0
  67. package/templates/fullstack/apps-api-fastify/package.json.hbs +22 -0
  68. package/templates/fullstack/apps-api-fastify/src/index.ts.hbs +28 -0
  69. package/templates/fullstack/apps-api-fastify/src/routes/health.ts.hbs +11 -0
  70. package/templates/fullstack/apps-api-fastify/src/routes/users.ts.hbs +43 -0
  71. package/templates/fullstack/apps-api-fastify/tsconfig.json.hbs +16 -0
  72. package/templates/fullstack/apps-api-hono/.env.example.hbs +6 -0
  73. package/templates/fullstack/apps-api-hono/.env.local.hbs +6 -0
  74. package/templates/fullstack/apps-api-hono/package.json.hbs +22 -0
  75. package/templates/fullstack/apps-api-hono/src/index.ts.hbs +35 -0
  76. package/templates/fullstack/apps-api-hono/src/routes/health.ts.hbs +11 -0
  77. package/templates/fullstack/apps-api-hono/src/routes/users.ts.hbs +43 -0
  78. package/templates/fullstack/apps-api-hono/tsconfig.json.hbs +16 -0
  79. package/templates/fullstack/apps-web-nextjs/.env.example.hbs +5 -0
  80. package/templates/fullstack/apps-web-nextjs/.env.local.hbs +5 -0
  81. package/templates/fullstack/apps-web-nextjs/app/(auth)/forgot-password/page.tsx.hbs +11 -0
  82. package/templates/fullstack/apps-web-nextjs/app/(auth)/reset-password/page.tsx.hbs +39 -0
  83. package/templates/fullstack/apps-web-nextjs/app/(auth)/sign-in/page.tsx.hbs +9 -0
  84. package/templates/fullstack/apps-web-nextjs/app/(auth)/sign-up/page.tsx.hbs +9 -0
  85. package/templates/fullstack/apps-web-nextjs/app/(auth)/verify-email/page.tsx.hbs +11 -0
  86. package/templates/fullstack/apps-web-nextjs/app/dashboard/layout.tsx.hbs +15 -0
  87. package/templates/fullstack/apps-web-nextjs/app/dashboard/page.tsx.hbs +27 -0
  88. package/templates/fullstack/apps-web-nextjs/app/globals.css.hbs +21 -0
  89. package/templates/fullstack/apps-web-nextjs/app/layout.tsx.hbs +30 -0
  90. package/templates/fullstack/apps-web-nextjs/app/page.tsx.hbs +17 -0
  91. package/templates/fullstack/apps-web-nextjs/next.config.ts.hbs +16 -0
  92. package/templates/fullstack/apps-web-nextjs/package.json.hbs +34 -0
  93. package/templates/fullstack/apps-web-nextjs/postcss.config.mjs.hbs +9 -0
  94. package/templates/fullstack/apps-web-nextjs/tsconfig.json.hbs +21 -0
  95. package/templates/fullstack/root/.gitignore.hbs +42 -0
  96. package/templates/fullstack/root/githat.yaml.hbs +17 -0
  97. package/templates/fullstack/root/package.json.hbs +15 -0
  98. package/templates/fullstack/root/turbo.json.hbs +20 -0
  99. package/templates/marketplace/CULTURE.md +74 -0
  100. package/templates/marketplace/app/(auth)/sign-in/page.tsx.hbs +9 -0
  101. package/templates/marketplace/app/(auth)/sign-up/page.tsx.hbs +9 -0
  102. package/templates/marketplace/app/(shop)/[slug]/p/[productId]/page.tsx.hbs +99 -0
  103. package/templates/marketplace/app/(shop)/[slug]/page.tsx.hbs +90 -0
  104. package/templates/marketplace/app/admin/page.tsx.hbs +95 -0
  105. package/templates/marketplace/app/cart/page.tsx.hbs +157 -0
  106. package/templates/marketplace/app/globals.css.hbs +87 -0
  107. package/templates/marketplace/app/layout.tsx.hbs +77 -0
  108. package/templates/marketplace/app/page.tsx.hbs +178 -0
  109. package/templates/marketplace/app/sell/page.tsx.hbs +78 -0
  110. package/templates/marketplace/next.config.ts.hbs +8 -0
  111. package/templates/marketplace/postcss.config.mjs.hbs +14 -0
  112. package/templates/marketplace/proxy.ts.hbs +10 -0
  113. package/templates/marketplace/src/lib/anon-session.ts.hbs +117 -0
  114. package/templates/marketplace/src/lib/categories.ts.hbs +35 -0
  115. package/templates/marketplace/tsconfig.json.hbs +21 -0
  116. package/templates/nextjs/.github/workflows/deploy.yml.hbs +107 -0
  117. package/templates/nextjs/app/(auth)/reset-password/page.tsx.hbs +106 -0
  118. package/templates/nextjs/app/globals.css.hbs +4 -3
  119. package/templates/nextjs/app/layout.tsx.hbs +5 -1
  120. package/templates/nextjs/app/page.tsx.hbs +3 -6
  121. package/templates/nextjs/next.config.ts.hbs +5 -2
  122. package/templates/nextjs/proxy.ts.hbs +10 -0
  123. package/templates/plain/app/(auth)/sign-in/page.tsx.hbs +9 -0
  124. package/templates/plain/app/(auth)/sign-up/page.tsx.hbs +9 -0
  125. package/templates/plain/app/globals.css.hbs +87 -0
  126. package/templates/plain/app/layout.tsx.hbs +41 -0
  127. package/templates/plain/app/page.tsx.hbs +123 -0
  128. package/templates/plain/next.config.ts.hbs +8 -0
  129. package/templates/plain/postcss.config.mjs.hbs +14 -0
  130. package/templates/plain/proxy.ts.hbs +10 -0
  131. package/templates/plain/tsconfig.json.hbs +21 -0
  132. package/templates/portfolio/app/(auth)/sign-in/page.tsx.hbs +9 -0
  133. package/templates/portfolio/app/(auth)/sign-up/page.tsx.hbs +9 -0
  134. package/templates/portfolio/app/globals.css.hbs +87 -0
  135. package/templates/portfolio/app/layout.tsx.hbs +41 -0
  136. package/templates/portfolio/app/page.tsx.hbs +86 -0
  137. package/templates/portfolio/next.config.ts.hbs +8 -0
  138. package/templates/portfolio/postcss.config.mjs.hbs +14 -0
  139. package/templates/portfolio/proxy.ts.hbs +10 -0
  140. package/templates/portfolio/tsconfig.json.hbs +21 -0
  141. package/templates/react-vite/src/App.tsx.hbs +11 -9
  142. package/templates/react-vite/src/index.css.hbs +4 -3
  143. package/templates/react-vite/src/pages/Home.tsx.hbs +3 -6
  144. package/templates/saas/app/(auth)/sign-in/page.tsx.hbs +9 -0
  145. package/templates/saas/app/(auth)/sign-up/page.tsx.hbs +9 -0
  146. package/templates/saas/app/admin/billing/page.tsx.hbs +145 -0
  147. package/templates/saas/app/admin/page.tsx.hbs +106 -0
  148. package/templates/saas/app/admin/team/page.tsx.hbs +134 -0
  149. package/templates/saas/app/globals.css.hbs +87 -0
  150. package/templates/saas/app/layout.tsx.hbs +41 -0
  151. package/templates/saas/app/page.tsx.hbs +108 -0
  152. package/templates/saas/app/pricing/page.tsx.hbs +131 -0
  153. package/templates/saas/next.config.ts.hbs +8 -0
  154. package/templates/saas/postcss.config.mjs.hbs +14 -0
  155. package/templates/saas/proxy.ts.hbs +10 -0
  156. package/templates/saas/tsconfig.json.hbs +21 -0
  157. package/templates/nextjs/middleware.ts.hbs +0 -10
@@ -0,0 +1,145 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { useAuth } from '@githat/nextjs';
5
+
6
+ /**
7
+ * Billing — Sebastn-powered subscription management.
8
+ *
9
+ * The starter shows the data shape; a real app fetches the
10
+ * org's subscription state from your backend (which subscribed
11
+ * to Sebastn webhooks) and renders accordingly.
12
+ *
13
+ * Key actions:
14
+ * - Change plan → opens Sebastn checkout via /api/billing/checkout
15
+ * - Cancel → calls /api/billing/cancel which calls Sebastn
16
+ * - Update payment method → opens Sebastn customer portal
17
+ *
18
+ * All three are stubs — wire them when you connect Sebastn.
19
+ */
20
+ export default function BillingPage() {
21
+ const { isSignedIn, isLoading } = useAuth();
22
+
23
+ if (isLoading) return <div style=\{{ padding: 'var(--space-8)', color: 'var(--fg-muted)' }}>Loading…</div>;
24
+ if (!isSignedIn) {
25
+ return (
26
+ <div style=\{{ padding: 'var(--space-8)', textAlign: 'center' }}>
27
+ <p>Sign in to manage billing.</p>
28
+ <Link href="/sign-in" style=\{{ color: 'var(--primary)' }}>Sign in →</Link>
29
+ </div>
30
+ );
31
+ }
32
+
33
+ const subscription = {
34
+ status: 'trialing' as 'trialing' | 'active' | 'past_due' | 'canceled',
35
+ plan: 'Team',
36
+ price: 29,
37
+ renews: '2026-05-14',
38
+ seats: 5,
39
+ seatsUsed: 3,
40
+ };
41
+
42
+ return (
43
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
44
+ <div style=\{{ maxWidth: '48rem', margin: '0 auto', padding: 'var(--space-8) var(--space-4)' }}>
45
+ <Link href="/admin" style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem', display: 'inline-block', marginBottom: 'var(--space-4)' }}>
46
+ ← Dashboard
47
+ </Link>
48
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-6)' }}>
49
+ Billing
50
+ </h1>
51
+
52
+ <section style=\{{
53
+ padding: 'var(--space-6)',
54
+ borderRadius: 'var(--radius-md, 0.5rem)',
55
+ border: '1px solid var(--border)',
56
+ background: 'var(--surface)',
57
+ marginBottom: 'var(--space-6)',
58
+ }}>
59
+ <div style=\{{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 'var(--space-4)' }}>
60
+ <div>
61
+ <h2 style=\{{ fontSize: '1.5rem', marginBottom: 'var(--space-1)' }}>{subscription.plan}</h2>
62
+ <p style=\{{ fontSize: '0.875rem', color: 'var(--fg-muted)' }}>${subscription.price}/mo · renews {subscription.renews}</p>
63
+ </div>
64
+ <Badge status={subscription.status} />
65
+ </div>
66
+
67
+ <div style=\{{ display: 'flex', gap: 'var(--space-3)', flexWrap: 'wrap' }}>
68
+ <button onClick={() => { /* TODO: Sebastn checkout */ }} style=\{{
69
+ padding: 'var(--space-3) var(--space-4)',
70
+ borderRadius: 'var(--radius-md, 0.5rem)',
71
+ border: 'none',
72
+ background: 'var(--primary)',
73
+ color: 'var(--bg)',
74
+ fontWeight: 600,
75
+ cursor: 'pointer',
76
+ }}>
77
+ Change plan
78
+ </button>
79
+ <button onClick={() => { /* TODO: Sebastn customer portal */ }} style=\{{
80
+ padding: 'var(--space-3) var(--space-4)',
81
+ borderRadius: 'var(--radius-md, 0.5rem)',
82
+ border: '1px solid var(--border)',
83
+ background: 'transparent',
84
+ color: 'var(--fg)',
85
+ cursor: 'pointer',
86
+ }}>
87
+ Update payment method
88
+ </button>
89
+ <button onClick={() => { /* TODO: cancel via /api/billing/cancel */ }} style=\{{
90
+ padding: 'var(--space-3) var(--space-4)',
91
+ borderRadius: 'var(--radius-md, 0.5rem)',
92
+ border: '1px solid var(--border)',
93
+ background: 'transparent',
94
+ color: 'var(--danger)',
95
+ cursor: 'pointer',
96
+ }}>
97
+ Cancel
98
+ </button>
99
+ </div>
100
+ </section>
101
+
102
+ <section style=\{{
103
+ padding: 'var(--space-5)',
104
+ borderRadius: 'var(--radius-md, 0.5rem)',
105
+ border: '1px solid var(--border)',
106
+ background: 'var(--surface)',
107
+ }}>
108
+ <h3 style=\{{ fontSize: '1rem', marginBottom: 'var(--space-2)' }}>Seats used</h3>
109
+ <div style=\{{ fontSize: '1.5rem', fontWeight: 600, marginBottom: 'var(--space-3)' }}>
110
+ {subscription.seatsUsed} of {subscription.seats}
111
+ </div>
112
+ <div style=\{{ height: '8px', borderRadius: '4px', background: 'var(--surface-sub)', overflow: 'hidden' }}>
113
+ <div style=\{{
114
+ width: `${(subscription.seatsUsed / subscription.seats) * 100}%`,
115
+ height: '100%',
116
+ background: 'var(--primary)',
117
+ }} />
118
+ </div>
119
+ </section>
120
+ </div>
121
+ </div>
122
+ );
123
+ }
124
+
125
+ function Badge({ status }: { status: 'trialing' | 'active' | 'past_due' | 'canceled' }) {
126
+ const styles: Record<typeof status, { bg: string; fg: string; label: string }> = {
127
+ trialing: { bg: 'var(--info)', fg: 'var(--bg)', label: 'TRIAL' },
128
+ active: { bg: 'var(--success)', fg: 'var(--bg)', label: 'ACTIVE' },
129
+ past_due: { bg: 'var(--warn)', fg: 'var(--bg)', label: 'PAST DUE' },
130
+ canceled: { bg: 'var(--danger)', fg: 'var(--bg)', label: 'CANCELED' },
131
+ };
132
+ const s = styles[status];
133
+ return (
134
+ <span style=\{{
135
+ padding: 'var(--space-1) var(--space-3)',
136
+ borderRadius: 'var(--radius-full, 9999px)',
137
+ background: s.bg,
138
+ color: s.fg,
139
+ fontSize: '0.75rem',
140
+ fontWeight: 700,
141
+ }}>
142
+ {s.label}
143
+ </span>
144
+ );
145
+ }
@@ -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
+ }