Masukkan Password
71 Hapus Item Pengeluaran
Lecture ini adalah bagian terakhir dari project Expense Tracker, yaitu bagaimana cara menghapus expense dari UI sekaligus memperbarui balance. Materinya sebenarnya menggabungkan beberapa konsep TypeScript/JavaScript yang sudah kita pelajari sebelumnya: template literal, event handling, find(), indexOf(), splice(), type assertion, dan manipulasi state.
1. Tujuan utama lecture
Sebelumnya kita sudah bisa:
Tambah Expense
↓
Expense object dibuat
↓
Masuk ke expenseItems[]
↓
Ditampilkan ke UI
↓
Balance dihitung
Sekarang kita ingin menambahkan:
Klik tombol X
↓
Cari expense yang dipilih
↓
Hapus expense dari expenseItems[]
↓
Update balance
↓
Render ulang UI
Jadi ada dua pekerjaan besar:
- Menghapus expense dari array dan UI.
- Mengembalikan balance ke kondisi yang benar.
Lecture menjelaskan bahwa ketika tombol silang/X diklik, expense tertentu harus dihapus. Jika yang dihapus adalah debit, uangnya harus dikembalikan ke balance. Jika yang dihapus adalah credit, credit tersebut harus dikurangi dari balance.
2. Kenapa tombol delete harus tahu expense mana yang dihapus?
Misalnya kita punya:
Salary +10,000
Groceries -250
Movie -150
YouTube +500
Di UI ada tombol:
Salary [X]
Groceries [X]
Movie [X]
YouTube [X]
Kalau user klik:
Movie [X]
program harus tahu:
“Oh, yang ingin dihapus adalah expense dengan ID berapa?”
Karena itu lecture menggunakan ID dari Expense object.
Misalnya:
const expense = new Expense("debit", "Movie", 150);
object tersebut mungkin memiliki:
{
id: 3,
type: "debit",
description: "Movie",
amount: 150
}
Maka tombol delete bisa diberi informasi:
deleteExpense(3)
Artinya:
“Hapus expense dengan ID 3.”
3. Memasukkan ID ke tombol delete
Sebelumnya kita membuat HTML expense menggunakan template literal.
Contohnya:
const template = `
<div class="${cssClass}">
<div class="expense-description">
${expenseItem.description}
</div>
<div class="expense-amount">
${expenseItem.amount}
</div>
<div class="exp-delete">
<button class="delete-expense">
X
</button>
</div>
</div>
`;
Sekarang tombol X perlu memanggil function:
deleteExpense(...)
dan kita berikan ID expense tersebut.
Konsepnya menjadi:
<button onclick="deleteExpense(${expenseItem.id})">
X
</button>
Misalnya:
expenseItem.id = 3
maka HTML yang dihasilkan kira-kira:
<button onclick="deleteExpense(3)">
X
</button>
Jadi ketika user klik:
X
↓
deleteExpense(3)
Lecture menjelaskan bahwa ID current expense dimasukkan ke HTML menggunakan template literal syntax.
4. Kenapa ID sangat berguna?
Bayangkan array kita:
const expenseItems: Expense[] = [
expense1,
expense2,
expense3,
expense4
];
Misalnya:
index ID
0 1
1 2
2 3
3 4
ID dan index bukan hal yang sama.
Contohnya:
expenseItems[2]
artinya:
ambil item pada posisi/index 2.
Sedangkan:
expense.id === 3
artinya:
expense tersebut memiliki identitas 3.
Ini penting karena setelah delete, index bisa berubah.
Contoh:
Sebelum delete:
index 0 → ID 1 → Salary
index 1 → ID 2 → Groceries
index 2 → ID 3 → Movie
index 3 → ID 4 → YouTube
Hapus ID 2:
Sesudah delete:
index 0 → ID 1 → Salary
index 1 → ID 3 → Movie
index 2 → ID 4 → YouTube
Perhatikan:
ID 3 tetap 3, tetapi index-nya berubah dari 2 menjadi 1.
Jadi:
ID digunakan untuk identitas object, sedangkan index digunakan untuk posisi object dalam array.
5. Membuat function deleteExpense()
Lecture kemudian membuat function:
function deleteExpense(id: number) {
}
Perhatikan:
id: number
Artinya function ini menerima sebuah ID berupa angka.
Contoh:
deleteExpense(3);
valid.
Tetapi:
deleteExpense("3");
akan bermasalah karena "3" adalah string.
Lecture juga menekankan bahwa nama parameter bebas. Kita bisa saja menulis:
function deleteExpense(id: number)
atau:
function deleteExpense(expenseId: number)
atau:
function deleteExpense(value: number)
Yang penting adalah:
: number
menunjukkan tipe datanya.
6. Langkah pertama: cari expense berdasarkan ID
Sekarang kita punya:
id
Misalnya:
id = 3
Kita ingin mencari:
expenseItems
↓
[Salary, Groceries, Movie, YouTube]
↑
ID 3
Untuk mencari object tersebut lecture menggunakan:
find()
7. Apa itu find()?
find() digunakan untuk:
mencari satu element pertama dalam array yang memenuhi kondisi tertentu.
Contoh sederhana:
const numbers = [10, 20, 30, 40];
const result = numbers.find((num) => num === 30);
Hasil:
30
Karena 30 memenuhi kondisi:
num === 30
8. find() pada Expense Tracker
Kita punya:
expenseItems
yang isinya:
Expense 1
Expense 2
Expense 3
Expense 4
Kita ingin:
Cari expense yang:
expense.id === id
Maka:
const exp = expenseItems.find((el) => {
return el.id === id;
});
Mari kita pecah.
Bagian 1
expenseItems.find(...)
Artinya:
cari sesuatu di dalam
expenseItems.
Bagian 2
(el) => {
el adalah element yang sedang diperiksa.
Misalnya array:
Expense 1
Expense 2
Expense 3
find() akan melakukan kira-kira:
el = Expense 1
cek...
el = Expense 2
cek...
el = Expense 3
cek...
Bagian 3
el.id === id
Misalnya:
id = 3
Maka:
Expense 1 → 1 === 3 → false
Expense 2 → 2 === 3 → false
Expense 3 → 3 === 3 → true
Ketika menemukan:
true
find() mengembalikan object tersebut. Lecture menjelaskan bahwa find() mengembalikan element pertama yang memenuhi kondisi callback.
9. Hasil find() bisa undefined
Ini konsep TypeScript yang sangat penting.
Ketika kita menulis:
const exp = expenseItems.find((el) => el.id === id);
TypeScript mengetahui bahwa hasilnya bisa:
Expense | undefined
Kenapa?
Karena bagaimana kalau ID tidak ditemukan?
Misalnya:
deleteExpense(999);
Padahal tidak ada expense dengan ID 999.
Maka:
find()
tidak menemukan apa pun.
Hasilnya:
undefined
Jadi secara konsep:
find()
│
├── ketemu → Expense
│
└── tidak ketemu → undefined
Lecture secara eksplisit membahas hal ini.
10. Kenapa kemudian lecture melakukan type assertion?
Lecture mengetahui bahwa dalam aplikasi ini ID yang diberikan memang seharusnya memiliki expense.
Karena itu mereka melakukan assertion menjadi:
const exp = expenseItems.find(
(el) => el.id === id
) as Expense;
Artinya:
“TypeScript, saya yakin hasil pencarian ini adalah
Expense.”
Tetapi perlu diingat:
as Expense tidak membuat object menjadi Expense secara runtime.
Ini hanya memberitahu TypeScript tentang tipe yang kita yakini.
11. Setelah menemukan object, kita butuh index
Sekarang kita sudah punya:
exp
Misalnya:
exp = Movie
Tetapi kita belum tahu Movie berada di index berapa.
Misalnya:
index:
0 → Salary
1 → Groceries
2 → Movie
3 → YouTube
Kita perlu:
Movie → index 2
Untuk itu digunakan:
indexOf()
12. Apa itu indexOf()?
indexOf() digunakan untuk mencari posisi sebuah element dalam array.
Contoh:
const fruits = [
"Apple",
"Banana",
"Orange"
];
const index = fruits.indexOf("Orange");
Hasil:
2
Karena:
index 0 → Apple
index 1 → Banana
index 2 → Orange
Jadi:
indexOf()
berarti:
“Object ini berada di posisi/index berapa?”
13. Digunakan pada expenseItems
Lecture menggunakan:
const index: number = expenseItems.indexOf(exp);
Misalnya:
expenseItems:
index 0 → Salary
index 1 → Groceries
index 2 → Movie
index 3 → YouTube
Jika:
exp = Movie
maka:
expenseItems.indexOf(exp)
menghasilkan:
2
14. indexOf() juga punya kemungkinan -1
Kalau object tidak ditemukan:
indexOf()
menghasilkan:
-1
Contoh:
const fruits = ["Apple", "Banana"];
fruits.indexOf("Mango");
hasil:
-1
Artinya:
tidak ditemukan.
Lecture juga menjelaskan bahwa indexOf() mengembalikan -1 jika object tidak ditemukan.
15. Sekarang kita punya index → gunakan splice()
Ini bagian penting.
Kita sudah melakukan:
ID
↓
find()
↓
Expense object
↓
indexOf()
↓
index
Sekarang kita ingin menghapus object dari array.
Gunakan:
splice()
16. Apa itu splice()?
splice() digunakan untuk mengubah isi array, termasuk menghapus element.
Sintaks sederhananya:
array.splice(startIndex, deleteCount);
Contoh:
const fruits = [
"Apple",
"Banana",
"Orange"
];
fruits.splice(1, 1);
Artinya:
mulai dari index 1
hapus 1 element
Hasil:
["Apple", "Orange"]
Karena:
index 0 → Apple
index 1 → Banana ← dihapus
index 2 → Orange
17. Dalam Expense Tracker
Kita punya:
const index = expenseItems.indexOf(exp);
Kemudian:
expenseItems.splice(index, 1);
Artinya:
mulai dari index yang ditemukan
hapus 1 object
Misalnya:
index = 2
maka:
expenseItems.splice(2, 1);
Menghapus:
index 2
yaitu Movie.
Lecture menjelaskan bahwa angka kedua 1 berarti kita hanya ingin menghapus satu item.
18. Jadi keseluruhan proses delete adalah
Ini bagian yang paling penting untuk dipahami:
User klik X
↓
deleteExpense(id)
↓
Cari object berdasarkan ID
↓
find()
↓
Dapat Expense object
↓
indexOf()
↓
Dapat index
↓
splice(index, 1)
↓
Expense dihapus dari array
Contoh nyata:
Klik tombol X pada Movie
ID = 3
expenseItems.find(...)
↓
Movie object
indexOf(Movie)
↓
2
splice(2, 1)
↓
Movie dihapus
19. Setelah array berubah, UI harus dirender ulang
Ini juga penting.
Ingat bahwa sebelumnya kita punya:
displayExpenseItems();
Function tersebut mengambil isi:
expenseItems
kemudian menampilkannya ke UI.
Jadi setelah:
expenseItems.splice(index, 1);
kita panggil:
displayExpenseItems();
Alurnya:
expenseItems sebelum:
[Salary, Groceries, Movie, YouTube]
↓
splice()
expenseItems sesudah:
[Salary, Groceries, YouTube]
↓
displayExpenseItems()
↓
UI diperbarui
Lecture memang melakukan render ulang setelah item dihapus.
20. Tapi masih ada satu masalah: balance
Misalnya balance sekarang:
$10,000
Kemudian kita punya:
Movie
debit
$150
Saat ditambahkan:
10,000 - 150 = 9,850
Sekarang Movie dihapus.
Uangnya harus dikembalikan:
9,850 + 150 = 10,000
Jadi delete bukan hanya:
hapus dari array
tetapi juga:
update balance
21. Membuat updateBalance()
Lecture kemudian membuat function:
function updateBalance(exp: Expense) {
}
Function ini menerima expense yang baru saja dihapus.
Kenapa perlu object expense?
Karena kita perlu tahu:
exp.type
dan:
exp.amount
Jadi kita bisa menentukan apakah harus:
+ amount
atau:
- amount
22. Ambil amount
Di dalam function:
let amount = exp.amount;
Misalnya:
exp.amount = 150
maka:
amount = 150
Kenapa menggunakan let, bukan const?
Karena lecture menggunakan variable tersebut sebagai nilai yang mungkin dimodifikasi/diolah.
23. Jika yang dihapus adalah CREDIT
Ini bagian yang sering membingungkan.
Misalnya kita punya:
Salary → credit → +10,000
Balance:
10,000
Kemudian Salary ternyata salah input dan dihapus.
Kita harus:
10,000 - 10,000 = 0
Jadi:
if (exp.type === "credit") {
totalAmount -= amount;
}
24. Jika yang dihapus adalah DEBIT
Misalnya:
Groceries → debit → 250
Balance awal:
10,000
Setelah expense:
10,000 - 250 = 9,750
Kalau Groceries dihapus:
9,750 + 250 = 10,000
Jadi:
else {
totalAmount += amount;
}
25. Kenapa CREDIT dikurangi, DEBIT ditambah?
Gunakan logika sederhana ini:
Saat menambahkan
CREDIT → balance naik
DEBIT → balance turun
Maka saat menghapus:
CREDIT → balance harus turun kembali
DEBIT → balance harus naik kembali
Dengan kata lain, delete melakukan kebalikan dari operasi saat add.
| Expense | Saat Add | Saat Delete |
|---|---|---|
| Credit | +amount | -amount |
| Debit | -amount | +amount |
Ini pola yang sangat penting.
26. Setelah update balance → tampilkan balance
Setelah:
totalAmount -= amount;
atau:
totalAmount += amount;
kita harus update UI:
showTotal();
Jadi:
totalAmount berubah
↓
showTotal()
↓
balance di UI berubah
Lecture menggunakan showTotal() setelah memodifikasi totalAmount.
27. Ada bug kecil yang menarik di lecture
Lecture sempat mengalami bug.
Awalnya mereka menulis konsep seperti:
totalAmount - amount;
dan:
totalAmount + amount;
Kelihatannya benar secara matematika.
Tapi sebenarnya tidak mengubah variable totalAmount.
Kenapa?
Misalnya:
let totalAmount = 1000;
totalAmount - 100;
Expression tersebut menghasilkan:
900
tetapi hasilnya tidak disimpan.
totalAmount masih:
1000
28. Harus menggunakan assignment
Yang benar:
totalAmount -= amount;
atau:
totalAmount += amount;
Karena:
totalAmount -= amount;
secara konsep sama dengan:
totalAmount = totalAmount - amount;
Sedangkan:
totalAmount += amount;
sama dengan:
totalAmount = totalAmount + amount;
Lecture menemukan bug ini ketika balance tidak berubah walaupun function delete sudah terpanggil.
29. Perbedaan + dengan +=
Ini wajib dipahami.
Hanya +
totalAmount + 100;
Artinya:
hitung hasil penjumlahan.
Tetapi hasilnya tidak disimpan.
+=
totalAmount += 100;
Artinya:
hitung penjumlahan lalu simpan kembali ke
totalAmount.
Sama dengan:
totalAmount = totalAmount + 100;
Begitu juga:
totalAmount -= 100;
sama dengan:
totalAmount = totalAmount - 100;
30. Full flow deleteExpense()
Sekarang kita bisa melihat keseluruhan function secara konseptual:
function deleteExpense(id: number) {
const exp = expenseItems.find(
(el) => el.id === id
) as Expense;
const index: number =
expenseItems.indexOf(exp);
expenseItems.splice(index, 1);
updateBalance(exp);
displayExpenseItems();
}
Dan:
function updateBalance(exp: Expense) {
const amount = exp.amount;
if (exp.type === "credit") {
totalAmount -= amount;
} else {
totalAmount += amount;
}
showTotal();
}
Catatan: ini mengikuti alur lecture. Untuk aplikasi production, kita biasanya akan menambahkan pengecekan jika find() menghasilkan undefined dan jika index === -1.
31. Mari simulasikan dari awal
Misalnya kita punya:
totalAmount = 0
Tambah Salary
credit
10,000
Balance:
0 + 10,000 = 10,000
Array:
[
Salary
]
Tambah Groceries
debit
250
Balance:
10,000 - 250 = 9,750
Array:
[
Salary,
Groceries
]
Tambah Movie
debit
150
Balance:
9,750 - 150 = 9,600
Array:
[
Salary,
Groceries,
Movie
]
User klik X pada Movie
ID Movie misalnya:
3
Maka:
deleteExpense(3)
Step 1 — find()
const exp = expenseItems.find(
el => el.id === 3
);
Hasil:
Movie object
Step 2 — indexOf()
const index = expenseItems.indexOf(exp);
Hasil:
2
Step 3 — splice()
expenseItems.splice(2, 1);
Array berubah:
[
Salary,
Groceries
]
Step 4 — update balance
Movie adalah:
debit
150
Maka:
totalAmount += 150;
Balance:
9,600 + 150
= 9,750
Step 5 — render ulang
displayExpenseItems();
UI sekarang:
Salary $10,000 [X]
Groceries $250 [X]
Movie sudah hilang.
Balance:
$9,750
32. Kenapa harus menghapus dari array?
Ini konsep arsitektur yang sangat penting.
Bayangkan kita hanya menghapus HTML:
UI
↓
hapus Movie
Tetapi array masih:
[
Salary,
Groceries,
Movie
]
Maka data sebenarnya masih menganggap Movie ada.
Nanti ketika kita menjalankan:
displayExpenseItems();
Movie bisa muncul lagi.
Karena itu:
Yang harus diubah pertama adalah data (
expenseItems), lalu UI mengikuti data tersebut.
33. Konsep “Single Source of Truth”
Dalam project ini:
expenseItems
bisa dianggap sebagai source of truth.
Artinya data utama berada di:
expenseItems
UI hanya merupakan representasi dari data tersebut.
Diagramnya:
expenseItems
│
┌─────────┴─────────┐
│ │
▼ ▼
displayExpenseItems() calculateTotal()
│ │
▼ ▼
UI totalAmount
│
▼
showTotal()
Jadi ketika menghapus:
expenseItems
↓
ubah data
↓
render ulang UI
↓
hitung/update balance
Ini pola yang sangat berguna ketika nanti belajar framework seperti React, Vue, Angular, dan sebagainya.
34. Method yang dipelajari dalam lecture
Lecture ini sebenarnya memperkenalkan beberapa method array yang penting.
find()
Mencari element:
const user = users.find(
user => user.id === id
);
Hasil:
User | undefined
indexOf()
Mencari index dari element:
const index = users.indexOf(user);
Hasil:
number
atau:
-1
splice()
Menghapus/mengubah array berdasarkan index:
users.splice(index, 1);
Hubungannya
find()
↓
dapat OBJECT
indexOf()
↓
dapat INDEX
splice()
↓
hapus OBJECT dari ARRAY
Ini salah satu hal terpenting dari lecture.
35. Bedakan find() dan filter()
Ini juga penting karena keduanya sama-sama mencari data.
find()
const user = users.find(
user => user.id === 3
);
Menghasilkan:
satu object
atau:
undefined
filter()
const users = users.filter(
user => user.id !== 3
);
Menghasilkan:
array baru
Jadi:
find() → cari satu element
filter() → pilih banyak element
Dalam lecture, find() dipilih karena kita ingin mendapatkan expense object tertentu berdasarkan ID.
36. TypeScript concepts yang muncul dalam lecture
Walaupun kelihatannya lecture ini hanya tentang delete button, sebenarnya banyak materi TypeScript sebelumnya yang dipakai.
1. Type assertion
as Expense
Digunakan karena find() bisa menghasilkan:
Expense | undefined
2. Typed parameter
function deleteExpense(id: number)
ID harus number.
3. Typed parameter object
function updateBalance(exp: Expense)
Function mengharapkan object Expense.
4. Class
Expense
digunakan sebagai tipe object.
5. Union/literal type
Sebelumnya:
type: "credit" | "debit"
dan sekarang digunakan untuk menentukan operasi balance:
if (exp.type === "credit") {
...
}
6. Array type
const expenseItems: Expense[] = [];
Kemudian:
find()
indexOf()
splice()
digunakan pada array tersebut.
37. Kesalahan yang perlu diwaspadai
Ada beberapa bagian dari implementasi lecture yang sebaiknya kamu pahami secara kritis.
Kesalahan 1: find() bisa menghasilkan undefined
Secara aman:
const exp = expenseItems.find(
el => el.id === id
);
if (!exp) {
return;
}
Baru kemudian:
const index = expenseItems.indexOf(exp);
Ini lebih aman daripada langsung:
as Expense
karena ID yang salah tetap mungkin terjadi.
Kesalahan 2: indexOf() bisa menghasilkan -1
Sebaiknya:
if (index === -1) {
return;
}
Baru:
expenseItems.splice(index, 1);
Kalau tidak, kita berisiko melakukan operasi dengan index yang tidak valid.
38. Versi yang lebih aman
Secara konsep, saya lebih menyarankan bentuk seperti:
function deleteExpense(id: number) {
const exp = expenseItems.find(
(el) => el.id === id
);
if (!exp) {
return;
}
const index = expenseItems.indexOf(exp);
if (index === -1) {
return;
}
expenseItems.splice(index, 1);
updateBalance(exp);
displayExpenseItems();
}
Dan:
function updateBalance(exp: Expense) {
const amount = exp.amount;
if (exp.type === "credit") {
totalAmount -= amount;
} else {
totalAmount += amount;
}
showTotal();
}
Perbedaannya adalah kita tidak memaksa TypeScript percaya dengan:
as Expense
kalau sebenarnya object belum terbukti ada.
39. Kenapa delete balance harus dilakukan sebelum/bersama render?
Karena kita ingin kedua hal ini konsisten:
DATA
↓
balance
↓
UI
Misalnya:
Array:
[Salary, Groceries]
Balance:
9,750
UI:
Salary
Groceries
Setelah delete Groceries:
Array:
[Salary]
Balance:
10,000
UI:
Salary
Ketiganya harus menggambarkan keadaan yang sama.
40. Gambaran besar seluruh Expense Tracker
Sekarang setelah lecture ini, keseluruhan project bisa dipahami seperti ini:
USER
│
▼
Form Input
│
▼
new Expense(...)
│
▼
expenseItems.push()
│
┌─────────┴─────────┐
│ │
▼ ▼
displayExpenseItems() calculateTotal()
│ │
▼ ▼
UI totalAmount
│
▼
showTotal()
DELETE
│
▼
click X
│
▼
deleteExpense(id)
│
▼
find()
│
▼
Expense object
│
▼
indexOf()
│
▼
splice()
│
├───────────────┐
▼ ▼
updateBalance() displayExpenseItems()
│ │
▼ ▼
showTotal() UI
41. Hal paling penting untuk diingat
Kalau kamu hanya mengingat satu alur dari lecture ini, ingat:
ID
↓
find()
↓
Expense object
↓
indexOf()
↓
index
↓
splice(index, 1)
↓
hapus dari array
↓
update balance
↓
render ulang UI
42. Cheat sheet lecture
| Konsep | Fungsi |
|---|---|
find() | Mencari element berdasarkan kondisi |
indexOf() | Mencari posisi/index sebuah element |
splice() | Menghapus/mengubah isi array |
id | Identitas unik expense |
index | Posisi expense dalam array |
+= | Tambahkan dan simpan hasil |
-= | Kurangkan dan simpan hasil |
as Expense | Type assertion |
Expense[] | Array berisi Expense |
updateBalance() | Mengubah balance setelah delete |
displayExpenseItems() | Render ulang daftar expense |
showTotal() | Menampilkan balance terbaru |
43. Hubungan find → indexOf → splice
Ini bisa kamu hafalkan dengan analogi mencari buku di perpustakaan:
find()
↓
"Carikan buku dengan ID 123."
↓
Dapat bukunya.
indexOf()
↓
"Buku itu ada di rak/posisi ke berapa?"
↓
Dapat posisi.
splice()
↓
"Ambil buku dari posisi tersebut."
↓
Buku dihapus dari koleksi.
Dalam Expense Tracker:
find()
→ cari Expense
indexOf()
→ cari posisi Expense
splice()
→ hapus Expense
Kesimpulan besar
Lecture ini mengajarkan bahwa delete bukan sekadar menghilangkan elemen dari HTML. Kita harus menghapus expense dari data utama (expenseItems), lalu menyesuaikan balance, kemudian memperbarui UI. Untuk menemukan expense berdasarkan ID digunakan find(), untuk mendapatkan posisinya digunakan indexOf(), dan untuk menghapusnya digunakan splice().
Yang paling penting:
find()mencari object →indexOf()mencari posisi →splice()menghapus object →updateBalance()memperbaiki balance →displayExpenseItems()danshowTotal()memperbarui UI.
Dengan lecture ini, project Expense Tracker sudah menggabungkan hampir semua konsep TypeScript yang dipelajari sebelumnya: class, type assertion, union/literal type, typed array, function, event handling, array methods, DOM manipulation, dan state sederhana. Lecture juga menyebut bahwa project tersebut bisa dikembangkan lagi, misalnya menampilkan detail expense atau memungkinkan user meng-update expense yang sudah ada.