create-githat-app 1.0.15 → 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 (40) hide show
  1. package/dist/cli.js +20 -10
  2. package/package.json +1 -1
  3. package/templates/agent/app/admin/agent/page.tsx.hbs +127 -0
  4. package/templates/agent/app/page.tsx.hbs +85 -108
  5. package/templates/classroom/app/(auth)/sign-in/page.tsx.hbs +9 -0
  6. package/templates/classroom/app/(auth)/sign-up/page.tsx.hbs +9 -0
  7. package/templates/classroom/app/globals.css.hbs +87 -0
  8. package/templates/classroom/app/layout.tsx.hbs +41 -0
  9. package/templates/classroom/app/page.tsx.hbs +103 -0
  10. package/templates/classroom/app/projects/[id]/feedback/page.tsx.hbs +159 -0
  11. package/templates/classroom/app/projects/[id]/present/page.tsx.hbs +113 -0
  12. package/templates/classroom/next.config.ts.hbs +7 -0
  13. package/templates/classroom/postcss.config.mjs.hbs +14 -0
  14. package/templates/classroom/proxy.ts.hbs +10 -0
  15. package/templates/classroom/tsconfig.json.hbs +21 -0
  16. package/templates/content/app/newsletter/page.tsx.hbs +90 -0
  17. package/templates/content/app/page.tsx.hbs +93 -111
  18. package/templates/content/app/posts/[slug]/page.tsx.hbs +119 -0
  19. package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +68 -0
  20. package/templates/dashboard/app/admin/page.tsx.hbs +59 -0
  21. package/templates/dashboard/app/page.tsx.hbs +42 -108
  22. package/templates/dashboard/src/lib/db.ts.hbs +39 -0
  23. package/templates/portfolio/app/(auth)/sign-in/page.tsx.hbs +9 -0
  24. package/templates/portfolio/app/(auth)/sign-up/page.tsx.hbs +9 -0
  25. package/templates/portfolio/app/globals.css.hbs +87 -0
  26. package/templates/portfolio/app/layout.tsx.hbs +41 -0
  27. package/templates/portfolio/app/page.tsx.hbs +86 -0
  28. package/templates/portfolio/next.config.ts.hbs +7 -0
  29. package/templates/portfolio/postcss.config.mjs.hbs +14 -0
  30. package/templates/portfolio/proxy.ts.hbs +10 -0
  31. package/templates/portfolio/tsconfig.json.hbs +21 -0
  32. package/templates/saas/app/admin/billing/page.tsx.hbs +145 -0
  33. package/templates/saas/app/admin/page.tsx.hbs +106 -0
  34. package/templates/saas/app/admin/team/page.tsx.hbs +134 -0
  35. package/templates/saas/app/page.tsx.hbs +95 -110
  36. package/templates/saas/app/pricing/page.tsx.hbs +131 -0
  37. package/templates/agent/TODO.md +0 -9
  38. package/templates/content/TODO.md +0 -9
  39. package/templates/dashboard/TODO.md +0 -9
  40. package/templates/saas/TODO.md +0 -9
@@ -1,123 +1,105 @@
1
1
  'use client';
2
2
 
3
- import { SignInButton, SignUpButton, UserButton, useAuth } from '@githat/nextjs';
3
+ import Link from 'next/link';
4
+ import { useAuth } from '@githat/nextjs';
4
5
 
5
6
  /**
6
- * Plain GitHat homepage.
7
+ * Content site homepage.
7
8
  *
8
- * No dashboard, no orgs, no agents just one page that flips between
9
- * "signed out" and "signed in." The smallest possible example of "I
10
- * have GitHat wired up." Replace this file with whatever your app
11
- * actually does auth keeps working.
9
+ * Replaces Substack + Stripe + a paywall plugin. Demonstrates:
10
+ * - Free posts, paid posts, subscriber-only posts
11
+ * - Sebastn for one-time tips and recurring subscriptions
12
+ * - GitHat email for the newsletter
13
+ *
14
+ * Sample posts are hardcoded — replace with a fetch from your CMS,
15
+ * Markdown files, or a Postgres `posts` table.
12
16
  */
