this is good i believe
3
.eslintrc.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
36
.gitignore
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.yarn/install-state.gz
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
36
README.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
BIN
app/favicon.ico
Normal file
After Width: | Height: | Size: 25 KiB |
202
app/globals.css
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--svg-fill: white;
|
||||||
|
--svg-background: black;
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 240 10% 3.9%;
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 240 10% 3.9%;
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 240 10% 3.9%;
|
||||||
|
--primary: 142.1 76.2% 36.3%;
|
||||||
|
--primary-foreground: 355.7 100% 97.3%;
|
||||||
|
--secondary: 240 4.8% 95.9%;
|
||||||
|
--secondary-foreground: 240 5.9% 10%;
|
||||||
|
--muted: 240 4.8% 95.9%;
|
||||||
|
--muted-foreground: 240 3.8% 46.1%;
|
||||||
|
--accent: 240 4.8% 95.9%;
|
||||||
|
--accent-foreground: 240 5.9% 10%;
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
--border: 240 5.9% 90%;
|
||||||
|
--input: 240 5.9% 90%;
|
||||||
|
--ring: 142.1 76.2% 36.3%;
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--svg-fill: black;
|
||||||
|
--svg-background: white;
|
||||||
|
--background: 20 14.3% 4.1%;
|
||||||
|
--foreground: 0 0% 95%;
|
||||||
|
--card: 24 9.8% 10%;
|
||||||
|
--card-foreground: 0 0% 95%;
|
||||||
|
--popover: 0 0% 9%;
|
||||||
|
--popover-foreground: 0 0% 95%;
|
||||||
|
--primary: 142.1 70.6% 45.3%;
|
||||||
|
--primary-foreground: 144.9 80.4% 10%;
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
--muted: 0 0% 15%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
--accent: 12 6.5% 15.1%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 85.7% 97.3%;
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
--ring: 142.4 71.8% 29.2%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-poppins);
|
||||||
|
user-select: text;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.wrapper {
|
||||||
|
@apply max-w-screen-xl lg:mx-auto p-5 md:px-10 xl:px-0 w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-center {
|
||||||
|
@apply flex justify-center items-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-between {
|
||||||
|
@apply flex justify-between items-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TYPOGRAPHY */
|
||||||
|
/* 64 */
|
||||||
|
.h1-bold {
|
||||||
|
@apply font-bold text-[40px] leading-[48px] lg:text-[48px] lg:leading-[60px] xl:text-[58px] xl:leading-[74px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 40 */
|
||||||
|
.h2-bold {
|
||||||
|
@apply font-bold text-[32px] leading-[40px] lg:text-[36px] lg:leading-[44px] xl:text-[40px] xl:leading-[48px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.h2-medium {
|
||||||
|
@apply font-medium text-[32px] leading-[40px] lg:text-[36px] lg:leading-[44px] xl:text-[40px] xl:leading-[48px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 36 */
|
||||||
|
.h3-bold {
|
||||||
|
@apply font-bold text-[28px] leading-[36px] md:text-[36px] md:leading-[44px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.h3-medium {
|
||||||
|
@apply font-medium text-[28px] leading-[36px] md:text-[36px] md:leading-[44px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 32 */
|
||||||
|
.h4-medium {
|
||||||
|
@apply font-medium text-[32px] leading-[40px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 28 */
|
||||||
|
.h5-bold {
|
||||||
|
@apply font-bold text-[28px] leading-[36px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 24 */
|
||||||
|
.p-bold-24 {
|
||||||
|
@apply font-bold text-[24px] leading-[36px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-medium-24 {
|
||||||
|
@apply font-medium text-[24px] leading-[36px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-regular-24 {
|
||||||
|
@apply font-normal text-[24px] leading-[36px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 20 */
|
||||||
|
.p-bold-20 {
|
||||||
|
@apply font-bold text-[20px] leading-[30px] tracking-[2%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-semibold-20 {
|
||||||
|
@apply text-[20px] font-semibold leading-[30px] tracking-[2%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-medium-20 {
|
||||||
|
@apply text-[20px] font-medium leading-[30px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-regular-20 {
|
||||||
|
@apply text-[20px] font-normal leading-[30px] tracking-[2%];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 18 */
|
||||||
|
.p-semibold-18 {
|
||||||
|
@apply text-[18px] font-semibold leading-[28px] tracking-[2%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-medium-18 {
|
||||||
|
@apply text-[18px] font-medium leading-[28px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-regular-18 {
|
||||||
|
@apply text-[18px] font-normal leading-[28px] tracking-[2%];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 16 */
|
||||||
|
.p-bold-16 {
|
||||||
|
@apply text-[16px] font-bold leading-[24px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-medium-16 {
|
||||||
|
@apply text-[16px] font-medium leading-[24px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-regular-16 {
|
||||||
|
@apply text-[16px] font-normal leading-[24px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 14 */
|
||||||
|
.p-semibold-14 {
|
||||||
|
@apply text-[14px] font-semibold leading-[20px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-medium-14 {
|
||||||
|
@apply text-[14px] font-medium leading-[20px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-regular-14 {
|
||||||
|
@apply text-[14px] font-normal leading-[20px];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 12 */
|
||||||
|
.p-medium-12 {
|
||||||
|
@apply text-[12px] font-medium leading-[20px];
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch {
|
||||||
|
@apply bg-gray-300 !important;
|
||||||
|
}
|
||||||
|
}
|
35
app/layout.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Poppins } from "next/font/google";
|
||||||
|
import "./globals.css";
|
||||||
|
import { ThemeProvider } from "@/components/shared/providers/themeprovider";
|
||||||
|
|
||||||
|
const poppins = Poppins({
|
||||||
|
weight: ["400", "600", "700", "900"],
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "SVRJS - A Web Server running on Nodejs",
|
||||||
|
description: "Open Source Software Library",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en" suppressHydrationWarning>
|
||||||
|
<body className={poppins.className}>
|
||||||
|
<ThemeProvider
|
||||||
|
attribute="class"
|
||||||
|
defaultTheme="dark"
|
||||||
|
enableSystem
|
||||||
|
disableTransitionOnChange
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
17
app/page.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import About from "@/components/shared/About";
|
||||||
|
import Hero from "@/components/shared/Hero";
|
||||||
|
import HowItWorks from "@/components/shared/HowItWorks";
|
||||||
|
import Navbar from "@/components/shared/Navbar";
|
||||||
|
|
||||||
|
const RootPage = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Navbar />
|
||||||
|
<Hero />
|
||||||
|
<About />
|
||||||
|
<HowItWorks />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RootPage;
|
17
components.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.ts",
|
||||||
|
"css": "app/globals.css",
|
||||||
|
"baseColor": "slate",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils"
|
||||||
|
}
|
||||||
|
}
|
42
components/shared/About.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import Image from "next/image";
|
||||||
|
import React from "react";
|
||||||
|
import Statistics from "./Statistics";
|
||||||
|
|
||||||
|
const About = () => {
|
||||||
|
return (
|
||||||
|
<section id="about" className=" container py-2 sm:py-24">
|
||||||
|
<div className="bg-accent/50 border rounded-lg py-12">
|
||||||
|
<div className="px-6 flex flex-col-reverse md:flex-row gap-8 md:gap-12">
|
||||||
|
<Image
|
||||||
|
src="/about.svg"
|
||||||
|
alt="aboutpicture"
|
||||||
|
width={300}
|
||||||
|
height={300}
|
||||||
|
className="w-[300px] object-contain rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-between">
|
||||||
|
<div className="pb-6">
|
||||||
|
<h2 className="text-3xl md:text-5xl font-bold">
|
||||||
|
About{" "}
|
||||||
|
<span className="bg-gradient-to-b from-green-200 to-primary text-transparent bg-clip-text">
|
||||||
|
SVRJS!
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg text-muted-foreground mt-4">
|
||||||
|
Host a webpage, run server-side JavaScript, use mods to expand
|
||||||
|
server functionality, or use it as a forward or reverse proxy.
|
||||||
|
SVRJS is a web server that runs on top of Node.JS, enabling
|
||||||
|
server-side JS on webpages. SVRJS also has an integrated log
|
||||||
|
viewer, log highlighter, and user management tool.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Statistics />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default About;
|
180
components/shared/Hero.tsx
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
"use client";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import HeroCards from "./HeroCards";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Check, Clipboard } from "lucide-react";
|
||||||
|
import GridPattern from "../ui/grid-pattern";
|
||||||
|
import AnimatedGradientText from "../ui/animated-gradient-text";
|
||||||
|
import { Happy_Monkey } from "next/font/google";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
const happyMonkey = Happy_Monkey({
|
||||||
|
preload: true,
|
||||||
|
weight: ["400"],
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Hero = () => {
|
||||||
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
|
const [command, setCommand] = useState(
|
||||||
|
"curl -fsSL https://downloads.svrjs.org/installer/svr.js.installer.linux.20240509.sh > /tmp/installer.sh && sudo bash /tmp/installer.sh"
|
||||||
|
);
|
||||||
|
const [selectedButton, setSelectedButton] = useState<"linux" | "docker">(
|
||||||
|
"linux"
|
||||||
|
);
|
||||||
|
|
||||||
|
const commands = {
|
||||||
|
linux:
|
||||||
|
"curl -fsSL https://downloads.svrjs.org/installer/svr.js.installer.linux.20240509.sh > /tmp/installer.sh && sudo bash /tmp/installer.sh",
|
||||||
|
docker:
|
||||||
|
"docker pull svrjs/svrjs && docker run --name mysvrjs -d -p 80:80 --restart=always svrjs/svrjs",
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyToClipboard = () => {
|
||||||
|
navigator.clipboard.writeText(command);
|
||||||
|
setIsCopied(true);
|
||||||
|
setTimeout(() => setIsCopied(false), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleButtonClick = (type: "linux" | "docker") => {
|
||||||
|
setCommand(commands[type]);
|
||||||
|
setSelectedButton(type);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative container grid lg:grid-cols-2 place-items-center py-20 md:py-24 gap-10">
|
||||||
|
{/* <GridPattern
|
||||||
|
squares={[
|
||||||
|
[4, 4],
|
||||||
|
[5, 1],
|
||||||
|
[8, 2],
|
||||||
|
[6, 6],
|
||||||
|
[10, 5],
|
||||||
|
[13, 3],
|
||||||
|
]}
|
||||||
|
className={cn(
|
||||||
|
"[mask-image:radial-gradient(300px_circle_at_center,white,transparent)]",
|
||||||
|
"inset-x-0 inset-y-[-50%] h-[200%] opacity-20"
|
||||||
|
)}
|
||||||
|
/> */}
|
||||||
|
<div className="text-center lg:text-start space-y-6">
|
||||||
|
<AnimatedGradientText className="mx-auto lg:mx-0">
|
||||||
|
🎉{" "}
|
||||||
|
<hr className="mx-2 h-4 w-[1px] shrink-0 bg-black dark:bg-gray-300" />
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
`inline animate-gradient bg-gradient-to-r from-[#235b1a] to-[#315620] dark:bg-gradient-to-r dark:from-[#6df458] dark:to-[#4c932a] bg-[length:var(--bg-size)_100%] bg-clip-text text-transparent`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Expanding server functionality
|
||||||
|
</span>
|
||||||
|
</AnimatedGradientText>
|
||||||
|
<main className="text-5xl md:text-6xl font-bold">
|
||||||
|
<h1 className="inline custom-title">
|
||||||
|
<span className="bg-gradient-to-r from-[#84F472] to-[#34a102] bg-clip-text text-transparent">
|
||||||
|
Simplify
|
||||||
|
</span>{" "}
|
||||||
|
your server logic performance
|
||||||
|
</h1>
|
||||||
|
</main>
|
||||||
|
<p className="text-lg text-muted-foreground md:w-10/12 mx-auto lg:mx-0">
|
||||||
|
SVR.JS is a web server that runs on top of Node.JS, thus enabling
|
||||||
|
server-side JavaScript on webpages. SVR.JS also has an integrated log
|
||||||
|
viewer and more...
|
||||||
|
</p>
|
||||||
|
<div className="relative mx-auto lg:mx-0 flex gap-2 flex-col-reverse lg:flex-row justify-start items-center w-fit">
|
||||||
|
<Button
|
||||||
|
className="w-fit"
|
||||||
|
onClick={copyToClipboard}
|
||||||
|
variant={!isCopied ? "secondary" : "secondary"}
|
||||||
|
>
|
||||||
|
{!isCopied ? (
|
||||||
|
<Clipboard className="w-4 h-4 mr-2" />
|
||||||
|
) : (
|
||||||
|
<Check className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
{command.slice(0, 40)}
|
||||||
|
</Button>
|
||||||
|
<p className="hidden lg:block">|</p>
|
||||||
|
<p className="block lg:hidden">or</p>
|
||||||
|
<Link className="w-full" href="/dashboard">
|
||||||
|
<Button className="w-full">Download</Button>
|
||||||
|
</Link>
|
||||||
|
<div className="pointer-events-none dark:invert -scale-x-100 absolute -bottom-14 max-lg:left-0 lg:right-20 inline-flex justify-center items-center gap-1">
|
||||||
|
<Image
|
||||||
|
src="/curly-arrow.png"
|
||||||
|
width={35}
|
||||||
|
height={35}
|
||||||
|
alt="Curly arrow"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
`mt-10 font-bold text-black -scale-x-100 text-sm ${happyMonkey.className}`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Try Now!
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center lg:justify-start justify-center gap-3 w-full">
|
||||||
|
<Button
|
||||||
|
className={`rounded-full w-12 h-12 lg:w-16 lg:h-16 ${
|
||||||
|
selectedButton === "linux"
|
||||||
|
? "bg-accent"
|
||||||
|
: "bg-primary-foreground/20"
|
||||||
|
}`}
|
||||||
|
variant={"ghost"}
|
||||||
|
onClick={() => handleButtonClick("linux")}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/linux.svg"
|
||||||
|
alt="linux"
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
className="block dark:hidden"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/linuxdark.svg"
|
||||||
|
alt="linux"
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
className="dark:block hidden"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className={`rounded-full w-12 h-12 lg:w-16 lg:h-16 ${
|
||||||
|
selectedButton === "docker"
|
||||||
|
? "bg-accent"
|
||||||
|
: "bg-primary-foreground/20"
|
||||||
|
}`}
|
||||||
|
variant={"ghost"}
|
||||||
|
onClick={() => handleButtonClick("docker")}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/docker.svg"
|
||||||
|
alt="docker"
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
className="block dark:hidden"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/dockerdark.svg"
|
||||||
|
alt="docker"
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
className="dark:block hidden"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="z-10">
|
||||||
|
<HeroCards />
|
||||||
|
</div>
|
||||||
|
<div className="shadow"></div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Hero;
|
342
components/shared/HeroCards.tsx
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
import { BorderBeam } from "@/components/ui/border-beam";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { Happy_Monkey } from "next/font/google";
|
||||||
|
import {
|
||||||
|
ArchiveRestore,
|
||||||
|
Github,
|
||||||
|
Headset,
|
||||||
|
Infinity,
|
||||||
|
LightbulbIcon,
|
||||||
|
Linkedin,
|
||||||
|
Twitter,
|
||||||
|
} from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const happyMonkey = Happy_Monkey({
|
||||||
|
preload: true,
|
||||||
|
weight: ["400"],
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const HeroCards = () => {
|
||||||
|
const cards = {
|
||||||
|
aboutCard: {
|
||||||
|
description:
|
||||||
|
"Discover more about our services and offerings. We aim to provide the best experience for our users.",
|
||||||
|
socialLinks: {
|
||||||
|
x: "https://x.com/SVR_JS",
|
||||||
|
Mastodon: "https://mastodon.social/@svrjs",
|
||||||
|
Bluesky: "https://bsky.app/profile/svrjs.org",
|
||||||
|
Odysee: "https://odysee.com/@SVRJS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pricingCard: {
|
||||||
|
planName: "Pro Plan",
|
||||||
|
badgeTitle: "Popular",
|
||||||
|
pricePerMonth: "$0",
|
||||||
|
description:
|
||||||
|
"Get the best features and priority support with our Pro Plan.",
|
||||||
|
primaryButtonText: "Download SVR Now",
|
||||||
|
onPrimaryButtonClick: () => alert("Plan chosen"),
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
title: "Unlimited Projects",
|
||||||
|
icons: <Infinity className="rounded-full" width={25} height={25} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Priority Support",
|
||||||
|
icons: (
|
||||||
|
<ArchiveRestore className="rounded-full" width={25} height={25} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Free Updates",
|
||||||
|
icons: <Headset className="rounded-full" width={25} height={25} />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
curlyText: "Best Value!",
|
||||||
|
},
|
||||||
|
serviceCard: {
|
||||||
|
title: "Our Services",
|
||||||
|
description:
|
||||||
|
"We offer a variety of services to cater to your needs, including web development, SEO, and more.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="hidden lg:flex flex-row flex-wrap gap-8 relative w-[700px] h-[500px]">
|
||||||
|
{/* Twitter First Top left */}
|
||||||
|
<Card className="absolute w-[340px] -top-[15px] drop-shadow-xl shadow-black/10 dark:shadow-white/10">
|
||||||
|
<CardHeader className="flex flex-row items-center gap-4 pb-2">
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage alt={""} src={"https://github.com/shadcn.png"} />
|
||||||
|
<AvatarFallback>Proxy</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<CardTitle className="text-lg">Proxy</CardTitle>
|
||||||
|
<CardDescription>@proxyxd_s</CardDescription>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
Svrjs has the best server side rendering{" "}
|
||||||
|
<span className="text-sky-400">#SVRJSONTOP</span>
|
||||||
|
</CardContent>
|
||||||
|
<BorderBeam className="-z-10" />
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Socials Second top right */}
|
||||||
|
<Card className="absolute right-[20px] top-4 w-80 flex flex-col justify-center items-center drop-shadow-xl shadow-black/10 dark:shadow-white/10">
|
||||||
|
<CardHeader className="flex justify-center items-center pb-2">
|
||||||
|
<CardTitle className="text-center">Socials</CardTitle>
|
||||||
|
<CardDescription className="font-medium text-primary"></CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="text-center text-muted-foreground pb-2">
|
||||||
|
<p>{cards.aboutCard.description}</p>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Link
|
||||||
|
href={cards.aboutCard.socialLinks.x}
|
||||||
|
target="_blank"
|
||||||
|
className={buttonVariants({
|
||||||
|
variant: "ghost",
|
||||||
|
size: "sm",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className="sr-only">X icon</span>
|
||||||
|
<Twitter />
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={cards.aboutCard.socialLinks.Mastodon}
|
||||||
|
target="_blank"
|
||||||
|
className={buttonVariants({
|
||||||
|
variant: "ghost",
|
||||||
|
size: "sm",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Mastodon icon</span>
|
||||||
|
<div className="hidden dark:block">
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M24.163 8.74512C24.163 3.99903 20.6083 2.60743 20.6083 2.60743C17.1206 1.20606 7.85163 1.22071 4.39739 2.60743C4.39739 2.60743 0.842704 3.99903 0.842704 8.74512C0.842704 14.3945 0.4744 21.4111 6.73556 22.8613C8.99561 23.3838 10.9376 23.4961 12.5001 23.418C15.3349 23.2813 16.9253 22.5342 16.9253 22.5342L16.8304 20.7324C16.8304 20.7324 14.8048 21.2891 12.528 21.2256C10.2735 21.1572 7.89627 21.0107 7.52797 18.5889C7.49366 18.3639 7.47688 18.1371 7.47775 17.9102C12.2545 18.9307 16.3338 18.3545 17.4554 18.2373C20.586 17.9102 23.3148 16.2207 23.6608 14.6777C24.2077 12.2461 24.163 8.74512 24.163 8.74512ZM19.9722 14.8584H17.3717V9.28223C17.3717 6.85548 13.8003 6.7627 13.8003 9.61915V12.6709H11.2166V9.61915C11.2166 6.7627 7.64516 6.85548 7.64516 9.28223V14.8584H5.03355C5.03355 8.89649 4.74337 7.63673 6.06034 6.31348C7.50565 4.90235 10.5135 4.80958 11.8527 6.61134L12.5001 7.56348L13.1474 6.61134C14.4923 4.79981 17.5057 4.91212 18.9398 6.31348C20.2623 7.64649 19.9666 8.90137 19.9666 14.8584H19.9722Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="dark:hidden block">
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M24.163 8.74512C24.163 3.99903 20.6083 2.60743 20.6083 2.60743C17.1206 1.20606 7.85163 1.22071 4.39739 2.60743C4.39739 2.60743 0.842704 3.99903 0.842704 8.74512C0.842704 14.3945 0.4744 21.4111 6.73556 22.8613C8.99561 23.3838 10.9376 23.4961 12.5001 23.418C15.3349 23.2813 16.9253 22.5342 16.9253 22.5342L16.8304 20.7324C16.8304 20.7324 14.8048 21.2891 12.528 21.2256C10.2735 21.1572 7.89627 21.0107 7.52797 18.5889C7.49366 18.3639 7.47688 18.1371 7.47775 17.9102C12.2545 18.9307 16.3338 18.3545 17.4554 18.2373C20.586 17.9102 23.3148 16.2207 23.6608 14.6777C24.2077 12.2461 24.163 8.74512 24.163 8.74512ZM19.9722 14.8584H17.3717V9.28223C17.3717 6.85548 13.8003 6.7627 13.8003 9.61915V12.6709H11.2166V9.61915C11.2166 6.7627 7.64516 6.85548 7.64516 9.28223V14.8584H5.03355C5.03355 8.89649 4.74337 7.63673 6.06034 6.31348C7.50565 4.90235 10.5135 4.80958 11.8527 6.61134L12.5001 7.56348L13.1474 6.61134C14.4923 4.79981 17.5057 4.91212 18.9398 6.31348C20.2623 7.64649 19.9666 8.90137 19.9666 14.8584H19.9722Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={cards.aboutCard.socialLinks.Bluesky}
|
||||||
|
target="_blank"
|
||||||
|
className={buttonVariants({
|
||||||
|
variant: "ghost",
|
||||||
|
size: "sm",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Bluesky icon</span>
|
||||||
|
<div className="dark:hidden block">
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 576 512"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_47_8)">
|
||||||
|
<path
|
||||||
|
d="M407.8 294.7C404.5 294.3 401.1 293.9 397.8 293.4C401.2 293.8 404.5 294.3 407.8 294.7ZM288 227.1C261.9 176.4 190.9 81.9 124.9 35.3C61.6 -9.40003 37.5 -1.70003 21.6 5.49997C3.3 13.8 0 41.9 0 58.4C0 74.9 9.1 194 15 213.9C34.5 279.6 104.1 301.8 168.2 294.6C171.5 294.1 174.8 293.7 178.2 293.2C174.9 293.7 171.6 294.2 168.2 294.6C74.3 308.6 -9.1 342.8 100.3 464.5C220.6 589.1 265.1 437.8 288 361.1C310.9 437.8 337.2 583.6 473.6 464.5C576 361.1 501.7 308.5 407.8 294.6C404.5 294.2 401.1 293.8 397.8 293.3C401.2 293.7 404.5 294.2 407.8 294.6C471.9 301.7 541.4 279.5 561 213.9C566.9 194 576 75 576 58.4C576 41.8 572.7 13.7 554.4 5.49997C538.6 -1.60003 514.4 -9.40003 451.2 35.3C385.1 81.9 314.1 176.4 288 227.1Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_47_8">
|
||||||
|
<rect width="576" height="512" fill="black" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="hidden dark:block">
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 576 512"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_47_8)">
|
||||||
|
<path
|
||||||
|
d="M407.8 294.7C404.5 294.3 401.1 293.9 397.8 293.4C401.2 293.8 404.5 294.3 407.8 294.7ZM288 227.1C261.9 176.4 190.9 81.9 124.9 35.3C61.6 -9.40003 37.5 -1.70003 21.6 5.49997C3.3 13.8 0 41.9 0 58.4C0 74.9 9.1 194 15 213.9C34.5 279.6 104.1 301.8 168.2 294.6C171.5 294.1 174.8 293.7 178.2 293.2C174.9 293.7 171.6 294.2 168.2 294.6C74.3 308.6 -9.1 342.8 100.3 464.5C220.6 589.1 265.1 437.8 288 361.1C310.9 437.8 337.2 583.6 473.6 464.5C576 361.1 501.7 308.5 407.8 294.6C404.5 294.2 401.1 293.8 397.8 293.3C401.2 293.7 404.5 294.2 407.8 294.6C471.9 301.7 541.4 279.5 561 213.9C566.9 194 576 75 576 58.4C576 41.8 572.7 13.7 554.4 5.49997C538.6 -1.60003 514.4 -9.40003 451.2 35.3C385.1 81.9 314.1 176.4 288 227.1Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_47_8">
|
||||||
|
<rect width="576" height="512" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={cards.aboutCard.socialLinks.Odysee}
|
||||||
|
target="_blank"
|
||||||
|
className={buttonVariants({
|
||||||
|
variant: "ghost",
|
||||||
|
size: "sm",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Odysee icon</span>
|
||||||
|
<div className="hidden dark:block">
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_47_10)">
|
||||||
|
<path
|
||||||
|
d="M19.8584 22.6074C17.793 24.1113 15.249 25 12.5 25C7.0752 25 2.45605 21.543 0.727539 16.709C0.844727 16.792 1.01562 16.8848 1.11328 16.9238C1.90918 17.2852 3.07129 16.6602 4.18457 15.5225C4.52148 15.1855 4.8877 14.917 5.29785 14.6777C6.19141 14.0967 7.13379 13.6621 8.14941 13.3496C8.14941 13.3496 9.23828 15.0195 10.2539 17.002C11.2695 18.9844 9.16504 19.6387 8.92578 19.6387C8.91113 19.6387 8.88672 19.6387 8.85254 19.6338C8.31543 19.6094 5.43457 19.4873 6.11816 22.1289C6.8457 24.9316 10.8789 23.916 12.9346 22.5635C14.9902 21.2109 14.4824 16.7871 14.4824 16.7871C16.4893 16.4746 17.1191 18.5986 17.3096 19.6875C17.3486 19.9121 17.3633 20.1709 17.3779 20.4443C17.4316 21.4795 17.4902 22.6709 19.1016 22.71C19.3604 22.71 19.6191 22.6709 19.8584 22.6123V22.6074ZM15.2051 21.4502C15.1074 21.4258 15.0342 21.3281 15.0586 21.2061C15.1074 21.084 15.2051 21.0352 15.3027 21.0596C15.4004 21.084 15.4736 21.2061 15.4492 21.3037C15.4248 21.4014 15.3271 21.4746 15.2051 21.4502ZM5.09766 16.7822C5.1709 16.7578 5.26855 16.8311 5.29297 16.9287C5.29297 17.0264 5.24414 17.124 5.14648 17.124C5.07324 17.1484 4.97559 17.0752 4.95117 16.9775C4.92676 16.9043 5 16.8066 5.09766 16.7822ZM22.0605 20.5566C23.8916 18.3789 25 15.5664 25 12.5C25 9.2041 23.7256 6.20605 21.6406 3.96973C21.6357 5.11719 21.3428 6.32324 20.8203 7.34863C20.2393 8.33984 18.4277 10.2246 17.4121 11.1914C17.3779 11.2061 17.3584 11.2354 17.3389 11.2598C17.3291 11.2695 17.3242 11.2793 17.3145 11.2891C17.0703 11.626 17.1191 12.1094 17.4609 12.3535C18.501 13.125 20.2148 14.5801 20.3613 15.9082C20.5322 17.6123 21.7236 19.5996 22.0313 20.1172C22.0703 20.1807 22.0947 20.2197 22.0996 20.2344C22.0996 20.3418 22.0801 20.4443 22.0605 20.5518V20.5566ZM19.0771 12.2559C19.0527 12.4023 19.126 12.5439 19.2725 12.5684C19.4189 12.5928 19.5605 12.5195 19.585 12.373C19.6094 12.2266 19.5361 12.085 19.3896 12.0605C19.2432 12.0117 19.1016 12.1094 19.0771 12.2559ZM22.0752 9.28223L21.5186 9.5459L21.3721 10.1758L21.1084 9.61914L20.4785 9.47266L21.0352 9.20898L21.1816 8.5791L21.4453 9.13574L22.0752 9.28223ZM19.3115 2.0166C18.5205 2.41699 18.2324 3.61816 17.8955 5.01465C17.8809 5.08301 17.8613 5.15137 17.8467 5.21973C17.3828 7.09961 16.3525 7.05566 15.8105 7.03613C15.7568 7.03613 15.7129 7.03125 15.6689 7.03125C15.4199 7.03125 15.376 6.83594 15.2344 6.19629C15.1074 5.60547 14.8975 4.63379 14.3604 3.09082C13.252 -0.12207 10.3223 0.678711 8.10059 2.00684C5.40039 3.62305 6.4209 6.98242 7.13379 9.30176C7.16797 9.40918 7.20215 9.5166 7.23145 9.62402C7.03613 9.81934 6.55762 9.99023 5.96191 10.2051C5.37109 10.415 4.66309 10.6689 3.99414 11.0303C2.33887 11.9092 0.561523 13.4229 0.0976562 14.0869C0.0341797 13.5645 0 13.0371 0 12.5C0 5.5957 5.5957 0 12.5 0C15.0098 0 17.3535 0.742187 19.3115 2.0166ZM2.87598 9.25781C2.80273 9.16016 2.65625 9.11133 2.56348 9.18457C2.4707 9.25781 2.41699 9.4043 2.49023 9.49707C2.56348 9.58984 2.70996 9.64355 2.80273 9.57031C2.9248 9.49707 2.94922 9.35059 2.87598 9.25781ZM15.9814 3.16895C16.0791 3.0957 16.2256 3.14453 16.2939 3.24219C16.3672 3.36426 16.3428 3.50586 16.2207 3.55469C16.123 3.62793 15.9766 3.5791 15.9082 3.48145C15.8398 3.38379 15.8838 3.2373 15.9814 3.16895ZM4.64355 5.12695C4.61914 5.2002 4.66797 5.27344 4.74121 5.27344C4.81445 5.29785 4.8877 5.24902 4.8877 5.17578C4.91211 5.10254 4.86328 5.0293 4.79004 5.0293C4.7168 5.0293 4.64355 5.05371 4.64355 5.12695ZM8.7793 5.10254C8.6084 2.99805 10.5908 2.46582 10.5908 2.46582C12.7441 1.71387 13.3252 2.75391 13.833 4.30176C14.3408 5.84961 13.9795 6.38184 11.9482 7.17773C9.91699 7.97363 8.92578 6.95801 8.7793 5.09766V5.10254ZM13.1543 5.83008H13.2031C13.3252 5.83008 13.4473 5.73242 13.4473 5.58594C13.5449 5.24902 13.4961 4.88281 13.3496 4.57031C13.2764 4.47266 13.1543 4.39941 13.0371 4.44824C12.8906 4.49707 12.8174 4.64355 12.8662 4.78516C12.9639 5.00488 13.0127 5.26855 12.9395 5.5127C12.915 5.65918 13.0127 5.80078 13.1592 5.8252L13.1543 5.83008ZM12.6709 3.79883C12.5732 3.79883 12.4756 3.75 12.4268 3.65234C12.3779 3.55469 12.3291 3.48145 12.2803 3.4082C12.1826 3.31055 12.1826 3.14453 12.2803 3.04688C12.3779 2.94922 12.5439 2.94922 12.6416 3.04688C12.7393 3.16895 12.8125 3.29102 12.8857 3.4082C12.959 3.52539 12.9102 3.69629 12.7637 3.76953C12.7344 3.76953 12.7148 3.7793 12.7002 3.78418C12.6904 3.78906 12.6807 3.79395 12.6709 3.79395V3.79883Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_47_10">
|
||||||
|
<rect width="25" height="25" fill="black" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="dark:hidden block">
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_47_10)">
|
||||||
|
<path
|
||||||
|
d="M19.8584 22.6074C17.793 24.1113 15.249 25 12.5 25C7.0752 25 2.45605 21.543 0.727539 16.709C0.844727 16.792 1.01562 16.8848 1.11328 16.9238C1.90918 17.2852 3.07129 16.6602 4.18457 15.5225C4.52148 15.1855 4.8877 14.917 5.29785 14.6777C6.19141 14.0967 7.13379 13.6621 8.14941 13.3496C8.14941 13.3496 9.23828 15.0195 10.2539 17.002C11.2695 18.9844 9.16504 19.6387 8.92578 19.6387C8.91113 19.6387 8.88672 19.6387 8.85254 19.6338C8.31543 19.6094 5.43457 19.4873 6.11816 22.1289C6.8457 24.9316 10.8789 23.916 12.9346 22.5635C14.9902 21.2109 14.4824 16.7871 14.4824 16.7871C16.4893 16.4746 17.1191 18.5986 17.3096 19.6875C17.3486 19.9121 17.3633 20.1709 17.3779 20.4443C17.4316 21.4795 17.4902 22.6709 19.1016 22.71C19.3604 22.71 19.6191 22.6709 19.8584 22.6123V22.6074ZM15.2051 21.4502C15.1074 21.4258 15.0342 21.3281 15.0586 21.2061C15.1074 21.084 15.2051 21.0352 15.3027 21.0596C15.4004 21.084 15.4736 21.2061 15.4492 21.3037C15.4248 21.4014 15.3271 21.4746 15.2051 21.4502ZM5.09766 16.7822C5.1709 16.7578 5.26855 16.8311 5.29297 16.9287C5.29297 17.0264 5.24414 17.124 5.14648 17.124C5.07324 17.1484 4.97559 17.0752 4.95117 16.9775C4.92676 16.9043 5 16.8066 5.09766 16.7822ZM22.0605 20.5566C23.8916 18.3789 25 15.5664 25 12.5C25 9.2041 23.7256 6.20605 21.6406 3.96973C21.6357 5.11719 21.3428 6.32324 20.8203 7.34863C20.2393 8.33984 18.4277 10.2246 17.4121 11.1914C17.3779 11.2061 17.3584 11.2354 17.3389 11.2598C17.3291 11.2695 17.3242 11.2793 17.3145 11.2891C17.0703 11.626 17.1191 12.1094 17.4609 12.3535C18.501 13.125 20.2148 14.5801 20.3613 15.9082C20.5322 17.6123 21.7236 19.5996 22.0313 20.1172C22.0703 20.1807 22.0947 20.2197 22.0996 20.2344C22.0996 20.3418 22.0801 20.4443 22.0605 20.5518V20.5566ZM19.0771 12.2559C19.0527 12.4023 19.126 12.5439 19.2725 12.5684C19.4189 12.5928 19.5605 12.5195 19.585 12.373C19.6094 12.2266 19.5361 12.085 19.3896 12.0605C19.2432 12.0117 19.1016 12.1094 19.0771 12.2559ZM22.0752 9.28223L21.5186 9.5459L21.3721 10.1758L21.1084 9.61914L20.4785 9.47266L21.0352 9.20898L21.1816 8.5791L21.4453 9.13574L22.0752 9.28223ZM19.3115 2.0166C18.5205 2.41699 18.2324 3.61816 17.8955 5.01465C17.8809 5.08301 17.8613 5.15137 17.8467 5.21973C17.3828 7.09961 16.3525 7.05566 15.8105 7.03613C15.7568 7.03613 15.7129 7.03125 15.6689 7.03125C15.4199 7.03125 15.376 6.83594 15.2344 6.19629C15.1074 5.60547 14.8975 4.63379 14.3604 3.09082C13.252 -0.12207 10.3223 0.678711 8.10059 2.00684C5.40039 3.62305 6.4209 6.98242 7.13379 9.30176C7.16797 9.40918 7.20215 9.5166 7.23145 9.62402C7.03613 9.81934 6.55762 9.99023 5.96191 10.2051C5.37109 10.415 4.66309 10.6689 3.99414 11.0303C2.33887 11.9092 0.561523 13.4229 0.0976562 14.0869C0.0341797 13.5645 0 13.0371 0 12.5C0 5.5957 5.5957 0 12.5 0C15.0098 0 17.3535 0.742187 19.3115 2.0166ZM2.87598 9.25781C2.80273 9.16016 2.65625 9.11133 2.56348 9.18457C2.4707 9.25781 2.41699 9.4043 2.49023 9.49707C2.56348 9.58984 2.70996 9.64355 2.80273 9.57031C2.9248 9.49707 2.94922 9.35059 2.87598 9.25781ZM15.9814 3.16895C16.0791 3.0957 16.2256 3.14453 16.2939 3.24219C16.3672 3.36426 16.3428 3.50586 16.2207 3.55469C16.123 3.62793 15.9766 3.5791 15.9082 3.48145C15.8398 3.38379 15.8838 3.2373 15.9814 3.16895ZM4.64355 5.12695C4.61914 5.2002 4.66797 5.27344 4.74121 5.27344C4.81445 5.29785 4.8877 5.24902 4.8877 5.17578C4.91211 5.10254 4.86328 5.0293 4.79004 5.0293C4.7168 5.0293 4.64355 5.05371 4.64355 5.12695ZM8.7793 5.10254C8.6084 2.99805 10.5908 2.46582 10.5908 2.46582C12.7441 1.71387 13.3252 2.75391 13.833 4.30176C14.3408 5.84961 13.9795 6.38184 11.9482 7.17773C9.91699 7.97363 8.92578 6.95801 8.7793 5.09766V5.10254ZM13.1543 5.83008H13.2031C13.3252 5.83008 13.4473 5.73242 13.4473 5.58594C13.5449 5.24902 13.4961 4.88281 13.3496 4.57031C13.2764 4.47266 13.1543 4.39941 13.0371 4.44824C12.8906 4.49707 12.8174 4.64355 12.8662 4.78516C12.9639 5.00488 13.0127 5.26855 12.9395 5.5127C12.915 5.65918 13.0127 5.80078 13.1592 5.8252L13.1543 5.83008ZM12.6709 3.79883C12.5732 3.79883 12.4756 3.75 12.4268 3.65234C12.3779 3.55469 12.3291 3.48145 12.2803 3.4082C12.1826 3.31055 12.1826 3.14453 12.2803 3.04688C12.3779 2.94922 12.5439 2.94922 12.6416 3.04688C12.7393 3.16895 12.8125 3.29102 12.8857 3.4082C12.959 3.52539 12.9102 3.69629 12.7637 3.76953C12.7344 3.76953 12.7148 3.7793 12.7002 3.78418C12.6904 3.78906 12.6807 3.79395 12.6709 3.79395V3.79883Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_47_10">
|
||||||
|
<rect width="25" height="25" fill="black" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</CardFooter>
|
||||||
|
<BorderBeam className="-z-10" />
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Pricings Bottom left */}
|
||||||
|
<Card className="absolute top-[170px] left-[50px] w-72 drop-shadow-xl shadow-black/10 dark:shadow-white/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center justify-between">
|
||||||
|
{cards.pricingCard.planName}
|
||||||
|
<Badge variant="secondary" className="text-sm text-primary">
|
||||||
|
{cards.pricingCard.badgeTitle}
|
||||||
|
</Badge>
|
||||||
|
</CardTitle>
|
||||||
|
<div>
|
||||||
|
<span className="text-3xl font-bold">
|
||||||
|
{cards.pricingCard.pricePerMonth}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground"> /month</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CardDescription>{cards.pricingCard.description}</CardDescription>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className="w-full"
|
||||||
|
onClick={cards.pricingCard.onPrimaryButtonClick}
|
||||||
|
>
|
||||||
|
{cards.pricingCard.primaryButtonText}
|
||||||
|
</Button>
|
||||||
|
</CardHeader>
|
||||||
|
<hr className="w-4/5 m-auto -mt-2 mb-4" />
|
||||||
|
<CardFooter className="flex">
|
||||||
|
<div className="space-y-3">
|
||||||
|
{cards.pricingCard.features.map((benefit) => (
|
||||||
|
<span
|
||||||
|
key={benefit.title}
|
||||||
|
className="inline-flex justify-center items-center gap-x-3"
|
||||||
|
>
|
||||||
|
{benefit.icons}
|
||||||
|
<h3>{benefit.title}</h3>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardFooter>
|
||||||
|
<BorderBeam className="-z-10" />
|
||||||
|
<div className="pointer-events-none dark:invert -scale-x-100 absolute w-36 top-[9.5rem] -left-[7.5rem] inline-flex justify-center items-center gap-1">
|
||||||
|
<Image
|
||||||
|
src="/curly-arrow.png"
|
||||||
|
width={35}
|
||||||
|
height={35}
|
||||||
|
alt="Curly arrow"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
style={happyMonkey.style}
|
||||||
|
className="mt-10 font-bold text-black -scale-x-100 text-sm"
|
||||||
|
>
|
||||||
|
{cards.pricingCard.curlyText}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
{/* Service */}
|
||||||
|
<Card className="absolute w-[350px] -right-[10px] bottom-[75px] drop-shadow-xl shadow-black/10 dark:shadow-white/10">
|
||||||
|
<CardHeader className="space-y-1 flex md:flex-row justify-start items-start gap-4">
|
||||||
|
<div className="mt-1 bg-primary/20 p-1 rounded-2xl">
|
||||||
|
<LightbulbIcon className="fill-black dark:fill-[#F596D3]" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<CardTitle>{cards.serviceCard.title}</CardTitle>
|
||||||
|
<CardDescription className="text-md mt-2">
|
||||||
|
{cards.serviceCard.description}
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<BorderBeam className="-z-10" />
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeroCards;
|
16
components/shared/HowItWorks.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const HowItWorks = () => {
|
||||||
|
return (
|
||||||
|
<section className="container text-center py-12 sm:py-24">
|
||||||
|
<h2 className="text-3xl md:text-5xl font-bold">
|
||||||
|
Accelerate your{" "}
|
||||||
|
<span className="bg-gradient-to-b from-green-200 to-primary text-transparent bg-clip-text">
|
||||||
|
development
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HowItWorks;
|
73
components/shared/MobileNav.tsx
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import {
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetHeader,
|
||||||
|
SheetTitle,
|
||||||
|
SheetTrigger,
|
||||||
|
} from "../ui/sheet";
|
||||||
|
import { Menu } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
import ThemeToggle from "../ui/theme-toggle";
|
||||||
|
import { NAVBAR } from "@/constants";
|
||||||
|
import { buttonVariants } from "../ui/button";
|
||||||
|
|
||||||
|
const MobileNav = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex md:hidden">
|
||||||
|
<ThemeToggle />
|
||||||
|
<Sheet>
|
||||||
|
<SheetTrigger>
|
||||||
|
<Menu className="w-5 h-5" />
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent>
|
||||||
|
<SheetHeader>
|
||||||
|
<SheetTitle>
|
||||||
|
<Image
|
||||||
|
src="/logo.svg"
|
||||||
|
alt={`logo`}
|
||||||
|
width={120}
|
||||||
|
height={40}
|
||||||
|
className="dark:block hidden"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/logodark.svg"
|
||||||
|
alt={`logo`}
|
||||||
|
width={120}
|
||||||
|
height={40}
|
||||||
|
className="dark:hidden block"
|
||||||
|
/>
|
||||||
|
</SheetTitle>
|
||||||
|
</SheetHeader>
|
||||||
|
<nav
|
||||||
|
id="mobile-menu"
|
||||||
|
className="flex flex-col justify-center items-center gap-2 mt-4"
|
||||||
|
>
|
||||||
|
{NAVBAR.centerLinks?.map(({ href = "", label, target }) => (
|
||||||
|
<Link
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
target={target}
|
||||||
|
className={buttonVariants({ variant: "ghost" })}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
{NAVBAR.rightLinks?.map(({ href = "", label, target }) => (
|
||||||
|
<Link
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
target={target}
|
||||||
|
className={`w-[110px] ${buttonVariants({
|
||||||
|
variant: "secondary",
|
||||||
|
})}`}
|
||||||
|
></Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MobileNav;
|
95
components/shared/Navbar.tsx
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuList,
|
||||||
|
} from "@radix-ui/react-navigation-menu";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
import ThemeToggle from "../ui/theme-toggle";
|
||||||
|
import { NAVBAR } from "@/constants";
|
||||||
|
import { buttonVariants } from "../ui/button";
|
||||||
|
import MobileNav from "./MobileNav";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
const Navbar = () => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
return (
|
||||||
|
<header className="sticky border-b top-0 z-40 w-full shadow-md bg-white dark:border-b-slate-800 dark:bg-background">
|
||||||
|
{/* LOGO LEFT NAVBAR */}
|
||||||
|
<NavigationMenu className="mx-auto">
|
||||||
|
<NavigationMenuList className="container h-16 px-4 w-screen flex justify-between items-center">
|
||||||
|
<NavigationMenuItem className="font-bold flex items-center">
|
||||||
|
<Link href="/#" className="inline-flex items-center gap-2">
|
||||||
|
<Image
|
||||||
|
src="/logo.svg"
|
||||||
|
alt={`logo`}
|
||||||
|
width={120}
|
||||||
|
height={40}
|
||||||
|
className="dark:block hidden"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/logodark.svg"
|
||||||
|
alt={`logo`}
|
||||||
|
width={120}
|
||||||
|
height={40}
|
||||||
|
className="dark:hidden block"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
|
||||||
|
{/* Mobile view */}
|
||||||
|
<MobileNav />
|
||||||
|
|
||||||
|
{/* Desktop Menu */}
|
||||||
|
<nav className="hidden md:flex gap-4">
|
||||||
|
{NAVBAR.centerLinks?.map(({ href, label, target }) => (
|
||||||
|
<Link
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
target={target}
|
||||||
|
className={`text-[18px] tracking-tight ${
|
||||||
|
pathname == href ? "bg-accent/40" : ""
|
||||||
|
} ${buttonVariants({
|
||||||
|
variant: "ghost",
|
||||||
|
})}`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="hidden md:flex gap-2 items-center">
|
||||||
|
{NAVBAR.rightLinks?.map(({ href = "", label, target }) => (
|
||||||
|
<Link
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
target={target}
|
||||||
|
className={`border ${buttonVariants({ variant: "ghost" })}`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/next.svg"
|
||||||
|
alt="git"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
className="hidden dark:block"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src="/nextdark.svg"
|
||||||
|
alt="git"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
className="block dark:hidden"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
|
</NavigationMenuList>
|
||||||
|
</NavigationMenu>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
22
components/shared/Statistics.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { stats } from "@/constants";
|
||||||
|
import NumTicker from "../widgets/num-tick";
|
||||||
|
|
||||||
|
const Statistics = () => {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8">
|
||||||
|
{stats.map(({ title, count }) => (
|
||||||
|
<div key={title} className="space-y-2 text-center">
|
||||||
|
<h2 className="text-3xl sm:text-4xl font-bold">
|
||||||
|
{count}
|
||||||
|
{/* <NumTicker value={count} /> */}
|
||||||
|
</h2>
|
||||||
|
<p className="text-xl text-muted-foreground">{title}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Statistics;
|
9
components/shared/providers/themeprovider.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { ThemeProvider as NextThemesProvider } from "next-themes"
|
||||||
|
import { type ThemeProviderProps } from "next-themes/dist/types"
|
||||||
|
|
||||||
|
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||||
|
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
|
||||||
|
}
|
25
components/ui/animated-gradient-text.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export default function AnimatedGradientText({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"group relative mx-auto flex max-w-fit flex-row items-center justify-center rounded-2xl bg-white/40 px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#8fdfff1f] backdrop-blur-sm transition-shadow duration-500 ease-out [--bg-size:300%] hover:shadow-[inset_0_-5px_10px_#8fdfff3f] dark:bg-black/40",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`direction-reverse absolute inset-0 block h-full w-full animate-gradient bg-gradient-to-r from-[#26751a] to-[#154300] dark:bg-gradient-to-r dark:from-[#aafc9e]/80 dark:to-[#97fd67]/30 bg-[length:var(--bg-size)_100%] p-[1px] ![mask-composite:subtract] [border-radius:inherit] [mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
39
components/ui/animated-shiny-text.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { CSSProperties, FC, ReactNode } from "react";
|
||||||
|
|
||||||
|
interface AnimatedShinyTextProps {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
shimmerWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimatedShinyText: FC<AnimatedShinyTextProps> = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
shimmerWidth = 100,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--shimmer-width": `${shimmerWidth}px`,
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
className={cn(
|
||||||
|
"mx-auto max-w-md text-neutral-600/50 dark:text-neutral-400/50 ",
|
||||||
|
|
||||||
|
// Shimmer effect
|
||||||
|
"animate-shimmer bg-clip-text bg-no-repeat [background-position:0_0] [background-size:var(--shimmer-width)_100%] [transition:background-position_1s_cubic-bezier(.6,.6,0,1)_infinite]",
|
||||||
|
|
||||||
|
// Shimmer gradient
|
||||||
|
"bg-gradient-to-r from-transparent via-black/80 via-50% to-transparent dark:via-white/80",
|
||||||
|
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AnimatedShinyText;
|
50
components/ui/avatar.tsx
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const Avatar = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AvatarPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
const AvatarImage = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AvatarPrimitive.Image
|
||||||
|
ref={ref}
|
||||||
|
className={cn("aspect-square h-full w-full", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
||||||
|
|
||||||
|
const AvatarFallback = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AvatarPrimitive.Fallback
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
||||||
|
|
||||||
|
export { Avatar, AvatarImage, AvatarFallback };
|
36
components/ui/badge.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||||
|
secondary:
|
||||||
|
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
destructive:
|
||||||
|
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||||
|
outline: "text-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface BadgeProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants };
|
49
components/ui/border-beam.tsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
interface BorderBeamProps {
|
||||||
|
className?: string;
|
||||||
|
size?: number;
|
||||||
|
duration?: number;
|
||||||
|
borderWidth?: number;
|
||||||
|
anchor?: number;
|
||||||
|
colorFrom?: string;
|
||||||
|
colorTo?: string;
|
||||||
|
delay?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BorderBeam = ({
|
||||||
|
className,
|
||||||
|
size = 200,
|
||||||
|
duration = 15,
|
||||||
|
anchor = 90,
|
||||||
|
borderWidth = 1.5,
|
||||||
|
colorFrom = "#8803AF",
|
||||||
|
colorTo = "#61DAFB",
|
||||||
|
delay = 0,
|
||||||
|
}: BorderBeamProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--size": size,
|
||||||
|
"--duration": duration,
|
||||||
|
"--anchor": anchor,
|
||||||
|
"--border-width": borderWidth,
|
||||||
|
"--color-from": colorFrom,
|
||||||
|
"--color-to": colorTo,
|
||||||
|
"--delay": `-${delay}s`,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
className={cn(
|
||||||
|
"absolute inset-[0] rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent] opacity-10",
|
||||||
|
|
||||||
|
// mask styles
|
||||||
|
"![mask-clip:padding-box,border-box] ![mask-composite:intersect] [mask:linear-gradient(transparent,transparent),linear-gradient(white,white)]",
|
||||||
|
|
||||||
|
// pseudo styles
|
||||||
|
"after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] after:animate-border-beam after:[animation-delay:var(--delay)] after:[background:linear-gradient(to_left,var(--color-from),var(--color-to),transparent)] after:[offset-anchor:calc(var(--anchor)*1%)_50%] after:[offset-path:rect(0_auto_auto_0_round_calc(var(--size)*1px))]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
56
components/ui/button.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||||
|
outline:
|
||||||
|
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-10 px-4 py-2",
|
||||||
|
sm: "h-9 rounded-md px-3",
|
||||||
|
lg: "h-11 rounded-md px-8",
|
||||||
|
icon: "h-10 w-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Button.displayName = "Button"
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
86
components/ui/card.tsx
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Card.displayName = "Card";
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardHeader.displayName = "CardHeader";
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h3
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-2xl font-semibold leading-none tracking-tight",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardTitle.displayName = "CardTitle";
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardDescription.displayName = "CardDescription";
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||||
|
));
|
||||||
|
CardContent.displayName = "CardContent";
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex items-center p-6 pt-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardFooter.displayName = "CardFooter";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardFooter,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
};
|
200
components/ui/dropdown-menu.tsx
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
||||||
|
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||||
|
|
||||||
|
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||||
|
|
||||||
|
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||||
|
|
||||||
|
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||||
|
|
||||||
|
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||||
|
|
||||||
|
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||||
|
|
||||||
|
const DropdownMenuSubTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||||
|
inset?: boolean
|
||||||
|
}
|
||||||
|
>(({ className, inset, children, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<ChevronRight className="ml-auto h-4 w-4" />
|
||||||
|
</DropdownMenuPrimitive.SubTrigger>
|
||||||
|
))
|
||||||
|
DropdownMenuSubTrigger.displayName =
|
||||||
|
DropdownMenuPrimitive.SubTrigger.displayName
|
||||||
|
|
||||||
|
const DropdownMenuSubContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.SubContent
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DropdownMenuSubContent.displayName =
|
||||||
|
DropdownMenuPrimitive.SubContent.displayName
|
||||||
|
|
||||||
|
const DropdownMenuContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||||
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.Portal>
|
||||||
|
<DropdownMenuPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</DropdownMenuPrimitive.Portal>
|
||||||
|
))
|
||||||
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const DropdownMenuItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||||
|
inset?: boolean
|
||||||
|
}
|
||||||
|
>(({ className, inset, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||||
|
|
||||||
|
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||||
|
>(({ className, children, checked, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
checked={checked}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
|
<Check className="h-4 w-4" />
|
||||||
|
</DropdownMenuPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
{children}
|
||||||
|
</DropdownMenuPrimitive.CheckboxItem>
|
||||||
|
))
|
||||||
|
DropdownMenuCheckboxItem.displayName =
|
||||||
|
DropdownMenuPrimitive.CheckboxItem.displayName
|
||||||
|
|
||||||
|
const DropdownMenuRadioItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.RadioItem
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
|
<Circle className="h-2 w-2 fill-current" />
|
||||||
|
</DropdownMenuPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
{children}
|
||||||
|
</DropdownMenuPrimitive.RadioItem>
|
||||||
|
))
|
||||||
|
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||||
|
|
||||||
|
const DropdownMenuLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||||
|
inset?: boolean
|
||||||
|
}
|
||||||
|
>(({ className, inset, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"px-2 py-1.5 text-sm font-semibold",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||||
|
|
||||||
|
const DropdownMenuSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DropdownMenuPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
||||||
|
|
||||||
|
const DropdownMenuShortcut = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
||||||
|
|
||||||
|
export {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuPortal,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
}
|
71
components/ui/grid-pattern.tsx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useId } from "react";
|
||||||
|
|
||||||
|
interface GridPatternProps {
|
||||||
|
width?: any;
|
||||||
|
height?: any;
|
||||||
|
x?: any;
|
||||||
|
y?: any;
|
||||||
|
squares?: Array<[x: number, y: number]>;
|
||||||
|
strokeDasharray?: any;
|
||||||
|
className?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GridPattern({
|
||||||
|
width = 40,
|
||||||
|
height = 40,
|
||||||
|
x = -1,
|
||||||
|
y = -1,
|
||||||
|
strokeDasharray = 0,
|
||||||
|
squares,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: GridPatternProps) {
|
||||||
|
const id = useId();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn(
|
||||||
|
"pointer-events-none absolute inset-0 h-full w-full fill-gray-400/35 stroke-gray-400/35",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<pattern
|
||||||
|
id={id}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
x={x}
|
||||||
|
y={y}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d={`M.5 ${height}V.5H${width}`}
|
||||||
|
fill="none"
|
||||||
|
strokeDasharray={strokeDasharray}
|
||||||
|
/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect width="100%" height="100%" strokeWidth={0} fill={`url(#${id})`} />
|
||||||
|
{squares && (
|
||||||
|
<svg x={x} y={y} className="overflow-visible">
|
||||||
|
{squares.map(([x, y]) => (
|
||||||
|
<rect
|
||||||
|
strokeWidth="0"
|
||||||
|
key={`${x}-${y}`}
|
||||||
|
width={width - 1}
|
||||||
|
height={height - 1}
|
||||||
|
x={x * width + 1}
|
||||||
|
y={y * height + 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GridPattern;
|
523
components/ui/icons.tsx
Normal file
128
components/ui/navigation-menu.tsx
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||||
|
import { cva } from "class-variance-authority"
|
||||||
|
import { ChevronDown } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const NavigationMenu = React.forwardRef<
|
||||||
|
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<NavigationMenuPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative z-10 flex max-w-max flex-1 items-center justify-center",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<NavigationMenuViewport />
|
||||||
|
</NavigationMenuPrimitive.Root>
|
||||||
|
))
|
||||||
|
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
|
||||||
|
|
||||||
|
const NavigationMenuList = React.forwardRef<
|
||||||
|
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<NavigationMenuPrimitive.List
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"group flex flex-1 list-none items-center justify-center space-x-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
|
||||||
|
|
||||||
|
const NavigationMenuItem = NavigationMenuPrimitive.Item
|
||||||
|
|
||||||
|
const navigationMenuTriggerStyle = cva(
|
||||||
|
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
|
||||||
|
)
|
||||||
|
|
||||||
|
const NavigationMenuTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<NavigationMenuPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}{" "}
|
||||||
|
<ChevronDown
|
||||||
|
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</NavigationMenuPrimitive.Trigger>
|
||||||
|
))
|
||||||
|
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const NavigationMenuContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<NavigationMenuPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const NavigationMenuLink = NavigationMenuPrimitive.Link
|
||||||
|
|
||||||
|
const NavigationMenuViewport = React.forwardRef<
|
||||||
|
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div className={cn("absolute left-0 top-full flex justify-center")}>
|
||||||
|
<NavigationMenuPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
NavigationMenuViewport.displayName =
|
||||||
|
NavigationMenuPrimitive.Viewport.displayName
|
||||||
|
|
||||||
|
const NavigationMenuIndicator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<NavigationMenuPrimitive.Indicator
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||||
|
</NavigationMenuPrimitive.Indicator>
|
||||||
|
))
|
||||||
|
NavigationMenuIndicator.displayName =
|
||||||
|
NavigationMenuPrimitive.Indicator.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
navigationMenuTriggerStyle,
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuList,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuContent,
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
NavigationMenuLink,
|
||||||
|
NavigationMenuIndicator,
|
||||||
|
NavigationMenuViewport,
|
||||||
|
}
|
140
components/ui/sheet.tsx
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const Sheet = SheetPrimitive.Root;
|
||||||
|
|
||||||
|
const SheetTrigger = SheetPrimitive.Trigger;
|
||||||
|
|
||||||
|
const SheetClose = SheetPrimitive.Close;
|
||||||
|
|
||||||
|
const SheetPortal = SheetPrimitive.Portal;
|
||||||
|
|
||||||
|
const SheetOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Overlay
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||||
|
|
||||||
|
const sheetVariants = cva(
|
||||||
|
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
side: {
|
||||||
|
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||||
|
bottom:
|
||||||
|
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||||
|
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||||
|
right:
|
||||||
|
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
side: "right",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
interface SheetContentProps
|
||||||
|
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||||
|
VariantProps<typeof sheetVariants> {}
|
||||||
|
|
||||||
|
const SheetContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||||
|
SheetContentProps
|
||||||
|
>(({ side = "right", className, children, ...props }, ref) => (
|
||||||
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
|
<SheetPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(sheetVariants({ side }), className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</SheetPrimitive.Close>
|
||||||
|
</SheetPrimitive.Content>
|
||||||
|
</SheetPortal>
|
||||||
|
));
|
||||||
|
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
const SheetHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col space-y-2 text-center sm:text-left",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
SheetHeader.displayName = "SheetHeader";
|
||||||
|
|
||||||
|
const SheetFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
SheetFooter.displayName = "SheetFooter";
|
||||||
|
|
||||||
|
const SheetTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-lg font-semibold text-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||||
|
|
||||||
|
const SheetDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Sheet,
|
||||||
|
SheetPortal,
|
||||||
|
SheetOverlay,
|
||||||
|
SheetTrigger,
|
||||||
|
SheetClose,
|
||||||
|
SheetContent,
|
||||||
|
SheetHeader,
|
||||||
|
SheetFooter,
|
||||||
|
SheetTitle,
|
||||||
|
SheetDescription,
|
||||||
|
};
|
41
components/ui/theme-toggle.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Moon, Sun } from "lucide-react";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
|
||||||
|
const ThemeToggle = () => {
|
||||||
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
|
const handleTheme = () => {
|
||||||
|
if (theme == "light") {
|
||||||
|
setTheme("dark");
|
||||||
|
} else {
|
||||||
|
setTheme("light");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="md:border-input border-transparent"
|
||||||
|
onClick={handleTheme}
|
||||||
|
>
|
||||||
|
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||||
|
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeToggle;
|
51
components/widgets/num-tick.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useInView, useMotionValue, useSpring } from "framer-motion";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export default function NumTicker({
|
||||||
|
value,
|
||||||
|
direction = "up",
|
||||||
|
delay = 0,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
value: number;
|
||||||
|
direction?: "up" | "down";
|
||||||
|
className?: string;
|
||||||
|
delay?: number;
|
||||||
|
}) {
|
||||||
|
const ref = useRef<HTMLSpanElement>(null);
|
||||||
|
const motionValue = useMotionValue(direction === "down" ? value : 0);
|
||||||
|
const springValue = useSpring(motionValue, {
|
||||||
|
damping: 60,
|
||||||
|
stiffness: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isInView = useInView(ref, { once: true, margin: "0px" });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isInView &&
|
||||||
|
setTimeout(() => {
|
||||||
|
motionValue.set(direction === "down" ? value : 0);
|
||||||
|
}, delay * 1000);
|
||||||
|
}, [motionValue, isInView, delay, value, direction]);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() =>
|
||||||
|
springValue.on("change", (latest) => {
|
||||||
|
if (ref.current) {
|
||||||
|
ref.current.textContent = Intl.NumberFormat("en-US").format(
|
||||||
|
latest.toFixed(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[springValue]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`inline-block tabular-nums text-black dark:text-white ${className}`}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
50
constants/index.tsx
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
export const NAVBAR = {
|
||||||
|
centerLinks: [
|
||||||
|
{
|
||||||
|
href: "/",
|
||||||
|
target: "_self",
|
||||||
|
label: "Home",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/docs",
|
||||||
|
target: "_self",
|
||||||
|
label: "Docs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/forum",
|
||||||
|
target: "_self",
|
||||||
|
label: "Forum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "/blog",
|
||||||
|
target: "_blank",
|
||||||
|
label: "Blog",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rightLinks: [
|
||||||
|
{
|
||||||
|
label: "Git",
|
||||||
|
target: "_blank",
|
||||||
|
href: "https://github.com/",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stats = [
|
||||||
|
{
|
||||||
|
title: "Downloads",
|
||||||
|
count: 69,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Users",
|
||||||
|
count: 42,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Stars",
|
||||||
|
count: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Products",
|
||||||
|
count: 2,
|
||||||
|
},
|
||||||
|
];
|
6
lib/utils.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { type ClassValue, clsx } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
4
next.config.mjs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {};
|
||||||
|
|
||||||
|
export default nextConfig;
|
6374
package-lock.json
generated
Normal file
43
package.json
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "svrjs",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
|
"@radix-ui/react-navigation-menu": "^1.1.4",
|
||||||
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
"@radix-ui/themes": "^3.0.5",
|
||||||
|
"class-variance-authority": "^0.7.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"framer-motion": "^11.2.10",
|
||||||
|
"lucide-react": "^0.394.0",
|
||||||
|
"mini-svg-data-uri": "^1.4.4",
|
||||||
|
"next": "14.2.3",
|
||||||
|
"next-themes": "^0.3.0",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"react-fontawesome": "^1.7.1",
|
||||||
|
"tailwind-merge": "^2.3.0",
|
||||||
|
"tailwindcss-animate": "^1.0.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-next": "14.2.3",
|
||||||
|
"postcss": "^8",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
8
postcss.config.mjs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
275
public/about.svg
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
public/curly-arrow.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
15
public/docker.svg
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_48_16)">
|
||||||
|
<mask id="mask0_48_16" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
|
||||||
|
<path d="M100 0H0V100H100V0Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_48_16)">
|
||||||
|
<path d="M27.3359 23.0762H22.1719V17.2754H27.3359V23.0762ZM27.3359 3.125H22.1719V9.05273H27.3359V3.125ZM33.4453 17.2656H28.2812V23.0664H33.4453V17.2656ZM21.2344 10.2246H16.0703V16.0938H21.2344V10.2246ZM27.3359 10.2246H22.1719V16.0938H27.3359V10.2246ZM48.9609 19.9902C47.8359 19.043 45.2422 18.7012 43.25 19.1699C42.9922 16.8262 41.9453 14.7852 40.039 12.9492L38.9453 12.041L38.2187 13.4082C36.7812 16.123 36.3906 20.5957 37.9297 23.5449C37.25 24.0039 35.914 24.6289 34.1484 24.5898H0.187487C-0.4922 29.5508 0.640613 35.9961 3.62499 40.4199C6.52343 44.707 10.8672 46.8848 16.5469 46.8848C28.8437 46.8848 37.9453 39.8047 42.2031 26.9434C43.875 26.9824 47.4844 26.9531 49.3359 22.5293C49.4531 22.2852 49.8515 21.2402 50 20.8594L48.9609 19.9902ZM9.03124 17.2656H3.87499V23.0664H9.03905V17.2656H9.03124ZM15.1328 17.2656H9.96874V23.0664H15.1328V17.2656ZM21.2344 17.2656H16.0703V23.0664H21.2344V17.2656ZM15.1328 10.2246H9.96874V16.0938H15.1328V10.2246Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_48_16">
|
||||||
|
<rect width="50" height="50" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
10
public/dockerdark.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="50" height="50" viewBox="0 0 50 50" fill="black" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_47_14)">
|
||||||
|
<path d="M27.3359 23.0762H22.1719V17.2754H27.3359V23.0762ZM27.3359 3.125H22.1719V9.05273H27.3359V3.125ZM33.4453 17.2656H28.2812V23.0664H33.4453V17.2656ZM21.2344 10.2246H16.0703V16.0938H21.2344V10.2246ZM27.3359 10.2246H22.1719V16.0938H27.3359V10.2246ZM48.9609 19.9902C47.8359 19.043 45.2422 18.7012 43.25 19.1699C42.9922 16.8262 41.9453 14.7852 40.039 12.9492L38.9453 12.041L38.2187 13.4082C36.7812 16.123 36.3906 20.5957 37.9297 23.5449C37.25 24.0039 35.914 24.6289 34.1484 24.5898H0.187487C-0.4922 29.5508 0.640613 35.9961 3.62499 40.4199C6.52343 44.707 10.8672 46.8848 16.5469 46.8848C28.8437 46.8848 37.9453 39.8047 42.2031 26.9434C43.875 26.9824 47.4844 26.9531 49.3359 22.5293C49.4531 22.2852 49.8515 21.2402 50 20.8594L48.9609 19.9902ZM9.03124 17.2656H3.87499V23.0664H9.03905V17.2656H9.03124ZM15.1328 17.2656H9.96874V23.0664H15.1328V17.2656ZM21.2344 17.2656H16.0703V23.0664H21.2344V17.2656ZM15.1328 10.2246H9.96874V16.0938H15.1328V10.2246Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_47_14">
|
||||||
|
<rect width="100" height="100" fill="black"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
public/linux.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M220.8 123.3c1 .5 1.8 1.7 3 1.7 1.1 0 2.8-.4 2.9-1.5 .2-1.4-1.9-2.3-3.2-2.9-1.7-.7-3.9-1-5.5-.1-.4 .2-.8 .7-.6 1.1 .3 1.3 2.3 1.1 3.4 1.7zm-21.9 1.7c1.2 0 2-1.2 3-1.7 1.1-.6 3.1-.4 3.5-1.6 .2-.4-.2-.9-.6-1.1-1.6-.9-3.8-.6-5.5 .1-1.3 .6-3.4 1.5-3.2 2.9 .1 1 1.8 1.5 2.8 1.4zM420 403.8c-3.6-4-5.3-11.6-7.2-19.7-1.8-8.1-3.9-16.8-10.5-22.4-1.3-1.1-2.6-2.1-4-2.9-1.3-.8-2.7-1.5-4.1-2 9.2-27.3 5.6-54.5-3.7-79.1-11.4-30.1-31.3-56.4-46.5-74.4-17.1-21.5-33.7-41.9-33.4-72C311.1 85.4 315.7 .1 234.8 0 132.4-.2 158 103.4 156.9 135.2c-1.7 23.4-6.4 41.8-22.5 64.7-18.9 22.5-45.5 58.8-58.1 96.7-6 17.9-8.8 36.1-6.2 53.3-6.5 5.8-11.4 14.7-16.6 20.2-4.2 4.3-10.3 5.9-17 8.3s-14 6-18.5 14.5c-2.1 3.9-2.8 8.1-2.8 12.4 0 3.9 .6 7.9 1.2 11.8 1.2 8.1 2.5 15.7 .8 20.8-5.2 14.4-5.9 24.4-2.2 31.7 3.8 7.3 11.4 10.5 20.1 12.3 17.3 3.6 40.8 2.7 59.3 12.5 19.8 10.4 39.9 14.1 55.9 10.4 11.6-2.6 21.1-9.6 25.9-20.2 12.5-.1 26.3-5.4 48.3-6.6 14.9-1.2 33.6 5.3 55.1 4.1 .6 2.3 1.4 4.6 2.5 6.7v.1c8.3 16.7 23.8 24.3 40.3 23 16.6-1.3 34.1-11 48.3-27.9 13.6-16.4 36-23.2 50.9-32.2 7.4-4.5 13.4-10.1 13.9-18.3 .4-8.2-4.4-17.3-15.5-29.7zM223.7 87.3c9.8-22.2 34.2-21.8 44-.4 6.5 14.2 3.6 30.9-4.3 40.4-1.6-.8-5.9-2.6-12.6-4.9 1.1-1.2 3.1-2.7 3.9-4.6 4.8-11.8-.2-27-9.1-27.3-7.3-.5-13.9 10.8-11.8 23-4.1-2-9.4-3.5-13-4.4-1-6.9-.3-14.6 2.9-21.8zM183 75.8c10.1 0 20.8 14.2 19.1 33.5-3.5 1-7.1 2.5-10.2 4.6 1.2-8.9-3.3-20.1-9.6-19.6-8.4 .7-9.8 21.2-1.8 28.1 1 .8 1.9-.2-5.9 5.5-15.6-14.6-10.5-52.1 8.4-52.1zm-13.6 60.7c6.2-4.6 13.6-10 14.1-10.5 4.7-4.4 13.5-14.2 27.9-14.2 7.1 0 15.6 2.3 25.9 8.9 6.3 4.1 11.3 4.4 22.6 9.3 8.4 3.5 13.7 9.7 10.5 18.2-2.6 7.1-11 14.4-22.7 18.1-11.1 3.6-19.8 16-38.2 14.9-3.9-.2-7-1-9.6-2.1-8-3.5-12.2-10.4-20-15-8.6-4.8-13.2-10.4-14.7-15.3-1.4-4.9 0-9 4.2-12.3zm3.3 334c-2.7 35.1-43.9 34.4-75.3 18-29.9-15.8-68.6-6.5-76.5-21.9-2.4-4.7-2.4-12.7 2.6-26.4v-.2c2.4-7.6 .6-16-.6-23.9-1.2-7.8-1.8-15 .9-20 3.5-6.7 8.5-9.1 14.8-11.3 10.3-3.7 11.8-3.4 19.6-9.9 5.5-5.7 9.5-12.9 14.3-18 5.1-5.5 10-8.1 17.7-6.9 8.1 1.2 15.1 6.8 21.9 16l19.6 35.6c9.5 19.9 43.1 48.4 41 68.9zm-1.4-25.9c-4.1-6.6-9.6-13.6-14.4-19.6 7.1 0 14.2-2.2 16.7-8.9 2.3-6.2 0-14.9-7.4-24.9-13.5-18.2-38.3-32.5-38.3-32.5-13.5-8.4-21.1-18.7-24.6-29.9s-3-23.3-.3-35.2c5.2-22.9 18.6-45.2 27.2-59.2 2.3-1.7 .8 3.2-8.7 20.8-8.5 16.1-24.4 53.3-2.6 82.4 .6-20.7 5.5-41.8 13.8-61.5 12-27.4 37.3-74.9 39.3-112.7 1.1 .8 4.6 3.2 6.2 4.1 4.6 2.7 8.1 6.7 12.6 10.3 12.4 10 28.5 9.2 42.4 1.2 6.2-3.5 11.2-7.5 15.9-9 9.9-3.1 17.8-8.6 22.3-15 7.7 30.4 25.7 74.3 37.2 95.7 6.1 11.4 18.3 35.5 23.6 64.6 3.3-.1 7 .4 10.9 1.4 13.8-35.7-11.7-74.2-23.3-84.9-4.7-4.6-4.9-6.6-2.6-6.5 12.6 11.2 29.2 33.7 35.2 59 2.8 11.6 3.3 23.7 .4 35.7 16.4 6.8 35.9 17.9 30.7 34.8-2.2-.1-3.2 0-4.2 0 3.2-10.1-3.9-17.6-22.8-26.1-19.6-8.6-36-8.6-38.3 12.5-12.1 4.2-18.3 14.7-21.4 27.3-2.8 11.2-3.6 24.7-4.4 39.9-.5 7.7-3.6 18-6.8 29-32.1 22.9-76.7 32.9-114.3 7.2zm257.4-11.5c-.9 16.8-41.2 19.9-63.2 46.5-13.2 15.7-29.4 24.4-43.6 25.5s-26.5-4.8-33.7-19.3c-4.7-11.1-2.4-23.1 1.1-36.3 3.7-14.2 9.2-28.8 9.9-40.6 .8-15.2 1.7-28.5 4.2-38.7 2.6-10.3 6.6-17.2 13.7-21.1 .3-.2 .7-.3 1-.5 .8 13.2 7.3 26.6 18.8 29.5 12.6 3.3 30.7-7.5 38.4-16.3 9-.3 15.7-.9 22.6 5.1 9.9 8.5 7.1 30.3 17.1 41.6 10.6 11.6 14 19.5 13.7 24.6zM173.3 148.7c2 1.9 4.7 4.5 8 7.1 6.6 5.2 15.8 10.6 27.3 10.6 11.6 0 22.5-5.9 31.8-10.8 4.9-2.6 10.9-7 14.8-10.4s5.9-6.3 3.1-6.6-2.6 2.6-6 5.1c-4.4 3.2-9.7 7.4-13.9 9.8-7.4 4.2-19.5 10.2-29.9 10.2s-18.7-4.8-24.9-9.7c-3.1-2.5-5.7-5-7.7-6.9-1.5-1.4-1.9-4.6-4.3-4.9-1.4-.1-1.8 3.7 1.7 6.5z"/></svg>
|
After Width: | Height: | Size: 3.5 KiB |
10
public/linuxdark.svg
Normal file
After Width: | Height: | Size: 6.7 KiB |
10
public/logo.svg
Normal file
After Width: | Height: | Size: 33 KiB |
10
public/logodark.svg
Normal file
After Width: | Height: | Size: 33 KiB |
15
public/next.svg
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<svg width="123" height="123" viewBox="0 0 123 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_46_2)">
|
||||||
|
<mask id="mask0_46_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="123" height="123">
|
||||||
|
<path d="M0 0.150635H122.516V122.667H0V0.150635Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_46_2)">
|
||||||
|
<path d="M120.208 55.9532L66.7147 2.46389C65.9824 1.73064 65.1128 1.14894 64.1556 0.752052C63.1983 0.355164 62.1723 0.150879 61.136 0.150879C60.0997 0.150879 59.0737 0.355164 58.1164 0.752052C57.1592 1.14894 56.2896 1.73064 55.5573 2.46389L44.448 13.5732L58.536 27.6612C60.1923 27.0987 61.9729 27.0109 63.6765 27.4077C65.3801 27.8045 66.9387 28.6701 68.176 29.9066C69.4199 31.1522 70.2885 32.7224 70.683 34.438C71.0774 36.1535 70.9816 37.9454 70.4067 39.6092L83.984 53.1879C85.6475 52.6098 87.4403 52.5123 89.1567 52.9066C90.8731 53.3009 92.4436 54.1711 93.688 55.4172C94.5598 56.2889 95.2514 57.3237 95.7232 58.4627C96.1951 59.6017 96.4379 60.8224 96.4379 62.0552C96.4379 63.288 96.1951 64.5088 95.7232 65.6478C95.2514 66.7867 94.5598 67.8216 93.688 68.6932C91.9265 70.4539 89.5379 71.443 87.0473 71.443C84.5568 71.443 82.1681 70.4539 80.4067 68.6932C79.0955 67.3829 78.2018 65.7136 77.8383 63.896C77.4747 62.0783 77.6577 60.1937 78.364 58.4799L65.6973 45.8172V79.1466C67.0343 79.807 68.1947 80.7764 69.0823 81.9746C69.97 83.1728 70.5595 84.5652 70.8019 86.0366C71.0443 87.5079 70.9327 89.0158 70.4762 90.4354C70.0198 91.855 69.2318 93.1455 68.1773 94.1999C67.3057 95.0717 66.2708 95.7633 65.1319 96.2351C63.9929 96.707 62.7722 96.9498 61.5393 96.9498C60.3065 96.9498 59.0858 96.707 57.9468 96.2351C56.8078 95.7633 55.773 95.0717 54.9013 94.1999C54.0293 93.3282 53.3376 92.2933 52.8656 91.1542C52.3937 90.0151 52.1508 88.7942 52.1508 87.5612C52.1508 86.3282 52.3937 85.1073 52.8656 83.9683C53.3376 82.8292 54.0293 81.7942 54.9013 80.9226C55.7807 80.0411 56.8268 79.3435 57.9787 78.8706V45.2346C56.8267 44.7642 55.7805 44.0684 54.9013 43.1879C53.5848 41.8696 52.6897 40.1899 52.3298 38.3619C51.9699 36.5339 52.1614 34.6402 52.88 32.9212L38.9893 19.0306L2.312 55.7026C1.57895 56.4353 0.997443 57.3053 0.600701 58.2629C0.203959 59.2204 -0.000244141 60.2467 -0.000244141 61.2832C-0.000244141 62.3197 0.203959 63.346 0.600701 64.3036C0.997443 65.2611 1.57895 66.1311 2.312 66.8639L55.8067 120.36C57.2867 121.838 59.2932 122.669 61.3853 122.669C63.4774 122.669 65.4839 121.838 66.964 120.36L120.208 67.1146C121.686 65.6335 122.516 63.6264 122.516 61.5339C122.516 59.4414 121.686 57.4343 120.208 55.9532Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_46_2">
|
||||||
|
<rect width="122.667" height="122.667" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
15
public/nextdark.svg
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<svg width="123" height="123" viewBox="0 0 123 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_46_2)">
|
||||||
|
<mask id="mask0_46_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="123" height="123">
|
||||||
|
<path d="M0 0.150635H122.516V122.667H0V0.150635Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_46_2)">
|
||||||
|
<path d="M120.208 55.9532L66.7147 2.46389C65.9824 1.73064 65.1128 1.14894 64.1556 0.752052C63.1983 0.355164 62.1723 0.150879 61.136 0.150879C60.0997 0.150879 59.0737 0.355164 58.1164 0.752052C57.1592 1.14894 56.2896 1.73064 55.5573 2.46389L44.448 13.5732L58.536 27.6612C60.1923 27.0987 61.9729 27.0109 63.6765 27.4077C65.3801 27.8045 66.9387 28.6701 68.176 29.9066C69.4199 31.1522 70.2885 32.7224 70.683 34.438C71.0774 36.1535 70.9816 37.9454 70.4067 39.6092L83.984 53.1879C85.6475 52.6098 87.4403 52.5123 89.1567 52.9066C90.8731 53.3009 92.4436 54.1711 93.688 55.4172C94.5598 56.2889 95.2514 57.3237 95.7232 58.4627C96.1951 59.6017 96.4379 60.8224 96.4379 62.0552C96.4379 63.288 96.1951 64.5088 95.7232 65.6478C95.2514 66.7867 94.5598 67.8216 93.688 68.6932C91.9265 70.4539 89.5379 71.443 87.0473 71.443C84.5568 71.443 82.1681 70.4539 80.4067 68.6932C79.0955 67.3829 78.2018 65.7136 77.8383 63.896C77.4747 62.0783 77.6577 60.1937 78.364 58.4799L65.6973 45.8172V79.1466C67.0343 79.807 68.1947 80.7764 69.0823 81.9746C69.97 83.1728 70.5595 84.5652 70.8019 86.0366C71.0443 87.5079 70.9327 89.0158 70.4762 90.4354C70.0198 91.855 69.2318 93.1455 68.1773 94.1999C67.3057 95.0717 66.2708 95.7633 65.1319 96.2351C63.9929 96.707 62.7722 96.9498 61.5393 96.9498C60.3065 96.9498 59.0858 96.707 57.9468 96.2351C56.8078 95.7633 55.773 95.0717 54.9013 94.1999C54.0293 93.3282 53.3376 92.2933 52.8656 91.1542C52.3937 90.0151 52.1508 88.7942 52.1508 87.5612C52.1508 86.3282 52.3937 85.1073 52.8656 83.9683C53.3376 82.8292 54.0293 81.7942 54.9013 80.9226C55.7807 80.0411 56.8268 79.3435 57.9787 78.8706V45.2346C56.8267 44.7642 55.7805 44.0684 54.9013 43.1879C53.5848 41.8696 52.6897 40.1899 52.3298 38.3619C51.9699 36.5339 52.1614 34.6402 52.88 32.9212L38.9893 19.0306L2.312 55.7026C1.57895 56.4353 0.997443 57.3053 0.600701 58.2629C0.203959 59.2204 -0.000244141 60.2467 -0.000244141 61.2832C-0.000244141 62.3197 0.203959 63.346 0.600701 64.3036C0.997443 65.2611 1.57895 66.1311 2.312 66.8639L55.8067 120.36C57.2867 121.838 59.2932 122.669 61.3853 122.669C63.4774 122.669 65.4839 121.838 66.964 120.36L120.208 67.1146C121.686 65.6335 122.516 63.6264 122.516 61.5339C122.516 59.4414 121.686 57.4343 120.208 55.9532Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_46_2">
|
||||||
|
<rect width="122.667" height="122.667" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
1
public/vercel.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
After Width: | Height: | Size: 629 B |
0
public/xlogodark.svg
Normal file
80
tailwind.config.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
darkMode: ["class"],
|
||||||
|
content: [
|
||||||
|
"./pages/**/*.{ts,tsx}",
|
||||||
|
"./components/**/*.{ts,tsx}",
|
||||||
|
"./app/**/*.{ts,tsx}",
|
||||||
|
"./src/**/*.{ts,tsx}",
|
||||||
|
],
|
||||||
|
prefix: "",
|
||||||
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: {
|
||||||
|
"2xl": "1400px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: "hsl(var(--border))",
|
||||||
|
input: "hsl(var(--input))",
|
||||||
|
ring: "hsl(var(--ring))",
|
||||||
|
background: "hsl(var(--background))",
|
||||||
|
foreground: "hsl(var(--foreground))",
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary))",
|
||||||
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
|
foreground: "hsl(var(--secondary-foreground))",
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted))",
|
||||||
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent))",
|
||||||
|
foreground: "hsl(var(--accent-foreground))",
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover))",
|
||||||
|
foreground: "hsl(var(--popover-foreground))",
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card))",
|
||||||
|
foreground: "hsl(var(--card-foreground))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)",
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
"accordion-down": {
|
||||||
|
from: { height: "0" },
|
||||||
|
to: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
},
|
||||||
|
"accordion-up": {
|
||||||
|
from: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
to: { height: "0" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [require("tailwindcss-animate")],
|
||||||
|
} satisfies Config;
|
||||||
|
|
||||||
|
export default config;
|
26
tsconfig.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
116
types/index.ts
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import { Icons } from "@/components/ui/icons";
|
||||||
|
import { HTMLAttributeAnchorTarget } from "react";
|
||||||
|
|
||||||
|
export type {
|
||||||
|
About,
|
||||||
|
Banner,
|
||||||
|
Feature,
|
||||||
|
FeatureCards,
|
||||||
|
FeatureList,
|
||||||
|
Footer,
|
||||||
|
FooterItem,
|
||||||
|
FrequentlyAskedQuestionItem,
|
||||||
|
FrequentlyAskedQuestions,
|
||||||
|
Hero,
|
||||||
|
Highlights,
|
||||||
|
Member,
|
||||||
|
Navbar,
|
||||||
|
NavbarItem,
|
||||||
|
Newsletter,
|
||||||
|
Partner,
|
||||||
|
Partners,
|
||||||
|
Pricing,
|
||||||
|
PricingCard,
|
||||||
|
Social,
|
||||||
|
Stat,
|
||||||
|
Team,
|
||||||
|
Testimonial,
|
||||||
|
Testimonials,
|
||||||
|
};
|
||||||
|
|
||||||
|
type Hero = Section<{ pretitle: string; title: string; subtitle: string; curlyText: string; primaryButtonText: string; secondaryButtonText: string; cards: { [key: string]: any } }>;
|
||||||
|
type Partners = Section<{ partners: Partner[] }>;
|
||||||
|
type About = Section<{ stats: Stat[]; aboutImage: string }>;
|
||||||
|
type Highlights = Section<{ features: Feature[] }>;
|
||||||
|
type FeatureCards = Section<{ tags: string[]; features: Feature[] }>;
|
||||||
|
type FeatureList = Section<{ features: Feature[]; featureImage: string }>;
|
||||||
|
type Banner = Section<{ primaryButtonText: string; onPrimaryButtonClick: () => void; secondaryButtonText: string; onSecondaryButtonClick: () => void }>;
|
||||||
|
type Testimonials = Section<{ testimonials: Testimonial[] }>;
|
||||||
|
type Team = Section<{ members: Member[] }>;
|
||||||
|
type Pricing = Section<{ plans: PricingCard[] }>;
|
||||||
|
type Newsletter = Section<{ buttonText: string; curlyText: string; inputPlaceholder: string }>;
|
||||||
|
type FrequentlyAskedQuestions = Section<{ contactEmail: string; items: FrequentlyAskedQuestionItem[] }>;
|
||||||
|
|
||||||
|
interface Navbar {
|
||||||
|
leftLinks: NavbarItem[];
|
||||||
|
centerLinks: NavbarItem[];
|
||||||
|
rightLinks: NavbarItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Footer {
|
||||||
|
brand: [FooterItem];
|
||||||
|
[key: string]: FooterItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavbarItem {
|
||||||
|
href?: string;
|
||||||
|
label?: string;
|
||||||
|
icon?: keyof typeof Icons;
|
||||||
|
target?: HTMLAttributeAnchorTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Partner {
|
||||||
|
name: string;
|
||||||
|
logoUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Stat {
|
||||||
|
title: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Feature {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: keyof typeof Icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Testimonial {
|
||||||
|
name: string;
|
||||||
|
userName: string;
|
||||||
|
avatarUrl: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Member {
|
||||||
|
name: string;
|
||||||
|
position: string;
|
||||||
|
description: string;
|
||||||
|
socials: Social[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Social {
|
||||||
|
href: string;
|
||||||
|
icon: keyof typeof Icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PricingCard {
|
||||||
|
planName: string;
|
||||||
|
buttonText: string;
|
||||||
|
pricePerMonth: string;
|
||||||
|
planDescription: string;
|
||||||
|
features: React.ReactNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FrequentlyAskedQuestionItem {
|
||||||
|
key: string;
|
||||||
|
question: string;
|
||||||
|
answer: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Section<T extends object> = {
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
} & T;
|
||||||
|
|
||||||
|
interface FooterItem extends NavbarItem {}
|