D. Integrasi Detail dan Update Book
1. Integrasi Detail Book
Membuat Routing Halaman Update
app/book/page.tsx
"use client";
import Button from "@/components/Button";
import { Pagination } from "../../components/Pagination";
import { Table, Th, Thead, Tr, Tbody, Td } from "../../components/Table";
import useBookModule from "./lib";
import { Drawer } from "@/components/Drawer";
import Filter from "./module/Filter";
import { useDisclosure } from "@/hook";
import { useRouter } from "next/navigation";
import { TrashIcon, PencilSquareIcon } from "@heroicons/react/20/solid";
import { DeleteButton, EditButton } from "@/components/ButtonAction";
const Book = () => {
const { useBookList } = useBookModule();
const router = useRouter();
const {
data,
isFetching,
isError,
params,
setParams,
handleFilter,
handleClear,
handlePageSize,
handlePage,
} = useBookList();
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<Drawer
onClose={onClose}
onClear={handleClear}
onSubmit={handleFilter}
title="Filter Buku"
isOpen={isOpen}
>
<Filter params={params} setParams={setParams} />
</Drawer>
<section className=" p-10 overflow-auto ">
<section className="flex items-center justify-between ">
<Button
width="sm"
onClick={onOpen}
colorSchema="blue"
title="Filter"
/>
<Button
onClick={() => {
router.push("/book/tambah");
}}
width="sm"
colorSchema="red"
title="tambah"
/>
</section>
<section className="h-full w-full mt-5 ">
<Table
isFetching={isFetching}
isEmpty={data?.data?.length === 0}
isError={isError}
>
<Thead>
<Tr>
<Th scope="col">
<div className="flex items-center gap-x-3">
<input
type="checkbox"
className="text-blue-500 border-gray-300 rounded dark:bg-gray-900 dark:ring-offset-gray-900 dark:border-gray-700"
/>
</div>
</Th>
<Th scope="col">No</Th>
<Th scope="col">Title</Th>
<Th scope="col">Author</Th>
<Th scope="col">Year</Th>
<Th scope="col">Created At</Th>
<Th scope="col">Updated At</Th>
<Th scope="col">Aksi</Th>
</Tr>
</Thead>
<Tbody>
{data &&
data.data.map((item, index) => (
<Tr key={index}>
<Td>
<input
type="checkbox"
className="text-blue-500 border-gray-300 rounded dark:bg-gray-900 dark:ring-offset-gray-900 dark:border-gray-700"
/>
</Td>
<Td>{(params.page - 1) * params.pageSize + index + 1}</Td>
<Td>
<span>{item.title}</span>
</Td>
<Td>
<span>{item.author}</span>
</Td>
<Td>
<span>{item.year}</span>
</Td>
<Td>
<span>{item.created_at}</span>
</Td>
<Td>
<span>{item.updated_at}</span>
</Td>
<Td>
<DeleteButton
onClick={() => {
console.log("ok");
}}
/>
<EditButton
onClick={() => {
router.push(`book/${item.id}/edit`)
}}
/>
</Td>
</Tr>
))}
</Tbody>
</Table>
<Pagination
page={params.page}
pageSize={params.pageSize}
handlePageSize={handlePageSize}
handlePage={handlePage}
pagination={data?.pagination}
/>
</section>
</section>
</>
);
};
export default Book;
Membuat Halaman Update
app/book/[id]/update/page.tsx
"use client";
import Button from "@/components/Button";
import InputText from "@/components/InputText";
import Label from "@/components/Label";
import Select from "@/components/Select";
import { useFormik, Form, FormikProvider } from "formik";
import * as yup from "yup";
import { BookUpdatePayload } from "../../interface";
import useBookModule from "../../lib";
import Link from "next/link";
import { ArrowLongLeftIcon } from "@heroicons/react/20/solid";
import { option } from "../../tambah/page";
const createBookSchema = yup.object().shape({
title: yup.string().nullable().default("").required("Wajib isi"),
author: yup.string().nullable().default("").required("Wajib isi"),
year: yup.number().nullable().default(undefined).required("Wajib pilih"),
});
const UpdateBook = ({ params }: { params: { id: string } }) => {
const formik = useFormik<BookUpdatePayload>({
initialValues: {
title: "",
year: "",
author: "",
id: 0,
},
validationSchema: createBookSchema,
enableReinitialize: true,
onSubmit: ()=> console.log('update'),
});
const {
handleChange,
handleSubmit,
setFieldValue,
handleBlur,
values,
errors,
resetForm,
setValues,
} = formik;
return (
<section className="flex items-center justify-center w-full h-full">
<section className="w-1/2">
<Link href={"/book"}>
<span className="flex items-center">
{" "}
<ArrowLongLeftIcon className="h-5 w-5 mr-2" />
Kembali
</span>
</Link>
<h2 className="text-xl font-bold text-gray-500">Perbaharui Buku</h2>
<FormikProvider value={formik}>
<Form className="space-y-5" onSubmit={handleSubmit}>
<section>
<Label htmlFor="title" title="Title" />
<InputText
value={values.title}
placeholder="Judul Buku"
id="title"
name="title"
onChange={handleChange}
onBlur={handleBlur}
isError={!!errors.title}
messageError={errors.title}
/>
</section>
<section>
<Label htmlFor="author" title="Auhtor" />
<InputText
value={values.author}
placeholder="Penulis Buku"
id="author"
name="author"
onChange={handleChange}
onBlur={handleBlur}
isError={!!errors.author}
messageError={errors.author}
/>
</section>
<section>
<Label htmlFor="year" title="Year" />
<Select
value={values.year}
id="year"
name="year"
onChange={handleChange}
onBlur={handleBlur}
options={option}
isError={!!errors.year}
messageError={errors.year}
/>
</section>
<section>
<Button
height="md"
title="Perbarui"
colorSchema="blue"
/>
</section>
</Form>
</FormikProvider>
</section>
</section>
);
};
export default UpdateBook;
Membuat interface Detail Book
response endpoint detail
{
"status": "Success",
"message": "OK",
"data": {
"id": 1,
"title": "nestjs testing update 1",
"author": "ihsan santana",
"year": 2021,
"created_at": "2023-09-22T06:05:48.000Z",
"updated_at": "2023-09-22T06:05:48.000Z"
}
}
app/book/interface/index.ts
import {
BaseResponsePagination,
} from "@/lib/axiosClient";
interface Book {
id: number | undefined;
title: string;
author: string;
year: number | undefined | string;
created_at: string;
updated_at: string;
}
export interface BookListResponse extends BaseResponsePagination {
data: Book[];
}
export interface BookListFilter extends Partial<Book> {
from_year?: string;
to_year?: string;
page : number ,
pageSize : number
}
export interface BookCreatePayload extends Pick<Book, "author" | "title" | "year"> {}
export interface BookDetail extends Book{}
Membuat Service Detail Book
app/book/lib/index.ts
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { axiosClient } from "@/lib/axiosClient";
import Swal from "sweetalert2";
import {
BookCreatePayload,
BookDetail,
BookListFilter,
BookListResponse,
} from "../interface";
import { usePagination } from "@/hook/usePagination";
const useBookModule = () => {
const queryClient = useQueryClient();
const defaultParams: BookListFilter = {
title: "",
author: "",
from_year: "",
to_year: "",
page: 1,
pageSize: 10,
};
....
const getDetailBook = async (
id:string
): Promise<BookDetail> => {
return axiosClient.get(`/book/detail/${id}`).then((res) => res.data.data);
};
const useDetailBook = (id:string) => {
const { data, isLoading, isFetching } = useQuery(
["/book/detail", { id }],
() => getDetailBook(id),
{
select: (response) => response,
}
);
return { data, isFetching, isLoading };
}
return { useBookList, useCreateBook, useDetailBook };
};
export default useBookModule;
Memanggil Service Detail di Halaman update
book/[id]/update/page.tsx
"use client";
import Button from "@/components/Button";
import InputText from "@/components/InputText";
import Label from "@/components/Label";
import Select from "@/components/Select";
import { useFormik, Form, FormikProvider } from "formik";
import * as yup from "yup";
import { BookUpdatePayload } from "../../interface";
import useBookModule from "../../lib";
import Link from "next/link";
import { ArrowLongLeftIcon } from "@heroicons/react/20/solid";
import { option } from "../../tambah/page";
const createBookSchema = yup.object().shape({
title: yup.string().nullable().default("").required("Wajib isi"),
author: yup.string().nullable().default("").required("Wajib isi"),
year: yup.number().nullable().default(undefined).required("Wajib pilih"),
});
const UpdateBook = ({ params }: { params: { id: string } }) => {
const { useDetailBook, useUpdateBook } = useBookModule();
const { mutate, isLoading } = useUpdateBook(params.id);
const { data, isFetching } = useDetailBook(params.id);
const formik = useFormik<BookUpdatePayload>({
initialValues: {
title: data?.title || "",
year: data?.year,
author: data?.author || "",
id: data?.id,
},
validationSchema: createBookSchema,
enableReinitialize: true,
onSubmit: ()=> console.log('update'),
});
const {
handleChange,
handleSubmit,
setFieldValue,
handleBlur,
values,
errors,
resetForm,
setValues,
} = formik;
if(isFetching) {
return (
<p>Loading</p>
)
}
return (
<section className="flex items-center justify-center w-full h-full">
<section className="w-1/2">
<Link href={"/book"}>
<span className="flex items-center">
{" "}
<ArrowLongLeftIcon className="h-5 w-5 mr-2" />
Kembali
</span>
</Link>
<h2 className="text-xl font-bold text-gray-500">Perbaharui Buku</h2>
<FormikProvider value={formik}>
<Form className="space-y-5" onSubmit={handleSubmit}>
<section>
<Label htmlFor="title" title="Title" />
<InputText
value={values.title}
placeholder="Judul Buku"
id="title"
name="title"
onChange={handleChange}
onBlur={handleBlur}
isError={!!errors.title}
messageError={errors.title}
/>
</section>
<section>
<Label htmlFor="author" title="Auhtor" />
<InputText
value={values.author}
placeholder="Penulis Buku"
id="author"
name="author"
onChange={handleChange}
onBlur={handleBlur}
isError={!!errors.author}
messageError={errors.author}
/>
</section>
<section>
<Label htmlFor="year" title="Year" />
<Select
value={values.year}
id="year"
name="year"
onChange={handleChange}
onBlur={handleBlur}
options={option}
isError={!!errors.year}
messageError={errors.year}
/>
</section>
<section>
<Button
height="md"
title="Perbarui"
colorSchema="blue"
isLoading={isLoading}
isDisabled={isLoading}
/>
</section>
</Form>
</FormikProvider>
</section>
</section>
);
};
export default UpdateBook;
2. Integrasi Update Book
Membuat interface update book
response endpoint update
{
"status": "Success ",
"message": "Buku berhasil di update",
"data": {
"title": "NestJS Up",
"author": "Ihsan Update",
"year": 2023,
"id": 1
}
}
book/interface/index.ts
import {
BaseResponsePagination,
} from "@/lib/axiosClient";
interface Book {
id: number | undefined;
title: string;
author: string;
year: number | undefined | string;
created_at: string;
updated_at: string;
}
export interface BookListResponse extends BaseResponsePagination {
data: Book[];
}
export interface BookListFilter extends Partial<Book> {
from_year?: string;
to_year?: string;
page : number ,
pageSize : number
}
export interface BookCreatePayload extends Pick<Book, "author" | "title" | "year"> {}
export interface BookUpdatePayload extends Pick<Book, "author" | "title" | "year" | "id"> {}
export interface BookDetail extends Book{}
Membuat Service Update Book
book/lib/index.ts
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { axiosClient } from "@/lib/axiosClient";
import Swal from "sweetalert2";
import {
BookCreatePayload,
BookDetail,
BookListFilter,
BookListResponse,
BookUpdatePayload,
} from "../interface";
import { usePagination } from "@/hook/usePagination";
const useBookModule = () => {
const queryClient = useQueryClient();
const defaultParams: BookListFilter = {
title: "",
author: "",
from_year: "",
to_year: "",
page: 1,
pageSize: 10,
};
...
const useUpdateBook = (id:string) => {
const { mutate, isLoading } = useMutation(
(payload: BookUpdatePayload) => {
return axiosClient.put(`/book/update/${id}`, payload);
},
{
onSuccess: (response) => {
Swal.fire({
position: "top-end",
icon: "success",
title: response.data.message,
showConfirmButton: false,
timer: 1000,
});
queryClient.invalidateQueries(["/book/detail"]);
},
onError: (error) => {
alert("ok");
},
}
);
return { mutate, isLoading };
};
return { useBookList, useCreateBook, useDetailBook, useUpdateBook };
};
export default useBookModule;
Memanggil Service Update Book di Halaman update
book/[id]/update/page.tsx
"use client";
import Button from "@/components/Button";
import InputText from "@/components/InputText";
import Label from "@/components/Label";
import Select from "@/components/Select";
import { useFormik, Form, FormikProvider } from "formik";
import * as yup from "yup";
import { BookUpdatePayload } from "../../interface";
import useBookModule from "../../lib";
import Link from "next/link";
import { ArrowLongLeftIcon } from "@heroicons/react/20/solid";
import { option } from "../../tambah/page";
const createBookSchema = yup.object().shape({
title: yup.string().nullable().default("").required("Wajib isi"),
author: yup.string().nullable().default("").required("Wajib isi"),
year: yup.number().nullable().default(undefined).required("Wajib pilih"),
});
const UpdateBook = ({ params }: { params: { id: string } }) => {
const { useDetailBook, useUpdateBook } = useBookModule();
const { mutate, isLoading } = useUpdateBook(params.id);
const { data, isFetching } = useDetailBook(params.id);
const onSubmit = async (values: BookUpdatePayload) => {
mutate(values);
};
const formik = useFormik<BookUpdatePayload>({
initialValues: {
title: data?.title || "",
year: data?.year,
author: data?.author || "",
id: data?.id,
},
validationSchema: createBookSchema,
enableReinitialize: true,
onSubmit: onSubmit,
});
const {
handleChange,
handleSubmit,
setFieldValue,
handleBlur,
values,
errors,
resetForm,
setValues,
} = formik;
if(isFetching) {
return (
<p>Loading</p>
)
}
return (
<section className="flex items-center justify-center w-full h-full">
<section className="w-1/2">
<Link href={"/book"}>
<span className="flex items-center">
{" "}
<ArrowLongLeftIcon className="h-5 w-5 mr-2" />
Kembali
</span>
</Link>
<h2 className="text-xl font-bold text-gray-500">Perbaharui Buku</h2>
<FormikProvider value={formik}>
<Form className="space-y-5" onSubmit={handleSubmit}>
<section>
<Label htmlFor="title" title="Title" />
<InputText
value={values.title}
placeholder="Judul Buku"
id="title"
name="title"
onChange={handleChange}
onBlur={handleBlur}
isError={!!errors.title}
messageError={errors.title}
/>
</section>
<section>
<Label htmlFor="author" title="Auhtor" />
<InputText
value={values.author}
placeholder="Penulis Buku"
id="author"
name="author"
onChange={handleChange}
onBlur={handleBlur}
isError={!!errors.author}
messageError={errors.author}
/>
</section>
<section>
<Label htmlFor="year" title="Year" />
<Select
value={values.year}
id="year"
name="year"
onChange={handleChange}
onBlur={handleBlur}
options={option}
isError={!!errors.year}
messageError={errors.year}
/>
</section>
<section>
<Button
height="md"
title="Perbarui"
colorSchema="blue"
isLoading={isLoading}
isDisabled={isLoading}
/>
</section>
</Form>
</FormikProvider>
</section>
</section>
);
};
export default UpdateBook;