diff --git a/app/(protected)/admin/orders/page.tsx b/app/(protected)/admin/orders/page.tsx
deleted file mode 100644
index 2767c88..0000000
--- a/app/(protected)/admin/orders/page.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { redirect } from "next/navigation";
-
-import { getCurrentUser } from "@/lib/session";
-import { constructMetadata } from "@/lib/utils";
-import { Button } from "@/components/ui/button";
-import { DashboardHeader } from "@/components/dashboard/header";
-import { EmptyPlaceholder } from "@/components/shared/empty-placeholder";
-
-export const metadata = constructMetadata({
- title: "Orders – WR.DO",
- description: "Check and manage your latest orders.",
-});
-
-export default async function OrdersPage() {
- // const user = await getCurrentUser();
- // if (!user || user.role !== "ADMIN") redirect("/login");
-
- return (
- <>
-
-
-
- No orders listed
-
- You don't have any orders yet. Start ordering a product.
-
-
-
- >
- );
-}
diff --git a/app/(protected)/admin/page.tsx b/app/(protected)/admin/page.tsx
index cb49f94..68776a6 100644
--- a/app/(protected)/admin/page.tsx
+++ b/app/(protected)/admin/page.tsx
@@ -1,7 +1,12 @@
import { redirect } from "next/navigation";
+import { siteConfig } from "@/config/site";
+import { getUserRecordCount } from "@/lib/dto/cloudflare-dns-record";
+import { getUserShortUrlCount } from "@/lib/dto/short-urls";
+import { getAllUsersCount } from "@/lib/dto/user";
import { getCurrentUser } from "@/lib/session";
import { constructMetadata } from "@/lib/utils";
+import { DashboardInfoCard } from "@/components/dashboard/dashboard-info-card";
import { DashboardHeader } from "@/components/dashboard/header";
import InfoCard from "@/components/dashboard/info-card";
import TransactionsList from "@/components/dashboard/transactions-list";
@@ -13,7 +18,11 @@ export const metadata = constructMetadata({
export default async function AdminPage() {
const user = await getCurrentUser();
- if (!user || user.role !== "ADMIN") redirect("/login");
+ if (!user || !user.id || user.role !== "ADMIN") redirect("/login");
+
+ const user_count = await getAllUsersCount();
+ const record_count = await getUserRecordCount(user.id, 1, "ADMIN");
+ const url_count = await getUserShortUrlCount(user.id, 1, "ADMIN");
return (
<>
@@ -22,14 +31,27 @@ export default async function AdminPage() {
text="Access only for users with ADMIN role."
/>
-
>
);
diff --git a/app/(protected)/admin/orders/loading.tsx b/app/(protected)/admin/records/loading.tsx
similarity index 57%
rename from app/(protected)/admin/orders/loading.tsx
rename to app/(protected)/admin/records/loading.tsx
index b144afc..590d4c6 100644
--- a/app/(protected)/admin/orders/loading.tsx
+++ b/app/(protected)/admin/records/loading.tsx
@@ -1,13 +1,10 @@
import { Skeleton } from "@/components/ui/skeleton";
import { DashboardHeader } from "@/components/dashboard/header";
-export default function OrdersLoading() {
+export default function DashboardRecordsLoading() {
return (
<>
-
+
>
);
diff --git a/app/(protected)/admin/records/page.tsx b/app/(protected)/admin/records/page.tsx
new file mode 100644
index 0000000..27a87f8
--- /dev/null
+++ b/app/(protected)/admin/records/page.tsx
@@ -0,0 +1,33 @@
+import { redirect } from "next/navigation";
+
+import { getCurrentUser } from "@/lib/session";
+import { constructMetadata } from "@/lib/utils";
+import { DashboardHeader } from "@/components/dashboard/header";
+
+import UserRecordsList from "../../dashboard/records/record-list";
+
+export const metadata = constructMetadata({
+ title: "DNS Records - WR.DO",
+ description: "List and manage records.",
+});
+
+export default async function DashboardPage() {
+ const user = await getCurrentUser();
+
+ if (!user?.id) redirect("/login");
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/app/(protected)/admin/urls/loading.tsx b/app/(protected)/admin/urls/loading.tsx
new file mode 100644
index 0000000..a5e3a6b
--- /dev/null
+++ b/app/(protected)/admin/urls/loading.tsx
@@ -0,0 +1,11 @@
+import { Skeleton } from "@/components/ui/skeleton";
+import { DashboardHeader } from "@/components/dashboard/header";
+
+export default function DashboardUrlsLoading() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/app/(protected)/admin/urls/page.tsx b/app/(protected)/admin/urls/page.tsx
new file mode 100644
index 0000000..000e0c6
--- /dev/null
+++ b/app/(protected)/admin/urls/page.tsx
@@ -0,0 +1,33 @@
+import { redirect } from "next/navigation";
+
+import { getCurrentUser } from "@/lib/session";
+import { constructMetadata } from "@/lib/utils";
+import { DashboardHeader } from "@/components/dashboard/header";
+
+import UserUrlsList from "../../dashboard/urls/url-list";
+
+export const metadata = constructMetadata({
+ title: "Short URLs - WR.DO",
+ description: "List and manage records.",
+});
+
+export default async function DashboardPage() {
+ const user = await getCurrentUser();
+
+ if (!user?.id) redirect("/login");
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/app/(protected)/admin/users/user-list.tsx b/app/(protected)/admin/users/user-list.tsx
index 5332161..618de65 100644
--- a/app/(protected)/admin/users/user-list.tsx
+++ b/app/(protected)/admin/users/user-list.tsx
@@ -2,8 +2,9 @@
import { useState } from "react";
import Link from "next/link";
+import { Pagination } from "@nextui-org/pagination";
import { User } from "@prisma/client";
-import { PenLine, RefreshCwIcon } from "lucide-react";
+import { ArrowLeft, ArrowRight, PenLine, RefreshCwIcon } from "lucide-react";
import useSWR, { useSWRConfig } from "swr";
import { siteConfig } from "@/config/site";
@@ -18,6 +19,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
+import { PaginationWrapper } from "@/components/ui/pagination";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
@@ -73,10 +75,12 @@ export default function UsersList({ user }: UrlListProps) {
const { isMobile } = useMediaQuery();
const [isShowForm, setShowForm] = useState(false);
const [currentEditUser, setcurrentEditUser] = useState
(null);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(10);
const { mutate } = useSWRConfig();
- const { data, error, isLoading } = useSWR(
- "/api/user/admin",
+ const { data, error, isLoading } = useSWR<{ total: number; list: User[] }>(
+ `/api/user/admin?page=${currentPage}&size=${pageSize}`,
fetcher,
{
revalidateOnFocus: false,
@@ -84,21 +88,19 @@ export default function UsersList({ user }: UrlListProps) {
);
const handleRefresh = () => {
- mutate("/api/user/admin", undefined);
+ mutate(`/api/user/admin?page=${currentPage}&size=${pageSize}`, undefined);
};
return (
<>
-
-
- Total Users:{" "}
-
- {data && }
-
-
-
+
+ Total Users:{" "}
+
+ {data && }
+
+
)}
- {/* */}
@@ -165,59 +156,53 @@ export default function UsersList({ user }: UrlListProps) {
>
- ) : data && data.length > 0 ? (
- data
- .sort(
- (a, b) =>
- new Date(a.createdAt).getTime() -
- new Date(b.createdAt).getTime(),
- )
- .map((user) => (
-
-
- {user.name || "Anonymous"}
-
-
-
-
-
- {user.email}
-
- {user.email}
-
-
-
-
-
- {user.role}
-
-
-
-
-
-
- {timeAgo(user.createdAt || "")}
-
-
-
-
-
- ))
+ ) : data && data.list ? (
+ data.list.map((user) => (
+
+
+ {user.name || "Anonymous"}
+
+
+
+
+
+ {user.email}
+
+ {user.email}
+
+
+
+
+
+ {user.role}
+
+
+
+
+
+
+ {timeAgo(user.createdAt || "")}
+
+
+
+
+
+ ))
) : (
@@ -239,6 +224,13 @@ export default function UsersList({ user }: UrlListProps) {
)}
+ {data && Math.ceil(data.total / pageSize) > 1 && (
+
+ )}
diff --git a/app/(protected)/dashboard/info-card.tsx b/app/(protected)/dashboard/info-card.tsx
deleted file mode 100644
index cca0379..0000000
--- a/app/(protected)/dashboard/info-card.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import Link from "next/link";
-import { GlobeLock, Link as LinkIcon } from "lucide-react";
-
-import { siteConfig } from "@/config/site";
-import { getUserRecordCount } from "@/lib/dto/cloudflare-dns-record";
-import { getUserShortUrlCount } from "@/lib/dto/short-urls";
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { Skeleton } from "@/components/ui/skeleton";
-import CountUp from "@/components/dashboard/count-up";
-
-export async function DNSInfoCard({ userId }: { userId: string }) {
- const count = await getUserRecordCount(userId);
-
- return (
-
-
-
-
- DNS Records
-
-
-
-
-
- {[-1, undefined].includes(count) ? (
-
- ) : (
-
-
-
- / {siteConfig.freeQuota.record}
-
-
- )}
- total
-
-
- );
-}
-
-export async function UrlsInfoCard({ userId }: { userId: string }) {
- const count = await getUserShortUrlCount(userId);
- return (
-
-
-
-
- Short URLs
-
-
-
-
-
- {[-1, undefined].includes(count) ? (
-
- ) : (
-
-
-
- / {siteConfig.freeQuota.url}
-
-
- )}
- total
-
-
- );
-}
diff --git a/app/(protected)/dashboard/page.tsx b/app/(protected)/dashboard/page.tsx
index 0096ad9..4d95ad8 100644
--- a/app/(protected)/dashboard/page.tsx
+++ b/app/(protected)/dashboard/page.tsx
@@ -1,12 +1,17 @@
import Link from "next/link";
import { redirect } from "next/navigation";
+import { siteConfig } from "@/config/site";
+import { getUserRecordCount } from "@/lib/dto/cloudflare-dns-record";
+import { getUserShortUrlCount } from "@/lib/dto/short-urls";
import { getCurrentUser } from "@/lib/session";
import { constructMetadata } from "@/lib/utils";
import { DashboardHeader } from "@/components/dashboard/header";
-import InfoCard from "@/components/dashboard/info-card";
-import { DNSInfoCard, UrlsInfoCard } from "./info-card";
+import {
+ DashboardInfoCard,
+ HeroCard,
+} from "../../../components/dashboard/dashboard-info-card";
import UserRecordsList from "./records/record-list";
import UserUrlsList from "./urls/url-list";
@@ -20,6 +25,9 @@ export default async function DashboardPage() {
if (!user?.id) redirect("/login");
+ const record_count = await getUserRecordCount(user.id);
+ const url_count = await getUserShortUrlCount(user.id);
+
return (
<>
-
-
- Free Records & URLs
-
-
- Learn more from{" "}
-
- documents.
- {" "}
-
-
-
-
+
+
+
-
-
+
+
>
);
diff --git a/app/(protected)/dashboard/records/page.tsx b/app/(protected)/dashboard/records/page.tsx
index f55fa38..9a33412 100644
--- a/app/(protected)/dashboard/records/page.tsx
+++ b/app/(protected)/dashboard/records/page.tsx
@@ -24,7 +24,10 @@ export default async function DashboardPage() {
link="/docs/dns-records"
linkText="DNS Records."
/>
-
+
>
);
}
diff --git a/app/(protected)/dashboard/records/record-list.tsx b/app/(protected)/dashboard/records/record-list.tsx
index ab39f0d..04408e4 100644
--- a/app/(protected)/dashboard/records/record-list.tsx
+++ b/app/(protected)/dashboard/records/record-list.tsx
@@ -3,12 +3,7 @@
import { useState } from "react";
import Link from "next/link";
import { User } from "@prisma/client";
-import {
- ArrowUpRight,
- DotSquareIcon,
- PenLine,
- RefreshCwIcon,
-} from "lucide-react";
+import { PenLine, RefreshCwIcon } from "lucide-react";
import useSWR, { useSWRConfig } from "swr";
import { TTL_ENUMS } from "@/lib/cloudflare";
@@ -23,6 +18,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
+import { PaginationWrapper } from "@/components/ui/pagination";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
@@ -38,15 +34,17 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
+import CountUpFn from "@/components/dashboard/count-up";
import StatusDot from "@/components/dashboard/status-dot";
import { FormType, RecordForm } from "@/components/forms/record-form";
import { EmptyPlaceholder } from "@/components/shared/empty-placeholder";
export interface RecordListProps {
user: Pick;
+ action: string;
}
-function TableColumnSekleton({ className }: { className?: string }) {
+function TableColumnSekleton() {
return (
@@ -71,35 +69,48 @@ function TableColumnSekleton({ className }: { className?: string }) {
);
}
-export default function UserRecordsList({ user }: RecordListProps) {
+export default function UserRecordsList({ user, action }: RecordListProps) {
const [isShowForm, setShowForm] = useState(false);
const [formType, setFormType] = useState("add");
const [currentEditRecord, setCurrentEditRecord] =
useState(null);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(10);
const { mutate } = useSWRConfig();
- const { data, error, isLoading } = useSWR(
- "/api/record",
- fetcher,
- {
- revalidateOnFocus: false,
- },
- );
+
+ console.log("base", action);
+
+ const { data, error, isLoading } = useSWR<{
+ total: number;
+ list: UserRecordFormData[];
+ }>(`${action}?page=${currentPage}&size=${pageSize}`, fetcher, {
+ revalidateOnFocus: false,
+ });
const handleRefresh = () => {
- mutate("/api/record", undefined);
+ mutate(`${action}?page=${currentPage}&size=${pageSize}`, undefined);
};
return (
<>
-
- Records
-
- All DNS Records
+ {action.includes("/admin") ? (
+
+ Total Records:{" "}
+
+ {data && }
+
-
+ ) : (
+
+ DNS Records
+
+ Your DNS Records
+
+
+ )}
)}
@@ -167,8 +179,8 @@ export default function UserRecordsList({ user }: RecordListProps) {
>
- ) : data && data.length > 0 ? (
- data.map((record) => (
+ ) : data && data.list ? (
+ data.list.map((record) => (
- {record.name.endsWith(".wr.do")
- ? record.name.slice(0, -6)
- : record.name}
+
+ {record.name.endsWith(".wr.do")
+ ? record.name.slice(0, -6)
+ : record.name}
+
@@ -240,6 +259,13 @@ export default function UserRecordsList({ user }: RecordListProps) {
)}
+ {data && Math.ceil(data.total / pageSize) > 1 && (
+
+ )}
diff --git a/app/(protected)/dashboard/urls/page.tsx b/app/(protected)/dashboard/urls/page.tsx
index 3b376d4..8578344 100644
--- a/app/(protected)/dashboard/urls/page.tsx
+++ b/app/(protected)/dashboard/urls/page.tsx
@@ -24,7 +24,10 @@ export default async function DashboardPage() {
link="/docs/short-urls"
linkText="Short urls."
/>
-
+
>
);
}
diff --git a/app/(protected)/dashboard/urls/url-list.tsx b/app/(protected)/dashboard/urls/url-list.tsx
index 9d527d0..7439c73 100644
--- a/app/(protected)/dashboard/urls/url-list.tsx
+++ b/app/(protected)/dashboard/urls/url-list.tsx
@@ -18,6 +18,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
+import { PaginationWrapper } from "@/components/ui/pagination";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
@@ -27,6 +28,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
+import CountUpFn from "@/components/dashboard/count-up";
import StatusDot from "@/components/dashboard/status-dot";
import { FormType } from "@/components/forms/record-form";
import { UrlForm } from "@/components/forms/url-form";
@@ -35,6 +37,7 @@ import { EmptyPlaceholder } from "@/components/shared/empty-placeholder";
export interface UrlListProps {
user: Pick;
+ action: string;
}
function TableColumnSekleton({ className }: { className?: string }) {
@@ -59,37 +62,47 @@ function TableColumnSekleton({ className }: { className?: string }) {
);
}
-export default function UserUrlsList({ user }: UrlListProps) {
+export default function UserUrlsList({ user, action }: UrlListProps) {
const { isMobile } = useMediaQuery();
const [isShowForm, setShowForm] = useState(false);
const [formType, setFormType] = useState("add");
const [currentEditUrl, setCurrentEditUrl] = useState(
null,
);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(10);
const { mutate } = useSWRConfig();
- const { data, error, isLoading } = useSWR(
- "/api/url",
- fetcher,
- {
- revalidateOnFocus: false,
- },
- );
+ const { data, error, isLoading } = useSWR<{
+ total: number;
+ list: ShortUrlFormData[];
+ }>(`${action}?page=${currentPage}&size=${pageSize}`, fetcher, {
+ revalidateOnFocus: false,
+ });
const handleRefresh = () => {
- mutate("/api/url", undefined);
+ mutate(`${action}?page=${currentPage}&size=${pageSize}`, undefined);
};
return (
<>
-
- Short URLs
-
- All Short URLs
+ {action.includes("/admin") ? (
+
+ Total URLs:{" "}
+
+ {data && }
+
-
+ ) : (
+
+ Short URLs
+
+ Your Short URLs
+
+
+ )}
)}
@@ -154,8 +168,8 @@ export default function UserUrlsList({ user }: UrlListProps) {
>
- ) : data && data.length > 0 ? (
- data.map((short) => (
+ ) : data && data.list ? (
+ data.list.map((short) => (
@@ -236,6 +249,13 @@ export default function UserUrlsList({ user }: UrlListProps) {
)}
+ {data && Math.ceil(data.total / pageSize) > 1 && (
+
+ )}
diff --git a/app/api/record/admin/route.ts b/app/api/record/admin/route.ts
new file mode 100644
index 0000000..01d055a
--- /dev/null
+++ b/app/api/record/admin/route.ts
@@ -0,0 +1,36 @@
+import { env } from "@/env.mjs";
+import { getUserRecords } from "@/lib/dto/cloudflare-dns-record";
+import { checkUserStatus } from "@/lib/dto/user";
+import { getCurrentUser } from "@/lib/session";
+
+export async function GET(req: Request) {
+ try {
+ const user = checkUserStatus(await getCurrentUser());
+ if (user instanceof Response) return user;
+ if (user.role !== "ADMIN") {
+ return Response.json("Unauthorized", {
+ status: 401,
+ statusText: "Unauthorized",
+ });
+ }
+
+ const url = new URL(req.url);
+ const page = url.searchParams.get("page");
+ const size = url.searchParams.get("size");
+
+ const data = await getUserRecords(
+ user.id,
+ 1,
+ Number(page || "1"),
+ Number(size || "10"),
+ "ADMIN",
+ );
+
+ return Response.json(data);
+ } catch (error) {
+ return Response.json(error?.statusText || error, {
+ status: error.status || 500,
+ statusText: error.statusText || "Server error",
+ });
+ }
+}
diff --git a/app/api/record/route.ts b/app/api/record/route.ts
index b18fd38..43ddfdf 100644
--- a/app/api/record/route.ts
+++ b/app/api/record/route.ts
@@ -7,11 +7,19 @@ export async function GET(req: Request) {
try {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
- // const { CLOUDFLARE_ZONE_ID, CLOUDFLARE_API_KEY, CLOUDFLARE_EMAIL } = env;
- const user_records = await getUserRecords(user.id, 1);
+ const url = new URL(req.url);
+ const page = url.searchParams.get("page");
+ const size = url.searchParams.get("size");
- return Response.json(user_records);
+ const data = await getUserRecords(
+ user.id,
+ 1,
+ Number(page || "1"),
+ Number(size || "10"),
+ );
+
+ return Response.json(data);
} catch (error) {
return Response.json(error?.statusText || error, {
status: error.status || 500,
diff --git a/app/api/url/admin/route.ts b/app/api/url/admin/route.ts
new file mode 100644
index 0000000..e23ae2c
--- /dev/null
+++ b/app/api/url/admin/route.ts
@@ -0,0 +1,35 @@
+import { env } from "@/env.mjs";
+import { getUserShortUrls } from "@/lib/dto/short-urls";
+import { checkUserStatus } from "@/lib/dto/user";
+import { getCurrentUser } from "@/lib/session";
+
+export async function GET(req: Request) {
+ try {
+ const user = checkUserStatus(await getCurrentUser());
+ if (user instanceof Response) return user;
+ if (user.role !== "ADMIN") {
+ return Response.json("Unauthorized", {
+ status: 401,
+ statusText: "Unauthorized",
+ });
+ }
+
+ const url = new URL(req.url);
+ const page = url.searchParams.get("page");
+ const size = url.searchParams.get("size");
+ const data = await getUserShortUrls(
+ user.id,
+ 1,
+ Number(page || "1"),
+ Number(size || "10"),
+ "ADMIN",
+ );
+
+ return Response.json(data);
+ } catch (error) {
+ return Response.json(error?.statusText || error, {
+ status: error.status || 500,
+ statusText: error.statusText || "Server error",
+ });
+ }
+}
diff --git a/app/api/url/route.ts b/app/api/url/route.ts
index 1568474..a595e99 100644
--- a/app/api/url/route.ts
+++ b/app/api/url/route.ts
@@ -8,10 +8,17 @@ export async function GET(req: Request) {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
- const user_urls = await getUserShortUrls(user.id, 1);
- // TODO: get meta info
+ const url = new URL(req.url);
+ const page = url.searchParams.get("page");
+ const size = url.searchParams.get("size");
+ const data = await getUserShortUrls(
+ user.id,
+ 1,
+ Number(page || "1"),
+ Number(size || "10"),
+ );
- return Response.json(user_urls);
+ return Response.json(data);
} catch (error) {
return Response.json(error?.statusText || error, {
status: error.status || 500,
diff --git a/app/api/user/admin/route.ts b/app/api/user/admin/route.ts
index a01a305..e6fcb13 100644
--- a/app/api/user/admin/route.ts
+++ b/app/api/user/admin/route.ts
@@ -13,9 +13,12 @@ export async function GET(req: Request) {
});
}
- const user_records = await getAllUsers();
+ const url = new URL(req.url);
+ const page = url.searchParams.get("page");
+ const size = url.searchParams.get("size");
+ const data = await getAllUsers(Number(page || "1"), Number(size || "10"));
- return Response.json(user_records);
+ return Response.json(data);
} catch (error) {
return Response.json(error?.statusText || error, {
status: error.status || 500,
diff --git a/app/api/user/admin/update/route.ts b/app/api/user/admin/update/route.ts
new file mode 100644
index 0000000..d52c31e
--- /dev/null
+++ b/app/api/user/admin/update/route.ts
@@ -0,0 +1,21 @@
+export async function POST(req: Request) {
+ try {
+ // const user = checkUserStatus(await getCurrentUser());
+ // if (user instanceof Response) return user;
+ // if (user.role !== "ADMIN") {
+ // return Response.json("Unauthorized", {
+ // status: 401,
+ // statusText: "Unauthorized",
+ // });
+ // }
+ // const body = await req.json();
+ // const { id, ...data } = body;
+ // const result = await updateUserById(id, data);
+ return Response.json("success");
+ } catch (error) {
+ return Response.json(error?.statusText || error, {
+ status: error.status || 500,
+ statusText: error.statusText || "Server error",
+ });
+ }
+}
diff --git a/app/api/user/route.ts b/app/api/user/route.ts
index 7937576..229f0b0 100644
--- a/app/api/user/route.ts
+++ b/app/api/user/route.ts
@@ -1,6 +1,6 @@
import { auth } from "@/auth";
-import { prisma } from "@/lib/db";
+import { deleteUserById } from "@/lib/dto/user";
export const DELETE = auth(async (req) => {
if (!req.auth) {
@@ -8,16 +8,12 @@ export const DELETE = auth(async (req) => {
}
const currentUser = req.auth.user;
- if (!currentUser) {
+ if (!currentUser || !currentUser?.id) {
return new Response("Invalid user", { status: 401 });
}
try {
- await prisma.user.delete({
- where: {
- id: currentUser.id,
- },
- });
+ await deleteUserById(currentUser.id);
} catch (error) {
return new Response("Internal server error", { status: 500 });
}
diff --git a/components/dashboard/dashboard-info-card.tsx b/components/dashboard/dashboard-info-card.tsx
new file mode 100644
index 0000000..4798bc9
--- /dev/null
+++ b/components/dashboard/dashboard-info-card.tsx
@@ -0,0 +1,76 @@
+import Link from "next/link";
+import { Link as LinkIcon } from "lucide-react";
+
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
+import CountUp from "@/components/dashboard/count-up";
+import { Icons } from "@/components/shared/icons";
+
+export async function DashboardInfoCard({
+ userId,
+ title,
+ total,
+ count,
+ link,
+ // icon = "user",
+}: {
+ userId: string;
+ title: string;
+ total?: number;
+ count: number;
+ link: string;
+ // icon: keyof typeof Icons;
+}) {
+ // const iconCpn = Icons[icon];
+ return (
+
+
+
+
+ {title}
+
+
+ {/* {icon && } */}
+
+
+
+ {[-1, undefined].includes(count) ? (
+
+ ) : (
+
+
+ {total && (
+
+ / {total}
+
+ )}
+
+ )}
+ total
+
+
+ );
+}
+
+export function HeroCard() {
+ return (
+
+
+ Free Records & URLs
+
+
+ Learn more from{" "}
+
+ documents.
+ {" "}
+
+
+ );
+}
diff --git a/components/forms/record-form.tsx b/components/forms/record-form.tsx
index a65aab4..e879b1f 100644
--- a/components/forms/record-form.tsx
+++ b/components/forms/record-form.tsx
@@ -38,6 +38,7 @@ export interface RecordFormProps {
setShowForm: Dispatch>;
type: FormType;
initData?: UserRecordFormData | null;
+ action: string;
onRefresh: () => void;
}
@@ -47,6 +48,7 @@ export function RecordForm({
setShowForm,
type,
initData,
+ action,
onRefresh,
}: RecordFormProps) {
const [isPending, startTransition] = useTransition();
@@ -86,7 +88,7 @@ export function RecordForm({
const handleCreateRecord = async (data: CreateDNSRecord) => {
startTransition(async () => {
- const response = await fetch("/api/record/add", {
+ const response = await fetch(`${action}/add`, {
method: "POST",
body: JSON.stringify({
records: [data],
@@ -108,7 +110,7 @@ export function RecordForm({
const handleUpdateRecord = async (data: CreateDNSRecord) => {
startTransition(async () => {
if (type === "edit") {
- const response = await fetch("/api/record/update", {
+ const response = await fetch(`${action}/update`, {
method: "POST",
body: JSON.stringify({
recordId: initData?.record_id,
@@ -132,7 +134,7 @@ export function RecordForm({
const handleDeleteRecord = async () => {
if (type === "edit") {
startDeleteTransition(async () => {
- const response = await fetch("/api/record/delete", {
+ const response = await fetch(`${action}/delete`, {
method: "POST",
body: JSON.stringify({
record_id: initData?.record_id,
diff --git a/components/forms/url-form.tsx b/components/forms/url-form.tsx
index 317f068..50557bd 100644
--- a/components/forms/url-form.tsx
+++ b/components/forms/url-form.tsx
@@ -28,6 +28,7 @@ export interface RecordFormProps {
setShowForm: Dispatch>;
type: FormType;
initData?: ShortUrlFormData | null;
+ action: string;
onRefresh: () => void;
}
@@ -35,6 +36,7 @@ export function UrlForm({
setShowForm,
type,
initData,
+ action,
onRefresh,
}: RecordFormProps) {
const [isPending, startTransition] = useTransition();
diff --git a/components/forms/user-auth-form.tsx b/components/forms/user-auth-form.tsx
index 0c9a929..120364a 100644
--- a/components/forms/user-auth-form.tsx
+++ b/components/forms/user-auth-form.tsx
@@ -147,7 +147,7 @@ export function UserAuthForm({ className, type, ...props }: UserAuthFormProps) {
{isGithubLoading ? (
) : (
-
+
)}{" "}
Github
diff --git a/components/layout/dashboard-sidebar.tsx b/components/layout/dashboard-sidebar.tsx
index c814de1..e72d7e1 100644
--- a/components/layout/dashboard-sidebar.tsx
+++ b/components/layout/dashboard-sidebar.tsx
@@ -179,7 +179,7 @@ export function DashboardSidebar({ links }: DashboardSidebarProps) {
{isSidebarExpanded && (
-
+
© 2024{" "}
-
+
GitHub
diff --git a/components/layout/navbar.tsx b/components/layout/navbar.tsx
index cea2fdb..e616e78 100644
--- a/components/layout/navbar.tsx
+++ b/components/layout/navbar.tsx
@@ -96,7 +96,7 @@ export function NavBar({ scroll = false }: NavBarProps) {
target="_blank"
rel="noreferrer"
>
-
+
GitHub
diff --git a/components/layout/site-footer.tsx b/components/layout/site-footer.tsx
index a77360d..51b062f 100644
--- a/components/layout/site-footer.tsx
+++ b/components/layout/site-footer.tsx
@@ -82,7 +82,7 @@ export function SiteFooter({ className }: React.HTMLAttributes) {
rel="noreferrer"
className="font-medium underline underline-offset-1"
>
-
+
diff --git a/components/sections/hero-landing.tsx b/components/sections/hero-landing.tsx
index 837121b..35bf38e 100644
--- a/components/sections/hero-landing.tsx
+++ b/components/sections/hero-landing.tsx
@@ -50,7 +50,7 @@ export default async function HeroLanding() {
"px-4 text-[15px]",
)}
>
-
+
Star on GitHub
diff --git a/components/shared/icons.tsx b/components/shared/icons.tsx
index 9eee7ce..5996a4e 100644
--- a/components/shared/icons.tsx
+++ b/components/shared/icons.tsx
@@ -51,7 +51,7 @@ export const Icons = {
copy: Copy,
dashboard: LayoutPanelLeft,
ellipsis: MoreVertical,
- gitHub: ({ ...props }: LucideProps) => (
+ github: ({ ...props }: LucideProps) => (
+ );
+}
diff --git a/config/dashboard.ts b/config/dashboard.ts
index 68dbaa6..7aa582b 100644
--- a/config/dashboard.ts
+++ b/config/dashboard.ts
@@ -25,13 +25,19 @@ export const sidebarLinks: SidebarNavItem[] = [
{
href: "/admin/users",
icon: "user",
- title: "User Management",
+ title: "Users",
authorizeOnly: UserRole.ADMIN,
},
{
- href: "/dashboard/charts",
+ href: "/admin/records",
icon: "lineChart",
- title: "Charts",
+ title: "Records",
+ authorizeOnly: UserRole.ADMIN,
+ },
+ {
+ href: "/admin/urls",
+ icon: "post",
+ title: "URLs",
authorizeOnly: UserRole.ADMIN,
},
],
@@ -42,6 +48,7 @@ export const sidebarLinks: SidebarNavItem[] = [
{ href: "/dashboard/settings", icon: "settings", title: "Settings" },
{ href: "/", icon: "home", title: "Homepage" },
{ href: "/docs", icon: "bookOpen", title: "Documentation" },
+ { href: siteConfig.links.github, icon: "github", title: "Github" },
{
href: "mailto:" + siteConfig.mailSupport,
icon: "mail",
diff --git a/lib/dto/cloudflare-dns-record.ts b/lib/dto/cloudflare-dns-record.ts
index 25b8e5e..341e58c 100644
--- a/lib/dto/cloudflare-dns-record.ts
+++ b/lib/dto/cloudflare-dns-record.ts
@@ -1,6 +1,7 @@
"use server";
import { auth } from "@/auth";
+import { UserRole } from "@prisma/client";
import { prisma } from "@/lib/db";
import {
@@ -82,22 +83,53 @@ export async function createUserRecord(
}
}
-export async function getUserRecords(userId: string, active: number = 1) {
- return await prisma.userRecord.findMany({
- where: {
- userId: userId,
- active,
- },
- });
+export async function getUserRecords(
+ userId: string,
+ active: number = 1,
+ page: number,
+ size: number,
+ role: UserRole = "USER",
+) {
+ const option =
+ role === "USER"
+ ? {
+ userId,
+ active,
+ }
+ : {};
+ const [total, list] = await prisma.$transaction([
+ prisma.userRecord.count({
+ where: option,
+ }),
+ prisma.userRecord.findMany({
+ where: option,
+ skip: (page - 1) * size,
+ take: size,
+ orderBy: {
+ modified_on: "asc",
+ },
+ }),
+ ]);
+ return {
+ total,
+ list,
+ };
}
-export async function getUserRecordCount(userId: string, active: number = 1) {
+export async function getUserRecordCount(
+ userId: string,
+ active: number = 1,
+ role: UserRole = "USER",
+) {
try {
return await prisma.userRecord.count({
- where: {
- userId: userId,
- active,
- },
+ where:
+ role === "USER"
+ ? {
+ userId: userId,
+ active,
+ }
+ : {},
});
} catch (error) {
return -1;
diff --git a/lib/dto/short-urls.ts b/lib/dto/short-urls.ts
index e025e79..ee8e055 100644
--- a/lib/dto/short-urls.ts
+++ b/lib/dto/short-urls.ts
@@ -1,5 +1,5 @@
import { auth } from "@/auth";
-import { UrlMeta } from "@prisma/client";
+import { UrlMeta, UserRole } from "@prisma/client";
import { prisma } from "@/lib/db";
@@ -20,22 +20,53 @@ export interface UserShortUrlInfo extends ShortUrlFormData {
meta?: UrlMeta;
}
-export async function getUserShortUrls(userId: string, active: number = 1) {
- return await prisma.userUrl.findMany({
- where: {
- userId,
- active,
- },
- });
+export async function getUserShortUrls(
+ userId: string,
+ active: number = 1,
+ page: number,
+ size: number,
+ role: UserRole = "USER",
+) {
+ const option =
+ role === "USER"
+ ? {
+ userId,
+ active,
+ }
+ : {};
+ const [total, list] = await prisma.$transaction([
+ prisma.userUrl.count({
+ where: option,
+ }),
+ prisma.userUrl.findMany({
+ where: option,
+ skip: (page - 1) * size,
+ take: size,
+ orderBy: {
+ updatedAt: "asc",
+ },
+ }),
+ ]);
+ return {
+ total,
+ list,
+ };
}
-export async function getUserShortUrlCount(userId: string, active: number = 1) {
+export async function getUserShortUrlCount(
+ userId: string,
+ active: number = 1,
+ role: UserRole = "USER",
+) {
try {
return await prisma.userUrl.count({
- where: {
- userId,
- active,
- },
+ where:
+ role === "USER"
+ ? {
+ userId,
+ active,
+ }
+ : {},
});
} catch (error) {
return -1;
diff --git a/lib/dto/user.ts b/lib/dto/user.ts
index 7fe302f..1ac591d 100644
--- a/lib/dto/user.ts
+++ b/lib/dto/user.ts
@@ -1,4 +1,4 @@
-import { User } from "@prisma/client";
+import { User, UserRole } from "@prisma/client";
import { prisma } from "@/lib/db";
@@ -43,12 +43,59 @@ export const getUserById = async (id: string) => {
}
};
-export const getAllUsers = async () => {
+export const getAllUsers = async (page: number, size: number) => {
try {
- // TODO: paginate
- const users = await prisma.user.findMany();
- return users;
- } catch {
+ const [total, list] = await prisma.$transaction([
+ prisma.user.count(), // 获取所有用户的总数
+ prisma.user.findMany({
+ skip: (page - 1) * size,
+ take: size,
+ orderBy: {
+ createdAt: "asc",
+ },
+ }),
+ ]);
+ return {
+ total,
+ list,
+ };
+ } catch (error) {
+ console.error(error);
+ return null;
+ }
+};
+
+export async function getAllUsersCount() {
+ try {
+ return await prisma.user.count();
+ } catch (error) {
+ return -1;
+ }
+}
+
+export const updateUser = async (userId: string, data: UpdateUserForm) => {
+ try {
+ const session = await prisma.user.update({
+ where: {
+ id: userId,
+ },
+ data: data,
+ });
+ return session;
+ } catch (error) {
+ return null;
+ }
+};
+
+export const deleteUserById = async (userId: string) => {
+ try {
+ const session = await prisma.user.delete({
+ where: {
+ id: userId,
+ },
+ });
+ return session;
+ } catch (error) {
return null;
}
};
diff --git a/lib/validations/record.ts b/lib/validations/record.ts
index a301614..174d260 100644
--- a/lib/validations/record.ts
+++ b/lib/validations/record.ts
@@ -10,7 +10,7 @@ export const createRecordSchema = z.object({
name: z
.string()
.regex(/^[a-zA-Z0-9-]+$/, "Invalid characters")
- .min(1)
+ .min(2)
.max(32),
content: z
.string()
diff --git a/package.json b/package.json
index ac649ba..35b265d 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"dependencies": {
"@auth/prisma-adapter": "^2.4.1",
"@hookform/resolvers": "^3.9.0",
+ "@nextui-org/pagination": "^2.0.35",
"@prisma/client": "^5.17.0",
"@radix-ui/react-accessible-icon": "^1.1.0",
"@radix-ui/react-accordion": "^1.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 558c772..a996d1d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
'@hookform/resolvers':
specifier: ^3.9.0
version: 3.9.0(react-hook-form@7.52.1(react@18.3.1))
+ '@nextui-org/pagination':
+ specifier: ^2.0.35
+ version: 2.0.35(@nextui-org/system@2.2.5(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(framer-motion@10.17.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@prisma/client':
specifier: ^5.17.0
version: 5.17.0(prisma@5.17.0)
@@ -782,6 +785,21 @@ packages:
'@floating-ui/utils@0.1.6':
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
+ '@formatjs/ecma402-abstract@2.0.0':
+ resolution: {integrity: sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==}
+
+ '@formatjs/fast-memoize@2.2.0':
+ resolution: {integrity: sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==}
+
+ '@formatjs/icu-messageformat-parser@2.7.8':
+ resolution: {integrity: sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==}
+
+ '@formatjs/icu-skeleton-parser@1.8.2':
+ resolution: {integrity: sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==}
+
+ '@formatjs/intl-localematcher@0.5.4':
+ resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==}
+
'@grpc/grpc-js@1.10.6':
resolution: {integrity: sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==}
engines: {node: '>=12.10.0'}
@@ -941,6 +959,18 @@ packages:
cpu: [x64]
os: [win32]
+ '@internationalized/date@3.5.5':
+ resolution: {integrity: sha512-H+CfYvOZ0LTJeeLOqm19E3uj/4YjrmOFtBufDHPfvtI80hFAMqtrp7oCACpe4Cil5l8S0Qu/9dYfZc/5lY8WQQ==}
+
+ '@internationalized/message@3.1.4':
+ resolution: {integrity: sha512-Dygi9hH1s7V9nha07pggCkvmRfDd3q2lWnMGvrJyrOwYMe1yj4D2T9BoH9I6MGR7xz0biQrtLPsqUkqXzIrBOw==}
+
+ '@internationalized/number@3.5.3':
+ resolution: {integrity: sha512-rd1wA3ebzlp0Mehj5YTuTI50AQEx80gWFyHcQu+u91/5NgdwBecO8BH6ipPfE+lmQ9d63vpB3H9SHoIUiupllw==}
+
+ '@internationalized/string@3.2.3':
+ resolution: {integrity: sha512-9kpfLoA8HegiWTeCbR2livhdVeKobCnVv8tlJ6M2jF+4tcMqDo94ezwlnrUANBWPgd8U7OXIHCk2Ov2qhk4KXw==}
+
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@@ -1124,6 +1154,53 @@ packages:
cpu: [x64]
os: [win32]
+ '@nextui-org/pagination@2.0.35':
+ resolution: {integrity: sha512-07KJgZcJBt2e9RY6TsiQm5qrjDLH+gT3yB7yQ4jPdCK9fkTB0r2kvTOYdPUvrtVJYRq2bwFCWOz+9mokdNfcwg==}
+ peerDependencies:
+ '@nextui-org/system': '>=2.0.0'
+ '@nextui-org/theme': '>=2.1.0'
+ react: '>=18'
+ react-dom: '>=18'
+
+ '@nextui-org/react-rsc-utils@2.0.13':
+ resolution: {integrity: sha512-QewsXtoQlMsR9stThdazKEImg9oyZkPLs7wsymhrzh6/HdQCl9bTdb6tJcROg4vg5LRYKGG11USSQO2nKlfCcQ==}
+
+ '@nextui-org/react-utils@2.0.16':
+ resolution: {integrity: sha512-QdDoqzhx+4t9cDTVmtw5iOrfyLvpqyKsq8PARHUniCiQQDQd1ao7FCpzHgvU9poYcEdRk+Lsna66zbeMkFBB6w==}
+ peerDependencies:
+ react: '>=18'
+
+ '@nextui-org/shared-icons@2.0.9':
+ resolution: {integrity: sha512-WG3yinVY7Tk9VqJgcdF4V8Ok9+fcm5ey7S1els7kujrfqLYxtqoKywgiY/7QHwZlfQkzpykAfy+NAlHkTP5hMg==}
+ peerDependencies:
+ react: '>=18'
+
+ '@nextui-org/shared-utils@2.0.7':
+ resolution: {integrity: sha512-FxY3N0i1Al7Oz3yOQN0dSpG8UUrLIP3iYh3ubD7BhdQoZLl5xbG6++q1gqOzZXV+ZWeUFMY/or0ofzWxGHiOow==}
+
+ '@nextui-org/system-rsc@2.1.5':
+ resolution: {integrity: sha512-tkJLAyJu34Rr5KUMMqoB7cZjOVXB+7a/7N4ushZfuiLdoYijgmcXFMzLxjm+tbt9zA5AV+ivsfbHvscg77dJ6w==}
+ peerDependencies:
+ '@nextui-org/theme': '>=2.1.0'
+ react: '>=18'
+
+ '@nextui-org/system@2.2.5':
+ resolution: {integrity: sha512-nrX6768aiyWtpxX3OTFBIVWR+v9nlMsC3KaBinNfek97sNm7gAfTHi7q5kylE3L5yIMpNG+DclAKpuxgDQEmvw==}
+ peerDependencies:
+ framer-motion: '>=10.17.0'
+ react: '>=18'
+ react-dom: '>=18'
+
+ '@nextui-org/theme@2.2.9':
+ resolution: {integrity: sha512-TN2I9sMriLaj00pXsIMlg19+UHeOdjzS2JV0u4gjL14mSbQl5BYNxgbvU3gbMqkZZQ6OpwT4RnT8RS+ks6TXCw==}
+ peerDependencies:
+ tailwindcss: '>=3.4.0'
+
+ '@nextui-org/use-pagination@2.0.9':
+ resolution: {integrity: sha512-p5Gssyb71/SjRezq2o1aRsYTmC9idziW3pLCJFpVwLGfgWNARf9C6NS1oQsqKgjF5lvzoa88soZRDhKKvRAt/g==}
+ peerDependencies:
+ react: '>=18'
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -2089,6 +2166,58 @@ packages:
'@radix-ui/rect@1.1.0':
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
+ '@react-aria/focus@3.17.1':
+ resolution: {integrity: sha512-FLTySoSNqX++u0nWZJPPN5etXY0WBxaIe/YuL/GTEeuqUIuC/2bJSaw5hlsM6T2yjy6Y/VAxBcKSdAFUlU6njQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-aria/focus@3.18.1':
+ resolution: {integrity: sha512-N0Cy61WCIv+57mbqC7hiZAsB+3rF5n4JKabxUmg/2RTJL6lq7hJ5N4gx75ymKxkN8GnVDwt4pKZah48Wopa5jw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/i18n@3.11.1':
+ resolution: {integrity: sha512-vuiBHw1kZruNMYeKkTGGnmPyMnM5T+gT8bz97H1FqIq1hQ6OPzmtBZ6W6l6OIMjeHI5oJo4utTwfZl495GALFQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-aria/interactions@3.21.3':
+ resolution: {integrity: sha512-BWIuf4qCs5FreDJ9AguawLVS0lV9UU+sK4CCnbCNNmYqOWY+1+gRXCsnOM32K+oMESBxilAjdHW5n1hsMqYMpA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-aria/interactions@3.22.1':
+ resolution: {integrity: sha512-5TLzQaDAQQ5C70yG8GInbO4wIylKY67RfTIIwQPGR/4n5OIjbUD8BOj3NuSsuZ/frUPaBXo1VEBBmSO23fxkjw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/overlays@3.22.1':
+ resolution: {integrity: sha512-GHiFMWO4EQ6+j6b5QCnNoOYiyx1Gk8ZiwLzzglCI4q1NY5AG2EAmfU4Z1+Gtrf2S5Y0zHbumC7rs9GnPoGLUYg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-aria/ssr@3.9.5':
+ resolution: {integrity: sha512-xEwGKoysu+oXulibNUSkXf8itW0npHHTa6c4AyYeZIJyRoegeteYuFpZUBPtIDE8RfHdNsSmE1ssOkxRnwbkuQ==}
+ engines: {node: '>= 12'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/utils@3.24.1':
+ resolution: {integrity: sha512-O3s9qhPMd6n42x9sKeJ3lhu5V1Tlnzhu6Yk8QOvDuXf7UGuUjXf9mzfHJt1dYzID4l9Fwm8toczBzPM9t0jc8Q==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-aria/utils@3.25.1':
+ resolution: {integrity: sha512-5Uj864e7T5+yj78ZfLnfHqmypLiqW2mN+nsdslog2z5ssunTqjolVeM15ootXskjISlZ7MojLpq97kIC4nlnAw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-aria/visually-hidden@3.8.14':
+ resolution: {integrity: sha512-DV3yagbAgO4ywQTq6D/AxcIaTC8c77r/SxlIMhQBMQ6vScJWTCh6zFG55wmLe3NKqvRrowv1OstlmYfZQ4v/XA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
'@react-email/body@0.0.8':
resolution: {integrity: sha512-gqdkNYlIaIw0OdpWu8KjIcQSIFvx7t2bZpXVxMMvBS859Ia1+1X3b5RNbjI3S1ZqLddUf7owOHkO4MiXGE+nxg==}
peerDependencies:
@@ -2218,6 +2347,41 @@ packages:
peerDependencies:
react: ^18.2.0
+ '@react-stately/overlays@3.6.9':
+ resolution: {integrity: sha512-4chfyzKw7P2UEainm0yzjUgYwG1ovBejN88eTrn+O62x5huuMCwe0cbMxmYh4y7IhRFSee3jIJd0SP0u/+i39w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-stately/utils@3.10.1':
+ resolution: {integrity: sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-stately/utils@3.10.2':
+ resolution: {integrity: sha512-fh6OTQtbeQC0ywp6LJuuKs6tKIgFvt/DlIZEcIpGho6/oZG229UnIk6TUekwxnDbumuYyan6D9EgUtEMmT8UIg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-types/button@3.9.6':
+ resolution: {integrity: sha512-8lA+D5JLbNyQikf8M/cPP2cji91aVTcqjrGpDqI7sQnaLFikM8eFR6l1ZWGtZS5MCcbfooko77ha35SYplSQvw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-types/overlays@3.8.9':
+ resolution: {integrity: sha512-9ni9upQgXPnR+K9cWmbYWvm3ll9gH8P/XsEZprqIV5zNLMF334jADK48h4jafb1X9RFnj0WbHo6BqcSObzjTig==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
+ '@react-types/shared@3.23.1':
+ resolution: {integrity: sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+
+ '@react-types/shared@3.24.1':
+ resolution: {integrity: sha512-AUQeGYEm/zDTN6zLzdXolDxz3Jk5dDL7f506F07U8tBwxNNI3WRdhU84G0/AaFikOZzDXhOZDr3MhQMzyE7Ydw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
+
'@resvg/resvg-wasm@2.4.0':
resolution: {integrity: sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==}
engines: {node: '>= 10'}
@@ -2925,6 +3089,9 @@ packages:
color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+ color2k@2.0.3:
+ resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==}
+
color@4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
@@ -2954,6 +3121,9 @@ packages:
compare-func@2.0.0:
resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
+ compute-scroll-into-view@3.1.0:
+ resolution: {integrity: sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==}
+
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -3559,6 +3729,10 @@ packages:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0}
+ flat@5.0.2:
+ resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
+ hasBin: true
+
flatted@3.2.9:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
@@ -3867,6 +4041,9 @@ packages:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
+ intl-messageformat@10.5.14:
+ resolution: {integrity: sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==}
+
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
@@ -4156,18 +4333,30 @@ packages:
lodash.castarray@4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
+ lodash.foreach@4.5.0:
+ resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==}
+
+ lodash.get@4.4.2:
+ resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
+
lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
lodash.kebabcase@4.1.1:
resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
+ lodash.mapkeys@4.6.0:
+ resolution: {integrity: sha512-0Al+hxpYvONWtg+ZqHpa/GaVzxuN3V7Xeo2p+bY06EaK/n+Y9R7nBePPN2o1LxmL0TWQSwP8LYZ008/hc9JzhA==}
+
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
lodash.mergewith@4.6.2:
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+ lodash.omit@4.5.0:
+ resolution: {integrity: sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==}
+
lodash.snakecase@4.1.1:
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
@@ -5174,6 +5363,9 @@ packages:
resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
engines: {node: '>= 10.13.0'}
+ scroll-into-view-if-needed@3.0.10:
+ resolution: {integrity: sha512-t44QCeDKAPf1mtQH3fYpWz8IM/DyvHLjs8wUvvwMYxk5moOqCzrMSxK6HQVD0QVmVjXFavoFIPRVrMuJPKAvtg==}
+
section-matter@1.0.0:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
@@ -5421,12 +5613,21 @@ packages:
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
+ tailwind-merge@1.14.0:
+ resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==}
+
tailwind-merge@2.2.0:
resolution: {integrity: sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==}
tailwind-merge@2.4.0:
resolution: {integrity: sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A==}
+ tailwind-variants@0.1.20:
+ resolution: {integrity: sha512-AMh7x313t/V+eTySKB0Dal08RHY7ggYK0MSn/ad8wKWOrDUIzyiWNayRUm2PIJ4VRkvRnfNuyRuKbLV3EN+ewQ==}
+ engines: {node: '>=16.x', pnpm: '>=7.x'}
+ peerDependencies:
+ tailwindcss: '*'
+
tailwindcss-animate@1.0.7:
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
peerDependencies:
@@ -6367,6 +6568,30 @@ snapshots:
'@floating-ui/utils@0.1.6': {}
+ '@formatjs/ecma402-abstract@2.0.0':
+ dependencies:
+ '@formatjs/intl-localematcher': 0.5.4
+ tslib: 2.6.2
+
+ '@formatjs/fast-memoize@2.2.0':
+ dependencies:
+ tslib: 2.6.2
+
+ '@formatjs/icu-messageformat-parser@2.7.8':
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.0.0
+ '@formatjs/icu-skeleton-parser': 1.8.2
+ tslib: 2.6.2
+
+ '@formatjs/icu-skeleton-parser@1.8.2':
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.0.0
+ tslib: 2.6.2
+
+ '@formatjs/intl-localematcher@0.5.4':
+ dependencies:
+ tslib: 2.6.2
+
'@grpc/grpc-js@1.10.6':
dependencies:
'@grpc/proto-loader': 0.7.12
@@ -6482,6 +6707,23 @@ snapshots:
'@img/sharp-win32-x64@0.33.4':
optional: true
+ '@internationalized/date@3.5.5':
+ dependencies:
+ '@swc/helpers': 0.5.5
+
+ '@internationalized/message@3.1.4':
+ dependencies:
+ '@swc/helpers': 0.5.5
+ intl-messageformat: 10.5.14
+
+ '@internationalized/number@3.5.3':
+ dependencies:
+ '@swc/helpers': 0.5.5
+
+ '@internationalized/string@3.2.3':
+ dependencies:
+ '@swc/helpers': 0.5.5
+
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@@ -6636,6 +6878,80 @@ snapshots:
'@next/swc-win32-x64-msvc@14.2.5':
optional: true
+ '@nextui-org/pagination@2.0.35(@nextui-org/system@2.2.5(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(framer-motion@10.17.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@nextui-org/react-utils': 2.0.16(react@18.3.1)
+ '@nextui-org/shared-icons': 2.0.9(react@18.3.1)
+ '@nextui-org/shared-utils': 2.0.7
+ '@nextui-org/system': 2.2.5(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(framer-motion@10.17.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@nextui-org/theme': 2.2.9(tailwindcss@3.4.6)
+ '@nextui-org/use-pagination': 2.0.9(react@18.3.1)
+ '@react-aria/focus': 3.17.1(react@18.3.1)
+ '@react-aria/i18n': 3.11.1(react@18.3.1)
+ '@react-aria/interactions': 3.21.3(react@18.3.1)
+ '@react-aria/utils': 3.24.1(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ scroll-into-view-if-needed: 3.0.10
+
+ '@nextui-org/react-rsc-utils@2.0.13': {}
+
+ '@nextui-org/react-utils@2.0.16(react@18.3.1)':
+ dependencies:
+ '@nextui-org/react-rsc-utils': 2.0.13
+ '@nextui-org/shared-utils': 2.0.7
+ react: 18.3.1
+
+ '@nextui-org/shared-icons@2.0.9(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+
+ '@nextui-org/shared-utils@2.0.7': {}
+
+ '@nextui-org/system-rsc@2.1.5(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(react@18.3.1)':
+ dependencies:
+ '@nextui-org/theme': 2.2.9(tailwindcss@3.4.6)
+ '@react-types/shared': 3.23.1(react@18.3.1)
+ clsx: 1.2.1
+ react: 18.3.1
+
+ '@nextui-org/system@2.2.5(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(framer-motion@10.17.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@internationalized/date': 3.5.5
+ '@nextui-org/react-utils': 2.0.16(react@18.3.1)
+ '@nextui-org/system-rsc': 2.1.5(@nextui-org/theme@2.2.9(tailwindcss@3.4.6))(react@18.3.1)
+ '@react-aria/i18n': 3.11.1(react@18.3.1)
+ '@react-aria/overlays': 3.22.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@react-aria/utils': 3.24.1(react@18.3.1)
+ '@react-stately/utils': 3.10.1(react@18.3.1)
+ framer-motion: 10.17.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ transitivePeerDependencies:
+ - '@nextui-org/theme'
+
+ '@nextui-org/theme@2.2.9(tailwindcss@3.4.6)':
+ dependencies:
+ clsx: 1.2.1
+ color: 4.2.3
+ color2k: 2.0.3
+ deepmerge: 4.3.1
+ flat: 5.0.2
+ lodash.foreach: 4.5.0
+ lodash.get: 4.4.2
+ lodash.kebabcase: 4.1.1
+ lodash.mapkeys: 4.6.0
+ lodash.omit: 4.5.0
+ tailwind-merge: 1.14.0
+ tailwind-variants: 0.1.20(tailwindcss@3.4.6)
+ tailwindcss: 3.4.6
+
+ '@nextui-org/use-pagination@2.0.9(react@18.3.1)':
+ dependencies:
+ '@nextui-org/shared-utils': 2.0.7
+ '@react-aria/i18n': 3.11.1(react@18.3.1)
+ react: 18.3.1
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -7900,6 +8216,99 @@ snapshots:
'@radix-ui/rect@1.1.0': {}
+ '@react-aria/focus@3.17.1(react@18.3.1)':
+ dependencies:
+ '@react-aria/interactions': 3.21.3(react@18.3.1)
+ '@react-aria/utils': 3.24.1(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ clsx: 2.1.1
+ react: 18.3.1
+
+ '@react-aria/focus@3.18.1(react@18.3.1)':
+ dependencies:
+ '@react-aria/interactions': 3.22.1(react@18.3.1)
+ '@react-aria/utils': 3.25.1(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ clsx: 2.1.1
+ react: 18.3.1
+
+ '@react-aria/i18n@3.11.1(react@18.3.1)':
+ dependencies:
+ '@internationalized/date': 3.5.5
+ '@internationalized/message': 3.1.4
+ '@internationalized/number': 3.5.3
+ '@internationalized/string': 3.2.3
+ '@react-aria/ssr': 3.9.5(react@18.3.1)
+ '@react-aria/utils': 3.24.1(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-aria/interactions@3.21.3(react@18.3.1)':
+ dependencies:
+ '@react-aria/ssr': 3.9.5(react@18.3.1)
+ '@react-aria/utils': 3.24.1(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-aria/interactions@3.22.1(react@18.3.1)':
+ dependencies:
+ '@react-aria/ssr': 3.9.5(react@18.3.1)
+ '@react-aria/utils': 3.25.1(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-aria/overlays@3.22.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@react-aria/focus': 3.18.1(react@18.3.1)
+ '@react-aria/i18n': 3.11.1(react@18.3.1)
+ '@react-aria/interactions': 3.22.1(react@18.3.1)
+ '@react-aria/ssr': 3.9.5(react@18.3.1)
+ '@react-aria/utils': 3.24.1(react@18.3.1)
+ '@react-aria/visually-hidden': 3.8.14(react@18.3.1)
+ '@react-stately/overlays': 3.6.9(react@18.3.1)
+ '@react-types/button': 3.9.6(react@18.3.1)
+ '@react-types/overlays': 3.8.9(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
+ '@react-aria/ssr@3.9.5(react@18.3.1)':
+ dependencies:
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-aria/utils@3.24.1(react@18.3.1)':
+ dependencies:
+ '@react-aria/ssr': 3.9.5(react@18.3.1)
+ '@react-stately/utils': 3.10.2(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ clsx: 2.1.1
+ react: 18.3.1
+
+ '@react-aria/utils@3.25.1(react@18.3.1)':
+ dependencies:
+ '@react-aria/ssr': 3.9.5(react@18.3.1)
+ '@react-stately/utils': 3.10.2(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ clsx: 2.1.1
+ react: 18.3.1
+
+ '@react-aria/visually-hidden@3.8.14(react@18.3.1)':
+ dependencies:
+ '@react-aria/interactions': 3.22.1(react@18.3.1)
+ '@react-aria/utils': 3.25.1(react@18.3.1)
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
'@react-email/body@0.0.8(react@18.3.1)':
dependencies:
react: 18.3.1
@@ -8024,6 +8433,41 @@ snapshots:
dependencies:
react: 18.3.1
+ '@react-stately/overlays@3.6.9(react@18.3.1)':
+ dependencies:
+ '@react-stately/utils': 3.10.2(react@18.3.1)
+ '@react-types/overlays': 3.8.9(react@18.3.1)
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-stately/utils@3.10.1(react@18.3.1)':
+ dependencies:
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-stately/utils@3.10.2(react@18.3.1)':
+ dependencies:
+ '@swc/helpers': 0.5.5
+ react: 18.3.1
+
+ '@react-types/button@3.9.6(react@18.3.1)':
+ dependencies:
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ react: 18.3.1
+
+ '@react-types/overlays@3.8.9(react@18.3.1)':
+ dependencies:
+ '@react-types/shared': 3.24.1(react@18.3.1)
+ react: 18.3.1
+
+ '@react-types/shared@3.23.1(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+
+ '@react-types/shared@3.24.1(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+
'@resvg/resvg-wasm@2.4.0': {}
'@rushstack/eslint-patch@1.5.1': {}
@@ -8777,6 +9221,8 @@ snapshots:
color-name: 1.1.4
simple-swizzle: 0.2.2
+ color2k@2.0.3: {}
+
color@4.2.3:
dependencies:
color-convert: 2.0.1
@@ -8805,6 +9251,8 @@ snapshots:
array-ify: 1.0.0
dot-prop: 5.3.0
+ compute-scroll-into-view@3.1.0: {}
+
concat-map@0.0.1: {}
concurrently@8.2.2:
@@ -9604,6 +10052,8 @@ snapshots:
keyv: 4.5.4
rimraf: 3.0.2
+ flat@5.0.2: {}
+
flatted@3.2.9: {}
for-each@0.3.3:
@@ -9984,6 +10434,13 @@ snapshots:
internmap@2.0.3: {}
+ intl-messageformat@10.5.14:
+ dependencies:
+ '@formatjs/ecma402-abstract': 2.0.0
+ '@formatjs/fast-memoize': 2.2.0
+ '@formatjs/icu-messageformat-parser': 2.7.8
+ tslib: 2.6.2
+
invariant@2.2.4:
dependencies:
loose-envify: 1.4.0
@@ -10240,14 +10697,22 @@ snapshots:
lodash.castarray@4.4.0: {}
+ lodash.foreach@4.5.0: {}
+
+ lodash.get@4.4.2: {}
+
lodash.isplainobject@4.0.6: {}
lodash.kebabcase@4.1.1: {}
+ lodash.mapkeys@4.6.0: {}
+
lodash.merge@4.6.2: {}
lodash.mergewith@4.6.2: {}
+ lodash.omit@4.5.0: {}
+
lodash.snakecase@4.1.1: {}
lodash.startcase@4.4.0: {}
@@ -11686,6 +12151,10 @@ snapshots:
ajv: 6.12.6
ajv-keywords: 3.5.2(ajv@6.12.6)
+ scroll-into-view-if-needed@3.0.10:
+ dependencies:
+ compute-scroll-into-view: 3.1.0
+
section-matter@1.0.0:
dependencies:
extend-shallow: 2.0.1
@@ -11987,12 +12456,19 @@ snapshots:
react: 18.3.1
use-sync-external-store: 1.2.2(react@18.3.1)
+ tailwind-merge@1.14.0: {}
+
tailwind-merge@2.2.0:
dependencies:
'@babel/runtime': 7.24.4
tailwind-merge@2.4.0: {}
+ tailwind-variants@0.1.20(tailwindcss@3.4.6):
+ dependencies:
+ tailwind-merge: 1.14.0
+ tailwindcss: 3.4.6
+
tailwindcss-animate@1.0.7(tailwindcss@3.4.6):
dependencies:
tailwindcss: 3.4.6