+>(({ className, ...props }, ref) => (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+))
+TableCell.displayName = "TableCell"
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableCaption.displayName = "TableCaption"
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx
new file mode 100644
index 0000000..0f4caeb
--- /dev/null
+++ b/components/ui/tabs.tsx
@@ -0,0 +1,55 @@
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx
new file mode 100644
index 0000000..e56b0af
--- /dev/null
+++ b/components/ui/textarea.tsx
@@ -0,0 +1,22 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Textarea = React.forwardRef<
+ HTMLTextAreaElement,
+ React.ComponentProps<"textarea">
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/hooks/use-mobile.tsx b/hooks/use-mobile.tsx
index 2b0fe1d..a4b4fb4 100644
--- a/hooks/use-mobile.tsx
+++ b/hooks/use-mobile.tsx
@@ -1,6 +1,6 @@
import * as React from "react"
-const MOBILE_BREAKPOINT = 768
+const MOBILE_BREAKPOINT = 985
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState(undefined)
diff --git a/lib/prisma.ts b/lib/prisma.ts
new file mode 100644
index 0000000..eee7550
--- /dev/null
+++ b/lib/prisma.ts
@@ -0,0 +1,8 @@
+import { PrismaClient } from "@prisma/client"
+
+const globalForPrisma = global as unknown as { prisma: PrismaClient }
+
+export const prisma = globalForPrisma.prisma || new PrismaClient()
+
+if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
+
diff --git a/middleware.ts b/middleware.ts
new file mode 100644
index 0000000..df98742
--- /dev/null
+++ b/middleware.ts
@@ -0,0 +1,5 @@
+export { auth as middleware } from "@/auth"
+
+export const config = {
+ matcher: "/account/dashboard/:path*",
+};
\ No newline at end of file
diff --git a/package.json b/package.json
index 00e7913..7d109e7 100644
--- a/package.json
+++ b/package.json
@@ -9,34 +9,57 @@
"lint": "next lint"
},
"dependencies": {
- "@radix-ui/react-dialog": "^1.1.5",
- "@radix-ui/react-progress": "^1.1.1",
- "@radix-ui/react-separator": "^1.1.1",
- "@radix-ui/react-slot": "^1.1.1",
- "@radix-ui/react-tooltip": "^1.1.7",
- "@radix-ui/themes": "^3.2.0",
+ "@hookform/resolvers": "^3.10.0",
+ "@prisma/client": "^6.3.1",
+ "@radix-ui/react-avatar": "^1.1.3",
+ "@radix-ui/react-collapsible": "^1.1.3",
+ "@radix-ui/react-dialog": "^1.1.6",
+ "@radix-ui/react-dropdown-menu": "^2.1.6",
+ "@radix-ui/react-label": "^2.1.2",
+ "@radix-ui/react-popover": "^1.1.6",
+ "@radix-ui/react-progress": "^1.1.2",
+ "@radix-ui/react-radio-group": "^1.2.3",
+ "@radix-ui/react-select": "^2.1.6",
+ "@radix-ui/react-separator": "^1.1.2",
+ "@radix-ui/react-slot": "^1.1.2",
+ "@radix-ui/react-switch": "^1.1.3",
+ "@radix-ui/react-tabs": "^1.1.3",
+ "@radix-ui/react-tooltip": "^1.1.8",
+ "@web3icons/react": "^4.0.8",
+ "axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "cmdk": "1.0.0",
"cookies-next": "^5.1.0",
- "framer-motion": "^12.1.0",
+ "framer-motion": "^12.4.3",
+ "geist": "^1.3.1",
"js-cookie": "^3.0.5",
"lucide-react": "^0.474.0",
- "next": "15.1.6",
+ "next": "^15.2.0-canary.62",
+ "next-auth": "^5.0.0-beta.25",
+ "next-themes": "^0.4.4",
+ "password-validator": "^5.3.0",
+ "prisma": "^6.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
+ "react-hook-form": "^7.54.2",
+ "react-icons": "^5.4.0",
"react-typed": "^2.0.12",
"tailwind-merge": "^2.6.0",
- "tailwindcss-animate": "^1.0.7"
+ "tailwindcss-animate": "^1.0.7",
+ "validator": "^13.12.0",
+ "zod": "^3.24.2"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@types/js-cookie": "^3.0.6",
- "@types/node": "^20.17.17",
- "@types/react": "^19.0.8",
+ "@types/node": "^20.17.19",
+ "@types/react": "^19.0.9",
"@types/react-dom": "^19.0.3",
- "eslint": "^9.19.0",
+ "@types/validator": "^13.12.2",
+ "eslint": "^9.20.1",
"eslint-config-next": "15.1.6",
- "postcss": "^8.5.1",
+ "postcss": "^8.5.2",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.3"
}
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
new file mode 100644
index 0000000..a9b76aa
--- /dev/null
+++ b/prisma/schema.prisma
@@ -0,0 +1,17 @@
+generator client {
+ provider = "prisma-client-js"
+}
+
+datasource db {
+ provider = "sqlite"
+ url = "file:./dev.db"
+}
+
+model User {
+ id String @id @default(cuid())
+ email String @unique
+ username String @unique
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
+
diff --git a/public/noise-dark.png b/public/noise-dark.png
new file mode 100644
index 0000000..6a53b81
Binary files /dev/null and b/public/noise-dark.png differ
diff --git a/public/noise-light.png b/public/noise-light.png
new file mode 100644
index 0000000..b8e59d3
Binary files /dev/null and b/public/noise-light.png differ
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 91f4b17..18a624b 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -1,73 +1,98 @@
import type { Config } from "tailwindcss"
+import { fontFamily } from "tailwindcss/defaultTheme"
import tailwindcssAnimate from "tailwindcss-animate"
const config = {
darkMode: ["class"],
- content: ["./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"],
+ content: ["./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}", "*.{js,ts,jsx,tsx,mdx}"],
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",
- },
- },
+ 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))'
+ },
+ sidebar: {
+ DEFAULT: 'hsl(var(--sidebar-background))',
+ foreground: 'hsl(var(--sidebar-foreground))',
+ primary: 'hsl(var(--sidebar-primary))',
+ 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
+ accent: 'hsl(var(--sidebar-accent))',
+ 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
+ border: 'hsl(var(--sidebar-border))',
+ ring: 'hsl(var(--sidebar-ring))'
+ }
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)'
+ },
+ fontFamily: {
+ sans: [
+ 'var(--font-sans)',
+ ...fontFamily.sans
+ ]
+ },
+ 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: [tailwindcssAnimate],
} satisfies Config
|