Studi Kasus #13: Aplikasi Wisata Indonesia – Membuat Halaman Beranda (Daftar Wisata)


Studi Kasus #13: Aplikasi Wisata Indonesia – Membuat Halaman Beranda (Daftar Wisata)

Halo, calon full-stack developer!

Di Studi Kasus #12, kita berhasil menghubungkan frontend dengan backend dan mengambil data dari API, meski baru sebatas menampilkannya di console. Sekarang saatnya kita menampilkan data tersebut secara visual di halaman beranda. Kita akan membuat halaman daftar tempat wisata yang menampilkan setiap destinasi dalam bentuk kartu (card) yang menarik.

Fitur yang akan dibuat:

  • Mengambil data destinasi dari endpoint /api/destinations menggunakan service yang sudah dibuat.
  • Menampilkan loading state saat data sedang diambil.
  • Menampilkan error state jika terjadi kesalahan.
  • Menampilkan daftar destinasi dalam bentuk grid kartu.
  • Setiap kartu berisi: foto cover, nama, lokasi, dan deskripsi singkat.
  • Menambahkan CSS sederhana agar tampilan rapi.

Mari kita mulai!

Langkah 1: Menyiapkan Service Destinasi

Kita sudah membuat src/services/destinationService.js di tutorial sebelumnya. Pastikan isinya seperti ini:

import API from './api';

export const getDestinations = async () => {
  try {
    const response = await API.get('/destinations');
    return response.data;
  } catch (error) {
    console.error('Gagal mengambil destinasi:', error);
    throw error;
  }
};

export const getDestinationById = async (id) => {
  try {
    const response = await API.get(`/destinations/${id}`);
    return response.data;
  } catch (error) {
    console.error('Gagal mengambil destinasi:', error);
    throw error;
  }
};

Jika belum, buat file tersebut.

Langkah 2: Membuat Komponen Card Destinasi

Kita akan membuat komponen khusus untuk menampilkan satu kartu destinasi. Buat file src/components/DestinationCard.jsx:

import React from 'react';
import './DestinationCard.css'; // kita akan buat CSS nya nanti

const DestinationCard = ({ destination }) => {
  // Base URL untuk gambar (backend menyajikan di /uploads)
  const imageUrl = destination.cover_image 
    ? `http://localhost:3000/uploads/${destination.cover_image}`
    : 'https://via.placeholder.com/300x200?text=No+Image';

  // Potong deskripsi jika terlalu panjang
  const shortDescription = destination.description 
    ? destination.description.substring(0, 100) + '...'
    : 'Tidak ada deskripsi';

  return (
    <div className="destination-card">
      <img src={imageUrl} alt={destination.name} className="card-image" />
      <div className="card-content">
        <h3>{destination.name}</h3>
        <p className="location">📍 {destination.location}</p>
        <p className="description">{shortDescription}</p>
      </div>
    </div>
  );
};

export default DestinationCard;

Penjelasan:

  • Komponen menerima prop destination berisi data satu destinasi.
  • imageUrl dibentuk dari URL backend. Jika tidak ada cover, gunakan placeholder.
  • Deskripsi dipotong agar tidak terlalu panjang di card.

Sekarang buat file CSS src/components/DestinationCard.css:

.destination-card {
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  transition: transform 0.2s;
  background: white;
}

.destination-card:hover {
  transform: scale(1.02);
  box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}

.card-image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card-content {
  padding: 15px;
}

.card-content h3 {
  margin: 0 0 10px 0;
  color: #333;
}

.location {
  color: #666;
  font-size: 0.9em;
  margin: 5px 0;
}

.description {
  color: #777;
  font-size: 0.9em;
  line-height: 1.4;
}

Langkah 3: Membuat Halaman Beranda (Home)

Sekarang kita ubah src/pages/Home.jsx untuk menampilkan daftar destinasi. Gunakan state untuk menyimpan data, loading, dan error.

import React, { useState, useEffect } from 'react';
import { getDestinations } from '../services/destinationService';
import DestinationCard from '../components/DestinationCard';
import './Home.css'; // CSS untuk halaman

