create-githat-app 1.6.0 → 1.8.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.
- package/dist/cli.js +5 -5
- package/package.json +6 -2
- package/templates/agent/app/(auth)/forgot-password/page.tsx.hbs +11 -0
- package/templates/agent/app/(auth)/reset-password/page.tsx.hbs +39 -0
- package/templates/agent/app/(auth)/verify-email/page.tsx.hbs +41 -0
- package/templates/agent/app/admin/agent/page.tsx.hbs +159 -62
- package/templates/agent/app/admin/layout.tsx.hbs +82 -0
- package/templates/agent/app/admin/mcp/page.tsx.hbs +156 -0
- package/templates/agent/app/dashboard/agents/page.tsx.hbs +9 -0
- package/templates/agent/app/dashboard/layout.tsx.hbs +9 -0
- package/templates/agent/app/dashboard/mcp/page.tsx.hbs +9 -0
- package/templates/agent/app/dashboard/page.tsx.hbs +128 -0
- package/templates/agent/app/globals.css.hbs +14 -9
- package/templates/agent/app/layout.tsx.hbs +7 -1
- package/templates/agent/app/page.tsx.hbs +127 -70
- package/templates/agent/app/verify/agent/page.tsx.hbs +124 -0
- package/templates/agent/next.config.ts.hbs +4 -5
- package/templates/agent/public/HERO_IMAGE.md +23 -0
- package/templates/base/githat/api/agents.ts.hbs +6 -6
- package/templates/base/githat/config.ts.hbs +7 -2
- package/templates/base/githat/dashboard/overview.tsx.hbs +106 -16
- package/templates/classroom/app/layout.tsx.hbs +6 -0
- package/templates/classroom/next.config.ts.hbs +4 -5
- package/templates/content/app/layout.tsx.hbs +6 -0
- package/templates/content/next.config.ts.hbs +4 -5
- package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +2 -2
- package/templates/dashboard/app/layout.tsx.hbs +6 -0
- package/templates/dashboard/next.config.ts.hbs +4 -5
- package/templates/fullstack/apps-web-nextjs/app/layout.tsx.hbs +6 -0
- package/templates/fullstack/apps-web-nextjs/next.config.ts.hbs +5 -5
- package/templates/marketplace/app/layout.tsx.hbs +6 -0
- package/templates/marketplace/next.config.ts.hbs +4 -5
- package/templates/nextjs/app/(auth)/forgot-password/page.tsx.hbs +2 -54
- package/templates/nextjs/app/(auth)/reset-password/page.tsx.hbs +8 -75
- package/templates/nextjs/app/layout.tsx.hbs +6 -0
- package/templates/nextjs/next.config.ts.hbs +4 -5
- package/templates/plain/app/layout.tsx.hbs +6 -0
- package/templates/plain/next.config.ts.hbs +4 -5
- package/templates/portfolio/app/layout.tsx.hbs +6 -0
- package/templates/portfolio/next.config.ts.hbs +4 -5
- package/templates/saas/app/layout.tsx.hbs +6 -0
- package/templates/saas/next.config.ts.hbs +4 -5
- package/templates/agent/proxy.ts.hbs +0 -10
- package/templates/classroom/proxy.ts.hbs +0 -10
- package/templates/content/proxy.ts.hbs +0 -10
- package/templates/dashboard/proxy.ts.hbs +0 -10
- package/templates/marketplace/app/(shop)/[slug]/p/[productId]/page.tsx.hbs +0 -99
- package/templates/marketplace/app/(shop)/[slug]/page.tsx.hbs +0 -90
- package/templates/marketplace/proxy.ts.hbs +0 -10
- package/templates/nextjs/proxy.ts.hbs +0 -10
- package/templates/plain/proxy.ts.hbs +0 -10
- package/templates/portfolio/proxy.ts.hbs +0 -10
- package/templates/saas/proxy.ts.hbs +0 -10
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public agent verification page.
|
|
5
|
+
* Anyone can visit /verify/agent?wallet=<wallet> to confirm an agent's
|
|
6
|
+
* identity, check its current status, and review its capabilities.
|
|
7
|
+
*
|
|
8
|
+
* Wallet is a query param (not a path) so this is a single static page
|
|
9
|
+
* that handles any wallet — output: 'export' can't pre-render an unbounded
|
|
10
|
+
* dynamic-route param space.
|
|
11
|
+
*/
|
|
12
|
+
import { Suspense, useEffect, useState } from 'react';
|
|
13
|
+
import { useSearchParams } from 'next/navigation';
|
|
14
|
+
|
|
15
|
+
const GITHAT_API = 'https://api.githat.io';
|
|
16
|
+
|
|
17
|
+
{{#if typescript}}
|
|
18
|
+
interface AgentVerify {
|
|
19
|
+
verified: boolean;
|
|
20
|
+
name: string;
|
|
21
|
+
wallet: string;
|
|
22
|
+
chainId: number;
|
|
23
|
+
status: 'active' | 'paused' | 'revoked' | 'pending';
|
|
24
|
+
verifiedSince: string;
|
|
25
|
+
lastActivity: string;
|
|
26
|
+
capabilities: string[];
|
|
27
|
+
actionsLast24h: number;
|
|
28
|
+
verificationUrl: string;
|
|
29
|
+
}
|
|
30
|
+
{{/if}}
|
|
31
|
+
|
|
32
|
+
function VerifyAgentContent() {
|
|
33
|
+
const searchParams = useSearchParams();
|
|
34
|
+
const wallet = searchParams.get('wallet') ?? '';
|
|
35
|
+
const [agent, setAgent] = useState{{#if typescript}}<AgentVerify | null>{{/if}}(null);
|
|
36
|
+
const [loading, setLoading] = useState(true);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!wallet) { setLoading(false); return; }
|
|
40
|
+
let cancelled = false;
|
|
41
|
+
fetch(`${GITHAT_API}/verify/agent/${wallet}`)
|
|
42
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
43
|
+
.then((data) => { if (!cancelled) setAgent(data); })
|
|
44
|
+
.catch(() => { if (!cancelled) setAgent(null); })
|
|
45
|
+
.finally(() => { if (!cancelled) setLoading(false); });
|
|
46
|
+
return () => { cancelled = true; };
|
|
47
|
+
}, [wallet]);
|
|
48
|
+
|
|
49
|
+
if (loading) {
|
|
50
|
+
return (
|
|
51
|
+
<main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#a1a1aa', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
52
|
+
<p style=\{{ fontSize: '0.875rem' }}>Loading…</p>
|
|
53
|
+
</main>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!agent?.verified) {
|
|
58
|
+
return (
|
|
59
|
+
<main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#fafafa', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '4rem 1rem' }}>
|
|
60
|
+
<div style=\{{ textAlign: 'center' }}>
|
|
61
|
+
<p style=\{{ fontSize: '1.125rem', color: '#a1a1aa', marginBottom: '0.5rem' }}>Agent not found</p>
|
|
62
|
+
<p style=\{{ fontSize: '0.875rem', color: '#52525b', fontFamily: "'JetBrains Mono', monospace", wordBreak: 'break-all' }}>{wallet || '(no wallet provided)'}</p>
|
|
63
|
+
</div>
|
|
64
|
+
</main>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const statusColor = ({
|
|
69
|
+
active: '#16a34a',
|
|
70
|
+
paused: '#d97706',
|
|
71
|
+
revoked: '#dc2626',
|
|
72
|
+
pending: '#6366f1',
|
|
73
|
+
}{{#if typescript}} as Record<string, string>{{/if}})[agent.status] ?? '#71717a';
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#fafafa', display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '4rem 1rem' }}>
|
|
77
|
+
<div style=\{{ width: '100%', maxWidth: '36rem' }}>
|
|
78
|
+
<div style=\{{ marginBottom: '2rem', textAlign: 'center' }}>
|
|
79
|
+
<div style=\{{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem', padding: '0.375rem 0.875rem', borderRadius: '9999px', background: statusColor + '22', border: `1px solid ${statusColor}44`, marginBottom: '1.5rem' }}>
|
|
80
|
+
<span style=\{{ width: '0.5rem', height: '0.5rem', borderRadius: '9999px', background: statusColor, display: 'inline-block' }} />
|
|
81
|
+
<span style=\{{ fontSize: '0.75rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: statusColor }}>{agent.status}</span>
|
|
82
|
+
</div>
|
|
83
|
+
<h1 style=\{{ fontSize: '1.75rem', fontWeight: 700, marginBottom: '0.5rem' }}>{agent.name}</h1>
|
|
84
|
+
<p style=\{{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem', color: '#71717a', wordBreak: 'break-all' }}>{agent.wallet}</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div style=\{{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginBottom: '1.5rem' }}>
|
|
88
|
+
{[
|
|
89
|
+
{ label: 'Actions (24h)', value: agent.actionsLast24h },
|
|
90
|
+
{ label: 'Verified since', value: new Date(agent.verifiedSince).toLocaleDateString() },
|
|
91
|
+
].map(({ label, value }) => (
|
|
92
|
+
<div key={label} style=\{{ padding: '1.25rem', borderRadius: '0.75rem', border: '1px solid #27272a', background: '#18181b' }}>
|
|
93
|
+
<div style=\{{ fontSize: '0.75rem', color: '#71717a', marginBottom: '0.375rem' }}>{label}</div>
|
|
94
|
+
<div style=\{{ fontSize: '1.125rem', fontWeight: 600 }}>{value}</div>
|
|
95
|
+
</div>
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
{agent.capabilities.length > 0 && (
|
|
100
|
+
<div style=\{{ padding: '1.25rem', borderRadius: '0.75rem', border: '1px solid #27272a', background: '#18181b', marginBottom: '1.5rem' }}>
|
|
101
|
+
<h2 style=\{{ fontSize: '0.875rem', fontWeight: 600, color: '#a1a1aa', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '1rem' }}>Capabilities</h2>
|
|
102
|
+
<div style=\{{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
|
|
103
|
+
{agent.capabilities.map((cap{{#if typescript}}: string{{/if}}) => (
|
|
104
|
+
<span key={cap} style=\{{ padding: '0.375rem 0.75rem', borderRadius: '0.375rem', border: '1px solid #3f3f46', fontSize: '0.8125rem', fontFamily: "'JetBrains Mono', monospace", color: '#a1a1aa' }}>{cap}</span>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
</main>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default function VerifyAgentPage() {
|
|
115
|
+
return (
|
|
116
|
+
<Suspense fallback={
|
|
117
|
+
<main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#a1a1aa', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
118
|
+
<p style=\{{ fontSize: '0.875rem' }}>Loading…</p>
|
|
119
|
+
</main>
|
|
120
|
+
}>
|
|
121
|
+
<VerifyAgentContent />
|
|
122
|
+
</Suspense>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
{{/if}}import { withGitHat } from '@githat/nextjs/server';
|
|
1
|
+
import { withGitHat } from '@githat/nextjs/server';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const nextConfig = {
|
|
4
|
+
output: 'export',
|
|
5
|
+
images: { unoptimized: true },
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
export default withGitHat(nextConfig);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Hero Image
|
|
2
|
+
|
|
3
|
+
Drop your logo or hero image (`.png`, `.jpg`, `.webp`) in this folder.
|
|
4
|
+
|
|
5
|
+
Then in `app/page.tsx`, replace `<HeroImagePlaceholder />` with:
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Image
|
|
9
|
+
src="/your-image.png"
|
|
10
|
+
alt="{{businessName}}"
|
|
11
|
+
fill
|
|
12
|
+
style={{ objectFit: 'cover', borderRadius: '1rem' }}
|
|
13
|
+
priority
|
|
14
|
+
/>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Wrap it in a `position: 'relative'` container with the same `aspectRatio: '4 / 3'` as the placeholder.
|
|
18
|
+
|
|
19
|
+
The `Image` component from `next/image` is not yet imported — add it at the top of `page.tsx`:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import Image from 'next/image';
|
|
23
|
+
```
|
|
@@ -5,13 +5,13 @@ import type { AiAgent } from './types';
|
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
7
|
export const agentsApi = {
|
|
8
|
-
register: (
|
|
9
|
-
githatApi.post{{#if typescript}}<{ agent: AiAgent }>{{/if}}('/agent/register', {
|
|
8
|
+
register: (walletAddress{{#if typescript}}: string{{/if}}, chainId{{#if typescript}}: number{{/if}}, name{{#if typescript}}: string{{/if}}) =>
|
|
9
|
+
githatApi.post{{#if typescript}}<{ agent: AiAgent }>{{/if}}('/agent/register', { walletAddress, chainId, name }),
|
|
10
10
|
|
|
11
|
-
challenge: (
|
|
12
|
-
githatApi.post{{#if typescript}}<{ nonce: string }>{{/if}}('/agent/challenge', {
|
|
11
|
+
challenge: (walletAddress{{#if typescript}}: string{{/if}}) =>
|
|
12
|
+
githatApi.post{{#if typescript}}<{ nonce: string }>{{/if}}('/agent/challenge', { walletAddress }),
|
|
13
13
|
|
|
14
|
-
getToken: (
|
|
15
|
-
githatApi.post{{#if typescript}}<{ accessToken: string }>{{/if}}('/agent/token', {
|
|
14
|
+
getToken: (walletAddress{{#if typescript}}: string{{/if}}, signature{{#if typescript}}: string{{/if}}, nonce{{#if typescript}}: string{{/if}}) =>
|
|
15
|
+
githatApi.post{{#if typescript}}<{ accessToken: string }>{{/if}}('/agent/token', { walletAddress, signature, nonce }),
|
|
16
16
|
};
|
|
17
17
|
{{/if}}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{{#if includeGithatFolder}}
|
|
2
2
|
export const githatConfig = {
|
|
3
3
|
appName: '{{businessName}}',
|
|
4
|
-
publishableKey: {{#
|
|
5
|
-
|
|
4
|
+
publishableKey: {{#ifNext framework}}process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY{{else}}import.meta.env.VITE_GITHAT_PUBLISHABLE_KEY{{/ifNext}} || '',
|
|
5
|
+
// For Next.js-shaped frameworks we point at the same-origin proxy
|
|
6
|
+
// mounted at app/api/githat/[...path]/route.ts — that proxy forwards
|
|
7
|
+
// to api.githat.io and re-emits Set-Cookie on this app's domain so
|
|
8
|
+
// cookies are visible to proxy.ts and getAuth(). For react-vite we
|
|
9
|
+
// hit api.githat.io directly (browser-token flows only).
|
|
10
|
+
apiUrl: {{#ifNext framework}}process.env.NEXT_PUBLIC_GITHAT_API_URL || '/api/githat'{{else}}import.meta.env.VITE_GITHAT_API_URL || '{{apiUrl}}'{{/ifNext}},
|
|
6
11
|
signInUrl: '/sign-in',
|
|
7
12
|
signUpUrl: '/sign-up',
|
|
8
13
|
afterSignInUrl: '/dashboard',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{{#unless includeDashboard}}{{else}}
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
+
import Link from 'next/link';
|
|
4
5
|
import { useAuth } from '@githat/nextjs';
|
|
5
6
|
|
|
6
7
|
export function DashboardOverview() {
|
|
@@ -8,29 +9,118 @@ export function DashboardOverview() {
|
|
|
8
9
|
|
|
9
10
|
return (
|
|
10
11
|
<div>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
<div style=\{{ marginBottom: '2rem' }}>
|
|
13
|
+
<h1 style=\{{ fontSize: '1.5rem', fontWeight: 700, color: '#fafafa', marginBottom: '0.375rem' }}>
|
|
14
|
+
Welcome back{user?.name ? `, ${user.name}` : ''}
|
|
15
|
+
</h1>
|
|
16
|
+
{org && (
|
|
17
|
+
<p style=\{{ color: '#71717a', fontSize: '0.875rem' }}>
|
|
18
|
+
{org.name} · <span style=\{{ color: '#a1a1aa' }}>{org.role}</span>
|
|
19
|
+
</p>
|
|
20
|
+
)}
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
{/* Stats row */}
|
|
24
|
+
<div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(14rem, 1fr))', gap: '1rem', marginBottom: '2.5rem' }}>
|
|
25
|
+
<StatCard label="Status" value="Active" sub="Account health" />
|
|
26
|
+
<StatCard label="Plan" value={org?.tier ?? 'Free'} sub="Current tier" />
|
|
27
|
+
<StatCard label="Identity" value="GitHat" sub="Auth provider" />
|
|
28
|
+
{{#if includeOrgManagement}}
|
|
29
|
+
<StatCard label="Members" value={String(org?.memberCount ?? 1)} sub="Team size" />
|
|
30
|
+
{{/if}}
|
|
31
|
+
</div>
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
33
|
+
{/* Quick actions */}
|
|
34
|
+
<div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr))', gap: '1rem' }}>
|
|
35
|
+
{{#if includeOrgManagement}}
|
|
36
|
+
<ActionCard
|
|
37
|
+
title="Invite team members"
|
|
38
|
+
desc="Add your business partner, accountant, or employee to your org."
|
|
39
|
+
cta="Invite"
|
|
40
|
+
href="/dashboard/members"
|
|
41
|
+
accent="#6366f1"
|
|
42
|
+
/>
|
|
43
|
+
<ActionCard
|
|
44
|
+
title="Organization settings"
|
|
45
|
+
desc="Update your org name, billing plan, and member permissions."
|
|
46
|
+
cta="Settings"
|
|
47
|
+
href="/dashboard/settings"
|
|
48
|
+
accent="#8b5cf6"
|
|
49
|
+
/>
|
|
50
|
+
{{/if}}
|
|
51
|
+
{{#if includeMcpModule}}
|
|
52
|
+
<ActionCard
|
|
53
|
+
title="Connect an MCP server"
|
|
54
|
+
desc="Hook your data into any AI assistant via the Model Context Protocol."
|
|
55
|
+
cta="Connect"
|
|
56
|
+
href="/dashboard/mcp"
|
|
57
|
+
accent="#0ea5e9"
|
|
58
|
+
/>
|
|
59
|
+
{{/if}}
|
|
60
|
+
{{#if includeAgentModule}}
|
|
61
|
+
<ActionCard
|
|
62
|
+
title="Deploy an AI agent"
|
|
63
|
+
desc="Register a wallet-bound autonomous agent with capability scoping and a kill switch."
|
|
64
|
+
cta="Deploy"
|
|
65
|
+
href="/dashboard/agents"
|
|
66
|
+
accent="#10b981"
|
|
67
|
+
/>
|
|
68
|
+
{{/if}}
|
|
69
|
+
<ActionCard
|
|
70
|
+
title="Manage apps"
|
|
71
|
+
desc="View and rotate your publishable keys, configure allowed origins."
|
|
72
|
+
cta="View apps"
|
|
73
|
+
href="/dashboard/apps"
|
|
74
|
+
accent="#f59e0b"
|
|
75
|
+
/>
|
|
24
76
|
</div>
|
|
25
77
|
</div>
|
|
26
78
|
);
|
|
27
79
|
}
|
|
28
80
|
|
|
29
|
-
function StatCard({
|
|
81
|
+
function StatCard({ label, value, sub }{{#if typescript}}: { label: string; value: string; sub: string }{{/if}}) {
|
|
82
|
+
return (
|
|
83
|
+
<div style=\{{
|
|
84
|
+
background: '#111113',
|
|
85
|
+
border: '1px solid #1e1e2e',
|
|
86
|
+
borderRadius: '0.75rem',
|
|
87
|
+
padding: '1.5rem',
|
|
88
|
+
}}>
|
|
89
|
+
<p style=\{{ fontSize: '0.8125rem', color: '#71717a', marginBottom: '0.375rem' }}>{label}</p>
|
|
90
|
+
<p style=\{{ fontSize: '1.5rem', fontWeight: 700, color: '#fafafa', marginBottom: '0.25rem' }}>{value}</p>
|
|
91
|
+
<p style=\{{ fontSize: '0.75rem', color: '#52525b' }}>{sub}</p>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function ActionCard({ title, desc, cta, href, accent }{{#if typescript}}: {
|
|
97
|
+
title: string; desc: string; cta: string; href: string; accent: string;
|
|
98
|
+
}{{/if}}) {
|
|
30
99
|
return (
|
|
31
|
-
<div style=\{{
|
|
32
|
-
|
|
33
|
-
|
|
100
|
+
<div style=\{{
|
|
101
|
+
background: '#111113',
|
|
102
|
+
border: '1px solid #1e1e2e',
|
|
103
|
+
borderRadius: '0.75rem',
|
|
104
|
+
padding: '1.5rem',
|
|
105
|
+
display: 'flex',
|
|
106
|
+
flexDirection: 'column',
|
|
107
|
+
gap: '0.75rem',
|
|
108
|
+
}}>
|
|
109
|
+
<h3 style=\{{ fontSize: '0.9375rem', fontWeight: 600, color: '#fafafa' }}>{title}</h3>
|
|
110
|
+
<p style=\{{ fontSize: '0.875rem', color: '#71717a', lineHeight: 1.6, flex: 1 }}>{desc}</p>
|
|
111
|
+
<Link href={href} style=\{{
|
|
112
|
+
display: 'inline-block',
|
|
113
|
+
background: accent,
|
|
114
|
+
color: '#fff',
|
|
115
|
+
textDecoration: 'none',
|
|
116
|
+
padding: '0.5rem 1rem',
|
|
117
|
+
borderRadius: '0.375rem',
|
|
118
|
+
fontSize: '0.875rem',
|
|
119
|
+
fontWeight: 600,
|
|
120
|
+
alignSelf: 'flex-start',
|
|
121
|
+
}}>
|
|
122
|
+
{cta} →
|
|
123
|
+
</Link>
|
|
34
124
|
</div>
|
|
35
125
|
);
|
|
36
126
|
}
|
|
@@ -19,6 +19,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
|
|
|
19
19
|
*/}
|
|
20
20
|
<GitHatProvider config=\{{
|
|
21
21
|
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
22
|
+
apiUrl: 'https://api.githat.io',
|
|
23
|
+
{{#if typescript}}
|
|
24
|
+
tokenStorage: 'localStorage' as const,
|
|
25
|
+
{{else}}
|
|
26
|
+
tokenStorage: 'localStorage',
|
|
27
|
+
{{/if}}
|
|
22
28
|
signInUrl: '/sign-in',
|
|
23
29
|
signUpUrl: '/sign-up',
|
|
24
30
|
afterSignInUrl: '/',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
{{/if}}import { withGitHat } from '@githat/nextjs/server';
|
|
1
|
+
import { withGitHat } from '@githat/nextjs/server';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const nextConfig = {
|
|
4
|
+
output: 'export',
|
|
5
|
+
images: { unoptimized: true },
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
export default withGitHat(nextConfig);
|
|
@@ -19,6 +19,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
|
|
|
19
19
|
*/}
|
|
20
20
|
<GitHatProvider config=\{{
|
|
21
21
|
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
22
|
+
apiUrl: 'https://api.githat.io',
|
|
23
|
+
{{#if typescript}}
|
|
24
|
+
tokenStorage: 'localStorage' as const,
|
|
25
|
+
{{else}}
|
|
26
|
+
tokenStorage: 'localStorage',
|
|
27
|
+
{{/if}}
|
|
22
28
|
signInUrl: '/sign-in',
|
|
23
29
|
signUpUrl: '/sign-up',
|
|
24
30
|
afterSignInUrl: '/',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
{{/if}}import { withGitHat } from '@githat/nextjs/server';
|
|
1
|
+
import { withGitHat } from '@githat/nextjs/server';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const nextConfig = {
|
|
4
|
+
output: 'export',
|
|
5
|
+
images: { unoptimized: true },
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
export default withGitHat(nextConfig);
|
|
@@ -6,8 +6,8 @@ import { listEntities } from '../../../../src/lib/db';
|
|
|
6
6
|
*
|
|
7
7
|
* Server component — runs `listEntities(entityName)` (which you
|
|
8
8
|
* implement in src/lib/db.ts) and renders rows as a basic table.
|
|
9
|
-
* Auth-gating
|
|
10
|
-
* need entity-level RBAC, check `verifyToken` claims here.
|
|
9
|
+
* Auth-gating happens client-side via `useAuth` in the static export;
|
|
10
|
+
* if you need entity-level RBAC, check `verifyToken` claims here.
|
|
11
11
|
*/
|
|
12
12
|
export default async function EntityPage({
|
|
13
13
|
params,
|
|
@@ -19,6 +19,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
|
|
|
19
19
|
*/}
|
|
20
20
|
<GitHatProvider config=\{{
|
|
21
21
|
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
22
|
+
apiUrl: 'https://api.githat.io',
|
|
23
|
+
{{#if typescript}}
|
|
24
|
+
tokenStorage: 'localStorage' as const,
|
|
25
|
+
{{else}}
|
|
26
|
+
tokenStorage: 'localStorage',
|
|
27
|
+
{{/if}}
|
|
22
28
|
signInUrl: '/sign-in',
|
|
23
29
|
signUpUrl: '/sign-up',
|
|
24
30
|
afterSignInUrl: '/',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
{{/if}}import { withGitHat } from '@githat/nextjs/server';
|
|
1
|
+
import { withGitHat } from '@githat/nextjs/server';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const nextConfig = {
|
|
4
|
+
output: 'export',
|
|
5
|
+
images: { unoptimized: true },
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
export default withGitHat(nextConfig);
|
|
@@ -14,6 +14,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
|
|
|
14
14
|
<body>
|
|
15
15
|
<GitHatProvider config=\{{
|
|
16
16
|
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
17
|
+
apiUrl: 'https://api.githat.io',
|
|
18
|
+
{{#if typescript}}
|
|
19
|
+
tokenStorage: 'localStorage' as const,
|
|
20
|
+
{{else}}
|
|
21
|
+
tokenStorage: 'localStorage',
|
|
22
|
+
{{/if}}
|
|
17
23
|
signInUrl: '/sign-in',
|
|
18
24
|
signUpUrl: '/sign-up',
|
|
19
25
|
afterSignInUrl: '/dashboard',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
{{/if}}import { withGitHat } from '@githat/nextjs/server';
|
|
1
|
+
import { withGitHat } from '@githat/nextjs/server';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const nextConfig = {
|
|
4
|
+
output: 'export',
|
|
5
|
+
images: { unoptimized: true },
|
|
6
|
+
// Proxy API calls to the backend in development
|
|
7
7
|
async rewrites() {
|
|
8
8
|
return [
|
|
9
9
|
{
|
|
@@ -25,6 +25,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
|
|
|
25
25
|
<body>
|
|
26
26
|
<GitHatProvider config=\{{
|
|
27
27
|
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
28
|
+
apiUrl: 'https://api.githat.io',
|
|
29
|
+
{{#if typescript}}
|
|
30
|
+
tokenStorage: 'localStorage' as const,
|
|
31
|
+
{{else}}
|
|
32
|
+
tokenStorage: 'localStorage',
|
|
33
|
+
{{/if}}
|
|
28
34
|
signInUrl: '/sign-in',
|
|
29
35
|
signUpUrl: '/sign-up',
|
|
30
36
|
afterSignInUrl: '/',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
{{/if}}import { withGitHat } from '@githat/nextjs/server';
|
|
1
|
+
import { withGitHat } from '@githat/nextjs/server';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const nextConfig = {
|
|
4
|
+
output: 'export',
|
|
5
|
+
images: { unoptimized: true },
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
export default withGitHat(nextConfig);
|
|
@@ -1,62 +1,10 @@
|
|
|
1
1
|
{{#if includeForgotPassword}}
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { useState } from 'react';
|
|
5
|
-
{{#if includeGithatFolder}}
|
|
6
|
-
import { authApi } from '../../../githat/api/auth{{#unless typescript}}.js{{/unless}}';
|
|
7
|
-
{{/if}}
|
|
2
|
+
import { ForgotPasswordForm } from '@githat/nextjs';
|
|
8
3
|
|
|
9
4
|
export default function ForgotPasswordPage() {
|
|
10
|
-
const [email, setEmail] = useState('');
|
|
11
|
-
const [sent, setSent] = useState(false);
|
|
12
|
-
const [error, setError] = useState('');
|
|
13
|
-
|
|
14
|
-
const handleSubmit = async (e{{#if typescript}}: React.FormEvent{{/if}}) => {
|
|
15
|
-
e.preventDefault();
|
|
16
|
-
setError('');
|
|
17
|
-
try {
|
|
18
|
-
{{#if includeGithatFolder}}
|
|
19
|
-
await authApi.forgotPassword(email);
|
|
20
|
-
{{else}}
|
|
21
|
-
await fetch('{{apiUrl}}/auth/forgot-password', {
|
|
22
|
-
method: 'POST',
|
|
23
|
-
headers: { 'Content-Type': 'application/json' },
|
|
24
|
-
body: JSON.stringify({ email }),
|
|
25
|
-
});
|
|
26
|
-
{{/if}}
|
|
27
|
-
setSent(true);
|
|
28
|
-
} catch (err) {
|
|
29
|
-
setError('Something went wrong. Please try again.');
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
5
|
return (
|
|
34
6
|
<main {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b]"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b' }}{{/if}}>
|
|
35
|
-
<
|
|
36
|
-
<h1 style=\{{ fontSize: '1.5rem', fontWeight: 600, color: '#fafafa', marginBottom: '0.5rem' }}>Reset password</h1>
|
|
37
|
-
{sent ? (
|
|
38
|
-
<p style=\{{ color: '#a1a1aa' }}>Check your email for a reset link.</p>
|
|
39
|
-
) : (
|
|
40
|
-
<form onSubmit={handleSubmit}>
|
|
41
|
-
<p style=\{{ color: '#a1a1aa', marginBottom: '1.5rem' }}>Enter your email to receive a reset link.</p>
|
|
42
|
-
{error && <p style=\{{ color: '#ef4444', marginBottom: '1rem', fontSize: '0.875rem' }}>{error}</p>}
|
|
43
|
-
<input
|
|
44
|
-
type="email"
|
|
45
|
-
value={email}
|
|
46
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
47
|
-
placeholder="you@example.com"
|
|
48
|
-
required
|
|
49
|
-
style=\{{ width: '100%', padding: '0.625rem 0.75rem', background: '#111113', border: '1px solid #1e1e2e', borderRadius: '0.375rem', color: '#fafafa', marginBottom: '1rem', outline: 'none' }}
|
|
50
|
-
/>
|
|
51
|
-
<button
|
|
52
|
-
type="submit"
|
|
53
|
-
style=\{{ width: '100%', padding: '0.625rem', background: '#7c3aed', color: '#fff', border: 'none', borderRadius: '0.375rem', fontWeight: 600, cursor: 'pointer' }}
|
|
54
|
-
>
|
|
55
|
-
Send reset link
|
|
56
|
-
</button>
|
|
57
|
-
</form>
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
7
|
+
<ForgotPasswordForm signInUrl="/sign-in" />
|
|
60
8
|
</main>
|
|
61
9
|
);
|
|
62
10
|
}
|