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,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Home, ArrowLeft } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
export default function NotFound() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex min-h-screen flex-col items-center justify-center px-6">
|
|
10
|
+
<div className="text-center space-y-6 max-w-md">
|
|
11
|
+
<div className="space-y-2">
|
|
12
|
+
<h1 className="text-9xl font-bold text-muted-foreground/20">404</h1>
|
|
13
|
+
<h2 className="text-4xl font-bold">Page Not Found</h2>
|
|
14
|
+
<p className="text-muted-foreground text-lg">
|
|
15
|
+
Sorry, we couldn't find the page you're looking for. It might have been moved or doesn't exist.
|
|
16
|
+
</p>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div className="flex flex-col sm:flex-row gap-3 justify-center items-center pt-4">
|
|
20
|
+
<Button asChild variant="default">
|
|
21
|
+
<Link href="/">
|
|
22
|
+
<Home className="mr-2 h-4 w-4" />
|
|
23
|
+
Go Home
|
|
24
|
+
</Link>
|
|
25
|
+
</Button>
|
|
26
|
+
<Button asChild variant="outline" onClick={() => window.history.back()}>
|
|
27
|
+
<button>
|
|
28
|
+
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
29
|
+
Go Back
|
|
30
|
+
</button>
|
|
31
|
+
</Button>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div className="pt-8 text-sm text-muted-foreground">
|
|
35
|
+
<p>
|
|
36
|
+
Need help?{" "}
|
|
37
|
+
<Link href="https://github.com/kedbrant/Saas-scaffold/issues" className="text-primary hover:underline" target="_blank" rel="noopener noreferrer">
|
|
38
|
+
Report an issue
|
|
39
|
+
</Link>
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Navigation from "@/components/homepage/navigation";
|
|
2
|
+
import FooterSection from "@/components/homepage/footer";
|
|
3
|
+
import HeroSection from "@/components/homepage/hero-section";
|
|
4
|
+
import CliWorkflowSection from "@/components/homepage/cli-workflow-section";
|
|
5
|
+
import FeaturesSection from "@/components/homepage/features-section";
|
|
6
|
+
import TestimonialsSection from "@/components/homepage/testimonials-section";
|
|
7
|
+
import NewsSection from "@/components/homepage/news-section";
|
|
8
|
+
import Integrations from "@/components/homepage/integrations";
|
|
9
|
+
import { getSubscriptionDetails } from "@/lib/subscription";
|
|
10
|
+
import PricingTable from "./pricing/_component/pricing-table";
|
|
11
|
+
|
|
12
|
+
export default async function Home() {
|
|
13
|
+
const subscriptionDetails = await getSubscriptionDetails();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<Navigation />
|
|
18
|
+
<HeroSection />
|
|
19
|
+
<CliWorkflowSection />
|
|
20
|
+
<FeaturesSection />
|
|
21
|
+
<TestimonialsSection />
|
|
22
|
+
<Integrations />
|
|
23
|
+
<NewsSection />
|
|
24
|
+
<PricingTable subscriptionDetails={subscriptionDetails} />
|
|
25
|
+
<FooterSection />
|
|
26
|
+
</>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardFooter,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
} from "@/components/ui/card";
|
|
12
|
+
import { authClient } from "@/lib/auth-client";
|
|
13
|
+
import { Check } from "lucide-react";
|
|
14
|
+
import { toast } from "sonner";
|
|
15
|
+
import { useRouter } from "next/navigation";
|
|
16
|
+
import { useEffect, useState } from "react";
|
|
17
|
+
|
|
18
|
+
type SubscriptionDetails = {
|
|
19
|
+
id: string;
|
|
20
|
+
productId: string;
|
|
21
|
+
status: string;
|
|
22
|
+
amount: number;
|
|
23
|
+
currency: string;
|
|
24
|
+
recurringInterval: string;
|
|
25
|
+
currentPeriodStart: Date;
|
|
26
|
+
currentPeriodEnd: Date;
|
|
27
|
+
cancelAtPeriodEnd: boolean;
|
|
28
|
+
canceledAt: Date | null;
|
|
29
|
+
organizationId: string | null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type SubscriptionDetailsResult = {
|
|
33
|
+
hasSubscription: boolean;
|
|
34
|
+
subscription?: SubscriptionDetails;
|
|
35
|
+
error?: string;
|
|
36
|
+
errorType?: "CANCELED" | "EXPIRED" | "GENERAL";
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
interface ProductDetails {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
description: string | null;
|
|
43
|
+
prices: Array<{
|
|
44
|
+
id: string;
|
|
45
|
+
amount: number;
|
|
46
|
+
currency: string;
|
|
47
|
+
recurring_interval: 'month' | 'year';
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface PricingTableProps {
|
|
52
|
+
subscriptionDetails: SubscriptionDetailsResult;
|
|
53
|
+
productDetails: ProductDetails | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default function PricingTable({
|
|
57
|
+
subscriptionDetails,
|
|
58
|
+
productDetails,
|
|
59
|
+
}: PricingTableProps) {
|
|
60
|
+
const router = useRouter();
|
|
61
|
+
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const checkAuth = async () => {
|
|
65
|
+
try {
|
|
66
|
+
const session = await authClient.getSession();
|
|
67
|
+
setIsAuthenticated(!!session.data?.user);
|
|
68
|
+
} catch {
|
|
69
|
+
setIsAuthenticated(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
checkAuth();
|
|
73
|
+
}, []);
|
|
74
|
+
|
|
75
|
+
const handleCheckout = async (productId: string, slug: string) => {
|
|
76
|
+
if (isAuthenticated === false) {
|
|
77
|
+
router.push("/sign-in");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await authClient.checkout({
|
|
83
|
+
products: [productId],
|
|
84
|
+
slug: slug,
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error("Checkout failed:", error);
|
|
88
|
+
// TODO: Add user-facing error notification
|
|
89
|
+
toast.error("Oops, something went wrong");
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const handleManageSubscription = async () => {
|
|
94
|
+
try {
|
|
95
|
+
await authClient.customer.portal();
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Failed to open customer portal:", error);
|
|
98
|
+
toast.error("Failed to open subscription management");
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const STARTER_TIER = process.env.NEXT_PUBLIC_STARTER_TIER;
|
|
103
|
+
const STARTER_SLUG = process.env.NEXT_PUBLIC_STARTER_SLUG;
|
|
104
|
+
|
|
105
|
+
// Check if Polar is configured
|
|
106
|
+
const isPolarConfigured = !!(STARTER_TIER && STARTER_SLUG);
|
|
107
|
+
|
|
108
|
+
if (!isPolarConfigured) {
|
|
109
|
+
// Return a message when Polar isn't configured instead of throwing
|
|
110
|
+
return (
|
|
111
|
+
<section id="pricing" className="flex flex-col items-center justify-center px-4 mb-24 w-full bg-black">
|
|
112
|
+
<div className="text-center mb-12">
|
|
113
|
+
<h1 className="text-4xl font-medium tracking-tight mb-4 bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
|
|
114
|
+
Simple, Transparent Pricing
|
|
115
|
+
</h1>
|
|
116
|
+
<p className="text-xl text-gray-400 max-w-2xl mx-auto">
|
|
117
|
+
saas-scaffold is free and open source. This section demonstrates the built-in Polar.sh subscription integration.
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div className="max-w-2xl w-full">
|
|
122
|
+
<Card className="p-8 bg-[#0a0a0a] border-[#2a2a2a]">
|
|
123
|
+
<div className="text-center space-y-4">
|
|
124
|
+
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-[#ff5722]/10 border border-[#ff5722]/30">
|
|
125
|
+
<span className="text-sm text-[#ff5722]">⚠️ Pricing Not Configured</span>
|
|
126
|
+
</div>
|
|
127
|
+
<p className="text-gray-400">
|
|
128
|
+
To enable subscription pricing, configure the following environment variables:
|
|
129
|
+
</p>
|
|
130
|
+
<div className="bg-black/50 rounded-lg p-4 font-mono text-sm text-left text-gray-300 space-y-1">
|
|
131
|
+
<div>NEXT_PUBLIC_STARTER_TIER</div>
|
|
132
|
+
<div>NEXT_PUBLIC_STARTER_SLUG</div>
|
|
133
|
+
<div>POLAR_ACCESS_TOKEN</div>
|
|
134
|
+
<div>POLAR_WEBHOOK_SECRET</div>
|
|
135
|
+
</div>
|
|
136
|
+
<p className="text-sm text-gray-500 pt-4">
|
|
137
|
+
Learn more at{" "}
|
|
138
|
+
<a href="https://polar.sh" target="_blank" rel="noopener noreferrer" className="text-[#ff5722] hover:underline">
|
|
139
|
+
polar.sh
|
|
140
|
+
</a>
|
|
141
|
+
</p>
|
|
142
|
+
</div>
|
|
143
|
+
</Card>
|
|
144
|
+
</div>
|
|
145
|
+
</section>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const isCurrentPlan = (tierProductId: string) => {
|
|
150
|
+
return (
|
|
151
|
+
subscriptionDetails.hasSubscription &&
|
|
152
|
+
subscriptionDetails.subscription?.productId === tierProductId &&
|
|
153
|
+
subscriptionDetails.subscription?.status === "active"
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const formatDate = (date: Date) => {
|
|
158
|
+
return new Date(date).toLocaleDateString("en-US", {
|
|
159
|
+
year: "numeric",
|
|
160
|
+
month: "long",
|
|
161
|
+
day: "numeric",
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Get product price details
|
|
166
|
+
const getProductPrice = () => {
|
|
167
|
+
if (!productDetails || !productDetails.prices || productDetails.prices.length === 0) {
|
|
168
|
+
return { amount: 1000, currency: 'USD', interval: 'month' };
|
|
169
|
+
}
|
|
170
|
+
const price = productDetails.prices[0];
|
|
171
|
+
return {
|
|
172
|
+
amount: price.amount / 100, // Convert cents to dollars
|
|
173
|
+
currency: price.currency.toUpperCase(),
|
|
174
|
+
interval: price.recurring_interval,
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const price = getProductPrice();
|
|
179
|
+
const productName = productDetails?.name || 'Starter';
|
|
180
|
+
const productDescription = productDetails?.description || 'Perfect for getting started';
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<section id="pricing" className="flex flex-col items-center justify-center px-4 mb-24 w-full bg-black">
|
|
184
|
+
<div className="text-center mb-12">
|
|
185
|
+
<h1 className="text-4xl font-medium tracking-tight mb-4 bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
|
|
186
|
+
Simple, Transparent Pricing
|
|
187
|
+
</h1>
|
|
188
|
+
<p className="text-xl text-gray-400">
|
|
189
|
+
saas-scaffold is free and open source. This section demonstrates the built-in Polar.sh subscription integration.
|
|
190
|
+
</p>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div className="grid md:grid-cols-2 gap-8 max-w-4xl w-full">
|
|
194
|
+
{/* Starter Tier */}
|
|
195
|
+
<Card className="relative h-fit bg-[#0a0a0a] border-[#2a2a2a] hover:border-[#ff5722]/30 transition-all duration-300">
|
|
196
|
+
{isCurrentPlan(STARTER_TIER) && (
|
|
197
|
+
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2">
|
|
198
|
+
<Badge
|
|
199
|
+
variant="secondary"
|
|
200
|
+
className="bg-[#ff5722]/20 text-[#ff5722] border border-[#ff5722]/30"
|
|
201
|
+
>
|
|
202
|
+
Current Plan
|
|
203
|
+
</Badge>
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
<CardHeader>
|
|
207
|
+
<CardTitle className="text-2xl text-white">{productName}</CardTitle>
|
|
208
|
+
<CardDescription className="text-gray-400">{productDescription}</CardDescription>
|
|
209
|
+
<div className="mt-4">
|
|
210
|
+
<span className="text-4xl font-bold bg-gradient-to-r from-[#ff5722] to-[#ff7043] bg-clip-text text-transparent">
|
|
211
|
+
${price.amount.toLocaleString()}
|
|
212
|
+
</span>
|
|
213
|
+
<span className="text-gray-400">/{price.interval}</span>
|
|
214
|
+
</div>
|
|
215
|
+
</CardHeader>
|
|
216
|
+
<CardContent className="space-y-4">
|
|
217
|
+
<div className="flex items-center gap-3">
|
|
218
|
+
<Check className="h-5 w-5 text-[#ff5722]" />
|
|
219
|
+
<span className="text-gray-300">5 Projects</span>
|
|
220
|
+
</div>
|
|
221
|
+
<div className="flex items-center gap-3">
|
|
222
|
+
<Check className="h-5 w-5 text-[#ff5722]" />
|
|
223
|
+
<span className="text-gray-300">10GB Storage</span>
|
|
224
|
+
</div>
|
|
225
|
+
<div className="flex items-center gap-3">
|
|
226
|
+
<Check className="h-5 w-5 text-[#ff5722]" />
|
|
227
|
+
<span className="text-gray-300">1 Team Member</span>
|
|
228
|
+
</div>
|
|
229
|
+
<div className="flex items-center gap-3">
|
|
230
|
+
<Check className="h-5 w-5 text-[#ff5722]" />
|
|
231
|
+
<span className="text-gray-300">Email Support</span>
|
|
232
|
+
</div>
|
|
233
|
+
</CardContent>
|
|
234
|
+
<CardFooter>
|
|
235
|
+
{isCurrentPlan(STARTER_TIER) ? (
|
|
236
|
+
<div className="w-full space-y-2">
|
|
237
|
+
<Button
|
|
238
|
+
className="w-full"
|
|
239
|
+
variant="outline"
|
|
240
|
+
onClick={handleManageSubscription}
|
|
241
|
+
>
|
|
242
|
+
Manage Subscription
|
|
243
|
+
</Button>
|
|
244
|
+
{subscriptionDetails.subscription && (
|
|
245
|
+
<p className="text-sm text-gray-400 text-center">
|
|
246
|
+
{subscriptionDetails.subscription.cancelAtPeriodEnd
|
|
247
|
+
? `Expires ${formatDate(subscriptionDetails.subscription.currentPeriodEnd)}`
|
|
248
|
+
: `Renews ${formatDate(subscriptionDetails.subscription.currentPeriodEnd)}`}
|
|
249
|
+
</p>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
) : (
|
|
253
|
+
<Button
|
|
254
|
+
className="w-full"
|
|
255
|
+
onClick={() => handleCheckout(STARTER_TIER, STARTER_SLUG)}
|
|
256
|
+
>
|
|
257
|
+
{isAuthenticated === false
|
|
258
|
+
? "Sign In to Get Started"
|
|
259
|
+
: "Get Started"}
|
|
260
|
+
</Button>
|
|
261
|
+
)}
|
|
262
|
+
</CardFooter>
|
|
263
|
+
</Card>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div className="mt-12 text-center">
|
|
267
|
+
<p className="text-gray-400">
|
|
268
|
+
Need a custom plan?{" "}
|
|
269
|
+
<span className="text-[#ff5722] cursor-pointer hover:underline">
|
|
270
|
+
Contact us
|
|
271
|
+
</span>
|
|
272
|
+
</p>
|
|
273
|
+
</div>
|
|
274
|
+
</section>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getSubscriptionDetails } from "@/lib/subscription";
|
|
2
|
+
import { getProductDetails } from "@/lib/polar-products";
|
|
3
|
+
import PricingTable from "./_component/pricing-table";
|
|
4
|
+
|
|
5
|
+
// Force dynamic rendering to fetch fresh product data
|
|
6
|
+
export const dynamic = 'force-dynamic';
|
|
7
|
+
|
|
8
|
+
export default async function PricingPage() {
|
|
9
|
+
const subscriptionDetails = await getSubscriptionDetails();
|
|
10
|
+
|
|
11
|
+
// Fetch actual product details from Polar
|
|
12
|
+
const productId = process.env.NEXT_PUBLIC_STARTER_TIER;
|
|
13
|
+
const productDetails = productId ? await getProductDetails(productId) : null;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="flex flex-col items-center justify-center w-full min-h-screen">
|
|
17
|
+
<PricingTable
|
|
18
|
+
subscriptionDetails={subscriptionDetails}
|
|
19
|
+
productDetails={productDetails}
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { ArrowLeft } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
export const metadata = {
|
|
5
|
+
title: "Privacy Policy",
|
|
6
|
+
description: "Privacy Policy for {{PROJECT_NAME}}",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function PrivacyPolicy() {
|
|
10
|
+
return (
|
|
11
|
+
<div className="min-h-screen bg-black">
|
|
12
|
+
{/* Back Link */}
|
|
13
|
+
<div className="border-b border-[#2a2a2a]">
|
|
14
|
+
<div className="container mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
|
15
|
+
<Link
|
|
16
|
+
href="/"
|
|
17
|
+
className="inline-flex items-center text-sm text-gray-400 hover:text-[#ff5722] transition-colors"
|
|
18
|
+
>
|
|
19
|
+
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
20
|
+
Back to Home
|
|
21
|
+
</Link>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
{/* Content */}
|
|
26
|
+
<div className="container mx-auto px-4 py-12 sm:px-6 lg:px-8">
|
|
27
|
+
<div className="mx-auto max-w-4xl">
|
|
28
|
+
<h1 className="text-4xl font-bold tracking-tight text-white sm:text-5xl mb-4">
|
|
29
|
+
Privacy Policy
|
|
30
|
+
</h1>
|
|
31
|
+
<p className="text-gray-400 mb-8">
|
|
32
|
+
Last updated: {new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })}
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<div className="space-y-8 text-gray-300">
|
|
36
|
+
{/* ChatGPT Customization Prompt */}
|
|
37
|
+
{/*
|
|
38
|
+
To customize this template, use ChatGPT with this prompt:
|
|
39
|
+
|
|
40
|
+
"I need to create a Privacy Policy for my SaaS product called [YOUR_PRODUCT_NAME].
|
|
41
|
+
Key details:
|
|
42
|
+
- Service description: [DESCRIBE YOUR SERVICE]
|
|
43
|
+
- Company name: [YOUR_COMPANY_NAME]
|
|
44
|
+
- Location: [YOUR_COUNTRY/STATE]
|
|
45
|
+
- Data collected: [LIST DATA TYPES: emails, names, payment info, usage data, etc.]
|
|
46
|
+
- Third-party services: [Google OAuth, Stripe, Polar, PostHog, etc.]
|
|
47
|
+
- Data retention: [HOW LONG YOU KEEP DATA]
|
|
48
|
+
- User rights: [GDPR/CCPA compliance requirements]
|
|
49
|
+
|
|
50
|
+
Please generate a comprehensive Privacy Policy covering: data collection,
|
|
51
|
+
use of information, data sharing, security measures, cookies, user rights,
|
|
52
|
+
and international data transfers."
|
|
53
|
+
*/}
|
|
54
|
+
<section>
|
|
55
|
+
<h2 className="text-2xl font-bold text-white mb-4">1. Introduction</h2>
|
|
56
|
+
<p className="leading-relaxed">
|
|
57
|
+
At {{PROJECT_NAME}}, we take your privacy seriously. This Privacy Policy explains how we
|
|
58
|
+
collect, use, disclose, and safeguard your information when you use our service.
|
|
59
|
+
Please read this privacy policy carefully. If you do not agree with the terms of this
|
|
60
|
+
privacy policy, please do not access the service.
|
|
61
|
+
</p>
|
|
62
|
+
</section>
|
|
63
|
+
|
|
64
|
+
<section>
|
|
65
|
+
<h2 className="text-2xl font-bold text-white mb-4">2. Information We Collect</h2>
|
|
66
|
+
|
|
67
|
+
<h3 className="text-xl font-bold text-white mb-3 mt-6">Personal Information</h3>
|
|
68
|
+
<p className="leading-relaxed mb-4">
|
|
69
|
+
We collect information that you provide directly to us when you:
|
|
70
|
+
</p>
|
|
71
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
72
|
+
<li>Create an account (name, email address, password)</li>
|
|
73
|
+
<li>Make a payment (billing information, processed by our payment providers)</li>
|
|
74
|
+
<li>Contact us for support (email, messages, attachments)</li>
|
|
75
|
+
<li>Subscribe to our newsletter or marketing communications</li>
|
|
76
|
+
</ul>
|
|
77
|
+
|
|
78
|
+
<h3 className="text-xl font-bold text-white mb-3 mt-6">Automatically Collected Information</h3>
|
|
79
|
+
<p className="leading-relaxed mb-4">
|
|
80
|
+
When you use our Service, we automatically collect certain information, including:
|
|
81
|
+
</p>
|
|
82
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
83
|
+
<li>Log data (IP address, browser type, operating system)</li>
|
|
84
|
+
<li>Device information (device type, unique device identifiers)</li>
|
|
85
|
+
<li>Usage data (pages visited, features used, time spent)</li>
|
|
86
|
+
<li>Cookies and similar tracking technologies</li>
|
|
87
|
+
</ul>
|
|
88
|
+
|
|
89
|
+
<h3 className="text-xl font-bold text-white mb-3 mt-6">Third-Party Information</h3>
|
|
90
|
+
<p className="leading-relaxed">
|
|
91
|
+
If you choose to authenticate using third-party services (Google, GitHub, etc.),
|
|
92
|
+
we receive basic profile information from those services in accordance with their policies.
|
|
93
|
+
</p>
|
|
94
|
+
</section>
|
|
95
|
+
|
|
96
|
+
<section>
|
|
97
|
+
<h2 className="text-2xl font-bold text-white mb-4">3. How We Use Your Information</h2>
|
|
98
|
+
<p className="leading-relaxed mb-4">
|
|
99
|
+
We use the information we collect to:
|
|
100
|
+
</p>
|
|
101
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
102
|
+
<li>Provide, operate, and maintain our Service</li>
|
|
103
|
+
<li>Process your transactions and manage your account</li>
|
|
104
|
+
<li>Send you technical notices, updates, and support messages</li>
|
|
105
|
+
<li>Respond to your comments, questions, and customer service requests</li>
|
|
106
|
+
<li>Communicate with you about products, services, and events</li>
|
|
107
|
+
<li>Monitor and analyze trends, usage, and activities</li>
|
|
108
|
+
<li>Detect, prevent, and address technical issues and security threats</li>
|
|
109
|
+
<li>Improve and personalize your experience</li>
|
|
110
|
+
<li>Comply with legal obligations</li>
|
|
111
|
+
</ul>
|
|
112
|
+
</section>
|
|
113
|
+
|
|
114
|
+
<section>
|
|
115
|
+
<h2 className="text-2xl font-bold text-white mb-4">4. How We Share Your Information</h2>
|
|
116
|
+
<p className="leading-relaxed mb-4">
|
|
117
|
+
We may share your information in the following circumstances:
|
|
118
|
+
</p>
|
|
119
|
+
|
|
120
|
+
<h3 className="text-xl font-bold text-white mb-3 mt-6">Service Providers</h3>
|
|
121
|
+
<p className="leading-relaxed mb-4">
|
|
122
|
+
We share information with third-party service providers who perform services on our behalf:
|
|
123
|
+
</p>
|
|
124
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
125
|
+
<li>Payment processing (Stripe, Polar.sh)</li>
|
|
126
|
+
<li>Cloud hosting and infrastructure (Vercel, AWS)</li>
|
|
127
|
+
<li>Analytics and monitoring (PostHog, Sentry)</li>
|
|
128
|
+
<li>Email delivery and communications</li>
|
|
129
|
+
<li>Authentication services (Google OAuth)</li>
|
|
130
|
+
</ul>
|
|
131
|
+
|
|
132
|
+
<h3 className="text-xl font-bold text-white mb-3 mt-6">Legal Requirements</h3>
|
|
133
|
+
<p className="leading-relaxed mb-4">
|
|
134
|
+
We may disclose your information if required to do so by law or in response to:
|
|
135
|
+
</p>
|
|
136
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
137
|
+
<li>Valid legal processes (subpoenas, court orders)</li>
|
|
138
|
+
<li>Requests from law enforcement or government agencies</li>
|
|
139
|
+
<li>Protection of our rights, property, or safety</li>
|
|
140
|
+
<li>Prevention of fraud or security threats</li>
|
|
141
|
+
</ul>
|
|
142
|
+
|
|
143
|
+
<h3 className="text-xl font-bold text-white mb-3 mt-6">Business Transfers</h3>
|
|
144
|
+
<p className="leading-relaxed">
|
|
145
|
+
If we are involved in a merger, acquisition, or sale of assets, your information may be
|
|
146
|
+
transferred as part of that transaction. We will notify you of any such change.
|
|
147
|
+
</p>
|
|
148
|
+
</section>
|
|
149
|
+
|
|
150
|
+
<section>
|
|
151
|
+
<h2 className="text-2xl font-bold text-white mb-4">5. Data Security</h2>
|
|
152
|
+
<p className="leading-relaxed mb-4">
|
|
153
|
+
We implement appropriate technical and organizational security measures to protect your
|
|
154
|
+
information, including:
|
|
155
|
+
</p>
|
|
156
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
157
|
+
<li>Encryption of data in transit using TLS/SSL</li>
|
|
158
|
+
<li>Encryption of sensitive data at rest</li>
|
|
159
|
+
<li>Regular security assessments and updates</li>
|
|
160
|
+
<li>Access controls and authentication requirements</li>
|
|
161
|
+
<li>Monitoring for security threats and vulnerabilities</li>
|
|
162
|
+
</ul>
|
|
163
|
+
<p className="leading-relaxed mt-4">
|
|
164
|
+
However, no method of transmission over the Internet or electronic storage is 100% secure.
|
|
165
|
+
While we strive to use commercially acceptable means to protect your information, we cannot
|
|
166
|
+
guarantee its absolute security.
|
|
167
|
+
</p>
|
|
168
|
+
</section>
|
|
169
|
+
|
|
170
|
+
<section>
|
|
171
|
+
<h2 className="text-2xl font-bold text-white mb-4">6. Cookies and Tracking Technologies</h2>
|
|
172
|
+
<p className="leading-relaxed mb-4">
|
|
173
|
+
We use cookies and similar tracking technologies to collect information and improve our Service:
|
|
174
|
+
</p>
|
|
175
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
176
|
+
<li><strong>Essential Cookies:</strong> Required for the Service to function properly</li>
|
|
177
|
+
<li><strong>Analytics Cookies:</strong> Help us understand how users interact with the Service</li>
|
|
178
|
+
<li><strong>Preference Cookies:</strong> Remember your settings and preferences</li>
|
|
179
|
+
</ul>
|
|
180
|
+
<p className="leading-relaxed mt-4">
|
|
181
|
+
You can control cookies through your browser settings. However, disabling cookies may affect
|
|
182
|
+
the functionality of the Service.
|
|
183
|
+
</p>
|
|
184
|
+
</section>
|
|
185
|
+
|
|
186
|
+
<section>
|
|
187
|
+
<h2 className="text-2xl font-bold text-white mb-4">7. Data Retention</h2>
|
|
188
|
+
<p className="leading-relaxed">
|
|
189
|
+
We retain your information for as long as necessary to provide the Service and fulfill the
|
|
190
|
+
purposes described in this Privacy Policy. When you delete your account, we will delete or
|
|
191
|
+
anonymize your personal information within 30 days, except where we are required to retain
|
|
192
|
+
it for legal, regulatory, or legitimate business purposes.
|
|
193
|
+
</p>
|
|
194
|
+
</section>
|
|
195
|
+
|
|
196
|
+
<section>
|
|
197
|
+
<h2 className="text-2xl font-bold text-white mb-4">8. Your Privacy Rights</h2>
|
|
198
|
+
<p className="leading-relaxed mb-4">
|
|
199
|
+
Depending on your location, you may have the following rights regarding your personal information:
|
|
200
|
+
</p>
|
|
201
|
+
<ul className="list-disc list-inside space-y-2 ml-4">
|
|
202
|
+
<li><strong>Access:</strong> Request a copy of your personal information</li>
|
|
203
|
+
<li><strong>Correction:</strong> Request correction of inaccurate information</li>
|
|
204
|
+
<li><strong>Deletion:</strong> Request deletion of your personal information</li>
|
|
205
|
+
<li><strong>Portability:</strong> Request transfer of your data to another service</li>
|
|
206
|
+
<li><strong>Objection:</strong> Object to certain processing of your information</li>
|
|
207
|
+
<li><strong>Restriction:</strong> Request restriction of processing your information</li>
|
|
208
|
+
<li><strong>Withdrawal of Consent:</strong> Withdraw consent for data processing</li>
|
|
209
|
+
</ul>
|
|
210
|
+
<p className="leading-relaxed mt-4">
|
|
211
|
+
To exercise these rights, please contact us at [your-email@example.com]. We will respond
|
|
212
|
+
to your request within 30 days.
|
|
213
|
+
</p>
|
|
214
|
+
</section>
|
|
215
|
+
|
|
216
|
+
<section>
|
|
217
|
+
<h2 className="text-2xl font-bold text-white mb-4">9. International Data Transfers</h2>
|
|
218
|
+
<p className="leading-relaxed">
|
|
219
|
+
Your information may be transferred to and maintained on servers located outside of your
|
|
220
|
+
country where data protection laws may differ. By using the Service, you consent to the
|
|
221
|
+
transfer of your information to [YOUR_COUNTRY] and other countries where we operate.
|
|
222
|
+
We ensure appropriate safeguards are in place for international data transfers.
|
|
223
|
+
</p>
|
|
224
|
+
</section>
|
|
225
|
+
|
|
226
|
+
<section>
|
|
227
|
+
<h2 className="text-2xl font-bold text-white mb-4">10. Children's Privacy</h2>
|
|
228
|
+
<p className="leading-relaxed">
|
|
229
|
+
Our Service is not intended for children under 13 years of age (or 16 in the European Union).
|
|
230
|
+
We do not knowingly collect personal information from children. If you believe we have
|
|
231
|
+
collected information from a child, please contact us immediately and we will delete it.
|
|
232
|
+
</p>
|
|
233
|
+
</section>
|
|
234
|
+
|
|
235
|
+
<section>
|
|
236
|
+
<h2 className="text-2xl font-bold text-white mb-4">11. Changes to This Privacy Policy</h2>
|
|
237
|
+
<p className="leading-relaxed">
|
|
238
|
+
We may update this Privacy Policy from time to time. We will notify you of any changes by
|
|
239
|
+
posting the new Privacy Policy on this page and updating the "Last updated" date.
|
|
240
|
+
You are advised to review this Privacy Policy periodically for any changes. Changes are
|
|
241
|
+
effective when posted on this page.
|
|
242
|
+
</p>
|
|
243
|
+
</section>
|
|
244
|
+
|
|
245
|
+
<section>
|
|
246
|
+
<h2 className="text-2xl font-bold text-white mb-4">12. Contact Us</h2>
|
|
247
|
+
<p className="leading-relaxed mb-4">
|
|
248
|
+
If you have any questions about this Privacy Policy or our privacy practices, please contact us at:
|
|
249
|
+
</p>
|
|
250
|
+
<div className="bg-[#0a0a0a] border border-[#2a2a2a] rounded-lg p-6">
|
|
251
|
+
<p className="text-gray-300">
|
|
252
|
+
Email: <span className="text-[#ff5722]">[your-email@example.com]</span>
|
|
253
|
+
</p>
|
|
254
|
+
<p className="text-gray-300 mt-2">
|
|
255
|
+
Company: <span className="text-[#ff5722]">[YOUR_COMPANY_NAME]</span>
|
|
256
|
+
</p>
|
|
257
|
+
<p className="text-gray-300 mt-2">
|
|
258
|
+
Address: <span className="text-[#ff5722]">[YOUR_ADDRESS]</span>
|
|
259
|
+
</p>
|
|
260
|
+
<p className="text-gray-300 mt-2">
|
|
261
|
+
Data Protection Officer: <span className="text-[#ff5722]">[DPO_EMAIL if applicable]</span>
|
|
262
|
+
</p>
|
|
263
|
+
</div>
|
|
264
|
+
</section>
|
|
265
|
+
|
|
266
|
+
<div className="mt-12 p-6 bg-[#ff5722]/10 border border-[#ff5722]/30 rounded-lg">
|
|
267
|
+
<p className="text-sm text-gray-300">
|
|
268
|
+
<strong className="text-[#ff5722]">Note:</strong> This is a template for a Privacy Policy.
|
|
269
|
+
Please consult with a legal professional to ensure this policy complies with all applicable
|
|
270
|
+
privacy laws (GDPR, CCPA, etc.) for your specific service and jurisdiction. Replace all
|
|
271
|
+
placeholder text (marked with [BRACKETS]) with your actual information and adjust the content
|
|
272
|
+
based on your actual data practices.
|
|
273
|
+
</p>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|