commit 67d05c891d769795fa8cdbdca6eb0cef67a30fed
parent 31606ea0d2f344c5d33cd2109ff5ad2546d2a219
Author: MTRNord <mtrnord1@gmail.com>
Date: Sat, 26 Apr 2025 21:56:59 +0200
react cleanups, Dockerfile and SSR improvements
Signed-off-by: MTRNord <mtrnord1@gmail.com>
Diffstat:
43 files changed, 566 insertions(+), 780 deletions(-)
diff --git a/.dockerignore b/.dockerignore
@@ -0,0 +1,7 @@
+Dockerfile
+.dockerignore
+node_modules
+npm-debug.log
+README.md
+.next
+.git
+\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
@@ -0,0 +1,62 @@
+# syntax=docker.io/docker/dockerfile:1
+
+FROM node:23-alpine AS base
+
+# Install dependencies only when needed
+FROM base AS deps
+# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+# Install dependencies based on the preferred package manager
+COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
+RUN \
+ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
+ elif [ -f package-lock.json ]; then npm ci; \
+ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
+ else echo "Lockfile not found." && exit 1; \
+ fi
+
+
+# Rebuild the source code only when needed
+FROM base AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN \
+ if [ -f yarn.lock ]; then yarn run build; \
+ elif [ -f package-lock.json ]; then npm run build; \
+ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
+ else echo "Lockfile not found." && exit 1; \
+ fi
+
+# Production image, copy all the files and run next
+FROM base AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+COPY --from=builder /app/public ./public
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+USER nextjs
+
+EXPOSE 3000
+
+ENV PORT=3000
+
+# server.js is created by next build from the standalone output
+# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
+ENV HOSTNAME="0.0.0.0"
+CMD ["node", "server.js"]
+\ No newline at end of file
diff --git a/next.config.ts b/next.config.ts
@@ -5,7 +5,8 @@ const nextConfig: NextConfig = {
experimental: {
ppr: 'incremental',
useCache: true,
- }
+ },
+ output: 'standalone'
};
const withBundleAnalyzer = analyzer({
diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts
@@ -1,5 +1,5 @@
import { type NextRequest, NextResponse } from "next/server"
-import { createSession, setSessionCookie } from "@/lib/auth"
+import { createSession } from "@/lib/auth"
export async function POST(request: NextRequest) {
try {
@@ -19,14 +19,11 @@ export async function POST(request: NextRequest) {
}
// Create a session token
- const token = await createSession(user)
+ await createSession(user)
// Create a response
const response = NextResponse.json({ success: true, user })
- // Set the session cookie
- await setSessionCookie(token, response)
-
return response
} catch (error) {
console.error("Login failed:", error)
diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts
@@ -1,5 +1,5 @@
import { NextResponse } from "next/server"
-import { clearSession } from "@/lib/auth"
+import { deleteSession } from "@/lib/auth"
export async function POST() {
try {
@@ -7,8 +7,7 @@ export async function POST() {
const response = NextResponse.json({ success: true })
// Clear the session cookie
- await clearSession(response)
-
+ await deleteSession()
return response
} catch (error) {
console.error("Logout failed:", error)
diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts
@@ -1,5 +1,5 @@
import { type NextRequest, NextResponse } from "next/server"
-import { createSession, setSessionCookie } from "@/lib/auth"
+import { createSession } from "@/lib/auth"
export async function POST(request: NextRequest) {
try {
@@ -19,14 +19,11 @@ export async function POST(request: NextRequest) {
}
// Create a session token
- const token = await createSession(user)
+ await createSession(user)
// Create a response
const response = NextResponse.json({ success: true, user })
- // Set the session cookie
- await setSessionCookie(token, response)
-
return response
} catch (error) {
console.error("Registration failed:", error)
diff --git a/src/app/api/auth/session/route.ts b/src/app/api/auth/session/route.ts
@@ -1,9 +1,9 @@
-import { type NextRequest, NextResponse } from "next/server"
+import { NextResponse } from "next/server"
import { getSessionUser } from "@/lib/auth"
-export async function GET(request: NextRequest) {
+export async function GET() {
try {
- const user = await getSessionUser(request)
+ const user = await getSessionUser()
if (!user) {
return NextResponse.json({ authenticated: false }, { status: 401 })
diff --git a/src/app/api/verify-matrix-token/route.ts b/src/app/api/verify-matrix-token/route.ts
@@ -28,6 +28,8 @@ export async function POST(request: Request) {
if (response.ok) {
const data = (await response.json()) as WellKnownResponse
if (data["m.homeserver"]?.base_url) {
+ // TODO: Fix me
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
homeserverUrl = data["m.homeserver"].base_url
}
}
diff --git a/src/app/dashboard/analytics/page.tsx b/src/app/dashboard/analytics/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useState } from "react"
-import { TrendingUp, Users } from "lucide-react"
+import { Users } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
@@ -15,35 +15,7 @@ import { BannedServerData, generateHeatmapData, getBannedServersConfig, getMonth
import { PlotlyChart } from "@/components/analytics/plotly-chart";
import { Button } from "@/components/ui/button";
import Link from "next/link";
-
-interface InfoCardWithTrendProps {
- title: string;
- changePercent: number;
- value: string | number;
- trendColorOverride?: string;
-}
-
-function InfoCardWithTrend({ title, changePercent, value, trendColorOverride }: InfoCardWithTrendProps) {
- const trendColor = changePercent > 0 ? "text-green-400" : "text-red-400"
- const trendIcon = changePercent > 0 ? <TrendingUp className="h-3.5 w-3.5 mr-1" /> : <TrendingUp className="h-3.5 w-3.5 mr-1 rotate-180" />
- const trendText = changePercent > 0 ? `+${changePercent}% from previous period` : `${changePercent}% from previous period`
- const trendClass = trendColorOverride || trendColor
-
- return (
- <Card className="border-gray-800 bg-gray-950">
- <CardHeader className="pb-2">
- <CardTitle className="text-sm font-medium">{title}</CardTitle>
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">{value}</div>
- <div className={`flex items-center text-xs ${trendClass}`}>
- {trendIcon}
- <span>{trendText}</span>
- </div>
- </CardContent>
- </Card>
- )
-}
+import InfoCardWithTrend from "@/components/analytics/info-card-with-trend";
// Mock data for charts
const roomActivityData: RoomActivityData[] = [
diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx
@@ -1,81 +1,13 @@
-import type React from "react"
+import type { ReactNode } from "react"
import { Suspense } from "react"
-import { redirect } from "next/navigation"
-import { getSessionUser } from "@/lib/auth"
import { DashboardHeader } from "../../components/dashboard/dashboard-header"
-
-// Mock data for teams/bots
-const mockTeams = [
- {
- id: "team1",
- name: "Main Community Bot",
- description: "Moderation for main community rooms",
- owner: "@admin:matrix.org",
- created: "2025-01-15T10:00:00Z",
- members: [
- {
- id: "user1",
- name: "@admin:matrix.org",
- role: "owner",
- avatar: null,
- joined: "2025-01-15T10:00:00Z",
- },
- {
- id: "user2",
- name: "@mod1:matrix.org",
- role: "moderator",
- avatar: null,
- joined: "2025-01-16T14:30:00Z",
- },
- {
- id: "user3",
- name: "@mod2:matrix.org",
- role: "moderator",
- avatar: null,
- joined: "2025-02-01T09:15:00Z",
- },
- ],
- rooms: ["#general:matrix.org", "#support:matrix.org", "#community:matrix.org"],
- },
- {
- id: "team2",
- name: "Development Team Bot",
- description: "Moderation for development-related rooms",
- owner: "@admin:matrix.org",
- created: "2025-02-10T11:30:00Z",
- members: [
- {
- id: "user1",
- name: "@admin:matrix.org",
- role: "owner",
- avatar: null,
- joined: "2025-02-10T11:30:00Z",
- },
- {
- id: "user4",
- name: "@dev1:matrix.org",
- role: "moderator",
- avatar: null,
- joined: "2025-02-11T13:45:00Z",
- },
- ],
- rooms: ["#development:matrix.org", "#coding:matrix.org"],
- },
-]
+import { mockTeams } from "./mockData"
export default async function DashboardLayout({
children,
}: {
- children: React.ReactNode
+ children: ReactNode
}) {
- // Check if the user is authenticated on the server
- const user = await getSessionUser()
-
- // If not authenticated, redirect to login
- if (!user) {
- redirect("/login")
- }
-
return (
<div className="flex min-h-screen flex-col bg-black text-white">
<Suspense
diff --git a/src/app/dashboard/overview/page.tsx b/src/app/dashboard/overview/page.tsx
@@ -1,35 +1,34 @@
-'use client';
-
import { AlertTriangle, Ban, CheckCircle } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
-import { useSearchParams } from "next/navigation"
-import { useEffect, useState } from "react"
import AddProtectedRoom from "../../../components/modals/add-protected-room"
import ProtectedRomsList from "../../../components/dashboard/protected-rooms-list"
-import { mockPolicyLists, mockReports, mockTeams } from "../mockData"
+import { mockPolicyLists, mockReports, mockTeams, PolicyList, Report, Team } from "../mockData"
import TabNavigation from "../../../components/dashboard/tab-navigation";
-export default function OverviewPage() {
- const searchParams = useSearchParams()
- const teamIdParam = searchParams.get("team")
- const [selectedTeam, setSelectedTeam] = useState(mockTeams.find((t) => t.id === teamIdParam) || mockTeams[0])
+interface OverviewPageProps {
+ selectedTeam: Team;
+ reports: Report[];
+ policyLists: PolicyList[];
+}
+async function fetchData(teamIdParam?: string): Promise<OverviewPageProps> {
+ const selectedTeam = mockTeams.find((t) => t.id === teamIdParam) || mockTeams[0];
+ const reports = mockReports.filter((report) => report.teamId === selectedTeam.id);
+ const policyLists = mockPolicyLists.filter((list) => list.teamId === selectedTeam.id);
- // Update selected team when URL param changes
- useEffect(() => {
- const team = mockTeams.find((t) => t.id === teamIdParam)
- if (team) {
- setSelectedTeam(team)
- }
- }, [teamIdParam])
+ return {
+ selectedTeam,
+ reports,
+ policyLists,
+ };
+}
- // Filter data based on selected team
- const reports = mockReports.filter((report) => report.teamId === selectedTeam.id)
- const policyLists = mockPolicyLists.filter((list) => list.teamId === selectedTeam.id)
+export default async function OverviewPage({ searchParams }: { searchParams: Promise<{ team?: string }> }) {
+ const { team } = await searchParams;
+ const { selectedTeam, reports, policyLists } = await fetchData(team);
return (
-
<>
<TabNavigation selectedTeam={selectedTeam} currentTab="overview" />
<div className="space-y-8">
@@ -140,4 +139,4 @@ export default function OverviewPage() {
</div>
</>
)
-}
+}
+\ No newline at end of file
diff --git a/src/app/dashboard/settings/page.tsx b/src/app/dashboard/settings/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import { useEffect, useState } from "react"
-import { UserPlus, Settings, Crown, Trash2, LogOut } from "lucide-react"
+import { useState } from "react"
+import { UserPlus, Settings, Trash2, LogOut } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
@@ -22,8 +22,6 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { ScrollArea } from "@/components/ui/scroll-area"
@@ -33,34 +31,15 @@ import { mockTeams } from "../mockData"
import AddProtectedRoom from "../../../components/modals/add-protected-room"
import ProtectedRomsList from "../../../components/dashboard/protected-rooms-list"
import TabNavigation from "../../../components/dashboard/tab-navigation"
-import CreateBotModal from "../../../components/modals/create-bot";
export default function TeamManagement() {
const searchParams = useSearchParams()
const teamIdParam = searchParams.get("team")
- const [selectedTeam, setSelectedTeam] = useState(mockTeams.find((t) => t.id === teamIdParam) || mockTeams[0])
- const [activeTab, setActiveTab] = useState("members")
-
- // Update selected team when URL param changes
- useEffect(() => {
- const team = mockTeams.find((t) => t.id === teamIdParam)
- if (team) {
- setSelectedTeam(team)
- }
- }, [teamIdParam])
+ const [selectedTeam] = useState(mockTeams.find((t) => t.id === teamIdParam) || mockTeams[0])
- const handleTeamChange = (teamId: string) => {
- const team = mockTeams.find((t) => t.id === teamId)
- if (team) {
- setSelectedTeam(team)
-
- // Update URL with team parameter
- const url = new URL(window.location.href)
- url.searchParams.set("team", teamId)
- window.history.pushState({}, "", url.toString())
- }
- }
+ // TODO: Make this actual pages so we can use more ssr
+ const [activeTab, setActiveTab] = useState("members")
return (
<>
@@ -72,29 +51,6 @@ export default function TeamManagement() {
<CardTitle>Team Management</CardTitle>
<CardDescription className="text-gray-400">Manage your bots and team members</CardDescription>
</div>
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button variant="outline" className="border-purple-500 text-purple-400 hover:bg-purple-950">
- <Crown className="mr-2 h-4 w-4" />
- {selectedTeam.name}
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent className="w-56 bg-gray-900 border-gray-800">
- <DropdownMenuLabel>Your Bots</DropdownMenuLabel>
- <DropdownMenuSeparator className="bg-gray-800" />
- {mockTeams.map((team) => (
- <DropdownMenuItem
- key={team.id}
- className={selectedTeam.id === team.id ? "bg-gray-800" : ""}
- onClick={() => handleTeamChange(team.id)}
- >
- {team.name}
- </DropdownMenuItem>
- ))}
- <DropdownMenuSeparator className="bg-gray-800" />
- <CreateBotModal />
- </DropdownMenuContent>
- </DropdownMenu>
</div>
</CardHeader>
<CardContent>
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
@@ -22,6 +22,8 @@ export default function LoginPage() {
setStep("login")
}
+ // TODO: Fix me
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleLoginSuccess = async (token: string) => {
try {
// Use the login function from session context
diff --git a/src/app/page.tsx b/src/app/page.tsx
@@ -4,6 +4,8 @@ import { Shield, Users, BarChart, Settings, AlertTriangle, CheckCircle, Ban, Eye
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+export const experimental_ppr = true
+
export default function Home() {
return (
<div className="flex min-h-screen flex-col bg-black text-white">
diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx
@@ -22,6 +22,8 @@ export default function RegisterPage() {
setStep("login")
}
+ // TODO: Fix me
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleLoginSuccess = async (token: string) => {
try {
// Use the register function from session context
diff --git a/src/components/analytics/info-card-with-trend.tsx b/src/components/analytics/info-card-with-trend.tsx
@@ -0,0 +1,31 @@
+import { TrendingDown, TrendingUp } from "lucide-react";
+import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
+
+interface InfoCardWithTrendProps {
+ title: string;
+ changePercent: number;
+ value: string | number;
+ trendColorOverride?: string;
+}
+
+export default function InfoCardWithTrend({ title, changePercent, value, trendColorOverride }: InfoCardWithTrendProps) {
+ const trendColor = changePercent > 0 ? "text-green-400" : "text-red-400"
+ const trendIcon = changePercent > 0 ? <TrendingUp className="h-3.5 w-3.5 mr-1" /> : <TrendingDown className="h-3.5 w-3.5 mr-1" />
+ const trendText = changePercent > 0 ? `+${changePercent}% from previous period` : `${changePercent}% from previous period`
+ const trendClass = trendColorOverride || trendColor
+
+ return (
+ <Card className="border-gray-800 bg-gray-950">
+ <CardHeader className="pb-2">
+ <CardTitle className="text-sm font-medium">{title}</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="text-2xl font-bold">{value}</div>
+ <div className={`flex items-center text-xs ${trendClass}`}>
+ {trendIcon}
+ <span>{trendText}</span>
+ </div>
+ </CardContent>
+ </Card>
+ )
+}
+\ No newline at end of file
diff --git a/src/components/dashboard/dashboard-header.tsx b/src/components/dashboard/dashboard-header.tsx
@@ -22,7 +22,7 @@ import { useSession } from "@/contexts/session-context"
import AddBan from "../modals/add-ban"
import KickUserModal from "../modals/kick-user"
import CreateBotModal from "../modals/create-bot"
-import { mockPolicyLists } from "../../app/dashboard/mockData"
+import { mockPolicyLists, Team } from "../../app/dashboard/mockData"
interface Notification {
id: string
@@ -34,12 +34,6 @@ interface Notification {
teamId?: string
}
-interface Team {
- id: string
- name: string
- description: string
-}
-
interface DashboardHeaderProps {
teams: Team[]
}
diff --git a/src/components/matrix-login-form.tsx b/src/components/matrix-login-form.tsx
@@ -31,6 +31,8 @@ export function MatrixLoginForm({ matrixId, onSuccess, onCancel }: MatrixLoginFo
// Extract username and server from Matrix ID
const serverName = matrixId.includes(":") ? matrixId.split(":").pop() || "matrix.org" : "matrix.org"
+ // TODO: Fix me
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
const username = matrixId.startsWith("@")
? matrixId.includes(":")
? matrixId.split(":")[0].substring(1)
@@ -111,6 +113,8 @@ export function MatrixLoginForm({ matrixId, onSuccess, onCancel }: MatrixLoginFo
await new Promise((resolve) => setTimeout(resolve, 1000))
// Simulate access token
+ // TODO: Fix me
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
const mockAccessToken = "syt_" + Math.random().toString(36).substring(2, 15)
// Step 2: Request an OpenID token
diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx
@@ -1,7 +1,7 @@
-import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
+import { forwardRef, HTMLAttributes } from "react"
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
@@ -19,9 +19,9 @@ const alertVariants = cva(
}
)
-const Alert = React.forwardRef<
+const Alert = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
+ HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
@@ -32,9 +32,9 @@ const Alert = React.forwardRef<
))
Alert.displayName = "Alert"
-const AlertTitle = React.forwardRef<
+const AlertTitle = forwardRef<
HTMLParagraphElement,
- React.HTMLAttributes<HTMLHeadingElement>
+ HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
@@ -44,9 +44,9 @@ const AlertTitle = React.forwardRef<
))
AlertTitle.displayName = "AlertTitle"
-const AlertDescription = React.forwardRef<
+const AlertDescription = forwardRef<
HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
+ HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
@@ -56,4 +56,4 @@ const AlertDescription = React.forwardRef<
))
AlertDescription.displayName = "AlertDescription"
-export { Alert, AlertTitle, AlertDescription }
+export { Alert, AlertTitle, AlertDescription }
+\ No newline at end of file
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
@@ -1,15 +1,13 @@
-"use client"
-
-import * as React from "react"
-import * as AvatarPrimitive from "@radix-ui/react-avatar"
+import { Root, Image, Fallback } from "@radix-ui/react-avatar"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef } from "react"
-const Avatar = React.forwardRef<
- React.ElementRef<typeof AvatarPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
+const Avatar = forwardRef<
+ ComponentRef<typeof Root>,
+ ComponentPropsWithoutRef<typeof Root>
>(({ className, ...props }, ref) => (
- <AvatarPrimitive.Root
+ <Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
@@ -18,25 +16,26 @@ const Avatar = React.forwardRef<
{...props}
/>
))
-Avatar.displayName = AvatarPrimitive.Root.displayName
+Avatar.displayName = Root.displayName
-const AvatarImage = React.forwardRef<
- React.ElementRef<typeof AvatarPrimitive.Image>,
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
+const AvatarImage = forwardRef<
+ ComponentRef<typeof Image>,
+ ComponentPropsWithoutRef<typeof Image>
>(({ className, ...props }, ref) => (
- <AvatarPrimitive.Image
+ // eslint-disable-next-line jsx-a11y/alt-text
+ <Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
))
-AvatarImage.displayName = AvatarPrimitive.Image.displayName
+AvatarImage.displayName = Image.displayName
-const AvatarFallback = React.forwardRef<
- React.ElementRef<typeof AvatarPrimitive.Fallback>,
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
+const AvatarFallback = forwardRef<
+ ComponentRef<typeof Fallback>,
+ ComponentPropsWithoutRef<typeof Fallback>
>(({ className, ...props }, ref) => (
- <AvatarPrimitive.Fallback
+ <Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
@@ -45,6 +44,6 @@ const AvatarFallback = React.forwardRef<
{...props}
/>
))
-AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
+AvatarFallback.displayName = Fallback.displayName
export { Avatar, AvatarImage, AvatarFallback }
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
@@ -1,7 +1,7 @@
-import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
+import { HTMLAttributes } from "react"
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",
@@ -24,8 +24,8 @@ const badgeVariants = cva(
)
export interface BadgeProps
- extends React.HTMLAttributes<HTMLDivElement>,
- VariantProps<typeof badgeVariants> {}
+ extends HTMLAttributes<HTMLDivElement>,
+ VariantProps<typeof badgeVariants> { }
function Badge({ className, variant, ...props }: BadgeProps) {
return (
diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx
@@ -1,20 +1,20 @@
-import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentPropsWithoutRef, ReactNode, ComponentProps } from "react"
-const Breadcrumb = React.forwardRef<
+const Breadcrumb = forwardRef<
HTMLElement,
- React.ComponentPropsWithoutRef<"nav"> & {
- separator?: React.ReactNode
+ ComponentPropsWithoutRef<"nav"> & {
+ separator?: ReactNode
}
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
Breadcrumb.displayName = "Breadcrumb"
-const BreadcrumbList = React.forwardRef<
+const BreadcrumbList = forwardRef<
HTMLOListElement,
- React.ComponentPropsWithoutRef<"ol">
+ ComponentPropsWithoutRef<"ol">
>(({ className, ...props }, ref) => (
<ol
ref={ref}
@@ -27,9 +27,9 @@ const BreadcrumbList = React.forwardRef<
))
BreadcrumbList.displayName = "BreadcrumbList"
-const BreadcrumbItem = React.forwardRef<
+const BreadcrumbItem = forwardRef<
HTMLLIElement,
- React.ComponentPropsWithoutRef<"li">
+ ComponentPropsWithoutRef<"li">
>(({ className, ...props }, ref) => (
<li
ref={ref}
@@ -39,9 +39,9 @@ const BreadcrumbItem = React.forwardRef<
))
BreadcrumbItem.displayName = "BreadcrumbItem"
-const BreadcrumbLink = React.forwardRef<
+const BreadcrumbLink = forwardRef<
HTMLAnchorElement,
- React.ComponentPropsWithoutRef<"a"> & {
+ ComponentPropsWithoutRef<"a"> & {
asChild?: boolean
}
>(({ asChild, className, ...props }, ref) => {
@@ -57,9 +57,9 @@ const BreadcrumbLink = React.forwardRef<
})
BreadcrumbLink.displayName = "BreadcrumbLink"
-const BreadcrumbPage = React.forwardRef<
+const BreadcrumbPage = forwardRef<
HTMLSpanElement,
- React.ComponentPropsWithoutRef<"span">
+ ComponentPropsWithoutRef<"span">
>(({ className, ...props }, ref) => (
<span
ref={ref}
@@ -76,7 +76,7 @@ const BreadcrumbSeparator = ({
children,
className,
...props
-}: React.ComponentProps<"li">) => (
+}: ComponentProps<"li">) => (
<li
role="presentation"
aria-hidden="true"
@@ -91,7 +91,7 @@ BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
const BreadcrumbEllipsis = ({
className,
...props
-}: React.ComponentProps<"span">) => (
+}: ComponentProps<"span">) => (
<span
role="presentation"
aria-hidden="true"
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
@@ -1,8 +1,8 @@
-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"
+import { ButtonHTMLAttributes, forwardRef } from "react"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@@ -34,12 +34,12 @@ const buttonVariants = cva(
)
export interface ButtonProps
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
- VariantProps<typeof buttonVariants> {
+ extends ButtonHTMLAttributes<HTMLButtonElement>,
+ VariantProps<typeof buttonVariants> {
asChild?: boolean
}
-const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
+const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
@@ -1,10 +1,9 @@
-import * as React from "react"
-
import { cn } from "@/lib/utils"
+import { forwardRef, HTMLAttributes } from "react"
-const Card = React.forwardRef<
+const Card = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
@@ -17,9 +16,9 @@ const Card = React.forwardRef<
))
Card.displayName = "Card"
-const CardHeader = React.forwardRef<
+const CardHeader = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
@@ -29,9 +28,9 @@ const CardHeader = React.forwardRef<
))
CardHeader.displayName = "CardHeader"
-const CardTitle = React.forwardRef<
+const CardTitle = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
@@ -44,9 +43,9 @@ const CardTitle = React.forwardRef<
))
CardTitle.displayName = "CardTitle"
-const CardDescription = React.forwardRef<
+const CardDescription = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
@@ -56,17 +55,17 @@ const CardDescription = React.forwardRef<
))
CardDescription.displayName = "CardDescription"
-const CardContent = React.forwardRef<
+const CardContent = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
-const CardFooter = React.forwardRef<
+const CardFooter = forwardRef<
HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx
@@ -1,16 +1,16 @@
"use client"
-import * as React from "react"
-import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { Root, Indicator } from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef } from "react"
-const Checkbox = React.forwardRef<
- React.ElementRef<typeof CheckboxPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
+const Checkbox = forwardRef<
+ ComponentRef<typeof Root>,
+ ComponentPropsWithoutRef<typeof Root>
>(({ className, ...props }, ref) => (
- <CheckboxPrimitive.Root
+ <Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
@@ -18,13 +18,13 @@ const Checkbox = React.forwardRef<
)}
{...props}
>
- <CheckboxPrimitive.Indicator
+ <Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
- </CheckboxPrimitive.Indicator>
- </CheckboxPrimitive.Root>
+ </Indicator>
+ </Root>
))
-Checkbox.displayName = CheckboxPrimitive.Root.displayName
+Checkbox.displayName = Root.displayName
export { Checkbox }
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
@@ -1,24 +1,24 @@
"use client"
-import * as React from "react"
-import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { Root, Trigger, Portal, Close, Overlay, Content, Title, Description } from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef, HTMLAttributes } from "react"
-const Dialog = DialogPrimitive.Root
+const Dialog = Root
-const DialogTrigger = DialogPrimitive.Trigger
+const DialogTrigger = Trigger
-const DialogPortal = DialogPrimitive.Portal
+const DialogPortal = Portal
-const DialogClose = DialogPrimitive.Close
+const DialogClose = Close
-const DialogOverlay = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Overlay>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
+const DialogOverlay = forwardRef<
+ ComponentRef<typeof Overlay>,
+ ComponentPropsWithoutRef<typeof Overlay>
>(({ className, ...props }, ref) => (
- <DialogPrimitive.Overlay
+ <Overlay
ref={ref}
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",
@@ -27,15 +27,15 @@ const DialogOverlay = React.forwardRef<
{...props}
/>
))
-DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+DialogOverlay.displayName = Overlay.displayName
-const DialogContent = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
+const DialogContent = forwardRef<
+ ComponentRef<typeof Content>,
+ ComponentPropsWithoutRef<typeof Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
- <DialogPrimitive.Content
+ <Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
@@ -44,19 +44,19 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
- <DialogPrimitive.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-accent data-[state=open]:text-muted-foreground">
+ <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-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
- </DialogPrimitive.Close>
- </DialogPrimitive.Content>
+ </Close>
+ </Content>
</DialogPortal>
))
-DialogContent.displayName = DialogPrimitive.Content.displayName
+DialogContent.displayName = Content.displayName
const DialogHeader = ({
className,
...props
-}: React.HTMLAttributes<HTMLDivElement>) => (
+}: HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
@@ -70,7 +70,7 @@ DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
-}: React.HTMLAttributes<HTMLDivElement>) => (
+}: HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
@@ -81,11 +81,11 @@ const DialogFooter = ({
)
DialogFooter.displayName = "DialogFooter"
-const DialogTitle = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Title>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
+const DialogTitle = forwardRef<
+ ComponentRef<typeof Title>,
+ ComponentPropsWithoutRef<typeof Title>
>(({ className, ...props }, ref) => (
- <DialogPrimitive.Title
+ <Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
@@ -94,19 +94,19 @@ const DialogTitle = React.forwardRef<
{...props}
/>
))
-DialogTitle.displayName = DialogPrimitive.Title.displayName
+DialogTitle.displayName = Title.displayName
-const DialogDescription = React.forwardRef<
- React.ElementRef<typeof DialogPrimitive.Description>,
- React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
+const DialogDescription = forwardRef<
+ ComponentRef<typeof Description>,
+ ComponentPropsWithoutRef<typeof Description>
>(({ className, ...props }, ref) => (
- <DialogPrimitive.Description
+ <Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
-DialogDescription.displayName = DialogPrimitive.Description.displayName
+DialogDescription.displayName = Description.displayName
export {
Dialog,
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
@@ -1,30 +1,30 @@
"use client"
-import * as React from "react"
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
+import { Root, Trigger, Group, Portal, Sub, RadioGroup, SubTrigger, Content, Item, SubContent, CheckboxItem, ItemIndicator, RadioItem, Label, Separator } from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef, HTMLAttributes } from "react"
-const DropdownMenu = DropdownMenuPrimitive.Root
+const DropdownMenu = Root
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+const DropdownMenuTrigger = Trigger
-const DropdownMenuGroup = DropdownMenuPrimitive.Group
+const DropdownMenuGroup = Group
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+const DropdownMenuPortal = Portal
-const DropdownMenuSub = DropdownMenuPrimitive.Sub
+const DropdownMenuSub = Sub
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+const DropdownMenuRadioGroup = RadioGroup
-const DropdownMenuSubTrigger = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
+const DropdownMenuSubTrigger = forwardRef<
+ ComponentRef<typeof SubTrigger>,
+ ComponentPropsWithoutRef<typeof SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
- <DropdownMenuPrimitive.SubTrigger
+ <SubTrigger
ref={ref}
className={cn(
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@@ -35,16 +35,16 @@ const DropdownMenuSubTrigger = React.forwardRef<
>
{children}
<ChevronRight className="ml-auto" />
- </DropdownMenuPrimitive.SubTrigger>
+ </SubTrigger>
))
DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName
+ SubTrigger.displayName
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
+const DropdownMenuSubContent = forwardRef<
+ ComponentRef<typeof SubContent>,
+ ComponentPropsWithoutRef<typeof SubContent>
>(({ className, ...props }, ref) => (
- <DropdownMenuPrimitive.SubContent
+ <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",
@@ -54,14 +54,14 @@ const DropdownMenuSubContent = React.forwardRef<
/>
))
DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName
+ SubContent.displayName
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
+const DropdownMenuContent = forwardRef<
+ ComponentRef<typeof Content>,
+ ComponentPropsWithoutRef<typeof Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
- <DropdownMenuPrimitive.Portal>
- <DropdownMenuPrimitive.Content
+ <Portal>
+ <Content
ref={ref}
sideOffset={sideOffset}
className={cn(
@@ -70,17 +70,17 @@ const DropdownMenuContent = React.forwardRef<
)}
{...props}
/>
- </DropdownMenuPrimitive.Portal>
+ </Portal>
))
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
+DropdownMenuContent.displayName = Content.displayName
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Item>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
+const DropdownMenuItem = forwardRef<
+ ComponentRef<typeof Item>,
+ ComponentPropsWithoutRef<typeof Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
- <DropdownMenuPrimitive.Item
+ <Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center gap-2 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@@ -90,13 +90,13 @@ const DropdownMenuItem = React.forwardRef<
{...props}
/>
))
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
+DropdownMenuItem.displayName = Item.displayName
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
+const DropdownMenuCheckboxItem = forwardRef<
+ ComponentRef<typeof CheckboxItem>,
+ ComponentPropsWithoutRef<typeof CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
- <DropdownMenuPrimitive.CheckboxItem
+ <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",
@@ -106,21 +106,21 @@ const DropdownMenuCheckboxItem = React.forwardRef<
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <DropdownMenuPrimitive.ItemIndicator>
+ <ItemIndicator>
<Check className="h-4 w-4" />
- </DropdownMenuPrimitive.ItemIndicator>
+ </ItemIndicator>
</span>
{children}
- </DropdownMenuPrimitive.CheckboxItem>
+ </CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName
+ CheckboxItem.displayName
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
+const DropdownMenuRadioItem = forwardRef<
+ ComponentRef<typeof RadioItem>,
+ ComponentPropsWithoutRef<typeof RadioItem>
>(({ className, children, ...props }, ref) => (
- <DropdownMenuPrimitive.RadioItem
+ <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",
@@ -129,22 +129,22 @@ const DropdownMenuRadioItem = React.forwardRef<
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <DropdownMenuPrimitive.ItemIndicator>
+ <ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
- </DropdownMenuPrimitive.ItemIndicator>
+ </ItemIndicator>
</span>
{children}
- </DropdownMenuPrimitive.RadioItem>
+ </RadioItem>
))
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
+DropdownMenuRadioItem.displayName = RadioItem.displayName
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Label>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
+const DropdownMenuLabel = forwardRef<
+ ComponentRef<typeof Label>,
+ ComponentPropsWithoutRef<typeof Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
- <DropdownMenuPrimitive.Label
+ <Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
@@ -154,24 +154,24 @@ const DropdownMenuLabel = React.forwardRef<
{...props}
/>
))
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
+DropdownMenuLabel.displayName = Label.displayName
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
+const DropdownMenuSeparator = forwardRef<
+ ComponentRef<typeof Separator>,
+ ComponentPropsWithoutRef<typeof Separator>
>(({ className, ...props }, ref) => (
- <DropdownMenuPrimitive.Separator
+ <Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
+DropdownMenuSeparator.displayName = Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
-}: React.HTMLAttributes<HTMLSpanElement>) => {
+}: HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
@@ -1,8 +1,7 @@
-import * as React from "react"
-
import { cn } from "@/lib/utils"
+import { ComponentProps, forwardRef } from "react"
-const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
+const Input = forwardRef<HTMLInputElement, ComponentProps<"input">>(
({ className, type, ...props }, ref) => {
return (
<input
@@ -19,4 +18,4 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
)
Input.displayName = "Input"
-export { Input }
+export { Input }
+\ No newline at end of file
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx
@@ -1,26 +1,26 @@
"use client"
-import * as React from "react"
-import * as LabelPrimitive from "@radix-ui/react-label"
+import { Root } from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef } from "react"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
-const Label = React.forwardRef<
- React.ElementRef<typeof LabelPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
- VariantProps<typeof labelVariants>
+const Label = forwardRef<
+ ComponentRef<typeof Root>,
+ ComponentPropsWithoutRef<typeof Root> &
+ VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
- <LabelPrimitive.Root
+ <Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
-Label.displayName = LabelPrimitive.Root.displayName
+Label.displayName = Root.displayName
export { Label }
diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx
@@ -1,10 +1,10 @@
-import * as React from "react"
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils"
import { ButtonProps, buttonVariants } from "@/components/ui/button"
+import { ComponentProps, forwardRef } from "react"
-const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
+const Pagination = ({ className, ...props }: ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
@@ -14,9 +14,9 @@ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
)
Pagination.displayName = "Pagination"
-const PaginationContent = React.forwardRef<
+const PaginationContent = forwardRef<
HTMLUListElement,
- React.ComponentProps<"ul">
+ ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
@@ -26,9 +26,9 @@ const PaginationContent = React.forwardRef<
))
PaginationContent.displayName = "PaginationContent"
-const PaginationItem = React.forwardRef<
+const PaginationItem = forwardRef<
HTMLLIElement,
- React.ComponentProps<"li">
+ ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
))
@@ -37,7 +37,7 @@ PaginationItem.displayName = "PaginationItem"
type PaginationLinkProps = {
isActive?: boolean
} & Pick<ButtonProps, "size"> &
- React.ComponentProps<"a">
+ ComponentProps<"a">
const PaginationLink = ({
className,
@@ -62,7 +62,7 @@ PaginationLink.displayName = "PaginationLink"
const PaginationPrevious = ({
className,
...props
-}: React.ComponentProps<typeof PaginationLink>) => (
+}: ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
@@ -78,7 +78,7 @@ PaginationPrevious.displayName = "PaginationPrevious"
const PaginationNext = ({
className,
...props
-}: React.ComponentProps<typeof PaginationLink>) => (
+}: ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
@@ -94,7 +94,7 @@ PaginationNext.displayName = "PaginationNext"
const PaginationEllipsis = ({
className,
...props
-}: React.ComponentProps<"span">) => (
+}: ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
@@ -1,20 +1,20 @@
"use client"
-import * as React from "react"
-import * as PopoverPrimitive from "@radix-ui/react-popover"
+import { Root, Trigger, Content, Portal } from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef } from "react"
-const Popover = PopoverPrimitive.Root
+const Popover = Root
-const PopoverTrigger = PopoverPrimitive.Trigger
+const PopoverTrigger = Trigger
-const PopoverContent = React.forwardRef<
- React.ElementRef<typeof PopoverPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
+const PopoverContent = forwardRef<
+ ComponentRef<typeof Content>,
+ ComponentPropsWithoutRef<typeof Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
- <PopoverPrimitive.Portal>
- <PopoverPrimitive.Content
+ <Portal>
+ <Content
ref={ref}
align={align}
sideOffset={sideOffset}
@@ -24,8 +24,8 @@ const PopoverContent = React.forwardRef<
)}
{...props}
/>
- </PopoverPrimitive.Portal>
+ </Portal>
))
-PopoverContent.displayName = PopoverPrimitive.Content.displayName
+PopoverContent.displayName = Content.displayName
export { Popover, PopoverTrigger, PopoverContent }
diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx
@@ -1,48 +1,48 @@
"use client"
-import * as React from "react"
-import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
+import { Root, Viewport, Corner, ScrollAreaScrollbar, ScrollAreaThumb } from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
+import { forwardRef, ComponentRef, ComponentPropsWithoutRef } from "react"
-const ScrollArea = React.forwardRef<
- React.ElementRef<typeof ScrollAreaPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
+const ScrollArea = forwardRef<
+ ComponentRef<typeof Root>,
+ ComponentPropsWithoutRef<typeof Root>
>(({ className, children, ...props }, ref) => (
- <ScrollAreaPrimitive.Root
+ <Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
- <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
+ <Viewport className="h-full w-full rounded-[inherit]">
{children}
- </ScrollAreaPrimitive.Viewport>
+ </Viewport>
<ScrollBar />
- <ScrollAreaPrimitive.Corner />
- </ScrollAreaPrimitive.Root>
+ <Corner />
+ </Root>
))
-ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
+ScrollArea.displayName = Root.displayName
-const ScrollBar = React.forwardRef<
- React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
- React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
+const ScrollBar = forwardRef<
+ ComponentRef<typeof ScrollAreaScrollbar>,
+ ComponentPropsWithoutRef<typeof ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
- <ScrollAreaPrimitive.ScrollAreaScrollbar
+ <ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
- "h-full w-2.5 border-l border-l-transparent p-[1px]",
+ "h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
- "h-2.5 flex-col border-t border-t-transparent p-[1px]",
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]",
className
)}
{...props}
>
- <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
- </ScrollAreaPrimitive.ScrollAreaScrollbar>
+ <ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
+ </ScrollAreaScrollbar>
))
-ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
+ScrollBar.displayName = ScrollAreaScrollbar.displayName
export { ScrollArea, ScrollBar }
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
@@ -1,22 +1,22 @@
"use client"
-import * as React from "react"
-import * as SelectPrimitive from "@radix-ui/react-select"
+import { Root, Group, Value, Trigger, Icon, ScrollUpButton, ScrollDownButton, Content, Portal, Viewport, Label, Item, ItemIndicator, ItemText, Separator } from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils"
+import React, { forwardRef, ComponentRef, ComponentPropsWithoutRef } from "react"
-const Select = SelectPrimitive.Root
+const Select = Root
-const SelectGroup = SelectPrimitive.Group
+const SelectGroup = Group
-const SelectValue = SelectPrimitive.Value
+const SelectValue = Value
-const SelectTrigger = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Trigger>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
+const SelectTrigger = forwardRef<
+ ComponentRef<typeof Trigger>,
+ ComponentPropsWithoutRef<typeof Trigger>
>(({ className, children, ...props }, ref) => (
- <SelectPrimitive.Trigger
+ <Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
@@ -25,18 +25,18 @@ const SelectTrigger = React.forwardRef<
{...props}
>
{children}
- <SelectPrimitive.Icon asChild>
+ <Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
- </SelectPrimitive.Icon>
- </SelectPrimitive.Trigger>
+ </Icon>
+ </Trigger>
))
-SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+SelectTrigger.displayName = Trigger.displayName
-const SelectScrollUpButton = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
+const SelectScrollUpButton = forwardRef<
+ ComponentRef<typeof ScrollUpButton>,
+ ComponentPropsWithoutRef<typeof ScrollUpButton>
>(({ className, ...props }, ref) => (
- <SelectPrimitive.ScrollUpButton
+ <ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
@@ -45,15 +45,15 @@ const SelectScrollUpButton = React.forwardRef<
{...props}
>
<ChevronUp className="h-4 w-4" />
- </SelectPrimitive.ScrollUpButton>
+ </ScrollUpButton>
))
-SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+SelectScrollUpButton.displayName = ScrollUpButton.displayName
-const SelectScrollDownButton = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
+const SelectScrollDownButton = forwardRef<
+ ComponentRef<typeof ScrollDownButton>,
+ ComponentPropsWithoutRef<typeof ScrollDownButton>
>(({ className, ...props }, ref) => (
- <SelectPrimitive.ScrollDownButton
+ <ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
@@ -62,60 +62,60 @@ const SelectScrollDownButton = React.forwardRef<
{...props}
>
<ChevronDown className="h-4 w-4" />
- </SelectPrimitive.ScrollDownButton>
+ </ScrollDownButton>
))
SelectScrollDownButton.displayName =
- SelectPrimitive.ScrollDownButton.displayName
+ ScrollDownButton.displayName
-const SelectContent = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
+const SelectContent = forwardRef<
+ ComponentRef<typeof Content>,
+ ComponentPropsWithoutRef<typeof Content>
>(({ className, children, position = "popper", ...props }, ref) => (
- <SelectPrimitive.Portal>
- <SelectPrimitive.Content
+ <Portal>
+ <Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover 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",
position === "popper" &&
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
- <SelectPrimitive.Viewport
+ <Viewport
className={cn(
"p-1",
position === "popper" &&
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
- </SelectPrimitive.Viewport>
+ </Viewport>
<SelectScrollDownButton />
- </SelectPrimitive.Content>
- </SelectPrimitive.Portal>
+ </Content>
+ </Portal>
))
-SelectContent.displayName = SelectPrimitive.Content.displayName
+SelectContent.displayName = Content.displayName
-const SelectLabel = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Label>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
+const SelectLabel = forwardRef<
+ ComponentRef<typeof Label>,
+ ComponentPropsWithoutRef<typeof Label>
>(({ className, ...props }, ref) => (
- <SelectPrimitive.Label
+ <Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
))
-SelectLabel.displayName = SelectPrimitive.Label.displayName
+SelectLabel.displayName = Label.displayName
-const SelectItem = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Item>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
+const SelectItem = forwardRef<
+ ComponentRef<typeof Item>,
+ ComponentPropsWithoutRef<typeof Item>
>(({ className, children, ...props }, ref) => (
- <SelectPrimitive.Item
+ <Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
@@ -124,27 +124,27 @@ const SelectItem = React.forwardRef<
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
- <SelectPrimitive.ItemIndicator>
+ <ItemIndicator>
<Check className="h-4 w-4" />
- </SelectPrimitive.ItemIndicator>
+ </ItemIndicator>
</span>
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
- </SelectPrimitive.Item>
+ <ItemText>{children}</ItemText>
+ </Item>
))
-SelectItem.displayName = SelectPrimitive.Item.displayName
+SelectItem.displayName = Item.displayName
-const SelectSeparator = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Separator>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
+const SelectSeparator = forwardRef<
+ ComponentRef<typeof Separator>,
+ ComponentPropsWithoutRef<typeof Separator>
>(({ className, ...props }, ref) => (
- <SelectPrimitive.Separator
+ <Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
-SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+SelectSeparator.displayName = Separator.displayName
export {
Select,
diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx
@@ -1,19 +1,19 @@
"use client"
-import * as React from "react"
-import * as SeparatorPrimitive from "@radix-ui/react-separator"
+import { Root } from "@radix-ui/react-separator"
import { cn } from "@/lib/utils"
+import { ComponentPropsWithoutRef, ComponentRef, forwardRef } from "react"
-const Separator = React.forwardRef<
- React.ElementRef<typeof SeparatorPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
+const Separator = forwardRef<
+ ComponentRef<typeof Root>,
+ ComponentPropsWithoutRef<typeof Root>
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref
) => (
- <SeparatorPrimitive.Root
+ <Root
ref={ref}
decorative={decorative}
orientation={orientation}
@@ -26,6 +26,6 @@ const Separator = React.forwardRef<
/>
)
)
-Separator.displayName = SeparatorPrimitive.Root.displayName
+Separator.displayName = Root.displayName
export { Separator }
diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx
@@ -1,25 +1,25 @@
"use client"
-import * as React from "react"
-import * as SheetPrimitive from "@radix-ui/react-dialog"
+import { Root, Trigger, Close, Portal, Overlay, Content, Title, Description } from "@radix-ui/react-dialog"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
+import { ComponentPropsWithoutRef, ComponentRef, forwardRef, HTMLAttributes } from "react"
-const Sheet = SheetPrimitive.Root
+const Sheet = Root
-const SheetTrigger = SheetPrimitive.Trigger
+const SheetTrigger = Trigger
-const SheetClose = SheetPrimitive.Close
+const SheetClose = Close
-const SheetPortal = SheetPrimitive.Portal
+const SheetPortal = Portal
-const SheetOverlay = React.forwardRef<
- React.ElementRef<typeof SheetPrimitive.Overlay>,
- React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
+const SheetOverlay = forwardRef<
+ ComponentRef<typeof Overlay>,
+ ComponentPropsWithoutRef<typeof Overlay>
>(({ className, ...props }, ref) => (
- <SheetPrimitive.Overlay
+ <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
@@ -28,7 +28,7 @@ const SheetOverlay = React.forwardRef<
ref={ref}
/>
))
-SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+SheetOverlay.displayName = 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",
@@ -50,34 +50,34 @@ const sheetVariants = cva(
)
interface SheetContentProps
- extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
- VariantProps<typeof sheetVariants> {}
+ extends ComponentPropsWithoutRef<typeof Content>,
+ VariantProps<typeof sheetVariants> { }
-const SheetContent = React.forwardRef<
- React.ElementRef<typeof SheetPrimitive.Content>,
+const SheetContent = forwardRef<
+ ComponentRef<typeof Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
- <SheetPrimitive.Content
+ <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">
+ <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>
+ </Close>
+ </Content>
</SheetPortal>
))
-SheetContent.displayName = SheetPrimitive.Content.displayName
+SheetContent.displayName = Content.displayName
const SheetHeader = ({
className,
...props
-}: React.HTMLAttributes<HTMLDivElement>) => (
+}: HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
@@ -91,7 +91,7 @@ SheetHeader.displayName = "SheetHeader"
const SheetFooter = ({
className,
...props
-}: React.HTMLAttributes<HTMLDivElement>) => (
+}: HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
@@ -102,29 +102,29 @@ const SheetFooter = ({
)
SheetFooter.displayName = "SheetFooter"
-const SheetTitle = React.forwardRef<
- React.ElementRef<typeof SheetPrimitive.Title>,
- React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
+const SheetTitle = forwardRef<
+ ComponentRef<typeof Title>,
+ ComponentPropsWithoutRef<typeof Title>
>(({ className, ...props }, ref) => (
- <SheetPrimitive.Title
+ <Title
ref={ref}
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
))
-SheetTitle.displayName = SheetPrimitive.Title.displayName
+SheetTitle.displayName = Title.displayName
-const SheetDescription = React.forwardRef<
- React.ElementRef<typeof SheetPrimitive.Description>,
- React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
+const SheetDescription = forwardRef<
+ ComponentRef<typeof Description>,
+ ComponentPropsWithoutRef<typeof Description>
>(({ className, ...props }, ref) => (
- <SheetPrimitive.Description
+ <Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
-SheetDescription.displayName = SheetPrimitive.Description.displayName
+SheetDescription.displayName = Description.displayName
export {
Sheet,
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
@@ -1,9 +1,10 @@
import { cn } from "@/lib/utils"
+import { HTMLAttributes } from "react"
function Skeleton({
className,
...props
-}: React.HTMLAttributes<HTMLDivElement>) {
+}: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-muted", className)}
diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx
@@ -1,10 +1,10 @@
-import * as React from "react"
import { cn } from "@/lib/utils"
+import { forwardRef, HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes } from "react"
-const Table = React.forwardRef<
+const Table = forwardRef<
HTMLTableElement,
- React.HTMLAttributes<HTMLTableElement>
+ HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
@@ -16,17 +16,17 @@ const Table = React.forwardRef<
))
Table.displayName = "Table"
-const TableHeader = React.forwardRef<
+const TableHeader = forwardRef<
HTMLTableSectionElement,
- React.HTMLAttributes<HTMLTableSectionElement>
+ HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
-const TableBody = React.forwardRef<
+const TableBody = forwardRef<
HTMLTableSectionElement,
- React.HTMLAttributes<HTMLTableSectionElement>
+ HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
@@ -36,9 +36,9 @@ const TableBody = React.forwardRef<
))
TableBody.displayName = "TableBody"
-const TableFooter = React.forwardRef<
+const TableFooter = forwardRef<
HTMLTableSectionElement,
- React.HTMLAttributes<HTMLTableSectionElement>
+ HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
@@ -51,9 +51,9 @@ const TableFooter = React.forwardRef<
))
TableFooter.displayName = "TableFooter"
-const TableRow = React.forwardRef<
+const TableRow = forwardRef<
HTMLTableRowElement,
- React.HTMLAttributes<HTMLTableRowElement>
+ HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
@@ -66,9 +66,9 @@ const TableRow = React.forwardRef<
))
TableRow.displayName = "TableRow"
-const TableHead = React.forwardRef<
+const TableHead = forwardRef<
HTMLTableCellElement,
- React.ThHTMLAttributes<HTMLTableCellElement>
+ ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
@@ -81,9 +81,9 @@ const TableHead = React.forwardRef<
))
TableHead.displayName = "TableHead"
-const TableCell = React.forwardRef<
+const TableCell = forwardRef<
HTMLTableCellElement,
- React.TdHTMLAttributes<HTMLTableCellElement>
+ TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
@@ -93,9 +93,9 @@ const TableCell = React.forwardRef<
))
TableCell.displayName = "TableCell"
-const TableCaption = React.forwardRef<
+const TableCaption = forwardRef<
HTMLTableCaptionElement,
- React.HTMLAttributes<HTMLTableCaptionElement>
+ HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
@@ -1,17 +1,17 @@
"use client"
-import * as React from "react"
-import * as TabsPrimitive from "@radix-ui/react-tabs"
+import { Root, List, Trigger, Content } from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils"
+import { ComponentPropsWithoutRef, ComponentRef, forwardRef } from "react"
-const Tabs = TabsPrimitive.Root
+const Tabs = Root
-const TabsList = React.forwardRef<
- React.ElementRef<typeof TabsPrimitive.List>,
- React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
+const TabsList = forwardRef<
+ ComponentRef<typeof List>,
+ ComponentPropsWithoutRef<typeof List>
>(({ className, ...props }, ref) => (
- <TabsPrimitive.List
+ <List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
@@ -20,13 +20,13 @@ const TabsList = React.forwardRef<
{...props}
/>
))
-TabsList.displayName = TabsPrimitive.List.displayName
+TabsList.displayName = List.displayName
-const TabsTrigger = React.forwardRef<
- React.ElementRef<typeof TabsPrimitive.Trigger>,
- React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
+const TabsTrigger = forwardRef<
+ ComponentRef<typeof Trigger>,
+ ComponentPropsWithoutRef<typeof Trigger>
>(({ className, ...props }, ref) => (
- <TabsPrimitive.Trigger
+ <Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
@@ -35,13 +35,13 @@ const TabsTrigger = React.forwardRef<
{...props}
/>
))
-TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+TabsTrigger.displayName = Trigger.displayName
-const TabsContent = React.forwardRef<
- React.ElementRef<typeof TabsPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
+const TabsContent = forwardRef<
+ ComponentRef<typeof Content>,
+ ComponentPropsWithoutRef<typeof Content>
>(({ className, ...props }, ref) => (
- <TabsPrimitive.Content
+ <Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
@@ -50,6 +50,6 @@ const TabsContent = React.forwardRef<
{...props}
/>
))
-TabsContent.displayName = TabsPrimitive.Content.displayName
+TabsContent.displayName = Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
@@ -1,8 +1,8 @@
-import * as React from "react"
import { cn } from "@/lib/utils"
+import { forwardRef } from "react"
-const Textarea = React.forwardRef<
+const Textarea = forwardRef<
HTMLTextAreaElement,
React.ComponentProps<"textarea">
>(({ className, ...props }, ref) => {
diff --git a/src/components/ui/use-mobile.tsx b/src/components/ui/use-mobile.tsx
@@ -1,11 +1,11 @@
-import * as React from "react"
+import { useEffect, useState } from "react"
const MOBILE_BREAKPOINT = 768
export function useIsMobile() {
- const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
+ const [isMobile, setIsMobile] = useState<boolean | undefined>(undefined)
- React.useEffect(() => {
+ useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
diff --git a/src/components/ui/use-toast.ts b/src/components/ui/use-toast.ts
@@ -1,195 +0,0 @@
-"use client"
-
-// Inspired by react-hot-toast library
-import * as React from "react"
-
-import type {
- ToastActionElement,
- ToastProps,
-} from "@/components/ui/toast"
-
-const TOAST_LIMIT = 1
-const TOAST_REMOVE_DELAY = 1000000
-
-type ToasterToast = ToastProps & {
- id: string
- title?: React.ReactNode
- description?: React.ReactNode
- action?: ToastActionElement
-}
-
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const actionTypes = {
- ADD_TOAST: "ADD_TOAST",
- UPDATE_TOAST: "UPDATE_TOAST",
- DISMISS_TOAST: "DISMISS_TOAST",
- REMOVE_TOAST: "REMOVE_TOAST",
-} as const
-
-let count = 0
-
-function genId() {
- count = (count + 1) % Number.MAX_SAFE_INTEGER
- return count.toString()
-}
-
-type ActionType = typeof actionTypes
-
-type Action =
- | {
- type: ActionType["ADD_TOAST"]
- toast: ToasterToast
- }
- | {
- type: ActionType["UPDATE_TOAST"]
- toast: Partial<ToasterToast>
- }
- | {
- type: ActionType["DISMISS_TOAST"]
- toastId?: ToasterToast["id"]
- }
- | {
- type: ActionType["REMOVE_TOAST"]
- toastId?: ToasterToast["id"]
- }
-
-interface State {
- toasts: ToasterToast[]
-}
-
-const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
-
-const addToRemoveQueue = (toastId: string) => {
- if (toastTimeouts.has(toastId)) {
- return
- }
-
- const timeout = setTimeout(() => {
- toastTimeouts.delete(toastId)
- dispatch({
- type: "REMOVE_TOAST",
- toastId: toastId,
- })
- }, TOAST_REMOVE_DELAY)
-
- toastTimeouts.set(toastId, timeout)
-}
-
-export const reducer = (state: State, action: Action): State => {
- switch (action.type) {
- case "ADD_TOAST":
- return {
- ...state,
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
- }
-
- case "UPDATE_TOAST":
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === action.toast.id ? { ...t, ...action.toast } : t
- ),
- }
-
- case "DISMISS_TOAST": {
- const { toastId } = action
-
- // ! Side effects ! - This could be extracted into a dismissToast() action,
- // but I'll keep it here for simplicity
- if (toastId) {
- addToRemoveQueue(toastId)
- } else {
- state.toasts.forEach((toast) => {
- addToRemoveQueue(toast.id)
- })
- }
-
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === toastId || toastId === undefined
- ? {
- ...t,
- open: false,
- }
- : t
- ),
- }
- }
- case "REMOVE_TOAST":
- if (action.toastId === undefined) {
- return {
- ...state,
- toasts: [],
- }
- }
- return {
- ...state,
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
- }
- }
-}
-
-const listeners: Array<(state: State) => void> = []
-
-let memoryState: State = { toasts: [] }
-
-function dispatch(action: Action) {
- memoryState = reducer(memoryState, action)
- listeners.forEach((listener) => {
- listener(memoryState)
- })
-}
-
-type Toast = Omit<ToasterToast, "id">
-
-function toast({ ...props }: Toast) {
- const id = genId()
-
- const update = (props: ToasterToast) =>
- dispatch({
- type: "UPDATE_TOAST",
- toast: { ...props, id },
- })
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
-
- dispatch({
- type: "ADD_TOAST",
- toast: {
- ...props,
- id,
- open: true,
- onOpenChange: (open: boolean) => {
- if (!open) dismiss()
- },
- },
- })
-
- return {
- id: id,
- dismiss,
- update,
- }
-}
-
-function useToast() {
- const [state, setState] = React.useState<State>(memoryState)
-
- React.useEffect(() => {
- listeners.push(setState)
- return () => {
- const index = listeners.indexOf(setState)
- if (index > -1) {
- listeners.splice(index, 1)
- }
- }
- }, [state])
-
- return {
- ...state,
- toast,
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
- }
-}
-
-export { useToast, toast }
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
@@ -1,11 +1,8 @@
import { cookies } from "next/headers"
-import type { NextRequest, NextResponse } from "next/server"
import { jwtVerify, SignJWT } from "jose"
-// Secret key for JWT signing - in production, use a proper secret management system
-const JWT_SECRET = new TextEncoder().encode(
- process.env.JWT_SECRET || "this_is_a_secret_key_that_should_be_in_env_variables",
-)
+const secretKey = process.env.SESSION_SECRET
+const encodedKey = new TextEncoder().encode(secretKey)
export type User = {
id: string
@@ -15,35 +12,68 @@ export type User = {
isAdmin: boolean
}
-export async function createSession(user: User): Promise<string> {
- // Create a JWT token with user data
- const token = await new SignJWT({ user })
- .setProtectedHeader({ alg: "HS256" })
+export async function encrypt(user: User) {
+ return new SignJWT({ user })
+ .setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
- .setExpirationTime("7d") // Token expires in 7 days
- .sign(JWT_SECRET)
+ .setExpirationTime('7d')
+ .sign(encodedKey)
+}
+
+export async function decrypt(session: string | undefined = '') {
+ try {
+ const { payload } = await jwtVerify(session, encodedKey, {
+ algorithms: ['HS256'],
+ })
+ return payload
+ } catch (error: unknown) {
+ console.log('Failed to verify session', error)
+ }
+}
- return token
+export async function createSession(user: User) {
+ const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+ const session = await encrypt(user)
+ const cookieStore = await cookies()
+
+ cookieStore.set('session', session, {
+ httpOnly: true,
+ secure: true,
+ expires: expiresAt,
+ sameSite: 'lax',
+ path: '/',
+ })
}
-export async function setSessionCookie(token: string, response: NextResponse): Promise<void> {
- // Set the session cookie with secure attributes
- response.cookies.set({
- name: "matrix_session",
- value: token,
+export async function updateSession() {
+ const session = (await cookies()).get('session')?.value
+ const payload = await decrypt(session)
+
+ if (!session || !payload) {
+ return null
+ }
+
+ const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
+
+ const cookieStore = await cookies()
+ cookieStore.set('session', session, {
httpOnly: true,
- secure: process.env.NODE_ENV === "production",
- sameSite: "lax",
- maxAge: 60 * 60 * 24 * 7, // 7 days
- path: "/",
+ secure: true,
+ expires: expires,
+ sameSite: 'lax',
+ path: '/',
})
}
-export async function getSessionUser(request?: NextRequest): Promise<User | null> {
+export async function deleteSession() {
+ const cookieStore = await cookies()
+ cookieStore.delete('session')
+}
+
+export async function getSessionUser(): Promise<User | null> {
try {
// Get the session cookie
- const cookieStore = request ? request.cookies : await cookies()
- const sessionCookie = cookieStore.get("matrix_session")
+ const sessionCookie = (await cookies()).get('session')
if (!sessionCookie?.value) {
return null
@@ -51,7 +81,7 @@ export async function getSessionUser(request?: NextRequest): Promise<User | null
// Verify the JWT token
try {
- const { payload } = await jwtVerify(sessionCookie.value, JWT_SECRET)
+ const { payload } = await jwtVerify(sessionCookie.value, encodedKey)
if (!payload.user) {
return null
@@ -66,17 +96,4 @@ export async function getSessionUser(request?: NextRequest): Promise<User | null
console.error("Failed to verify session:", error)
return null
}
-}
-
-export async function clearSession(response: NextResponse): Promise<void> {
- // Clear the session cookie
- response.cookies.set({
- name: "matrix_session",
- value: "",
- httpOnly: true,
- secure: process.env.NODE_ENV === "production",
- sameSite: "lax",
- maxAge: 0,
- path: "/",
- })
-}
+}
+\ No newline at end of file
diff --git a/src/middleware.ts b/src/middleware.ts
@@ -10,7 +10,7 @@ export async function middleware(request: NextRequest) {
const isPublicPath = path === "/" || path === "/login" || path === "/register"
// Check if the user is authenticated
- const user = await getSessionUser(request)
+ const user = await getSessionUser()
const isAuthenticated = !!user
// If the path is dashboard and the user is not authenticated, redirect to login