feat: enhance user profile update functionality and error handling
- Added detailed error messages for various HTTP status codes in useUpdateProfile hook. - Refactored updateProfile service to handle full_name as separate first and last name fields. - Updated UpdateProfilePayload type to allow profile_picture to be a string or File. - Introduced resolveFileUrl utility function for consistent file URL resolution. - Improved loading and error state management in useUpdateProfile hook. - Added DEFAULT_AVATAR constant for default user avatar.
This commit is contained in:
+63
-9
@@ -1,9 +1,10 @@
|
||||
// hooks/useUser.ts
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { loginUser, getProfile, updateProfile } from "../services/UserService";
|
||||
import type { Mahasiswa } from '../types/project.types';
|
||||
import { getMahasiswa } from '../services/MahasiswaService';
|
||||
import type { Mahasiswa } from "../types/project.types";
|
||||
import { getMahasiswa } from "../services/MahasiswaService";
|
||||
import type { CurrentUser, UpdateProfilePayload } from "../types/user.types";
|
||||
import { useRef } from "react";
|
||||
|
||||
// GET /auth-api/me — ambil data user yang sedang login
|
||||
export const useCurrentUser = () => {
|
||||
@@ -54,29 +55,80 @@ export const useLogin = () => {
|
||||
return { login, loading, error, setError };
|
||||
};
|
||||
|
||||
export const DEFAULT_AVATAR = "https://placehold.co/150";
|
||||
|
||||
export const resolveFileUrl = (path?: string | null): string | null => {
|
||||
if (!path) return null;
|
||||
if (/^(https?:|data:|blob:)/i.test(path)) return path;
|
||||
const base = import.meta.env.VITE_API_BASE_URL as string;
|
||||
return `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
||||
};
|
||||
|
||||
// PATCH /auth-api/update-profile
|
||||
export const useUpdateProfile = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [successMsg, setSuccessMsg] = useState<string | null>(null);
|
||||
|
||||
const msgRef = useRef<{ success: string | null; error: string | null }>({
|
||||
success: null,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const save = async (payload: UpdateProfilePayload) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setSuccessMsg(null);
|
||||
msgRef.current = { success: null, error: null };
|
||||
|
||||
try {
|
||||
const updated = await updateProfile(payload);
|
||||
setSuccessMsg("Profile updated successfully!");
|
||||
|
||||
const msg = "Profile updated successfully!";
|
||||
msgRef.current.success = msg;
|
||||
setSuccessMsg(msg);
|
||||
return updated;
|
||||
} catch (err: any) {
|
||||
setError(err?.response?.data?.message || "Failed to save changes.");
|
||||
return null;
|
||||
const status = err?.response?.status;
|
||||
const apiMessage = err?.response?.data?.message;
|
||||
|
||||
let message: string;
|
||||
switch (status) {
|
||||
case 400:
|
||||
message =
|
||||
apiMessage ||
|
||||
"Data yang dikirim tidak valid. Periksa kembali isian Anda.";
|
||||
break;
|
||||
case 401:
|
||||
message = "Sesi Anda telah berakhir. Silakan login kembali.";
|
||||
break;
|
||||
case 403:
|
||||
message = "Anda tidak memiliki izin untuk mengubah profil ini.";
|
||||
break;
|
||||
case 413:
|
||||
message = "Ukuran file terlalu besar. Maksimal 2MB.";
|
||||
break;
|
||||
case 417:
|
||||
message = apiMessage || "Validasi gagal. Periksa kembali isian Anda.";
|
||||
break;
|
||||
case 500:
|
||||
message = "Terjadi kesalahan di server. Coba beberapa saat lagi.";
|
||||
break;
|
||||
default:
|
||||
message = apiMessage || "Failed to save changes.";
|
||||
}
|
||||
|
||||
msgRef.current.error = message;
|
||||
setError(message);
|
||||
|
||||
// WAJIB throw — kalau `return null`, toast selalu masuk cabang success.
|
||||
throw new Error(message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { save, loading, error, successMsg, setSuccessMsg, setError };
|
||||
return { save, loading, error, successMsg, setSuccessMsg, setError, msgRef };
|
||||
};
|
||||
|
||||
// Ambil Seluruh Mahasiswa
|
||||
@@ -89,11 +141,13 @@ export const useMahasiswa = () => {
|
||||
setLoading(true);
|
||||
getMahasiswa()
|
||||
.then(setMahasiswa)
|
||||
.catch(() => setError('Gagal memuat data mahasiswa.'))
|
||||
.catch(() => setError("Gagal memuat data mahasiswa."))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
useEffect(() => { refetch(); }, [refetch]);
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
return { mahasiswa, loading, error, refetch };
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user