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
|
@@ -1,56 +1,21 @@
|
|
|
1
1
|
{{#if includeForgotPassword}}
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import { Suspense
|
|
5
|
-
import { useSearchParams
|
|
6
|
-
{
|
|
7
|
-
import { authApi } from '../../../githat/api/auth{{#unless typescript}}.js{{/unless}}';
|
|
8
|
-
{{/if}}
|
|
4
|
+
import { Suspense } from 'react';
|
|
5
|
+
import { useSearchParams } from 'next/navigation';
|
|
6
|
+
import { ResetPasswordForm } from '@githat/nextjs';
|
|
9
7
|
|
|
10
8
|
function ResetPasswordContent() {
|
|
11
9
|
const searchParams = useSearchParams();
|
|
12
|
-
const router = useRouter();
|
|
13
10
|
const token = searchParams.get('token');
|
|
14
11
|
|
|
15
|
-
const [password, setPassword] = useState('');
|
|
16
|
-
const [confirm, setConfirm] = useState('');
|
|
17
|
-
const [error, setError] = useState('');
|
|
18
|
-
const [loading, setLoading] = useState(false);
|
|
19
|
-
|
|
20
|
-
const handleSubmit = async (e{{#if typescript}}: React.FormEvent{{/if}}) => {
|
|
21
|
-
e.preventDefault();
|
|
22
|
-
if (password !== confirm) {
|
|
23
|
-
setError('Passwords do not match');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
setError('');
|
|
27
|
-
setLoading(true);
|
|
28
|
-
try {
|
|
29
|
-
{{#if includeGithatFolder}}
|
|
30
|
-
await authApi.resetPassword(token!, password);
|
|
31
|
-
{{else}}
|
|
32
|
-
const res = await fetch('{{apiUrl}}/auth/reset-password', {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: { 'Content-Type': 'application/json' },
|
|
35
|
-
body: JSON.stringify({ token, password }),
|
|
36
|
-
});
|
|
37
|
-
if (!res.ok) throw new Error('Reset failed');
|
|
38
|
-
{{/if}}
|
|
39
|
-
router.push('/sign-in?reset=success');
|
|
40
|
-
} catch (err) {
|
|
41
|
-
setError('Failed to reset password. The link may have expired.');
|
|
42
|
-
} finally {
|
|
43
|
-
setLoading(false);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
12
|
if (!token) {
|
|
48
13
|
return (
|
|
49
14
|
<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}}>
|
|
50
|
-
<div style=\{{ textAlign: 'center' }}>
|
|
51
|
-
<h1 style=\{{ fontSize: '1.
|
|
52
|
-
<p style=\{{ color: '#a1a1aa' }}>
|
|
53
|
-
<a href="/forgot-password" style=\{{ color: '#7c3aed' }}>Request a new one</a>
|
|
15
|
+
<div {{#if useTailwind}}className="text-center"{{else}}style=\{{ textAlign: 'center' }}{{/if}}>
|
|
16
|
+
<h1 {{#if useTailwind}}className="text-xl font-semibold text-white mb-2"{{else}}style=\{{ fontSize: '1.25rem', fontWeight: 600, color: '#fafafa', marginBottom: '0.5rem' }}{{/if}}>Invalid reset link</h1>
|
|
17
|
+
<p {{#if useTailwind}}className="text-zinc-400"{{else}}style=\{{ color: '#a1a1aa' }}{{/if}}>
|
|
18
|
+
<a href="/forgot-password" {{#if useTailwind}}className="text-violet-500 hover:underline"{{else}}style=\{{ color: '#7c3aed' }}{{/if}}>Request a new one</a>
|
|
54
19
|
</p>
|
|
55
20
|
</div>
|
|
56
21
|
</main>
|
|
@@ -59,39 +24,7 @@ function ResetPasswordContent() {
|
|
|
59
24
|
|
|
60
25
|
return (
|
|
61
26
|
<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}}>
|
|
62
|
-
<
|
|
63
|
-
<h1 style=\{{ fontSize: '1.5rem', fontWeight: 600, color: '#fafafa', marginBottom: '0.5rem' }}>Reset password</h1>
|
|
64
|
-
<p style=\{{ color: '#a1a1aa', marginBottom: '1.5rem' }}>Enter your new password below.</p>
|
|
65
|
-
{error && <p style=\{{ color: '#ef4444', marginBottom: '1rem', fontSize: '0.875rem' }}>{error}</p>}
|
|
66
|
-
<form onSubmit={handleSubmit}>
|
|
67
|
-
<input
|
|
68
|
-
type="password"
|
|
69
|
-
value={password}
|
|
70
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
71
|
-
placeholder="New password"
|
|
72
|
-
required
|
|
73
|
-
minLength={8}
|
|
74
|
-
disabled={loading}
|
|
75
|
-
style=\{{ width: '100%', padding: '0.625rem 0.75rem', background: '#111113', border: '1px solid #1e1e2e', borderRadius: '0.375rem', color: '#fafafa', marginBottom: '1rem', outline: 'none' }}
|
|
76
|
-
/>
|
|
77
|
-
<input
|
|
78
|
-
type="password"
|
|
79
|
-
value={confirm}
|
|
80
|
-
onChange={(e) => setConfirm(e.target.value)}
|
|
81
|
-
placeholder="Confirm password"
|
|
82
|
-
required
|
|
83
|
-
disabled={loading}
|
|
84
|
-
style=\{{ width: '100%', padding: '0.625rem 0.75rem', background: '#111113', border: '1px solid #1e1e2e', borderRadius: '0.375rem', color: '#fafafa', marginBottom: '1rem', outline: 'none' }}
|
|
85
|
-
/>
|
|
86
|
-
<button
|
|
87
|
-
type="submit"
|
|
88
|
-
disabled={loading}
|
|
89
|
-
style=\{{ width: '100%', padding: '0.625rem', background: '#7c3aed', color: '#fff', border: 'none', borderRadius: '0.375rem', fontWeight: 600, cursor: loading ? 'not-allowed' : 'pointer', opacity: loading ? 0.7 : 1 }}
|
|
90
|
-
>
|
|
91
|
-
{loading ? 'Resetting...' : 'Reset password'}
|
|
92
|
-
</button>
|
|
93
|
-
</form>
|
|
94
|
-
</div>
|
|
27
|
+
<ResetPasswordForm token={token} signInUrl="/sign-in" />
|
|
95
28
|
</main>
|
|
96
29
|
);
|
|
97
30
|
}
|
|
@@ -20,6 +20,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
|
|
|
20
20
|
...githatConfig,
|
|
21
21
|
{{else}}
|
|
22
22
|
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
23
|
+
apiUrl: 'https://api.githat.io',
|
|
24
|
+
{{#if typescript}}
|
|
25
|
+
tokenStorage: 'localStorage' as const,
|
|
26
|
+
{{else}}
|
|
27
|
+
tokenStorage: 'localStorage',
|
|
28
|
+
{{/if}}
|
|
23
29
|
signInUrl: '/sign-in',
|
|
24
30
|
signUpUrl: '/sign-up',
|
|
25
31
|
afterSignInUrl: '/dashboard',
|
|
@@ -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);
|
|
@@ -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);
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Product detail — `/<slug>/p/<productId>`.
|
|
3
|
-
*
|
|
4
|
-
* Server component. Fetch by (slug, productId), render. The starter
|
|
5
|
-
* ships sample content so the route works even before a backend
|
|
6
|
-
* exists. Cart writes go through `/api/cart/add` (you build that
|
|
7
|
-
* route handler against your data layer).
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import Link from 'next/link';
|
|
11
|
-
|
|
12
|
-
interface PageProps {
|
|
13
|
-
params: Promise<{ slug: string; productId: string }>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default async function ProductPage({ params }: PageProps) {
|
|
17
|
-
const { slug, productId } = await params;
|
|
18
|
-
|
|
19
|
-
// TODO: replace with a real fetch
|
|
20
|
-
const product = {
|
|
21
|
-
id: productId,
|
|
22
|
-
name: 'Plátanos verdes (libra)',
|
|
23
|
-
price: 0.5,
|
|
24
|
-
description:
|
|
25
|
-
'Plátanos verdes recién llegados, perfectos para mangú. Vendido por libra.',
|
|
26
|
-
fresh: true,
|
|
27
|
-
storeName: 'Colmado de ejemplo',
|
|
28
|
-
storeOwner: 'Don Tito',
|
|
29
|
-
storeSlug: slug,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
|
|
34
|
-
<div style=\{{ maxWidth: '40rem', margin: '0 auto', padding: 'var(--space-8) var(--space-4)' }}>
|
|
35
|
-
<Link href={`/${product.storeSlug}`} style=\{{
|
|
36
|
-
fontSize: '0.875rem',
|
|
37
|
-
color: 'var(--fg-muted)',
|
|
38
|
-
marginBottom: 'var(--space-4)',
|
|
39
|
-
display: 'inline-block',
|
|
40
|
-
}}>
|
|
41
|
-
← {product.storeName}
|
|
42
|
-
</Link>
|
|
43
|
-
|
|
44
|
-
<h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-2)' }}>
|
|
45
|
-
{product.name}
|
|
46
|
-
</h1>
|
|
47
|
-
|
|
48
|
-
{product.fresh && (
|
|
49
|
-
<p style=\{{
|
|
50
|
-
display: 'inline-block',
|
|
51
|
-
padding: 'var(--space-1) var(--space-3)',
|
|
52
|
-
borderRadius: 'var(--radius-full, 9999px)',
|
|
53
|
-
background: 'var(--success)',
|
|
54
|
-
color: 'var(--bg)',
|
|
55
|
-
fontSize: '0.75rem',
|
|
56
|
-
fontWeight: 600,
|
|
57
|
-
marginBottom: 'var(--space-3)',
|
|
58
|
-
}}>
|
|
59
|
-
Está fresquecito
|
|
60
|
-
</p>
|
|
61
|
-
)}
|
|
62
|
-
|
|
63
|
-
<p style=\{{ fontSize: '1.5rem', fontWeight: 600, marginBottom: 'var(--space-4)' }}>
|
|
64
|
-
${product.price.toFixed(2)}
|
|
65
|
-
</p>
|
|
66
|
-
|
|
67
|
-
<p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-6)', lineHeight: 1.6 }}>
|
|
68
|
-
{product.description}
|
|
69
|
-
</p>
|
|
70
|
-
|
|
71
|
-
<p style=\{{ fontSize: '0.875rem', color: 'var(--fg-subtle)', marginBottom: 'var(--space-6)' }}>
|
|
72
|
-
De tu colmadero: <strong>{product.storeOwner}</strong>
|
|
73
|
-
</p>
|
|
74
|
-
|
|
75
|
-
{/*
|
|
76
|
-
Cart-add is a real fetch in production — you wire it to
|
|
77
|
-
/api/cart/add against your data layer + the anon-session
|
|
78
|
-
cookie from src/lib/anon-session.ts.
|
|
79
|
-
*/}
|
|
80
|
-
<form action="/api/cart/add" method="POST">
|
|
81
|
-
<input type="hidden" name="productId" value={product.id} />
|
|
82
|
-
<input type="hidden" name="slug" value={product.storeSlug} />
|
|
83
|
-
<button type="submit" style=\{{
|
|
84
|
-
padding: 'var(--space-3) var(--space-6)',
|
|
85
|
-
borderRadius: 'var(--radius-md, 0.5rem)',
|
|
86
|
-
border: 'none',
|
|
87
|
-
background: 'var(--primary)',
|
|
88
|
-
color: 'var(--bg)',
|
|
89
|
-
fontWeight: 600,
|
|
90
|
-
fontSize: '1rem',
|
|
91
|
-
cursor: 'pointer',
|
|
92
|
-
}}>
|
|
93
|
-
Echar a la funda — Add to bag
|
|
94
|
-
</button>
|
|
95
|
-
</form>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A single colmadero's storefront — `/<slug>`.
|
|
3
|
-
*
|
|
4
|
-
* Server component. Take the slug, fetch the colmado's profile +
|
|
5
|
-
* products from your data layer, render. The starter ships static
|
|
6
|
-
* sample content so the route renders even without a backend.
|
|
7
|
-
*
|
|
8
|
-
* Real implementations:
|
|
9
|
-
* - Read by slug from your `colmados` table (Postgres, DynamoDB,
|
|
10
|
-
* whatever)
|
|
11
|
-
* - 404 via `notFound()` from 'next/navigation' if the slug is
|
|
12
|
-
* unknown
|
|
13
|
-
* - Pre-render the popular ones via `generateStaticParams()`
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import Link from 'next/link';
|
|
17
|
-
|
|
18
|
-
interface PageProps {
|
|
19
|
-
params: Promise<{ slug: string }>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default async function ShopPage({ params }: PageProps) {
|
|
23
|
-
const { slug } = await params;
|
|
24
|
-
|
|
25
|
-
// TODO: replace with a real fetch
|
|
26
|
-
const colmado = {
|
|
27
|
-
name: 'Colmado de ejemplo',
|
|
28
|
-
slug,
|
|
29
|
-
owner: 'Don Tito',
|
|
30
|
-
address: 'Calle Duarte #42',
|
|
31
|
-
open: true,
|
|
32
|
-
products: [
|
|
33
|
-
{ id: '1', name: 'Plátanos verdes (libra)', price: 0.5, fresh: true },
|
|
34
|
-
{ id: '2', name: 'Café Santo Domingo (1lb)', price: 6.99, fresh: false },
|
|
35
|
-
{ id: '3', name: 'Salami Induveca', price: 4.5, fresh: false },
|
|
36
|
-
{ id: '4', name: 'Recarga Claro $5', price: 5.0, fresh: false },
|
|
37
|
-
],
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
|
|
42
|
-
<div style=\{{ maxWidth: '48rem', margin: '0 auto', padding: 'var(--space-8) var(--space-4)' }}>
|
|
43
|
-
<header style=\{{ marginBottom: 'var(--space-8)' }}>
|
|
44
|
-
<h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2.25rem', marginBottom: 'var(--space-2)' }}>
|
|
45
|
-
{colmado.name}
|
|
46
|
-
</h1>
|
|
47
|
-
<p style=\{{ color: 'var(--fg-muted)', marginBottom: 'var(--space-2)' }}>
|
|
48
|
-
Tu colmadero: <strong>{colmado.owner}</strong>
|
|
49
|
-
</p>
|
|
50
|
-
<p style=\{{ color: 'var(--fg-subtle)', fontSize: '0.875rem', display: 'flex', gap: 'var(--space-3)', alignItems: 'center' }}>
|
|
51
|
-
<span>{colmado.address}</span>
|
|
52
|
-
<span>·</span>
|
|
53
|
-
<span style=\{{ color: colmado.open ? 'var(--success)' : 'var(--danger)' }}>
|
|
54
|
-
{colmado.open ? 'Abierto ahorita' : 'Cerrado'}
|
|
55
|
-
</span>
|
|
56
|
-
</p>
|
|
57
|
-
</header>
|
|
58
|
-
|
|
59
|
-
<section>
|
|
60
|
-
<h2 style=\{{ fontSize: '1.5rem', marginBottom: 'var(--space-4)' }}>Productos</h2>
|
|
61
|
-
<ul style=\{{ listStyle: 'none', display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 'var(--space-3)' }}>
|
|
62
|
-
{colmado.products.map((p) => (
|
|
63
|
-
<li key={p.id}>
|
|
64
|
-
<Link href={`/${slug}/p/${p.id}`} style=\{{
|
|
65
|
-
display: 'block',
|
|
66
|
-
padding: 'var(--space-4)',
|
|
67
|
-
borderRadius: 'var(--radius-md, 0.5rem)',
|
|
68
|
-
border: '1px solid var(--border)',
|
|
69
|
-
background: 'var(--surface)',
|
|
70
|
-
color: 'inherit',
|
|
71
|
-
textDecoration: 'none',
|
|
72
|
-
}}>
|
|
73
|
-
<div style=\{{ fontWeight: 600, marginBottom: 'var(--space-1)' }}>{p.name}</div>
|
|
74
|
-
{p.fresh && (
|
|
75
|
-
<div style=\{{ fontSize: '0.75rem', color: 'var(--success)', marginBottom: 'var(--space-1)' }}>
|
|
76
|
-
Está fresquecito
|
|
77
|
-
</div>
|
|
78
|
-
)}
|
|
79
|
-
<div style=\{{ fontSize: '0.875rem', color: 'var(--fg-muted)' }}>
|
|
80
|
-
${p.price.toFixed(2)}
|
|
81
|
-
</div>
|
|
82
|
-
</Link>
|
|
83
|
-
</li>
|
|
84
|
-
))}
|
|
85
|
-
</ul>
|
|
86
|
-
</section>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|