diff --git a/src/app/[orgId]/sites/[siteId]/layout.tsx b/src/app/[orgId]/sites/[siteId]/layout.tsx
index 973323c..01863e0 100644
--- a/src/app/[orgId]/sites/[siteId]/layout.tsx
+++ b/src/app/[orgId]/sites/[siteId]/layout.tsx
@@ -1,19 +1,21 @@
-import { Metadata } from "next"
-import Image from "next/image"
+import { Metadata } from "next";
+import Image from "next/image";
-import { Separator } from "@/components/ui/separator"
-import { SidebarNav } from "@/components/sidebar-nav"
-import SiteProvider from "@app/providers/SiteProvider"
-import { internal } from "@app/api"
-import { GetSiteResponse } from "@server/routers/site"
-import { AxiosResponse } from "axios"
-import { redirect } from "next/navigation"
-import { authCookieHeader } from "@app/api/cookies"
+import { Separator } from "@/components/ui/separator";
+import { SidebarNav } from "@/components/sidebar-nav";
+import SiteProvider from "@app/providers/SiteProvider";
+import { internal } from "@app/api";
+import { GetSiteResponse } from "@server/routers/site";
+import { AxiosResponse } from "axios";
+import { redirect } from "next/navigation";
+import { authCookieHeader } from "@app/api/cookies";
+import Link from "next/link";
+import { ArrowLeft, ChevronLeft } from "lucide-react";
export const metadata: Metadata = {
title: "Forms",
description: "Advanced form example using react-hook-form and Zod.",
-}
+};
const sidebarNavItems = [
{
@@ -32,21 +34,27 @@ const sidebarNavItems = [
title: "Display",
href: "/{orgId}/sites/{siteId}/display",
},
-]
+];
interface SettingsLayoutProps {
- children: React.ReactNode,
- params: { siteId: string, orgId: string }
+ children: React.ReactNode;
+ params: { siteId: string; orgId: string };
}
-export default async function SettingsLayout({ children, params }: SettingsLayoutProps) {
+export default async function SettingsLayout({
+ children,
+ params,
+}: SettingsLayoutProps) {
let site = null;
if (params.siteId !== "create") {
try {
- const res = await internal.get
>(`/site/${params.siteId}`, authCookieHeader());
+ const res = await internal.get>(
+ `/site/${params.siteId}`,
+ authCookieHeader(),
+ );
site = res.data.data;
} catch {
- redirect(`/${params.orgId}/sites`)
+ redirect(`/${params.orgId}/sites`);
}
}
@@ -68,25 +76,47 @@ export default async function SettingsLayout({ children, params }: SettingsLayou
className="hidden dark:block"
/>
-
Settings
+
+ {params.siteId == "create"
+ ? "New Site"
+ : site?.name + " Settings" || "Site Settings"
+ }
+
- {params.siteId == "create" ? "Create site..." : "Manage settings on " + site?.name || ""}.
+ {params.siteId == "create"
+ ? "Create a new site"
+ : "Configure the settings on your site: " +
+ site?.name || ""}
+ .
-
-
+
-
- {children}
-
+ {children}
>
- )
+ );
}
diff --git a/src/app/[orgId]/sites/components/DataTable.tsx b/src/app/[orgId]/sites/components/DataTable.tsx
new file mode 100644
index 0000000..a797df0
--- /dev/null
+++ b/src/app/[orgId]/sites/components/DataTable.tsx
@@ -0,0 +1,140 @@
+"use client";
+
+import {
+ ColumnDef,
+ flexRender,
+ getCoreRowModel,
+ useReactTable,
+ getPaginationRowModel,
+ SortingState,
+ getSortedRowModel,
+ ColumnFiltersState,
+ getFilteredRowModel,
+} from "@tanstack/react-table";
+
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Button } from "@app/components/ui/button";
+import { useState } from "react";
+import { Input } from "@app/components/ui/input";
+import { DataTablePagination } from "./DataTablePagination";
+import { Plus } from "lucide-react";
+
+interface DataTableProps
{
+ columns: ColumnDef[];
+ data: TData[];
+ addSite?: () => void;
+}
+
+export function DataTable({
+ addSite,
+ columns,
+ data,
+}: DataTableProps) {
+ const [sorting, setSorting] = useState([]);
+ const [columnFilters, setColumnFilters] = useState([]);
+
+ const table = useReactTable({
+ data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onSortingChange: setSorting,
+ getSortedRowModel: getSortedRowModel(),
+ onColumnFiltersChange: setColumnFilters,
+ getFilteredRowModel: getFilteredRowModel(),
+ state: {
+ sorting,
+ columnFilters,
+ },
+ });
+
+ return (
+
+
+
+ table
+ .getColumn("name")
+ ?.setFilterValue(event.target.value)
+ }
+ className="max-w-sm"
+ />
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => {
+ return (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef
+ .header,
+ header.getContext(),
+ )}
+
+ );
+ })}
+
+ ))}
+
+
+ {table.getRowModel().rows?.length ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext(),
+ )}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ No results.
+
+
+ )}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/[orgId]/sites/components/DataTablePagination.tsx b/src/app/[orgId]/sites/components/DataTablePagination.tsx
new file mode 100644
index 0000000..ac481a8
--- /dev/null
+++ b/src/app/[orgId]/sites/components/DataTablePagination.tsx
@@ -0,0 +1,102 @@
+import {
+ ChevronLeftIcon,
+ ChevronRightIcon,
+ DoubleArrowLeftIcon,
+ DoubleArrowRightIcon,
+} from "@radix-ui/react-icons";
+import { Table } from "@tanstack/react-table";
+
+import { Button } from "@app/components/ui/button";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@app/components/ui/select";
+
+interface DataTablePaginationProps {
+ table: Table;
+}
+
+export function DataTablePagination({
+ table,
+}: DataTablePaginationProps) {
+ return (
+
+
+
+
Rows per page
+
+
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "}
+ {table.getPageCount()}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/[orgId]/sites/components/SitesTable.tsx b/src/app/[orgId]/sites/components/SitesTable.tsx
new file mode 100644
index 0000000..4fadab8
--- /dev/null
+++ b/src/app/[orgId]/sites/components/SitesTable.tsx
@@ -0,0 +1,110 @@
+"use client";
+
+import { ColumnDef } from "@tanstack/react-table";
+import { DataTable } from "./DataTable";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@app/components/ui/dropdown-menu";
+import { Button } from "@app/components/ui/button";
+import { ArrowUpDown, MoreHorizontal } from "lucide-react";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+
+export type SiteRow = {
+ id: string;
+ name: string;
+ mbIn: number;
+ mbOut: number;
+ orgId: string;
+};
+
+export const columns: ColumnDef[] = [
+ {
+ accessorKey: "id",
+ header: ({ column }) => {
+ return (
+
+ );
+ },
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => {
+ return (
+
+ );
+ },
+ },
+ {
+ accessorKey: "mbIn",
+ header: "MB In",
+ },
+ {
+ accessorKey: "mbOut",
+ header: "MB Out",
+ },
+ {
+ id: "actions",
+ cell: ({ row }) => {
+ const siteRow = row.original;
+
+ return (
+
+
+
+
+
+
+
+ View settings
+
+
+
+
+ );
+ },
+ },
+];
+
+type SitesTableProps = {
+ sites: SiteRow[];
+ orgId: string;
+};
+
+export default function SitesTable({ sites, orgId }: SitesTableProps) {
+ const router = useRouter();
+
+ return (
+ {
+ router.push(`/${orgId}/sites/create`);
+ }}
+ />
+ );
+}
diff --git a/src/app/[orgId]/sites/page.tsx b/src/app/[orgId]/sites/page.tsx
index 15b8677..86190f0 100644
--- a/src/app/[orgId]/sites/page.tsx
+++ b/src/app/[orgId]/sites/page.tsx
@@ -2,6 +2,7 @@ import { internal } from "@app/api";
import { authCookieHeader } from "@app/api/cookies";
import { ListSitesResponse } from "@server/routers/site";
import { AxiosResponse } from "axios";
+import SitesTable, { SiteRow } from "./components/SitesTable";
type SitesPageProps = {
params: { orgId: string };
@@ -19,9 +20,19 @@ export default async function Page({ params }: SitesPageProps) {
console.error("Error fetching sites", e);
}
+ const siteRows: SiteRow[] = sites.map((site) => {
+ return {
+ id: site.siteId.toString(),
+ name: site.name,
+ mbIn: site.megabytesIn || 0,
+ mbOut: site.megabytesOut || 0,
+ orgId: params.orgId,
+ };
+ });
+
return (
<>
-
+
Manage Sites
@@ -29,6 +40,8 @@ export default async function Page({ params }: SitesPageProps) {
Manage your existing sites here or create a new one.
+
+
>
);
}
diff --git a/src/app/globals.css b/src/app/globals.css
index e8e65eb..bc8a229 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -36,7 +36,7 @@
--primary-foreground: 0 0% 100%;
--secondary: 231 10% 20%;
--secondary-foreground: 0 0% 100%;
- --muted: 193 10% 25%;
+ --muted: 231 10% 18%;
--muted-foreground: 231 0% 65%;
--accent: 193 10% 25%;
--accent-foreground: 231 0% 95%;
diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx
new file mode 100644
index 0000000..7f3502f
--- /dev/null
+++ b/src/components/ui/table.tsx
@@ -0,0 +1,117 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Table = React.forwardRef<
+ HTMLTableElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Table.displayName = "Table"
+
+const TableHeader = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableHeader.displayName = "TableHeader"
+
+const TableBody = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableBody.displayName = "TableBody"
+
+const TableFooter = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+))
+TableFooter.displayName = "TableFooter"
+
+const TableRow = React.forwardRef<
+ HTMLTableRowElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableRow.displayName = "TableRow"
+
+const TableHead = React.forwardRef<
+ HTMLTableCellElement,
+ React.ThHTMLAttributes
+>(({ className, ...props }, ref) => (
+ |
+))
+TableHead.displayName = "TableHead"
+
+const TableCell = React.forwardRef<
+ HTMLTableCellElement,
+ React.TdHTMLAttributes
+>(({ className, ...props }, ref) => (
+ |
+))
+TableCell.displayName = "TableCell"
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableCaption.displayName = "TableCaption"
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}