Tutorial #18: Menambahkan Fitur Profil dan Edit Profil
Halo para pengguna setia! Setelah bisa mengubah password, sekarang saatnya kita membuat halaman profil yang keren di mana pengguna bisa melihat data diri mereka dan mengeditnya (nama dan email). Fitur ini akan tersedia untuk semua level: Nasabah, Petugas, dan Admin. Siap tampil beda?
Yang akan kita lakukan:
- ✅ Membuat endpoint API di Laravel untuk mengambil data profil (GET /api/profile).
- ✅ Membuat endpoint API untuk mengupdate profil (PUT /api/profile).
- ✅ Membuat halaman Profile di React dengan form yang sudah terisi data.
- ✅ Validasi input (client & server) agar email unik dan nama tidak kosong.
- ✅ Menampilkan informasi tambahan sesuai role (misal untuk nasabah: no rekening dan saldo).
- ✅ Menambahkan link ke halaman profil di dropdown navbar.
Langkah 1: Membuat Endpoint API di Laravel
Kita perlu dua endpoint: satu untuk mengambil data profil, satu untuk mengupdate. Buka routes/api.php dan tambahkan di dalam group auth:sanctum:
Route::middleware('auth:sanctum')->group(function () {
Route::get('/profile', [App\Http\Controllers\Api\ProfileController::class, 'show']);
Route::put('/profile', [App\Http\Controllers\Api\ProfileController::class, 'update']);
});
Buat controller baru:
php artisan make:controller Api/ProfileController
Isi app/Http/Controllers/Api/ProfileController.php dengan:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
class ProfileController extends Controller
{
/**
* Menampilkan data profil user yang sedang login
*/
public function show(Request $request)
{
$user = $request->user()->load('role');
$data = [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'role' => $user->role->name,
];
// Jika user adalah nasabah, tambahkan data rekening
if ($user->role_id == 3) {
$account = $user->account;
$data['account_number'] = $account ? $account->account_number : null;
$data['balance'] = $account ? $account->balance : 0;
}
return response()->json($data);
}
/**
* Mengupdate data profil (name, email)
*/
public function update(Request $request)
{
$user = $request->user();
$request->validate([
'name' => 'required|string|max:255',
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
]);
$user->name = $request->name;
$user->email = $request->email;
$user->save();
return response()->json([
'message' => 'Profil berhasil diperbarui',
'user' => [
'name' => $user->name,
'email' => $user->email,
]
]);
}
}
Langkah 2: Membuat Halaman Profil di React
Buat file baru src/pages/Profile.js dengan komponen berikut:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Swal from 'sweetalert2';
import { useNavigate } from 'react-router-dom';
function Profile() {
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
const [editing, setEditing] = useState(false);
const [formData, setFormData] = useState({ name: '', email: '' });
const [submitting, setSubmitting] = useState(false);
const navigate = useNavigate();
useEffect(() => {
fetchProfile();
}, []);
const fetchProfile = async () => {
const token = localStorage.getItem('token');
try {
const response = await axios.get('http://127.0.0.1:8000/api/profile', {
headers: { Authorization: `Bearer ${token}` }
});
setProfile(response.data);
setFormData({ name: response.data.name, email: response.data.email });
} catch (error) {
Swal.fire('Error', 'Gagal memuat profil', 'error');
} finally {
setLoading(false);
}
};
const handleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
setSubmitting(true);
const token = localStorage.getItem('token');
try {
await axios.put('http://127.0.0.1:8000/api/profile', formData, {
headers: { Authorization: `Bearer ${token}` }
});
Swal.fire('Berhasil', 'Profil diperbarui', 'success');
setEditing(false);
fetchProfile(); // refresh data
} catch (error) {
const message = error.response?.data?.message || 'Terjadi kesalahan';
Swal.fire('Gagal', message, 'error');
} finally {
setSubmitting(false);
}
};
if (loading) {
return (
<div className="text-center my-5">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
);
}
return (
<div className="container mt-4">
<div className="row justify-content-center">
<div className="col-md-8">
<div className="card">
<div className="card-header bg-primary text-white">
<h4 className="mb-0">Profil Saya</h4>
</div>
<div className="card-body">
{!editing ? (
<>
<div className="profile-info">
<p><strong>Nama:</strong> {profile.name}</p>
<p><strong>Email:</strong> {profile.email}</p>
<p><strong>Role:</strong>
<span className={`badge ms-2 ${
profile.role === 'admin' ? 'bg-danger' :
profile.role === 'petugas' ? 'bg-success' : 'bg-info'
}`}>
{profile.role}
</span>
</p>
{/* Informasi tambahan untuk nasabah */}
{profile.role === 'nasabah' && (
<>
<p><strong>No. Rekening:</strong> {profile.account_number}</p>
<p><strong>Saldo:</strong>
<span className="text-success fw-bold ms-2">
Rp {profile.balance?.toLocaleString()}
</span>
</p>
</>
)}
</div>
<button className="btn btn-primary" onClick={() => setEditing(true)}>
Edit Profil
</button>
</>
) : (
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label className="form-label">Nama</label>
<input
type="text"
className="form-control"
name="name"
value={formData.name}
onChange={handleChange}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Email</label>
<input
type="email"
className="form-control"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="d-flex gap-2">
<button type="submit" className="btn btn-success" disabled={submitting}>
{submitting ? 'Menyimpan...' : 'Simpan'}
</button>
<button type="button" className="btn btn-secondary" onClick={() => setEditing(false)}>
Batal
</button>
</div>
</form>
)}
</div>
</div>
</div>
</div>
</div>
);
}
export default Profile;
Langkah 3: Menambahkan Route dengan Proteksi
Buka src/App.js dan tambahkan route ke halaman profil:
import Profile from './pages/Profile';
// ... di dalam Routes
<Route path="/profile" element={
<PrivateRoute>
<Profile />
</PrivateRoute>
} />
Langkah 4: Menambahkan Link Profil di Navbar
Update src/components/Navbar.js dengan menambahkan item "Profil Saya" di dropdown:
<li>
<Link className="dropdown-item" to="/profile">
Profil Saya
</Link>
</li>
Sehingga dropdown menjadi:
<ul className="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<li>
<Link className="dropdown-item" to="/profile">👤 Profil Saya</Link>
</li>
<li>
<Link className="dropdown-item" to="/change-password">🔐 Ubah Password</Link>
</li>
<li><hr className="dropdown-divider" /></li>
<li>
<button className="dropdown-item text-danger" onClick={handleLogout}>🚪 Logout</button>
</li>
</ul>
Langkah 5: Uji Coba Fitur Profil
- Login sebagai nasabah, petugas, atau admin.
- Klik dropdown nama user, pilih "Profil Saya".
- Lihat data profil: untuk nasabah muncul nomor rekening dan saldo, untuk admin/petugas hanya nama, email, role.
- Klik tombol "Edit Profil", ubah nama atau email, simpan.
- Pastikan data berubah dan muncul notifikasi sukses.
- Coba ubah email dengan email yang sudah dipakai user lain → muncul error validasi dari server.
Validasi dan Keamanan
- Di frontend, kita hanya melakukan validasi required (bisa ditambah validasi email format jika perlu).
- Di backend, kita validasi email unik kecuali milik sendiri.
- Endpoint hanya bisa diakses dengan token yang valid (auth:sanctum).
Kesimpulan
- ✅ Endpoint profil (GET dan PUT) berhasil dibuat di Laravel.
- ✅ Halaman profil di React menampilkan data sesuai role.
- ✅ Pengguna bisa mengedit nama dan email dengan mudah.
- ✅ Validasi memastikan data yang dikirim valid.
Di tutorial selanjutnya (#19: Laporan Transaksi (Admin) – Filter dan Export ke Excel/PDF) kita akan membuat fitur laporan untuk admin, lengkap dengan filter tanggal dan ekspor ke file Excel/PDF. Sampai jumpa!
Daftar Tutorial Lanjutan
- Tutorial #19: Laporan Transaksi (Admin) – Filter dan Export ke Excel/PDF
- Tutorial #20: Finalisasi dan Penyempurnaan Aplikasi