diff --git a/README.md b/README.md
index e145bd5..e215bc4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,36 @@
-# griffith1deady.github.io
\ No newline at end of file
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/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/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## 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/app/building-your-application/deploying) for more details.
diff --git a/public/beatifulGirl.jpg b/app/beatifulGirl.jpg
similarity index 100%
rename from public/beatifulGirl.jpg
rename to app/beatifulGirl.jpg
diff --git a/public/beatifulGirl1.jpg b/app/beatifulGirl1.jpg
similarity index 100%
rename from public/beatifulGirl1.jpg
rename to app/beatifulGirl1.jpg
diff --git a/public/beatifulGirl2.jpg b/app/beatifulGirl2.jpg
similarity index 100%
rename from public/beatifulGirl2.jpg
rename to app/beatifulGirl2.jpg
diff --git a/public/beatifulGirl3.jpg b/app/beatifulGirl3.jpg
similarity index 100%
rename from public/beatifulGirl3.jpg
rename to app/beatifulGirl3.jpg
diff --git a/public/beatifulGirl4.jpg b/app/beatifulGirl4.jpg
similarity index 100%
rename from public/beatifulGirl4.jpg
rename to app/beatifulGirl4.jpg
diff --git a/public/beatifulGirl5.jpg b/app/beatifulGirl5.jpg
similarity index 100%
rename from public/beatifulGirl5.jpg
rename to app/beatifulGirl5.jpg
diff --git a/public/beatifulGirl6.jpg b/app/beatifulGirl6.jpg
similarity index 100%
rename from public/beatifulGirl6.jpg
rename to app/beatifulGirl6.jpg
diff --git a/app/favicon.ico b/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/app/favicon.ico differ
diff --git a/app/fonts/GeistMonoVF.woff b/app/fonts/GeistMonoVF.woff
new file mode 100644
index 0000000..f2ae185
Binary files /dev/null and b/app/fonts/GeistMonoVF.woff differ
diff --git a/app/fonts/GeistVF.woff b/app/fonts/GeistVF.woff
new file mode 100644
index 0000000..1b62daa
Binary files /dev/null and b/app/fonts/GeistVF.woff differ
diff --git a/app/globals.css b/app/globals.css
new file mode 100644
index 0000000..1849479
--- /dev/null
+++ b/app/globals.css
@@ -0,0 +1,28 @@
+@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700&display=swap');
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+* {
+ font-family: 'Nunito', serif;
+}
+
+body {
+ font-family: 'Nunito', serif;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+main {
+ display: block;
+}
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
+
diff --git a/app/layout.tsx b/app/layout.tsx
new file mode 100644
index 0000000..f7ace43
--- /dev/null
+++ b/app/layout.tsx
@@ -0,0 +1,39 @@
+"use client";
+
+import "./globals.css";
+import {ThemeProvider} from "@/components/theme-provider";
+import NavbarComponent from "@/components/layout/NavbarComponent";
+import React from "react";
+import {Box, Container} from "@chakra-ui/react";
+import {Toaster} from "sonner";
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+ );
+}
diff --git a/app/page.tsx b/app/page.tsx
new file mode 100644
index 0000000..f726da5
--- /dev/null
+++ b/app/page.tsx
@@ -0,0 +1,191 @@
+"use client";
+
+import {AspectRatio} from "@/components/ui/aspect-ratio";
+import Image from "next/image";
+import { toast } from "sonner"
+
+import background from "@/app/spare.png";
+import myGirlfriend from "@/app/beatifulGirl.jpg";
+import myGirlfriendOne from "@/app/beatifulGirl1.jpg";
+import myGirlfriendTwo from "@/app/beatifulGirl2.jpg";
+import myGirlfriendThree from "@/app/beatifulGirl3.jpg";
+import myGirlfriendFour from "@/app/beatifulGirl4.jpg";
+import myGirlfriendFive from "@/app/beatifulGirl5.jpg";
+import myGirlfriendSix from "@/app/beatifulGirl6.jpg";
+
+import {Box, Center, Container, SimpleGrid, StackSeparator, VStack} from "@chakra-ui/react";
+import {Button} from "@/components/ui/button";
+import Link from "next/link";
+
+import { Card, CardContent } from "@/components/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/components/ui/carousel"
+
+export default function Home() {
+ return (
+
+ }
+ >
+
+
+
+
+
+
+
+ griffith1deady
+
+
+ 18 y/o, Software Developer, Web Developer, Gamer, Linux
+ Enthusiast, and a little bit of everything.
+
+
+
+
+
+
+ "Fork in the eye or in the ass, which one?"
+
+
+
+ @someone
+
+
+
+ "I don't see that you have one eye."
+
+
+
+ @me
+
+
+
+
+
+
+
+
+ Skills I'm good at
+
+
+
+
+ Personal project's
+
+
+
+
+ Contact me
+
+
+
+
+ Buy me a beer
+
+
+
+
+ About me
+
+
+
+
+ My best girlfriend
+
+
+
+
+
+
+ Buy me a beer!
+
+
+
+ I don't like beer, but I wouldn't mind if you donate to me for beer. After all, I need something to survive on, as developing cheats for games and my own business cards doesn't give me the money I would like :)
+
+
+
+ @me, give me a beer!
+
+
+
+
+
+
+ "A jar in Monobank"
+
+
+ {
+ toast.success("sadness1rip@gmail.com is my email for receiving beer :)")
+ }}
+ >
+ "Paypal"
+
+ {
+ toast.success("`TReZG6poXtSFQ1wZdMpRL2BzKpALBdGXzT` is my address for receiving beer :)")
+ }}
+ >
+ "TRC20"
+
+
+
+
+
+ Beautiful girl!
+
+
+
+
+
+
+ The most perfect girl I've ever met in my entire life. Even though things don't always go as smoothly as we would like, or as many people imagine the ideal relationship to be, but it's a great relationship, and the most beautiful girl, and most importantly, mine. :) I have so many words and ideas for variations on how to write about how beautiful she is - but the only thing that comes to mind is that I constantly want to see her beautiful sweet smile, for which I am willing to do anything. I may be silly, I would even say foolish, but I love her. She is my beautiful ray of sunshine :) She's my beautiful little kitten, very, very fluffy, who I just want to cuddle and cuddle, you have no idea how much :)
+
+
+
+ @me, sayed that some one year ago :)
+
+
+
+
+
+
+ {Array.from([myGirlfriendOne, myGirlfriendTwo, myGirlfriendThree, myGirlfriendFour, myGirlfriendFive, myGirlfriendSix]).map((image, index) => (
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/public/spare.png b/app/spare.png
similarity index 100%
rename from public/spare.png
rename to app/spare.png
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..f863811
Binary files /dev/null and b/bun.lockb differ
diff --git a/components.json b/components.json
new file mode 100644
index 0000000..6af4ad0
--- /dev/null
+++ b/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": false,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
\ No newline at end of file
diff --git a/components/AppBlock.tsx b/components/AppBlock.tsx
deleted file mode 100644
index acd3166..0000000
--- a/components/AppBlock.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Box, ChakraProps } from "@chakra-ui/react";
-import { motion } from "framer-motion";
-import { ReactElement } from "react";
-
-export type AppBlockProps = {
- children: ReactElement | Array | string;
- delay: number;
-} & ChakraProps;
-
-const AppBlock = ({ children, delay, ...props }: AppBlockProps) => {
- return (
-
-
- {children}
-
-
- );
-};
-
-export default AppBlock;
diff --git a/components/AppNavBar.tsx b/components/AppNavBar.tsx
deleted file mode 100644
index 6ce7022..0000000
--- a/components/AppNavBar.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import {Box, Container, Flex, Heading,} from "@chakra-ui/react";
-import {useRouter} from "next/router";
-import AppNavBarLink from "./AppNavBarLink";
-
-const AppNavBar = () => {
- const router = useRouter()
- return (
-
-
-
-
- router.push('/')}>
- griffith1deady
-
-
-
-
-
- );
-};
-
-export default AppNavBar;
diff --git a/components/AppNavBarLink.tsx b/components/AppNavBarLink.tsx
deleted file mode 100644
index a958178..0000000
--- a/components/AppNavBarLink.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { ChakraProps, Link } from "@chakra-ui/react";
-import NextLink from "next/link";
-import React, { ReactElement } from "react";
-
-export type AppNavBarLinkProps = {
- href: string;
- children: ReactElement | string;
- target?: string;
-} & ChakraProps;
-
-const AppNavBarLink = ({
- href,
- children,
- target,
- ...props
-}: AppNavBarLinkProps) => {
- return (
- // @ts-ignore
-
-
- {children}
-
-
- );
-};
-
-export default AppNavBarLink;
diff --git a/components/Fonts.tsx b/components/Fonts.tsx
deleted file mode 100644
index 4b0e45a..0000000
--- a/components/Fonts.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-const Fonts = () => {
- return (
-
- );
-};
-
-export default Fonts;
diff --git a/components/FooterComponent.tsx b/components/FooterComponent.tsx
deleted file mode 100644
index 1e4be0f..0000000
--- a/components/FooterComponent.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import {Box, Container, Text} from "@chakra-ui/react";
-
-const FooterComponent = () => {
- return
-
-
- 🛠 Builded by griffith1deady with Next / ChakraUI & VKUI
-
-
-
-}
-
-export default FooterComponent
\ No newline at end of file
diff --git a/components/PageHeader.tsx b/components/PageHeader.tsx
deleted file mode 100644
index a5fe880..0000000
--- a/components/PageHeader.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import {Box, ChakraProps} from "@chakra-ui/react";
-import {motion} from "framer-motion";
-import {ReactElement} from "react";
-
-export type PageHeaderProps = {
- duration: number;
- children: ReactElement | ReactElement[] | string;
-} & ChakraProps;
-
-const PageHeader = ({duration, children, ...props}: PageHeaderProps) => {
- return (
-
- {children}
-
- );
-};
-
-export default PageHeader;
diff --git a/components/language/LanguageItem.tsx b/components/language/LanguageItem.tsx
deleted file mode 100644
index 26da472..0000000
--- a/components/language/LanguageItem.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import {Language, StackItem} from "../../types";
-import {Box, Center, ChakraProps, Divider, List, ListIcon, ListItem, Progress, SimpleGrid} from "@chakra-ui/react";
-import {FaCodepen, FaJava, FaRust} from "react-icons/fa";
-import {FiHexagon} from "react-icons/fi";
-import {HTMLAttributes} from "react";
-import {DiDart} from "react-icons/di";
-import {TbBrandCpp, TbBrandGolang, TbBrandKotlin, TbBrandTypescript} from "react-icons/tb";
-import AppBlock from "../AppBlock";
-
-type LanguageItemProps = {
- language: Language
- stackItems?: StackItem[]
-} & ChakraProps & HTMLAttributes
-const LanguageItem = ({language, stackItems, ...props}: LanguageItemProps) => {
- const languageIcon = getIconForLanguage(language.name)
- return (<>
-
-
- {language.name}
- {language.description}
-
-
-
- {stackItems ?
-
- {stackItems.map((stack, index) => (
-
-
-
- {stack.name}
-
- {stack.additional ? : null}
-
- {stack.additional ? stack.additional.map((stack, index) => (
-
-
- {stack.name}
-
- )) : null}
-
-
- ))}
-
-
: null}
-
- >)
-}
-
-function getIconForLanguage(language: string) {
- if (language == 'Java') {
- return FaJava
- } else if (language == 'Kotlin') {
- return TbBrandKotlin
- } else if (language == 'TypeScript') {
- return TbBrandTypescript
- } else if (language == 'Go') {
- return TbBrandGolang
- } else if (language == 'C++') {
- return TbBrandCpp
- } else if (language == 'Rust') {
- return FaRust
- } else if (language == 'Dart') {
- return DiDart
- } else {
- return FiHexagon
- }
-}
-
-export default LanguageItem
\ No newline at end of file
diff --git a/components/layout/NavbarComponent.tsx b/components/layout/NavbarComponent.tsx
new file mode 100644
index 0000000..aab602c
--- /dev/null
+++ b/components/layout/NavbarComponent.tsx
@@ -0,0 +1,19 @@
+import {Box, Container, Flex} from "@chakra-ui/react";
+import {Button} from "@/components/ui/button";
+import Link from "next/link";
+import {ColorModeButton} from "@/components/ui/color-mode";
+
+export default function NavbarComponent() {
+ return (
+
+
+
+
+ griffith1deady
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/layouts/AnimationLayout.tsx b/components/layouts/AnimationLayout.tsx
deleted file mode 100644
index b4057ad..0000000
--- a/components/layouts/AnimationLayout.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { ChakraProps } from "@chakra-ui/react";
-import { AnimatePresence, motion } from "framer-motion";
-import { ReactElement } from "react";
-
-export type AnimationLayoutProps = {
- route: string;
- children: ReactElement | ReactElement[] | string;
-} & ChakraProps;
-
-const AnimationLayout = ({ route, children }: AnimationLayoutProps) => {
- const variants = {
- hidden: { opacity: 0, x: 0, y: 0 },
- enter: { opacity: 1, x: 0, y: 0 },
- exit: { opacity: 0, x: -0, y: -0 },
- };
-
- return (
- {
- window.scrollTo(0, 0);
- }}
- >
-
- {children}
-
-
- );
-};
-
-export default AnimationLayout;
diff --git a/components/layouts/AppLayout.tsx b/components/layouts/AppLayout.tsx
deleted file mode 100644
index 64be50c..0000000
--- a/components/layouts/AppLayout.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import {Box, Container} from "@chakra-ui/react";
-import {ReactElement} from "react";
-import AppNavBar from "../AppNavBar";
-import FooterComponent from "../FooterComponent";
-
-const AppLayout = ({children}: { children: ReactElement }) => {
- return (
-
-
-
- {children}
-
-
-
- );
-};
-
-export default AppLayout;
diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx
new file mode 100644
index 0000000..723e9f5
--- /dev/null
+++ b/components/theme-provider.tsx
@@ -0,0 +1,13 @@
+"use client"
+
+import * as React from "react"
+import { ThemeProvider as NextThemesProvider } from "next-themes"
+import {Provider} from "@/components/ui/provider";
+
+export function ThemeProvider({children, ...props}: React.ComponentProps) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx
new file mode 100644
index 0000000..24c788c
--- /dev/null
+++ b/components/ui/accordion.tsx
@@ -0,0 +1,58 @@
+"use client"
+
+import * as React from "react"
+import * as AccordionPrimitive from "@radix-ui/react-accordion"
+import { ChevronDown } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Accordion = AccordionPrimitive.Root
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AccordionItem.displayName = "AccordionItem"
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+))
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+))
+
+AccordionContent.displayName = AccordionPrimitive.Content.displayName
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/components/ui/aspect-ratio.tsx b/components/ui/aspect-ratio.tsx
new file mode 100644
index 0000000..d6a5226
--- /dev/null
+++ b/components/ui/aspect-ratio.tsx
@@ -0,0 +1,7 @@
+"use client"
+
+import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
+
+const AspectRatio = AspectRatioPrimitive.Root
+
+export { AspectRatio }
diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx
new file mode 100644
index 0000000..cd84664
--- /dev/null
+++ b/components/ui/avatar.tsx
@@ -0,0 +1,74 @@
+"use client"
+
+import type { GroupProps, SlotRecipeProps } from "@chakra-ui/react"
+import { Avatar as ChakraAvatar, Group } from "@chakra-ui/react"
+import * as React from "react"
+
+type ImageProps = React.ImgHTMLAttributes
+
+export interface AvatarProps extends ChakraAvatar.RootProps {
+ name?: string
+ src?: string
+ srcSet?: string
+ loading?: ImageProps["loading"]
+ icon?: React.ReactElement
+ fallback?: React.ReactNode
+}
+
+export const Avatar = React.forwardRef(
+ function Avatar(props, ref) {
+ const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
+ props
+ return (
+
+
+ {fallback}
+
+
+ {children}
+
+ )
+ },
+)
+
+interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
+ name?: string
+ icon?: React.ReactElement
+}
+
+const AvatarFallback = React.forwardRef(
+ function AvatarFallback(props, ref) {
+ const { name, icon, children, ...rest } = props
+ return (
+
+ {children}
+ {name != null && children == null && <>{getInitials(name)}>}
+ {name == null && children == null && (
+ {icon}
+ )}
+
+ )
+ },
+)
+
+function getInitials(name: string) {
+ const names = name.trim().split(" ")
+ const firstName = names[0] != null ? names[0] : ""
+ const lastName = names.length > 1 ? names[names.length - 1] : ""
+ return firstName && lastName
+ ? `${firstName.charAt(0)}${lastName.charAt(0)}`
+ : firstName.charAt(0)
+}
+
+interface AvatarGroupProps extends GroupProps, SlotRecipeProps<"avatar"> {}
+
+export const AvatarGroup = React.forwardRef(
+ function AvatarGroup(props, ref) {
+ const { size, variant, borderless, ...rest } = props
+ return (
+
+
+
+ )
+ },
+)
diff --git a/components/ui/blockquote.tsx b/components/ui/blockquote.tsx
new file mode 100644
index 0000000..166446b
--- /dev/null
+++ b/components/ui/blockquote.tsx
@@ -0,0 +1,31 @@
+import { Blockquote as ChakraBlockquote } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface BlockquoteProps extends ChakraBlockquote.RootProps {
+ cite?: React.ReactNode
+ citeUrl?: string
+ icon?: React.ReactNode
+ showDash?: boolean
+}
+
+export const Blockquote = React.forwardRef(
+ function Blockquote(props, ref) {
+ const { children, cite, citeUrl, showDash, icon, ...rest } = props
+
+ return (
+
+ {icon}
+
+ {children}
+
+ {cite && (
+
+ {showDash ? <>—> : null} {cite}
+
+ )}
+
+ )
+ },
+)
+
+export const BlockquoteIcon = ChakraBlockquote.Icon
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
new file mode 100644
index 0000000..fe539b0
--- /dev/null
+++ b/components/ui/button.tsx
@@ -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 gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300",
+ {
+ variants: {
+ variant: {
+ default: "bg-neutral-900 text-neutral-50 hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90",
+ destructive:
+ "bg-red-500 text-neutral-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/90",
+ outline:
+ "border border-neutral-200 bg-white hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
+ secondary:
+ "bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80",
+ ghost: "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
+ link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
+ },
+ 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,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
new file mode 100644
index 0000000..2945509
--- /dev/null
+++ b/components/ui/card.tsx
@@ -0,0 +1,79 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/components/ui/carousel.tsx b/components/ui/carousel.tsx
new file mode 100644
index 0000000..ec505d0
--- /dev/null
+++ b/components/ui/carousel.tsx
@@ -0,0 +1,262 @@
+"use client"
+
+import * as React from "react"
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react"
+import { ArrowLeft, ArrowRight } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+
+type CarouselApi = UseEmblaCarouselType[1]
+type UseCarouselParameters = Parameters
+type CarouselOptions = UseCarouselParameters[0]
+type CarouselPlugin = UseCarouselParameters[1]
+
+type CarouselProps = {
+ opts?: CarouselOptions
+ plugins?: CarouselPlugin
+ orientation?: "horizontal" | "vertical"
+ setApi?: (api: CarouselApi) => void
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0]
+ api: ReturnType[1]
+ scrollPrev: () => void
+ scrollNext: () => void
+ canScrollPrev: boolean
+ canScrollNext: boolean
+} & CarouselProps
+
+const CarouselContext = React.createContext(null)
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext)
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ")
+ }
+
+ return context
+}
+
+const Carousel = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes & CarouselProps
+>(
+ (
+ {
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+ },
+ ref
+ ) => {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins
+ )
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) {
+ return
+ }
+
+ setCanScrollPrev(api.canScrollPrev())
+ setCanScrollNext(api.canScrollNext())
+ }, [])
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev()
+ }, [api])
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext()
+ }, [api])
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault()
+ scrollPrev()
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault()
+ scrollNext()
+ }
+ },
+ [scrollPrev, scrollNext]
+ )
+
+ React.useEffect(() => {
+ if (!api || !setApi) {
+ return
+ }
+
+ setApi(api)
+ }, [api, setApi])
+
+ React.useEffect(() => {
+ if (!api) {
+ return
+ }
+
+ onSelect(api)
+ api.on("reInit", onSelect)
+ api.on("select", onSelect)
+
+ return () => {
+ api?.off("select", onSelect)
+ }
+ }, [api, onSelect])
+
+ return (
+
+
+ {children}
+
+
+ )
+ }
+)
+Carousel.displayName = "Carousel"
+
+const CarouselContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { carouselRef, orientation } = useCarousel()
+
+ return (
+
+ )
+})
+CarouselContent.displayName = "CarouselContent"
+
+const CarouselItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { orientation } = useCarousel()
+
+ return (
+
+ )
+})
+CarouselItem.displayName = "CarouselItem"
+
+const CarouselPrevious = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
+
+ return (
+
+
+ Previous slide
+
+ )
+})
+CarouselPrevious.displayName = "CarouselPrevious"
+
+const CarouselNext = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
+
+ return (
+
+
+ Next slide
+
+ )
+})
+CarouselNext.displayName = "CarouselNext"
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+}
diff --git a/components/ui/chart.tsx b/components/ui/chart.tsx
new file mode 100644
index 0000000..96773e3
--- /dev/null
+++ b/components/ui/chart.tsx
@@ -0,0 +1,366 @@
+"use client"
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "@/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+}
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ }
+>(({ id, className, children, config, ...props }, ref) => {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+})
+ChartContainer.displayName = "Chart"
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ([_, config]) => config.theme || config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+