13
- export default function Home() {
14
- const { isSignedIn, isLoading, user } = useAuth();
15
17
 
16
- if (isLoading) {
17
- return (
18
- <div
19
- style=\{{
20
- display: 'flex',
21
- minHeight: 'calc(100vh - 64px)',
22
- alignItems: 'center',
23
- justifyContent: 'center',
24
- color: 'var(--fg-muted)',
25
- }}
26
- >
27
- Loading…
28
- </div>
29
- );
30
- }
18
+ const SAMPLE_POSTS = [
19
+ { slug: 'welcome', title: 'Welcome to {{businessName}}', excerpt: 'What this newsletter is about.', gated: false },
20
+ { slug: 'first-deep-dive', title: 'Why I started this', excerpt: 'The one thing nobody told me.', gated: false },
21
+ { slug: 'paid-essay-1', title: 'Paid essay: the long version', excerpt: 'For paid subscribers — the unfiltered take.', gated: true },
22
+ ];
23
+
24
+ export default function Home() {
25
+ const { isSignedIn } = useAuth();
31
26
 
32
27
  return (
33
- <div
34
- style=\{{
35
- display: 'flex',
36
- minHeight: 'calc(100vh - 64px)',
37
- alignItems: 'center',
38
- justifyContent: 'center',
39
- padding: 'var(--space-8) var(--space-4)',
40
- background: 'var(--bg)',
41
- }}
42
- >
43
- <main
44
- style=\{{
45
- width: '100%',
46
- maxWidth: '32rem',
47
- textAlign: 'center',
48
- color: 'var(--fg)',
49
- }}
50
- >
51
- {!isSignedIn ? (
52
- <>
53
- <h1
54
- style=\{{
55
- fontFamily: 'var(--font-wordmark)',
56
- fontSize: '2.5rem',
57
- lineHeight: 1.1,
58
- marginBottom: 'var(--space-3)',
59
- }}
60
- >
61
- Welcome to {{businessName}}
62
- </h1>
63
- <p
64
- style=\{{
65
- color: 'var(--fg-muted)',
66
- marginBottom: 'var(--space-6)',
67
- lineHeight: 1.6,
68
- }}
69
- >
70
- Sign in to continue, or create an account in 30 seconds.
71
- We use GitHat for identity — your password never touches
72
- this app.
73
- </p>
74
- <div
75
- style=\{{
76
- display: 'flex',
77
- gap: 'var(--space-3)',
78
- justifyContent: 'center',
79
- flexWrap: 'wrap',
80
- }}
81
- >
82
- <SignInButton />
83
- <SignUpButton />
84
- </div>
85
- </>
86
- ) : (
87
- <>
88
- <div
89
- style=\{{
90
- display: 'flex',
91
- alignItems: 'center',
92
- justifyContent: 'center',
93
- gap: 'var(--space-4)',
94
- marginBottom: 'var(--space-6)',
95
- }}
96
- >
97
- <UserButton />
98
- <h1
99
- style=\{{
100
- fontFamily: 'var(--font-wordmark)',
101
- fontSize: '2rem',
102
- margin: 0,
103
- }}
104
- >
105
- Hello{user?.name ? `, ${user.name}` : ''}.
106
- </h1>
107
- </div>
108
- <p
109
- style=\{{
110
- color: 'var(--fg-muted)',
111
- lineHeight: 1.6,
112
- }}
113
- >
114
- That's it. The plain template doesn't ship a dashboard or
115
- any other route — replace this page with whatever your
116
- app actually does. Authentication will keep working.
117
- </p>
118
- </>
119
- )}
120
- </main>
28
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)' }}>
29
+ <section style=\{{
30
+ padding: 'var(--space-12) var(--space-4)',
31
+ textAlign: 'center',
32
+ maxWidth: '40rem',
33
+ margin: '0 auto',
34
+ }}>
35
+ <h1 style=\{{
36
+ fontFamily: 'var(--font-wordmark, Georgia, serif)',
37
+ fontSize: 'clamp(2rem, 5vw, 3rem)',
38
+ lineHeight: 1.1,
39
+ marginBottom: 'var(--space-3)',
40
+ }}>
41
+ {{businessName}}
42
+ </h1>
43
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '1.125rem', marginBottom: 'var(--space-6)' }}>
44
+ {{description}}
45
+ </p>
46
+ <Link href="/newsletter" style=\{{
47
+ display: 'inline-block',
48
+ padding: 'var(--space-3) var(--space-6)',
49
+ borderRadius: 'var(--radius-md, 0.5rem)',
50
+ background: 'var(--primary)',
51
+ color: 'var(--bg)',
52
+ fontWeight: 600,
53
+ textDecoration: 'none',
54
+ }}>
55
+ Subscribe to the newsletter →
56
+ </Link>
57
+ </section>
58
+
59
+ <section style=\{{ padding: 'var(--space-8) var(--space-4)', maxWidth: '48rem', margin: '0 auto' }}>
60
+ <h2 style=\{{ fontSize: '1.5rem', marginBottom: 'var(--space-4)' }}>Recent posts</h2>
61
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
62
+ {SAMPLE_POSTS.map((post) => (
63
+ <li key={post.slug}>
64
+ <Link href={`/posts/${post.slug}`} style=\{{
65
+ display: 'block',
66
+ padding: 'var(--space-5)',
67
+ borderRadius: 'var(--radius-md, 0.5rem)',
68
+ border: '1px solid var(--border)',
69
+ background: 'var(--surface)',
70
+ color: 'var(--fg)',
71
+ textDecoration: 'none',
72
+ }}>
73
+ <div style=\{{ display: 'flex', alignItems: 'center', gap: 'var(--space-2)', marginBottom: 'var(--space-2)' }}>
74
+ <h3 style=\{{ fontSize: '1.25rem', fontWeight: 600, margin: 0 }}>{post.title}</h3>
75
+ {post.gated && (
76
+ <span style=\{{
77
+ padding: '2px var(--space-2)',
78
+ borderRadius: 'var(--radius-full, 9999px)',
79
+ background: 'var(--accent)',
80
+ color: 'var(--bg)',
81
+ fontSize: '0.625rem',
82
+ fontWeight: 700,
83
+ }}>
84
+ PAID
85
+ </span>
86
+ )}
87
+ </div>
88
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem' }}>{post.excerpt}</p>
89
+ </Link>
90
+ </li>
91
+ ))}
92
+ </ul>
93
+ </section>
94
+
95
+ {!isSignedIn && (
96
+ <section style=\{{ padding: 'var(--space-8) var(--space-4)', background: 'var(--surface-sub)', textAlign: 'center' }}>
97
+ <p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-3)' }}>
98
+ Already a subscriber?
99
+ </p>
100
+ <Link href="/sign-in" style=\{{ color: 'var(--primary)' }}>Sign in →</Link>
101
+ </section>
102
+ )}
121
103
  </div>
