Logo Universitas

Universitas ASA Indonesia

Program Studi: Teknologi Informasi

Mata Kuliah: Pengembangan Aplikasi Web Lanjut

SKS: 3 (2 teori, 1 praktikum)

Dosen Pengampu: Istiqomah Sumadikarta, S.T., M.Kom.

Beranda Mundur Maju

Pertemuan 9: Implementasi Halaman Admin #3

Konfigurasi Private Route Sliders Index


Pada materi kali ini kita akan belajar membuat konfigurasi private route untuk menampilkan halaman sliders index. Sebelum itu kita akan membuat view/component-nya terlebih dahulu.

Langkah 1 - Membuat View/Component Sliders Index

Sekarang kita akan membuat view/component untuk data sliders index terlebih dahulu. Silahkan buat folder baru dengan nama sliders di dalam folder src/pages/admin/. Setelah itu silahkan buat file baru dengan nama Index.jsx di dalam folder sliders tersebut dan masukkan kode berikut ini :

//import react  
import React from "react";

//import layout admin
import LayoutAdmin from "../../../layouts/Admin";

function SlidersIndex() {

    return(
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-images"></i> SLIDERS</span>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    )

}

export default SlidersIndex

Di atas, kita memberikan sample kode untuk halaman sliders index, dimateri selanjutnya kita akan ubah sesuai dengan data yang akan kita tampilkan.

Langkah 2 - Konfigurasi Private Route Sliders Index

Setelah berhasil membuat view/component untuk halaman sliders index, kita lanjutkan untuk membuat konfigurasi private route-nya. Silahkan buka file src/routes/routes.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react router dom
import { Routes, Route } from "react-router-dom";

//=======================================================================
//ADMIN
//=======================================================================

//import view Login
import Login from '../pages/admin/Login.jsx';

//import component private routes
import PrivateRoute from "./PrivateRoutes";

//import view admin Dashboard
import Dashboard from '../pages/admin/dashboard/Index.jsx';

//import view admin categories Index
import CategoriesIndex from '../pages/admin/categories/Index.jsx';

//import view admin category Create
import CategoryCreate from '../pages/admin/categories/Create.jsx';

//import view admin category Edit
import CategoryEdit from '../pages/admin/categories/Edit.jsx';

//import view admin places Index
import PlacesIndex from '../pages/admin/places/Index.jsx';

//import view admin places Create
import PlaceCreate from '../pages/admin/places/Create.jsx';

//import view admin places Edit
import PlaceEdit from '../pages/admin/places/Edit.jsx';

//import view admin sliders Index
import SlidersIndex from '../pages/admin/sliders/Index.jsx';

function RoutesIndex() {
    return (
        <Routes>

            {/* route "/admin/login" */}
            <Route path="/admin/login" element={<Login />} />

            {/* private route "/admin/dashboard" */}
            <Route
                path="/admin/dashboard"
                element={
                        <PrivateRoute>
                            <Dashboard />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories" */}
            <Route
                path="/admin/categories"
                element={
                        <PrivateRoute>
                            <CategoriesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/create" */}
            <Route
                path="/admin/categories/create"
                element={
                        <PrivateRoute>
                            <CategoryCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/edit/:id" */}
            <Route
                path="/admin/categories/edit/:id"
                element={
                        <PrivateRoute>
                            <CategoryEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places" */}
            <Route
                path="/admin/places"
                element={
                        <PrivateRoute>
                            <PlacesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/create" */}
            <Route
                path="/admin/places/create"
                element={
                        <PrivateRoute>
                            <PlaceCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/edit/:id" */}
            <Route
                path="/admin/places/edit/:id"
                element={
                        <PrivateRoute>
                            <PlaceEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders" */}
            <Route
                path="/admin/sliders"
                element={
                        <PrivateRoute>
                            <SlidersIndex />
                        </PrivateRoute>
                }
            />

        </Routes>
    )
}

export default RoutesIndex

Dari perubahan kode di atas, pertama-tama kita import view/component sliders index terlebih dahulu.

//import view admin sliders Index
import SlidersIndex from '../pages/admin/sliders/Index.jsx';

Setelah itu, kita buat konfigurasi private route-nya. Kurang lebih seperti berikut ini :

{/* private route "/admin/sliders" */}
<Route
    path="/admin/sliders"
    element={
    <PrivateRoute>
        <SlidersIndex />
    </PrivateRoute>
    }
/>

Di atas, kita membuat private route dengan URL /admin/sliders dan otomatis akan memanggil view/component yang bernama SlidersIndex.

Sekarang jika kita klik menu SLIDERS yang ada di dalam sidebar halaman admin atau bisa ke URL di browser http://localhost:5173/admin/sliders, maka kurang lebih hasilnya seperti berikut ini :

Menampilkan Data Sliders


Pada materi sebelumnya kita telah belajar melakukan konfigurasi private route untuk menampilkan halaman sliders index, sekarang kita akan lanjutkan belajar untuk menampilkan data di dalam halaman tersebut, kita juga akan belajar menambahkan pagination untuk membatasi jumlah data yang ditampilkan di setiap halaman.

Langkah 1 -Edit View/Component Sliders Index

Sekarang kita akan melakukan perubahan kode yang ada di dalam view/component sliders index. Silahkan buka file src/pages/admin/sliders/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

//import Link from react router dom
import { Link } from "react-router-dom";

function SlidersIndex() {

    //title page
    document.title = "Sliders - Administrator Travel GIS";

    //state posts
    const [sliders, setSliders] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);
    
    //token
    const token = Cookies.get("token");

    //function "fetchData"
    const fetchData = async () => {

        //fetching data from Rest API
        await Api.get('/api/admin/sliders', {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "sliders"
            setSliders(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-images"></i> SLIDERS</span>
                            </div>
                            <div className="card-body">
                                <div className="input-group mb-3">
                                    <Link to="/admin/sliders/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
                                </div>
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Image</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                            {sliders.map((slider, index) => (
                                                <tr key={index}>
                                                    <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                    <td className="text-center">
                                                        <img src={slider.image} alt="" width="200" className="rounded" />
                                                    </td>
                                                    <td className="text-center"></td>
                                                </tr>
                                            ))}
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default SlidersIndex;

Dari perubahan kode di atas, pertama kita import hook dari react. Yaitu useState dan useEffect.

//import react
import React, { useState, useEffect } from "react";

Setelah itu, kita import konfigurasi endpoint API yang sudah pernah kita buat sebelumnya.

//import BASE URL API
import Api from "../../../api";

Kemudian kita import package Js Cookie, karena kita akan gunakan untuk mempermudah dalam mengambil nilai cookies pada browser.

//import js cookie
import Cookies from "js-cookie";

Dan kita juga import provider Link dari React Router DOM. Ini akan kita gunakan untuk melakukan navigate ke halaman-halaman lain.

//import Link from react router dom
import { Link } from "react-router-dom";

Pada function component SlidersIndex, pertama-tama kita melakukan inisialisasi title untuk halaman ini.

//title page
document.title = "Sliders - Administrator Travel GIS";

Kemudian kita membuat beberapa state untuk menyimpan response data yang di dapatkan dari Rest API.

//state posts
const [sliders, setSliders] = useState([]);

//state currentPage
const [currentPage, setCurrentPage] = useState(1);

//state perPage
const [perPage, setPerPage] = useState(0);

//state total
const [total, setTotal] = useState(0);

Selanjutnya kita buat sebuah variable baru dengan nama token, yang isinya adalah cookies yang bernama token yang berada di dalam browser.

Token tersebut akan kita gunakan sebagai headers Authorization saat melakukan HTTP request ke dalam server yang membutuhkan proses otentikasi.

//token
const token = Cookies.get("token");

Setelah itu, kita buta function baru yang bernama fetchData dengan jenis asynchronus.

//function "fetchData"
const fetchData = async () => {

	//...
	
}

Di dalam function tersebut kita melakukan HTTP request ke server dengan endpoint /api/admin/sliders dan menggunakan method GET.

//fetching data from Rest API
await Api.get('/api/admin/sliders', {
    headers: {
        //header Bearer + Token
        Authorization: `Bearer ${token}`,
    }
})

Jika proses fetching data di dalam server berhasil dilakukan, maka kita akan melakukan assign response dari Rest API ke dalam state yang sudah kita buat.

//set data response to state "sliders"
setSliders(response.data.data.data);

//set currentPage
setCurrentPage(response.data.data.current_page);

//set perPage
setPerPage(response.data.data.per_page);

//total
setTotal(response.data.data.total);

Agar function fetchData dapat dijalankan pertama kali saat halaman diload, maka kita perlu memanggilnya di dalam hook useEffect.

//hook
useEffect(() => {
    //call function "fetchData"
    fetchData();

    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Dan untuk menampilkan data-nya di dalam JSX, kita cukup menggunakan perulangan dari JavaScript, yaitu map.

{sliders.map((slider, index) => (

	//...
	
))}

Sekarang jika halaman sliders index direload/direfresh. Maka kita akan mendapatkan hasil seperti berikut ini :

Langkah 2 - Membuat Fitur Pagination

Kita lanjutkan untuk menambahkan fitur pagination. Dimana kita akan menampilkan navigasi nomor untuk berpindah-pindah antar halaman. Tujuannya agar tidak semua data ditampilkan semuanya.

Silahkan buka file src/pages/admin/sliders/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

//import Link from react router dom
import { Link } from "react-router-dom";

//import pagination component
import PaginationComponent from "../../../components/utilities/Pagination";

function SlidersIndex() {

    //title page
    document.title = "Sliders - Administrator Travel GIS";

    //state posts
    const [sliders, setSliders] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);
    
    //token
    const token = Cookies.get("token");

    //function "fetchData"
    const fetchData = async (pageNumber) => {

        //define variable "page"
        const page = pageNumber ? pageNumber : currentPage;

        //fetching data from Rest API
        await Api.get(`/api/admin/sliders?page=${page}`, {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "sliders"
            setSliders(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-images"></i> SLIDERS</span>
                            </div>
                            <div className="card-body">
                                <div className="input-group mb-3">
                                    <Link to="/admin/sliders/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
                                </div>
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Image</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                            {sliders.map((slider, index) => (
                                                <tr key={index}>
                                                    <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                    <td className="text-center">
                                                        <img src={slider.image} alt="" width="200" className="rounded" />
                                                    </td>
                                                    <td className="text-center"></td>
                                                </tr>
                                            ))}
                                        </tbody>
                                    </table>
                                    <PaginationComponent 
                                        currentPage={currentPage} 
                                        perPage={perPage} 
                                        total={total} 
                                        onChange={(pageNumber) => fetchData(pageNumber)}
                                        position="end"
                                    />
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default SlidersIndex;

Dari perubahan kode di atas, pertama kita melakukan import component pagination terlebih dahulu.

//import pagination component
import PaginationComponent from "../../../components/utilities/Pagination";

Setelah itu, di dalam function fetchData kita berikan parameter pageNumber yang nanti akan diisi oleh angka dari page.

Kemudian, di dalamnya kita buat variable yang bernama page yang isinya adalah kondisi menggunakan ternary operator. Apabila parameter pageNumber memiliki value, maka kita akan gunakan pageNumber sebagai isinya, tapi jika tidak memiliki value, maka kita akan isi dengan state currentPage.

//define variable "page"
const page = pageNumber ? pageNumber : currentPage;

Dan kita akan gunakan variable page di atas sebagai parameter untuk melakukan HTTP request ke dalam endpoint Rest API.

await Api.get(`/api/admin/sliders?page=${page}`, {

	//...
	
}

Dan untuk menampilkan pagination di dalam JSX, kita cukup memanggil component pagination kemudian kita tambahkan beberapa data sebagai props di dalamnya.

<PaginationComponent 
    currentPage={currentPage} 
    perPage={perPage} 
    total={total} 
    onChange={(pageNumber) => fetchData(pageNumber)}
    position="end"
/>

Sekarang, jika kita reload halaman sliders index, maka kita akan mendapatkan button navigasi untuk pagination disebelah kanan bawah. Kurang lebih seperti berikut ini :

Langkah 3 - Membuat Proses Hapus Data

Terakhir, kita akan menambahkan fitur hapus data slider. Dimana sebelum melakukan proses hapus data, kita akan menampilkan jendela konfirmasi terlebih dahulu menggunakan package React Confirm Alert, yang mana digunakan untuk memastikan apakah kita akan benar-benar menghapus data tersebut atau tidak.

Silahkan buka file src/pages/admin/sliders/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

//import Link from react router dom
import { Link } from "react-router-dom";

//import pagination component
import PaginationComponent from "../../../components/utilities/Pagination";

//import toats
import toast from "react-hot-toast";

//import react-confirm-alert
import { confirmAlert } from 'react-confirm-alert';

//import CSS react-confirm-alert
import 'react-confirm-alert/src/react-confirm-alert.css'; // Import css

function SlidersIndex() {

    //title page
    document.title = "Sliders - Administrator Travel GIS";

    //state posts
    const [sliders, setSliders] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);
    
    //token
    const token = Cookies.get("token");

    //function "fetchData"
    const fetchData = async (pageNumber) => {

        //define variable "page"
        const page = pageNumber ? pageNumber : currentPage;

        //fetching data from Rest API
        await Api.get(`/api/admin/sliders?page=${page}`, {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "sliders"
            setSliders(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    //function "deleteSlider"
    const deleteSlider = (id) => {

        //show confirm alert
        confirmAlert({
            title: 'Are You Sure ?',
            message: 'want to delete this data ?',
            buttons: [{
                    label: 'YES',
                    onClick: async () => {
                        await Api.delete(`/api/admin/sliders/${id}`, {
                                headers: {
                                    //header Bearer + Token
                                    Authorization: `Bearer ${token}`,
                                }
                            })
                            .then(() => {

                                //show toast
                                toast.success("Data Deleted Successfully!", {
                                    duration: 4000,
                                    position: "top-right",
                                    style: {
                                        borderRadius: '10px',
                                        background: '#333',
                                        color: '#fff',
                                    },
                                });

                                //call function "fetchData"
                                fetchData();
                            })
                    }
                },
                {
                    label: 'NO',
                    onClick: () => {}
                }
            ]
        });
    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-images"></i> SLIDERS</span>
                            </div>
                            <div className="card-body">
                                <div className="input-group mb-3">
                                    <Link to="/admin/sliders/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
                                </div>
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Image</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                            {sliders.map((slider, index) => (
                                                <tr key={index}>
                                                    <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                    <td className="text-center">
                                                        <img src={slider.image} alt="" width="200" className="rounded" />
                                                    </td>
                                                    <td className="text-center">
                                                        <button onClick={() => deleteSlider(slider.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
                                                    </td>
                                                </tr>
                                            ))}
                                        </tbody>
                                    </table>
                                    <PaginationComponent 
                                        currentPage={currentPage} 
                                        perPage={perPage} 
                                        total={total} 
                                        onChange={(pageNumber) => fetchData(pageNumber)}
                                        position="end"
                                    />
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default SlidersIndex;

Dari perubahan kode di atas, kita import package React Hot Toast dan React Confirm Alert.

//import toats
import toast from "react-hot-toast";

//import react-confirm-alert
import { confirmAlert } from 'react-confirm-alert';

//import CSS react-confirm-alert
import 'react-confirm-alert/src/react-confirm-alert.css'; // Import css

Setelah itu, kita buat function baru yang bernama deleteSlider. Fungsi ini akan dijalankan ketika button delete di dalam JSX di klik.

<button onClick={() => deleteSlider(slider.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>

Di atas, di dalam button kita menambahkan event onClick yang isinya mengarah ke dalam function yang bernama deleteSlider.

//function "deleteSlider"
const deleteSlider = (id) => {

	//...
	
}

Di dalam function deleteSlider, pertama-tama kita memanggil confirmAlert untuk menampilkan jendela konfirmasi, yang berisi sebuah message/pesan apakah kita yakin ingin menghapus data-nya. Dan kita berikan 2 opsi button, yaitu YES dan NO.

Jika button YES diklik, maka akan melakukan HTTP request ke dalam server dengan endpoint /api/admin/sliders/${id} menggunakan method DELETE.

await Api.delete(`/api/admin/sliders/${id}`, {
    headers: {
        //header Bearer + Token
        Authorization: `Bearer ${token}`,
    }
})

Jika proses hapus data berhasil dilakukan di dalam server, maka kita akan menampilkan notifikasi menggunakan React Hot Toast yang berisi informasi data berhasil dihapus.

//show toast
toast.success("Data Deleted Successfully!", {
    duration: 4000,
    position: "top-right",
    style: {
        borderRadius: '10px',
        background: '#333',
        color: '#fff',
    },
});

Setelah itu, kita panggil function fetchData lagi, dengan tujuan melakukan fetching ulang dengan data yang telah diperbarui.

//call function "fetchData"
fetchData();

Jika button NO yang diklik, maka tidak menjalankan aksi apapun.

Sekarang jika kita lihat halaman sliders index, maka akan muncul button baru untuk delete data. Kurang lebih seperti berikut ini :

Jika kita klik salah satu button tersebut, maka akan menampilkan jendela konfirmasi dari React Confirm Alert. Kurang lebih seperti berikut ini :

Konfigurasi Private Route Slider Create


Setelah berhasil menampilkan data sliders, sekarang kita akan lanjutkan untuk membuat proses create slider atau insert data slider baru ke dalam database. Sebelum itu, kita akan melakukan konfigurasi private route untuk halaman slider create.

Langkah 1 - Membuat View/Component Slider Create

Pertama-tama, kita akan membuat view/component untuk halaman slider create terlebih dahulu dan kita akan berikan sample kode di dalamnya, yang mana akan kita ubah lagi dimateri selanjutnya untuk disesuaikan dengan kebutuhan.

Silahkan buat file baru dengan nama Create.jsx di dalam folder src/pages/admin/sliders/. Setelah itu, silahkan masukkan kode berikut ini di dalamnya.

//import react  
import React from "react";

//import layout admin
import LayoutAdmin from "../../../layouts/Admin";

function SliderCreate() {

    return(
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-images"></i> ADD NEW SLIDER</span>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    )

}

export default SliderCreate

Di atas, kita memberikan sample kode untuk halaman slider create, nantinya kita akan ubah lagi dengan sebuah form untuk melakukan upload gambar slider.

Langkah 2 - Konfigurasi Private Route Slider Create

Setelah berhasil membuat view/component untuk halaman slider create, maka kita lanjutkan untuk membuat konfigurasi private route-nya.

Silahkan buka file src/routes/routes.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react router dom
import { Routes, Route } from "react-router-dom";

//=======================================================================
//ADMIN
//=======================================================================

//import view Login
import Login from '../pages/admin/Login.jsx';

//import component private routes
import PrivateRoute from "./PrivateRoutes";

//import view admin Dashboard
import Dashboard from '../pages/admin/dashboard/Index.jsx';

//import view admin categories Index
import CategoriesIndex from '../pages/admin/categories/Index.jsx';

//import view admin category Create
import CategoryCreate from '../pages/admin/categories/Create.jsx';

//import view admin category Edit
import CategoryEdit from '../pages/admin/categories/Edit.jsx';

//import view admin places Index
import PlacesIndex from '../pages/admin/places/Index.jsx';

//import view admin places Create
import PlaceCreate from '../pages/admin/places/Create.jsx';

//import view admin places Edit
import PlaceEdit from '../pages/admin/places/Edit.jsx';

//import view admin sliders Index
import SlidersIndex from '../pages/admin/sliders/Index.jsx';

//import view admin slider Create
import SliderCreate from '../pages/admin/sliders/Create.jsx';

function RoutesIndex() {
    return (
        <Routes>

            {/* route "/admin/login" */}
            <Route path="/admin/login" element={<Login />} />

            {/* private route "/admin/dashboard" */}
            <Route
                path="/admin/dashboard"
                element={
                        <PrivateRoute>
                            <Dashboard />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories" */}
            <Route
                path="/admin/categories"
                element={
                        <PrivateRoute>
                            <CategoriesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/create" */}
            <Route
                path="/admin/categories/create"
                element={
                        <PrivateRoute>
                            <CategoryCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/edit/:id" */}
            <Route
                path="/admin/categories/edit/:id"
                element={
                        <PrivateRoute>
                            <CategoryEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places" */}
            <Route
                path="/admin/places"
                element={
                        <PrivateRoute>
                            <PlacesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/create" */}
            <Route
                path="/admin/places/create"
                element={
                        <PrivateRoute>
                            <PlaceCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/edit/:id" */}
            <Route
                path="/admin/places/edit/:id"
                element={
                        <PrivateRoute>
                            <PlaceEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders" */}
            <Route
                path="/admin/sliders"
                element={
                        <PrivateRoute>
                            <SlidersIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders/create" */}
            <Route
                path="/admin/sliders/create"
                element={
                        <PrivateRoute>
                            <SliderCreate />
                        </PrivateRoute>
                }
            />

        </Routes>
    )
}

export default RoutesIndex

Dari perubahan kode di atas, pertama-tama kita melakukan import view/component slider create yang sudah kita buat sebelumnya.

//import view admin slider Create
import SliderCreate from '../pages/admin/sliders/Create.jsx';

Setelah itu, kita lakukan konfigurasi private route-nya. Kurang lebih seperti berikut ini :

{/* private route "/admin/sliders/create" */}
<Route
    path="/admin/sliders/create"
    element={
    <PrivateRoute>
        <SliderCreate />
    </PrivateRoute>
    }
/>

Dari konfigurasi route diatas, kita atur untuk path/URL dari route-nya adalah /admin/sliders/create. Jika URL tersebut diakses, maka akan menampilkan view/component SliderCreate.

Kita coba klik button ADD NEW yang ada di dalam halaman sliders index atau bisa ke URL berikut ini : http://localhost:5173/admin/sliders/create, jika berhasil maka hasilnya akan seperti berikut ini :

Membuat Proses Create Data Slider


Pada materi sebelumnya kita telah belajar bagaimana cara menampilkan data slider di dalam halaman index, sekarang kita akan lanjutkan belajar bagaimana cara membuat proses create slider atau memasukkan data slider baru ke dalam database.

Disini kita akan mengubah sample kode yang sudah kita buat sebelumnya di dalam halaman slider create dengan menjadikan sebuah form untuk melakukan upload gambar.

Langkah 1 - Edit View/Component Slider Create

Sekarang kita akan mengubah kode di dalam view/component slider create terlebih dahulu. Silahkan buka file src/pages/admin/sliders/Create.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import hook useState from react
import React, { useState } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import hook navigate dari react router dom
import { useNavigate } from "react-router-dom";

//import js cookie
import Cookies from "js-cookie";

//import toats
import toast from "react-hot-toast";

function SliderCreate() {

    //title page
    document.title = "Add New Slider - Administrator Travel GIS";

    //state
    const [image, setImage] = useState("");

    //state validation
    const [validation, setValidation] = useState({});

    //token
    const token = Cookies.get("token");

    //navigate
    const navigate = useNavigate();

    //function "handleFileChange"
    const handleFileChange = (e) => {

        //define variable for get value image data
        const imageData = e.target.files[0]

        //check validation file
        if (!imageData.type.match('image.*')) {

            //set state "image" to null
            setImage('');

            //show toast
            toast.error("Format File not Supported!", {
                duration: 4000,
                position: "top-right",
                style: {
                    borderRadius: '10px',
                    background: '#333',
                    color: '#fff',
                },
            });

            return
        }

        //assign file to state "image"
        setImage(imageData);
    }

    //function "storeSlider"
    const storeSlider = async (e) => {
        e.preventDefault();

        //define formData
        const formData = new FormData();

        //append data to "formData"
        formData.append('image', image);

        await Api.post('/api/admin/sliders', formData, {

                //header
                headers: {
                    //header Bearer + Token
                    'Authorization': `Bearer ${token}`,
                    'content-type': 'multipart/form-data'
                }

            }).then(() => {

                //show toast
                toast.success("Data Saved Successfully!", {
                    duration: 4000,
                    position: "top-right",
                    style: {
                        borderRadius: '10px',
                        background: '#333',
                        color: '#fff',
                    },
                });

                //redirect dashboard page
                navigate("/admin/sliders");

            })
            .catch((error) => {

                //set state "validation"
                setValidation(error.response.data);
            })

    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-images"></i> ADD NEW SLIDER</span>
                            </div>
                            <div className="card-body">
                                <form onSubmit={storeSlider}>
                                    <div className="mb-3">
                                        <label className="form-label fw-bold">Image</label>
                                        <input type="file" className="form-control" onChange={handleFileChange}/>
                                    </div>
                                    {validation.image && (
                                        <div className="alert alert-danger">
                                            {validation.image[0]}
                                        </div>
                                    )}
                                    <div>
                                        <button type="submit" className="btn btn-md btn-success me-2"><i className="fa fa-save"></i> SAVE</button>
                                        <button type="reset" className="btn btn-md btn-warning"><i className="fa fa-redo"></i> RESET</button>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
              </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default SliderCreate;

Dari perubahan kode di atas, pertama kita import hook useState dari react.

//import hook useState from react
import React, { useState } from "react";

Setelah itu kita import konfigurasi endpoint yang sudah kita buat sebelumnya.

//import BASE URL API
import Api from "../../../api";

Karena nanti akan melakukan navigasi setelah berhasil insert data, maka kita butuh hook useNavigate dari React Router DOM.

//import hook navigate dari react router dom
import { useNavigate } from "react-router-dom";

Kemudian kita juga import package Js Cookie, akan kita gunakan untuk mempermudah dalam mengelola cookies yang ada di dalam browser.

//import js cookie
import Cookies from "js-cookie";

Dan untuk membuat notifikasi, kita akan butuh package yang bernama React Hot Toast.

//import toats
import toast from "react-hot-toast";

Di dalam function component SliderCreate, pertam kita melakukan inisialisasi title untuk halaman slider create. Kurang lebih seperti berikut ini :

//title page
document.title = "Add New Slider - Administrator Travel GIS";

Selanjutnya, kita buat 2 state baru, yaitu image dan validation. Untuk state image akan kita gunakan untuk menyimpan gambar dari form, sedangkan state validation kita gunakan untuk menyimpan error response validasi dari Rest API.

//state
const [image, setImage] = useState("");

//state validation
const [validation, setValidation] = useState({});

Kemudian kita buat variable baru dengan nama token, yang isinya adalah token yang diambil dari cookies di browser.

//token
const token = Cookies.get("token");

Untuk mempermudah dalam menggunakan hook useNavigate, maka kita perlu meng-assignnya ke dalam sebuah variable.

//navigate
const navigate = useNavigate();

Kemudian kita membuat sebuah function yang bernama handleFileChange. Fungsi tersebut akan dijalankan ketika kita memilih sebuah file di dalam input.

<input type="file" className="form-control" onChange={handleFileChange}/>

Di dalam input di atas, kita berikan event onChange yang mengarah ke dalam function yang bernama handleFileChange.

//function "handleFileChange"
const handleFileChange = (e) => {

	//...

}

Fungsi di atas digunakan untuk melakukan validasi terhadap file yang akan diupload. Yaitu apakah file tersebut berupa gambar atau tidak.

//define variable for get value image data
const imageData = e.target.files[0]

//check validation file
if (!imageData.type.match('image.*')) {

	//...

}

Jika file yang akan di upload tidak sesuai dengan format extensi yang sudah ditentukan, maka akan menampilkan sebuah notifikasi error yang berisi informasi format file tidak didukung.

//show toast
toast.error("Format File not Supported!", {
    duration: 4000,
    position: "top-right",
    style: {
        borderRadius: '10px',
        background: '#333',
        color: '#fff',
    },
});

Jika format file yang akan di upload sudah sesuai, maka kita akan assign ke dalam state image.

//assign file to state "image"
setImage(imageData);

Kemudian kita buat function lagi dengan nama storeSlider dengan jenis asynchronus. Fungsi ini akan dijalankan ketika form di dalam JSX disubmit.

<form onSubmit={storeSlider}>

	//...
	
</form>
//function "storeSlider"
const storeSlider = async (e) => {

	//...
	
}

Di dalam function tersebut, pertama kita inisialisasi formData terlebih dahulu.

//define formData
const formData = new FormData();

Setelah itu, kita buat append dari formData yang berisi gambar yang telah disimpan di dalam state.

//append data to "formData"
formData.append('image', image);

Kemudian, kita akan kirimkan formData tersebut ke dalam server menggunakan Rest API dengan method POST.

await Api.post('/api/admin/sliders', formData, {

    //header
    headers: {
        //header Bearer + Token
        'Authorization': `Bearer ${token}`,
        'content-type': 'multipart/form-data'
    }

})

Jika proses insert data berhasil dilakukan di dalam server, maka kita akan menampilkan notifikasi menggunakan React Hot Toast yang berisi informasi data berhasil disimpan.

//show toast
toast.success("Data Saved Successfully!", {
    duration: 4000,
    position: "top-right",
    style: {
        borderRadius: '10px',
        background: '#333',
        color: '#fff',
    },
});

Setelah itu, kita arahkan ke dalam halaman sliders index.

//redirect dashboard page
navigate("/admin/sliders");

Tapi, jika proses insert gagal dilakukan, maka akan assign error response validasi dari Rest API ke dalam state validation.

//set state "validation"
setValidation(error.response.data);

Langkag 2 - Uji Coba Proses Insert Data

Sekarang kita akan melakukan proses uji coba insert data slider baru. Silahkan klik button ADD NEW yang ada di dalam halaman slider index atau bisa ke URL berikut ini http://localhost:5173/admin/sliders/create, jika berhasil maka akan menampilkan halaman seperti berikut ini :

Silahkan klik button SAVE tanpa mengisi data apapun, maka akan muncul error validasi dari server / backend.

Dan sekarang, silahkan coba masukkan sebuah gambar dan klik button SAVE. Jika berhasil maka kita akan di arahkan ke dalam halaman sliders index dengan data yang baru saja ditambahkan.

Konfigurasi private Route Users Index


Sekarang kita akan belajar membuat konfigurasi private route untuk menampilkan halaman users index. Sebelum kita membuat konfigurasu private route-nya, maka kita akan membuatkan sebuah view/component yang digunakan untuk menampilkan data-nya nanti.

Langkah 1 - Membuat View/Component Users Index

Pertama kita akan membuat sebuah view terlebih dahulu, karena untuk melakukan konfigurasi private route kita membutuhkan sebuah view/component. Yang mana view/component tersebut yang nantinya dirender saat route tersebut diakses.

Silahkan buat folder baru dengan nama users di dalam folder src/pages/admin/. Setelah itu silahkan buat file baru dengan nama Index.jsx di dalam folder users tersebut dan masukkan kode berikut ini :

//import react  
import React from "react";

//import layout admin
import LayoutAdmin from "../../../layouts/Admin";

function UsersIndex() {

    return(
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    )

}

export default UsersIndex

Dari penambahan kode diatas, kita hanya menambahkan sample template saja. Karena nantinya kita akan ubah lagi dimateri-materi selanjutnya.

Langkah 2 - Konfigurasi Private Route Users Index

Setelah kita membuat view/component untuk halaman users index diatas, maka kita akan lanjutkan untuk membuatkan konfigurasi private route untuk halaman users index.

Silahkan buka file src/routes/routes.jsx, kemudian ubah kode-nya menjadi seperti berikut ini :

//import react router dom
import { Routes, Route } from "react-router-dom";

//=======================================================================
//ADMIN
//=======================================================================

//import view Login
import Login from '../pages/admin/Login.jsx';

//import component private routes
import PrivateRoute from "./PrivateRoutes";

//import view admin Dashboard
import Dashboard from '../pages/admin/dashboard/Index.jsx';

//import view admin categories Index
import CategoriesIndex from '../pages/admin/categories/Index.jsx';

//import view admin category Create
import CategoryCreate from '../pages/admin/categories/Create.jsx';

//import view admin category Edit
import CategoryEdit from '../pages/admin/categories/Edit.jsx';

//import view admin places Index
import PlacesIndex from '../pages/admin/places/Index.jsx';

//import view admin places Create
import PlaceCreate from '../pages/admin/places/Create.jsx';

//import view admin places Edit
import PlaceEdit from '../pages/admin/places/Edit.jsx';

//import view admin sliders Index
import SlidersIndex from '../pages/admin/sliders/Index.jsx';

//import view admin slider Create
import SliderCreate from '../pages/admin/sliders/Create.jsx';

//import view admin users Index
import UsersIndex from '../pages/admin/users/Index.jsx';

function RoutesIndex() {
    return (
        <Routes>

            {/* route "/admin/login" */}
            <Route path="/admin/login" element={<Login />} />

            {/* private route "/admin/dashboard" */}
            <Route
                path="/admin/dashboard"
                element={
                        <PrivateRoute>
                            <Dashboard />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories" */}
            <Route
                path="/admin/categories"
                element={
                        <PrivateRoute>
                            <CategoriesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/create" */}
            <Route
                path="/admin/categories/create"
                element={
                        <PrivateRoute>
                            <CategoryCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/edit/:id" */}
            <Route
                path="/admin/categories/edit/:id"
                element={
                        <PrivateRoute>
                            <CategoryEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places" */}
            <Route
                path="/admin/places"
                element={
                        <PrivateRoute>
                            <PlacesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/create" */}
            <Route
                path="/admin/places/create"
                element={
                        <PrivateRoute>
                            <PlaceCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/edit/:id" */}
            <Route
                path="/admin/places/edit/:id"
                element={
                        <PrivateRoute>
                            <PlaceEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders" */}
            <Route
                path="/admin/sliders"
                element={
                        <PrivateRoute>
                            <SlidersIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders/create" */}
            <Route
                path="/admin/sliders/create"
                element={
                        <PrivateRoute>
                            <SliderCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/users" */}
            <Route
                path="/admin/users"
                element={
                        <PrivateRoute>
                            <UsersIndex />
                        </PrivateRoute>
                }
            />


        </Routes>
    )
}

export default RoutesIndex

Dari perubahan kode diatas, pertama-tama kita lakukan import view/component users index terlebih dahulu.

//import view admin users Index
import UsersIndex from '../pages/admin/users/Index.jsx';

Setelah itu, di dalam konfigurasi route kita menambahkan sebuah konfigurasi baru. Kurang lebih seperti berikut ini :

{/* private route "/admin/users" */}
<Route
    path="/admin/users"
    element={
    <PrivateRoute>
        <UsersIndex />
    </PrivateRoute>
    }
/>

Di atas, untuk path dari route-nya kita arahkan ke dalam URL /admin/users. Dan jika URL tersebut diakses, maka akan memanggil view/component yang bernama <UsersIndex>.

Sekarang coba buka URL berikut ini http://localhost:5173/admin/users atau bisa klik menu USERS yang ada di menu sidebar. Jika berhasil maka akan meuncul halaman seperti berikut ini :

Menampilkan Data Users


Setelah kita berhasil membuat konfigurasi untuk menampilkan halaman users index, maka sekarang kita akan lanjutkan untuk menampilkan data users-nya dihalaman tersebut. Kita juga akan belajar untuk menambahkan beberapa fitur, yaitu seperti proses pencarian dan pagination untuk membagi data yang ditampilkan per-halaman.

Langkah 1 - Menampilkan Data Users

Sekarang kita akan belajar menampilkan data users yang ada di Laravel ke dalam React.js menggunakan Rest API. Sebelum itu, pastikan backend Laravel-nya sudah dijalankan terlebih dahulu.

Silahkan buka file pages/admin/users/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

function UsersIndex() {

    //title page
    document.title = "Users - Administrator Travel GIS";

    //state posts
    const [users, setUsers] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);

    //token
    const token = Cookies.get("token");

    //function "fetchData"
    const fetchData = async () => {

        //fetching data from Rest API
        await Api.get('/api/admin/users', {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "categories"
            setUsers(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
                            </div>
                            <div className="card-body">
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Full Name</th>
                                            <th scope="col">Email Address</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        {users.map((user, index) => (
                                            <tr key={index}>
                                                <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                <td>{user.name}</td>
                                                <td>{user.email}</td>
                                                <td className="text-center">
                                                    
                                                </td>
                                            </tr>
                                        ))}
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default UsersIndex;

Dari perubahan kode di atas, pertama-tama kita import beberapa hook dari React, yaitu useState dan useEffect.

//import react
import React, { useState, useEffect } from "react";

Setelah itu, kita import layout admin yang sudah pernah kita buat sebelumnya. Layout inilah yang akan membungkus view/component.

//import layout
import LayoutAdmin from "../../../layouts/Admin";

Karena akan melakukan HTTP Request ke dalam server, maka kita perlu melakukan import konfigurasi Api yang sudah pernah kita buat sebelumnya.

//import BASE URL API
import Api from "../../../api";

Dan karena endpoint yang akan kita akses bersifat private, maka kita butuh sebuah token untuk proses verifikasinya dan disini kita akan import package Js Cookie untuk mempermudah kita dalam mengelola token yang disimpan di dalam cookies browser.

//import js cookie
import Cookies from "js-cookie";

Di dalam function component UsersIndex, pertama kita membuat konfigurasi title untuk halaman.

//title page
document.title = "Users - Administrator Travel GIS";

Setelah itu, kita membuat beberapa state yang nantinya akan kita gunakan untuk menampung data yang didapatkan dari Rest API, seperti data users, current page, per-page dan total.

//state posts
const [users, setUsers] = useState([]);

//state currentPage
const [currentPage, setCurrentPage] = useState(1);

//state perPage
const [perPage, setPerPage] = useState(0);

//state total
const [total, setTotal] = useState(0);

Kemudian kita buat variable baru dengan nama token, yang isinya adalah token yang disimpan di dalam browser.

//token
const token = Cookies.get("token");

Dan setelah itu kita buat sebuah function yang bernama fetchData, dimana didalamnya kita akan gunakan untuk melakukan HTTP request ke dalam server menggunakan Rest API.

//function "fetchData"
const fetchData = async () => {

	//...
	
}

Di dalam function fetchData kita melakukan fetching ke dalam endpoint /api/admin/users dan karena bersifat private, maka kita perlu menambahkan sebuah headers dengan Authorization + token.

//fetching data from Rest API
await Api.get('/api/admin/users', {
    headers: {
        //header Bearer + Token
        Authorization: `Bearer ${token}`,
    }
})

Jika dari proses fetching berhasil dilakukan, maka kita akan melakukan assign response data ke dalam beberapa state.

//set data response to state "categories"
setUsers(response.data.data.data);

//set currentPage
setCurrentPage(response.data.data.current_page);

//set perPage
setPerPage(response.data.data.per_page);

//total
setTotal(response.data.data.total);

Agar function fetchData dapat dijalankan pertama kali, maka kita perlu memanggilnya di dalam hook useEffect.

//hook
useEffect(() => {
    //call function "fetchData"
    fetchData();

    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Dan untuk menampilkan data di dalam JSX, kita bisa menggunakan perulangan dengan map.

{users.map((user, index) => (

	//...

))}

Sekarang jika halaman users index direload/direfresh. Maka kita akan mendapatkan hasil seperti berikut ini :

Langkah 2 - Membuat Fitur Pencarian

Setelah berhasil menampilkan data, sekarang kita akan lanjutkan untuk menambahkan fitur pencarian data. Dan disini kita akan melakukan perubahan dengan menambahkan beberapa kode di dalam file yang sama.

Silahkan buka file src/pages/admin/users/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

//import Link from react router dom
import { Link } from "react-router-dom";

function UsersIndex() {

    //title page
    document.title = "Users - Administrator Travel GIS";

    //state posts
    const [users, setUsers] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);

    //token
    const token = Cookies.get("token");

    //state search
    const [search, setSearch] = useState("");

    //function "fetchData"
    const fetchData = async (searchData) => {

        //define variable "searchQuery"
        const searchQuery = searchData ? searchData : search;

        //fetching data from Rest API
        await Api.get(`/api/admin/users?q=${searchQuery}`, {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "categories"
            setUsers(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    //function "searchHandler"
    const searchHandlder = (e) => {
        e.preventDefault();

        //call function "fetchDataPost" with state search
        fetchData(search)
    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
                            </div>
                            <div className="card-body">
                                <form onSubmit={searchHandlder} className="form-group">
                                    <div className="input-group mb-3">
                                        <Link to="/admin/users/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
                                        <input type="text" className="form-control" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="search by user name" />
                                        <button type="submit" className="btn btn-md btn-success"><i className="fa fa-search"></i> SEARCH</button>
                                    </div>
                                </form>
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Full Name</th>
                                            <th scope="col">Email Address</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        {users.map((user, index) => (
                                            <tr key={index}>
                                                <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                <td>{user.name}</td>
                                                <td>{user.email}</td>
                                                <td className="text-center">
                                                    
                                                </td>
                                            </tr>
                                        ))}
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default UsersIndex;

Dari perubahan kode di atas, pertama kita import provider Link dari React Router DOM. Ini akan digunakan untuk melakukan navigasi ke tambah user nanti-nya.

//import Link from react router dom
import { Link } from "react-router-dom";

Setelah itu, kita tambahkan sebuah state baru yang bernama search. State ini yang akan kita gunakan untuk menampung kata kunci yang diketikkan di dalam form input.

//state search
const [search, setSearch] = useState("");

Kemudian di dalam function fetchData kita berikan parameter baru dengan nama searchData. Dan di dalam function tersebut kita membuat variable dengan nama searchQuery yang berisi kondisi dengan ternary operator.

//define variable "searchQuery"
const searchQuery = searchData ? searchData : search;

Dan kita gunakan variable tersebut sebagai parameter di dalam endpoint Rest API.

await Api.get(`/api/admin/users?q=${searchQuery}`, {

	//...
	
}

Kemudian kita membuat function baru yang bernama serachHandler. Dimana function tersebut akan dijalankan ketikan form pencarian disubmit.

<form onSubmit={searchHandlder} className="form-group">

	//...
	
</form>
//function "searchHandler"
const searchHandlder = (e) => {
    e.preventDefault();

    //call function "fetchDataPost" with state search
    fetchData(search)
}

Di dalam function searchHandler kita memanggil function fetchData dengan parameter state search.

Sekarang silahkan teman-teman coba melakukan proses pencarian pada halaman users.

Langkah 3 - Membuat Fitur Pagination

Kita lanjutkan untuk menambahkan fitur pagination. Dimana kita akan menampilkan navigasi nomor untuk berpindah-pindah antar halaman.

karena sebelumnya kita sudah pernah membuat component untuk pagination, maka kita akan import lalu menggunakan component tersebut (reusable).

Silahkan buka file src/pages/admin/users/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

//import Link from react router dom
import { Link } from "react-router-dom";

//import pagination component
import PaginationComponent from "../../../components/utilities/Pagination";

function UsersIndex() {

    //title page
    document.title = "Users - Administrator Travel GIS";

    //state posts
    const [users, setUsers] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);

    //token
    const token = Cookies.get("token");

    //state search
    const [search, setSearch] = useState("");

    //function "fetchData"
    const fetchData = async (pageNumber, searchData) => {

        //define variable "page"
        const page = pageNumber ? pageNumber : currentPage;

        //define variable "searchQuery"
        const searchQuery = searchData ? searchData : search;

        //fetching data from Rest API
        await Api.get(`/api/admin/users?q=${searchQuery}&page=${page}`, {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "categories"
            setUsers(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    //function "searchHandler"
    const searchHandlder = (e) => {
        e.preventDefault();

        //call function "fetchDataPost" with state search and page number
        fetchData(1, search)
    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
                            </div>
                            <div className="card-body">
                                <form onSubmit={searchHandlder} className="form-group">
                                    <div className="input-group mb-3">
                                        <Link to="/admin/users/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
                                        <input type="text" className="form-control" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="search by user name" />
                                        <button type="submit" className="btn btn-md btn-success"><i className="fa fa-search"></i> SEARCH</button>
                                    </div>
                                </form>
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Full Name</th>
                                            <th scope="col">Email Address</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        {users.map((user, index) => (
                                            <tr key={index}>
                                                <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                <td>{user.name}</td>
                                                <td>{user.email}</td>
                                                <td className="text-center">
                                                    
                                                </td>
                                            </tr>
                                        ))}
                                        </tbody>
                                    </table>
                                    <PaginationComponent 
                                        currentPage={currentPage} 
                                        perPage={perPage} 
                                        total={total} 
                                        onChange={(pageNumber) => fetchData(pageNumber)}
                                        position="end"
                                    />
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default UsersIndex;

Dari perubahan kode di atas, pertama kita melakukan import component PaginationComponent yang sudah kita buat sebelumnya.

//import pagination component
import PaginationComponent from "../../../components/utilities/Pagination";

Setelah itu, di dalam function fetchData kita berikan parameter satu lagi untuk page number.

//function "fetchData"
const fetchData = async (pageNumber, searchData) => {

	//...
	
}

Dan di dalam function fetchData kita membuat variable yang bernama page yang mana isinya adalah parameter diatas dengan kondisi ternary operator.

//define variable "page"
const page = pageNumber ? pageNumber : currentPage;

Dan kita gunakan variable page tersebut sebagai parameter di dalam endpoint Rest API.

//fetching data from Rest API
await Api.get(`/api/admin/users?q=${searchQuery}&page=${page}`, {

	//...
	
}

Dan kita juga melakukan perubahan di dalam function searchHandler dengan menambahkan paramter 1 sebagai default page saat proses pencarian.

//call function "fetchDataPost" with state search and page number
fetchData(1, search)

Kemudian untuk menampilkan pagination di dalam JSX, kita cukup memanggil component PaginationComponent dengan menambahkan beberapa data props dari state.

<PaginationComponent 
   currentPage={currentPage} 
   perPage={perPage} 
   total={total} 
   onChange={(pageNumber) => fetchData(pageNumber)}
   position="end"
/>

Di atas, kita mengirimkan beberapa data sebagai props, yaitu currentPage, perPage, total, onChange dan position.

Sekarang, jika kita reload/refresh halaman-nya, maka kita akan mendapatkan hasil kurang lebih seperti berikut ini :

Langkah 4 - Membuat Proses Hapus Data

Terakhir, kita akan membuat fungsi untuk melakukan proses hapus data, dimana kita akan menambahkan satu button yang mengarah ke dalam sebuah function dan di dalamnya kita melakukan delete data ke dalam server menggunakan Rest API.

Sebelum data dihapus, kita juga akan menampilkan sebuah jendela konfirmasi, guna memastikan apakah kita benar-benar ingin menghapus data tersebut atau tidak. Dan untuk membuat fitur tersebut, kita butuh package tambahan yang bernama React Confirm Alert.

Silahkan buka file src/pages/admin/users/Index.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import js cookie
import Cookies from "js-cookie";

//import Link from react router dom
import { Link } from "react-router-dom";

//import pagination component
import PaginationComponent from "../../../components/utilities/Pagination";

//import toats
import toast from "react-hot-toast";

//import react-confirm-alert
import { confirmAlert } from 'react-confirm-alert';

//import CSS react-confirm-alert
import 'react-confirm-alert/src/react-confirm-alert.css'; // Import css

function UsersIndex() {

    //title page
    document.title = "Users - Administrator Travel GIS";

    //state posts
    const [users, setUsers] = useState([]);

    //state currentPage
    const [currentPage, setCurrentPage] = useState(1);

    //state perPage
    const [perPage, setPerPage] = useState(0);

    //state total
    const [total, setTotal] = useState(0);

    //token
    const token = Cookies.get("token");

    //state search
    const [search, setSearch] = useState("");

    //function "fetchData"
    const fetchData = async (pageNumber, searchData) => {

        //define variable "page"
        const page = pageNumber ? pageNumber : currentPage;

        //define variable "searchQuery"
        const searchQuery = searchData ? searchData : search;

        //fetching data from Rest API
        await Api.get(`/api/admin/users?q=${searchQuery}&page=${page}`, {
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        }).then(response => {
            //set data response to state "categories"
            setUsers(response.data.data.data);

            //set currentPage
            setCurrentPage(response.data.data.current_page);

            //set perPage
            setPerPage(response.data.data.per_page);

            //total
            setTotal(response.data.data.total);
        });
    };

    //hook
    useEffect(() => {
        //call function "fetchData"
        fetchData();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    //function "searchHandler"
    const searchHandlder = (e) => {
        e.preventDefault();

        //call function "fetchDataPost" with state search and page number
        fetchData(1, search)
    }

    //function "deleteUser"
    const deleteUser = (id) => {

        //show confirm alert
        confirmAlert({
            title: 'Are You Sure ?',
            message: 'want to delete this data ?',
            buttons: [{
                    label: 'YES',
                    onClick: async () => {
                        await Api.delete(`/api/admin/users/${id}`, {
                                headers: {
                                    //header Bearer + Token
                                    Authorization: `Bearer ${token}`,
                                }
                            })
                            .then(() => {

                                //show toast
                                toast.success("Data Deleted Successfully!", {
                                    duration: 4000,
                                    position: "top-right",
                                    style: {
                                        borderRadius: '10px',
                                        background: '#333',
                                        color: '#fff',
                                    },
                                });

                                //call function "fetchData"
                                fetchData();
                            })
                    }
                },
                {
                    label: 'NO',
                    onClick: () => {}
                }
            ]
        });
    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-md-12">
                        <div className="card border-0 border-top-success rounded shadow-sm mb-5">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
                            </div>
                            <div className="card-body">
                                <form onSubmit={searchHandlder} className="form-group">
                                    <div className="input-group mb-3">
                                        <Link to="/admin/users/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
                                        <input type="text" className="form-control" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="search by user name" />
                                        <button type="submit" className="btn btn-md btn-success"><i className="fa fa-search"></i> SEARCH</button>
                                    </div>
                                </form>
                                <div className="table-responsive">
                                    <table className="table table-bordered table-striped table-hovered">
                                        <thead>
                                        <tr>
                                            <th scope="col">No.</th>
                                            <th scope="col">Full Name</th>
                                            <th scope="col">Email Address</th>
                                            <th scope="col">Actions</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        {users.map((user, index) => (
                                            <tr key={index}>
                                                <td className="text-center">{++index + (currentPage-1) * perPage}</td>
                                                <td>{user.name}</td>
                                                <td>{user.email}</td>
                                                <td className="text-center">
                                                    <button onClick={() => deleteUser(user.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
                                                </td>
                                            </tr>
                                        ))}
                                        </tbody>
                                    </table>
                                    <PaginationComponent 
                                        currentPage={currentPage} 
                                        perPage={perPage} 
                                        total={total} 
                                        onChange={(pageNumber) => fetchData(pageNumber)}
                                        position="end"
                                    />
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default UsersIndex;

Dari perubahan kode di atas, pertama kita import React Hot Toast dan React Confirm Alert terlebih dahulu.

//import toats
import toast from "react-hot-toast";

//import react-confirm-alert
import { confirmAlert } from 'react-confirm-alert';

//import CSS react-confirm-alert
import 'react-confirm-alert/src/react-confirm-alert.css'; // Import css

Setelah itu, kita tambahkan sebuah button delete di dalam perulangan data users dan di dalam button tersebut kita berikan event onClick yang mengarah ke dalam function deleteUser.

<button onClick={() => deleteUser(user.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>

Dan di dalam function deleteUser, pertama kita akan menggunakan confirmAlert untuk menampilkan jendela konfirmasi.

//show confirm alert
confirmAlert({

	//...
	
})

Di dalamnya kita berikan 2 button, yaitu YES dan NO. Jika button YES yang di klik, maka akan menjalankan sebuah endpoint dengan method DELETE ke dalam server.

await Api.delete(`/api/admin/users/${id}`, {

	//...
	
}

Jika proses hapus data berhasil dilakukan di dalam server, maka kita akan menampilkan notifikasi menggunakan React Hot Toast dengan status success.

//show toast
toast.success("Data Deleted Successfully!", {
    duration: 4000,
    position: "top-right",
    style: {
        borderRadius: '10px',
        background: '#333',
        color: '#fff',
    },
});

Kemudian kita panggil method fetchData, agar data yang ditampilkan diperbarui dengan data yang terbaru.

//call function "fetchData"
fetchData();

Tapi, jika button yang di klik adalah NO, maka tidak akan menjalankan perintah apapun.

{
    label: 'NO',
    onClick: () => {}
}

Sekarang kita bisa melakukan reload halaman users index, jika berhasil maka kita akan mendapatkan button delete yang kurang lebih seperti berikut ini :

Silahkan klik salah satu button delete yang ada, jika berhasil maka akan menampilkan jendela konfirmasi yang kurang lebih seperti berikut ini :

Konfigurasi Private Route User Create


Setelah berhasil menampilkan data users, sekarang kita akan lanjutkan lagi belajar bagaimana cara membuat konfigurasi private route untuk menampilkan halaman tambah user. Sebelum itu kita akan membuat view/component-nya terlebih dahulu.

Langkah 1 - Membuat View/Component User Create

Silahkan buat file baru dengan nama Create.jsx, di dalam folder src/pages/admin/users/. Setelah itu masukkan kode berikut ini di dalamnya.

//import react  
import React from "react";

//import layout admin
import LayoutAdmin from "../../../layouts/Admin";

function UserCreate() {

    return(
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> ADD NEW USER</span>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    )

}

export default UserCreate

Dari penambahan kode diatas, kita hanya memberikan sample kode untuk halaman user create, dimana dimateri-materi selanjutnya kita akan ubah lagi sesuai dengan kebutuhan.

Langkah 2 - Konfigurasi Private Route User Create

Sekarang kita lanjutkan untuk membuat konfigurasi private route-nya. Dimana ketika route tersebut diakses, maka akan memanggil view/component yang sudah kita buat diatas.

Silahkan buka file src/routes/routes.jsx dan ubah semua kode-nya menjadi seperti berikut ini :

//import react router dom
import { Routes, Route } from "react-router-dom";

//=======================================================================
//ADMIN
//=======================================================================

//import view Login
import Login from '../pages/admin/Login.jsx';

//import component private routes
import PrivateRoute from "./PrivateRoutes";

//import view admin Dashboard
import Dashboard from '../pages/admin/dashboard/Index.jsx';

//import view admin categories Index
import CategoriesIndex from '../pages/admin/categories/Index.jsx';

//import view admin category Create
import CategoryCreate from '../pages/admin/categories/Create.jsx';

//import view admin category Edit
import CategoryEdit from '../pages/admin/categories/Edit.jsx';

//import view admin places Index
import PlacesIndex from '../pages/admin/places/Index.jsx';

//import view admin places Create
import PlaceCreate from '../pages/admin/places/Create.jsx';

//import view admin places Edit
import PlaceEdit from '../pages/admin/places/Edit.jsx';

//import view admin sliders Index
import SlidersIndex from '../pages/admin/sliders/Index.jsx';

//import view admin slider Create
import SliderCreate from '../pages/admin/sliders/Create.jsx';

//import view admin users Index
import UsersIndex from '../pages/admin/users/Index.jsx';

//import view admin user Create
import UserCreate from '../pages/admin/users/Create.jsx';

function RoutesIndex() {
    return (
        <Routes>

            {/* route "/admin/login" */}
            <Route path="/admin/login" element={<Login />} />

            {/* private route "/admin/dashboard" */}
            <Route
                path="/admin/dashboard"
                element={
                        <PrivateRoute>
                            <Dashboard />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories" */}
            <Route
                path="/admin/categories"
                element={
                        <PrivateRoute>
                            <CategoriesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/create" */}
            <Route
                path="/admin/categories/create"
                element={
                        <PrivateRoute>
                            <CategoryCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/edit/:id" */}
            <Route
                path="/admin/categories/edit/:id"
                element={
                        <PrivateRoute>
                            <CategoryEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places" */}
            <Route
                path="/admin/places"
                element={
                        <PrivateRoute>
                            <PlacesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/create" */}
            <Route
                path="/admin/places/create"
                element={
                        <PrivateRoute>
                            <PlaceCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/edit/:id" */}
            <Route
                path="/admin/places/edit/:id"
                element={
                        <PrivateRoute>
                            <PlaceEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders" */}
            <Route
                path="/admin/sliders"
                element={
                        <PrivateRoute>
                            <SlidersIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders/create" */}
            <Route
                path="/admin/sliders/create"
                element={
                        <PrivateRoute>
                            <SliderCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/users" */}
            <Route
                path="/admin/users"
                element={
                        <PrivateRoute>
                            <UsersIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/users/create" */}
            <Route
                path="/admin/users/create"
                element={
                        <PrivateRoute>
                            <UserCreate />
                        </PrivateRoute>
                }
            />


        </Routes>
    )
}

export default RoutesIndex

Dari perubahan kode diatas, pertama-tama kita import view/component user create terlebih dahulu.

//import view admin user Create
import UserCreate from '../pages/admin/users/Create.jsx';

Setelah itu, kita buat konfigurasi private route-nya dengan path /admin/users/create. Jadi jika path atau URL tersebut diakses, maka akan menjalankan view/component <UserCreate />.

Sekarang, jika kita klik button ADD NEW di halaman users index atau ke URL berikut ini http://localhost:5173/admin/users/create, maka jika berhasil akan mendapatkan hasil seperti berikut ini :

Membuat Proses Create Data User


Pada materi sebelumnya kita telah belajar melakukan konfigurasi private route untuk menampilkan halaman tambah data user, sekarang kita akan melakukan perubahan dihalaman tersebut dengan menambahkan sebuah form yang nanti digunakan untuk proses input data.

Langkah 1 - Edit View/Component User Create

Sekarang kita akan melakukan perubahan kode yang ada dihalaman user create. Dimana kita akan menambahkan sebuah form input dan juga proses insert data ke dalam server melalui Rest API.

Silahkan buka file src/pages/admin/users/Create.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import hook useState from react
import React, { useState } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import hook navigate dari react router dom
import { useNavigate } from "react-router-dom";

//import js cookie
import Cookies from "js-cookie";

//import toats
import toast from "react-hot-toast";

function UserCreate() {

    //title page
    document.title = "Add New User - Administrator Travel GIS";

    //state
    const [name, setName] = useState("");
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [passwordConfirmation, setPasswordConfirmation] = useState("");

    //state validation
    const [validation, setValidation] = useState({});

    //token
    const token = Cookies.get("token");

    //navigate
    const navigate = useNavigate();

    //function "storeUser"
    const storeUser = async (e) => {
        e.preventDefault();

        //define formData
        const formData = new FormData();

        //append data to "formData"
        formData.append('name', name);
        formData.append('email', email);
        formData.append('password', password);
        formData.append('password_confirmation', passwordConfirmation);

        await Api.post('/api/admin/users', formData, {

                //header
                headers: {
                    //header Bearer + Token
                    Authorization: `Bearer ${token}`,
                }

            }).then(() => {

                //show toast
                toast.success("Data Saved Successfully!", {
                    duration: 4000,
                    position: "top-right",
                    style: {
                        borderRadius: '10px',
                        background: '#333',
                        color: '#fff',
                    },
                });

                //redirect dashboard page
                navigate("/admin/users");

            })
            .catch((error) => {

                //set state "validation"
                setValidation(error.response.data);
            })

    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> ADD USER</span>
                            </div>
                            <div className="card-body">
                                <form onSubmit={storeUser}>

                                    <div className="row">
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Full Name</label>
                                                <input type="text" className="form-control" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter Full Name"/>
                                            </div>
                                            {validation.name && (
                                                <div className="alert alert-danger">
                                                    {validation.name[0]}
                                                </div>
                                            )}
                                        </div>
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Email Address</label>
                                                <input type="text" className="form-control" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Enter Email Address"/>
                                            </div>
                                            {validation.email && (
                                                <div className="alert alert-danger">
                                                    {validation.email[0]}
                                                </div>
                                            )}
                                        </div>
                                    </div>

                                    <div className="row">
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Password</label>
                                                <input type="password" className="form-control" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Enter Password"/>
                                            </div>
                                            {validation.password && (
                                                <div className="alert alert-danger">
                                                    {validation.password[0]}
                                                </div>
                                            )}
                                        </div>
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Password Confirmation</label>
                                                <input type="password" className="form-control" value={passwordConfirmation} onChange={(e) => setPasswordConfirmation(e.target.value)} placeholder="Enter Password Confirmation"/>
                                            </div>
                                        </div>
                                    </div>

                                    <div>
                                        <button type="submit" className="btn btn-md btn-success me-2"><i className="fa fa-save"></i> SAVE</button>
                                        <button type="reset" className="btn btn-md btn-warning"><i className="fa fa-redo"></i> RESET</button>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
              </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default UserCreate;

Dari perubahan kode diatas, pertama-tama kita import hook useState dari React. Ini akan kita gunakan untuk mendefinisikan sebuah state baru di dalam view/component.

//import hook useState from react
import React, { useState } from "react";

Seteleh itu, kita juga import layout admin, dimana kode yang akan kita tulis di dalam view/component berada di dalam layout tersebut.

//import layout
import LayoutAdmin from "../../../layouts/Admin";

Karena akan melakukan HTTP request ke dalam server, maka kita butuh import konfigurasi global API yang sudah pernah kita buat sebelumnya.

//import BASE URL API
import Api from "../../../api";

Kemudain kita import hook useHistory dari React Router DOM. Ini akan kita gunakan untuk melakukan navigasi ke halaman-halaman lain.

//import hook navigate dari react router dom
import { useNavigate } from "react-router-dom";

Karena akan membutuhan token yang ada di dalam cookies browser, maka kita juga import package Js Cookie. Ini bertujuan untuk mempermudah kita dalam mengelola cookies yang ada di dalam browser.

//import js cookie
import Cookies from "js-cookie";

Dan kita juga import package yang bernama React Hot Toast, package ini akan kita gunakan untuk menampilkan notifikasi saat kita berhasil melakukan proses insert data baru.

//import toats
import toast from "react-hot-toast";

Di dalama function component UserCreate, pertam-tama kita buat konfigurasi title untuk halaman ini.

//title page
document.title = "Add New User - Administrator Travel GIS";

Selanjutnya, kita membuat beberapa state baru yang nanti kita gunakan untuk menyimpan data yang diinputkan di dalam form.

//state
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [passwordConfirmation, setPasswordConfirmation] = useState("");

Dan kita buat state baru lagi untuk menyimpan error response validasi yang di dapatkan dari Rest API.

//state validation
const [validation, setValidation] = useState({});

Kemudian kita buat variable baru dengan nama token, dimana variable tersebut berisi token yang diambil dari cookies browser.

//token
const token = Cookies.get("token");

Untuk mempermudah dalam menggunakan hook useHistory, maka kita perlu memasukkannya ke dalam sebuah variable.

//navigate
const navigate = useNavigate();

Kemudian kita buat function baru dengan nama storeUser, function tersebut yang nantinya dieksekusi ketika form disubmit.

<form onSubmit={storeUser}>

	//...
	
</form>
//function "storeUser"
const storeUser = async (e) => {
    
    //...
    
}

Di dalam function storeUser, pertama-tama kita melakukan inisialisasi sebuah formData terlebih dahulu. Tujuannya untuk mempermudah kita dalam menampung data dan mengirimkannya ke dalam server.

//define formData
const formData = new FormData();

Setelah berhasil melakukan inisialisasi, selanjutnya kita akan append data yang ada di dalam state ke dalam formData.

//append data to "formData"
formData.append('name', name);
formData.append('email', email);
formData.append('password', password);
formData.append('password_confirmation', passwordConfirmation);

Selanjutnya, kita akan kirimkan formData tersebut ke dalam endpoint /api/admin/users dengan method POST.

await Api.post('/api/admin/users', formData, {

    //header
    headers: {
        //header Bearer + Token
        Authorization: `Bearer ${token}`,
    }

})

Jika data berhasil terkirim dan diinsert ke dalam database, maka kita akan menampilkan notifikasi sukses yang berisi informasi data berhasil disimpan menggunakan React Hot Toast.

//show toast
toast.success("Data Saved Successfully!", {
    duration: 4000,
    position: "top-right",
    style: {
        borderRadius: '10px',
        background: '#333',
        color: '#fff',
    },
});

Dan setelah itu kita arahkan ke dalam URL /admin/users.

//redirect dashboard page
navigate("/admin/users");

Tapi, jika data gagal dimasukkan ke dalam database, maka kita akan assign error response validasi ke dalam state validation dan nantinya kita akan tampilkan di dalam JSX.

//set state "validation"
setValidation(error.response.data);

Di dalam JSX, untuk menampilkan error validasi kita cukup seperti berikut ini :

{validation.name && (
    <div className="alert alert-danger">
        {validation.name[0]}
    </div>
)}

Di atas, kita membuat sebuah kondisi, jika ada state validation.name, maka isi didalamnya akan ditampilkan.

Langkag 2 - Uji Coba Proses Insert Data

Sekarang kita akan melakukan proses uji coba insert data user baru. Silahkan klik button ADD NEW yang ada di dalam halaman users index atau bisa ke URL berikut ini http://localhost:5173/admin/users/create, jika berhasil maka akan menampilkan halaman seperti berikut ini :

Silahkan klik button SAVE tanpa mengisi data apapun, maka kita akan mendapatkan error validasi yang di dapatkan dari Rest API.

Dan jika sekarang kita coba masukkan data yang benar dan klik button SAVE, maka kita akan diarahkan ke halaman users index dengan data yang baru saja diinput.

Konfigurasi Private Route User Edit


Sekarang kita lanjutkan membuat sebuah private route untuk menampilkan halaman edit data user. Sebelum kita melakukan konfigurasi private route, seperti sebelum-sebelumnya yaitu harus membuat view/component-nya terlebih dahulu.

Langkah 1 - Membuat View/Component User Edit

Sekarang kita akan membuat view/component untuk halaman edit user dan untuk kode-nya akan kita berikan sample terlebih dahulu, karena kita akan ubah lagi nanti di materi-materi selanjutnya.

Silahkan buat file baru dengan nama Edit.jsx di dalam folder src/pages/admin/users, kemudian masukkan kode berikut ini di dalamnya.

//import react  
import React from "react";

//import layout admin
import LayoutAdmin from "../../../layouts/Admin";

function UserEdit() {

    return(
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> EDIT USER</span>
                            </div>
                        </div>
                    </div>
                </div>
            </LayoutAdmin>
        </React.Fragment>
    )

}

export default UserEdit

Di atas, kita menambahkan kode untuk sample halaman edit user, dimateri selanjutnya kita akan sesuaikan lagi untuk menampilkan form untuk proses edit dan update data user.

Langkah 2 - Konfigurasi Private Route User Edit

Setelah berhasil membuat view/component untuk halaman user edit, sekarang kita lanjutkan membuat konfigurasi private route-nya.

Silahkan buka file src/routes/routes.jsx, kemudian ubah semua kode-nya menjadi seperti berikut ini :

//import react router dom
import { Routes, Route } from "react-router-dom";

//=======================================================================
//ADMIN
//=======================================================================

//import view Login
import Login from '../pages/admin/Login.jsx';

//import component private routes
import PrivateRoute from "./PrivateRoutes";

//import view admin Dashboard
import Dashboard from '../pages/admin/dashboard/Index.jsx';

//import view admin categories Index
import CategoriesIndex from '../pages/admin/categories/Index.jsx';

//import view admin category Create
import CategoryCreate from '../pages/admin/categories/Create.jsx';

//import view admin category Edit
import CategoryEdit from '../pages/admin/categories/Edit.jsx';

//import view admin places Index
import PlacesIndex from '../pages/admin/places/Index.jsx';

//import view admin places Create
import PlaceCreate from '../pages/admin/places/Create.jsx';

//import view admin places Edit
import PlaceEdit from '../pages/admin/places/Edit.jsx';

//import view admin sliders Index
import SlidersIndex from '../pages/admin/sliders/Index.jsx';

//import view admin slider Create
import SliderCreate from '../pages/admin/sliders/Create.jsx';

//import view admin users Index
import UsersIndex from '../pages/admin/users/Index.jsx';

//import view admin user Create
import UserCreate from '../pages/admin/users/Create.jsx';

//import view admin user Edit
import UserEdit from '../pages/admin/users/Edit.jsx';

function RoutesIndex() {
    return (
        <Routes>

            {/* route "/admin/login" */}
            <Route path="/admin/login" element={<Login />} />

            {/* private route "/admin/dashboard" */}
            <Route
                path="/admin/dashboard"
                element={
                        <PrivateRoute>
                            <Dashboard />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories" */}
            <Route
                path="/admin/categories"
                element={
                        <PrivateRoute>
                            <CategoriesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/create" */}
            <Route
                path="/admin/categories/create"
                element={
                        <PrivateRoute>
                            <CategoryCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/categories/edit/:id" */}
            <Route
                path="/admin/categories/edit/:id"
                element={
                        <PrivateRoute>
                            <CategoryEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places" */}
            <Route
                path="/admin/places"
                element={
                        <PrivateRoute>
                            <PlacesIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/create" */}
            <Route
                path="/admin/places/create"
                element={
                        <PrivateRoute>
                            <PlaceCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/places/edit/:id" */}
            <Route
                path="/admin/places/edit/:id"
                element={
                        <PrivateRoute>
                            <PlaceEdit />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders" */}
            <Route
                path="/admin/sliders"
                element={
                        <PrivateRoute>
                            <SlidersIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/sliders/create" */}
            <Route
                path="/admin/sliders/create"
                element={
                        <PrivateRoute>
                            <SliderCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/users" */}
            <Route
                path="/admin/users"
                element={
                        <PrivateRoute>
                            <UsersIndex />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/users/create" */}
            <Route
                path="/admin/users/create"
                element={
                        <PrivateRoute>
                            <UserCreate />
                        </PrivateRoute>
                }
            />

            {/* private route "/admin/users/edit/:id" */}
            <Route
                path="/admin/users/edit/:id"
                element={
                        <PrivateRoute>
                            <UserEdit />
                        </PrivateRoute>
                }
            />


        </Routes>
    )
}

export default RoutesIndex

Dari perubahan kode di atas, pertama kita import file view/component user edit yang sudah kita buat sebelumnya.

//import view admin user Edit
import UserEdit from '../pages/admin/users/Edit.jsx';

Setelah itu, kita buat konfigurasi private route-nya dengan path admin/users/edit/:id. Jadi jika URL tersebut diakses, maka akan memanggil view/component <UserEdit>.

Membuat Proses Edit Data User


Sekarang kita akan belajar bagaimana cara membuat proses edit dan update data user ke dalam database di React.js menggunakan Rest API. Pertama-tama kita akan menambahkan sebuah button edit terlebih dahulu di dalam halaman users index, dimana button tersebut ketika diklik akan menuju ke halaman form edit user berdasarkan ID user yang dipilih.

Langkah 1 - Menampilkan Button Edit

Sekarang kita akan melakukan sedikit penambahan kode di dalam halaman users index, yaitu dengan menambahkan button baru untuk proses edit data.

Silahkan buka file src/pages/admin/users/Index.jsx, kemudian cari kode berikut ini :

<td className="text-center">
  <button onClick={() => deleteUser(user.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
</td>

Dan ubahlah menjadi seperti berikut ini :

<td className="text-center">
  <Link to={`/admin/users/edit/${user.id}`} className="btn btn-sm btn-primary me-2"><i className="fa fa-pencil-alt"></i></Link>
  <button onClick={() => deleteUser(user.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
</td>

Di atas, kita menambahkan 1 button baru untuk proses edit data, dimana untuk URL-nya kita arahkan ke dalam /admin/users/edit/:id.

Sekarang jika halaman users index di reload/refresh, maka akan muncul button edit tersebut. Kurang lebih seperti berikut ini :

Langkah 2 - Edit View/Component User Edit

Setelah berhasil menambahkan button edit, sekarang kita lanjutkan untuk melakukan perubahan di dalam view/component user edit sesuai dengan kebutuhan.

Silahkan buka file src/pages/admin/users/Edit.jsx, kemudian ubah kode-nya menjadi seperti berikut ini :

//import hook useState from react
import React, { useState, useEffect } from "react";

//import layout
import LayoutAdmin from "../../../layouts/Admin";

//import BASE URL API
import Api from "../../../api";

//import hook navigate dari react router dom
import { useNavigate, useParams } from "react-router-dom";

//import js cookie
import Cookies from "js-cookie";

//import toats
import toast from "react-hot-toast";

function UserEdit() {

    //title page
    document.title = "Edit User - Administrator Travel GIS";

    //state
    const [name, setName] = useState("");
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [passwordConfirmation, setPasswordConfirmation] = useState("");

    //state validation
    const [validation, setValidation] = useState({});

    //token
    const token = Cookies.get("token");

    //navigate
    const navigate = useNavigate();

    //get ID from parameter URL
    const { id } = useParams();

    //function "getUserById"
    const getUserById = async () => {

        //get data from server
        const response = await Api.get(`/api/admin/users/${id}`, {

            //header
            headers: {
                //header Bearer + Token
                Authorization: `Bearer ${token}`,
            }
        });

        //get response data
        const data = await response.data.data

        //assign data to state "name"
        setName(data.name);
        //assign data to state "email"
        setEmail(data.email);
    };

    //hook useEffect
    useEffect(() => {

        //panggil function "getUserById"
        getUserById();

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    //function "updateUser"
    const updateUser = async (e) => {
        e.preventDefault();

        //define formData
        const formData = new FormData();

        //append data to "formData"
        formData.append('name', name);
        formData.append('email', email);
        formData.append('password', password);
        formData.append('password_confirmation', passwordConfirmation);
        formData.append('_method', 'PATCH');

        await Api.post(`/api/admin/users/${id}`, formData, {

                //header
                headers: {
                    //header Bearer + Token
                    Authorization: `Bearer ${token}`,
                }

            }).then(() => {

                //show toast
                toast.success("Data Updated Successfully!", {
                    duration: 4000,
                    position: "top-right",
                    style: {
                        borderRadius: '10px',
                        background: '#333',
                        color: '#fff',
                    },
                });

                //redirect dashboard page
                navigate("/admin/users");

            })
            .catch((error) => {

                //set state "validation"
                setValidation(error.response.data);
            })

    }

    return (
        <React.Fragment>
            <LayoutAdmin>
                <div className="row mt-4">
                    <div className="col-12">
                        <div className="card border-0 rounded shadow-sm border-top-success">
                            <div className="card-header">
                                <span className="font-weight-bold"><i className="fa fa-users"></i> EDIT USER</span>
                            </div>
                            <div className="card-body">
                                <form onSubmit={updateUser}>

                                    <div className="row">
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Full Name</label>
                                                <input type="text" className="form-control" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter Full Name"/>
                                            </div>
                                            {validation.name && (
                                                <div className="alert alert-danger">
                                                    {validation.name[0]}
                                                </div>
                                            )}
                                        </div>
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Email Address</label>
                                                <input type="text" className="form-control" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Enter Email Address"/>
                                            </div>
                                            {validation.email && (
                                                <div className="alert alert-danger">
                                                    {validation.email[0]}
                                                </div>
                                            )}
                                        </div>
                                    </div>

                                    <div className="row">
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Password</label>
                                                <input type="text" className="form-control" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Enter Password"/>
                                            </div>
                                            {validation.password && (
                                                <div className="alert alert-danger">
                                                    {validation.password[0]}
                                                </div>
                                            )}
                                        </div>
                                        <div className="col-md-6">
                                            <div className="mb-3">
                                                <label className="form-label fw-bold">Password Confirmation</label>
                                                <input type="text" className="form-control" value={passwordConfirmation} onChange={(e) => setPasswordConfirmation(e.target.value)} placeholder="Enter Password Confirmation"/>
                                            </div>
                                        </div>
                                    </div>

                                    <div>
                                        <button type="submit" className="btn btn-md btn-success me-2"><i className="fa fa-save"></i> UPDATE</button>
                                        <button type="reset" className="btn btn-md btn-warning"><i className="fa fa-redo"></i> RESET</button>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
              </div>
            </LayoutAdmin>
        </React.Fragment>
    );
}

export default UserEdit;

Dari perubahan kode di atas, pertama-tama kita import hook dari React, yaitu useState dan useEffect.

//import hook useState from react
import React, { useState, useEffect } from "react";

Setelah itu kita import layout admin, karena kita akan menggunakan layout tersebut untuk membungkus kode yang akan kita tulis.

//import layout
import LayoutAdmin from "../../../layouts/Admin";

Dan karena akan melakukan HTTP request, maka kita perlu mengimport konfigurasi global API yang sudah pernah kita buat sebelumnya.

//import BASE URL API
import Api from "../../../api";

Kemudian kita import 2 hook dari React Router DOM, yaitu useHistory yang mana digunakan untuk melakukan navigate ke halaman-halaman lain dan useParams digunakan untuk mengambil sebuah data parameter dari URL.

//import hook navigate dari react router dom
import { useNavigate, useParams } from "react-router-dom";

Dan karena data token berada di dalam cookies browser, maka kita butuh package tambahan yang bernama JS Cookie untuk mempermudah kita dalam mengelola token.

//import js cookie
import Cookies from "js-cookie";

Terkahir, kita import package yang bernama React Hot Toast. Dimana package tersebut akan kita gunakan untuk menampilkan notifikasi ketika sukses melakukan proses update data.

//import toats
import toast from "react-hot-toast";

Di dalam function component UserEdit, pertama-tama kita atur title untuk halamannya terlebih dahulu.

//title page
document.title = "Edit User - Administrator Travel GIS";

Setelah itu, kita buat beberapa state yang nanti digunakan untuk menampung data yang diinputkan di dalam form.

//state
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [passwordConfirmation, setPasswordConfirmation] = useState("");

Kemudian buat satu state lagi dengan nama validation. State tersebut akan kita gunakan untuk menyimpan error response validasi yang di dapatkan dari Laravel.

//state validation
const [validation, setValidation] = useState({});

Kemudian kita buat variable baru dengan nama token, dimana variable tersebut berisi token yang diambil dari cookies browser.

//token
const token = Cookies.get("token");

Untuk mempermudah dalam menggunakan hook useHistory, maka kita perlu memasukkannya ke dalam sebuah variable.

//navigate
const navigate = useNavigate();

Setelah itu, kita buat variable dengan jenis object yang bernama id dan isinya adalah hook useParams dari React Router DOM. Variable ini akan digunakan untuk mengambil nilai ID yang ada di URL browser.

//get ID from parameter URL
const { id } = useParams();

Kemudian kita buat function yang bernama getUserById. Function ini akan kita gunakan untuk mendapatkan data user berdasarkan ID dari server dan data tersebut akan kita parsing ke dalam form untuk di edit.

//function "getUserById"
const getUserById = async () => {

	//...
	
}

Di dalam function getUserById kita melakukan fetching ke dalam endpoint /api/admin/users/:id dengan method GET.

//get data from server
const response = await Api.get(`/api/admin/users/${id}`, {

    //header
    headers: {
        //header Bearer + Token
        Authorization: `Bearer ${token}`,
    }
});

Jika proses fetching berhasil dilakukan, maka kita akan assign response data-nya ke dalam state name dan email.

//get response data
const data = await response.data.data

//assign data to state "name"
setName(data.name);
//assign data to state "email"
setEmail(data.email);

Setelah itu, agar function getUserById dapat dijalankan pertama kali, maka kita perlu memanggilnya di dalam hook useEffect.

//hook useEffect
useEffect(() => {

    //panggil function "getUserById"
    getUserById();

    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Kemudian kita buat function lagi dengan nama updateUser. Function tersebut akan digunakan untuk melakukan proses update data user ke dalam server. Dan function tersebut akan dijalankan ketika form disubmit.

<form onSubmit={updateUser}>

	//...
	
</form>
//function "updateUser"
const updateUser = async (e) => {

	//...
	
}

Di dalam function updateUser, pertama-tama kita melakukan inisialisasi formData. Tujuannya untuk mempermudah kita dalam mengelompokan data dan mengirimkannya ke dalam server.

//define formData
const formData = new FormData();

Setelah berhasil melakukan inisialisasi, selanjutnya kita lakukan append data yang ada di dalam state ke dalam formData.

//append data to "formData"
formData.append('name', name);
formData.append('email', email);
formData.append('password', password);
formData.append('password_confirmation', passwordConfirmation);
formData.append('_method', 'PATCH');

Kemudian kita kirimkan formData tersebut ke dalam server menggunakan endpoint /api/admin/users/:id dengan method POST.

await Api.post(`/api/admin/users/${id}`, formData, {

    //header
    headers: {
        //header Bearer + Token
        Authorization: `Bearer ${token}`,
    }

})

Jika proses update data di dalam server berhasil dilakukan, maka kita akan menampilkan notifikasi menggunakan React Hot Toast.

//show toast
toast.success("Data Updated Successfully!", {
    duration: 4000,
    position: "top-right",
    style: {
        borderRadius: '10px',
        background: '#333',
        color: '#fff',
    },
});

Dan kita arahkan ke dalam halaman users index.

//redirect dashboard page
navigate("/admin/users");

Tapi, jika data gagal diupdate, maka akan melakukan assign response error validasi ke dalam state validation.

//set state "validation"
setValidation(error.response.data);

Langkah 3 - Uji Coba Proses Edit dan Update User

Sekarang silahkan klik button edit di salah satu data user. Jika berhasil maka kita akan menampilkan halaman edit user seperti berikut ini :

Silahkan disesuikan data yang ingin diupdate dan jika sudah silahkan klik button UPDATE. Jika berhasil maka kita akan diarahkan ke halaman users index dengan data yang telah diperbarui dan menampilkan notifikasi sukses update data.

Beranda Mundur Maju