diff --git a/package.json b/package.json
index e13f001..ef9a8c4 100644
--- a/package.json
+++ b/package.json
@@ -21,16 +21,19 @@
"@lucia-auth/adapter-drizzle": "1.0.0",
"@planetscale/database": "^1.11.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
+ "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@react-email/components": "^0.0.12",
"@react-email/render": "^0.0.10",
"@t3-oss/env-nextjs": "^0.7.1",
"@tanstack/react-query": "^4.36.1",
+ "@tanstack/react-table": "^8.12.0",
"@trpc/client": "^10.43.6",
"@trpc/next": "^10.43.6",
"@trpc/react-query": "^10.43.6",
@@ -41,6 +44,7 @@
"drizzle-orm": "^0.29.3",
"json-to-zod": "^1.1.2",
"lucia": "3.0.0",
+ "lucide-react": "^0.335.0",
"next": "^14.1.0",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.7",
diff --git a/src/app/(main)/form/[id]/page.tsx b/src/app/(main)/form/[id]/page.tsx
index 9a881c8..887af60 100644
--- a/src/app/(main)/form/[id]/page.tsx
+++ b/src/app/(main)/form/[id]/page.tsx
@@ -1,18 +1,42 @@
import { api } from "~/trpc/server";
+import { Copy } from "lucide-react";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { SubmissionsTable } from "./submissions-table";
export default async function FormPage({ params }: { params: { id: string } }) {
- const formSubmissions = await api.formData.all.query({ formId: params.id });
+ const formId = params.id;
+ const [form, formSubmissions] = await Promise.all([
+ api.form.get.query({ formId }),
+ api.formData.all.query({ formId }),
+ ]);
return (
-
-
My Form Id: {params.id}
-
My Submittions
-
+
- {formSubmissions.map((f) => {
- return
{JSON.stringify(f.data as string)}
;
- })}
+
{form?.title}
+
+
+
+ {formId}
+
+
+
+
+
+
+ Submissions
+ Setup
+ Analytics
+ Settings
+
+
+
+
+ Change your password here.
+ Look at your analytics here
+ Edit your form here
+
);
}
diff --git a/src/app/(main)/form/[id]/submissions-table.tsx b/src/app/(main)/form/[id]/submissions-table.tsx
new file mode 100644
index 0000000..9d5b359
--- /dev/null
+++ b/src/app/(main)/form/[id]/submissions-table.tsx
@@ -0,0 +1,228 @@
+/* eslint-disable @typescript-eslint/no-unsafe-argument */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-return */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+"use client";
+
+import * as React from "react";
+import { DotsHorizontalIcon } from "@radix-ui/react-icons";
+import {
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+} from "@tanstack/react-table";
+import type {
+ ColumnDef,
+ ColumnFiltersState,
+ SortingState,
+ VisibilityState,
+} from "@tanstack/react-table";
+
+import { Button } from "@/components/ui/button";
+import { Checkbox } from "@/components/ui/checkbox";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import type { FormData } from "@/server/db/schema";
+
+type SubmissionsTableProps = {
+ submissions: FormData[];
+};
+
+export function SubmissionsTable({ submissions }: SubmissionsTableProps) {
+ const [sorting, setSorting] = React.useState
([]);
+ const [columnFilters, setColumnFilters] = React.useState(
+ [],
+ );
+ const [columnVisibility, setColumnVisibility] =
+ React.useState({});
+ const [rowSelection, setRowSelection] = React.useState({});
+
+ const columns: ColumnDef[] = [
+ {
+ id: "select",
+ header: ({ table }) => {
+ console.log(table.getIsAllPageRowsSelected());
+
+ return (
+
+ table.toggleAllPageRowsSelected(!!value)
+ }
+ aria-label="Select all"
+ />
+ );
+ },
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Select row"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+
+ ...Object.keys(submissions[0]?.data as object).map((submission: any) => {
+ return {
+ accessorKey: submission,
+ header: () => {
+ return (
+
+ );
+ },
+ cell: ({ row }: any) => (
+ {row.getValue(submission)}
+ ),
+ };
+ }),
+
+ {
+ id: "actions",
+ enableHiding: false,
+ cell: ({ row }) => {
+ return (
+
+
+
+
+
+ Actions
+ Copy submission ID
+
+ View customer
+ View payment details
+
+
+ );
+ },
+ },
+ ];
+
+ const table = useReactTable({
+ data: submissions.map((submission: any) => submission.data),
+ columns: columns,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ onColumnVisibilityChange: setColumnVisibility,
+ onRowSelectionChange: setRowSelection,
+ state: {
+ sorting,
+ columnFilters,
+ columnVisibility,
+ rowSelection,
+ },
+ });
+
+ return (
+
+
+
+
+ {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.
+
+
+ )}
+
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "}
+ {table.getFilteredRowModel().rows.length} row(s) selected.
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..ad4ddbc
--- /dev/null
+++ b/src/components/ui/checkbox.tsx
@@ -0,0 +1,30 @@
+"use client"
+
+import * as React from "react"
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { Check } from "lucide-react"
+
+import { cn } from "~/lib/utils"
+
+const Checkbox = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+))
+Checkbox.displayName = CheckboxPrimitive.Root.displayName
+
+export { Checkbox }
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
new file mode 100644
index 0000000..1a477d6
--- /dev/null
+++ b/src/components/ui/popover.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import * as React from "react";
+import * as PopoverPrimitive from "@radix-ui/react-popover";
+
+import { cn } from "~/lib/utils";
+
+const Popover = PopoverPrimitive.Root;
+
+const PopoverTrigger = PopoverPrimitive.Trigger;
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+
+
+));
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
+
+export { Popover, PopoverTrigger, PopoverContent };
diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx
new file mode 100644
index 0000000..b3b3711
--- /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,
+}
diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts
index 6773815..2b83c87 100644
--- a/src/server/db/schema.ts
+++ b/src/server/db/schema.ts
@@ -155,3 +155,9 @@ export const postRelations = relations(posts, ({ one }) => ({
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;
+
+export type FormData = typeof formDatas.$inferSelect;
+export type NewFormData = typeof formDatas.$inferInsert;
+
+export type Form = typeof forms.$inferSelect;
+export type NewForm = typeof forms.$inferInsert;
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 35b3a19..65781ed 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -9,7 +9,7 @@ export default {
center: true,
padding: "2rem",
screens: {
- lg: "768px",
+ lg: "1000px",
},
},
extend: {