122
104
  );
123
105
  }
@@ -0,0 +1,119 @@
1
+ 'use client';
2
+
3
+ import { use } from 'react';
4
+ import Link from 'next/link';
5
+ import { useAuth } from '@githat/nextjs';
6
+
7
+ /**
8
+ * Post viewer with paywall.
9
+ *
10
+ * Three states a reader can be in:
11
+ * 1. Post is free → show full content
12
+ * 2. Post is gated + reader is signed in + has active subscription → show full
13
+ * 3. Post is gated + reader is anonymous OR not subscribed → show preview + CTA
14
+ *
15
+ * Subscription check is stubbed (`hasActiveSubscription` always
16
+ * false). Wire to your backend that watches Sebastn webhooks.
17
+ */
18
+
19
+ const SAMPLE_POSTS: Record<string, { title: string; gated: boolean; body: string }> = {
20
+ welcome: {
21
+ title: 'Welcome to {{businessName}}',
22
+ gated: false,
23
+ body: 'This is the first post. Free to read. The rest of this template ' +
24
+ 'is a working content site — paywall, newsletter, author dashboard. ' +
25
+ 'Replace the sample posts with content from your CMS, Markdown files, ' +
26
+ 'or a Postgres table.',
27
+ },
28
+ 'first-deep-dive': {
29
+ title: 'Why I started this',
30
+ gated: false,
31
+ body: 'The one thing nobody told me when I started writing this newsletter: ' +
32
+ 'it takes the first ten posts before you find the voice. Free to read.',
33
+ },
34
+ 'paid-essay-1': {
35
+ title: 'Paid essay: the long version',
36
+ gated: true,
37
+ body: 'This is the unfiltered version of the paid essay. It runs about 3,000 ' +
38
+ 'words and gets into the parts I keep out of the free posts. Subscribe ' +
39
+ 'to read the rest. Sebastn handles the checkout — we never touch your card.',
40
+ },
41
+ };
42
+
43
+ export default function PostPage({ params }: { params: Promise<{ slug: string }> }) {
44
+ const { slug } = use(params);
45
+ const post = SAMPLE_POSTS[slug];
46
+ const { isSignedIn } = useAuth();
47
+
48
+ // Stub: real apps fetch the user's subscription state from your
49
+ // backend (which watches Sebastn webhooks for the source of truth).
50
+ const hasActiveSubscription = false;
51
+
52
+ if (!post) {
53
+ return (
54
+ <div style=\{{ padding: 'var(--space-12)', textAlign: 'center' }}>
55
+ <p>Post not found.</p>
56
+ <Link href="/" style=\{{ color: 'var(--primary)' }}>← Home</Link>
57
+ </div>
58
+ );
59
+ }
60
+
61
+ const showPaywall = post.gated && (!isSignedIn || !hasActiveSubscription);
62
+
63
+ return (
64
+ <article style=\{{ maxWidth: '40rem', margin: '0 auto', padding: 'var(--space-8) var(--space-4)' }}>
65
+ <Link href="/" style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem', display: 'inline-block', marginBottom: 'var(--space-4)' }}>
66
+ ← All posts
67
+ </Link>
68
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2.25rem', lineHeight: 1.2, marginBottom: 'var(--space-4)' }}>
69
+ {post.title}
70
+ </h1>
71
+
72
+ <div style=\{{ color: 'var(--fg)', lineHeight: 1.8, fontSize: '1.0625rem' }}>
73
+ {showPaywall ? (
74
+ <>
75
+ <p style=\{{ marginBottom: 'var(--space-4)' }}>{post.body.slice(0, 200)}…</p>
76
+ <Paywall />
77
+ </>
78
+ ) : (
79
+ <p>{post.body}</p>
80
+ )}
81
+ </div>
82
+ </article>
83
+ );
84
+ }
85
+
86
+ function Paywall() {
87
+ return (
88
+ <div style=\{{
89
+ marginTop: 'var(--space-8)',
90
+ padding: 'var(--space-6)',
91
+ borderRadius: 'var(--radius-md, 0.5rem)',
92
+ border: '2px solid var(--accent)',
93
+ background: 'var(--surface-sub)',
94
+ textAlign: 'center',
95
+ }}>
96
+ <h3 style=\{{ fontFamily: 'var(--font-wordmark)', marginBottom: 'var(--space-2)' }}>
97
+ This post is for subscribers
98
+ </h3>
99
+ <p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-4)' }}>
100
+ Get access to every paid essay, plus the newsletter.
101
+ </p>
102
+ <button
103
+ onClick={() => { /* TODO: open Sebastn checkout */ }}
104
+ style=\{{
105
+ padding: 'var(--space-3) var(--space-6)',
106
+ borderRadius: 'var(--radius-md, 0.5rem)',
107
+ border: 'none',
108
+ background: 'var(--primary)',
109
+ color: 'var(--bg)',
110
+ fontWeight: 600,
111
+ fontSize: '1rem',
112
+ cursor: 'pointer',
113
+ }}
114
+ >
115
+ Subscribe — $5/mo
116
+ </button>
117
+ </div>
118
+ );
119
+ }
@@ -0,0 +1,68 @@
1
+ import Link from 'next/link';
2
+ import { listEntities } from '../../../../src/lib/db';
3
+
4
+ /**
5
+ * Generic data-table view for any entity in your DB.
6
+ *
7
+ * Server component — runs `listEntities(entityName)` (which you
8
+ * implement in src/lib/db.ts) and renders rows as a basic table.
9
+ * Auth-gating is enforced by the middleware in proxy.ts; if you
10
+ * need entity-level RBAC, check `verifyToken` claims here.
11
+ */
12
+ export default async function EntityPage({
13
+ params,
14
+ }: {
15
+ params: Promise<{ entity: string }>;
16
+ }) {
17
+ const { entity } = await params;
18
+ const rows = await listEntities(entity);
19
+
20
+ const columns = rows.length > 0 ? Object.keys(rows[0]) : ['id'];
21
+
22
+ return (
23
+ <div style=\{{ padding: 'var(--space-8) var(--space-4)', maxWidth: '64rem', margin: '0 auto' }}>
24
+ <Link href="/admin" style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem', display: 'inline-block', marginBottom: 'var(--space-4)' }}>
25
+ ← Admin
26
+ </Link>
27
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-2)' }}>
28
+ {entity}
29
+ </h1>
30
+ <p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-6)' }}>
31
+ {rows.length} row{rows.length === 1 ? '' : 's'}
32
+ </p>
33
+
34
+ <div style=\{{ overflowX: 'auto', borderRadius: 'var(--radius-md, 0.5rem)', border: '1px solid var(--border)' }}>
35
+ <table style=\{{ width: '100%', borderCollapse: 'collapse', background: 'var(--surface)' }}>
36
+ <thead>
37
+ <tr style=\{{ borderBottom: '1px solid var(--border)', background: 'var(--surface-sub)' }}>
38
+ {columns.map((c) => (
39
+ <th key={c} style=\{{
40
+ padding: 'var(--space-3) var(--space-4)',
41
+ textAlign: 'left',
42
+ fontSize: '0.75rem',
43
+ fontWeight: 600,
44
+ textTransform: 'uppercase',
45
+ letterSpacing: '0.05em',
46
+ color: 'var(--fg-muted)',
47
+ }}>
48
+ {c}
49
+ </th>
50
+ ))}
51
+ </tr>
52
+ </thead>
53
+ <tbody>
54
+ {rows.map((row) => (
55
+ <tr key={String(row.id)} style=\{{ borderBottom: '1px solid var(--border)' }}>
56
+ {columns.map((c) => (
57
+ <td key={c} style=\{{ padding: 'var(--space-3) var(--space-4)', fontSize: '0.875rem' }}>
58
+ {String(row[c])}
59
+ </td>
60
+ ))}
61
+ </tr>
62
+ ))}
63
+ </tbody>
64
+ </table>
65
+ </div>
66
+ </div>
67
+ );
68
+ }
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { useAuth } from '@githat/nextjs';
5
+
6
+ /**
7
+ * Auth-gated admin home — overview of every entity in your DB.
8
+ *
9
+ * The list is hardcoded for the starter. Real apps generate it
10
+ * from your schema (drizzle, prisma) or list it server-side via
11
+ * `listEntities()` from src/lib/db.ts.
12
+ */
13
+ const ENTITIES = ['users', 'orders', 'invoices', 'products'];
14
+
15
+ export default function AdminPage() {
16
+ const { isSignedIn, isLoading } = useAuth();
17
+
18
+ if (isLoading) return <div style=\{{ padding: 'var(--space-8)', color: 'var(--fg-muted)' }}>Loading…</div>;
19
+ if (!isSignedIn) {
20
+ return (
21
+ <div style=\{{ padding: 'var(--space-8)', textAlign: 'center' }}>
22
+ <p>Sign in to access admin.</p>
23
+ <Link href="/sign-in" style=\{{ color: 'var(--primary)' }}>Sign in →</Link>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ return (
29
+ <div style=\{{ padding: 'var(--space-8) var(--space-4)', maxWidth: '48rem', margin: '0 auto' }}>
30
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-6)' }}>
31
+ Admin
32
+ </h1>
33
+ <p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-6)' }}>
34
+ Pick an entity to browse. Edit src/lib/db.ts to point at your
35
+ real database.
36
+ </p>
37
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px, 1fr))', gap: 'var(--space-3)' }}>
38
+ {ENTITIES.map((e) => (
39
+ <li key={e}>
40
+ <Link href={`/admin/data/${e}`} style=\{{
41
+ display: 'block',
42
+ padding: 'var(--space-4)',
43
+ borderRadius: 'var(--radius-md, 0.5rem)',
44
+ border: '1px solid var(--border)',
45
+ background: 'var(--surface)',
46
+ color: 'var(--fg)',
47
+ textDecoration: 'none',
48
+ }}>
49
+ <div style=\{{ fontWeight: 600 }}>{e}</div>
50
+ <div style=\{{ fontSize: '0.75rem', color: 'var(--fg-muted)', marginTop: 'var(--space-1)' }}>
51
+ Browse {e} →
52
+ </div>
53
+ </Link>
54
+ </li>
55
+ ))}
56
+ </ul>
57
+ </div>
58
+ );
59
+ }
@@ -1,123 +1,57 @@
1
1
  'use client';
