Perjalanan membuat app dengan Next Js

Galih Setiawan Nurohim
8 min readNov 5, 2024

--

joyoalkes tabung oksigen

Saya adalah seorang dosen yang mengajar data science dan pengembangan aplikasi web berbasis PHP. Dalam aktivitas akademis, saya terbiasa membimbing mahasiswa memahami konsep data, analisis, hingga pengembangan aplikasi. Namun, di luar lingkungan akademis, keluarga saya memiliki bisnis yang cukup unik dan penting, yaitu pengelolaan tabung gas, termasuk oksigen, asetilena, dan nitrogen. Bisnis ini memiliki tantangan tersendiri, terutama dalam hal pengelolaan stok, pengisian, dan pengantaran tabung-tabung tersebut ke berbagai pelanggan. Karena itulah, saya merasa perlu untuk membangun sebuah aplikasi manajemen tabung yang membantu operasional bisnis ini berjalan lebih lancar dan efisien.

Mengapa Memilih Next.js untuk Proyek Ini?

Meskipun berpengalaman dalam pengembangan aplikasi web menggunakan Laravel, saya memilih Next.js untuk aplikasi manajemen ini. Ada beberapa alasan utama yang membuat saya tertarik mencoba Next.js, antara lain:

  1. Eksplorasi Teknologi Baru: Sebagai pengajar, saya ingin terus memperbarui keterampilan dan pengetahuan dalam pengembangan web. Next.js, dengan semua fitur modernnya, merupakan kesempatan menarik bagi saya untuk mempelajari lebih dalam ekosistem React dan cara pengembangannya yang lebih dinamis dan terstruktur.
  2. Reaktivitas yang Cepat dan Efisien: Next.js dikenal dengan reaktivitasnya yang cepat dan sangat responsif. Framework ini memanfaatkan server-side rendering (SSR) dan client-side rendering (CSR) yang optimal, sehingga memungkinkan data tampil lebih cepat sesuai kebutuhan pengguna. Untuk aplikasi manajemen seperti ini, di mana data stok, lokasi, dan status tabung perlu di-update secara real-time, kemampuan Next.js dalam mengelola komponen yang responsif memberikan kelebihan yang tidak bisa saya dapatkan di Laravel.

Dan inilah susunan folder dan file

app
├───auth # Halaman terkait autentikasi
│ ├───edit
│ │ page.js # Halaman untuk mengedit profil pengguna
│ ├───login
│ │ page.js # Halaman login untuk autentikasi pengguna
│ ├───register
│ │ page.js # Halaman registrasi untuk pengguna baru
│ └───user-management
│ page.js # Dashboard manajemen pengguna untuk admin
├───components # Komponen yang dapat digunakan ulang di seluruh aplikasi
│ formatDate.js # Fungsi utilitas untuk memformat tanggal secara konsisten
│ Layout.tsx # Komponen layout utama untuk struktur UI yang konsisten
│ MapComponent.js # Komponen peta untuk menampilkan data lokasi
├───laporan # Halaman laporan
│ page.js # Halaman utama untuk melihat laporan
├───laporan-stok # Halaman laporan stok
│ page.js # Halaman untuk melihat laporan stok tabung
├───pengantaran # Halaman terkait pengantaran
│ │ page.js # Halaman utama untuk mengelola pengantaran
│ ├───create
│ │ CreateTabung.js # Komponen untuk membuat entri pengantaran baru
│ │ page.js # Halaman untuk memulai pengantaran baru
│ ├───scanner
│ │ page.js # Halaman untuk memindai barcode saat pengantaran
│ └───[id] # Halaman dinamis untuk entri pengantaran spesifik
│ page.js # Halaman untuk melihat atau mengedit pengantaran spesifik
├───pengisian # Halaman terkait pengisian ulang
│ │ page.js # Halaman utama untuk mengelola pengisian tabung
│ ├───create
│ │ CreateTabung.js # Komponen untuk membuat entri pengisian baru
│ │ page.js # Halaman untuk memulai pengisian baru
│ └───[id] # Halaman dinamis untuk entri pengisian spesifik
│ page.js # Halaman untuk melihat atau mengedit pengisian spesifik
└───tabung # Halaman manajemen tabung
│ page.js # Halaman utama untuk melihat dan mengelola tabung
├───create
│ CreateTabung.js # Komponen untuk menambah tabung baru
│ page.js # Halaman untuk membuat entri tabung baru
├───scanner
│ page.js # Halaman untuk memindai barcode untuk identifikasi tabung
└───[id] # Halaman dinamis untuk entri tabung spesifik
page.js # Halaman untuk melihat atau mengedit tabung spesifik

