BPMN Part 1
BPMN: https://alurkerja-starter.alurkerja.com/camunda/app/cockpit/default/#/processes
Endpoint:
- POST
https://alurkerja-starter.alurkerja.com/bpmn/Cuti - GET
https://alurkerja-starter.alurkerja.com/bpmn/Cuti/{id}
Swagger: https://alurkerja-starter.alurkerja.com/swagger-ui/index.html#/cuti-controller
Form Pengajuan Cuti
Kita bisa mulai dengan membuat hook useCutiCreate pada berkas src/api/cuti.ts.
export type CutiPayload = Omit<CutiDTO, "id">;
export function useCutiCreate() {
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState<unknown>();
async function mutate(values: CutiPayload) {
setIsPending(true);
try {
await axiosInstance.post("/bpmn/Cuti", values);
} catch (error) {
console.error(error);
setError(error);
}
setIsPending(false);
}
return { mutate, isPending, error };
}
Kemudian, kita membuat komponen formulir yang akan memakai hook ini di src/feature/CutiCreate.tsx.
import { Button, Input } from "alurkerja-ui";
import { useForm } from "react-hook-form";
import { useCutiCreate } from "../api/cuti";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Link } from "react-router-dom";
const cutiCreateFormSchema = z.object({
name: z.string().min(1, { message: "Nama wajib diisi" }),
reason: z.string().min(1, { message: "Alasan wajib diisi" }),
});
type CutiCreateFormSchema = z.infer<typeof cutiCreateFormSchema>;
export function CutiCreate() {
const { mutate, isPending, error } = useCutiCreate();
const { register, handleSubmit, formState } = useForm<CutiCreateFormSchema>({
resolver: zodResolver(cutiCreateFormSchema),
});
const nameError = formState.errors["name"];
const reasonError = formState.errors["reason"];
function handleCreateCuti(values: CutiCreateFormSchema) {
mutate(values);
}
return (
<form
onSubmit={handleSubmit(handleCreateCuti)}
className="flex flex-col gap-y-4"
>
<div>
<label htmlFor="name">Nama</label>
<Input id="name" {...register("name")} />
{nameError && <p>{nameError.message}</p>}
</div>
<div>
<label htmlFor="reason">Alasan</label>
<Input id="reason" {...register("reason")} />
{reasonError && <p>{reasonError.message}</p>}
</div>
<div>
<Link to=".." className="w-fit">
Kembali
</Link>
<Button loading={isPending}>Kirim</Button>
</div>
{Boolean(error) && <p>Error</p>}
</form>
);
}
Langkah yang mirip juga bisa kita terapkan untuk membuat src/feature/CutiDetail.tsx dan hook useCutiDetail pada src/api/cuti.ts.
src/api/cuti.ts:
export function useCutiDetail(
id?: string,
options: { enabled?: boolean } = {
enabled: true,
}
) {
const [data, setData] = useState<CutiDTO>();
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState<unknown>();
useEffect(() => {
async function getCutiDetail() {
setIsPending(true);
try {
const response = await axiosInstance.get(`/bpmn/Cuti/${id}`);
if (axios.isAxiosError(response)) {
setError(response);
} else {
console.log(response.data);
setData(response.data?.data?.object);
}
} catch (error) {
console.error(error);
setError(error);
}
setIsPending(false);
}
if (options.enabled) {
getCutiDetail();
}
}, [id, options.enabled]);
return { data, isPending, error };
}
src/feature/CutiDetail.tsx:
import { useForm } from "react-hook-form";
import { useCutiDetail } from "../api/cuti";
import { Link, useParams } from "react-router-dom";
import { Input } from "alurkerja-ui";
export function CutiDetail() {
const { id } = useParams();
const { data, isPending, error } = useCutiDetail(id, {
enabled: Boolean(id),
});
const { register } = useForm({
values: data,
});
if (isPending) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error</div>;
}
return (
<div className="flex flex-col gap-y-4">
<div>
<label htmlFor="name">Nama</label>
<Input id="name" {...register("name")} disabled />
</div>
<div>
<label htmlFor="reason">Alasan</label>
<Input id="reason" {...register("reason")} disabled />
</div>
<Link to=".." className="w-fit">
Kembali
</Link>
</div>
);
}
Jangan lupa untuk menambahkan rute pada src/routes.tsx agar CutiDetail bisa diakses.
...
import { CutiCreate } from "./feature/CutiCreate";
import { CutiDetail } from "./feature/CutiDetail";
export const routes: RouteObject[] = [
{
path: "/",
element: <Layout />,
errorElement: <div>404</div>,
children: [
{
index: true,
element: <div>Selamat Datang</div>,
},
{
path: "cuti",
children: [
{
index: true,
element: (
<RoleGate allowed="Pegawai">
<CutiTable />
</RoleGate>
),
},
{
path: "create",
element: (
<RoleGate allowed="Pegawai">
<CutiCreate />
</RoleGate>
),
},
{
path: ":id",
element: (
<RoleGate allowed="Pegawai">
<CutiDetail />
</RoleGate>
),
},
],
},
...
],
},
];