Panduan Membuat Template Website Desa di React + Vite

1. Persiapan Awal

Pastikan kamu sudah menginstal Node.js dan npm (Node Package Manager) di komputer. Untuk memeriksa, jalankan perintah berikut di terminal:

a. periksa Node.js

node -v

b. Periksa npm

npm -v

Jika perintah tersebut tidak mengembalikan versi dari Node.js dan npm, itu berarti Node.js belum terinstal di komputer kamu. Silakan unduh dan instal dari situs resmi:

Ikuti panduan instalasi sesuai dengan sistem operasi kamu (Windows, macOS, atau Linux).

2. Membuat Proyek Website desa menggunakan react + vite

Jika sudah, kamu bisa membuat proyek React baru menggunakan Vite dengan perintah berikut:

# Membuat project Vite baru dengan template React
npm create vite@latest desa-app --template react

# Masuk ke direktori project
cd desa-app

# Menginstal dependensi
npm install

# Menjalankan server development
npm run dev
    

Perintah ini akan membuat proyek baru bernama desa-app dan membuka proyek tersebut di browser secara otomatis.

3. Menginstal Axios

Untuk mengakses API, kita akan menggunakan library Axios. Kamu bisa menginstalnya dengan perintah berikut:

npm install axios

4. Buat Folder Components, Helpers, Hooks, Pages di dalam src

Struktur folder sebaiknya seperti ini:

src/
├── components/
├── helpers/
├── hooks/
└── pages/

5. Buat file .env

Untuk mengakses API, kita akan host api di dalam file .env diluar folder src. Buat file ini di root proyek dan tambahkan URL API di dalamnya, misalnya:

REACT_APP_API_URL=https://https://api-kawal-desa.lskk.co.id

6. Di dalam Folder Components, buatkan file komponen

Di dalam folder src/components, kamu dapat membuat beberapa folder komponen sesuai dengan fungsinya. Berikut adalah beberapa komponen yang bisa kamu buat:

7. Kemudian pada Folder Helpers, buatkan file ini

a. api.js

Pada file ini, buatlah konfigurasi dasar untuk API menggunakan Axios:

import axios from 'axios';
        
const baseURL = import.meta.env.VITE_API_BASE_URL;

const API = axios.create({
  baseURL,
  headers: {
    'Content-Type': 'application/json',
  },
});

export default API;

b. service.js (Pilih antara b atau c)

Pada file ini, buatkan untuk konfigurasi dasar untuk API berdasarkan guid yang ada pada url website menggunakan Axios:


import API from "./api";

const getGUIDFromURL = () => {
  const params = new URLSearchParams(window.location.search);
  return params.get('guid');
};

