shipd 0.1.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/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1366 -0
- package/docs-template/README.md +255 -0
- package/docs-template/[slug]/[subslug]/page.tsx +1242 -0
- package/docs-template/[slug]/page.tsx +422 -0
- package/docs-template/api/page.tsx +47 -0
- package/docs-template/components/docs/docs-category-page.tsx +162 -0
- package/docs-template/components/docs/docs-code-card.tsx +135 -0
- package/docs-template/components/docs/docs-header.tsx +69 -0
- package/docs-template/components/docs/docs-nav.ts +95 -0
- package/docs-template/components/docs/docs-sidebar.tsx +112 -0
- package/docs-template/components/docs/docs-toc.tsx +38 -0
- package/docs-template/components/ui/badge.tsx +47 -0
- package/docs-template/components/ui/button.tsx +60 -0
- package/docs-template/components/ui/card.tsx +93 -0
- package/docs-template/components/ui/sheet.tsx +140 -0
- package/docs-template/documentation/page.tsx +80 -0
- package/docs-template/layout.tsx +27 -0
- package/docs-template/lib/utils.ts +7 -0
- package/docs-template/page.tsx +360 -0
- package/package.json +66 -0
- package/template/.env.example +45 -0
- package/template/README.md +239 -0
- package/template/app/api/auth/[...all]/route.ts +4 -0
- package/template/app/api/chat/route.ts +16 -0
- package/template/app/api/subscription/route.ts +25 -0
- package/template/app/api/upload-image/route.ts +64 -0
- package/template/app/blog/[slug]/page.tsx +314 -0
- package/template/app/blog/page.tsx +107 -0
- package/template/app/dashboard/_components/chart-interactive.tsx +289 -0
- package/template/app/dashboard/_components/chatbot.tsx +39 -0
- package/template/app/dashboard/_components/mode-toggle.tsx +46 -0
- package/template/app/dashboard/_components/navbar.tsx +84 -0
- package/template/app/dashboard/_components/section-cards.tsx +102 -0
- package/template/app/dashboard/_components/sidebar.tsx +90 -0
- package/template/app/dashboard/_components/subscribe-button.tsx +49 -0
- package/template/app/dashboard/billing/page.tsx +277 -0
- package/template/app/dashboard/chat/page.tsx +73 -0
- package/template/app/dashboard/cli/page.tsx +260 -0
- package/template/app/dashboard/layout.tsx +24 -0
- package/template/app/dashboard/page.tsx +216 -0
- package/template/app/dashboard/payment/_components/manage-subscription.tsx +22 -0
- package/template/app/dashboard/payment/page.tsx +126 -0
- package/template/app/dashboard/settings/page.tsx +613 -0
- package/template/app/dashboard/upload/page.tsx +324 -0
- package/template/app/error.tsx +78 -0
- package/template/app/favicon.ico +0 -0
- package/template/app/globals.css +126 -0
- package/template/app/layout.tsx +135 -0
- package/template/app/not-found.tsx +45 -0
- package/template/app/page.tsx +28 -0
- package/template/app/pricing/_component/pricing-table.tsx +276 -0
- package/template/app/pricing/page.tsx +23 -0
- package/template/app/privacy-policy/page.tsx +280 -0
- package/template/app/robots.txt +12 -0
- package/template/app/sign-in/page.tsx +228 -0
- package/template/app/sign-up/page.tsx +243 -0
- package/template/app/sitemap.ts +62 -0
- package/template/app/success/page.tsx +123 -0
- package/template/app/terms-of-service/page.tsx +212 -0
- package/template/auth-schema.ts +47 -0
- package/template/components/homepage/cli-workflow-section.tsx +138 -0
- package/template/components/homepage/features-section.tsx +150 -0
- package/template/components/homepage/footer.tsx +53 -0
- package/template/components/homepage/hero-section.tsx +112 -0
- package/template/components/homepage/integrations.tsx +124 -0
- package/template/components/homepage/navigation.tsx +116 -0
- package/template/components/homepage/news-section.tsx +82 -0
- package/template/components/homepage/testimonials-section.tsx +34 -0
- package/template/components/logos/BetterAuth.tsx +21 -0
- package/template/components/logos/NeonPostgres.tsx +41 -0
- package/template/components/logos/Nextjs.tsx +72 -0
- package/template/components/logos/Polar.tsx +7 -0
- package/template/components/logos/TailwindCSS.tsx +27 -0
- package/template/components/logos/index.ts +6 -0
- package/template/components/logos/shadcnui.tsx +8 -0
- package/template/components/provider.tsx +8 -0
- package/template/components/ui/avatar.tsx +53 -0
- package/template/components/ui/badge.tsx +46 -0
- package/template/components/ui/button.tsx +59 -0
- package/template/components/ui/card.tsx +92 -0
- package/template/components/ui/chart.tsx +353 -0
- package/template/components/ui/checkbox.tsx +32 -0
- package/template/components/ui/dialog.tsx +135 -0
- package/template/components/ui/dropdown-menu.tsx +257 -0
- package/template/components/ui/form.tsx +167 -0
- package/template/components/ui/input.tsx +21 -0
- package/template/components/ui/label.tsx +24 -0
- package/template/components/ui/progress.tsx +31 -0
- package/template/components/ui/resizable.tsx +56 -0
- package/template/components/ui/select.tsx +185 -0
- package/template/components/ui/separator.tsx +28 -0
- package/template/components/ui/sheet.tsx +139 -0
- package/template/components/ui/skeleton.tsx +13 -0
- package/template/components/ui/sonner.tsx +25 -0
- package/template/components/ui/switch.tsx +31 -0
- package/template/components/ui/tabs.tsx +66 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/toggle-group.tsx +73 -0
- package/template/components/ui/toggle.tsx +47 -0
- package/template/components/ui/tooltip.tsx +61 -0
- package/template/components/user-profile.tsx +139 -0
- package/template/components.json +21 -0
- package/template/db/drizzle.ts +14 -0
- package/template/db/migrations/0000_worried_rawhide_kid.sql +77 -0
- package/template/db/migrations/meta/0000_snapshot.json +494 -0
- package/template/db/migrations/meta/_journal.json +13 -0
- package/template/db/schema.ts +85 -0
- package/template/drizzle.config.ts +13 -0
- package/template/emails/components/layout.tsx +181 -0
- package/template/emails/password-reset.tsx +67 -0
- package/template/emails/payment-failed.tsx +167 -0
- package/template/emails/subscription-confirmation.tsx +129 -0
- package/template/emails/welcome.tsx +100 -0
- package/template/eslint.config.mjs +16 -0
- package/template/hooks/use-mobile.ts +21 -0
- package/template/lib/auth-client.ts +8 -0
- package/template/lib/auth.ts +276 -0
- package/template/lib/email.ts +118 -0
- package/template/lib/polar-products.ts +49 -0
- package/template/lib/subscription.ts +148 -0
- package/template/lib/upload-image.ts +28 -0
- package/template/lib/utils.ts +6 -0
- package/template/middleware.ts +30 -0
- package/template/next-env.d.ts +5 -0
- package/template/next.config.ts +27 -0
- package/template/package.json +99 -0
- package/template/postcss.config.mjs +5 -0
- package/template/public/add.png +0 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/file.svg +1 -0
- package/template/public/globe.svg +1 -0
- package/template/public/iphone.png +0 -0
- package/template/public/logo.png +0 -0
- package/template/public/next.svg +1 -0
- package/template/public/polar-sh.svg +1 -0
- package/template/public/shadcn-ui.svg +1 -0
- package/template/public/site.webmanifest +21 -0
- package/template/public/vercel.svg +1 -0
- package/template/public/window.svg +1 -0
- package/template/tailwind.config.ts +89 -0
- package/template/template.config.json +138 -0
- package/template/tsconfig.json +27 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from "@/components/ui/card";
|
|
11
|
+
import { Input } from "@/components/ui/input";
|
|
12
|
+
import { Label } from "@/components/ui/label";
|
|
13
|
+
import { authClient } from "@/lib/auth-client";
|
|
14
|
+
import { cn } from "@/lib/utils";
|
|
15
|
+
import Link from "next/link";
|
|
16
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
17
|
+
import { Suspense, useState } from "react";
|
|
18
|
+
import { toast } from "sonner";
|
|
19
|
+
|
|
20
|
+
function SignInContent() {
|
|
21
|
+
const [loading, setLoading] = useState(false);
|
|
22
|
+
const [email, setEmail] = useState("");
|
|
23
|
+
const [password, setPassword] = useState("");
|
|
24
|
+
const searchParams = useSearchParams();
|
|
25
|
+
const returnTo = searchParams.get("returnTo");
|
|
26
|
+
const router = useRouter();
|
|
27
|
+
|
|
28
|
+
const handleEmailSignIn = async (e: React.FormEvent) => {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
setLoading(true);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const { error } = await authClient.signIn.email({
|
|
34
|
+
email,
|
|
35
|
+
password,
|
|
36
|
+
callbackURL: returnTo || "/dashboard",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (error) {
|
|
40
|
+
console.error("Sign-in error:", error);
|
|
41
|
+
|
|
42
|
+
// Provide helpful error messages
|
|
43
|
+
if (error.message?.includes("database") || error.message?.includes("connect")) {
|
|
44
|
+
toast.error("Database connection failed. Please configure DATABASE_URL in your .env.local file.", {
|
|
45
|
+
duration: 7000,
|
|
46
|
+
});
|
|
47
|
+
} else if (error.message?.includes("Invalid") || error.message?.includes("credentials")) {
|
|
48
|
+
toast.error("Invalid email or password. Please try again.");
|
|
49
|
+
} else {
|
|
50
|
+
toast.error(error.message || "Sign-in failed. Check your environment configuration.", {
|
|
51
|
+
duration: 5000,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
toast.success("Signed in successfully!");
|
|
56
|
+
router.push(returnTo || "/dashboard");
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error("Authentication error:", err);
|
|
60
|
+
toast.error("An unexpected error occurred. Check console for details.", {
|
|
61
|
+
duration: 5000,
|
|
62
|
+
});
|
|
63
|
+
} finally {
|
|
64
|
+
setLoading(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="flex flex-col justify-center items-center w-full h-screen bg-black">
|
|
70
|
+
<Card className="max-w-md w-full bg-[#0a0a0a] border-[#2a2a2a]">
|
|
71
|
+
<CardHeader>
|
|
72
|
+
<CardTitle className="text-lg md:text-xl text-white">
|
|
73
|
+
Welcome Back to {{PROJECT_NAME}}
|
|
74
|
+
</CardTitle>
|
|
75
|
+
<CardDescription className="text-xs md:text-sm text-gray-400">
|
|
76
|
+
Sign in to your account to continue
|
|
77
|
+
</CardDescription>
|
|
78
|
+
</CardHeader>
|
|
79
|
+
<CardContent>
|
|
80
|
+
<form onSubmit={handleEmailSignIn} className="grid gap-4">
|
|
81
|
+
<div className="grid gap-2">
|
|
82
|
+
<Label htmlFor="email" className="text-gray-300">Email</Label>
|
|
83
|
+
<Input
|
|
84
|
+
id="email"
|
|
85
|
+
type="email"
|
|
86
|
+
placeholder="you@example.com"
|
|
87
|
+
value={email}
|
|
88
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
89
|
+
required
|
|
90
|
+
disabled={loading}
|
|
91
|
+
className="bg-[#1a1a1a] border-[#2a2a2a] text-white placeholder:text-gray-500"
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
<div className="grid gap-2">
|
|
95
|
+
<Label htmlFor="password" className="text-gray-300">Password</Label>
|
|
96
|
+
<Input
|
|
97
|
+
id="password"
|
|
98
|
+
type="password"
|
|
99
|
+
placeholder="Enter your password"
|
|
100
|
+
value={password}
|
|
101
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
102
|
+
required
|
|
103
|
+
disabled={loading}
|
|
104
|
+
className="bg-[#1a1a1a] border-[#2a2a2a] text-white placeholder:text-gray-500"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
108
|
+
{loading ? "Signing in..." : "Sign In"}
|
|
109
|
+
</Button>
|
|
110
|
+
</form>
|
|
111
|
+
|
|
112
|
+
<div className="relative my-4">
|
|
113
|
+
<div className="absolute inset-0 flex items-center">
|
|
114
|
+
<span className="w-full border-t border-[#2a2a2a]" />
|
|
115
|
+
</div>
|
|
116
|
+
<div className="relative flex justify-center text-xs uppercase">
|
|
117
|
+
<span className="bg-[#0a0a0a] px-2 text-gray-500">Or continue with</span>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<Button
|
|
122
|
+
variant="outline"
|
|
123
|
+
className={cn("w-full gap-2 bg-[#1a1a1a] border-[#2a2a2a] text-white hover:bg-[#2a2a2a]")}
|
|
124
|
+
disabled={loading}
|
|
125
|
+
onClick={async () => {
|
|
126
|
+
try {
|
|
127
|
+
setLoading(true);
|
|
128
|
+
await authClient.signIn.social(
|
|
129
|
+
{
|
|
130
|
+
provider: "google",
|
|
131
|
+
callbackURL: returnTo || "/dashboard",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
onResponse: () => {
|
|
135
|
+
setLoading(false);
|
|
136
|
+
},
|
|
137
|
+
onError: (ctx) => {
|
|
138
|
+
setLoading(false);
|
|
139
|
+
console.error("Sign-in failed:", ctx.error);
|
|
140
|
+
|
|
141
|
+
if (ctx.error?.message?.includes("database") || ctx.error?.message?.includes("connect")) {
|
|
142
|
+
toast.error("Database connection failed. Please configure DATABASE_URL in your .env.local file.", {
|
|
143
|
+
duration: 7000,
|
|
144
|
+
});
|
|
145
|
+
} else {
|
|
146
|
+
toast.error("Google sign-in failed. Check your Google OAuth configuration.", {
|
|
147
|
+
duration: 5000,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
setLoading(false);
|
|
155
|
+
console.error("Authentication error:", error);
|
|
156
|
+
toast.error("Authentication error. Check console for details.", {
|
|
157
|
+
duration: 5000,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
<svg
|
|
163
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
164
|
+
width="0.98em"
|
|
165
|
+
height="1em"
|
|
166
|
+
viewBox="0 0 256 262"
|
|
167
|
+
>
|
|
168
|
+
<path
|
|
169
|
+
fill="#4285F4"
|
|
170
|
+
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
|
|
171
|
+
></path>
|
|
172
|
+
<path
|
|
173
|
+
fill="#34A853"
|
|
174
|
+
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
|
|
175
|
+
></path>
|
|
176
|
+
<path
|
|
177
|
+
fill="#FBBC05"
|
|
178
|
+
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
|
|
179
|
+
></path>
|
|
180
|
+
<path
|
|
181
|
+
fill="#EB4335"
|
|
182
|
+
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
|
|
183
|
+
></path>
|
|
184
|
+
</svg>
|
|
185
|
+
Continue with Google
|
|
186
|
+
</Button>
|
|
187
|
+
|
|
188
|
+
<p className="mt-4 text-center text-sm text-gray-500">
|
|
189
|
+
Don't have an account?{" "}
|
|
190
|
+
<Link href="/sign-up" className="text-[#ff5722] hover:underline">
|
|
191
|
+
Sign up
|
|
192
|
+
</Link>
|
|
193
|
+
</p>
|
|
194
|
+
</CardContent>
|
|
195
|
+
</Card>
|
|
196
|
+
<p className="mt-6 text-xs text-center text-gray-500 dark:text-gray-400 max-w-md">
|
|
197
|
+
By signing in, you agree to our{" "}
|
|
198
|
+
<Link
|
|
199
|
+
href="/terms-of-service"
|
|
200
|
+
className="underline hover:text-gray-700 dark:hover:text-gray-300"
|
|
201
|
+
>
|
|
202
|
+
Terms of Service
|
|
203
|
+
</Link>{" "}
|
|
204
|
+
and{" "}
|
|
205
|
+
<Link
|
|
206
|
+
href="/privacy-policy"
|
|
207
|
+
className="underline hover:text-gray-700 dark:hover:text-gray-300"
|
|
208
|
+
>
|
|
209
|
+
Privacy Policy
|
|
210
|
+
</Link>
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default function SignIn() {
|
|
217
|
+
return (
|
|
218
|
+
<Suspense
|
|
219
|
+
fallback={
|
|
220
|
+
<div className="flex flex-col justify-center items-center w-full h-screen">
|
|
221
|
+
<div className="max-w-md w-full bg-gray-200 dark:bg-gray-800 animate-pulse rounded-lg h-96"></div>
|
|
222
|
+
</div>
|
|
223
|
+
}
|
|
224
|
+
>
|
|
225
|
+
<SignInContent />
|
|
226
|
+
</Suspense>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from "@/components/ui/card";
|
|
11
|
+
import { Input } from "@/components/ui/input";
|
|
12
|
+
import { Label } from "@/components/ui/label";
|
|
13
|
+
import { authClient } from "@/lib/auth-client";
|
|
14
|
+
import { cn } from "@/lib/utils";
|
|
15
|
+
import Link from "next/link";
|
|
16
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
17
|
+
import { Suspense, useState } from "react";
|
|
18
|
+
import { toast } from "sonner";
|
|
19
|
+
|
|
20
|
+
function SignUpContent() {
|
|
21
|
+
const [loading, setLoading] = useState(false);
|
|
22
|
+
const [name, setName] = useState("");
|
|
23
|
+
const [email, setEmail] = useState("");
|
|
24
|
+
const [password, setPassword] = useState("");
|
|
25
|
+
const searchParams = useSearchParams();
|
|
26
|
+
const returnTo = searchParams.get("returnTo");
|
|
27
|
+
const router = useRouter();
|
|
28
|
+
|
|
29
|
+
const handleEmailSignUp = async (e: React.FormEvent) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
setLoading(true);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const { error } = await authClient.signUp.email({
|
|
35
|
+
email,
|
|
36
|
+
password,
|
|
37
|
+
name,
|
|
38
|
+
callbackURL: returnTo || "/dashboard",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (error) {
|
|
42
|
+
console.error("Sign-up error:", error);
|
|
43
|
+
|
|
44
|
+
if (error.message?.includes("database") || error.message?.includes("connect")) {
|
|
45
|
+
toast.error("Database connection failed. Please configure DATABASE_URL in your .env.local file.", {
|
|
46
|
+
duration: 7000,
|
|
47
|
+
});
|
|
48
|
+
} else if (error.message?.includes("already exists") || error.message?.includes("duplicate")) {
|
|
49
|
+
toast.error("An account with this email already exists. Please sign in instead.");
|
|
50
|
+
} else {
|
|
51
|
+
toast.error(error.message || "Sign-up failed. Check your environment configuration.", {
|
|
52
|
+
duration: 5000,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
toast.success("Account created successfully!");
|
|
57
|
+
router.push(returnTo || "/dashboard");
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error("Authentication error:", err);
|
|
61
|
+
toast.error("An unexpected error occurred. Check console for details.", {
|
|
62
|
+
duration: 5000,
|
|
63
|
+
});
|
|
64
|
+
} finally {
|
|
65
|
+
setLoading(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="flex flex-col justify-center items-center w-full h-screen bg-black">
|
|
71
|
+
<Card className="max-w-md w-full bg-[#0a0a0a] border-[#2a2a2a]">
|
|
72
|
+
<CardHeader>
|
|
73
|
+
<CardTitle className="text-lg md:text-xl text-white">
|
|
74
|
+
Create Your {{PROJECT_NAME}} Account
|
|
75
|
+
</CardTitle>
|
|
76
|
+
<CardDescription className="text-xs md:text-sm text-gray-400">
|
|
77
|
+
Get started in seconds
|
|
78
|
+
</CardDescription>
|
|
79
|
+
</CardHeader>
|
|
80
|
+
<CardContent>
|
|
81
|
+
<form onSubmit={handleEmailSignUp} className="grid gap-4">
|
|
82
|
+
<div className="grid gap-2">
|
|
83
|
+
<Label htmlFor="name" className="text-gray-300">Name</Label>
|
|
84
|
+
<Input
|
|
85
|
+
id="name"
|
|
86
|
+
type="text"
|
|
87
|
+
placeholder="John Doe"
|
|
88
|
+
value={name}
|
|
89
|
+
onChange={(e) => setName(e.target.value)}
|
|
90
|
+
required
|
|
91
|
+
disabled={loading}
|
|
92
|
+
className="bg-[#1a1a1a] border-[#2a2a2a] text-white placeholder:text-gray-500"
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
<div className="grid gap-2">
|
|
96
|
+
<Label htmlFor="email" className="text-gray-300">Email</Label>
|
|
97
|
+
<Input
|
|
98
|
+
id="email"
|
|
99
|
+
type="email"
|
|
100
|
+
placeholder="you@example.com"
|
|
101
|
+
value={email}
|
|
102
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
103
|
+
required
|
|
104
|
+
disabled={loading}
|
|
105
|
+
className="bg-[#1a1a1a] border-[#2a2a2a] text-white placeholder:text-gray-500"
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
<div className="grid gap-2">
|
|
109
|
+
<Label htmlFor="password" className="text-gray-300">Password</Label>
|
|
110
|
+
<Input
|
|
111
|
+
id="password"
|
|
112
|
+
type="password"
|
|
113
|
+
placeholder="Create a strong password"
|
|
114
|
+
value={password}
|
|
115
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
116
|
+
required
|
|
117
|
+
disabled={loading}
|
|
118
|
+
minLength={8}
|
|
119
|
+
className="bg-[#1a1a1a] border-[#2a2a2a] text-white placeholder:text-gray-500"
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
123
|
+
{loading ? "Creating account..." : "Create Account"}
|
|
124
|
+
</Button>
|
|
125
|
+
</form>
|
|
126
|
+
|
|
127
|
+
<div className="relative my-4">
|
|
128
|
+
<div className="absolute inset-0 flex items-center">
|
|
129
|
+
<span className="w-full border-t border-[#2a2a2a]" />
|
|
130
|
+
</div>
|
|
131
|
+
<div className="relative flex justify-center text-xs uppercase">
|
|
132
|
+
<span className="bg-[#0a0a0a] px-2 text-gray-500">Or continue with</span>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<Button
|
|
137
|
+
variant="outline"
|
|
138
|
+
className={cn("w-full gap-2 bg-[#1a1a1a] border-[#2a2a2a] text-white hover:bg-[#2a2a2a]")}
|
|
139
|
+
disabled={loading}
|
|
140
|
+
onClick={async () => {
|
|
141
|
+
try {
|
|
142
|
+
setLoading(true);
|
|
143
|
+
await authClient.signIn.social(
|
|
144
|
+
{
|
|
145
|
+
provider: "google",
|
|
146
|
+
callbackURL: returnTo || "/dashboard",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
onResponse: () => {
|
|
150
|
+
setLoading(false);
|
|
151
|
+
},
|
|
152
|
+
onError: (ctx) => {
|
|
153
|
+
setLoading(false);
|
|
154
|
+
console.error("Sign-up failed:", ctx.error);
|
|
155
|
+
|
|
156
|
+
if (ctx.error?.message?.includes("database") || ctx.error?.message?.includes("connect")) {
|
|
157
|
+
toast.error("Database connection failed. Please configure DATABASE_URL in your .env.local file.", {
|
|
158
|
+
duration: 7000,
|
|
159
|
+
});
|
|
160
|
+
} else {
|
|
161
|
+
toast.error("Google sign-up failed. Check your Google OAuth configuration.", {
|
|
162
|
+
duration: 5000,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
setLoading(false);
|
|
170
|
+
console.error("Authentication error:", error);
|
|
171
|
+
toast.error("Authentication error. Check console for details.", {
|
|
172
|
+
duration: 5000,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<svg
|
|
178
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
179
|
+
width="0.98em"
|
|
180
|
+
height="1em"
|
|
181
|
+
viewBox="0 0 256 262"
|
|
182
|
+
>
|
|
183
|
+
<path
|
|
184
|
+
fill="#4285F4"
|
|
185
|
+
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
|
|
186
|
+
></path>
|
|
187
|
+
<path
|
|
188
|
+
fill="#34A853"
|
|
189
|
+
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
|
|
190
|
+
></path>
|
|
191
|
+
<path
|
|
192
|
+
fill="#FBBC05"
|
|
193
|
+
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
|
|
194
|
+
></path>
|
|
195
|
+
<path
|
|
196
|
+
fill="#EB4335"
|
|
197
|
+
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
|
|
198
|
+
></path>
|
|
199
|
+
</svg>
|
|
200
|
+
Continue with Google
|
|
201
|
+
</Button>
|
|
202
|
+
|
|
203
|
+
<p className="mt-4 text-center text-sm text-gray-500">
|
|
204
|
+
Already have an account?{" "}
|
|
205
|
+
<Link href="/sign-in" className="text-[#ff5722] hover:underline">
|
|
206
|
+
Sign in
|
|
207
|
+
</Link>
|
|
208
|
+
</p>
|
|
209
|
+
</CardContent>
|
|
210
|
+
</Card>
|
|
211
|
+
<p className="mt-6 text-xs text-center text-gray-500 max-w-md">
|
|
212
|
+
By signing up, you agree to our{" "}
|
|
213
|
+
<Link
|
|
214
|
+
href="/terms-of-service"
|
|
215
|
+
className="underline hover:text-gray-400"
|
|
216
|
+
>
|
|
217
|
+
Terms of Service
|
|
218
|
+
</Link>{" "}
|
|
219
|
+
and{" "}
|
|
220
|
+
<Link
|
|
221
|
+
href="/privacy-policy"
|
|
222
|
+
className="underline hover:text-gray-400"
|
|
223
|
+
>
|
|
224
|
+
Privacy Policy
|
|
225
|
+
</Link>
|
|
226
|
+
</p>
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export default function SignUp() {
|
|
232
|
+
return (
|
|
233
|
+
<Suspense
|
|
234
|
+
fallback={
|
|
235
|
+
<div className="flex flex-col justify-center items-center w-full h-screen bg-black">
|
|
236
|
+
<div className="max-w-md w-full bg-[#0a0a0a] animate-pulse rounded-lg h-96"></div>
|
|
237
|
+
</div>
|
|
238
|
+
}
|
|
239
|
+
>
|
|
240
|
+
<SignUpContent />
|
|
241
|
+
</Suspense>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MetadataRoute } from 'next'
|
|
2
|
+
|
|
3
|
+
export default function sitemap(): MetadataRoute.Sitemap {
|
|
4
|
+
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
|
|
5
|
+
|
|
6
|
+
// Blog posts
|
|
7
|
+
const blogPosts = [
|
|
8
|
+
{
|
|
9
|
+
slug: 'getting-started-with-saas',
|
|
10
|
+
date: '2024-12-15',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
slug: 'optimizing-conversion-rates',
|
|
14
|
+
date: '2024-12-10',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
slug: 'scaling-your-saas',
|
|
18
|
+
date: '2024-12-05',
|
|
19
|
+
},
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const blogRoutes = blogPosts.map((post) => ({
|
|
23
|
+
url: `${baseUrl}/blog/${post.slug}`,
|
|
24
|
+
lastModified: new Date(post.date),
|
|
25
|
+
changeFrequency: 'monthly' as const,
|
|
26
|
+
priority: 0.7,
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
url: baseUrl,
|
|
32
|
+
lastModified: new Date(),
|
|
33
|
+
changeFrequency: 'weekly',
|
|
34
|
+
priority: 1,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
url: `${baseUrl}/blog`,
|
|
38
|
+
lastModified: new Date(),
|
|
39
|
+
changeFrequency: 'weekly',
|
|
40
|
+
priority: 0.9,
|
|
41
|
+
},
|
|
42
|
+
...blogRoutes,
|
|
43
|
+
{
|
|
44
|
+
url: `${baseUrl}/pricing`,
|
|
45
|
+
lastModified: new Date(),
|
|
46
|
+
changeFrequency: 'monthly',
|
|
47
|
+
priority: 0.8,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
url: `${baseUrl}/terms-of-service`,
|
|
51
|
+
lastModified: new Date(),
|
|
52
|
+
changeFrequency: 'yearly',
|
|
53
|
+
priority: 0.3,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
url: `${baseUrl}/privacy-policy`,
|
|
57
|
+
lastModified: new Date(),
|
|
58
|
+
changeFrequency: 'yearly',
|
|
59
|
+
priority: 0.3,
|
|
60
|
+
},
|
|
61
|
+
]
|
|
62
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
5
|
+
import { CheckCircle2, Terminal, BookOpen, Zap } from "lucide-react";
|
|
6
|
+
import { useRouter } from "next/navigation";
|
|
7
|
+
import Link from "next/link";
|
|
8
|
+
|
|
9
|
+
export default function SuccessPage() {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="min-h-screen bg-black flex items-center justify-center p-6">
|
|
14
|
+
<Card className="bg-[#0a0a0a] border-[#2a2a2a] max-w-2xl w-full">
|
|
15
|
+
<CardContent className="p-12">
|
|
16
|
+
{/* Success Icon */}
|
|
17
|
+
<div className="w-20 h-20 bg-green-500/10 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
18
|
+
<CheckCircle2 className="w-12 h-12 text-green-400" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
{/* Success Message */}
|
|
22
|
+
<div className="text-center mb-8">
|
|
23
|
+
<h1 className="text-3xl font-bold text-white mb-3">
|
|
24
|
+
Welcome! 🎉
|
|
25
|
+
</h1>
|
|
26
|
+
<p className="text-gray-400 text-lg">
|
|
27
|
+
Your subscription is now active. Let's get you started building your SaaS.
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
{/* Next Steps */}
|
|
32
|
+
<div className="space-y-6 mb-8">
|
|
33
|
+
<h2 className="text-xl font-semibold text-white">Next Steps</h2>
|
|
34
|
+
|
|
35
|
+
{/* Step 1 */}
|
|
36
|
+
<div className="flex gap-4">
|
|
37
|
+
<div className="w-8 h-8 rounded-full bg-[#ff5722] flex items-center justify-center text-white text-sm font-semibold flex-shrink-0">
|
|
38
|
+
1
|
|
39
|
+
</div>
|
|
40
|
+
<div className="flex-1">
|
|
41
|
+
<h3 className="text-white font-semibold mb-1">Authenticate the CLI</h3>
|
|
42
|
+
<p className="text-gray-400 text-sm mb-2">
|
|
43
|
+
Run this command in your terminal to authenticate:
|
|
44
|
+
</p>
|
|
45
|
+
<div className="bg-black/70 p-3 rounded-lg border border-[#2a2a2a]">
|
|
46
|
+
<code className="text-[#ff5722] font-mono text-sm">
|
|
47
|
+
npx your-cli login
|
|
48
|
+
</code>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{/* Step 2 */}
|
|
54
|
+
<div className="flex gap-4">
|
|
55
|
+
<div className="w-8 h-8 rounded-full bg-[#ff5722] flex items-center justify-center text-white text-sm font-semibold flex-shrink-0">
|
|
56
|
+
2
|
|
57
|
+
</div>
|
|
58
|
+
<div className="flex-1">
|
|
59
|
+
<h3 className="text-white font-semibold mb-1">Create your first project</h3>
|
|
60
|
+
<p className="text-gray-400 text-sm mb-2">
|
|
61
|
+
Generate a complete SaaS application:
|
|
62
|
+
</p>
|
|
63
|
+
<div className="bg-black/70 p-3 rounded-lg border border-[#2a2a2a]">
|
|
64
|
+
<code className="text-[#ff5722] font-mono text-sm">
|
|
65
|
+
npx your-cli init my-app
|
|
66
|
+
</code>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{/* Step 3 */}
|
|
72
|
+
<div className="flex gap-4">
|
|
73
|
+
<div className="w-8 h-8 rounded-full bg-[#ff5722] flex items-center justify-center text-white text-sm font-semibold flex-shrink-0">
|
|
74
|
+
3
|
|
75
|
+
</div>
|
|
76
|
+
<div className="flex-1">
|
|
77
|
+
<h3 className="text-white font-semibold mb-1">Start building</h3>
|
|
78
|
+
<p className="text-gray-400 text-sm">
|
|
79
|
+
Your project includes authentication, billing, dashboard, and more - all ready to customize and deploy.
|
|
80
|
+
</p>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{/* Action Buttons */}
|
|
86
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
|
87
|
+
<Button
|
|
88
|
+
onClick={() => router.push("/dashboard")}
|
|
89
|
+
className="w-full bg-[#ff5722] hover:bg-[#d84315]"
|
|
90
|
+
>
|
|
91
|
+
<Zap className="w-4 h-4 mr-2" />
|
|
92
|
+
Go to Dashboard
|
|
93
|
+
</Button>
|
|
94
|
+
<Link href="/dashboard/cli" className="w-full">
|
|
95
|
+
<Button variant="outline" className="w-full border-[#2a2a2a] hover:bg-[#1a1a1a]">
|
|
96
|
+
<Terminal className="w-4 h-4 mr-2" />
|
|
97
|
+
CLI Guide
|
|
98
|
+
</Button>
|
|
99
|
+
</Link>
|
|
100
|
+
<Link href="/docs" className="w-full">
|
|
101
|
+
<Button variant="outline" className="w-full border-[#2a2a2a] hover:bg-[#1a1a1a]">
|
|
102
|
+
<BookOpen className="w-4 h-4 mr-2" />
|
|
103
|
+
Documentation
|
|
104
|
+
</Button>
|
|
105
|
+
</Link>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
{/* Help Section */}
|
|
109
|
+
<div className="mt-8 pt-8 border-t border-[#2a2a2a] text-center">
|
|
110
|
+
<p className="text-gray-400 text-sm mb-2">
|
|
111
|
+
Need help getting started?
|
|
112
|
+
</p>
|
|
113
|
+
<Link href="mailto:support@yourdomain.com">
|
|
114
|
+
<Button variant="link" className="text-[#ff5722] hover:text-[#d84315]">
|
|
115
|
+
Contact Support
|
|
116
|
+
</Button>
|
|
117
|
+
</Link>
|
|
118
|
+
</div>
|
|
119
|
+
</CardContent>
|
|
120
|
+
</Card>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|