Skip to content

Commit

Permalink
Merge pull request #9 from eight-labs/form-cards
Browse files Browse the repository at this point in the history
Add Form cards
  • Loading branch information
ephraimduncan authored Feb 21, 2024
2 parents 8e8f612 + e8bee29 commit 2f371cd
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 20 deletions.
72 changes: 72 additions & 0 deletions src/app/(main)/dashboard/_components/delete-form-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { TrashIcon } from "@radix-ui/react-icons";
import { useRouter } from "next/navigation";
import React from "react";
import { toast } from "sonner";

import { api } from "~/trpc/react";

type DeleteFormDialogProps = {
formId: string;
};

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

const router = useRouter();
const deleteFormMutation = api.form.delete.useMutation();

const handleDelete = async () => {
deleteFormMutation.mutate(
{ id: formId },
{
onSuccess: () => {
router.refresh();
toast.success("Form deleted successfully");
},
},
);
};

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">
<TrashIcon className="h-4 w-4 duration-300 group-hover:text-red-500 dark:text-white" />
</div>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Are you sure you want to delete this form?</DialogTitle>
<DialogDescription>
This will delete all associated submissions and cannot be undone.
</DialogDescription>
</DialogHeader>
<div className="mt-3 flex w-full gap-4">
<Button
variant="outline"
className="w-full"
onClick={() => setOpen(false)}
>
Cancel
</Button>
<Button
variant="destructive"
className="w-full"
onClick={handleDelete}
>
Delete
</Button>
</div>
</DialogContent>
</Dialog>
);
}
81 changes: 81 additions & 0 deletions src/app/(main)/dashboard/_components/form-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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 { DeleteFormDialog } from "./delete-form-dialog";

type FormCardProp = {
form: RouterOutputs["form"]["userForms"][number];
};

export function FormCard({ form }: FormCardProp) {
const handleCopyAction = async () => {
await navigator.clipboard.writeText(form.id);
toast("Copied to clipboard", {
icon: <CopyIcon className="h-4 w-4" />,
});
};

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}
/>
</div>
</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">
<p className="text-sm text-muted-foreground">
Last submission: 2/12/2021
</p>
</div>
<Link
href={`/form/s/${form.id}`}
className="text-sm underline underline-offset-2"
>
View all
</Link>
</CardFooter>
</Card>
);
}
15 changes: 6 additions & 9 deletions src/app/(main)/dashboard/_components/forms.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";

import { type RouterOutputs } from "~/trpc/shared";
import { Input } from "@/components/ui/input";
import * as React from "react";
import { CreateFormDialog } from "./new-form-dialog";
import Link from "next/link";
import { type RouterOutputs } from "~/trpc/shared";
import { FormCard } from "./form-card";

interface FormsProps {
promises: Promise<
Expand All @@ -26,14 +26,11 @@ export function Forms({ promises }: FormsProps) {

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

<div className="flex flex-col space-y-2">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{forms.map((form) => (
<Link href={`/form/${form.id}`} key={form.id}>
{/* <pre>{JSON.stringify(form, null, 2)}</pre> */}
Name: {form.title}
</Link>
<FormCard form={form} key={form.id} />
))}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(main)/dashboard/_components/new-form-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function CreateFormDialog() {

return (
<Form {...form}>
<div className="w-2/3 space-y-6">
<div className="space-y-6">
<Dialog open={showDialog} onOpenChange={setShowDialog}>
<DialogTrigger asChild>
<Button variant="outline">New Form</Button>
Expand Down
15 changes: 12 additions & 3 deletions src/app/(main)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { validateRequest } from "~/lib/auth/validate-request";
import { redirects } from "~/lib/constants";
import { redirect } from "next/navigation";
import * as React from "react";
import { validateRequest } from "~/lib/auth/validate-request";
import { redirects } from "~/lib/constants";

import { Poppins } from "next/font/google";

type DashboardLayoutProps = {
children: React.ReactNode;
};

const poppinsFont = Poppins({
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
});

export default async function DashboardLayout({
children,
}: DashboardLayoutProps) {
Expand All @@ -15,7 +22,9 @@ export default async function DashboardLayout({
if (!user) redirect(redirects.toLogin);

return (
<div className="container flex min-h-[calc(100vh-180px)] flex-col gap-6 px-2 pt-6 md:flex-row md:px-4 lg:gap-10">
<div
className={`container flex min-h-[calc(100vh-180px)] flex-col gap-6 px-2 pt-6 md:flex-row md:px-4 lg:gap-10 ${poppinsFont.className}`}
>
<main className="w-full">{children}</main>
</div>
);
Expand Down
16 changes: 11 additions & 5 deletions src/app/(main)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { env } from "~/env";
import { api } from "~/trpc/server";
import { type Metadata } from "next";
import * as React from "react";
import { env } from "~/env";
import { api } from "~/trpc/server";
import { Forms } from "./_components/forms";
import { CreateFormDialog } from "./_components/new-form-dialog";
import { PostsSkeleton } from "./_components/posts-skeleton";

export const metadata: Metadata = {
Expand All @@ -25,9 +26,14 @@ export default async function DashboardPage() {

return (
<div className="py-10 md:py-8">
<div className="mb-6">
<h1 className="text-3xl font-bold md:text-4xl">Forms</h1>
<p className="text-sm text-muted-foreground">Manage your forms here</p>
<div className="mb-10 flex w-full items-center justify-between">
<div className="flex flex-col gap-1">
<h1 className="text-3xl font-bold md:text-4xl">Forms</h1>
<p className="text-sm text-muted-foreground">
Manage your forms here
</p>
</div>
<CreateFormDialog />
</div>
<React.Suspense fallback={<PostsSkeleton />}>
<Forms promises={promises} />
Expand Down
4 changes: 2 additions & 2 deletions src/server/api/routers/form.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { forms, posts } from "~/server/db/schema";
import { eq } from "drizzle-orm";
import { generateId } from "lucia";
import { z } from "zod";
import { forms, posts } from "~/server/db/schema";
import { createTRPCRouter, protectedProcedure } from "../trpc";

export const formRouter = createTRPCRouter({
Expand Down Expand Up @@ -86,7 +86,7 @@ export const formRouter = createTRPCRouter({
}),
)
.mutation(async ({ ctx, input }) => {
await ctx.db.delete(forms).where(eq(posts.id, input.id));
await ctx.db.delete(forms).where(eq(forms.id, input.id));
}),

userForms: protectedProcedure
Expand Down

0 comments on commit 2f371cd

Please sign in to comment.