export const fetchVillageProfile = async () => {
  const guid = getGUIDFromURL(); 
  if (!guid) {
    throw new Error("GUID tidak ditemukan");
  }

  try {
    const response = await API.get(`/api/village/profile`, {
      params: { guid },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village profile:', error);
    throw error;
  }
};

export const fetchVillageUMKM = async (page = 1, limit = 10) => {
  const guid = getGUIDFromURL();
  if (!guid) throw new Error("GUID tidak ditemukan di URL");

  try {
    const response = await API.get(`/api/umkm/village`, {
      params: { guid, page, limit },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village UMKM:', error);
    throw error;
  }
};

export const fetchVillageNews = async (page = 1, limit = 10) => {
  const guid = getGUIDFromURL();
  if (!guid) throw new Error("GUID tidak ditemukan di URL");

  try {
    const response = await API.get(`/api/report/village`, {
      params: { guid, page, limit, type: 'news' },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village news:', error);
    throw error;
  }
};

export const fetchVillageActivities = async (page = 1, limit = 10) => {
  const guid = getGUIDFromURL();
  if (!guid) throw new Error("GUID tidak ditemukan di URL");

  try {
    const response = await API.get(`/api/report/village`, {
      params: { guid, page, limit, type: 'activity' },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village activities:', error);
    throw error;
  }
};

export const fetchVillageAssets = async (page = 1, limit = 10) => {
  const guid = getGUIDFromURL();
  if (!guid) throw new Error("GUID tidak ditemukan di URL");

  try {
    const response = await API.get(`/api/report/village`, {
      params: { guid, page, limit, type: 'asset' },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village assets:', error);
    throw error;
  }
};
    

c. service.js (Pilih antara b atau c)

Pada file ini, kita akan membuat beberapa fungsi untuk mengambil data dari berdasarkan GUID yang sudah dideklarasikan untuk memakai API:

import API from './api';

// GUID Desa
const GUID = '2305e536-0ef6-48e7-9aac-79ca236433fa';

// Ambil data profile desa
export const fetchVillageProfile = async () => {
  try {
    const response = await API.get('/api/village/profile', {
      params: { guid: GUID },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village profile:', error);
    throw error;
  }
};

// Ambil data UMKM Desa
export const fetchVillageUMKM = async (page = 1, limit = 10) => {
  try {
    const response = await API.get('/api/umkm/village', {
      params: { guid: GUID, page, limit },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village UMKM:', error);
    throw error;
  }
};

// Ambil data berita desa
export const fetchVillageNews = async (page = 1, limit = 10) => {
  try {
    const response = await API.get('/api/report/village', {
      params: { guid: GUID, page, limit, type: 'news' },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village news:', error);
    throw error;
  }
};

// Ambil data Aktifitas desa
export const fetchVillageActivities = async (page = 1, limit = 10) => {
  try {
    const response = await API.get('/api/report/village', {
      params: { guid: GUID, page, limit, type: 'activity' },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village activities:', error);
    throw error;
  }
};

// Ambil data aset desa
export const fetchVillageAssets = async (page = 1, limit = 10) => {
  try {
    const response = await API.get('/api/report/village', {
      params: { guid: GUID, page, limit, type: 'asset' },
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching village assets:', error);
    throw error;
  }
};

8. Lalu, didalam folder hooks

a. useApi.js

isikan ini


import { useState, useEffect } from 'react';
import { fetchVillageActivities, fetchVillageAssets, fetchVillageNews, fetchVillageProfile, fetchVillageUMKM } from '../helpers/service';

export const useVillageAssets = (page = 1, limit = 10) => {
    const [assets, setAssets] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadAssets = async () => {
            try {
                const data = await fetchVillageAssets(page, limit);
                setAssets(data);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        };

        loadAssets();
    }, [page, limit]);

    return { assets, loading, error };
};

export const useVillageActivities = (page = 1, limit = 10) => {
    const [activities, setActivities] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadActivities = async () => {
            try {
                const data = await fetchVillageActivities(page, limit);
                setActivities(data);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        };

        loadActivities();
    }, [page, limit]);

    return { activities, loading, error };
};

export const useVillageUMKM = (page = 1, limit = 10) => {
    const [umkm, setUMKM] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadUMKM = async () => {
            try {
                const data = await fetchVillageUMKM(page, limit);
                setUMKM(data);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        };

        loadUMKM();
    }, [page, limit]);

    return { umkm, loading, error };
};

export const useVillageNews = (page = 1, limit = 10) => {
    const [news, setNews] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadNews = async () => {
            try {
                const data = await fetchVillageNews(page, limit);
                setNews(data);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        };

        loadNews();
    }, [page, limit]);

    return { news, loading, error };
};

export const useVillageProfile = () => {
    const [profile, setProfile] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadProfile = async () => {
            try {
                const data = await fetchVillageProfile();
                setProfile(data);
            } catch (err) {
                setError(err);
            } finally {
                setLoading(false);
            }
        };

        loadProfile();
    }, []);

    return { profile, loading, error };
};

9. Penggunaan dalam Komponen

Setelah semua siap, kamu bisa menggunakan hooks yang sudah dibuat dalam komponen React-mu. Berikut adalah contoh penggunaannya dalam komponen:

import React from 'react';
import { useVillageProfile, useVillageUMKM } from '../hooks/useApi';

const VillageProfile = () => {
    const { profile, loading, error } = useVillageProfile();

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return (
        <div>
            <h1>Profil Desa</h1>
            <p>Nama: {profile.name}</p>
            <p>Deskripsi: {profile.description}</p>
        </div>
    );
};

export default VillageProfile;

10. Cara Menggunakan Funcition Map di Proyek React

Dalam React, kamu dapat menggunakan fungsi map untuk membuat elemen secara dinamis berdasarkan array data. Berikut adalah contoh penggunaannya untuk menampilkan daftar UMKM:


import React from 'react';
import { useVillageUMKM } from '../hooks/useApi';

const VillageUMKMList = () => {
    const { umkm, loading, error } = useVillageUMKM();
    const umkmList = umkm || []; // Pastikan umkmList adalah array

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return (
        <div>
            <h1>Daftar UMKM Desa</h1>
            {umkmList.length > 0 ? ( // Cek jika ada data
                <ul>
                    {umkmList.map((umkm) => (
                        <li key={umkm.id}>
                            <h2>{umkm.name}</h2>
                            <p>{umkm.description}</p>
                        </li>
                    ))}
                </ul>
            ) : (
                <p>Belum ada UMKM yang terdaftar.</p>
            )}
        </div>
    );
};

export default VillageUMKMList;
    

11. Styling dengan Tailwind CSS atau Framework CSS Lain

Setelah komponen berhasil dibuat dan berfungsi, kamu bisa menambahkan styling agar tampilannya lebih menarik. Kamu dapat menggunakan Tailwind CSS, framework CSS lainnya, atau bahkan CSS kustom sesuai preferensi.


import React from 'react';
import { useVillageUMKM } from '../hooks/useApi';

const VillageUMKMList = () => {
    const { umkm, loading, error } = useVillageUMKM();
    const umkmList = umkm || []; // Pastikan umkmList adalah array

    if (loading) return <div className="text-center text-gray-500">Loading...</div>;
    if (error) return <div className="text-center text-red-500">Error: {error.message}</div>;

    return (
        <div className="max-w-4xl mx-auto p-4">
            <h1 className="text-2xl font-bold text-center mb-4">Daftar UMKM Desa</h1>
            {umkmList.length > 0 ? ( // Cek jika ada data
                <ul className="grid grid-cols-1 sm:grid-cols-2 gap-4">
                    {umkmList.map((umkm) => (
                        <li key={umkm.id} className="border p-4 rounded-lg shadow">
                            <h2 className="text-lg font-semibold">{umkm.name}</h2>
                            <p className="text-gray-600">{umkm.description}</p>
                        </li>
                    ))}
                </ul>
            ) : (
                <p className="text-center text-gray-500">Belum ada UMKM yang terdaftar.</p>
            )}
        </div>
    );
};

export default VillageUMKMList;
    

Code diatas adalah contoh penerapan styling menggunakan Tailwind CSS, selanjutnya kamu bisa menerapkan styling sesuai dengan keinginan kamu sendiri.

12. Menjalankan Aplikasi

Setelah semua langkah selesai, jalankan aplikasi dengan perintah:

npm run dev

Kesimpulan

Dengan mengikuti panduan ini, kamu dapat mengintegrasikan API desa dengan projek React-mu. Pastikan untuk mengatur endpoint API dan menyesuaikan styling tampilan data sesuai kebutuhan website desa yang kamu buat.

Hapy Hacking!