Aplikasi Cashier Sederhana dengan React, Dexie, dan Papa Parse
( Edited Image )

Program Cashier Sederhana dengan React

5 Jun 2024, (terakhir diedit pada 5 Jun 2024)
129
0

Kali ini, saya membuat program untuk mencatat penjualan Es Teh Jombo. Mengapa spesifik sekali? Karena kakak saya kebetulan buka warung Es Teh Jumbo. Aplikasi ini saya niatkan untuk membantunya, sekalipun akhirnya tidak digunakan juga sih, hehehe. Aplikasi ini juga sudah dulu sekali saya buatnya. Namun, ada baiknya saya bagikan di sini. Sebut saja aplikasi Cashier Sederhana, sekalipun memang tidak bisa digunakan untuk menerima pembayaran. Secara garis besar, aplikasi ini akan memuat daftar produk beserta harganya, menerima data penjualan, menyimpannya, lalu menghitung banyak penjualan berdasarkan item, jumlah, dan harga.

Aplikasi ini berbasis web, dengan fitur Progressive Web Application (PWA). Artinya, program ini tidak hanya bisa diakses dengan memasukkan alamat ke kolom address di browser, tapi juga dapat diinstal ke perangkat. Perlu diperhatikan bahwa PWA sebenarnya masih merupakan aplikasi web, hanya saja OS sekarang (Android, Windows, iOS) dapat membuatnya seakan seperti program native. Karena itu, 'benda ini' tetap perlu browser yang aktif.

Pada kesempatan ini, saya menggunakan React sebagai library utama, sekalipun saya mengawali belajar web development dari Vue. Ceritanya, cukup lama yang lalu saya melamar kerja di Samsung RnD (SRIN). Sampai di tahap wawancara, saya dapat tugas untuk membuat aplikasi dengan React. Alasannya, mereka ingin tahu kemampuan saya dalam hal belajar library/framework yang belum pernah saya gunakan. Lowongannya tidak keterima, saya yang malah jadi keterusan pakai React, ya sudah lah, hehehehe.

Selain React, library yang perlu disebutkan di sini adalah Dexie dan Papa Parse.

Oh iya, untuk demonya, bisa dilihat di sini. User Name pakai 'admin' dan Password pakai 'test1234'.

React

Menggunakan React sebenarnya mirip dengan menggunakan Vue. Sama-sama menghasilkan SPA (Single Page Application), sama-sama perlu pemahaman life cycle untuk menangani tampilan yang interaktif, serta sama-sama punya kode tersendiri dalam HTML-nya. Namun, secara umum saya merasa kodenya React itu lebih bergaya programmer. Lebih logika dan algoritma. Kalau sekilas dilihat, kode React akan seperti kode JavaScript bahkan di bagian return yang menurut saya ekuivalen dengan template di Vue. Sedangkan Vue, kodenya akan lebih terasa alami untuk web dengan pemisahan antara JavaScript (script), HTML (template), dan CSS (style). Saya merasa kalau background-nya programmer, React akan lebih mudah dibaca dan ditulis. Namun, kalau background-nya web designer sepertinya akan lebih cocok dengan Vue.

Okey, kita beralih ke Cashier Sederhana. Dengan konsep SPA, pertama-tama saya perlu mengatur routing. Routing adalah page untuk SPA, hanya saja tidak benar-benar berganti halaman. Dengan JavaScript, maka SPA akan mengganti alamat web dan tampilan tanpa harus loading ulang seluruh elemen.

Kalau di React, ternyata perlu instal terlebih dahulu library tambahan React Router (react-router-dom untuk React web). Untuk mengaturnya, maka saya perlu menulis tiap-tiap route dengan kode JSX.

Dalam Cashier Sederhana, terdapat empat halaman yaitu Home, Products (Daftar Produk), Transactions (Transaksi), dan Login.

  • Home adalah bagian yang akan memperlihatkan menu yang cuma ada tiga: ke Daftar Produk, ke Transaksi, dan Keluar (Logout)
  • Products merupakan halaman tempat pengguna bisa menginput produknya, melihat produk, dan mengubah atau menghapus produk yang sudah tersimpan dalam aplikasi.
  • Transactions merupakan halaman untuk menginput data penjualan, melihat transaksi yang sudah tersimpan, mengubah/menghapus data transaksi, dan melihat rekapitulasi hasil penjualan. Konsep rekapitulasi yang saya gunakan adalah menghitung jumlah penjualan per item dan harga totalnya, dan menghitung jumlah penjualan minuman dalam satuan cup.
  • Login adalah halaman untuk masuk ke aplikasi dengan user name dan password.

Halaman di Cashier Sederhama Tampilan Keempat Halaman Cashier Sederhana

