Skip to content

Commit

Permalink
add edit ability
Browse files Browse the repository at this point in the history
  • Loading branch information
patelnets committed Feb 21, 2024
1 parent df0d870 commit 01afc2e
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 34 deletions.
2 changes: 1 addition & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { Products } from '@/components/products/Products';
import { getProducts } from '@/fetch-queries/products';
import { AddNewProductForm } from '@/components/products/AddNewProductForm';
import { ProductForm } from '@/components/products/ProductForm';

const STORES = [
{ label: 'Tesco', value: 'tesco', description: 'Tesco' },
Expand Down
22 changes: 22 additions & 0 deletions app/product/[slug]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getProducts } from '@/fetch-queries/products';
import { getProduct } from '@/fetch-queries/products/get-product';

import { ProductForm } from '@/components/products/ProductForm';

export default async function Page({ params }: { params: { slug: string } }) {
const product = await getProduct({ id: params.slug });

return (
<div>
<ProductForm initialValues={product} />
</div>
);
}

export async function generateStaticParams() {
const res = await getProducts();

return res.products.map(({ id }) => ({
slug: id,
}));
}
17 changes: 16 additions & 1 deletion app/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { getProducts } from '@/fetch-queries/products';
import { getProduct } from '@/fetch-queries/products/get-product';
import { Button } from '@nextui-org/button';
import Link from 'next/link';

export default async function Page({ params }: { params: { slug: string } }) {
const product = await getProduct({ id: params.slug });
return <div>Name: {product.name}</div>;
return (
<div className={'flex gap-2 flex-col'}>
<h1> {product.name} </h1>
<h2>Stores </h2>
<ul>
{product.stores.map((store) => (
<li key={store}>{store}</li>
))}
</ul>
<Link href={`/product/${params.slug}/edit`}>
<Button color='primary'>Edit</Button>
</Link>
</div>
);
}

export async function generateStaticParams() {
Expand Down
4 changes: 2 additions & 2 deletions app/products/add/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AddNewProductForm } from '@/components/products/AddNewProductForm';
import { ProductForm } from '@/components/products/ProductForm';

export default async function AddNewProduct() {
return <AddNewProductForm />;
return <ProductForm />;
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
'use client';

import { useForm, SubmitHandler, Controller } from 'react-hook-form';
import {
useForm,
SubmitHandler,
Controller,
UseControllerProps,
useController,
} from 'react-hook-form';
import { Input, CheckboxGroup, Checkbox, Button } from '@nextui-org/react';
import { useMutation } from '@tanstack/react-query';
import { addProduct } from '@/fetch-queries/products';
import { addProduct, editProduct } from '@/fetch-queries/products';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';

type Inputs = {
interface Inputs {
name: string;
stores: string[];
};
id?: string;
}

interface Store {
value: string;
displayName: string;
}

const STORES: Store[] = [
{
value: 'asda',
displayName: 'Asda',
},
{
value: 'asda',
displayName: 'Asda',
Expand Down Expand Up @@ -60,44 +63,73 @@ const STORES: Store[] = [
},
];

export const AddNewProductForm = () => {
function ControlledInput(props: UseControllerProps<Inputs>) {
const { field, fieldState } = useController(props);

return (
// @ts-ignore TODO Check how to narrow type
<Input
isRequired
label='Name'
className='max-w-xs'
placeholder={props.name}
{...field}
/>
);
}

export const ProductForm = ({ initialValues }: { initialValues?: Inputs }) => {
const { data: session } = useSession();
const router = useRouter();

const { mutate, isPending, data } = useMutation({
const { mutate: mutateAdd, isPending: isPendingAdd } = useMutation({
mutationFn: addProduct,
throwOnError: true,
});
const { mutate: mutateEdit, isPending: isPendingEdit } = useMutation({
mutationFn: editProduct,
throwOnError: true,
});

const {
control,
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<Inputs>();
} = useForm<Inputs>({
defaultValues: initialValues || { name: '', stores: [] },
});

// @ts-ignore TODO: fix
if (!session?.token.id_token) {
return null;
}
const onSubmit: SubmitHandler<Inputs> = (data) => {
mutate(
{
// @ts-ignore TODO: fix
token: session?.token.id_token,
data: { name: data.name, stores: data.stores },
},
{ onSuccess: () => router.push('/products') }
);
if (initialValues?.id) {
mutateEdit(
{
// @ts-ignore TODO: fix
token: session?.token.id_token,
data: { name: data.name, stores: data.stores, id: initialValues.id },
},
{ onSuccess: () => router.push('/products') }
);
} else {
mutateAdd(
{
// @ts-ignore TODO: fix
token: session?.token.id_token,
data: { name: data.name, stores: data.stores },
},
{ onSuccess: () => router.push('/products') }
);
}
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<Input
isRequired
label='Name'
className='max-w-xs'
{...register('name', { required: true })}
<ControlledInput
name='name'
control={control}
rules={{ required: true }}
/>

{errors.name && <span>This field is required</span>}
Expand Down
35 changes: 33 additions & 2 deletions fetch-queries/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,37 @@ export async function addProduct({

throw new Error(data.message || 'An error occurred');
}
const resData = await response.json();
return resData;
return await response.json();
}

export async function editProduct({
data,
token,
}: {
data: { name: string; stores: string[]; id: string };
token: string;
}) {
const { id, ...body } = data;
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/products/${id}`,
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: token,
},
body: JSON.stringify(body),
}
);

if (!response.ok) {
const data: { message: string } = await response.json();

if (response.status === 400) {
throw new Error(data.message);
}

throw new Error(data.message || 'An error occurred');
}
return await response.json();
}
2 changes: 1 addition & 1 deletion fetch-queries/products/get-product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface Response {

export async function getProduct({ id }: { id: string }) {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/product/${id}`,
`${process.env.NEXT_PUBLIC_BACKEND_URL}/products/${id}`,
{
method: 'GET',
}
Expand Down

0 comments on commit 01afc2e

Please sign in to comment.