const Home = () => {
  const [destinations, setDestinations] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchDestinations = async () => {
      try {
        const data = await getDestinations();
        setDestinations(data);
      } catch (err) {
        setError('Gagal mengambil data wisata. Silakan coba lagi.');
      } finally {
        setLoading(false);
      }
    };

    fetchDestinations();
  }, []);

  if (loading) {
    return <div className="loading">Memuat data...</div>;
  }

  if (error) {
    return <div className="error">{error}</div>;
  }

  return (
    <div className="home">
      <h1>Destinasi Wisata Indonesia</h1>
      {destinations.length === 0 ? (
        <p>Belum ada destinasi wisata.</p>
      ) : (
        <div className="destination-grid">
          {destinations.map((dest) => (
            <DestinationCard key={dest.id} destination={dest} />
          ))}
        </div>
      )}
    </div>
  );
};

export default Home;

Penjelasan:

  • useState untuk menyimpan data, loading, dan error.
  • useEffect memanggil API saat komponen dimuat.
  • Menampilkan loading atau error jika diperlukan.
  • Jika data ada, kita render grid dengan memetakan setiap destinasi ke komponen DestinationCard.

Buat file CSS src/pages/Home.css:

.home {
  padding: 20px;
}

.home h1 {
  text-align: center;
  margin-bottom: 30px;
  color: #2c3e50;
}

.destination-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}

.loading, .error {
  text-align: center;
  margin-top: 50px;
  font-size: 1.2em;
}

.error {
  color: #e74c3c;
}

Langkah 4: Menyesuaikan URL Gambar di Environment

URL backend saat ini hardcode http://localhost:3000. Sebaiknya kita simpan di environment variable agar mudah diganti saat production. Buat file .env di root folder frontend:

VITE_API_URL=http://localhost:3000/api
VITE_UPLOAD_URL=http://localhost:3000/uploads

Kemudian di src/services/api.js, gunakan:

const API = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
});

Di komponen DestinationCard, kita bisa gunakan:

const imageUrl = destination.cover_image 
  ? `${import.meta.env.VITE_UPLOAD_URL}/${destination.cover_image}`
  : 'https://via.placeholder.com/300x200?text=No+Image';

Jangan lupa untuk merestart dev server setelah menambah .env.

Langkah 5: Menguji Halaman Beranda

Pastikan backend berjalan. Jalankan frontend dengan npm run dev. Buka browser ke http://localhost:5173. Seharusnya halaman beranda menampilkan daftar destinasi dalam bentuk kartu yang rapi. Jika belum ada data, pastikan backend sudah memiliki data destinasi (bisa ditambahkan melalui Postman).

Jika terjadi error, buka console browser untuk melihat pesan error. Pastikan CORS sudah diatur di backend dan URL environment benar.

💡 Tips: Jika gambar tidak muncul, periksa apakah file benar-benar ada di folder backend/uploads dan apakah URL sudah benar. Kamu bisa langsung mengakses gambar via browser untuk mengetes.

Langkah 6: Menambahkan Link ke Halaman Detail

Nantinya, setiap kartu akan bisa diklik menuju halaman detail destinasi. Untuk itu, kita bungkus kartu dengan Link dari react-router-dom. Ubah DestinationCard.jsx:

import { Link } from 'react-router-dom';
// ... di dalam return
<Link to={`/destination/${destination.id}`} className="card-link">
  <div className="destination-card">
    {/* ... isi kartu ... */}
  </div>
</Link>

Tambahkan CSS untuk menghilangkan underline dan mempertahankan warna:

.card-link {
  text-decoration: none;
  color: inherit;
}

Sekarang kartu dapat diklik menuju halaman detail (yang akan kita buat di tutorial selanjutnya).

Penanganan Error dan Loading

Kita sudah menambahkan state loading dan error. Ini penting untuk pengalaman pengguna yang baik. Jangan lupa untuk menguji dengan mematikan backend sejenak untuk melihat tampilan error.

Langkah Selanjutnya

Di Studi Kasus #14, kita akan membuat halaman detail destinasi yang menampilkan informasi lengkap: galeri foto, fasilitas, dan paket harga. Kita juga akan menggunakan parameter ID dari URL.

Pastikan halaman beranda sudah berfungsi dengan baik. Jika ada kendala, periksa kembali kode dan pastikan API merespons dengan benar.

Sampai jumpa di tutorial berikutnya! 👋😊


Ditulis dengan ❤️ untuk para pengembang yang ingin membangun aplikasi wisata.

Lebih baru Lebih lama

نموذج الاتصال