Struktur dan Fitur Aplikasi Manajemen Tabung

Dalam pengembangan aplikasi ini, saya merancang struktur proyek dengan fokus pada kebutuhan operasional bisnis. Ada beberapa halaman dan komponen utama yang saya bangun untuk memastikan aplikasi ini memenuhi semua aspek manajemen tabung, antara lain:

  1. Autentikasi Pengguna: Di dalam app/auth, saya membangun autentikasi pengguna sehingga hanya mereka yang berwenang yang bisa mengakses data dan fitur aplikasi. Ini sangat penting untuk memastikan data tabung, pengantaran, dan stok hanya dikelola oleh orang-orang yang terpercaya.
  2. Komponen Format Tanggal: Saya menambahkan komponen formatdate.js untuk membantu format tanggal yang konsisten di seluruh aplikasi. Dengan ini, setiap catatan terkait tabung, pengisian, dan pengantaran bisa dicatat dan ditampilkan dengan format tanggal yang mudah dipahami.
  3. Layout dan Peta Lokasi Tabung: layout.tsx bertindak sebagai struktur dasar dari aplikasi yang menyediakan navigasi dan antarmuka konsisten di setiap halaman. Saya juga menambahkan mapcomponent.js untuk menunjukkan lokasi tabung secara visual, sehingga tim bisa melihat apakah tabung berada di lokasi "Joyo" atau "Pelanggan". Ini sangat membantu dalam melacak tabung yang ada di berbagai lokasi dan memastikan penempatan yang tepat sesuai kebutuhan.
  4. Halaman Laporan dan Laporan Stok: Di aplikasi ini, saya membuat halaman laporan untuk melacak status dan stok tabung:
  • app/laporan/page.js menyediakan laporan menyeluruh dari semua tabung, termasuk jenis, lokasi, dan status (isi atau kosong).
  • app/laporan-stok/page.js fokus pada stok dan menampilkan ringkasan jumlah tabung berdasarkan lokasi dan status. Ini membantu keluarga saya mendapatkan informasi cepat tentang stok tabung yang tersedia.
  1. Pengantaran Tabung: Pengantaran merupakan bagian penting dari bisnis kami, sehingga saya membuat halaman app/pengantaran/[id]/create untuk mencatat setiap pengantaran baru berdasarkan ID yang unik. Di sini, pengguna bisa menambahkan data pengiriman untuk memastikan tabung yang benar dikirim ke lokasi yang tepat. Selain itu, saya menambahkan scanner page.js untuk memungkinkan tim memindai barcode pada tabung saat melakukan pengiriman. Ini mencegah kesalahan pengiriman dan memastikan tabung yang tepat sampai ke pelanggan.
  2. Pengisian Tabung: Setiap tabung yang kosong harus diisi ulang sebelum bisa digunakan lagi. Oleh karena itu, saya membuat app/pengisian/[id]/create untuk mencatat pengisian tabung. Halaman ini berfungsi untuk mencatat status tabung yang diisi dan lokasi pengisian. Fitur scanner page.js juga tersedia di sini untuk memastikan setiap tabung yang diisi sudah teridentifikasi dan dicatat dengan benar.
  3. Manajemen Tabung: Halaman app/tabung/[id]/create berfungsi untuk menambah tabung baru atau memperbarui informasi tabung yang sudah ada. Dengan fitur ini, saya bisa mencatat jenis, status, dan lokasi tabung dengan mudah. Saya juga menggunakan scanner di halaman ini untuk membantu pencarian tabung melalui barcode, sehingga tabung bisa diidentifikasi dengan cepat.