Semua data yang dimasukkan dalam aplikasi ini akan disimpan dalam media penyimpanan browser bernama IndexedDB. Ini juga adalah ilmu baru bagi saya. Awalnya dengan menggunakan program client-side, yang saya tahu adalah kita bisa menyimpan data dengan localStorage. Dengan itu pulalah saya berniat membuat aplikasi ini. Namun, setelah saya cari-cari, ternyata ada sistem penyimpanan di browser yang lebih cocok untuk data yang rumit seperti yang ada di sistem database. Sistem itulah yang bernama IndexedDB.

Cerita Sedikit Mengenai Firebase

Namun, saya sempat ragu-ragu. Kakak saya berjualan itu tidak selalu dia, tapi berganti-gantian. Saya jadi merasa kalau pakai penyimpanan lokal, maka akan ribet karena data tidak terhubung antara satu perangkat dengan perangkat yang lain. Saya lalu terpikir menggunakan Firestore milik Firebase.

Nah, kalau ini sebenarnya sangat menarik. Firestore adalah sistem cloud database miliki Firebase yang dapat digunakan sebagai tempat penyimpanan data baik aplikasi web, Android, maupun iOS. Mereka menyediakan SDK (software development kit) untuk masing-masing proyek sesuai platform-nya. Kalau pakai web, berarti saya perlu instal dengan NPM. Lalu saya juga perlu membuat project di Firebase console, menginisiasi Firestore, dan menghubungkannya dengan aplikasi Cashier Sederhana ini.

Semua saya lakukan dengan cukup baik. Mulai dari membuat sistem login, menginput data produk dan transaksi ke Firestore, hingga membuat subscriber untuk mendapatkan data hasil inputan secara realtime. Terkadang ada masalah yang membingungkan tapi tidak terlalu sulit sehingga cepat terselesaikan. Hingga saya akhirnya melihat berapa jumlah operasi yang terjadi antara aplikasi saya dengan Firestore. Dalam waktu beberapa menit saja, jumlah read yang terjadi sudah melebihi 1,4 ribu!

Memangnya itu penting? Kejutan! Firebase itu tidak gratis, atau setidaknya tidak gratis sampai titik tertentu. Benar sekali, Firebase dan semua produknya termasuk Firestore itu sebenarnya menjual jasa, hanya saja mereka punya promo tidak berbayar jika di bawah batas penggunaan tertentu. Batas gratis Firestore adalah 1 GiB data tersimpan, 10 GiB pertukaran data, 20k write, 50k read, dan 20k delete. Tidak buruk sebenarnya jika mengingat aplikasi saya ini paling hanya dipakai satu user yaitu kakak saya saja. Namun, mengapa dalam tes yang saya lakukan jumlah read bisa lebih dari seribu? Ada dua penyebabnya. Yang pertama konsep dari aplikasi web. Setiap kali masuk ke aplikasi, maka elemen akan terbentuk. Setiap kali keluar, maka elemen akan dihapus. Begitu juga untuk tiap-tiap route maupun elemen dalam route. Karena itu, saat aplikasi berpindah dari Home ke Transactions, maka akan ada proses pengiriman data baru dari Firestore, sekalipun awalnya sudah terjadi sebelumnya. Sebenarnya hal ini bisa lebih efisien dengan memakai variabel yang lebih umum untuk semua elemen, tapi ilmu saya belum setingkat itu. Sementara penyebab kedua adalah read, write, dan delete di Firebase dihitung berdasarkan dokumen. Jadi misal dalam sekali query meminta sebanyak 20 dokumen, maka terbaca 20 read. Padahal, konsep NoSQL dalam database semacam Firestore akan optimal jika kita menyimpan data dalam dokumen-dokumen kecil. Jadi misal satu item produk sama dengan satu dokumen. Begitu juga dengan data transaksi.

Catatan: setelah saya menulis tulisan ini, saya jadi kepikiran mengapa tidak saya satukan banyak data sejenis ke satu dokumen saja ya? Misal data produk jadi satu dokumen dan data transaksi untuk satu hari jadi satu dokumen. Yah, namanya juga orang bingung hehehe.

Karena itulah, pada akhirnya saya kembali ke penyimpanan data lokal dengan IndexedDB.

IndexedDB dengan Dexie

Saat saya browsing-browsing ke sana ke mari, memakai IndexedDB sebenarnya tidak terlalu sulit. Terdapat syntax asli JavaScript di browser modern yang bisa digunakan untuk mengakses fitur ini. Namun, ternyata ada pula library sebagai wrapper. Namanya adalah Dexie.

Saya memutuskan pakai library ini untuk mempercepat proses pembuatan aplikasi. Selain itu, agar saya seperti developer React yang sering pakai banyak library. Karena tahu sendiri React itu populernya seperti apa. Pasti banyak library untuk segala macam kebutuhan. Dexie sendiri punya library khusus untuk memudahkan penggunaannya di React, yaitu dexie-react-hooks.

