DashCaddy.net website
18
eslint.config.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import nextTs from "eslint-config-next/typescript";
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
...nextTs,
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
]),
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
7
next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "dashcaddy-site",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stripe/stripe-js": "^9.2.0",
|
||||
"next": "16.2.4",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"stripe": "^22.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.2.4",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
7
postcss.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
1
public/file.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
1
public/globe.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
0
public/images/.gitkeep
Normal file
1
public/images/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
Place your samiahmed7777-logo.png file in this directory
|
||||
BIN
public/images/samiahmed7777-logo.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
1
public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
1
public/window.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
197
src/app/about/page.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex-1">
|
||||
{/* Hero */}
|
||||
<section className="relative pt-32 pb-20 px-4">
|
||||
<div className="absolute inset-0 hero-glow pointer-events-none" />
|
||||
<div className="max-w-4xl mx-auto text-center relative z-10">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
Built for the{" "}
|
||||
<span className="gradient-text">Self-Hosting Community</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
DashCaddy was born from the frustration of managing dozens of
|
||||
Docker containers, SSL certificates, and DNS records by hand. We
|
||||
built the tool we wished existed.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Story */}
|
||||
<section className="py-20 px-4">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Our Story</h2>
|
||||
<div className="space-y-4 text-surface-300 leading-relaxed">
|
||||
<p>
|
||||
Self-hosting is powerful. You own your data, you control your
|
||||
infrastructure, and you're not at the mercy of SaaS
|
||||
providers who can change their terms, raise prices, or shut down
|
||||
overnight. But let's be honest — it can also be a
|
||||
pain.
|
||||
</p>
|
||||
<p>
|
||||
Every new service means editing Caddyfiles, creating DNS
|
||||
records, configuring SSL certificates, writing Docker Compose
|
||||
files, and hoping everything plays nicely together. Multiply that
|
||||
by 20, 30, or 50 services, and you've got a full-time
|
||||
operations job on your hands.
|
||||
</p>
|
||||
<p>
|
||||
DashCaddy was built to solve this. One click to deploy an app.
|
||||
SSL, DNS, and reverse proxy configuration happen automatically.
|
||||
A beautiful dashboard to monitor everything. And when something
|
||||
goes wrong, you know about it immediately — not when a
|
||||
family member texts you that Plex is down.
|
||||
</p>
|
||||
<p>
|
||||
We believe self-hosting should be accessible to everyone, not
|
||||
just people who enjoy writing YAML at 2 AM. DashCaddy makes it
|
||||
beautiful and effortless.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
What We Believe In
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{[
|
||||
{
|
||||
icon: "🔓",
|
||||
title: "Open Core",
|
||||
description:
|
||||
"The core of DashCaddy is free and always will be. Premium features fund development, but the essentials are open to everyone.",
|
||||
},
|
||||
{
|
||||
icon: "🏠",
|
||||
title: "Your Data, Your Server",
|
||||
description:
|
||||
"DashCaddy runs entirely on your hardware. No cloud dependency, no telemetry, no phoning home. Your data never leaves your network.",
|
||||
},
|
||||
{
|
||||
icon: "🛠️",
|
||||
title: "Built to Last",
|
||||
description:
|
||||
"We use proven technologies — Caddy, Docker, Node.js. No bleeding-edge frameworks that break every six months. Stable, reliable, boring (in the best way).",
|
||||
},
|
||||
].map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
className="glass-card rounded-xl p-8 text-center"
|
||||
>
|
||||
<div className="text-4xl mb-4">{value.icon}</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-3">
|
||||
{value.title}
|
||||
</h3>
|
||||
<p className="text-surface-400">{value.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="text-2xl font-bold text-white mb-12 text-center">
|
||||
Built With
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{[
|
||||
{
|
||||
name: "Caddy",
|
||||
role: "Reverse Proxy & SSL",
|
||||
icon: "🔒",
|
||||
},
|
||||
{
|
||||
name: "Docker",
|
||||
role: "Container Runtime",
|
||||
icon: "🐳",
|
||||
},
|
||||
{
|
||||
name: "Node.js",
|
||||
role: "API Backend",
|
||||
icon: "🟢",
|
||||
},
|
||||
{
|
||||
name: "Technitium",
|
||||
role: "DNS Server",
|
||||
icon: "🌐",
|
||||
},
|
||||
].map((tech) => (
|
||||
<div
|
||||
key={tech.name}
|
||||
className="glass-card rounded-xl p-6 text-center hover:border-brand-500/30 transition-colors"
|
||||
>
|
||||
<div className="text-3xl mb-3">{tech.icon}</div>
|
||||
<div className="font-semibold text-white">{tech.name}</div>
|
||||
<div className="text-sm text-surface-400">{tech.role}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact / Support */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-2xl font-bold text-white mb-6">Get In Touch</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Have questions, feedback, or want to contribute? We'd love to
|
||||
hear from you.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="px-6 py-3 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-medium transition-colors"
|
||||
>
|
||||
Email Us
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
Join Discord
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-20 px-4 border-t border-surface-800">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl font-bold text-white mb-4">
|
||||
Ready to simplify your homelab?
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8">
|
||||
Start with the free tier. Upgrade when you're ready.
|
||||
</p>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-block px-8 py-4 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-semibold text-lg transition-colors"
|
||||
>
|
||||
View Pricing
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
74
src/app/api/checkout/route.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import Stripe from "stripe";
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
||||
apiVersion: "2026-03-25.dahlia",
|
||||
});
|
||||
|
||||
const PRICE_IDS: Record<string, string | undefined> = {
|
||||
monthly: process.env.STRIPE_PRICE_MONTHLY,
|
||||
yearly: process.env.STRIPE_PRICE_YEARLY,
|
||||
};
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { plan, email } = body;
|
||||
|
||||
if (!plan || !PRICE_IDS[plan]) {
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid plan. Choose 'monthly' or 'yearly'." },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const priceId = PRICE_IDS[plan];
|
||||
if (!priceId) {
|
||||
return NextResponse.json(
|
||||
{ error: "Price not configured. Please contact support." },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
const appUrl = process.env.NEXT_PUBLIC_APP_URL || "https://dashcaddy.net";
|
||||
|
||||
const sessionParams: Stripe.Checkout.SessionCreateParams = {
|
||||
mode: "subscription",
|
||||
payment_method_types: ["card"],
|
||||
line_items: [
|
||||
{
|
||||
price: priceId,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
success_url: `${appUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
|
||||
cancel_url: `${appUrl}/pricing`,
|
||||
allow_promotion_codes: true,
|
||||
billing_address_collection: "required",
|
||||
subscription_data: {
|
||||
trial_period_days: 14,
|
||||
metadata: {
|
||||
plan,
|
||||
source: "dashcaddy-website",
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
plan,
|
||||
},
|
||||
};
|
||||
|
||||
// Pre-fill email if provided
|
||||
if (email) {
|
||||
sessionParams.customer_email = email;
|
||||
}
|
||||
|
||||
const session = await stripe.checkout.sessions.create(sessionParams);
|
||||
|
||||
return NextResponse.json({ url: session.url });
|
||||
} catch (error) {
|
||||
console.error("Stripe checkout error:", error);
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Internal server error";
|
||||
return NextResponse.json({ error: message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
99
src/app/api/webhooks/stripe/route.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import Stripe from "stripe";
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
||||
apiVersion: "2026-03-25.dahlia",
|
||||
});
|
||||
|
||||
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.text();
|
||||
const signature = request.headers.get("stripe-signature");
|
||||
|
||||
if (!signature) {
|
||||
return NextResponse.json(
|
||||
{ error: "Missing stripe-signature header" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
let event: Stripe.Event;
|
||||
|
||||
try {
|
||||
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Unknown error";
|
||||
console.error(`Webhook signature verification failed: ${message}`);
|
||||
return NextResponse.json({ error: message }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
switch (event.type) {
|
||||
case "checkout.session.completed": {
|
||||
const session = event.data.object as Stripe.Checkout.Session;
|
||||
console.log("Checkout completed:", {
|
||||
sessionId: session.id,
|
||||
customerEmail: session.customer_email,
|
||||
plan: session.metadata?.plan,
|
||||
subscriptionId: session.subscription,
|
||||
});
|
||||
|
||||
// TODO: Generate and deliver license key to customer
|
||||
// This is where you'd:
|
||||
// 1. Generate a DC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX license code
|
||||
// 2. Store it in your database
|
||||
// 3. Email it to the customer
|
||||
// 4. Associate it with the Stripe subscription ID
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "customer.subscription.updated": {
|
||||
const subscription = event.data.object as Stripe.Subscription;
|
||||
console.log("Subscription updated:", {
|
||||
subscriptionId: subscription.id,
|
||||
status: subscription.status,
|
||||
});
|
||||
|
||||
// TODO: Update license expiration based on subscription status
|
||||
break;
|
||||
}
|
||||
|
||||
case "customer.subscription.deleted": {
|
||||
const subscription = event.data.object as Stripe.Subscription;
|
||||
console.log("Subscription cancelled:", {
|
||||
subscriptionId: subscription.id,
|
||||
status: subscription.status,
|
||||
});
|
||||
|
||||
// TODO: Deactivate/expire the license key
|
||||
// The DashCaddy instance will gracefully downgrade to free tier
|
||||
break;
|
||||
}
|
||||
|
||||
case "invoice.payment_failed": {
|
||||
const invoice = event.data.object as Stripe.Invoice;
|
||||
console.log("Payment failed:", {
|
||||
invoiceId: invoice.id,
|
||||
customerEmail: invoice.customer_email,
|
||||
});
|
||||
|
||||
// TODO: Notify customer about failed payment
|
||||
// Consider a grace period before deactivating license
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unhandled event type: ${event.type}`);
|
||||
}
|
||||
|
||||
return NextResponse.json({ received: true });
|
||||
} catch (error) {
|
||||
console.error("Webhook handler error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Webhook handler failed" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
389
src/app/docs/page.tsx
Normal file
@@ -0,0 +1,389 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
|
||||
export default function DocsPage() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/20 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-6">
|
||||
Getting <span className="text-brand-400">Started</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl">
|
||||
Get DashCaddy up and running on your server in just a few minutes.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content */}
|
||||
<section className="relative py-12 sm:py-16 lg:py-20">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
|
||||
{/* Table of Contents */}
|
||||
<div className="mb-16 rounded-lg border border-surface-700/50 bg-surface-800/50 p-8">
|
||||
<h2 className="text-2xl font-bold text-surface-50 mb-6">Table of Contents</h2>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a href="#prerequisites" className="text-brand-400 hover:text-brand-300 transition-colors flex items-center gap-2">
|
||||
<span>→</span> Prerequisites
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#installation" className="text-brand-400 hover:text-brand-300 transition-colors flex items-center gap-2">
|
||||
<span>→</span> Installation
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#configuration" className="text-brand-400 hover:text-brand-300 transition-colors flex items-center gap-2">
|
||||
<span>→</span> Configuration
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#first-run" className="text-brand-400 hover:text-brand-300 transition-colors flex items-center gap-2">
|
||||
<span>→</span> First Run
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#troubleshooting" className="text-brand-400 hover:text-brand-300 transition-colors flex items-center gap-2">
|
||||
<span>→</span> Troubleshooting
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Prerequisites Section */}
|
||||
<section id="prerequisites" className="mb-16">
|
||||
<h2 className="text-3xl font-bold text-surface-50 mb-6 flex items-center gap-3">
|
||||
<span className="text-brand-400">📋</span> Prerequisites
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-6 leading-relaxed">
|
||||
Before you begin, ensure you have the following installed on your server:
|
||||
</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6 space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-xl flex-shrink-0">🐳</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Docker</h3>
|
||||
<p className="text-sm text-surface-400">Version 20.10+ required. <a href="https://docs.docker.com/get-docker/" className="text-brand-400 hover:text-brand-300">Install Docker</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-xl flex-shrink-0">⚙️</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Caddy</h3>
|
||||
<p className="text-sm text-surface-400">Version 2.7+ required. <a href="https://caddyserver.com/docs/install" className="text-brand-400 hover:text-brand-300">Install Caddy</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-xl flex-shrink-0">🟢</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Node.js</h3>
|
||||
<p className="text-sm text-surface-400">Version 18+ required. <a href="https://nodejs.org/" className="text-brand-400 hover:text-brand-300">Install Node.js</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-xl flex-shrink-0">🔧</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Git</h3>
|
||||
<p className="text-sm text-surface-400">For cloning the repository. <a href="https://git-scm.com/" className="text-brand-400 hover:text-brand-300">Install Git</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-surface-700/30 pt-4 mt-4">
|
||||
<h3 className="font-semibold text-surface-50 mb-2">Optional</h3>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-xl flex-shrink-0">🌐</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Technitium DNS</h3>
|
||||
<p className="text-sm text-surface-400">For automatic DNS management. If not installed, you can still manage DNS manually.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Installation Section */}
|
||||
<section id="installation" className="mb-16">
|
||||
<h2 className="text-3xl font-bold text-surface-50 mb-6 flex items-center gap-3">
|
||||
<span className="text-brand-400">📦</span> Installation
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8 leading-relaxed">
|
||||
Follow these steps to install DashCaddy on your server:
|
||||
</p>
|
||||
|
||||
{/* Step 1 */}
|
||||
<div className="mb-10">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-4">Step 1: Clone the Repository</h3>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4">
|
||||
<code className="text-sm text-green-400 font-mono block">
|
||||
git clone https://github.com/dashcaddy/dashcaddy.git
|
||||
<br />
|
||||
cd dashcaddy
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step 2 */}
|
||||
<div className="mb-10">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-4">Step 2: Install Dependencies</h3>
|
||||
<p className="text-surface-300 mb-4">Install Node.js dependencies:</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4">
|
||||
<code className="text-sm text-green-400 font-mono block">
|
||||
npm install
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step 3 */}
|
||||
<div className="mb-10">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-4">Step 3: Configure Environment Variables</h3>
|
||||
<p className="text-surface-300 mb-4">Copy the example environment file and customize it:</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4 mb-4">
|
||||
<code className="text-sm text-green-400 font-mono block">
|
||||
cp .env.example .env
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-surface-300 mb-4">Then edit <code className="bg-surface-800 px-2 py-1 rounded text-brand-400">.env</code> with your configuration:</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4">
|
||||
<code className="text-sm text-surface-300 font-mono whitespace-pre-wrap">
|
||||
{`# Server
|
||||
PORT=3000
|
||||
NODE_ENV=production
|
||||
|
||||
# Database (optional - defaults to SQLite)
|
||||
DATABASE_URL=sqlite:./data/dashcaddy.db
|
||||
|
||||
# Security
|
||||
JWT_SECRET=your-secure-random-secret-here
|
||||
SESSION_SECRET=another-secure-random-secret
|
||||
|
||||
# Caddy
|
||||
CADDY_PORT=80
|
||||
CADDY_HTTPS_PORT=443
|
||||
CADDY_ADMIN_LISTEN=localhost:2019
|
||||
|
||||
# Technitium DNS (optional)
|
||||
TECHNITIUM_API_URL=http://localhost:5380/api
|
||||
TECHNITIUM_API_KEY=your-api-key
|
||||
|
||||
# Email (optional - for notifications)
|
||||
SMTP_HOST=smtp.example.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your-email@example.com
|
||||
SMTP_PASSWORD=your-password`}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step 4 */}
|
||||
<div className="mb-10">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-4">Step 4: Build and Start</h3>
|
||||
<p className="text-surface-300 mb-4">Build the application:</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4 mb-6">
|
||||
<code className="text-sm text-green-400 font-mono block">
|
||||
npm run build
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-surface-300 mb-4">Start DashCaddy:</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4">
|
||||
<code className="text-sm text-green-400 font-mono block">
|
||||
npm start
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-surface-300 mt-4 text-sm">
|
||||
The application will be available at <code className="bg-surface-800 px-2 py-1 rounded text-brand-400">http://localhost:3000</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Step 5 */}
|
||||
<div className="mb-10">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-4">Step 5: Configure Caddy</h3>
|
||||
<p className="text-surface-300 mb-4">Update your Caddy configuration to proxy requests to DashCaddy:</p>
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-4">
|
||||
<code className="text-sm text-surface-300 font-mono whitespace-pre-wrap">
|
||||
{`dashcaddy.local {
|
||||
reverse_proxy localhost:3000
|
||||
|
||||
# Enable automatic HTTPS
|
||||
encode gzip
|
||||
|
||||
# Security headers
|
||||
header Strict-Transport-Security "max-age=31536000"
|
||||
header X-Content-Type-Options "nosniff"
|
||||
header X-Frame-Options "DENY"
|
||||
}`}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Configuration Section */}
|
||||
<section id="configuration" className="mb-16">
|
||||
<h2 className="text-3xl font-bold text-surface-50 mb-6 flex items-center gap-3">
|
||||
<span className="text-brand-400">⚙️</span> Configuration
|
||||
</h2>
|
||||
<p className="text-surface-300 mb-8 leading-relaxed">
|
||||
Key environment variables for DashCaddy configuration:
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-brand-400 mb-2 font-mono">PORT</h3>
|
||||
<p className="text-surface-300 text-sm mb-2">The port DashCaddy runs on. Default: <code className="bg-surface-800 px-1 rounded">3000</code></p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-brand-400 mb-2 font-mono">JWT_SECRET</h3>
|
||||
<p className="text-surface-300 text-sm mb-2">Secret key for JWT tokens. Generate a secure random string:</p>
|
||||
<code className="bg-surface-800 px-2 py-1 rounded text-brand-400 text-xs">openssl rand -hex 32</code>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-brand-400 mb-2 font-mono">DATABASE_URL</h3>
|
||||
<p className="text-surface-300 text-sm">Connection string for your database. Defaults to SQLite if not provided.</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-brand-400 mb-2 font-mono">CADDY_ADMIN_LISTEN</h3>
|
||||
<p className="text-surface-300 text-sm mb-2">Caddy admin API endpoint. Default: <code className="bg-surface-800 px-1 rounded">localhost:2019</code></p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-brand-400 mb-2 font-mono">TECHNITIUM_API_URL</h3>
|
||||
<p className="text-surface-300 text-sm">URL to your Technitium DNS API. Optional for DNS management features.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* First Run Section */}
|
||||
<section id="first-run" className="mb-16">
|
||||
<h2 className="text-3xl font-bold text-surface-50 mb-6 flex items-center gap-3">
|
||||
<span className="text-brand-400">🚀</span> First Run
|
||||
</h2>
|
||||
<div className="rounded-lg border border-brand-500/30 bg-brand-950/50 p-8">
|
||||
<ol className="space-y-4 list-decimal list-inside text-surface-300">
|
||||
<li>Access the dashboard at <code className="bg-surface-800 px-2 py-1 rounded text-brand-400">http://dashcaddy.local</code></li>
|
||||
<li>Create your admin account with a strong password</li>
|
||||
<li>Enable TOTP 2FA for enhanced security</li>
|
||||
<li>Configure your Technitium DNS API key (optional)</li>
|
||||
<li>Deploy your first application from the app templates library</li>
|
||||
<li>Monitor your apps in real-time from the dashboard</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Troubleshooting Section */}
|
||||
<section id="troubleshooting" className="mb-16">
|
||||
<h2 className="text-3xl font-bold text-surface-50 mb-6 flex items-center gap-3">
|
||||
<span className="text-brand-400">🔧</span> Troubleshooting
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-surface-50 mb-3 flex items-center gap-2">
|
||||
<span className="text-red-400">❌</span> Docker daemon not running
|
||||
</h3>
|
||||
<p className="text-surface-300 text-sm mb-3">Make sure the Docker daemon is started:</p>
|
||||
<code className="bg-surface-800 px-2 py-1 rounded text-green-400 text-xs block">sudo systemctl start docker</code>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-surface-50 mb-3 flex items-center gap-2">
|
||||
<span className="text-red-400">❌</span> Port 3000 already in use
|
||||
</h3>
|
||||
<p className="text-surface-300 text-sm mb-3">Change the PORT in your .env file or stop the process using that port:</p>
|
||||
<code className="bg-surface-800 px-2 py-1 rounded text-green-400 text-xs block">lsof -i :3000</code>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-surface-50 mb-3 flex items-center gap-2">
|
||||
<span className="text-red-400">❌</span> Cannot connect to Caddy admin API
|
||||
</h3>
|
||||
<p className="text-surface-300 text-sm mb-3">Verify Caddy is running and the admin API is accessible:</p>
|
||||
<code className="bg-surface-800 px-2 py-1 rounded text-green-400 text-xs block">curl http://localhost:2019/config/</code>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-surface-50 mb-3 flex items-center gap-2">
|
||||
<span className="text-red-400">❌</span> Database connection errors
|
||||
</h3>
|
||||
<p className="text-surface-300 text-sm mb-3">Check that your DATABASE_URL is correct and the database is accessible. For SQLite, ensure the data directory exists:</p>
|
||||
<code className="bg-surface-800 px-2 py-1 rounded text-green-400 text-xs block">mkdir -p ./data</code>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-900/50 p-6">
|
||||
<h3 className="font-semibold text-surface-50 mb-3 flex items-center gap-2">
|
||||
<span className="text-red-400">❌</span> SSL certificate issues
|
||||
</h3>
|
||||
<p className="text-surface-300 text-sm mb-3">DashCaddy uses Caddy's internal CA for certificate generation. If you have issues, check the Caddy logs:</p>
|
||||
<code className="bg-surface-800 px-2 py-1 rounded text-green-400 text-xs block">journalctl -u caddy -f</code>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Next Steps */}
|
||||
<section className="mb-16">
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-800/50 p-8">
|
||||
<h2 className="text-2xl font-bold text-surface-50 mb-6">Next Steps</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<Link
|
||||
href="/features"
|
||||
className="flex items-start gap-4 p-4 rounded-lg border border-brand-500/20 bg-brand-950/30 hover:bg-brand-950/50 transition-colors"
|
||||
>
|
||||
<span className="text-2xl flex-shrink-0">✨</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Explore Features</h3>
|
||||
<p className="text-sm text-surface-400">Learn about all the powerful features DashCaddy offers.</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-start gap-4 p-4 rounded-lg border border-surface-700/50 bg-surface-800/50 hover:bg-surface-800 transition-colors"
|
||||
>
|
||||
<span className="text-2xl flex-shrink-0">📚</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">API Reference</h3>
|
||||
<p className="text-sm text-surface-400">Full API documentation for developers.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="flex items-start gap-4 p-4 rounded-lg border border-surface-700/50 bg-surface-800/50 hover:bg-surface-800 transition-colors"
|
||||
>
|
||||
<span className="text-2xl flex-shrink-0">💎</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">View Pricing</h3>
|
||||
<p className="text-sm text-surface-400">Check out our free and premium plans.</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="flex items-start gap-4 p-4 rounded-lg border border-surface-700/50 bg-surface-800/50 hover:bg-surface-800 transition-colors"
|
||||
>
|
||||
<span className="text-2xl flex-shrink-0">💬</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Get Support</h3>
|
||||
<p className="text-sm text-surface-400">Contact our support team for help.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
BIN
src/app/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
336
src/app/features/page.tsx
Normal file
@@ -0,0 +1,336 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import FeatureCard from '@/components/FeatureCard';
|
||||
|
||||
interface FeatureItem {
|
||||
icon: string;
|
||||
label: string;
|
||||
premium?: boolean;
|
||||
}
|
||||
|
||||
interface FeatureSection {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
features: FeatureItem[];
|
||||
}
|
||||
|
||||
export default function FeaturesPage() {
|
||||
const features: FeatureSection[] = [
|
||||
{
|
||||
id: 'deployment',
|
||||
title: 'App Deployment',
|
||||
description: 'Deploy your favorite applications instantly from our library of 50+ pre-configured Docker templates. No manual configuration needed—just click, deploy, and go live.',
|
||||
icon: '🚀',
|
||||
features: [
|
||||
{ icon: '⚡', label: 'One-click deployment' },
|
||||
{ icon: '📦', label: '50+ app templates' },
|
||||
{ icon: '⚙️', label: 'Auto-configuration' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ssl-security',
|
||||
title: 'SSL & Security',
|
||||
description: 'Enterprise-grade security built in. Automatic SSL certificates, TOTP 2FA, encrypted credentials, and comprehensive audit logs to track every action.',
|
||||
icon: '🔒',
|
||||
features: [
|
||||
{ icon: '🔐', label: 'Automatic SSL certificates' },
|
||||
{ icon: '📱', label: 'TOTP 2FA authentication' },
|
||||
{ icon: '🔑', label: 'Encrypted credentials' },
|
||||
{ icon: '📝', label: 'Audit logs' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dns',
|
||||
title: 'DNS Management',
|
||||
description: 'Manage all your app subdomains from one dashboard. Automatic DNS record creation with Technitium DNS integration makes domain management effortless.',
|
||||
icon: '🌐',
|
||||
features: [
|
||||
{ icon: '✨', label: 'Automatic DNS records' },
|
||||
{ icon: '🔗', label: 'Technitium integration' },
|
||||
{ icon: '📋', label: 'Subdomain management' },
|
||||
{ icon: '🎯', label: 'Zero DNS config' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'monitoring',
|
||||
title: 'Monitoring & Health',
|
||||
description: 'Real-time visibility into your infrastructure. Monitor container health, response times, and resource usage with detailed metrics and alerts.',
|
||||
icon: '📊',
|
||||
features: [
|
||||
{ icon: '🟢', label: 'Real-time status' },
|
||||
{ icon: '⏱️', label: 'Response time tracking' },
|
||||
{ icon: '💾', label: 'Resource monitoring' },
|
||||
{ icon: '📈', label: 'Performance metrics' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'docker',
|
||||
title: 'Docker Management',
|
||||
description: 'Control your entire Docker environment visually. View, manage, and scale containers, access logs, and perform updates without touching the command line.',
|
||||
icon: '🐳',
|
||||
features: [
|
||||
{ icon: '🎮', label: 'Container control' },
|
||||
{ icon: '📜', label: 'Live logs access' },
|
||||
{ icon: '🔄', label: 'Auto-updates' },
|
||||
{ icon: '📊', label: 'Resource monitoring' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'premium',
|
||||
title: 'Premium Features',
|
||||
description: 'Advanced features for power users and production deployments. SSO integration, multi-container recipes, and Docker Swarm orchestration.',
|
||||
icon: '⭐',
|
||||
features: [
|
||||
{ icon: '🔑', label: 'Auto-Login SSO', premium: true },
|
||||
{ icon: '📚', label: 'Recipes (stack deployment)', premium: true },
|
||||
{ icon: '🚀', label: 'Docker Swarm orchestration', premium: true },
|
||||
{ icon: '🚀', label: 'Priority support', premium: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/20 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-6">
|
||||
Powerful <span className="text-brand-400">Features</span> Built In
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-2xl mx-auto">
|
||||
Everything you need to manage Docker applications professionally. From deployment to monitoring, SSL to security—it's all included.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Feature Sections */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="space-y-24">
|
||||
{features.map((feature, idx) => (
|
||||
<div key={feature.id} className={`grid grid-cols-1 lg:grid-cols-2 gap-12 items-center ${idx % 2 === 1 ? 'lg:flex-row-reverse' : ''}`}>
|
||||
{/* Content Side */}
|
||||
<div className={idx % 2 === 1 ? 'lg:order-2' : ''}>
|
||||
<div className="mb-6 inline-flex rounded-lg bg-brand-500/10 p-4 text-brand-400">
|
||||
<div className="text-3xl">{feature.icon}</div>
|
||||
</div>
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-surface-50">
|
||||
{feature.title}
|
||||
</h2>
|
||||
<p className="text-lg text-surface-300 mb-8 leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{feature.features.map((item, itemIdx) => (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border transition-all ${
|
||||
item.premium
|
||||
? 'border-brand-500/30 bg-brand-500/5 hover:bg-brand-500/10'
|
||||
: 'border-surface-700/30 bg-surface-800/30 hover:border-surface-700/50'
|
||||
}`}
|
||||
>
|
||||
<span className="text-xl flex-shrink-0">{item.icon}</span>
|
||||
<span className={`text-sm font-medium ${item.premium ? 'text-brand-300' : 'text-surface-300'}`}>
|
||||
{item.label}
|
||||
{item.premium && (
|
||||
<span className="ml-1 inline-block text-xs px-2 py-0.5 bg-brand-500/20 text-brand-300 rounded font-semibold">
|
||||
Premium
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Visual Side */}
|
||||
<div className={idx % 2 === 1 ? 'lg:order-1' : ''}>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/10 to-brand-600/5 rounded-2xl blur-xl" />
|
||||
<div className="relative rounded-2xl border border-surface-700/50 bg-surface-800/50 backdrop-blur p-8">
|
||||
<div className="space-y-4">
|
||||
{feature.features.slice(0, 3).map((item, itemIdx) => (
|
||||
<div key={itemIdx} className="flex items-center gap-3 p-3 bg-surface-900/50 rounded-lg border border-surface-700/30">
|
||||
<div className="w-2 h-2 rounded-full bg-brand-400" />
|
||||
<span className="text-sm text-surface-300">{item.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 pt-6 border-t border-surface-700/30">
|
||||
<div className="text-sm text-surface-500 text-center">
|
||||
<span className="text-brand-400 font-semibold">{feature.features.length} features</span> included
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Feature Comparison Grid */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-900 to-surface-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
|
||||
What's Included in <span className="text-brand-400">Free</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
Start with the free tier and upgrade anytime when you need advanced features.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<FeatureCard
|
||||
icon="🚀"
|
||||
title="One-Click Deployment"
|
||||
description="Deploy from 50+ pre-configured templates with zero configuration."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🔒"
|
||||
title="Automatic SSL"
|
||||
description="Secure your apps with automatically renewed SSL certificates."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🌐"
|
||||
title="DNS Management"
|
||||
description="Automatic DNS record creation with Technitium integration."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="📊"
|
||||
title="Real-Time Monitoring"
|
||||
description="Track container health, response times, and resource usage."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🐳"
|
||||
title="Docker Control"
|
||||
description="Manage containers, access logs, and updates from one dashboard."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🔐"
|
||||
title="Security Built-In"
|
||||
description="TOTP 2FA, encrypted credentials, and audit logging included."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Premium Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-surface-950">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 right-0 w-96 h-96 bg-brand-600/10 rounded-full blur-3xl opacity-20" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="rounded-2xl border border-brand-500/30 bg-gradient-to-br from-brand-950/50 to-surface-900 p-12">
|
||||
<div className="max-w-3xl">
|
||||
<div className="mb-6 inline-block">
|
||||
<span className="rounded-full bg-brand-500/20 px-4 py-2 text-sm font-semibold text-brand-300">
|
||||
Premium Features
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-6 text-surface-50">
|
||||
Level Up With <span className="text-brand-400">Premium</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-300 mb-8">
|
||||
Get advanced features designed for power users and production deployments. Auto-Login SSO, stack recipes, Docker Swarm orchestration, and priority support.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div className="flex items-start gap-4 p-4 rounded-lg bg-surface-800/50 border border-surface-700/30">
|
||||
<span className="text-2xl flex-shrink-0">🔑</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Auto-Login SSO</h3>
|
||||
<p className="text-sm text-surface-400">Deploy apps with automatic single sign-on integration.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 p-4 rounded-lg bg-surface-800/50 border border-surface-700/30">
|
||||
<span className="text-2xl flex-shrink-0">📚</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Recipes</h3>
|
||||
<p className="text-sm text-surface-400">Deploy multi-container stacks with one click.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 p-4 rounded-lg bg-surface-800/50 border border-surface-700/30">
|
||||
<span className="text-2xl flex-shrink-0">🚀</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Docker Swarm</h3>
|
||||
<p className="text-sm text-surface-400">Orchestrate multi-node clusters effortlessly.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 p-4 rounded-lg bg-surface-800/50 border border-surface-700/30">
|
||||
<span className="text-2xl flex-shrink-0">⚡</span>
|
||||
<div>
|
||||
<h3 className="font-semibold text-surface-50 mb-1">Priority Support</h3>
|
||||
<p className="text-sm text-surface-400">Get faster responses from our support team.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-6 py-3 font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30"
|
||||
>
|
||||
Explore Premium Plans
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-4">
|
||||
Questions About <span className="text-brand-400">Features?</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
Check our documentation or contact support.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-8 py-3 font-semibold text-white hover:bg-brand-600 transition-all"
|
||||
>
|
||||
Read Documentation
|
||||
</Link>
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg border border-surface-700 bg-surface-800/50 px-8 py-3 font-semibold text-surface-50 hover:border-brand-400 transition-colors"
|
||||
>
|
||||
Contact Support
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
73
src/app/globals.css
Normal file
@@ -0,0 +1,73 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme inline {
|
||||
--color-brand-50: #eef6ff;
|
||||
--color-brand-100: #d9eaff;
|
||||
--color-brand-200: #bcdbff;
|
||||
--color-brand-300: #8ec5ff;
|
||||
--color-brand-400: #59a4ff;
|
||||
--color-brand-500: #3381ff;
|
||||
--color-brand-600: #1a5ff5;
|
||||
--color-brand-700: #1349e1;
|
||||
--color-brand-800: #163cb6;
|
||||
--color-brand-900: #18378f;
|
||||
--color-brand-950: #142357;
|
||||
|
||||
--color-surface-50: #f8fafc;
|
||||
--color-surface-100: #f1f5f9;
|
||||
--color-surface-200: #e2e8f0;
|
||||
--color-surface-300: #cbd5e1;
|
||||
--color-surface-400: #94a3b8;
|
||||
--color-surface-500: #64748b;
|
||||
--color-surface-600: #475569;
|
||||
--color-surface-700: #334155;
|
||||
--color-surface-800: #1e293b;
|
||||
--color-surface-900: #0f172a;
|
||||
--color-surface-950: #020617;
|
||||
|
||||
--font-sans: "Inter", system-ui, -apple-system, sans-serif;
|
||||
--font-mono: "JetBrains Mono", "Fira Code", monospace;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--color-surface-950);
|
||||
color: var(--color-surface-100);
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var(--color-brand-500);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, var(--color-brand-400), var(--color-brand-300), #a78bfa);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: rgba(30, 41, 59, 0.5);
|
||||
backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(148, 163, 184, 0.1);
|
||||
}
|
||||
|
||||
.glow-border {
|
||||
box-shadow: 0 0 0 1px rgba(51, 129, 255, 0.3),
|
||||
0 0 20px rgba(51, 129, 255, 0.1);
|
||||
}
|
||||
|
||||
.hero-glow {
|
||||
background: radial-gradient(
|
||||
ellipse 80% 60% at 50% -20%,
|
||||
rgba(51, 129, 255, 0.15),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
46
src/app/layout.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 50+ Docker apps with one click. Automatic SSL, DNS, and reverse proxy configuration. The all-in-one self-hosting dashboard.",
|
||||
keywords: [
|
||||
"self-hosting",
|
||||
"docker",
|
||||
"dashboard",
|
||||
"caddy",
|
||||
"reverse proxy",
|
||||
"ssl",
|
||||
"dns",
|
||||
"homelab",
|
||||
],
|
||||
openGraph: {
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 50+ Docker apps with one click. Automatic SSL, DNS, and reverse proxy configuration.",
|
||||
url: "https://dashcaddy.net",
|
||||
siteName: "DashCaddy",
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "DashCaddy - Self-Hosting Made Beautiful",
|
||||
description:
|
||||
"Deploy 50+ Docker apps with one click. Automatic SSL, DNS, and reverse proxy.",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className="h-full antialiased">
|
||||
<body className="min-h-full flex flex-col bg-surface-950 text-surface-100" style={{ fontFamily: "'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif" }}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
511
src/app/page.tsx
Normal file
@@ -0,0 +1,511 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import FeatureCard from '@/components/FeatureCard';
|
||||
import AppShowcase from '@/components/AppShowcase';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
{/* Navigation */}
|
||||
<Navbar />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section id="hero" className="relative w-full overflow-hidden py-20 sm:py-32 lg:py-40">
|
||||
{/* Background gradient effects */}
|
||||
<div className="absolute inset-0 -z-10">
|
||||
{/* Radial gradient glow */}
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/20 rounded-full blur-3xl opacity-30 animate-pulse-slow" />
|
||||
<div className="absolute top-1/3 right-0 w-72 h-72 bg-brand-600/10 rounded-full blur-3xl opacity-20" />
|
||||
<div className="absolute bottom-0 left-1/4 w-80 h-80 bg-brand-400/10 rounded-full blur-3xl opacity-20" />
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
<div className="flex flex-col justify-center space-y-8">
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight">
|
||||
<span className="text-brand-400">Self-Hosting</span>
|
||||
<br />
|
||||
<span>Made Beautiful</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 max-w-lg leading-relaxed">
|
||||
Deploy Docker apps with one click. Automatic SSL certificates, DNS management, and reverse proxy configuration—everything you need for self-hosted perfection.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-8 py-3 text-base font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30 hover:scale-105"
|
||||
>
|
||||
<span>Get Started Free</span>
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</Link>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg border border-surface-700 bg-surface-800/50 px-8 py-3 text-base font-semibold text-surface-50 hover:border-brand-400 hover:bg-surface-800 transition-all duration-200 hover:text-brand-400"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.6.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
View on GitHub
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Trust indicators */}
|
||||
<div className="flex flex-col sm:flex-row gap-6 pt-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex -space-x-2">
|
||||
<div className="w-8 h-8 rounded-full bg-brand-400/20 border border-brand-400 flex items-center justify-center text-xs font-semibold">👤</div>
|
||||
<div className="w-8 h-8 rounded-full bg-brand-400/20 border border-brand-400 flex items-center justify-center text-xs font-semibold">👤</div>
|
||||
<div className="w-8 h-8 rounded-full bg-brand-400/20 border border-brand-400 flex items-center justify-center text-xs font-semibold">👤</div>
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">Trusted by self-hosting enthusiasts</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right - Dashboard Mockup */}
|
||||
<div className="relative hidden lg:block">
|
||||
<div className="relative mx-auto aspect-square max-w-md">
|
||||
{/* Outer glow */}
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-br from-brand-400/20 to-brand-600/20 blur-2xl" />
|
||||
|
||||
{/* Dashboard mockup container */}
|
||||
<div className="relative rounded-2xl border border-surface-700/50 bg-gradient-to-br from-surface-800 to-surface-900 p-6 overflow-hidden">
|
||||
{/* Terminal-like dashboard header */}
|
||||
<div className="space-y-4">
|
||||
{/* Top bar */}
|
||||
<div className="flex items-center justify-between bg-surface-900/50 rounded-lg p-3 border border-surface-700/30">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/60" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/60" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/60" />
|
||||
</div>
|
||||
<span className="text-xs font-mono text-surface-500">dashcaddy.local</span>
|
||||
</div>
|
||||
|
||||
{/* Content area with animated elements */}
|
||||
<div className="space-y-3">
|
||||
{/* Container item 1 */}
|
||||
<div className="bg-surface-900/70 border border-surface-700/30 rounded p-3 animate-fade-in">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-mono text-brand-400">🐳 plex</span>
|
||||
<span className="text-xs bg-green-500/20 text-green-400 px-2 py-1 rounded">running</span>
|
||||
</div>
|
||||
<div className="w-full bg-surface-700/30 rounded-full h-1.5 overflow-hidden">
|
||||
<div className="bg-gradient-to-r from-brand-400 to-brand-500 h-full w-2/3 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Container item 2 */}
|
||||
<div className="bg-surface-900/70 border border-surface-700/30 rounded p-3 animate-fade-in" style={{ animationDelay: '0.1s' }}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-mono text-brand-400">☁️ nextcloud</span>
|
||||
<span className="text-xs bg-green-500/20 text-green-400 px-2 py-1 rounded">running</span>
|
||||
</div>
|
||||
<div className="w-full bg-surface-700/30 rounded-full h-1.5 overflow-hidden">
|
||||
<div className="bg-gradient-to-r from-brand-400 to-brand-500 h-full w-3/4 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Container item 3 */}
|
||||
<div className="bg-surface-900/70 border border-surface-700/30 rounded p-3 animate-fade-in" style={{ animationDelay: '0.2s' }}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-mono text-brand-400">📊 prometheus</span>
|
||||
<span className="text-xs bg-green-500/20 text-green-400 px-2 py-1 rounded">running</span>
|
||||
</div>
|
||||
<div className="w-full bg-surface-700/30 rounded-full h-1.5 overflow-hidden">
|
||||
<div className="bg-gradient-to-r from-brand-400 to-brand-500 h-full w-1/2 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats row */}
|
||||
<div className="grid grid-cols-3 gap-2 pt-2 border-t border-surface-700/30">
|
||||
<div className="text-center py-2">
|
||||
<div className="text-lg font-bold text-brand-400">3</div>
|
||||
<div className="text-xs text-surface-500">Containers</div>
|
||||
</div>
|
||||
<div className="text-center py-2 border-l border-r border-surface-700/30">
|
||||
<div className="text-lg font-bold text-brand-400">100%</div>
|
||||
<div className="text-xs text-surface-500">Uptime</div>
|
||||
</div>
|
||||
<div className="text-center py-2">
|
||||
<div className="text-lg font-bold text-brand-400">42GB</div>
|
||||
<div className="text-xs text-surface-500">Storage</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Key Features Grid */}
|
||||
<section id="features" className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
{/* Section Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4 text-surface-50">
|
||||
Powerful Features
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
|
||||
Everything you need to manage your self-hosted Docker applications professionally.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<FeatureCard
|
||||
icon="🚀"
|
||||
title="One-Click App Deployment"
|
||||
description="Choose from 50+ pre-configured Docker app templates. Deploy your favorite applications instantly without manual configuration."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🔒"
|
||||
title="Automatic SSL Certificates"
|
||||
description="Caddy internal CA automatically generates and renews SSL certificates. Secure your apps with zero configuration overhead."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🌐"
|
||||
title="DNS Integration"
|
||||
description="Automatic DNS record creation with Technitium DNS. Manage all your app subdomains effortlessly from one dashboard."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="📊"
|
||||
title="Real-Time Monitoring"
|
||||
description="Monitor container health, response times, and resource usage. Get instant insights into your application performance."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🐳"
|
||||
title="Docker Management"
|
||||
description="View, manage, and scale your containers. Access logs, manage volumes, and handle Docker operations visually."
|
||||
/>
|
||||
<FeatureCard
|
||||
icon="🛡️"
|
||||
title="Security First"
|
||||
description="TOTP 2FA, audit logs, encrypted credentials, and role-based access control built in."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How It Works Section */}
|
||||
<section id="how-it-works" className="relative py-16 sm:py-20 lg:py-24 bg-surface-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
{/* Section Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4 text-surface-50">
|
||||
How It Works
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
|
||||
Get up and running in minutes, not hours.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Steps */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Step 1 */}
|
||||
<div className="relative">
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
{/* Number Circle */}
|
||||
<div className="relative">
|
||||
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-brand-400/20 to-brand-600/20 border border-brand-500/30 flex items-center justify-center">
|
||||
<span className="text-2xl font-bold text-brand-400">1</span>
|
||||
</div>
|
||||
{/* Connector line (hidden on mobile, visible on larger screens) */}
|
||||
<div className="hidden md:block absolute top-8 left-16 w-full h-0.5 bg-gradient-to-r from-brand-500/50 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="text-center pt-4">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-2">
|
||||
Install
|
||||
</h3>
|
||||
<p className="text-surface-400">
|
||||
Clone the DashCaddy repository and run the setup script. Takes just a few minutes on your server.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step 2 */}
|
||||
<div className="relative">
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
{/* Number Circle */}
|
||||
<div className="relative">
|
||||
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-brand-400/20 to-brand-600/20 border border-brand-500/30 flex items-center justify-center">
|
||||
<span className="text-2xl font-bold text-brand-400">2</span>
|
||||
</div>
|
||||
{/* Connector line */}
|
||||
<div className="hidden md:block absolute top-8 left-16 w-full h-0.5 bg-gradient-to-r from-brand-500/50 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="text-center pt-4">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-2">
|
||||
Deploy
|
||||
</h3>
|
||||
<p className="text-surface-400">
|
||||
Click "Deploy" on any app template. SSL certificates and DNS records are automatically configured.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step 3 */}
|
||||
<div className="relative">
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
{/* Number Circle */}
|
||||
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-brand-400/20 to-brand-600/20 border border-brand-500/30 flex items-center justify-center">
|
||||
<span className="text-2xl font-bold text-brand-400">3</span>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="text-center pt-4">
|
||||
<h3 className="text-xl font-semibold text-surface-50 mb-2">
|
||||
Done
|
||||
</h3>
|
||||
<p className="text-surface-400">
|
||||
Your app is live with SSL and DNS configured. Monitor health, manage containers, and backup from the dashboard.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* App Showcase */}
|
||||
<AppShowcase />
|
||||
|
||||
{/* Why DashCaddy Section */}
|
||||
<section id="why-dashcaddy" className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
{/* Section Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4 text-surface-50">
|
||||
Why DashCaddy?
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
|
||||
Compare the pain of manual setup versus the simplicity of DashCaddy.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Comparison Table */}
|
||||
<div className="overflow-hidden rounded-lg border border-surface-700/50 bg-surface-800/50 backdrop-blur-sm">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-surface-700/50">
|
||||
<th className="px-6 py-4 text-left text-sm font-semibold text-surface-300 bg-surface-900/50">
|
||||
Feature
|
||||
</th>
|
||||
<th className="px-6 py-4 text-center text-sm font-semibold text-surface-300 bg-surface-900/50">
|
||||
Manual Docker Setup
|
||||
</th>
|
||||
<th className="px-6 py-4 text-center text-sm font-semibold text-brand-400 bg-surface-900/70">
|
||||
DashCaddy
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-surface-700/30">
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">SSL Certificates</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Manual configuration via Let's Encrypt</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ Automatic</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">DNS Management</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Manual DNS records</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ Automatic</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">Reverse Proxy Setup</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Complex Nginx/Caddy config</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ One-click</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">App Templates</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Write your own compose files</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ 50+ pre-configured</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">Health Monitoring</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Third-party tools required</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ Built-in</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">Backup & Restore</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Manual scripts and planning</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ One-click backup</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="hover:bg-surface-800/30 transition-colors">
|
||||
<td className="px-6 py-4 text-sm text-surface-300">Audit Logs</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-surface-500">Not included</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-center text-sm">
|
||||
<span className="text-green-400 font-semibold">✓ Built-in</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Additional Benefits */}
|
||||
<div className="mt-16 grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-800/50 backdrop-blur-sm p-6 space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-2xl">⏱️</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-surface-50 mb-2">
|
||||
Save Countless Hours
|
||||
</h3>
|
||||
<p className="text-surface-400 text-sm">
|
||||
What takes days of manual configuration takes minutes with DashCaddy's automated setup and configuration.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-800/50 backdrop-blur-sm p-6 space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-2xl">🔐</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-surface-50 mb-2">
|
||||
Enterprise Security
|
||||
</h3>
|
||||
<p className="text-surface-400 text-sm">
|
||||
TOTP 2FA, encrypted credentials, audit logging, and role-based access control built in.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-800/50 backdrop-blur-sm p-6 space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-2xl">📈</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-surface-50 mb-2">
|
||||
Scale Confidently
|
||||
</h3>
|
||||
<p className="text-surface-400 text-sm">
|
||||
Real-time monitoring and health checks give you visibility into your infrastructure 24/7.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-surface-700/50 bg-surface-800/50 backdrop-blur-sm p-6 space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-2xl">🛠️</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-surface-50 mb-2">
|
||||
Never Lose Data
|
||||
</h3>
|
||||
<p className="text-surface-400 text-sm">
|
||||
One-click backup and restore functionality ensures your data is always protected and recoverable.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Final CTA Section */}
|
||||
<section id="get-started" className="relative py-20 sm:py-24 lg:py-32 bg-gradient-to-b from-surface-900 to-surface-950 overflow-hidden">
|
||||
{/* Background effects */}
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/10 rounded-full blur-3xl opacity-20" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h2 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-6 text-surface-50">
|
||||
Ready to Transform Your
|
||||
<br />
|
||||
<span className="text-brand-400">Self-Hosting Setup?</span>
|
||||
</h2>
|
||||
<p className="text-xl text-surface-300 mb-12 max-w-2xl mx-auto leading-relaxed">
|
||||
Join developers and self-hosting enthusiasts who've simplified their infrastructure management with DashCaddy.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-10 py-4 text-lg font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30 hover:scale-105"
|
||||
>
|
||||
Start Free Today
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</Link>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg border border-surface-700 bg-surface-800/50 px-10 py-4 text-lg font-semibold text-surface-50 hover:border-brand-400 hover:bg-surface-800 transition-all duration-200 hover:text-brand-400"
|
||||
>
|
||||
View Documentation
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Trust badges */}
|
||||
<div className="mt-12 flex flex-wrap justify-center gap-8 text-center">
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-brand-400">100%</div>
|
||||
<p className="text-sm text-surface-400">Open Source</p>
|
||||
</div>
|
||||
<div className="border-l border-surface-700/50" />
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-brand-400">Self-Hosted</div>
|
||||
<p className="text-sm text-surface-400">Your Data, Your Rules</p>
|
||||
</div>
|
||||
<div className="border-l border-surface-700/50" />
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-brand-400">24/7</div>
|
||||
<p className="text-sm text-surface-400">Community Support</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
280
src/app/pricing/page.tsx
Normal file
@@ -0,0 +1,280 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
|
||||
export default function PricingPage() {
|
||||
const [isAnnual, setIsAnnual] = useState(false);
|
||||
|
||||
const plans = [
|
||||
{
|
||||
name: 'Free',
|
||||
price: '0',
|
||||
period: 'forever',
|
||||
description: 'Perfect for getting started with self-hosting',
|
||||
features: [
|
||||
'Dashboard & monitoring',
|
||||
'Up to 10 services',
|
||||
'50+ app templates',
|
||||
'Automatic SSL & DNS',
|
||||
'TOTP 2FA',
|
||||
'Community support',
|
||||
],
|
||||
cta: {
|
||||
text: 'Get Started Free',
|
||||
href: '/docs',
|
||||
},
|
||||
highlighted: false,
|
||||
},
|
||||
{
|
||||
name: 'Premium',
|
||||
price: isAnnual ? '99' : '20',
|
||||
period: isAnnual ? 'per year' : 'per month',
|
||||
savings: isAnnual ? 'Save 58%' : null,
|
||||
description: 'For power users and production deployments',
|
||||
features: [
|
||||
'Everything in Free, plus:',
|
||||
'Unlimited services',
|
||||
'Auto-Login SSO for deployed apps',
|
||||
'Recipes (multi-container stack deployment)',
|
||||
'Docker Swarm (multi-node cluster orchestration)',
|
||||
'Priority email support',
|
||||
'Early access to new features',
|
||||
],
|
||||
cta: {
|
||||
text: 'Start 14-Day Free Trial',
|
||||
href: '/api/checkout?plan=premium',
|
||||
},
|
||||
highlighted: true,
|
||||
},
|
||||
];
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
question: 'Can I try Premium for free?',
|
||||
answer: 'Yes! Premium includes a 14-day free trial. No credit card required. You can cancel anytime.',
|
||||
},
|
||||
{
|
||||
question: 'What happens when my subscription ends?',
|
||||
answer: 'Your subscription gracefully downgrades to the Free tier. All your data remains intact—no data loss. You can resubscribe at any time.',
|
||||
},
|
||||
{
|
||||
question: 'Can I self-host the license server?',
|
||||
answer: 'Coming soon! We\'re working on a self-hosted license server option for enterprise deployments.',
|
||||
},
|
||||
{
|
||||
question: 'Do you offer refunds?',
|
||||
answer: 'Yes, we offer a 30-day money-back guarantee. If you\'re not satisfied with Premium, contact support for a full refund.',
|
||||
},
|
||||
{
|
||||
question: 'Is my data safe?',
|
||||
answer: '100% self-hosted means your data never leaves your server. DashCaddy runs entirely on your infrastructure. We have no access to your applications, configurations, or data.',
|
||||
},
|
||||
];
|
||||
|
||||
const [expandedFaq, setExpandedFaq] = useState<number | null>(0);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-surface-950 text-surface-50">
|
||||
<Navbar />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24">
|
||||
<div className="absolute inset-0 -z-10">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-brand-500/20 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-6">
|
||||
Simple, Transparent <span className="text-brand-400">Pricing</span>
|
||||
</h1>
|
||||
<p className="text-xl text-surface-300 mb-8 max-w-2xl mx-auto">
|
||||
Start free. Upgrade when you need advanced features. No surprises, no lock-in.
|
||||
</p>
|
||||
|
||||
{/* Toggle for Monthly/Yearly */}
|
||||
<div className="flex items-center justify-center gap-4 mb-12">
|
||||
<span className={`text-sm font-medium ${!isAnnual ? 'text-surface-50' : 'text-surface-400'}`}>
|
||||
Monthly
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setIsAnnual(!isAnnual)}
|
||||
className={`relative inline-flex h-8 w-14 items-center rounded-full transition-colors ${
|
||||
isAnnual ? 'bg-brand-500' : 'bg-surface-700'
|
||||
}`}
|
||||
aria-label="Toggle annual pricing"
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-6 w-6 transform rounded-full bg-white transition-transform ${
|
||||
isAnnual ? 'translate-x-7' : 'translate-x-1'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<span className={`text-sm font-medium ${isAnnual ? 'text-surface-50' : 'text-surface-400'}`}>
|
||||
Annual
|
||||
</span>
|
||||
{isAnnual && (
|
||||
<span className="ml-2 inline-block rounded-full bg-brand-500/20 px-3 py-1 text-sm font-semibold text-brand-300">
|
||||
Best Value
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Pricing Cards */}
|
||||
<section className="relative py-12 sm:py-16 lg:py-20">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-5xl mx-auto">
|
||||
{plans.map((plan, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`relative rounded-2xl border transition-all duration-300 ${
|
||||
plan.highlighted
|
||||
? 'border-brand-500/50 bg-gradient-to-br from-surface-800 to-surface-900 shadow-2xl shadow-brand-500/20 scale-105 md:scale-105'
|
||||
: 'border-surface-700/50 bg-surface-800/50 hover:border-surface-700 hover:bg-surface-800/80'
|
||||
}`}
|
||||
>
|
||||
{/* Popular Badge */}
|
||||
{plan.highlighted && (
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
||||
<span className="inline-block rounded-full bg-brand-500 px-4 py-1 text-xs font-bold uppercase tracking-wide text-white">
|
||||
Most Popular
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="p-8 sm:p-10">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-2xl font-bold text-surface-50 mb-2">{plan.name}</h3>
|
||||
<p className="text-surface-400 text-sm mb-6">{plan.description}</p>
|
||||
|
||||
{/* Price */}
|
||||
<div className="flex items-baseline gap-2 mb-2">
|
||||
<span className="text-5xl font-bold text-surface-50">${plan.price}</span>
|
||||
<span className="text-surface-400">/{plan.period}</span>
|
||||
</div>
|
||||
{plan.savings && (
|
||||
<p className="text-sm text-brand-400 font-semibold">{plan.savings}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
<Link
|
||||
href={plan.cta.href}
|
||||
className={`block w-full rounded-lg px-6 py-3 text-center font-semibold transition-all duration-200 mb-8 ${
|
||||
plan.highlighted
|
||||
? 'bg-brand-500 text-white hover:bg-brand-600 hover:shadow-lg hover:shadow-brand-500/30'
|
||||
: 'border border-surface-700 bg-surface-700/50 text-surface-50 hover:border-brand-400 hover:bg-surface-700 hover:text-brand-400'
|
||||
}`}
|
||||
>
|
||||
{plan.cta.text}
|
||||
</Link>
|
||||
|
||||
{/* Features List */}
|
||||
<div className="border-t border-surface-700/50 pt-8">
|
||||
<ul className="space-y-4">
|
||||
{plan.features.map((feature, featureIdx) => (
|
||||
<li key={featureIdx} className="flex items-start gap-3">
|
||||
{feature.startsWith('Everything in') ? (
|
||||
<span className="text-sm font-semibold text-surface-300">{feature}</span>
|
||||
) : (
|
||||
<>
|
||||
<svg className="h-5 w-5 flex-shrink-0 text-green-400 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<span className="text-surface-300 text-sm">{feature}</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ Section */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold mb-4">
|
||||
Frequently Asked <span className="text-brand-400">Questions</span>
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400">
|
||||
Have a question? We've got answers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* FAQ Accordion */}
|
||||
<div className="space-y-4">
|
||||
{faqs.map((faq, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="rounded-lg border border-surface-700/50 bg-surface-800/50 overflow-hidden transition-all duration-200 hover:border-surface-700"
|
||||
>
|
||||
<button
|
||||
onClick={() => setExpandedFaq(expandedFaq === idx ? null : idx)}
|
||||
className="w-full px-6 py-4 flex items-center justify-between hover:bg-surface-800/70 transition-colors"
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-surface-50 text-left">{faq.question}</h3>
|
||||
<svg
|
||||
className={`h-6 w-6 flex-shrink-0 text-brand-400 transition-transform duration-200 ${
|
||||
expandedFaq === idx ? 'rotate-180' : ''
|
||||
}`}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2}
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{expandedFaq === idx && (
|
||||
<div className="border-t border-surface-700/50 bg-surface-900/50 px-6 py-4">
|
||||
<p className="text-surface-300 leading-relaxed">{faq.answer}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Final CTA */}
|
||||
<section className="relative py-16 sm:py-20 lg:py-24 bg-surface-950">
|
||||
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold mb-6">
|
||||
Ready to Get Started?
|
||||
</h2>
|
||||
<p className="text-xl text-surface-300 mb-8 max-w-2xl mx-auto">
|
||||
Try DashCaddy free forever or upgrade to Premium for advanced features.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-8 py-3 text-base font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30"
|
||||
>
|
||||
View Documentation
|
||||
</Link>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg border border-surface-700 bg-surface-800/50 px-8 py-3 text-base font-semibold text-surface-50 hover:border-brand-400 hover:bg-surface-800 transition-colors"
|
||||
>
|
||||
Back to Home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
121
src/app/success/page.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Suspense } from "react";
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
function SuccessContent() {
|
||||
const searchParams = useSearchParams();
|
||||
const sessionId = searchParams.get("session_id");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex-1 flex items-center justify-center px-4 py-24">
|
||||
<div className="max-w-lg w-full text-center">
|
||||
{/* Success icon */}
|
||||
<div className="mx-auto w-20 h-20 rounded-full bg-green-500/20 flex items-center justify-center mb-8">
|
||||
<svg
|
||||
className="w-10 h-10 text-green-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl font-bold text-white mb-4">
|
||||
Welcome to DashCaddy Premium!
|
||||
</h1>
|
||||
|
||||
<p className="text-lg text-surface-300 mb-8">
|
||||
Your subscription is active. Check your email for your license key
|
||||
and setup instructions. Your 14-day free trial has started.
|
||||
</p>
|
||||
|
||||
<div className="glass-card rounded-xl p-6 mb-8 text-left">
|
||||
<h2 className="text-lg font-semibold text-white mb-4">
|
||||
Next Steps
|
||||
</h2>
|
||||
<ol className="space-y-3 text-surface-300">
|
||||
<li className="flex gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-500/20 text-brand-400 text-sm flex items-center justify-center font-medium">
|
||||
1
|
||||
</span>
|
||||
<span>
|
||||
Check your email for your license key (DC-XXXXX-...)
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-500/20 text-brand-400 text-sm flex items-center justify-center font-medium">
|
||||
2
|
||||
</span>
|
||||
<span>
|
||||
Open your DashCaddy dashboard and go to Admin → License
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-500/20 text-brand-400 text-sm flex items-center justify-center font-medium">
|
||||
3
|
||||
</span>
|
||||
<span>Paste your license key and click Activate</span>
|
||||
</li>
|
||||
<li className="flex gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-500/20 text-brand-400 text-sm flex items-center justify-center font-medium">
|
||||
4
|
||||
</span>
|
||||
<span>
|
||||
Enjoy SSO, Recipes, Docker Swarm, and all premium features!
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
{sessionId && (
|
||||
<p className="text-sm text-surface-500 mb-6">
|
||||
Session ID: {sessionId.substring(0, 20)}...
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="px-6 py-3 rounded-lg bg-brand-600 hover:bg-brand-500 text-white font-medium transition-colors"
|
||||
>
|
||||
View Setup Guide
|
||||
</Link>
|
||||
<Link
|
||||
href="/"
|
||||
className="px-6 py-3 rounded-lg border border-surface-700 hover:border-surface-500 text-surface-300 font-medium transition-colors"
|
||||
>
|
||||
Back to Home
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SuccessPage() {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-surface-400">Loading...</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<SuccessContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
181
src/components/AppShowcase.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
interface App {
|
||||
name: string;
|
||||
icon: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
const apps: App[] = [
|
||||
// Media
|
||||
{ name: 'Plex', icon: '🎬', category: 'Media' },
|
||||
{ name: 'Jellyfin', icon: '📽️', category: 'Media' },
|
||||
{ name: 'Kaleidescape', icon: '🎞️', category: 'Media' },
|
||||
{ name: 'Emby', icon: '🎥', category: 'Media' },
|
||||
{ name: 'Subsonic', icon: '🎵', category: 'Media' },
|
||||
{ name: 'Synology Photos', icon: '📸', category: 'Media' },
|
||||
|
||||
// Downloads
|
||||
{ name: 'Sonarr', icon: '📺', category: 'Downloads' },
|
||||
{ name: 'Radarr', icon: '🎬', category: 'Downloads' },
|
||||
{ name: 'Lidarr', icon: '🎶', category: 'Downloads' },
|
||||
{ name: 'qBittorrent', icon: '⚡', category: 'Downloads' },
|
||||
{ name: 'Transmission', icon: '📤', category: 'Downloads' },
|
||||
{ name: 'SABnzbd', icon: '📥', category: 'Downloads' },
|
||||
|
||||
// Productivity
|
||||
{ name: 'Nextcloud', icon: '☁️', category: 'Productivity' },
|
||||
{ name: 'Vikunja', icon: '✓', category: 'Productivity' },
|
||||
{ name: 'OpenProject', icon: '📋', category: 'Productivity' },
|
||||
{ name: 'Plane', icon: '🚀', category: 'Productivity' },
|
||||
{ name: 'Actual Budget', icon: '💰', category: 'Productivity' },
|
||||
{ name: 'HedgeDoc', icon: '📝', category: 'Productivity' },
|
||||
|
||||
// Management
|
||||
{ name: 'Portainer', icon: '🐋', category: 'Management' },
|
||||
{ name: 'Homelabs', icon: '🏠', category: 'Management' },
|
||||
{ name: 'Yacht', icon: '⛵', category: 'Management' },
|
||||
{ name: 'DockSTARTer', icon: '⭐', category: 'Management' },
|
||||
{ name: 'Unraid', icon: '📦', category: 'Management' },
|
||||
{ name: 'TrueNAS', icon: '💾', category: 'Management' },
|
||||
|
||||
// Security
|
||||
{ name: 'Vaultwarden', icon: '🔐', category: 'Security' },
|
||||
{ name: 'Bitwarden', icon: '🗝️', category: 'Security' },
|
||||
{ name: 'Keycloak', icon: '🔑', category: 'Security' },
|
||||
{ name: 'Authelia', icon: '🛡️', category: 'Security' },
|
||||
{ name: 'OpenVPN', icon: '🌐', category: 'Security' },
|
||||
{ name: 'WireGuard', icon: '📡', category: 'Security' },
|
||||
|
||||
// Development
|
||||
{ name: 'Gitea', icon: '🐙', category: 'Development' },
|
||||
{ name: 'GitLab', icon: '🦊', category: 'Development' },
|
||||
{ name: 'Forgejo', icon: '🔧', category: 'Development' },
|
||||
{ name: 'Jenkins', icon: '🤖', category: 'Development' },
|
||||
{ name: 'Drone', icon: '🚁', category: 'Development' },
|
||||
{ name: 'Code Server', icon: '💻', category: 'Development' },
|
||||
|
||||
// Monitoring
|
||||
{ name: 'Grafana', icon: '📊', category: 'Monitoring' },
|
||||
{ name: 'Prometheus', icon: '⚙️', category: 'Monitoring' },
|
||||
{ name: 'Uptime Kuma', icon: '📈', category: 'Monitoring' },
|
||||
{ name: 'Netdata', icon: '🔍', category: 'Monitoring' },
|
||||
{ name: 'New Relic', icon: '👁️', category: 'Monitoring' },
|
||||
{ name: 'Kibana', icon: '📉', category: 'Monitoring' },
|
||||
|
||||
// Additional
|
||||
{ name: 'Home Assistant', icon: '🏡', category: 'Smart Home' },
|
||||
{ name: 'Node-RED', icon: '🔴', category: 'Smart Home' },
|
||||
{ name: 'OpenHAB', icon: '⚙️', category: 'Smart Home' },
|
||||
{ name: 'Immich', icon: '📷', category: 'Media' },
|
||||
{ name: 'Calibre', icon: '📚', category: 'Productivity' },
|
||||
{ name: 'Paperless', icon: '📄', category: 'Productivity' },
|
||||
];
|
||||
|
||||
const categories = [
|
||||
'All',
|
||||
'Media',
|
||||
'Downloads',
|
||||
'Productivity',
|
||||
'Management',
|
||||
'Security',
|
||||
'Development',
|
||||
'Monitoring',
|
||||
'Smart Home',
|
||||
];
|
||||
|
||||
export default function AppShowcase() {
|
||||
const [activeCategory, setActiveCategory] = useState('All');
|
||||
|
||||
const filteredApps =
|
||||
activeCategory === 'All'
|
||||
? apps
|
||||
: apps.filter((app) => app.category === activeCategory);
|
||||
|
||||
return (
|
||||
<section className="relative py-12 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-surface-950 to-surface-900">
|
||||
<div className="mx-auto max-w-7xl">
|
||||
{/* Header */}
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-surface-50 mb-4">
|
||||
50+ One-Click App Templates
|
||||
</h2>
|
||||
<p className="text-lg text-surface-400 max-w-2xl mx-auto">
|
||||
Deploy your favorite apps instantly with pre-configured templates, automatic SSL certificates, and integrated DNS management.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="mb-10 flex flex-wrap justify-center gap-2">
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
onClick={() => setActiveCategory(category)}
|
||||
className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 ${
|
||||
activeCategory === category
|
||||
? 'bg-brand-500 text-white shadow-lg shadow-brand-500/30'
|
||||
: 'bg-surface-800 text-surface-300 hover:bg-surface-700 hover:text-surface-200'
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* App Grid */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
||||
{filteredApps.map((app) => (
|
||||
<div
|
||||
key={`${app.name}-${app.category}`}
|
||||
className="group relative flex flex-col items-center justify-center rounded-lg border border-surface-700 bg-surface-800/50 backdrop-blur-sm p-4 transition-all duration-300 hover:border-brand-500/50 hover:bg-surface-800/80 hover:shadow-lg hover:shadow-brand-500/10 hover:-translate-y-1"
|
||||
>
|
||||
{/* Background gradient on hover */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-brand-500/0 group-hover:from-brand-500/5 group-hover:to-brand-500/10 rounded-lg transition-all duration-300 pointer-events-none" />
|
||||
|
||||
<div className="relative z-10 flex flex-col items-center justify-center text-center">
|
||||
{/* Icon */}
|
||||
<div className="text-4xl mb-2 transition-transform duration-300 group-hover:scale-110">
|
||||
{app.icon}
|
||||
</div>
|
||||
|
||||
{/* App Name */}
|
||||
<h3 className="text-sm font-semibold text-surface-50 line-clamp-2 group-hover:text-brand-400 transition-colors">
|
||||
{app.name}
|
||||
</h3>
|
||||
|
||||
{/* Category Tag */}
|
||||
<span className="text-xs text-surface-500 mt-1 group-hover:text-brand-400/70">
|
||||
{app.category}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="mt-16 grid grid-cols-3 gap-4 sm:gap-8">
|
||||
<div className="text-center">
|
||||
<div className="text-3xl sm:text-4xl font-bold text-brand-400 mb-2">
|
||||
50+
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">App Templates</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl sm:text-4xl font-bold text-brand-400 mb-2">
|
||||
0
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">Configuration</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-3xl sm:text-4xl font-bold text-brand-400 mb-2">
|
||||
1-Click
|
||||
</div>
|
||||
<p className="text-sm text-surface-400">Deploy</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
37
src/components/FeatureCard.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
'use client';
|
||||
|
||||
interface FeatureCardProps {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default function FeatureCard({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
}: FeatureCardProps) {
|
||||
return (
|
||||
<div className="group relative overflow-hidden rounded-lg border border-surface-700 bg-surface-800/50 backdrop-blur-sm p-6 transition-all duration-300 hover:border-brand-500/50 hover:bg-surface-800/80 hover:shadow-lg hover:shadow-brand-500/10">
|
||||
{/* Background gradient effect on hover */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-brand-500/0 group-hover:from-brand-500/5 group-hover:to-brand-500/10 transition-all duration-300 pointer-events-none" />
|
||||
|
||||
<div className="relative z-10">
|
||||
{/* Icon */}
|
||||
<div className="mb-4 inline-flex rounded-lg bg-brand-500/10 p-3 text-brand-400 group-hover:bg-brand-500/20 group-hover:text-brand-300 transition-all duration-300">
|
||||
<div className="text-2xl">{icon}</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="mb-2 text-lg font-semibold text-surface-50">
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-surface-300 text-sm leading-relaxed">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
139
src/components/Footer.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
|
||||
export default function Footer() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const footerSections = [
|
||||
{
|
||||
title: 'Product',
|
||||
links: [
|
||||
{ label: 'Features', href: '/features' },
|
||||
{ label: 'Pricing', href: '/pricing' },
|
||||
{ label: 'Documentation', href: '/docs' },
|
||||
{ label: 'About', href: '/about' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Resources',
|
||||
links: [
|
||||
{ label: 'Getting Started', href: '/docs' },
|
||||
{ label: 'API Reference', href: '/docs#api' },
|
||||
{ label: 'GitHub', href: 'https://git.dashcaddy.net/sami7777/dashcaddy' },
|
||||
{ label: 'Community', href: '#' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Support',
|
||||
links: [
|
||||
{ label: 'Contact', href: 'mailto:support@dashcaddy.net' },
|
||||
{ label: 'Discord', href: '#' },
|
||||
{ label: 'Privacy Policy', href: '#' },
|
||||
{ label: 'Terms of Service', href: '#' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const socialLinks = [
|
||||
{
|
||||
icon: (
|
||||
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.6.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
),
|
||||
label: 'GitHub',
|
||||
href: 'https://git.dashcaddy.net/sami7777/dashcaddy',
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<svg className="h-5 w-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515a.074.074 0 00-.079.037c-.211.375-.444.864-.607 1.25a18.27 18.27 0 00-5.487 0c-.163-.386-.395-.875-.607-1.25a.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.873-1.295 1.226-1.994a.076.076 0 00-.042-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.294.075.075 0 01.078-.01c3.928 1.793 8.18 1.793 12.062 0a.075.075 0 01.079.009c.12.098.246.198.373.295a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.076.076 0 00-.041.107c.359.698.77 1.364 1.225 1.994a.077.077 0 00.084.028 19.839 19.839 0 006.002-3.03.076.076 0 00.032-.057c.534-4.506-.9-8.4-3.821-11.865a.055.055 0 00-.032-.027zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.948-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.948 2.419-2.157 2.419zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.948-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.948 2.419-2.157 2.419z" />
|
||||
</svg>
|
||||
),
|
||||
label: 'Discord',
|
||||
href: '#',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<footer className="border-t border-surface-700/50 bg-surface-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 mb-8">
|
||||
{/* Brand Section */}
|
||||
<div>
|
||||
<Link href="/" className="flex items-center gap-2 font-bold text-lg text-brand-400 hover:text-brand-300 transition-colors mb-4">
|
||||
<span>DashCaddy</span>
|
||||
</Link>
|
||||
<p className="text-surface-400 text-sm leading-relaxed mb-4">
|
||||
Self-hosted Docker dashboard with automatic SSL, DNS, and reverse proxy. Making self-hosting beautiful and effortless.
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
{socialLinks.map((link) => (
|
||||
<a
|
||||
key={link.label}
|
||||
href={link.href}
|
||||
className="text-surface-400 hover:text-brand-400 transition-colors"
|
||||
aria-label={link.label}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{link.icon}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Link Sections */}
|
||||
{footerSections.map((section) => (
|
||||
<div key={section.title}>
|
||||
<h3 className="text-sm font-semibold text-surface-50 mb-4">
|
||||
{section.title}
|
||||
</h3>
|
||||
<ul className="space-y-3">
|
||||
{section.links.map((link) => (
|
||||
<li key={link.label}>
|
||||
<Link
|
||||
href={link.href}
|
||||
className="text-sm text-surface-400 hover:text-brand-400 transition-colors"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Support Link */}
|
||||
<div className="border-t border-surface-700/50 pt-8 mb-8">
|
||||
<p className="text-sm text-surface-400">
|
||||
Need help? Email us at{' '}
|
||||
<a
|
||||
href="mailto:support@dashcaddy.net"
|
||||
className="text-brand-400 hover:text-brand-300 transition-colors font-medium"
|
||||
>
|
||||
support@dashcaddy.net
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Copyright with samiahmed7777 logo */}
|
||||
<div className="border-t border-surface-700/50 pt-8">
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-3">
|
||||
<p className="text-sm text-surface-500">
|
||||
© {currentYear} DashCaddy. All rights reserved. A product by
|
||||
</p>
|
||||
<Image
|
||||
src="/images/samiahmed7777-logo.png"
|
||||
alt="samiahmed7777"
|
||||
width={160}
|
||||
height={32}
|
||||
className="h-7 w-auto opacity-80 hover:opacity-100 transition-opacity"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
98
src/components/Navbar.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function Navbar() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const navLinks = [
|
||||
{ href: '#features', label: 'Features' },
|
||||
{ href: '#pricing', label: 'Pricing' },
|
||||
{ href: '#docs', label: 'Docs' },
|
||||
{ href: '#about', label: 'About' },
|
||||
];
|
||||
|
||||
return (
|
||||
<nav className="sticky top-0 z-50 border-b border-surface-700/50 bg-surface-950/95 backdrop-blur supports-[backdrop-filter]:bg-surface-950/75">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex h-16 items-center justify-between">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex items-center gap-2 font-bold text-xl text-brand-400 hover:text-brand-300 transition-colors">
|
||||
<span>🚀</span>
|
||||
<span>DashCaddy</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<div className="hidden md:flex items-center gap-8">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="text-surface-300 hover:text-brand-400 transition-colors text-sm font-medium"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* CTA Button (Desktop) */}
|
||||
<div className="hidden md:block">
|
||||
<Link
|
||||
href="#get-started"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-600 transition-all duration-200 hover:shadow-lg hover:shadow-brand-500/30"
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="md:hidden inline-flex items-center justify-center rounded-lg p-2 text-surface-400 hover:bg-surface-800 hover:text-surface-200 transition-colors"
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
<svg
|
||||
className={`h-6 w-6 transition-transform duration-300 ${isOpen ? 'rotate-90' : ''}`}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Navigation */}
|
||||
{isOpen && (
|
||||
<div className="border-t border-surface-700/50 bg-surface-900/50 backdrop-blur md:hidden">
|
||||
<div className="space-y-1 px-2 py-4">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="block rounded-lg px-3 py-2 text-base font-medium text-surface-300 hover:bg-surface-800 hover:text-brand-400 transition-colors"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href="#get-started"
|
||||
className="block rounded-lg bg-brand-500 px-3 py-2 text-base font-medium text-white hover:bg-brand-600 transition-colors mt-4"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
72
tailwind.config.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: {
|
||||
50: "#eef6ff",
|
||||
100: "#d9eaff",
|
||||
200: "#bcdbff",
|
||||
300: "#8ec5ff",
|
||||
400: "#59a4ff",
|
||||
500: "#3381ff",
|
||||
600: "#1a5ff5",
|
||||
700: "#1349e1",
|
||||
800: "#163cb6",
|
||||
900: "#18378f",
|
||||
950: "#142357",
|
||||
},
|
||||
surface: {
|
||||
50: "#f8fafc",
|
||||
100: "#f1f5f9",
|
||||
200: "#e2e8f0",
|
||||
300: "#cbd5e1",
|
||||
400: "#94a3b8",
|
||||
500: "#64748b",
|
||||
600: "#475569",
|
||||
700: "#334155",
|
||||
800: "#1e293b",
|
||||
900: "#0f172a",
|
||||
950: "#020617",
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["Inter", "system-ui", "-apple-system", "sans-serif"],
|
||||
mono: ["JetBrains Mono", "Fira Code", "monospace"],
|
||||
},
|
||||
animation: {
|
||||
"fade-in": "fadeIn 0.5s ease-out",
|
||||
"slide-up": "slideUp 0.6s ease-out",
|
||||
"slide-in-left": "slideInLeft 0.6s ease-out",
|
||||
"pulse-slow": "pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
||||
float: "float 6s ease-in-out infinite",
|
||||
},
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
"0%": { opacity: "0" },
|
||||
"100%": { opacity: "1" },
|
||||
},
|
||||
slideUp: {
|
||||
"0%": { opacity: "0", transform: "translateY(20px)" },
|
||||
"100%": { opacity: "1", transform: "translateY(0)" },
|
||||
},
|
||||
slideInLeft: {
|
||||
"0%": { opacity: "0", transform: "translateX(-20px)" },
|
||||
"100%": { opacity: "1", transform: "translateX(0)" },
|
||||
},
|
||||
float: {
|
||||
"0%, 100%": { transform: "translateY(0)" },
|
||||
"50%": { transform: "translateY(-10px)" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
export default config;
|
||||
34
tsconfig.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts",
|
||||
"**/*.mts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||