387 lines
16 KiB
JavaScript
387 lines
16 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 GenerateUrl from "../../Utils/GenerateUrl";
|
|
import useSWR, { mutate } from "swr";
|
|
import { fetcher } from "../../Utils/Fetcher";
|
|
import NoDataTable from "../../Components/NoDataTable";
|
|
import { debounce } from "../../Utils/Debounce";
|
|
import CustomModal from "../../Components/CustomModal";
|
|
import { IoEye, IoEyeOff } from "react-icons/io5";
|
|
import Swal from "sweetalert2";
|
|
import HitApi from "../../Utils/HitApi";
|
|
import DeleteData from "../../Utils/DeleteData";
|
|
|
|
const User = (props) => {
|
|
const [page, setPage] = useState(1);
|
|
const [search, setSearch] = useState("");
|
|
const URL = GenerateUrl(
|
|
"/api/v1/user",
|
|
`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: "",
|
|
email: "",
|
|
password: "",
|
|
roles: "admin",
|
|
category: "",
|
|
option: "tambah",
|
|
});
|
|
|
|
const clearForm = () => {
|
|
setForm({
|
|
name: "",
|
|
email: "",
|
|
password: "",
|
|
roles: "admin",
|
|
category: "",
|
|
option: "tambah",
|
|
});
|
|
};
|
|
|
|
const myState = {
|
|
showModal,
|
|
setShowModal,
|
|
URL,
|
|
props,
|
|
form,
|
|
setForm,
|
|
clearForm,
|
|
};
|
|
|
|
return (
|
|
<AdminLayout title="Users">
|
|
<Modal state={myState} />
|
|
<div className="w-full h-full flex flex-col px-3 py-4">
|
|
<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-[300px]">
|
|
<input
|
|
onChange={handleChangeSearch}
|
|
type="text"
|
|
className="w-full"
|
|
placeholder="Search"
|
|
/>
|
|
<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>Roles</th>
|
|
<th>Category</th>
|
|
<th>Created At</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>
|
|
<div>
|
|
<h1 className="font-semibold">
|
|
{item.name}
|
|
</h1>
|
|
<p className="text-xs">
|
|
{item.email}
|
|
</p>
|
|
</div>
|
|
</td>
|
|
<td>{item.role}</td>
|
|
<td>
|
|
{item.kategori?.nama ?? "-"}
|
|
</td>
|
|
<td>
|
|
{new Date(
|
|
item.created_at
|
|
).toLocaleString()}
|
|
</td>
|
|
<td>
|
|
<div className="flex gap-2 items-center">
|
|
{/* button edit */}
|
|
<button
|
|
onClick={() => {
|
|
setForm({
|
|
id: item.id,
|
|
name: item.name,
|
|
email: item.email,
|
|
password:
|
|
"",
|
|
roles: item.role,
|
|
category:
|
|
item
|
|
.kategori
|
|
?.id ??
|
|
"",
|
|
option: "edit",
|
|
});
|
|
setShowModal(
|
|
true
|
|
);
|
|
}}
|
|
className="btn btn-sm btn-accent"
|
|
>
|
|
<FaEdit size={15} />
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
DeleteData(
|
|
`/api/v1/user?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 [showPassword, setShowPassword] = useState(false);
|
|
|
|
const { form, setForm, clearForm } = 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/user",
|
|
method: form.option == "tambah" ? "POST" : "PUT",
|
|
body: form,
|
|
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={"Users"}
|
|
>
|
|
<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">Email</span>
|
|
</div>
|
|
<input
|
|
value={form.email}
|
|
onChange={handleChange}
|
|
name="email"
|
|
type="text"
|
|
placeholder=""
|
|
className="input input-bordered w-full"
|
|
/>
|
|
</label>
|
|
|
|
<label className="form-control w-full">
|
|
<div className="label">
|
|
<span className="label-text">Password</span>
|
|
</div>
|
|
<div className="flex items-center relative">
|
|
<input
|
|
type={showPassword ? "text" : "password"}
|
|
placeholder=""
|
|
value={form.password}
|
|
onChange={handleChange}
|
|
name="password"
|
|
className="input input-bordered w-full pr-12"
|
|
/>
|
|
<div
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
className="absolute right-3 px-2 h-full items-center flex"
|
|
>
|
|
{showPassword ? <IoEye /> : <IoEyeOff />}
|
|
</div>
|
|
</div>
|
|
</label>
|
|
|
|
<label className="form-control w-full">
|
|
<div className="label">
|
|
<span className="label-text">Roles</span>
|
|
</div>
|
|
<select
|
|
onChange={handleChange}
|
|
value={form.roles}
|
|
name="roles"
|
|
className="select select-bordered"
|
|
>
|
|
<option value="admin">Admin</option>
|
|
<option value="user">User</option>
|
|
</select>
|
|
</label>
|
|
|
|
{form.roles == "user" && (
|
|
<label className="form-control w-full">
|
|
<div className="label">
|
|
<span className="label-text">Category</span>
|
|
</div>
|
|
<select
|
|
value={form.category}
|
|
onChange={handleChange}
|
|
name="category"
|
|
className="select select-bordered"
|
|
>
|
|
<option disabled value=""></option>
|
|
{state.props.kategori.map((item, index) => (
|
|
<option key={index} value={item.id}>
|
|
{item.nama}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
)}
|
|
<button className="btn btn-primary mb-2 mt-2">Save</button>
|
|
</form>
|
|
</CustomModal>
|
|
);
|
|
};
|
|
|
|
export default User;
|