Skip to content

Commit

Permalink
Merge pull request #1 from moreorover/customer-form
Browse files Browse the repository at this point in the history
Customer Form as modals within intercepted routes
  • Loading branch information
moreorover authored Dec 2, 2024
2 parents e78fa10 + c0c49bf commit 0ca4f21
Show file tree
Hide file tree
Showing 17 changed files with 631 additions and 24 deletions.
12 changes: 12 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_APP_URL=http://localhost:3000

# This was inserted by `prisma init`:
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://admin:admin@localhost:5432/sensepro?schema=public"
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ yarn-debug.log*
yarn-error.log*

# env files (can opt-in for committing if needed)
.env*
# .env*

# vercel
.vercel
Expand Down
31 changes: 31 additions & 0 deletions app/@modal/(.)customers/delete/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import CustomerDeleteForm from "@/app/customers/CustomerDeleteForm";
import { Modal } from "@/components/Modal";
import { getCustomer } from "@/data-access/customer";

export default async function EditCustomerModal({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;

const customer = await getCustomer(id);

if (!customer?.id) {
return (
<Modal title="Not found" description="No Customer Found for that ID">
<div className="p-8 max-w-md space-y-2">
<h1 className="text-2xl">No Customer Found for that ID.</h1>
</div>
</Modal>
);
}

return (
<Modal title="Delete customer" description="Deleted customer as needed.">
<div className="p-2 max-w-md">
<CustomerDeleteForm customer={customer} />
</div>
</Modal>
);
}
31 changes: 31 additions & 0 deletions app/@modal/(.)customers/edit/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import CustomerForm from "@/app/customers/CustomerForm";
import { Modal } from "@/components/Modal";
import { getCustomer } from "@/data-access/customer";

export default async function EditCustomerModal({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;

const customer = await getCustomer(id);

if (!customer?.id) {
return (
<Modal title="Not found" description="No Customer Found for that ID">
<div className="p-8 max-w-md space-y-2">
<h1 className="text-2xl">No Customer Found for that ID.</h1>
</div>
</Modal>
);
}

return (
<Modal title="Update customer" description="Update customer as needed.">
<div className="p-2 max-w-md">
<CustomerForm customer={customer} />
</div>
</Modal>
);
}
14 changes: 14 additions & 0 deletions app/@modal/(.)customers/new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import CustomerForm from "@/app/customers/CustomerForm";
import { Modal } from "@/components/Modal";

export default async function NewCustomerModal() {
const customer = { name: "" };

return (
<Modal title="New customer" description="Create customer as needed.">
<div className="p-2 max-w-md">
<CustomerForm customer={customer} />
</div>
</Modal>
);
}
3 changes: 3 additions & 0 deletions app/@modal/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Default() {
return null;
}
105 changes: 105 additions & 0 deletions app/customers/CustomerDeleteForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"use client";

import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Customer, customerSchema } from "@/lib/schemas";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";

import { Button } from "@/components/ui/button";
import { deleteCustomer } from "@/data-access/customer";
import { useToast } from "@/hooks/use-toast";
import { Loader2 } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";

type Props = {
customer: Customer;
className?: string;
};

export default function CustomerDeleteForm({ customer, className }: Props) {
const router = useRouter();
const { toast } = useToast();
const [submitting, setSubmitting] = useState(false);

const form = useForm<Customer>({
resolver: zodResolver(customerSchema),
defaultValues: {
...customer,
},
});

async function onSubmit(values: Customer) {
setSubmitting(true);
const response = await deleteCustomer(values);
setSubmitting(false);

if (response.type === "ERROR") {
toast({
variant: "destructive",
description: response.message,
});
} else {
toast({
variant: "default",
description: response.message,
});
router.back();
}
}

return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className={cn("space-y-2", className)}
>
<FormField
control={form.control}
name="id"
render={({ field }) => (
<FormItem className="sr-only">
<FormLabel>ID</FormLabel>
<FormControl>
<Input type="hidden" {...field} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input disabled={true} placeholder="John Doe" {...field} />
{/* <ZodErrors error={state?.zodErrors?.name} /> */}
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" disabled={submitting}>
{submitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{"Deleteing..."}
</>
) : (
"Confirm"
)}
</Button>
</form>
</Form>
);
}
109 changes: 109 additions & 0 deletions app/customers/CustomerForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use client";

import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Customer, customerSchema } from "@/lib/schemas";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";

import { Button } from "@/components/ui/button";
import { createCustomer, updateCustomer } from "@/data-access/customer";
import { useToast } from "@/hooks/use-toast";
import { Loader2 } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";

type Props = {
customer: Customer;
className?: string;
};

export default function CustomerForm({ customer, className }: Props) {
const router = useRouter();
const { toast } = useToast();
const [submitting, setSubmitting] = useState(false);

const form = useForm<Customer>({
resolver: zodResolver(customerSchema),
defaultValues: {
...customer,
},
});

async function onSubmit(values: Customer) {
setSubmitting(true);
const response = customer.id
? await updateCustomer(values)
: await createCustomer(values);
setSubmitting(false);

if (response.type === "ERROR") {
toast({
variant: "destructive",
description: response.message,
});
} else {
toast({
variant: "default",
description: response.message,
});
router.back();
}
}

return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className={cn("space-y-2", className)}
>
{customer.id && (
<FormField
control={form.control}
name="id"
render={({ field }) => (
<FormItem className="sr-only">
<FormLabel>ID</FormLabel>
<FormControl>
<Input type="hidden" {...field} />
</FormControl>
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="John Doe" {...field} />
{/* <ZodErrors error={state?.zodErrors?.name} /> */}
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" disabled={submitting}>
{submitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{customer.id ? "Updating..." : "Creating..."}
</>
) : (
"Submit"
)}
</Button>
</form>
</Form>
);
}
34 changes: 34 additions & 0 deletions app/customers/CustomerRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client";
import { TableCell, TableRow } from "@/components/ui/table";
import { Customer } from "@/lib/schemas";
import { Pencil, Trash } from "lucide-react";
import { useRouter } from "next/navigation";

type Props = {
customer: Customer;
};

export default function UserRow({ customer }: Props) {
const router = useRouter();

const handleClickEdit = () => {
router.push(`/customers/edit/${customer.id}`);
};

const handleClickDelete = () => {
router.push(`/customers/delete/${customer.id}`);
};

return (
<TableRow>
<TableCell>{customer.id}</TableCell>
<TableCell>{customer.name}</TableCell>
<TableCell onClick={handleClickEdit}>
<Pencil />
</TableCell>
<TableCell onClick={handleClickDelete}>
<Trash />
</TableCell>
</TableRow>
);
}
Loading

0 comments on commit 0ca4f21

Please sign in to comment.