2
2
 
3
- import { SignInButton, SignUpButton, UserButton, useAuth } from '@githat/nextjs';
3
+ import Link from 'next/link';
4
+ import { SignInButton, useAuth } from '@githat/nextjs';
4
5
 
5
6
  /**
6
- * Plain GitHat homepage.
7
+ * Dashboard template homepage.
7
8
  *
8
- * No dashboard, no orgs, no agents just one page that flips between
9
- * "signed out" and "signed in." The smallest possible example of "I
10
- * have GitHat wired up." Replace this file with whatever your app
11
- * actually does — auth keeps working.
9
+ * The pitch: you already have a database (Postgres, MySQL,
10
+ * DynamoDB, anything). You want auth-gated admin tooling on top of
11
+ * it without building auth from scratch.
12
+ *
13
+ * Replaces Retool + Auth0 + Vercel. Demonstrates GitHat as a
14
+ * "drop auth on your existing data" platform.
12
15
  */
13
16
  export default function Home() {
14
- const { isSignedIn, isLoading, user } = useAuth();
15
-
16
- if (isLoading) {
17
- return (
18
- <div
19
- style=\{{
20
- display: 'flex',
21
- minHeight: 'calc(100vh - 64px)',
22
- alignItems: 'center',
23
- justifyContent: 'center',
24
- color: 'var(--fg-muted)',
25
- }}
26
- >
27
- Loading…
28
- </div>
29
- );
30
- }
17
+ const { isSignedIn } = useAuth();
31
18
 
32
19
  return (
33
- <div
34
- style=\{{
35
- display: 'flex',
36
- minHeight: 'calc(100vh - 64px)',
37
- alignItems: 'center',
38
- justifyContent: 'center',
39
- padding: 'var(--space-8) var(--space-4)',
40
- background: 'var(--bg)',
41
- }}
42
- >
43
- <main
44
- style=\{{
45
- width: '100%',
46
- maxWidth: '32rem',
47
- textAlign: 'center',
48
- color: 'var(--fg)',
49
- }}
50
- >
20
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
21
+ <section style=\{{
22
+ padding: 'var(--space-12) var(--space-4)',
23
+ textAlign: 'center',
24
+ maxWidth: '40rem',
25
+ margin: '0 auto',
26
+ }}>
27
+ <h1 style=\{{
28
+ fontFamily: 'var(--font-wordmark, Georgia, serif)',
29
+ fontSize: 'clamp(2rem, 5vw, 3rem)',
30
+ lineHeight: 1.1,
31
+ marginBottom: 'var(--space-3)',
32
+ }}>
33
+ {{businessName}}
34
+ </h1>
35
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '1.125rem', marginBottom: 'var(--space-6)' }}>
36
+ Auth-gated admin UI for your existing database. Bring your
37
+ Postgres / MySQL / DynamoDB — we handle who can see it.
38
+ </p>
51
39
  {!isSignedIn ? (
52
- <>
53
- <h1
54
- style=\{{
55
- fontFamily: 'var(--font-wordmark)',
56
- fontSize: '2.5rem',
57
- lineHeight: 1.1,
58
- marginBottom: 'var(--space-3)',
59
- }}
60
- >
61
- Welcome to {{businessName}}
62
- </h1>
63
- <p
64
- style=\{{
65
- color: 'var(--fg-muted)',
66
- marginBottom: 'var(--space-6)',
67
- lineHeight: 1.6,
68
- }}
69
- >
70
- Sign in to continue, or create an account in 30 seconds.
71
- We use GitHat for identity — your password never touches
72
- this app.
73
- </p>
74
- <div
75
- style=\{{
76
- display: 'flex',
77
- gap: 'var(--space-3)',
78
- justifyContent: 'center',
79
- flexWrap: 'wrap',
80
- }}
81
- >
82
- <SignInButton />
83
- <SignUpButton />
84
- </div>
85
- </>
40
+ <SignInButton />
86
41
  ) : (
87
- <>
88
- <div
89
- style=\{{
90
- display: 'flex',
91
- alignItems: 'center',
92
- justifyContent: 'center',
93
- gap: 'var(--space-4)',
94
- marginBottom: 'var(--space-6)',
95
- }}
96
- >
97
- <UserButton />
98
- <h1
99
- style=\{{
100
- fontFamily: 'var(--font-wordmark)',
101
- fontSize: '2rem',
102
- margin: 0,
103
- }}
104
- >
105
- Hello{user?.name ? `, ${user.name}` : ''}.
106
- </h1>
107
- </div>
108
- <p
109
- style=\{{
110
- color: 'var(--fg-muted)',
111
- lineHeight: 1.6,
112
- }}
113
- >
114
- That's it. The plain template doesn't ship a dashboard or
115
- any other route — replace this page with whatever your
116
- app actually does. Authentication will keep working.
117
- </p>
118
- </>
42
+ <Link href="/admin" style=\{{
43
+ display: 'inline-block',
44
+ padding: 'var(--space-3) var(--space-6)',
45
+ borderRadius: 'var(--radius-md, 0.5rem)',
46
+ background: 'var(--primary)',
47
+ color: 'var(--bg)',
48
+ fontWeight: 600,
49
+ textDecoration: 'none',
50
+ }}>
51
+ Open dashboard →
52
+ </Link>
119
53
  )}
120
- </main>
54
+ </section>
121
55
  </div>
122
56
  );
123
57
  }