Skip to content

Commit

Permalink
Merge pull request #33 from eight-labs/fix/table-freezing
Browse files Browse the repository at this point in the history
fix: table freezing issue
  • Loading branch information
ephraimduncan authored Feb 25, 2024
2 parents 8e6104b + a114066 commit 0df5451
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 50 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions src/app/(main)/dashboard/_components/delete-form-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TrashIcon } from "@radix-ui/react-icons";
import { useRouter } from "next/navigation";
import React from "react";
import { useState } from "react";
import { toast } from "sonner";

import { Button } from "~/components/ui/button";
Expand All @@ -19,7 +19,7 @@ type DeleteFormDialogProps = {
};

export function DeleteFormDialog({ formId }: DeleteFormDialogProps) {
const [open, setOpen] = React.useState(false);
const [open, setOpen] = useState(false);

const router = useRouter();
const { mutateAsync: deleteForm } = api.form.delete.useMutation();
Expand Down
5 changes: 3 additions & 2 deletions src/app/(main)/dashboard/_components/new-form-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const FormSchema = z.object({
}),
returnUrl: z.string().optional(),
description: z.string().optional(),
keys: z.string(),
});

export function CreateFormDialog() {
Expand All @@ -43,6 +44,7 @@ export function CreateFormDialog() {
name: "",
description: "",
returnUrl: "",
keys: "",
},
});
const [showDialog, setShowDialog] = useState<boolean>(false);
Expand All @@ -57,6 +59,7 @@ export function CreateFormDialog() {
title: data.name,
description: data.description,
returningUrl: data.returnUrl,
keys: "",
},
{
onSuccess: ({ id }) => {
Expand All @@ -76,8 +79,6 @@ export function CreateFormDialog() {
};

function onSubmit(data: z.infer<typeof FormSchema>) {
console.log("data", data);

createPost(data);

setShowDialog(false);
Expand Down
12 changes: 10 additions & 2 deletions src/app/(main)/form/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { SubmissionsTable } from "./submissions-table";

export default async function FormPage({ params }: { params: { id: string } }) {
const formId = params.id;
const [form, _formSubmissions] = await Promise.all([
const [form, formSubmissions] = await Promise.all([
api.form.get.query({ formId }),
api.formData.all.query({ formId }),
]);
Expand All @@ -32,7 +32,15 @@ export default async function FormPage({ params }: { params: { id: string } }) {
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="submissions" className="my-6">
<SubmissionsTable formId={formId} />
{formSubmissions.length < 1 && form?.keys === "" ? (
<div>No form submissions</div>
) : (
<SubmissionsTable
formKeys={form?.keys || ""}
formId={formId}
submissions={formSubmissions}
/>
)}
</TabsContent>
<TabsContent value="setup">Change your password here.</TabsContent>
<TabsContent value="analytics">Look at your analytics here</TabsContent>
Expand Down
76 changes: 38 additions & 38 deletions src/app/(main)/form/[id]/submissions-table.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";

import { DotsHorizontalIcon } from "@radix-ui/react-icons";
import { CaretSortIcon, DotsHorizontalIcon } from "@radix-ui/react-icons";
import type {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
} from "@tanstack/react-table";
import {
flexRender,
getCoreRowModel,
Expand All @@ -10,12 +16,6 @@ import {
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import type {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
} from "@tanstack/react-table";
import * as React from "react";

import { Button } from "~/components/ui/button";
Expand All @@ -37,28 +37,25 @@ import {
TableRow,
} from "~/components/ui/table";
import type { FormData } from "~/server/db/schema";
import { api } from "~/trpc/react";

type SubmissionsTableProps = {
formKeys: string;
formId: string;
submissions: any; // FIXME: take care of this
};

export function SubmissionsTable({ formId }: SubmissionsTableProps) {
export function SubmissionsTable({
submissions,
formKeys,
}: SubmissionsTableProps) {
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[],
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});

const { data: submissions, isLoading } = api.formData.all.useQuery({
formId,
});

if (!isLoading && submissions && submissions.length < 0) {
return <div>No submissions have been collected for this form!</div>;
}
const formKeysArray = formKeys.split("~?").filter((key) => key.length > 0);

const columns: ColumnDef<FormData["data"]>[] = [
{
Expand Down Expand Up @@ -88,24 +85,29 @@ export function SubmissionsTable({ formId }: SubmissionsTableProps) {
enableHiding: false,
},

...Object.keys(submissions ? (submissions[0]?.data as object) : {}).map(
(submission: any) => {
return {
accessorKey: submission,
header: () => {
return (
<Button variant="ghost" className="px-0 py-0 capitalize">
{submission}
{/* <CaretSortIcon className="ml-2 h-4 w-4" /> */}
</Button>
);
},
cell: ({ row }: any) => (
<div className="lowercase">{row.getValue(submission)}</div>
),
};
},
),
...formKeysArray.map((submission: any) => {
return {
accessorKey: submission,
header: () => {
return (
<Button
variant="ghost"
className="px-0 py-0 capitalize hover:bg-transparent"
>
{submission}
<CaretSortIcon className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }: any) => {
return (
<div className="lowercase">
{row.original[`data.${submission}`]}
</div>
);
},
};
}),

{
id: "actions",
Expand Down Expand Up @@ -133,9 +135,7 @@ export function SubmissionsTable({ formId }: SubmissionsTableProps) {
];

const table = useReactTable({
data: submissions
? submissions.map((submission: any) => submission.data)
: [],
data: submissions,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
Expand Down
9 changes: 8 additions & 1 deletion src/app/api/s/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export async function POST(
where: (table, { eq }) => eq(table.id, formId),
});

const formDataKeys = Object.keys(formData);
const formKeys = form?.keys?.split("~?") || [];
const updatedKeys = [...new Set([...formKeys, ...formDataKeys])].join("~?");

if (!form) {
return new Response("Form not found", { status: 404 });
}
Expand All @@ -35,7 +39,10 @@ export async function POST(

await db
.update(forms)
.set({ updatedAt: new Date() })
.set({
updatedAt: new Date(),
keys: updatedKeys,
})
.where(eq(forms.id, formId));

// only send the email if the user has enabled it: it is enabled by default
Expand Down
22 changes: 22 additions & 0 deletions src/lib/flatten-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
interface FlattenedObject {
[key: string]: any;
}

export const flattenObject = (
obj: Record<string, any>,
parentKey = "",
): FlattenedObject => {
let flattenedObj: FlattenedObject = {};

for (const key in obj) {
if (typeof obj[key] === "object" && !Array.isArray(obj[key])) {
const nestedObj = flattenObject(obj[key], `${parentKey}${key}.`);
flattenedObj = { ...flattenedObj, ...nestedObj };
} else {
flattenedObj[`${parentKey}${key}`] = obj[key];
}
}

return flattenedObj;
};
2 changes: 2 additions & 0 deletions src/server/api/routers/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const formRouter = createTRPCRouter({
title: z.string().min(1).max(255),
description: z.string().optional(),
returningUrl: z.string().optional(),
keys: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
Expand All @@ -57,6 +58,7 @@ export const formRouter = createTRPCRouter({
description: input.description,
updatedAt: new Date(),
returnUrl: input.returningUrl,
keys: "",
});

return { id };
Expand Down
14 changes: 9 additions & 5 deletions src/server/api/routers/formData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { eq } from "drizzle-orm";
import { nanoid as generateId } from "nanoid";
import { z } from "zod";

import { flattenObject } from "~/lib/flatten-object";
import { formDatas, forms } from "~/server/db/schema";

import { createTRPCRouter, protectedProcedure } from "../trpc";
Expand Down Expand Up @@ -79,10 +80,13 @@ export const formDataRouter = createTRPCRouter({
formId: z.string(),
}),
)
.query(({ ctx, input }) =>
ctx.db.query.formDatas.findMany({
.query(async ({ ctx, input }) => {
const formData = await ctx.db.query.formDatas.findMany({
where: (table, { eq }) => eq(table.formId, input.formId),
// orderBy: (table, { desc }) => desc(table.createdAt),
}),
),
});

return formData.map((data) => {
return { ...flattenObject(data), data: data.data };
});
}),
});
1 change: 1 addition & 0 deletions src/server/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const forms = createTable(
sendEmailForNewSubmissions: boolean("send_email_for_new_submissions")
.default(true)
.notNull(),
keys: varchar("keys").notNull(),
},
(t) => ({
userIdx: index("form_user_idx").on(t.userId),
Expand Down

0 comments on commit 0df5451

Please sign in to comment.