Skip to content

Commit

Permalink
Merge pull request #23 from eight-labs/fix/form-submsission
Browse files Browse the repository at this point in the history
fix: form submission empty
  • Loading branch information
ephraimduncan authored Feb 21, 2024
2 parents 435d54e + 3b16e63 commit 7e2c6fc
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 78 deletions.
4 changes: 2 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const config = {
plugins: ["@typescript-eslint"],
extends: [
"plugin:@next/next/recommended",
"plugin:@typescript-eslint/recommended-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked",
// "plugin:@typescript-eslint/recommended-type-checked",
// "plugin:@typescript-eslint/stylistic-type-checked",
],
rules: {
// These opinionated rules are enabled in stylistic-type-checked above.
Expand Down
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
"arctic": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"data-fns": "^1.1.0",
"date-fns": "^3.3.1",
"drizzle-orm": "^0.29.3",
"json-to-zod": "^1.1.2",
"lucia": "3.0.0",
Expand Down
15 changes: 10 additions & 5 deletions src/app/(main)/dashboard/_components/delete-form-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ export function DeleteFormDialog({ formId }: DeleteFormDialogProps) {
const [open, setOpen] = React.useState(false);

const router = useRouter();
const deleteFormMutation = api.form.delete.useMutation();
const { mutateAsync: deleteForm } = api.form.delete.useMutation();

const handleDelete = async () => {
deleteFormMutation.mutate(
{ id: formId },
await deleteForm(
{
id: formId,
},
{
onSuccess: () => {
router.refresh();
toast.success("Form deleted successfully");
toast.success("Your form has been deleted", {
icon: <TrashIcon className="h-4 w-4" />,
});
},
},
);
Expand All @@ -39,8 +43,9 @@ export function DeleteFormDialog({ formId }: DeleteFormDialogProps) {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<div className="group rounded-md bg-gray-100 p-2 hover:cursor-pointer dark:bg-gray-900">
<div className="group flex items-center gap-2 rounded-md hover:cursor-pointer dark:bg-gray-900">
<TrashIcon className="h-4 w-4 duration-300 group-hover:text-red-500 dark:text-white" />
<span className="hover:text-red-500">Delete</span>
</div>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
Expand Down
108 changes: 52 additions & 56 deletions src/app/(main)/dashboard/_components/form-card.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { CopyIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import { toast } from "sonner";

import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { type RouterOutputs } from "@/trpc/shared";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import type { RouterOutputs } from "@/trpc/shared";

import { DeleteFormDialog } from "./delete-form-dialog";
import { CopyIcon } from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { DotsVerticalIcon } from "@radix-ui/react-icons";
import { formatDistanceToNow } from "date-fns";

type FormCardProp = {
form: RouterOutputs["form"]["userForms"][number];
Expand All @@ -26,56 +29,49 @@ export function FormCard({ form }: FormCardProp) {
};

return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg">{form.title}</CardTitle>
<div className="flex items-center gap-2">
<p className="text-sm text-muted-foreground">{form.id}</p>
<CopyIcon
className="h-4 w-4 cursor-pointer text-muted-foreground transition-transform hover:scale-110 hover:transform"
onClick={handleCopyAction}
/>
<Link href={`/form/${form.id}`} className="text-sm underline-offset-2">
<Card>
<CardHeader>
<div className="flex items-start justify-between">
<div>
<CardTitle className="text-lg">{form.title}</CardTitle>
<div className="mt-0.5 flex items-center gap-2">
<span className="inline-flex items-center rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
{form.id}
</span>
<CopyIcon
className="h-3.5 w-3.5 cursor-pointer text-muted-foreground transition-transform hover:scale-110 hover:transform"
onClick={handleCopyAction}
/>
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open Form menu</span>
<DotsVerticalIcon className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>
<DeleteFormDialog formId={form.id} />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<DeleteFormDialog formId={form.id} />
</div>
</CardHeader>
<CardContent>
<h3 className="mb-4 font-semibold text-muted-foreground">
Submissions
</h3>
<div className="flex flex-col gap-3">
<div className="flex items-center justify-between">
<p className="text-sm text-muted-foreground">
<span className="text-muted-foreground">Monthly</span>
</p>
<p>343</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm text-muted-foreground">Last month</p>
<p>24</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm text-muted-foreground">All time</p>
<p>531</p>
</div>
</div>
</CardContent>
<CardFooter className="flex items-center justify-between">
<div className="flex items-center gap-1">
</CardHeader>

<CardContent>
<p className="mb-1 text-sm text-muted-foreground">10 submissions</p>

<p className="text-sm text-muted-foreground">
Last submission: 2/12/2021
Last submission:{" "}
{formatDistanceToNow(new Date(form.createdAt), {
addSuffix: true,
})}
</p>
</div>
<Link
href={`/form/${form.id}`}
className="text-sm underline underline-offset-2"
>
View all
</Link>
</CardFooter>
</Card>
</CardContent>
</Card>
</Link>
);
}
4 changes: 1 addition & 3 deletions src/app/(main)/dashboard/_components/forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ export function Forms({ promises }: FormsProps) {

return (
<div className="space-y-8">
<Input placeholder="Search for your form by name or id" />

<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
{forms.map((form) => (
<FormCard form={form} key={form.id} />
))}
Expand Down
5 changes: 3 additions & 2 deletions src/app/(main)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,16 @@ export default async function DashboardPage() {

return (
<div className="py-10 md:py-8">
<div className="mb-10 flex w-full items-center justify-between">
<div className="mb-10 flex w-full items-end justify-between">
<div className="flex flex-col gap-1">
<h1 className="text-3xl font-bold md:text-4xl">Forms</h1>
<h1 className="text-3xl font-bold">Forms</h1>
<p className="text-sm text-muted-foreground">
Manage your forms here
</p>
</div>
<CreateFormDialog />
</div>

<React.Suspense fallback={<PostsSkeleton />}>
<Forms promises={promises} />
</React.Suspense>
Expand Down
32 changes: 32 additions & 0 deletions src/app/(main)/form/[id]/copy-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use client";

import { useCopyToClipboard } from "~/lib/hooks/use-copy-to-clipboard";
import { Copy } from "lucide-react";
import React from "react";
import { toast } from "sonner";

type CopyFormIdProps = {
formId: string;
};

export default function CopyFormId({ formId }: CopyFormIdProps) {
const [_, copy] = useCopyToClipboard();

return (
<div className="mt-2 flex items-center gap-2">
<span className="inline-flex items-center rounded-lg bg-muted px-2 py-0.5 text-sm font-medium">
{formId}
</span>
<Copy
onClick={() =>
copy(formId).then(() => {
toast("Copied to Clipboard", {
icon: <Copy className="h-4 w-4" />,
});
})
}
className="h-4 w-4 text-muted-foreground"
/>
</div>
);
}
9 changes: 2 additions & 7 deletions src/app/(main)/form/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { api } from "~/trpc/server";
import { Copy } from "lucide-react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { SubmissionsTable } from "./submissions-table";
import CopyFormId from "./copy-button";

export default async function FormPage({ params }: { params: { id: string } }) {
const formId = params.id;
Expand All @@ -15,12 +15,7 @@ export default async function FormPage({ params }: { params: { id: string } }) {
<div>
<h1 className="text-3xl font-medium">{form?.title}</h1>

<div className="mt-2 flex items-center gap-2">
<span className="inline-flex items-center rounded-lg bg-muted px-2 py-0.5 text-sm font-medium">
{formId}
</span>
<Copy className="h-4 w-4 text-muted-foreground" />
</div>
<CopyFormId formId={formId} />
</div>

<Tabs defaultValue="submissions">
Expand Down
6 changes: 4 additions & 2 deletions src/app/(main)/form/[id]/submissions-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ export function SubmissionsTable({ submissions }: SubmissionsTableProps) {
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});

if (submissions.length < 1) {
return <div>No submissions have been collected for this form!</div>;
}

const columns: ColumnDef<FormData["data"]>[] = [
{
id: "select",
header: ({ table }) => {
console.log(table.getIsAllPageRowsSelected());

return (
<Checkbox
checked={
Expand Down
26 changes: 26 additions & 0 deletions src/lib/hooks/use-copy-to-clipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useCallback, useState } from "react";

type CopiedValue = string | null;
type CopyFn = (text: string) => Promise<boolean>;

export function useCopyToClipboard(): [CopiedValue, CopyFn] {
const [copiedText, setCopiedText] = useState<CopiedValue>(null);

const copy: CopyFn = useCallback(async (text) => {
if (!navigator?.clipboard) {
console.warn("Clipboard not supported");
return false;
}

try {
await navigator.clipboard.writeText(text);
setCopiedText(text);
return true;
} catch (error) {
setCopiedText(null);
return false;
}
}, []);

return [copiedText, copy];
}
2 changes: 1 addition & 1 deletion src/server/api/routers/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const stripeRouter = createTRPCRouter({
? formatPrice((proPrice.unit_amount ?? 0) / 100, {
currency: proPrice.currency,
})
: formatPrice(0 / 100, { currency: proPrice.currency }),
: formatPrice(0, { currency: proPrice.currency }),
};
});
} catch (err) {
Expand Down

0 comments on commit 7e2c6fc

Please sign in to comment.