Integrasi dengan Supabase

Supabase menjadi pilihan saya untuk database aplikasi ini. Platform ini mudah diintegrasikan dengan Next.js dan menyediakan layanan yang cocok untuk aplikasi yang membutuhkan akses data real-time. Supabase memungkinkan saya mengelola data secara efektif, mulai dari stok tabung, status pengisian, hingga lokasi pengiriman.

Di dalam file TabungIndex, saya menggunakan Supabase sebagai berikut:

"use client";
import React, { useEffect, useState, useCallback } from 'react';
import Link from 'next/link';
import { supabase } from '/supabaseClient';
import Layout from '../components/Layout';

Dalam komponen ini, saya mendefinisikan berbagai state seperti tabungList, tabungCount, jenisTabungList, lokasiList, dan lainnya. Menggunakan useCallback, saya mendefinisikan fetchTabungData untuk mengambil data dari tabel tabung di Supabase, kemudian mengurutkannya berdasarkan jenis tabung dan menyediakan filter berdasarkan selectedJenisTabung dan selectedLokasi. Dengan ini, pengguna dapat mencari tabung berdasarkan jenis, barcode, atau lokasi dengan mudah.

Saya juga menambahkan fitur infinite scrolling yang memungkinkan data tambahan dimuat ketika pengguna menggulir ke bawah halaman. handleScroll memantau posisi scroll dan, jika pengguna berada dekat bagian bawah, akan otomatis memuat data pada halaman berikutnya.

  1. Inisialisasi State dan Hook di Komponen : Pertama, kita mendefinisikan state untuk menyimpan data dan filter:
import React, { useEffect, useState, useCallback } from 'react';
import Link from 'next/link';
import { supabase } from '/supabaseClient';

