314 lines
13 KiB
JavaScript
314 lines
13 KiB
JavaScript
import React, { useState } from "react";
|
|
import AdminLayout from "./Layout/AdminLayout";
|
|
import { FaPlus, FaTrash } from "react-icons/fa6";
|
|
import Pagination from "../../Components/Pagination";
|
|
import { FaEdit } from "react-icons/fa";
|
|
import { IoBookOutline } from "react-icons/io5";
|
|
import { Link } from "@inertiajs/react";
|
|
import GenerateUrl from "../../Utils/GenerateUrl";
|
|
import useSWR, { mutate } from "swr";
|
|
import { fetcher } from "../../Utils/Fetcher";
|
|
import { debounce } from "../../Utils/Debounce";
|
|
import NoDataTable from "../../Components/NoDataTable";
|
|
import Swal from "sweetalert2";
|
|
import HitApi from "../../Utils/HitApi";
|
|
import CustomModal from "../../Components/CustomModal";
|
|
import DeleteData from "../../Utils/DeleteData";
|
|
|
|
const ModuleDetail = (props) => {
|
|
const [page, setPage] = useState(1);
|
|
const [search, setSearch] = useState("");
|
|
const URL = GenerateUrl(
|
|
"/api/v1/module",
|
|
`id=${props.id}`,
|
|
`page=${page}`,
|
|
`search=${encodeURIComponent(search)}`
|
|
);
|
|
const { data, error, isLoading } = useSWR(URL, fetcher);
|
|
|
|
const handleSearch = debounce((term) => {
|
|
setSearch(term);
|
|
}, 500);
|
|
|
|
const handleChangeSearch = (e) => {
|
|
const { value } = e.target;
|
|
setPage(1);
|
|
handleSearch(value);
|
|
};
|
|
|
|
const [showModal, setShowModal] = useState(false);
|
|
|
|
const [form, setForm] = useState({
|
|
name: "",
|
|
description: "",
|
|
option: "tambah",
|
|
});
|
|
|
|
const clearForm = () => {
|
|
setForm({
|
|
name: "",
|
|
description: "",
|
|
option: "tambah",
|
|
});
|
|
};
|
|
|
|
const myState = {
|
|
showModal,
|
|
setShowModal,
|
|
URL,
|
|
form,
|
|
setForm,
|
|
clearForm,
|
|
props,
|
|
};
|
|
|
|
return (
|
|
<AdminLayout title="Materi">
|
|
<Modal state={myState} />
|
|
<div className="w-full h-full flex flex-col px-3 py-1">
|
|
<div className="text-xs breadcrumbs mb-2">
|
|
<ul>
|
|
<li>
|
|
<Link href="/admin/materi">SD</Link>
|
|
</li>
|
|
<li>
|
|
<a>Materi</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div className="flex items-center w-full gap-4 mb-3">
|
|
<label className="input input-bordered flex w-full items-center gap-2 max-w-[200px] md:max-w-[400px]">
|
|
<input
|
|
type="text"
|
|
className="w-full"
|
|
placeholder="Search"
|
|
onChange={handleChangeSearch}
|
|
/>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 16 16"
|
|
fill="currentColor"
|
|
className="w-4 h-4 opacity-70"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M9.965 11.026a5 5 0 1 1 1.06-1.06l2.755 2.754a.75.75 0 1 1-1.06 1.06l-2.755-2.754ZM10.5 7a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
</label>
|
|
<div className="flex-1 justify-end flex w-full">
|
|
<button
|
|
onClick={() => {
|
|
clearForm();
|
|
setShowModal(true);
|
|
}}
|
|
className="btn btn-primary w-fit"
|
|
>
|
|
<FaPlus />
|
|
<p className="hidden md:flex">Tambah</p>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<NoDataTable
|
|
isLoading={isLoading}
|
|
isError={error}
|
|
message={error ? "Failed get data" : "No data"}
|
|
isEmpty={
|
|
data && data.result.data.length == 0 ? true : false
|
|
}
|
|
>
|
|
{data && (
|
|
<>
|
|
<div className="overflow-x-auto">
|
|
<table className="table">
|
|
{/* head */}
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>Name</th>
|
|
<th>Deskripsi</th>
|
|
<th>Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{data.result.data.map((item, index) => (
|
|
<tr
|
|
key={index}
|
|
className="hover border-t-2"
|
|
>
|
|
<th>
|
|
{data.result.from + index}
|
|
</th>
|
|
<td>{item.name}</td>
|
|
<td>{item.description}</td>
|
|
<td>
|
|
<div className="flex gap-2 items-center">
|
|
<Link
|
|
href={`/admin/materi/${item.id}`}
|
|
>
|
|
<button className="btn btn-sm btn-warning">
|
|
<IoBookOutline
|
|
size={15}
|
|
/>
|
|
</button>
|
|
</Link>
|
|
<button
|
|
onClick={() => {
|
|
clearForm();
|
|
setForm({
|
|
id: item.id,
|
|
name: item.name,
|
|
description:
|
|
item.description,
|
|
option: "edit",
|
|
});
|
|
setShowModal(
|
|
true
|
|
);
|
|
}}
|
|
className="btn btn-sm btn-accent"
|
|
>
|
|
<FaEdit size={15} />
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
DeleteData(
|
|
`/api/v1/module?id=${item.id}`,
|
|
() => {
|
|
mutate(
|
|
URL
|
|
);
|
|
}
|
|
);
|
|
}}
|
|
className="btn btn-sm btn-error"
|
|
>
|
|
<FaTrash
|
|
size={15}
|
|
/>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<Pagination
|
|
total={data.result.total}
|
|
showItem={data.result.data.length}
|
|
page={page}
|
|
setPage={setPage}
|
|
limit={data.result.per_page}
|
|
/>
|
|
</>
|
|
)}
|
|
</NoDataTable>
|
|
</div>
|
|
</AdminLayout>
|
|
);
|
|
};
|
|
|
|
const Modal = ({ state }) => {
|
|
const { form, setForm, clearForm, props } = state;
|
|
|
|
// handle change form
|
|
const handleChange = (e) => {
|
|
const { name, value } = e.target;
|
|
setForm((prev) => ({ ...prev, [name]: value }));
|
|
};
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
|
|
Swal.fire({
|
|
title: "Konfirmasi",
|
|
text:
|
|
form.option == "tambah"
|
|
? "Apakah anda yakin ingin menambahkan data?"
|
|
: "Apakah anda yakin ingin mengubah data?",
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonColor: "#3085d6",
|
|
cancelButtonColor: "#d33",
|
|
confirmButtonText: "Ya",
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
title: "Loading",
|
|
html: '<div class="body-loading"><div class="loadingspinner"></div></div>', // add html attribute if you want or remove
|
|
allowOutsideClick: false,
|
|
showConfirmButton: false,
|
|
});
|
|
|
|
HitApi({
|
|
url: "/api/v1/module",
|
|
method: form.option == "tambah" ? "POST" : "PUT",
|
|
body: { ...form, id_category: props.id },
|
|
onSuccess: () => {
|
|
Swal.fire(
|
|
"Berhasil",
|
|
form.option == "tambah"
|
|
? "Data berhasil ditambahkan"
|
|
: "Data berhasil diubah",
|
|
"success"
|
|
);
|
|
|
|
clearForm();
|
|
|
|
state.setShowModal(false);
|
|
mutate(state.URL);
|
|
},
|
|
onError: () => {
|
|
// Swal.fire("Gagal", "Data gagal ditambahkan", "error");
|
|
},
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<CustomModal
|
|
show={state.showModal}
|
|
setShow={state.setShowModal}
|
|
title={"Module"}
|
|
>
|
|
<form
|
|
onSubmit={handleSubmit}
|
|
className="w-full flex flex-col gap-2"
|
|
>
|
|
<label className="form-control w-full">
|
|
<div className="label">
|
|
<span className="label-text">Nama</span>
|
|
</div>
|
|
<input
|
|
value={form.name}
|
|
onChange={handleChange}
|
|
name="name"
|
|
type="text"
|
|
placeholder=""
|
|
className="input input-bordered w-full"
|
|
/>
|
|
</label>
|
|
<label className="form-control w-full">
|
|
<div className="label">
|
|
<span className="label-text">Deskripsi</span>
|
|
</div>
|
|
<textarea
|
|
className="textarea textarea-bordered h-24"
|
|
placeholder=""
|
|
name="description"
|
|
value={form.description}
|
|
onChange={handleChange}
|
|
></textarea>
|
|
</label>
|
|
|
|
<button className="btn btn-primary mb-2 mt-2">Save</button>
|
|
</form>
|
|
</CustomModal>
|
|
);
|
|
};
|
|
|
|
export default ModuleDetail;
|