shipd 0.1.2 → 0.1.4
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/base-package/app/globals.css +126 -0
- package/base-package/app/layout.tsx +53 -0
- package/base-package/app/page.tsx +15 -0
- package/base-package/base.config.json +57 -0
- package/base-package/components/ui/avatar.tsx +53 -0
- package/base-package/components/ui/badge.tsx +46 -0
- package/base-package/components/ui/button.tsx +59 -0
- package/base-package/components/ui/card.tsx +92 -0
- package/base-package/components/ui/chart.tsx +353 -0
- package/base-package/components/ui/checkbox.tsx +32 -0
- package/base-package/components/ui/dialog.tsx +135 -0
- package/base-package/components/ui/dropdown-menu.tsx +257 -0
- package/base-package/components/ui/form.tsx +167 -0
- package/base-package/components/ui/input.tsx +21 -0
- package/base-package/components/ui/label.tsx +24 -0
- package/base-package/components/ui/progress.tsx +31 -0
- package/base-package/components/ui/resizable.tsx +56 -0
- package/base-package/components/ui/select.tsx +185 -0
- package/base-package/components/ui/separator.tsx +28 -0
- package/base-package/components/ui/sheet.tsx +139 -0
- package/base-package/components/ui/skeleton.tsx +13 -0
- package/base-package/components/ui/sonner.tsx +25 -0
- package/base-package/components/ui/switch.tsx +31 -0
- package/base-package/components/ui/tabs.tsx +66 -0
- package/base-package/components/ui/textarea.tsx +18 -0
- package/base-package/components/ui/toggle-group.tsx +73 -0
- package/base-package/components/ui/toggle.tsx +47 -0
- package/base-package/components/ui/tooltip.tsx +61 -0
- package/base-package/components.json +21 -0
- package/base-package/eslint.config.mjs +16 -0
- package/base-package/lib/utils.ts +6 -0
- package/base-package/middleware.ts +12 -0
- package/base-package/next.config.ts +27 -0
- package/base-package/package.json +49 -0
- package/base-package/postcss.config.mjs +5 -0
- package/base-package/public/favicon.svg +4 -0
- package/base-package/tailwind.config.ts +89 -0
- package/base-package/tsconfig.json +27 -0
- package/dist/index.js +1862 -948
- package/features/ai-chat/README.md +258 -0
- package/features/ai-chat/app/api/chat/route.ts +16 -0
- package/features/ai-chat/app/dashboard/_components/chatbot.tsx +39 -0
- package/features/ai-chat/app/dashboard/chat/page.tsx +73 -0
- package/features/ai-chat/feature.config.json +22 -0
- package/features/analytics/README.md +308 -0
- package/features/analytics/feature.config.json +20 -0
- package/features/analytics/lib/posthog.ts +36 -0
- package/features/auth/README.md +336 -0
- package/features/auth/app/api/auth/[...all]/route.ts +4 -0
- package/features/auth/app/dashboard/layout.tsx +15 -0
- package/features/auth/app/dashboard/page.tsx +140 -0
- package/features/auth/app/sign-in/page.tsx +228 -0
- package/features/auth/app/sign-up/page.tsx +243 -0
- package/features/auth/auth-schema.ts +47 -0
- package/features/auth/components/auth/setup-instructions.tsx +123 -0
- package/features/auth/feature.config.json +33 -0
- package/features/auth/lib/auth-client.ts +8 -0
- package/features/auth/lib/auth.ts +295 -0
- package/features/auth/lib/email-stub.ts +55 -0
- package/features/auth/lib/email.ts +47 -0
- package/features/auth/middleware.patch.ts +43 -0
- package/features/database/README.md +256 -0
- package/features/database/db/drizzle.ts +48 -0
- package/features/database/db/schema.ts +21 -0
- package/features/database/drizzle.config.ts +13 -0
- package/features/database/feature.config.json +30 -0
- package/features/email/README.md +282 -0
- package/features/email/emails/components/layout.tsx +181 -0
- package/features/email/emails/password-reset.tsx +67 -0
- package/features/email/emails/payment-failed.tsx +167 -0
- package/features/email/emails/subscription-confirmation.tsx +129 -0
- package/features/email/emails/welcome.tsx +100 -0
- package/features/email/feature.config.json +22 -0
- package/features/email/lib/email.ts +118 -0
- package/features/file-upload/README.md +271 -0
- package/features/file-upload/app/api/upload-image/route.ts +64 -0
- package/features/file-upload/app/dashboard/upload/page.tsx +324 -0
- package/features/file-upload/feature.config.json +23 -0
- package/features/file-upload/lib/upload-image.ts +28 -0
- package/features/marketing-landing/README.md +266 -0
- package/features/marketing-landing/app/page.tsx +25 -0
- package/features/marketing-landing/components/homepage/cli-workflow-section.tsx +231 -0
- package/features/marketing-landing/components/homepage/features-section.tsx +152 -0
- package/features/marketing-landing/components/homepage/footer.tsx +53 -0
- package/features/marketing-landing/components/homepage/hero-section.tsx +112 -0
- package/features/marketing-landing/components/homepage/integrations.tsx +124 -0
- package/features/marketing-landing/components/homepage/navigation.tsx +116 -0
- package/features/marketing-landing/components/homepage/news-section.tsx +82 -0
- package/features/marketing-landing/components/homepage/pricing-section.tsx +98 -0
- package/features/marketing-landing/components/homepage/testimonials-section.tsx +34 -0
- package/features/marketing-landing/components/logos/BetterAuth.tsx +21 -0
- package/features/marketing-landing/components/logos/NeonPostgres.tsx +41 -0
- package/features/marketing-landing/components/logos/Nextjs.tsx +72 -0
- package/features/marketing-landing/components/logos/Polar.tsx +7 -0
- package/features/marketing-landing/components/logos/TailwindCSS.tsx +27 -0
- package/features/marketing-landing/components/logos/index.ts +6 -0
- package/features/marketing-landing/components/logos/shadcnui.tsx +8 -0
- package/features/marketing-landing/feature.config.json +23 -0
- package/features/payments/README.md +306 -0
- package/features/payments/app/api/subscription/route.ts +25 -0
- package/features/payments/app/dashboard/payment/_components/manage-subscription.tsx +22 -0
- package/features/payments/app/dashboard/payment/page.tsx +126 -0
- package/features/payments/app/success/page.tsx +123 -0
- package/features/payments/feature.config.json +31 -0
- package/features/payments/lib/polar-products.ts +49 -0
- package/features/payments/lib/subscription.ts +148 -0
- package/features/payments/payments-schema.ts +30 -0
- package/features/seo/README.md +244 -0
- package/features/seo/app/blog/[slug]/page.tsx +314 -0
- package/features/seo/app/blog/page.tsx +107 -0
- package/features/seo/app/robots.txt +13 -0
- package/features/seo/app/sitemap.ts +70 -0
- package/features/seo/feature.config.json +19 -0
- package/features/seo/lib/seo-utils.ts +163 -0
- package/package.json +3 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardFooter,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from "@/components/ui/card";
|
|
13
|
+
import { Check } from "lucide-react";
|
|
14
|
+
|
|
15
|
+
const plans = [
|
|
16
|
+
{
|
|
17
|
+
name: "Starter",
|
|
18
|
+
price: "$29",
|
|
19
|
+
interval: "month",
|
|
20
|
+
description: "Perfect to kick off your SaaS",
|
|
21
|
+
highlights: ["All core pages", "UI components", "Auth-ready"],
|
|
22
|
+
cta: "Get Starter",
|
|
23
|
+
featured: false,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "Pro",
|
|
27
|
+
price: "$99",
|
|
28
|
+
interval: "month",
|
|
29
|
+
description: "For teams shipping faster",
|
|
30
|
+
highlights: ["Everything in Starter", "Dashboard layout", "CLI + append"],
|
|
31
|
+
cta: "Go Pro",
|
|
32
|
+
featured: true,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export default function PricingSection() {
|
|
37
|
+
return (
|
|
38
|
+
<section id="pricing" className="bg-black px-4 pb-24">
|
|
39
|
+
<div className="mx-auto max-w-5xl text-center">
|
|
40
|
+
<div className="mb-12 space-y-4">
|
|
41
|
+
<h2 className="text-4xl font-semibold bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
|
|
42
|
+
Simple, transparent pricing
|
|
43
|
+
</h2>
|
|
44
|
+
<p className="text-gray-400 text-lg">
|
|
45
|
+
Choose a plan that fits your launch. Swap later as you grow.
|
|
46
|
+
</p>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
50
|
+
{plans.map((plan) => (
|
|
51
|
+
<Card
|
|
52
|
+
key={plan.name}
|
|
53
|
+
className={`bg-[#0a0a0a] border-[#2a2a2a] hover:border-[#ff5722]/30 transition-all duration-300 ${
|
|
54
|
+
plan.featured ? "shadow-lg shadow-[#ff5722]/10" : ""
|
|
55
|
+
}`}
|
|
56
|
+
>
|
|
57
|
+
<CardHeader className="space-y-2">
|
|
58
|
+
<div className="flex items-center justify-between">
|
|
59
|
+
<CardTitle className="text-2xl text-white">{plan.name}</CardTitle>
|
|
60
|
+
{plan.featured && (
|
|
61
|
+
<Badge
|
|
62
|
+
variant="secondary"
|
|
63
|
+
className="bg-[#ff5722]/20 text-[#ff5722] border border-[#ff5722]/30"
|
|
64
|
+
>
|
|
65
|
+
Popular
|
|
66
|
+
</Badge>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
<CardDescription className="text-gray-400">
|
|
70
|
+
{plan.description}
|
|
71
|
+
</CardDescription>
|
|
72
|
+
<div className="mt-2">
|
|
73
|
+
<span className="text-4xl font-bold bg-gradient-to-r from-[#ff5722] to-[#ff7043] bg-clip-text text-transparent">
|
|
74
|
+
{plan.price}
|
|
75
|
+
</span>
|
|
76
|
+
<span className="text-gray-400">/{plan.interval}</span>
|
|
77
|
+
</div>
|
|
78
|
+
</CardHeader>
|
|
79
|
+
<CardContent className="space-y-3">
|
|
80
|
+
{plan.highlights.map((item) => (
|
|
81
|
+
<div key={item} className="flex items-center gap-3">
|
|
82
|
+
<Check className="h-5 w-5 text-[#ff5722]" />
|
|
83
|
+
<span className="text-gray-300">{item}</span>
|
|
84
|
+
</div>
|
|
85
|
+
))}
|
|
86
|
+
</CardContent>
|
|
87
|
+
<CardFooter>
|
|
88
|
+
<Button className="w-full" variant={plan.featured ? "default" : "outline"}>
|
|
89
|
+
{plan.cta}
|
|
90
|
+
</Button>
|
|
91
|
+
</CardFooter>
|
|
92
|
+
</Card>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</section>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export default function TestimonialsSection() {
|
|
2
|
+
return (
|
|
3
|
+
<section className="py-20 px-6 lg:px-0 bg-gradient-to-b from-black via-[#0a0a0a] to-black">
|
|
4
|
+
<div className="mx-auto max-w-6xl">
|
|
5
|
+
<div className="text-center mb-16">
|
|
6
|
+
<h2 className="text-4xl font-bold mb-4 bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
|
|
7
|
+
Built for Speed
|
|
8
|
+
</h2>
|
|
9
|
+
<p className="text-gray-400 text-xl max-w-2xl mx-auto">
|
|
10
|
+
Everything you need to launch your SaaS in minutes, not weeks
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
{/* Stats */}
|
|
15
|
+
<div className="flex flex-wrap items-center justify-center gap-8 text-center">
|
|
16
|
+
<div>
|
|
17
|
+
<p className="text-4xl font-bold bg-gradient-to-r from-[#ff5722] to-[#ff7043] bg-clip-text text-transparent">10+</p>
|
|
18
|
+
<p className="text-gray-400 mt-1">Pre-built Integrations</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div className="h-12 w-px bg-[#2a2a2a] hidden sm:block" />
|
|
21
|
+
<div>
|
|
22
|
+
<p className="text-4xl font-bold bg-gradient-to-r from-[#ff5722] to-[#ff7043] bg-clip-text text-transparent">24+</p>
|
|
23
|
+
<p className="text-gray-400 mt-1">UI Components</p>
|
|
24
|
+
</div>
|
|
25
|
+
<div className="h-12 w-px bg-[#2a2a2a] hidden sm:block" />
|
|
26
|
+
<div>
|
|
27
|
+
<p className="text-4xl font-bold bg-gradient-to-r from-[#ff5722] to-[#ff7043] bg-clip-text text-transparent">5 Min</p>
|
|
28
|
+
<p className="text-gray-400 mt-1">Setup Time</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</section>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { SVGProps } from "react";
|
|
3
|
+
const BetterAuth = (props: SVGProps<SVGSVGElement>) => (
|
|
4
|
+
<svg
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
fill="none"
|
|
7
|
+
viewBox="0 0 500 500"
|
|
8
|
+
width="1em"
|
|
9
|
+
height="1em"
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
<path fill="#fff" d="M0 0h500v500H0z" />
|
|
13
|
+
<path fill="#000" d="M69 121h86.988v259H69zM337.575 121H430v259h-92.425z" />
|
|
14
|
+
<path
|
|
15
|
+
fill="#000"
|
|
16
|
+
d="M427.282 121v83.456h-174.52V121zM430 296.544V380H252.762v-83.456z"
|
|
17
|
+
/>
|
|
18
|
+
<path fill="#000" d="M252.762 204.455v92.089h-96.774v-92.089z" />
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
export default BetterAuth;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { SVGProps } from "react";
|
|
3
|
+
const Neon = (props: SVGProps<SVGSVGElement>) => (
|
|
4
|
+
<svg
|
|
5
|
+
viewBox="0 0 256 256"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
width="1em"
|
|
8
|
+
height="1em"
|
|
9
|
+
preserveAspectRatio="xMidYMid"
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
<defs>
|
|
13
|
+
<linearGradient id="a" x1="100%" x2="12.069%" y1="100%" y2="0%">
|
|
14
|
+
<stop offset="0%" stopColor="#62F755" />
|
|
15
|
+
<stop offset="100%" stopColor="#8FF986" stopOpacity={0} />
|
|
16
|
+
</linearGradient>
|
|
17
|
+
<linearGradient id="b" x1="100%" x2="40.603%" y1="100%" y2="76.897%">
|
|
18
|
+
<stop offset="0%" stopOpacity={0.9} />
|
|
19
|
+
<stop offset="100%" stopColor="#1A1A1A" stopOpacity={0} />
|
|
20
|
+
</linearGradient>
|
|
21
|
+
</defs>
|
|
22
|
+
<path
|
|
23
|
+
fill="#00E0D9"
|
|
24
|
+
d="M0 44.139C0 19.762 19.762 0 44.139 0H211.86C236.238 0 256 19.762 256 44.139v142.649c0 25.216-31.915 36.16-47.388 16.256l-48.392-62.251v75.484c0 21.939-17.784 39.723-39.722 39.723h-76.36C19.763 256 0 236.238 0 211.861V44.14Zm44.139-8.825c-4.879 0-8.825 3.946-8.825 8.818v167.73c0 4.878 3.946 8.831 8.818 8.831h77.688c2.44 0 3.087-1.977 3.087-4.416v-101.22c0-25.222 31.914-36.166 47.395-16.255l48.391 62.243V44.14c0-4.879.455-8.825-4.416-8.825H44.14Z"
|
|
25
|
+
/>
|
|
26
|
+
<path
|
|
27
|
+
fill="url(#a)"
|
|
28
|
+
d="M0 44.139C0 19.762 19.762 0 44.139 0H211.86C236.238 0 256 19.762 256 44.139v142.649c0 25.216-31.915 36.16-47.388 16.256l-48.392-62.251v75.484c0 21.939-17.784 39.723-39.722 39.723h-76.36C19.763 256 0 236.238 0 211.861V44.14Zm44.139-8.825c-4.879 0-8.825 3.946-8.825 8.818v167.73c0 4.878 3.946 8.831 8.818 8.831h77.688c2.44 0 3.087-1.977 3.087-4.416v-101.22c0-25.222 31.914-36.166 47.395-16.255l48.391 62.243V44.14c0-4.879.455-8.825-4.416-8.825H44.14Z"
|
|
29
|
+
/>
|
|
30
|
+
<path
|
|
31
|
+
fill="url(#b)"
|
|
32
|
+
fillOpacity={0.4}
|
|
33
|
+
d="M0 44.139C0 19.762 19.762 0 44.139 0H211.86C236.238 0 256 19.762 256 44.139v142.649c0 25.216-31.915 36.16-47.388 16.256l-48.392-62.251v75.484c0 21.939-17.784 39.723-39.722 39.723h-76.36C19.763 256 0 236.238 0 211.861V44.14Zm44.139-8.825c-4.879 0-8.825 3.946-8.825 8.818v167.73c0 4.878 3.946 8.831 8.818 8.831h77.688c2.44 0 3.087-1.977 3.087-4.416v-101.22c0-25.222 31.914-36.166 47.395-16.255l48.391 62.243V44.14c0-4.879.455-8.825-4.416-8.825H44.14Z"
|
|
34
|
+
/>
|
|
35
|
+
<path
|
|
36
|
+
fill="#63F655"
|
|
37
|
+
d="M211.861 0C236.238 0 256 19.762 256 44.139v142.649c0 25.216-31.915 36.16-47.388 16.256l-48.392-62.251v75.484c0 21.939-17.784 39.723-39.722 39.723a4.409 4.409 0 0 0 4.409-4.409V115.058c0-25.223 31.914-36.167 47.395-16.256l48.391 62.243V8.825c0-4.871-3.953-8.825-8.832-8.825Z"
|
|
38
|
+
/>
|
|
39
|
+
</svg>
|
|
40
|
+
);
|
|
41
|
+
export default Neon;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { SVGProps } from "react";
|
|
3
|
+
const Nextjs = (props: SVGProps<SVGSVGElement>) => (
|
|
4
|
+
<svg
|
|
5
|
+
width="1em"
|
|
6
|
+
height="1em"
|
|
7
|
+
viewBox="0 0 180 180"
|
|
8
|
+
fill="none"
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
<mask
|
|
13
|
+
id="mask0_408_139"
|
|
14
|
+
style={{
|
|
15
|
+
maskType: "alpha",
|
|
16
|
+
}}
|
|
17
|
+
maskUnits="userSpaceOnUse"
|
|
18
|
+
x={0}
|
|
19
|
+
y={0}
|
|
20
|
+
width={180}
|
|
21
|
+
height={180}
|
|
22
|
+
>
|
|
23
|
+
<circle cx={90} cy={90} r={90} fill="black" />
|
|
24
|
+
</mask>
|
|
25
|
+
<g mask="url(#mask0_408_139)">
|
|
26
|
+
<circle
|
|
27
|
+
cx={90}
|
|
28
|
+
cy={90}
|
|
29
|
+
r={87}
|
|
30
|
+
fill="black"
|
|
31
|
+
stroke="white"
|
|
32
|
+
strokeWidth={6}
|
|
33
|
+
/>
|
|
34
|
+
<path
|
|
35
|
+
d="M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z"
|
|
36
|
+
fill="url(#paint0_linear_408_139)"
|
|
37
|
+
/>
|
|
38
|
+
<rect
|
|
39
|
+
x={115}
|
|
40
|
+
y={54}
|
|
41
|
+
width={12}
|
|
42
|
+
height={72}
|
|
43
|
+
fill="url(#paint1_linear_408_139)"
|
|
44
|
+
/>
|
|
45
|
+
</g>
|
|
46
|
+
<defs>
|
|
47
|
+
<linearGradient
|
|
48
|
+
id="paint0_linear_408_139"
|
|
49
|
+
x1={109}
|
|
50
|
+
y1={116.5}
|
|
51
|
+
x2={144.5}
|
|
52
|
+
y2={160.5}
|
|
53
|
+
gradientUnits="userSpaceOnUse"
|
|
54
|
+
>
|
|
55
|
+
<stop stopColor="white" />
|
|
56
|
+
<stop offset={1} stopColor="white" stopOpacity={0} />
|
|
57
|
+
</linearGradient>
|
|
58
|
+
<linearGradient
|
|
59
|
+
id="paint1_linear_408_139"
|
|
60
|
+
x1={121}
|
|
61
|
+
y1={54}
|
|
62
|
+
x2={120.799}
|
|
63
|
+
y2={106.875}
|
|
64
|
+
gradientUnits="userSpaceOnUse"
|
|
65
|
+
>
|
|
66
|
+
<stop stopColor="white" />
|
|
67
|
+
<stop offset={1} stopColor="white" stopOpacity={0} />
|
|
68
|
+
</linearGradient>
|
|
69
|
+
</defs>
|
|
70
|
+
</svg>
|
|
71
|
+
);
|
|
72
|
+
export default Nextjs;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { SVGProps } from "react";
|
|
3
|
+
const TailwindCSS = (props: SVGProps<SVGSVGElement>) => (
|
|
4
|
+
<svg
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
fill="none"
|
|
7
|
+
viewBox="0 0 54 33"
|
|
8
|
+
width="1em"
|
|
9
|
+
height="1em"
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
<g clipPath="url(#a)">
|
|
13
|
+
<path
|
|
14
|
+
fill="#38bdf8"
|
|
15
|
+
fillRule="evenodd"
|
|
16
|
+
d="M27 0c-7.2 0-11.7 3.6-13.5 10.8 2.7-3.6 5.85-4.95 9.45-4.05 2.054.513 3.522 2.004 5.147 3.653C30.744 13.09 33.808 16.2 40.5 16.2c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C36.756 3.11 33.692 0 27 0zM13.5 16.2C6.3 16.2 1.8 19.8 0 27c2.7-3.6 5.85-4.95 9.45-4.05 2.054.514 3.522 2.004 5.147 3.653C17.244 29.29 20.308 32.4 27 32.4c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C23.256 19.31 20.192 16.2 13.5 16.2z"
|
|
17
|
+
clipRule="evenodd"
|
|
18
|
+
/>
|
|
19
|
+
</g>
|
|
20
|
+
<defs>
|
|
21
|
+
<clipPath id="a">
|
|
22
|
+
<path fill="#fff" d="M0 0h54v32.4H0z" />
|
|
23
|
+
</clipPath>
|
|
24
|
+
</defs>
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
export default TailwindCSS;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as Nextjs } from "./Nextjs";
|
|
2
|
+
export { default as Polar } from "./Polar";
|
|
3
|
+
export { default as BetterAuth } from "./BetterAuth";
|
|
4
|
+
export { default as NeonPostgres } from "./NeonPostgres";
|
|
5
|
+
export { default as TailwindCSS } from "./TailwindCSS";
|
|
6
|
+
export { default as Shadcnui } from "./shadcnui";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "marketing-landing",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Landing page module with hero, features, workflow, integrations, pricing, and footer",
|
|
5
|
+
"files": [
|
|
6
|
+
"app/page.tsx",
|
|
7
|
+
"components/homepage/navigation.tsx",
|
|
8
|
+
"components/homepage/hero-section.tsx",
|
|
9
|
+
"components/homepage/cli-workflow-section.tsx",
|
|
10
|
+
"components/homepage/features-section.tsx",
|
|
11
|
+
"components/homepage/testimonials-section.tsx",
|
|
12
|
+
"components/homepage/news-section.tsx",
|
|
13
|
+
"components/homepage/integrations.tsx",
|
|
14
|
+
"components/homepage/pricing-section.tsx",
|
|
15
|
+
"components/homepage/footer.tsx",
|
|
16
|
+
"components/logos/*"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {},
|
|
19
|
+
"devDependencies": {},
|
|
20
|
+
"envVars": [],
|
|
21
|
+
"requires": [],
|
|
22
|
+
"conflicts": []
|
|
23
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Payments Module - Integration Guide
|
|
2
|
+
|
|
3
|
+
**Module Version:** 1.0.0
|
|
4
|
+
**Last Updated:** 2025-12-22
|
|
5
|
+
**Standalone:** ⚠️ Requires Auth + Database Modules
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This module adds complete subscription management and payment processing using Polar.sh. It includes subscription management pages, billing interfaces, webhook handling, and success pages.
|
|
12
|
+
|
|
13
|
+
**Key Features:**
|
|
14
|
+
- Polar.sh subscription integration
|
|
15
|
+
- Subscription management dashboard
|
|
16
|
+
- Customer portal access
|
|
17
|
+
- Webhook handling for subscription events
|
|
18
|
+
- Payment success page
|
|
19
|
+
- Subscription status tracking
|
|
20
|
+
- Product and pricing management
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Files Added
|
|
25
|
+
|
|
26
|
+
### Pages/Routes
|
|
27
|
+
- `app/dashboard/payment/page.tsx` - Payment and subscription management page
|
|
28
|
+
- `app/success/page.tsx` - Success page after payment completion
|
|
29
|
+
- `app/api/subscription/route.ts` - Subscription API endpoint
|
|
30
|
+
|
|
31
|
+
### Components
|
|
32
|
+
- `app/dashboard/payment/_components/manage-subscription.tsx` - Subscription management component
|
|
33
|
+
|
|
34
|
+
### Utilities/Libraries
|
|
35
|
+
- `lib/subscription.ts` - Subscription utilities and helpers
|
|
36
|
+
- `lib/polar-products.ts` - Polar product fetching utilities
|
|
37
|
+
- `payments-schema.ts` - Subscription table schema (merged into db/schema.ts)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Dependencies
|
|
42
|
+
|
|
43
|
+
### Package Dependencies
|
|
44
|
+
The following packages will be added to `package.json`:
|
|
45
|
+
- `@polar-sh/sdk@^0.42.1` - Polar.sh SDK
|
|
46
|
+
|
|
47
|
+
### Required Modules
|
|
48
|
+
- **Auth Module** - Payments requires authentication
|
|
49
|
+
- **Database Module** - Payments requires database for subscription storage
|
|
50
|
+
|
|
51
|
+
### Included Components
|
|
52
|
+
This module uses UI components from the base package:
|
|
53
|
+
- ✅ `components/ui/button.tsx` - For action buttons
|
|
54
|
+
- ✅ `components/ui/card.tsx` - For subscription cards
|
|
55
|
+
- ✅ `components/ui/badge.tsx` - For status badges
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Environment Variables
|
|
60
|
+
|
|
61
|
+
Add these to your `.env.local`:
|
|
62
|
+
|
|
63
|
+
```env
|
|
64
|
+
# Payments (Polar.sh)
|
|
65
|
+
POLAR_ACCESS_TOKEN="your-polar-access-token"
|
|
66
|
+
POLAR_WEBHOOK_SECRET="your-webhook-secret"
|
|
67
|
+
POLAR_SUCCESS_URL="success" # Path to success page (e.g., "success")
|
|
68
|
+
NEXT_PUBLIC_STARTER_TIER="your-product-id" # Product ID for starter tier
|
|
69
|
+
NEXT_PUBLIC_STARTER_SLUG="starter" # Product slug
|
|
70
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000" # Your app URL (for webhooks)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Required Variables:**
|
|
74
|
+
- `POLAR_ACCESS_TOKEN` - Your Polar.sh API access token
|
|
75
|
+
- `POLAR_WEBHOOK_SECRET` - Secret for webhook signature verification
|
|
76
|
+
- `NEXT_PUBLIC_STARTER_TIER` - Product ID from Polar.sh dashboard
|
|
77
|
+
- `NEXT_PUBLIC_STARTER_SLUG` - Product slug
|
|
78
|
+
- `NEXT_PUBLIC_APP_URL` - Your application URL (for webhook callbacks)
|
|
79
|
+
|
|
80
|
+
**Optional Variables:**
|
|
81
|
+
- `POLAR_SUCCESS_URL` - Custom success page path (defaults to "success")
|
|
82
|
+
|
|
83
|
+
**Where to get them:**
|
|
84
|
+
- **Polar.sh Dashboard**: [polar.sh](https://polar.sh) - Create account and products
|
|
85
|
+
- Get access token from API settings
|
|
86
|
+
- Create webhook secret for signature verification
|
|
87
|
+
- Create products and get product IDs
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Manual Integration Steps
|
|
92
|
+
|
|
93
|
+
If you're appending this module to an existing project, follow these steps:
|
|
94
|
+
|
|
95
|
+
### Step 1: Install Dependencies
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install
|
|
99
|
+
# or
|
|
100
|
+
pnpm install
|
|
101
|
+
# or
|
|
102
|
+
yarn install
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The append command automatically adds missing dependencies to `package.json`, but you need to install them.
|
|
106
|
+
|
|
107
|
+
### Step 2: Add Environment Variables
|
|
108
|
+
|
|
109
|
+
1. Copy `.env.example` to `.env.local` (if not exists)
|
|
110
|
+
2. Add the required environment variables listed above
|
|
111
|
+
3. Get your Polar.sh credentials from the [Polar.sh dashboard](https://polar.sh)
|
|
112
|
+
|
|
113
|
+
### Step 3: Update Database Schema
|
|
114
|
+
|
|
115
|
+
The payments module adds a subscription table. If you have the database module installed, the schema will be merged automatically. Otherwise:
|
|
116
|
+
|
|
117
|
+
1. Add the subscription table to your `db/schema.ts`:
|
|
118
|
+
```typescript
|
|
119
|
+
// Import from payments-schema.ts or copy the table definition
|
|
120
|
+
export { subscription } from '@/payments-schema';
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2. Generate and run migrations:
|
|
124
|
+
```bash
|
|
125
|
+
npm run db:generate
|
|
126
|
+
npm run db:push
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Step 4: Update Auth Configuration
|
|
130
|
+
|
|
131
|
+
The payments module integrates with the auth module's Polar webhook handling. The auth module already includes webhook code, but you need to ensure:
|
|
132
|
+
|
|
133
|
+
1. `lib/auth.ts` includes Polar integration (already done if auth module is installed)
|
|
134
|
+
2. Webhook endpoint is accessible at `/api/auth/callback/polar`
|
|
135
|
+
3. Webhook secret matches your Polar.sh configuration
|
|
136
|
+
|
|
137
|
+
### Step 5: Verify Installation
|
|
138
|
+
|
|
139
|
+
1. Start your dev server: `npm run dev`
|
|
140
|
+
2. Visit `http://localhost:3000/dashboard/payment` to see subscription management
|
|
141
|
+
3. Visit `http://localhost:3000/success` to see the success page
|
|
142
|
+
4. Test the subscription API: `GET /api/subscription`
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Usage Examples
|
|
147
|
+
|
|
148
|
+
### Checking Subscription Status
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { getSubscriptionDetails, hasActiveSubscription } from "@/lib/subscription";
|
|
152
|
+
|
|
153
|
+
// Get full subscription details
|
|
154
|
+
const details = await getSubscriptionDetails();
|
|
155
|
+
if (details.hasSubscription && details.subscription) {
|
|
156
|
+
console.log("Status:", details.subscription.status);
|
|
157
|
+
console.log("Amount:", details.subscription.amount);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Quick check for active subscription
|
|
161
|
+
const isActive = await hasActiveSubscription();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Opening Customer Portal
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
"use client";
|
|
168
|
+
|
|
169
|
+
import { authClient } from "@/lib/auth-client";
|
|
170
|
+
|
|
171
|
+
export function ManageSubscriptionButton() {
|
|
172
|
+
const handleOpenPortal = async () => {
|
|
173
|
+
try {
|
|
174
|
+
await authClient.customer.portal();
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error("Failed to open portal:", error);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return <button onClick={handleOpenPortal}>Manage Subscription</button>;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Creating Checkout Session
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
"use client";
|
|
188
|
+
|
|
189
|
+
import { authClient } from "@/lib/auth-client";
|
|
190
|
+
|
|
191
|
+
export function SubscribeButton({ productId }: { productId: string }) {
|
|
192
|
+
const handleSubscribe = async () => {
|
|
193
|
+
try {
|
|
194
|
+
await authClient.checkout({
|
|
195
|
+
productId,
|
|
196
|
+
successUrl: `${window.location.origin}/success`,
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error("Checkout failed:", error);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return <button onClick={handleSubscribe}>Subscribe</button>;
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Customization
|
|
210
|
+
|
|
211
|
+
### Styling
|
|
212
|
+
- Payment pages use Tailwind CSS classes
|
|
213
|
+
- Customize colors in `tailwind.config.ts`
|
|
214
|
+
- Modify page layouts in `app/dashboard/payment/page.tsx`
|
|
215
|
+
|
|
216
|
+
### Configuration
|
|
217
|
+
- Product IDs are configured via environment variables
|
|
218
|
+
- Webhook handling is in `lib/auth.ts` (auth module)
|
|
219
|
+
- Customize success page in `app/success/page.tsx`
|
|
220
|
+
|
|
221
|
+
### Subscription Management
|
|
222
|
+
- Customize subscription display in `app/dashboard/payment/page.tsx`
|
|
223
|
+
- Add custom subscription actions in `_components/manage-subscription.tsx`
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Integration Points
|
|
228
|
+
|
|
229
|
+
### Files That May Need Manual Updates
|
|
230
|
+
|
|
231
|
+
**lib/auth.ts** (if auth module installed):
|
|
232
|
+
- Polar webhook handling is already included
|
|
233
|
+
- Ensure webhook secret matches your Polar.sh configuration
|
|
234
|
+
|
|
235
|
+
**db/schema.ts** (if database module installed):
|
|
236
|
+
- Subscription table will be automatically merged
|
|
237
|
+
- If manual merge needed, import from `payments-schema.ts`
|
|
238
|
+
|
|
239
|
+
**Middleware** (if auth module installed):
|
|
240
|
+
- No changes needed - payments uses existing auth protection
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Troubleshooting
|
|
245
|
+
|
|
246
|
+
### Common Issues
|
|
247
|
+
|
|
248
|
+
**Issue:** "Subscription table not found" error
|
|
249
|
+
**Solution:**
|
|
250
|
+
- Ensure database module is installed
|
|
251
|
+
- Run `npm run db:push` to create tables
|
|
252
|
+
- Verify subscription table exists in `db/schema.ts`
|
|
253
|
+
|
|
254
|
+
**Issue:** Webhooks not working
|
|
255
|
+
**Solution:**
|
|
256
|
+
- Verify `POLAR_WEBHOOK_SECRET` matches Polar.sh dashboard
|
|
257
|
+
- Check webhook URL is correct: `https://yourdomain.com/api/auth/callback/polar`
|
|
258
|
+
- Ensure webhook events are enabled in Polar.sh dashboard
|
|
259
|
+
|
|
260
|
+
**Issue:** "POLAR_ACCESS_TOKEN is required"
|
|
261
|
+
**Solution:**
|
|
262
|
+
- Add `POLAR_ACCESS_TOKEN` to `.env.local`
|
|
263
|
+
- Get token from Polar.sh API settings
|
|
264
|
+
- Restart dev server after adding env vars
|
|
265
|
+
|
|
266
|
+
**Issue:** Checkout not redirecting
|
|
267
|
+
**Solution:**
|
|
268
|
+
- Verify `NEXT_PUBLIC_APP_URL` is set correctly
|
|
269
|
+
- Check `POLAR_SUCCESS_URL` matches your success page path
|
|
270
|
+
- Ensure product IDs are correct
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Next Steps
|
|
275
|
+
|
|
276
|
+
After installing this module:
|
|
277
|
+
|
|
278
|
+
1. Set up Polar.sh account and create products
|
|
279
|
+
2. Configure environment variables
|
|
280
|
+
3. Run database migrations
|
|
281
|
+
4. Test checkout flow
|
|
282
|
+
5. Configure webhooks in Polar.sh dashboard
|
|
283
|
+
6. Customize success page
|
|
284
|
+
7. Add pricing page (optional - can use marketing landing pricing section)
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Related Modules
|
|
289
|
+
|
|
290
|
+
This module requires:
|
|
291
|
+
- **Auth Module** - For user authentication and webhook handling
|
|
292
|
+
- **Database Module** - For subscription storage
|
|
293
|
+
|
|
294
|
+
This module works well with:
|
|
295
|
+
- **Email Module** - For subscription confirmation emails
|
|
296
|
+
- **Marketing Landing Module** - For pricing page display
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Module Status
|
|
301
|
+
|
|
302
|
+
✅ **Core Files Included** - All payment pages and utilities
|
|
303
|
+
⚠️ **Requires Auth + Database** - Must be installed first
|
|
304
|
+
✅ **Webhook Integration** - Works with auth module's webhook handler
|
|
305
|
+
✅ **Well Documented** - Comprehensive integration guide
|
|
306
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { auth } from "@/lib/auth";
|
|
2
|
+
import { getSubscriptionDetails } from "@/lib/subscription";
|
|
3
|
+
import { headers } from "next/headers";
|
|
4
|
+
import { NextResponse } from "next/server";
|
|
5
|
+
|
|
6
|
+
export async function GET() {
|
|
7
|
+
try {
|
|
8
|
+
const result = await auth.api.getSession({
|
|
9
|
+
headers: await headers(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!result?.session?.userId) {
|
|
13
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const subscriptionDetails = await getSubscriptionDetails();
|
|
17
|
+
return NextResponse.json(subscriptionDetails);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("Error fetching subscription details:", error);
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: "Failed to fetch subscription details" },
|
|
22
|
+
{ status: 500 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|