Compare commits
9 Commits
4d00d4b936
...
mus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4278f1f0d7 | ||
|
|
85b62a35f4 | ||
|
|
35dcff135f | ||
|
|
ae8cd7f6b4 | ||
|
|
f2fe965ce6 | ||
|
|
94b07af7e7 | ||
|
|
15558d11ce | ||
|
|
78d5bd3af6 | ||
|
|
8e1a327c6d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -38,3 +38,4 @@ next-env.d.ts
|
|||||||
.env.production
|
.env.production
|
||||||
.env.development
|
.env.development
|
||||||
.env*.local
|
.env*.local
|
||||||
|
pnpm*.yaml
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
"@radix-ui/react-hover-card": "^1.0.7",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-menubar": "^1.0.4",
|
"@radix-ui/react-menubar": "^1.0.4",
|
||||||
"@radix-ui/react-navigation-menu": "^1.1.4",
|
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@radix-ui/react-progress": "^1.0.3",
|
"@radix-ui/react-progress": "^1.0.3",
|
||||||
"@radix-ui/react-radio-group": "^1.1.3",
|
"@radix-ui/react-radio-group": "^1.1.3",
|
||||||
@@ -46,7 +45,6 @@
|
|||||||
"embla-carousel-react": "^8.1.3",
|
"embla-carousel-react": "^8.1.3",
|
||||||
"framer-motion": "^11.2.10",
|
"framer-motion": "^11.2.10",
|
||||||
"input-otp": "^1.2.4",
|
"input-otp": "^1.2.4",
|
||||||
"lightweight-charts": "^4.1.4",
|
|
||||||
"lucide-react": "^0.387.0",
|
"lucide-react": "^0.387.0",
|
||||||
"next": "14.2.3",
|
"next": "14.2.3",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
@@ -62,11 +60,9 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"jest": "^29.7.0",
|
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|||||||
2283
pnpm-lock.yaml
generated
2283
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,6 @@ import {ThemeProvider} from "@/components/providers/theme-provider";
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import {Footer} from "@/components/footer";
|
import {Footer} from "@/components/footer";
|
||||||
import {Header} from "@/components/header";
|
import {Header} from "@/components/header";
|
||||||
import {PrimaryNavigationMenu} from "@/components/primary-nav";
|
|
||||||
import {Providers} from "@/components/providers/providers";
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "YeloBit",
|
title: "YeloBit",
|
||||||
@@ -24,13 +22,15 @@ export default function RootLayout({
|
|||||||
<link rel="icon" href="/public/favicon.ico" sizes="any"/>
|
<link rel="icon" href="/public/favicon.ico" sizes="any"/>
|
||||||
</head>
|
</head>
|
||||||
<body className={"w-full min-h-screen flex flex-col items-center justify-between"}>
|
<body className={"w-full min-h-screen flex flex-col items-center justify-between"}>
|
||||||
<Providers>
|
<ThemeProvider
|
||||||
<Header>
|
attribute="class"
|
||||||
<PrimaryNavigationMenu/>
|
defaultTheme="system"
|
||||||
</Header>
|
enableSystem
|
||||||
|
>
|
||||||
|
<Header></Header>
|
||||||
{children}
|
{children}
|
||||||
<Footer/>
|
<Footer/>
|
||||||
</Providers>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Image from "next/image";
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col items-center justify-end h-full w-2/4">
|
<main className="flex flex-col items-center justify-between p-24">
|
||||||
<h1>Hello world !</h1>
|
<h1>Hello world !</h1>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import {useContext} from "react";
|
|
||||||
import {UserDataContext} from "@/components/providers/userdata-provider";
|
|
||||||
import {AccountInfo} from "@/components/account-info";
|
|
||||||
|
|
||||||
|
|
||||||
export function AccountDialog() {
|
|
||||||
const userContext = useContext(UserDataContext)
|
|
||||||
|
|
||||||
if (!userContext?.userData) {
|
|
||||||
userContext?.setUserData({name: "Mathis"})
|
|
||||||
return (<p>Loading...</p>)
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO No account context
|
|
||||||
|
|
||||||
//TODO Loading context
|
|
||||||
|
|
||||||
//TODO Account context
|
|
||||||
return (<AccountInfo userData={userContext.userData}/>)
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import {IUserData} from "@/interfaces/userdata.interface";
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { Label } from "@/components/ui/label"
|
|
||||||
import {
|
|
||||||
Sheet,
|
|
||||||
SheetClose,
|
|
||||||
SheetContent,
|
|
||||||
SheetDescription,
|
|
||||||
SheetFooter,
|
|
||||||
SheetHeader,
|
|
||||||
SheetTitle,
|
|
||||||
SheetTrigger,
|
|
||||||
} from "@/components/ui/sheet"
|
|
||||||
import {User} from "lucide-react";
|
|
||||||
|
|
||||||
|
|
||||||
export function AccountInfo({userData}: {userData: IUserData}) {
|
|
||||||
return (
|
|
||||||
<Sheet>
|
|
||||||
<SheetTrigger asChild>
|
|
||||||
<Button variant="outline" className={"gap-1"}>
|
|
||||||
<User />
|
|
||||||
{userData?.firstName || "?"}
|
|
||||||
</Button>
|
|
||||||
</SheetTrigger>
|
|
||||||
<SheetContent>
|
|
||||||
<SheetHeader>
|
|
||||||
<SheetTitle>Edit profile</SheetTitle>
|
|
||||||
<SheetDescription>
|
|
||||||
Make changes to your profile here. Click save when you're done.
|
|
||||||
</SheetDescription>
|
|
||||||
</SheetHeader>
|
|
||||||
<div className="grid gap-4 py-4">
|
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
|
||||||
<Label htmlFor="name" className="text-right">
|
|
||||||
Name
|
|
||||||
</Label>
|
|
||||||
<Input id="name" placeholder={userData.firstName} className="col-span-3" onChange={(event)=>{console.log(event.target.value)}} />
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
|
||||||
<Label htmlFor="username" className="text-right">
|
|
||||||
Username
|
|
||||||
</Label>
|
|
||||||
<Input id="username" value="@peduarte" className="col-span-3" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<SheetFooter>
|
|
||||||
<SheetClose asChild>
|
|
||||||
<Button type="submit">Save changes</Button>
|
|
||||||
</SheetClose>
|
|
||||||
</SheetFooter>
|
|
||||||
</SheetContent>
|
|
||||||
</Sheet>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {ThemeBtnSelector} from "@/components/theme-btn-selector";
|
import {ThemeBtnSelector} from "@/components/theme-btn-selector";
|
||||||
import {AccountDialog} from "@/components/account-dialog";
|
|
||||||
|
|
||||||
|
|
||||||
export function Header({title, children}: {title?: string, children?: React.ReactNode}) {
|
export function Header({title, children}: {title?: string, children?: React.ReactNode}) {
|
||||||
@@ -17,8 +16,7 @@ export function Header({title, children}: {title?: string, children?: React.Reac
|
|||||||
<div className={"w-1/3 flex flex-row justify-center items-center"}>
|
<div className={"w-1/3 flex flex-row justify-center items-center"}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div className={"w-1/3 flex flex-row justify-end gap-2 items-center"}>
|
<div className={"w-1/3 flex flex-row justify-end items-center"}>
|
||||||
<AccountDialog/>
|
|
||||||
<ThemeBtnSelector/>
|
<ThemeBtnSelector/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
117
src/components/hero.tsx
Normal file
117
src/components/hero.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
"use client";
|
||||||
|
import React from "react";
|
||||||
|
import { calsans } from "@/fonts/calsans";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { TracingBeam } from "@/components/ui/tracing-beam";
|
||||||
|
|
||||||
|
export function TracingBeamDemo() {
|
||||||
|
return (
|
||||||
|
<TracingBeam className="px-6">
|
||||||
|
<div className="max-w-2xl mx-auto antialiased pt-4 relative">
|
||||||
|
{dummyContent.map((item, index) => (
|
||||||
|
<div key={`content-${index}`} className="mb-10">
|
||||||
|
<h2 className="bg-black text-white rounded-full text-sm w-fit px-4 py-1 mb-4">
|
||||||
|
{item.badge}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p className={twMerge(calsans.className, "text-xl mb-4")}>
|
||||||
|
{item.title}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="text-sm prose prose-sm dark:prose-invert">
|
||||||
|
{item?.image && (
|
||||||
|
<Image
|
||||||
|
src={item.image}
|
||||||
|
alt="blog thumbnail"
|
||||||
|
height="1000"
|
||||||
|
width="1000"
|
||||||
|
className="rounded-lg mb-10 object-cover"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</TracingBeam>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dummyContent = [
|
||||||
|
{
|
||||||
|
title: "Lorem Ipsum Dolor Sit Amet",
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Sit duis est minim proident non nisi velit non consectetur. Esse
|
||||||
|
adipisicing laboris consectetur enim ipsum reprehenderit eu deserunt
|
||||||
|
Lorem ut aliqua anim do. Duis cupidatat qui irure cupidatat incididunt
|
||||||
|
incididunt enim magna id est qui sunt fugiat. Laboris do duis pariatur
|
||||||
|
fugiat Lorem aute sit ullamco. Qui deserunt non reprehenderit dolore
|
||||||
|
nisi velit exercitation Lorem qui do enim culpa. Aliqua eiusmod in
|
||||||
|
occaecat reprehenderit laborum nostrud fugiat voluptate do Lorem culpa
|
||||||
|
officia sint labore. Tempor consectetur excepteur ut fugiat veniam
|
||||||
|
commodo et labore dolore commodo pariatur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Dolor minim irure ut Lorem proident. Ipsum do pariatur est ad ad
|
||||||
|
veniam in commodo id reprehenderit adipisicing. Proident duis
|
||||||
|
exercitation ad quis ex cupidatat cupidatat occaecat adipisicing.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Tempor quis dolor veniam quis dolor. Sit reprehenderit eiusmod
|
||||||
|
reprehenderit deserunt amet laborum consequat adipisicing officia qui
|
||||||
|
irure id sint adipisicing. Adipisicing fugiat aliqua nulla nostrud.
|
||||||
|
Amet culpa officia aliquip deserunt veniam deserunt officia
|
||||||
|
adipisicing aliquip proident officia sunt.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
badge: "React",
|
||||||
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?auto=format&fit=crop&q=80&w=3540&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Lorem Ipsum Dolor Sit Amet",
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Ex irure dolore veniam ex velit non aute nisi labore ipsum occaecat
|
||||||
|
deserunt cupidatat aute. Enim cillum dolor et nulla sunt exercitation
|
||||||
|
non voluptate qui aliquip esse tempor. Ullamco ut sunt consectetur
|
||||||
|
sint qui qui do do qui do. Labore laborum culpa magna reprehenderit ea
|
||||||
|
velit id esse adipisicing deserunt amet dolore. Ipsum occaecat veniam
|
||||||
|
commodo proident aliqua id ad deserunt dolor aliquip duis veniam sunt.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In dolore veniam excepteur eu est et sunt velit. Ipsum sint esse
|
||||||
|
veniam fugiat esse qui sint ad sunt reprehenderit do qui proident
|
||||||
|
reprehenderit. Laborum exercitation aliqua reprehenderit ea sint
|
||||||
|
cillum ut mollit.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
badge: "Changelog",
|
||||||
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1519681393784-d120267933ba?auto=format&fit=crop&q=80&w=3540&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Lorem Ipsum Dolor Sit Amet",
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Ex irure dolore veniam ex velit non aute nisi labore ipsum occaecat
|
||||||
|
deserunt cupidatat aute. Enim cillum dolor et nulla sunt exercitation
|
||||||
|
non voluptate qui aliquip esse tempor. Ullamco ut sunt consectetur
|
||||||
|
sint qui qui do do qui do. Labore laborum culpa magna reprehenderit ea
|
||||||
|
velit id esse adipisicing deserunt amet dolore. Ipsum occaecat veniam
|
||||||
|
commodo proident aliqua id ad deserunt dolor aliquip duis veniam sunt.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
badge: "Launch Week",
|
||||||
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&q=80&w=3506&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import React from "react";
|
|
||||||
import {Header} from "@/components/header";
|
|
||||||
import {Footer} from "@/components/footer";
|
|
||||||
import {ThemeProvider} from "@/components/providers/theme-provider";
|
|
||||||
import {UserDataProvider} from "@/components/providers/userdata-provider";
|
|
||||||
|
|
||||||
|
|
||||||
export function Providers({children}: { children: React.ReactNode }) {
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeProvider
|
|
||||||
attribute="class"
|
|
||||||
defaultTheme="system"
|
|
||||||
enableSystem
|
|
||||||
>
|
|
||||||
<UserDataProvider>
|
|
||||||
{children}
|
|
||||||
</UserDataProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {IUserData} from "@/interfaces/userdata.interface";
|
|
||||||
import {useEncodedLocalStorage} from "@/services/localStorage";
|
|
||||||
|
|
||||||
export interface IUserDataProvider {
|
|
||||||
userData: IUserData | undefined;
|
|
||||||
setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const UserDataContext = React.createContext<IUserDataProvider | undefined>(undefined);
|
|
||||||
|
|
||||||
export const UserDataProvider = ({children}: {children: React.ReactNode}) => {
|
|
||||||
const [userData, setUserData] = useEncodedLocalStorage<IUserData | undefined>("user_data", undefined);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<UserDataContext.Provider value={{userData, setUserData}}>
|
|
||||||
{children}
|
|
||||||
</UserDataContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
128
src/components/ui/tracing-beam.tsx
Normal file
128
src/components/ui/tracing-beam.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
"use client";
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import {
|
||||||
|
motion,
|
||||||
|
useTransform,
|
||||||
|
useScroll,
|
||||||
|
useVelocity,
|
||||||
|
useSpring,
|
||||||
|
} from "framer-motion";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
export const TracingBeam = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const { scrollYProgress } = useScroll({
|
||||||
|
target: ref,
|
||||||
|
offset: ["start start", "end start"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [svgHeight, setSvgHeight] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (contentRef.current) {
|
||||||
|
setSvgHeight(contentRef.current.offsetHeight);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const y1 = useSpring(
|
||||||
|
useTransform(scrollYProgress, [0, 0.8], [50, svgHeight]),
|
||||||
|
{
|
||||||
|
stiffness: 500,
|
||||||
|
damping: 90,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const y2 = useSpring(
|
||||||
|
useTransform(scrollYProgress, [0, 1], [50, svgHeight - 200]),
|
||||||
|
{
|
||||||
|
stiffness: 500,
|
||||||
|
damping: 90,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("relative w-full max-w-4xl mx-auto h-full", className)}
|
||||||
|
>
|
||||||
|
<div className="absolute -left-4 md:-left-20 top-3">
|
||||||
|
<motion.div
|
||||||
|
transition={{
|
||||||
|
duration: 0.2,
|
||||||
|
delay: 0.5,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
boxShadow:
|
||||||
|
scrollYProgress.get() > 0
|
||||||
|
? "none"
|
||||||
|
: "rgba(0, 0, 0, 0.24) 0px 3px 8px",
|
||||||
|
}}
|
||||||
|
className="ml-[27px] h-4 w-4 rounded-full border border-netural-200 shadow-sm flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
transition={{
|
||||||
|
duration: 0.2,
|
||||||
|
delay: 0.5,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
backgroundColor:
|
||||||
|
scrollYProgress.get() > 0 ? "white" : "var(--emerald-500)",
|
||||||
|
borderColor:
|
||||||
|
scrollYProgress.get() > 0 ? "white" : "var(--emerald-600)",
|
||||||
|
}}
|
||||||
|
className="h-2 w-2 rounded-full border border-neutral-300 bg-white"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
<svg
|
||||||
|
viewBox={`0 0 20 ${svgHeight}`}
|
||||||
|
width="20"
|
||||||
|
height={svgHeight} // Set the SVG height
|
||||||
|
className=" ml-4 block"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<motion.path
|
||||||
|
d={`M 1 0V -36 l 18 24 V ${svgHeight * 0.8} l -18 24V ${svgHeight}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="#9091A0"
|
||||||
|
strokeOpacity="0.16"
|
||||||
|
transition={{
|
||||||
|
duration: 10,
|
||||||
|
}}
|
||||||
|
></motion.path>
|
||||||
|
<motion.path
|
||||||
|
d={`M 1 0V -36 l 18 24 V ${svgHeight * 0.8} l -18 24V ${svgHeight}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="url(#gradient)"
|
||||||
|
strokeWidth="1.25"
|
||||||
|
className="motion-reduce:hidden"
|
||||||
|
transition={{
|
||||||
|
duration: 10,
|
||||||
|
}}
|
||||||
|
></motion.path>
|
||||||
|
<defs>
|
||||||
|
<motion.linearGradient
|
||||||
|
id="gradient"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="0"
|
||||||
|
x2="0"
|
||||||
|
y1={y1} // set y1 for gradient
|
||||||
|
y2={y2} // set y2 for gradient
|
||||||
|
>
|
||||||
|
<stop stopColor="#18CCFC" stopOpacity="0"></stop>
|
||||||
|
<stop stopColor="#18CCFC"></stop>
|
||||||
|
<stop offset="0.325" stopColor="#6344F5"></stop>
|
||||||
|
<stop offset="1" stopColor="#AE48FF" stopOpacity="0"></stop>
|
||||||
|
</motion.linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div ref={contentRef}>{children}</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import {IUserData} from "@/interfaces/userdata.interface";
|
|
||||||
|
|
||||||
// ----- Request -----
|
|
||||||
|
|
||||||
export interface IApiRegisterReq {
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
pseudo: string;
|
|
||||||
city: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
age: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IApiLoginReq {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----- Response -----
|
|
||||||
|
|
||||||
export interface IAbstractApiResponse {
|
|
||||||
message?: Array<string>;
|
|
||||||
error?: string;
|
|
||||||
statusCode?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IApiRegisterRes extends IAbstractApiResponse {
|
|
||||||
access_token?: string;
|
|
||||||
user?: IUserData
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IApiLoginRes extends IAbstractApiResponse {
|
|
||||||
access_token?: string
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
export interface IUserData {
|
|
||||||
id: string;
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
pseudo: string;
|
|
||||||
email: string;
|
|
||||||
roleId: string;
|
|
||||||
isActive: boolean;
|
|
||||||
city: string;
|
|
||||||
dollarAvailables: number;
|
|
||||||
age: number;
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { createContext, useContext, useState } from 'react';
|
|
||||||
import {IUserData} from "@/interfaces/userdata.interface";
|
|
||||||
import {IApiLoginReq, IApiLoginRes, IApiRegisterReq, IApiRegisterRes} from "@/interfaces/api.interface";
|
|
||||||
import ApiRequest from "@/services/apiRequest";
|
|
||||||
import {useEncodedLocalStorage} from "@/services/localStorage";
|
|
||||||
|
|
||||||
const UserDataContext = createContext<IUserData | null>(null)
|
|
||||||
const [userData, setUserData] = useEncodedLocalStorage<IUserData | null>("user_data", null)
|
|
||||||
|
|
||||||
//TODO Run register task
|
|
||||||
export async function doRegister(registerData: IApiRegisterReq): Promise<IApiRegisterRes | null> {
|
|
||||||
console.trace(registerData)
|
|
||||||
try {
|
|
||||||
const ReqRes = await ApiRequest.standard.post.json<IApiRegisterReq, IApiRegisterRes>("auth/signup", registerData)
|
|
||||||
console.trace(ReqRes.data)
|
|
||||||
if (ReqRes.data.user) {
|
|
||||||
setUserData(ReqRes.data.user)
|
|
||||||
}
|
|
||||||
ReqRes.data.message?.forEach((err)=> console.warn(err))
|
|
||||||
return ReqRes.data
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error during registration:', error);
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Run login task
|
|
||||||
export async function doLogin(loginData: IApiLoginReq) {
|
|
||||||
try {
|
|
||||||
const ReqRes = await ApiRequest.standard.post.json<IApiLoginReq, IApiLoginRes>("auth/login", loginData)
|
|
||||||
console.trace(ReqRes.data)
|
|
||||||
//if (ReqRes.data.user) {
|
|
||||||
// setUserData(ReqRes.data.user)
|
|
||||||
//}
|
|
||||||
ReqRes.data.message?.forEach((err)=> console.warn(err))
|
|
||||||
return ReqRes.data
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error during login:', err);
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Run disconnect task
|
|
||||||
export function doDisconnect() {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
window.localStorage.removeItem('sub')
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
console.log('Whut ? Why trying to remove an item from the localStorage when runner in SSR ?')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Run update user data
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import axios, {type AxiosResponse} from "axios";
|
import axios, {type AxiosResponse} from "axios";
|
||||||
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3333'
|
const baseUrl = ""
|
||||||
|
|
||||||
const AxiosConfigs = {
|
const AxiosConfigs = {
|
||||||
authenticated: {
|
authenticated: {
|
||||||
@@ -10,7 +10,7 @@ const AxiosConfigs = {
|
|||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
Authorization: `Bearer ${typeof window !== 'undefined' ? window.localStorage.getItem('sub') : "not-ssr"}`,
|
Authorization: `Bearer ${localStorage.getItem('sub')}`,
|
||||||
},
|
},
|
||||||
validateStatus: function (status: number) {
|
validateStatus: function (status: number) {
|
||||||
return status < 500; // Resolve only if the status code is less than 500
|
return status < 500; // Resolve only if the status code is less than 500
|
||||||
@@ -32,30 +32,72 @@ const AxiosConfigs = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doAuthenticatedJsonPostReq<ReqT, ResT>(route:string, body:ReqT): Promise<AxiosResponse<ResT>> {
|
/**
|
||||||
|
* Makes an authenticated JSON POST request using axios.
|
||||||
|
* @param {string} route - The route to send the request to.
|
||||||
|
* @param {object} body - The request body.
|
||||||
|
* @returns {Promise<AxiosResponse<ReqT, ResT>>} - The promise that resolves to the response from the server.
|
||||||
|
*/
|
||||||
|
async function doAuthenticatedJsonPostReq<ReqT, ResT>(route:string, body:object): Promise<AxiosResponse<ReqT, ResT>> {
|
||||||
return await axios.post(baseUrl + route, body, AxiosConfigs.authenticated.json())
|
return await axios.post(baseUrl + route, body, AxiosConfigs.authenticated.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doAuthenticatedGetReq<ResT>(route:string): Promise<AxiosResponse<ResT>> {
|
/**
|
||||||
|
* Makes an authenticated GET request to the specified route using Axios.
|
||||||
|
*
|
||||||
|
* @param {string} route - The route to which the GET request is sent.
|
||||||
|
* @returns {Promise<AxiosResponse>} - A promise that resolves to the Axios response object containing the request data and response details.
|
||||||
|
*/
|
||||||
|
async function doAuthenticatedGetReq<ReqT, ResT>(route:string): Promise<AxiosResponse<ReqT, ResT>> {
|
||||||
return await axios.get(baseUrl + route, AxiosConfigs.authenticated.json())
|
return await axios.get(baseUrl + route, AxiosConfigs.authenticated.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doAuthenticatedPatchReq<ReqT, ResT>(route:string, body: ReqT): Promise<AxiosResponse<ResT>> {
|
/**
|
||||||
|
* Performs an authenticated PATCH request to the specified route with the given body.
|
||||||
|
*
|
||||||
|
* @param {string} route - The route to send the PATCH request to.
|
||||||
|
* @param {object} body - The body of the request.
|
||||||
|
* @returns {Promise<AxiosResponse<ReqT, ResT>>} - A Promise that resolves to the AxiosResponse object containing the response data.
|
||||||
|
*/
|
||||||
|
async function doAuthenticatedPatchReq<ReqT, ResT>(route:string, body: object): Promise<AxiosResponse<ReqT, ResT>> {
|
||||||
return await axios.patch(baseUrl + route, body, AxiosConfigs.authenticated.json())
|
return await axios.patch(baseUrl + route, body, AxiosConfigs.authenticated.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doAuthenticatedDelReq<ResT>(route:string): Promise<AxiosResponse<ResT>> {
|
/**
|
||||||
|
* Sends an authenticated DELETE request to the specified route.
|
||||||
|
*
|
||||||
|
* @param {string} route - The route to send the request to.
|
||||||
|
*
|
||||||
|
* @return {Promise<AxiosResponse<ReqT, ResT>>} A Promise that resolves to the AxiosResponse object containing the response data.
|
||||||
|
*/
|
||||||
|
async function doAuthenticatedDelReq<ReqT, ResT>(route:string): Promise<AxiosResponse<ReqT, ResT>> {
|
||||||
return await axios.delete(baseUrl + route, AxiosConfigs.authenticated.json())
|
return await axios.delete(baseUrl + route, AxiosConfigs.authenticated.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO form/multipart req
|
//TODO form/multipart req
|
||||||
|
|
||||||
|
|
||||||
async function doJsonPostReq<ReqT, ResT>(route:string, body: ReqT): Promise<AxiosResponse<ResT>> {
|
/**
|
||||||
|
* Perform a JSON POST request.
|
||||||
|
*
|
||||||
|
* @param {string} route - The route to send the request to.
|
||||||
|
* @param {object} body - The JSON object to send in the request body.
|
||||||
|
*
|
||||||
|
* @return {Promise<AxiosResponse<ReqT, ResT>>} - A promise that resolves with the response from the server.
|
||||||
|
*
|
||||||
|
* @throws {Error} - If an error occurs during the request.
|
||||||
|
*/
|
||||||
|
async function doJsonPostReq<ReqT, ResT>(route:string, body: object): Promise<AxiosResponse<ReqT, ResT>> {
|
||||||
return await axios.post(baseUrl + route, body, AxiosConfigs.standard.json())
|
return await axios.post(baseUrl + route, body, AxiosConfigs.standard.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doJsonGetReq<ResT>(route:string): Promise<AxiosResponse<ResT>> {
|
/**
|
||||||
|
* Perform a JSON GET request using Axios.
|
||||||
|
*
|
||||||
|
* @param {string} route - The route URL to make the GET request to.
|
||||||
|
* @returns {Promise<AxiosResponse<ReqT, ResT>>} - A promise that resolves to the AxiosResponse object.
|
||||||
|
*/
|
||||||
|
async function doJsonGetReq<ReqT, ResT>(route:string): Promise<AxiosResponse<ReqT, ResT>> {
|
||||||
return await axios.get(baseUrl + route, AxiosConfigs.standard.json());
|
return await axios.get(baseUrl + route, AxiosConfigs.standard.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, {useEffect, useRef, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom React hook that allows you to store and retrieve data in the browser's localStorage.
|
* A custom React hook that allows you to store and retrieve data in the browser's localStorage.
|
||||||
@@ -48,20 +48,12 @@ export function useLocalStorage<T>(key: string, initial: T): [T, React.Dispatch<
|
|||||||
* @return {readonly [T, React.Dispatch<React.SetStateAction<T>>]} - An array containing the encoded value and a function to update the encoded value.
|
* @return {readonly [T, React.Dispatch<React.SetStateAction<T>>]} - An array containing the encoded value and a function to update the encoded value.
|
||||||
*/
|
*/
|
||||||
export function useEncodedLocalStorage<T>(key: string, fallbackValue: T): readonly [T, React.Dispatch<React.SetStateAction<T>>] {
|
export function useEncodedLocalStorage<T>(key: string, fallbackValue: T): readonly [T, React.Dispatch<React.SetStateAction<T>>] {
|
||||||
console.log("Pong !")
|
|
||||||
const [encodedValue, setEncodedValue] = useState<T>(() => {
|
const [encodedValue, setEncodedValue] = useState<T>(() => {
|
||||||
const stored = localStorage.getItem(key);
|
const stored = localStorage.getItem(key);
|
||||||
return stored ? safelyParse(stored, fallbackValue) : fallbackValue;
|
return stored ? safelyParse(stored, fallbackValue) : fallbackValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
const prevValue = useRef(encodedValue);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log({encodedValue})
|
|
||||||
if (!b64ValEqual(prevValue.current, encodedValue)) {
|
|
||||||
localStorage.setItem(key, safelyStringify(encodedValue));
|
localStorage.setItem(key, safelyStringify(encodedValue));
|
||||||
}
|
|
||||||
prevValue.current = encodedValue; // Set ref to current value
|
|
||||||
}, [key, encodedValue]);
|
}, [key, encodedValue]);
|
||||||
return [encodedValue, setEncodedValue] as const;
|
return [encodedValue, setEncodedValue] as const;
|
||||||
|
|
||||||
@@ -72,12 +64,6 @@ export function useEncodedLocalStorage<T>(key: string, fallbackValue: T): readon
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function b64ValEqual<T>(v1: T, v2: T): boolean {
|
|
||||||
return btoa(JSON.stringify(v1)) === btoa(JSON.stringify(v2));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function safelyStringify(value: T): string {
|
function safelyStringify(value: T): string {
|
||||||
try {
|
try {
|
||||||
return btoa(JSON.stringify(value));
|
return btoa(JSON.stringify(value));
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import type { Config } from "tailwindcss"
|
import type { Config } from "tailwindcss"
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import {default as flattenColorPalette} from "tailwindcss/lib/util/flattenColorPalette";
|
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
content: [
|
content: [
|
||||||
@@ -77,19 +74,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("tailwindcss-animate"), addVariablesForColors],
|
plugins: [require("tailwindcss-animate")],
|
||||||
} satisfies Config
|
} satisfies Config
|
||||||
|
|
||||||
// This plugin adds each Tailwind color as a global CSS variable, e.g. var(--gray-200).
|
|
||||||
function addVariablesForColors({ addBase, theme }: any) {
|
|
||||||
let allColors = flattenColorPalette(theme("colors"));
|
|
||||||
let newVars = Object.fromEntries(
|
|
||||||
Object.entries(allColors).map(([key, val]) => [`--${key}`, val])
|
|
||||||
);
|
|
||||||
|
|
||||||
addBase({
|
|
||||||
":root": newVars,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
Reference in New Issue
Block a user