Beralih dari Firestore ke IndexedDB dengan Dexie tidak terlalu susah. Terutama karena dalam pembuatan aplikasi ini, saya juga telah banyak membuat tampilan frontend. Jadinya, pergantian kode-kode ke sistem penyimpanan data bisa langsung dilihat hasilnya, dan setiap langkahnya menjadi terasa menyenangkan. Kodenya sendiri tidak terlalu berbeda. Penggunaan Dexie menggunakan prinsip asynchronous yang sama dengan konsep penggunaan database melalui internet. Fungsi-fungsinya juga menggunakan notasi titik yang tidak ribet. Namanya juga library wrapper ya. Kalau jadi tambah ribet lalu apa gunanya?

Selain masalah menulis dan membaca data, fungsionalitas lain yang perlu saya buat adalah mengubah dan menghapus data. Kalau ini, perlu menggunakan id dari data yang ingin diubah/dihapus. Id ini berfungsi untuk membedakan data-data yang disimpan. Yang saya pergunakan adalah bilangan bulat yang otomatis dibuat oleh IndexedDB. Untuk alurnya sendiri di tampilan, saya menggunakan sebuah komponen yang berbentuk modal window dan menampilkan form ubah dan tombol untuk menghapus. Komponen ini akan muncul jika pengguna menekan kotak produk atau transaksi yang ingin diubah. Untuk menghapus, maka terdapat satu lagi komponen yang akan muncul untuk mengkonfirmasi apakah pengguna benar-benar ingin menghapus data yang dimaksud.

Kembali Membahas React

Nah karena sudah menyinggung masalah komponen, sekalian saja saya perlihatkan daftar komponen yang ada dalam aplikasi ini.

  • AddProduct. Komponen ini juga berbentuk modal window yang menampilkan form input produk baru.
  • AddTransaction. Komponen ini memperlihatkan form untuk menginput data penjualan baru. Komponen ini tidak berbentuk modal window, melainkan kotak biasa yang akan muncul di halaman Transaksi. Komponen ini dapat digunakan untuk menginput data penjualan beberapa item sekaligus. Dengan demikian, pengguna dapat melihat jumlah harga dari suatu transaksi yang akan dilakukan.
  • ConfirmDelete. Komponen ini seperti yang disebutkan sebelumnya adalah komponen untuk meminta konfirmasi pengguna apakah benar-benar ingin menghapus data. Baik data produk maupun penjualan sama-sama menggunakan komponen ini. Untuk fungsi, digunakan fungsi dari tempat ia dipanggil. Jadi, fungsi itu akan dimasukkan (pass) ke komponen ini dan dihubungkan dengan tombol di sana.
  • EditProduct. Komponen yang tadi dibahas, berbentuk modal window untuk mengubah data produk dan menampilkan tombol delete.
  • EditTransaction. Komponen yang tadi juga dibahas, berbentuk modal window untuk mengubah data penjualan dan menampilkan tombol delete.
  • Loader. Komponen ini sebenarnya dibuat untuk menangani jeda proses saat akan login dengna Firebase. Karena tidak jadi pakai Firebase, komponen ini jadi kurang guna.
  • ProductDisplay. Komponen ini merupakan kotak yang memperlihatkan data tiap produk di halaman Daftar Produk.
  • RecapTransaction. Komponen ini memuat fungsi untuk menghitung rekapitulasi penjualan pada satu hari, sekaligus menampilkan data tersebut ke halaman Transaksi.
  • SaleDisplay. Mirip dengan ProductDisplay, komponen ini adalah kotak yang menampilkan data tiap penjualan.
  • UploadProductList. Komponen yang menangani upload data produk.

Komponen-komponen dalam Cashier Sederhana Komponen-komponen dalam Cashier Sederhana

Untuk komponen yang terakhir, saya buat khusus karena bingung cara pakai Papa Parse React. Dokumentasinya itu cuma memperlihatkan kode jadi tanpa penjelasan yang jelas. Mungkin ada sih penjelasannya, tapi di webnya sulit ditemukan. Okey, sekalian saja kita bahas Papa Parse ini.

Papa Parse di React

Papa Parse sendiri adalah library untuk menangani file CSV, baik untuk membaca maupun membuat. Saya menggunakan library ini karena saya ingin agar semua data bisa di-export keluar aplikasi. Namanya juga pakai penyimpanan lokal. Bisa saja terhapus saat browser tiba-tiba menampilkan pesan menghapus atau terhapus karena si pengguna gagal paham. Selain itu kalau bisa di-export, nantinya data bisa diolah sesuai dengan keinginan di software pengolah angka favorit mereka masing-masing (sebut saja Excel).

