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 + +
+ )}
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) => (