export default function TabungIndex() {
const [tabungList, setTabungList] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [currentPage, setCurrentPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const [selectedJenisTabung, setSelectedJenisTabung] = useState("Semua");
const [selectedLokasi, setSelectedLokasi] = useState("Semua");
}

2. Fungsi Fetch Data dari Supabase : Fungsi ini mengambil data tabung dari Supabase, dengan mendukung filter berdasarkan jenis tabung dan lokasi. Pagination juga diatur di sini:

const fetchTabungData = useCallback(async (page = 1, reset = false) => {
// Menandakan bahwa data sedang dalam proses pengambilan
setLoading(true);

// Menentukan rentang data berdasarkan halaman yang diminta
// Jika halaman pertama, `rangeStart` = 0 dan `rangeEnd` = 9
// Artinya, mengambil 10 item per halaman
const rangeStart = (page - 1) * 10;
const rangeEnd = rangeStart + 9;

// Memulai query ke tabel 'tabung' di Supabase
// Mengambil semua kolom dengan `select('*')`
// Menggunakan `range` untuk membatasi rentang data berdasarkan `rangeStart` dan `rangeEnd`
let query = supabase
.from('tabung')
.select('*')
.range(rangeStart, rangeEnd);

// Menambahkan filter berdasarkan `jenis_tabung` jika dipilih (bukan 'Semua')
if (selectedJenisTabung !== "Semua") {
query = query.eq("jenis_tabung", selectedJenisTabung);
}

// Menambahkan filter berdasarkan `lokasi` jika dipilih (bukan 'Semua')
if (selectedLokasi !== "Semua") {
query = query.eq("lokasi", selectedLokasi);
}

// Menjalankan query ke Supabase dan menunggu hasilnya
const { data } = await query;

// Jika `reset` bernilai `true`, mengganti `tabungList` dengan data baru
// Jika `reset` bernilai `false`, menambahkan data baru ke `tabungList` yang sudah ada
setTabungList(reset ? data : [...tabungList, ...data]);

// Mengatur apakah masih ada data yang dapat diambil untuk pagination
// Jika data yang diambil kurang dari 10, `setHasMore` akan bernilai `false`
setHasMore(data.length === 10);

// Menandakan bahwa proses pengambilan data telah selesai
setLoading(false);

// Mendefinisikan dependensi useCallback agar fungsi ini hanya dibuat ulang ketika `selectedJenisTabung`, `selectedLokasi`, atau `currentPage` berubah
}, [selectedJenisTabung, selectedLokasi, currentPage]);

3. Mengambil Data Saat Komponen Dimuat dan Filter Berubah

Gunakan useEffect untuk memuat data saat komponen pertama kali dimuat atau ketika filter diubah:

useEffect(() => {
fetchTabungData(1, true);
}, [selectedJenisTabung, selectedLokasi]);

4. Render Tabel dan Tombol Filter : Tambahkan UI untuk tabel daftar tabung serta input pencarian dan dropdown filter:

return (
<div>
<input
type="text"
placeholder="Cari tabung..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<select value={selectedJenisTabung} onChange={(e) => setSelectedJenisTabung(e.target.value)}>
<option value="Semua">Semua Jenis Tabung</option>
{/* Isi opsi jenis tabung */}
</select>
<select value={selectedLokasi} onChange={(e) => setSelectedLokasi(e.target.value)}>
<option value="Semua">Semua Lokasi</option>
{/* Isi opsi lokasi */}
</select>

<table>
<tbody>
{tabungList.filter(tabung =>
tabung.jenis_tabung.includes(searchTerm) ||
tabung.lokasi.includes(searchTerm)
).map(tabung => (
<tr key={tabung.id}>
<td>{tabung.barcode} - {tabung.jenis_tabung}</td>
<td>{tabung.status}</td>
<td>{tabung.lokasi}</td>
</tr>
))}
</tbody>
</table>
</div>
);

5. Menambahkan Pagination Infinite Scroll : Fungsi handleScroll menambahkan data saat pengguna mencapai bagian bawah halaman:

const handleScroll = useCallback(() => {
if (window.innerHeight + document.documentElement.scrollTop >= document.documentElement.offsetHeight - 100) {
if (hasMore && !loading) {
setCurrentPage(prev => prev + 1);
fetchTabungData(currentPage + 1);
}
}
}, [hasMore, loading, currentPage]);

Tambahkan event scroll di useEffect:

useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [handleScroll]);

Dengan komponen ini, Anda telah membuat halaman daftar tabung yang mendukung pencarian, filter, dan pagination. Next.js memungkinkan pengaturan state yang dinamis dan penggunaan data langsung dari database seperti Supabase untuk membuat aplikasi full-stack yang responsif.

Manfaat Aplikasi ini

Dengan menggunakan Next.js dan Supabase, aplikasi ini memberikan banyak manfaat, di antaranya:

  • Kecepatan Akses Data: Data tabung bisa diakses secara cepat dan real-time, memastikan pengisian, pengantaran, dan pengecekan stok selalu up-to-date.
  • Kemudahan Penggunaan: Antarmuka yang responsif dan fitur pencarian serta filter yang mudah digunakan membuat tim lebih efisien dalam melacak dan mengelola tabung.
  • Keamanan Data: Sistem autentikasi di app/auth memastikan bahwa hanya pengguna yang berizin yang bisa mengakses atau mengubah data tabung.

Secara keseluruhan, aplikasi ini diharapkan mampu meningkatkan efisiensi dan akurasi dalam pengelolaan tabung bisnis keluarga kami, mengurangi potensi kesalahan, dan mempercepat proses bisnis dari pengisian hingga pengantaran. Sebagai dosen, pengalaman membangun aplikasi ini juga memberi saya pemahaman lebih dalam yang bisa saya bagikan kepada mahasiswa di kelas, khususnya terkait dengan implementasi teknologi Next.js dan Supabase di dunia nyata.

--

--

Galih Setiawan Nurohim
Galih Setiawan Nurohim

No responses yet