Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(saas): Landing page and footer #16

Merged
merged 6 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion starterkits/saas/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const nextConfig = {
experimental: {
optimizePackageImports: ["lucide-react"],
},
images: { remotePatterns: [{ hostname: "fakeimg.pl" }] },
images: {
remotePatterns: [{ hostname: "fakeimg.pl" }, { hostname: "utfs.io" }],
},
typescript: {
ignoreBuildErrors: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { features, pricingPlans } from "@/config/pricing";
import { pricingFeatures, pricingPlans } from "@/config/pricing";
import { cn } from "@/lib/utils";
import type { OrgSubscription } from "@/types/org-subscription";
import { CheckIcon, XIcon } from "lucide-react";
Expand Down Expand Up @@ -44,7 +44,7 @@ export function AvailablePlans({ subscription }: AvailablePlansProps) {
</li>
))}

{features.map((feature) => (
{pricingFeatures.map((feature) => (
<li
key={feature.id}
className={cn(
Expand Down
2 changes: 1 addition & 1 deletion starterkits/saas/src/app/(app)/_components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function Sidebar({
<aside className={cn("h-full w-full")}>
<div className={cn(" flex h-16 items-center justify-between")}>
<Link
href={siteUrls.home}
href={siteUrls.dashboard.home}
className={cn("z-10 transition-transform hover:scale-90")}
>
<Icons.logo
Expand Down
34 changes: 34 additions & 0 deletions starterkits/saas/src/app/(web)/_components/background.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
type BackgroundProps = {
children: React.ReactNode;
};

export function Background({ children }: BackgroundProps) {
return (
<>
<svg
className="absolute inset-0 -z-10 h-full w-full stroke-muted-foreground/25 [mask-image:radial-gradient(100%_130%_at_top,white,transparent)]"
aria-hidden="true"
>
<defs>
<pattern
id="0787a7c5-978c-4f66-83c7-11c213f99cb7"
width={200}
height={200}
x="50%"
y={-1}
patternUnits="userSpaceOnUse"
>
<path d="M.5 200V.5H200" fill="none" />
</pattern>
</defs>
<rect
width="100%"
height="100%"
strokeWidth={0}
fill="url(#0787a7c5-978c-4f66-83c7-11c213f99cb7)"
/>
</svg>
{children}
</>
);
}
90 changes: 90 additions & 0 deletions starterkits/saas/src/app/(web)/_components/features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { type Feature, features } from "@/config/features";
import { cn } from "@/lib/utils";
import Image from "next/image";
import Balancer from "react-wrap-balancer";

export default function Features() {
return (
<section className="flex flex-col items-center justify-center gap-20 py-20">
<div className="grid gap-3">
<h2 className="text-center text-4xl font-bold text-foreground">
Starterkit Features
</h2>
<Balancer
as="p"
className="max-w-2xl text-center text-xl text-muted-foreground"
>
Starterkit features are designed to help you build a robust
and scalable SaaS project.
</Balancer>
</div>
<div className="grid max-w-6xl grid-cols-2 gap-4">
{features.map((feature, idx) => (
<FeatureCard
key={feature.title + idx}
index={idx + 1}
{...feature}
/>
))}
</div>
</section>
);
}

type FeatureCardProps = Feature & {
index: number;
};

function FeatureCard({
title,
description,
image,
imageDark,
index,
}: FeatureCardProps) {
return (
<div className="grid gap-10 rounded-[25px] border border-border p-10 transition-colors duration-300 hover:bg-muted/50 md:grid-cols-1">
<div
className={cn(
"-m-2 w-full rounded-xl bg-foreground/5 p-2 ring-1 ring-inset ring-foreground/10 lg:rounded-2xl",
index % 2 === 0 ? "order-1" : "order-2",
)}
>
<div className="relative aspect-video w-full rounded-md bg-muted">
<Image
src={image}
alt={title}
fill
className={cn(
"block rounded-md border border-border",
imageDark && "dark:hidden",
)}
priority
/>

{imageDark && (
<Image
src={imageDark}
alt={title}
fill
className="hidden rounded-md border border-border dark:block"
priority
/>
)}
</div>
</div>

<div
className={cn(
"order-1 flex flex-col gap-2",
index % 2 === 0 ? "order-2" : "order-1",
)}
>
<h3 className="text-2xl font-bold text-foreground">{title}</h3>
<Balancer as="p" className="text-base text-muted-foreground">
{description}
</Balancer>
</div>
</div>
);
}
131 changes: 131 additions & 0 deletions starterkits/saas/src/app/(web)/_components/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { ThemeToggle } from "@/components/theme-toggle";
import { buttonVariants } from "@/components/ui/button";
import { Icons } from "@/components/ui/icons";
import { navigation } from "@/config/header";
import { siteConfig } from "@/config/site";
import { siteUrls } from "@/config/urls";
import { cn } from "@/lib/utils";
import { ArrowUpRightIcon, BookOpenIcon } from "lucide-react";
import Link from "next/link";
import Balancer from "react-wrap-balancer";

export function WebFooter() {
return (
<div className="py-8">
<footer className="container grid grid-cols-2 gap-8 rounded-lg border border-border p-8">
<div className="grid place-content-between gap-2">
<div className="grid gap-2">
<Link
href={siteUrls.dashboard.home}
className={cn("z-10")}
>
<Icons.logo
className="text-xl"
iconProps={{
className: "w-6 h-6 fill-primary",
}}
/>
</Link>
<Balancer as="p" className="text-muted-foreground">
{siteConfig.description}
</Balancer>
</div>

<div className="flex items-center gap-2">
<Link
href={siteUrls.docs}
className={buttonVariants({
variant: "outline",
size: "icon",
})}
>
<BookOpenIcon className="h-4 w-4" />
</Link>
<Link
href={siteUrls.github}
className={buttonVariants({
variant: "outline",
size: "icon",
})}
target="_blank"
rel="noreferrer"
>
<Icons.gitHub className="h-4 w-4" />
</Link>
<ThemeToggle />
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-col gap-2">
<h3 className="text-sm font-semibold">Resources</h3>
{navigation.map((item) => (
<FooterLink
key={item.label}
href={item.href}
label={item.label}
external={item.external}
/>
))}
<FooterLink
href={siteUrls.github}
label="Github"
external
/>
</div>
<div className="flex flex-col gap-2">
<h3 className="text-sm font-semibold">Usefull links</h3>
{navigation.map((item) => (
<FooterLink
key={item.label}
href={item.href}
label={item.label}
external={item.external}
/>
))}
</div>
<div className="flex flex-col gap-2">
<h3 className="text-sm font-semibold">More</h3>
{navigation.map((item) => (
<FooterLink
key={item.label}
href={item.href}
label={item.label}
external={item.external}
/>
))}
</div>
</div>
</footer>
</div>
);
}

interface FooterLinkProps {
href: string;
label: string;
external?: boolean;
}

function FooterLink({ href, label, external = false }: FooterLinkProps) {
const isExternal = external || href.startsWith("http");

const externalProps = isExternal
? {
target: "_blank",
rel: "noreferrer",
}
: {};

return (
<Link
className="inline-flex items-center text-sm text-muted-foreground underline underline-offset-4 hover:text-foreground hover:no-underline"
href={href}
{...externalProps}
>
{label}
{isExternal ? (
<ArrowUpRightIcon className="ml-1 h-4 w-4 flex-shrink-0" />
) : null}
</Link>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function WebPageWrapper({
}

// This is a page heading used in all public web pages
export function WebPageHeading({
export function WebPageHeader({
title,
badge,
children,
Expand All @@ -46,7 +46,7 @@ export function WebPageHeading({
)}
<Balancer
as="h1"
className="max-w-2xl text-center font-heading text-5xl font-bold leading-none sm:text-6xl"
className="max-w-2xl text-center font-heading text-5xl font-bold leading-none sm:text-5xl"
>
{title}
</Balancer>
Expand Down
5 changes: 4 additions & 1 deletion starterkits/saas/src/app/(web)/_components/header-auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export async function HeaderAuth() {
})}
>
<span>Sign Up</span>
<span className="font-normal"> — it&apos;s free</span>
<span className="font-light italic">
{" "}
— it&apos;s free
</span>
</Link>
</Fragment>
)}
Expand Down
20 changes: 20 additions & 0 deletions starterkits/saas/src/app/(web)/_components/promotion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Balancer from "react-wrap-balancer";

export function Promotion() {
return (
<section className="flex min-h-96 w-full flex-col items-center justify-center gap-5 rounded-[26px] bg-foreground p-8 py-10 text-background">
<Balancer as="h2" className="font-heading font-bold md:text-5xl">
Launch your SaaS in just a few days 🚀
</Balancer>
<Balancer as="p" className="text-center text-xl text-background/70">
Because Rapidlaunch comes with a SaaS starter kit, Blocks and
guides, and more, you can launch your SaaS in just a few days.
Get started with our starter kits, components, building guides,
and more. Customizable.{" "}
<span className="rounded-[5px] bg-background p-1 font-semibold text-foreground">
Open Source.
</span>
</Balancer>
</section>
);
}
Loading
Loading