Seperti yang lain, Papa Parse juga ada library langsung untuk React. Namun seperti yang tadi sudah disinggung, dokumentasinya itu membingungkan. Jadinya yang saya lakukan hanyalah menyalin kode di dokumentasi ke kode aplikasi Cashier Sederhana ini. Karena itu saya buat komponen sendiri biar tidak membuat kodenya kurang enak dipandang. Tujuannya sebenarnya bagus, memberikan kode program dengan fungsionalitas pasti dengan tampilan desain yang bagus. Yah, ada-ada saja memang pembuat library JavaScript di dunia ini.

Selain masalah dokumentasi, menggunakan Papa Parse sebenarnya mudah sekali. Untuk menangani upload CSV, maka saya perlu menyediakan variabel sebagai tempat menyimpan data. Selain itu, saya juga perlu memberi tanda apakah saya punya header di file CSV yang dimaksud atau tidak. Header adalah kepala tabel dalam CSV. Jika ada header, maka Papa Parse akan membuat array of object dengan isi header sebagai keys dalam object. Itulah yang saya inginkan.

Sedangkan untuk membuat file CSV dari data yang saya punya, prinsipnya sama hanya saja dibalik. Saya perlu menentukan variabel mana yang ingin dijadikan CSV berserta konfigurasi semisal apakah ada header atau tidak sama seperti sebelumnya. Jenis variabelnya juga sama, yaitu array of object. Jadi kalau menerima CSV, maka file jadi array of object, kalau buat CSV maka array of object jadi file.

Soal Login yang Terlanjur Dibuat

Karena sebelumnya terlanjur pakai Firebase, maka ada halaman login. Saya memutuskan untuk tetap mempertahankan halaman ini. Berbicara masalah pengamanan, sistemnya memakai pengamanan ringan saja. Tujuan utamanya untuk mencegah aplikasi tertekan atau diotak-atik orang yang tidak paham, misalnya anak kecil yang sedang dipinjami HP.

Cara kerjanya memakai cara login bekas Firebase yang sebelumnya saya buat. Jadi, untuk semua proses login/logout terjadi di sebuah context. Context adalah sebuah hook yang berfungsi menyimpan nilai tapi bisa diakses oleh semua komponen dalam aplikasi. Context bisa menyimpan hook lain semisal state ataupun ref, dan itulah yang saya pakai dalam aplikasi ini.

Jika tidak login, maka pengguna tidak bisa mengakses halaman Home, Transaksi, dan Daftar Produk. Hal ini karena mereka telah dilindungi dengan sebuah komponen halaman yaitu PrivateRoute. Fungsi utama komponen ini adalah mengecek apakah ada data login user dalam context. Jika tidak maka ia akan mengarahkan aplikasi ke halaman Login. Jika ada, maka ia akan mengarahkan aplikasi ke halaman yang diinginkan.

Saat login, maka fungsi login dari context akan dipanggil dan dijalankan. Fungsi ini akan mencocokkan data user name dan password yang diberikan. Jika cocok, maka fungsi ini akan menyimpan sebuah tanda di sebuah state bahwa pengguna sudah login. Nah, state inilah yang akan diakses oleh PrivateRoute untuk menentukan bagaimana ia menavigasikan halaman.

Sedangkan saat logout, maka fungsi logout dari context akan dijalankan. Fungsi ini bertugas untuk menghapus data login user di state yang tadi disebutkan yang ada di context. Dengan begitu, PrivateRoute tidak akan mengizinkan pengguna untuk mengakses tiga halaman utama dari aplikasi ini.

Konsep login/logout yang ada di aplikasi ini hanya hardcode saja. User name dan password sudah saya tentukan di awal dan tersimpan dalam kode. Tidak untuk ditiru ya. Sebenarnya, untuk proses login/logout jika menggunakan layanan jasa cloud atau semacamnya kurang lebih akan seperti ini juga. Hanya saja, fungsi-fungsi yang ada di context tinggal disesuaikan dengan cara kerja dari jasa cloud yang digunakan. Mungkin juga perlu menambah fungsi lain, misalnya fetchUser untuk mengecek apakan ada user yang sudah login di aplikasi atau belum. Sebab, terkadang untuk mengecek status user di aplikasi perlu dicocokkan antara yang ada di client side dan di server side jasa cloud. Fungsi signup, forgetPassword, dan changePassword juga adalah fungsi-fungsi yang penting jika ingin membuat aplikasi yang memang sebenar-benarnya aplikasi. Sepertinya aplikasi saya ini kurang banget ya? Yah, namanya juga saya cuma buat untuk iseng saja, hehehe.

Penutup

Kira-kira seperti itu yang bisa saya bagi di kesempatan kali ini. Semoga bermanfaat, maaf-maaf jika ada salah kata atau salah tulis, sampai jumpa di tulisan selanjutnya!

Sumber

Komentar

Ingin berkomentar?