Initial commit - Lost and Found Revisi
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
// assets/js/utils/api.js
|
||||
const ApiUtils = {
|
||||
call: async (endpoint, options = {}) => {
|
||||
const token = AuthUtils.getToken();
|
||||
const defaultOptions = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(token && { Authorization: `Bearer ${token}` }),
|
||||
},
|
||||
};
|
||||
|
||||
const finalOptions = { ...defaultOptions, ...options };
|
||||
if (options.headers) {
|
||||
finalOptions.headers = { ...defaultOptions.headers, ...options.headers };
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${CONFIG.API_URL}${endpoint}`,
|
||||
finalOptions
|
||||
);
|
||||
|
||||
// 1. Cek jika Unauthorized (401)
|
||||
if (response.status === 401) {
|
||||
AuthUtils.clearAuth();
|
||||
window.location.href = "/login";
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Cek apakah response valid JSON
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (!contentType || !contentType.includes("application/json")) {
|
||||
// Jika bukan JSON (misal text "200 OK" atau HTML error page)
|
||||
const text = await response.text();
|
||||
console.error("API Error (Non-JSON Response):", text);
|
||||
throw new Error("Server returned invalid response format");
|
||||
}
|
||||
|
||||
// 3. Parse JSON dengan aman
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || data.message || "Request failed");
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Fetch error:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
get: async (endpoint) => {
|
||||
return ApiUtils.call(endpoint, { method: "GET" });
|
||||
},
|
||||
|
||||
post: async (endpoint, body) => {
|
||||
return ApiUtils.call(endpoint, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
},
|
||||
|
||||
put: async (endpoint, body) => {
|
||||
return ApiUtils.call(endpoint, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
},
|
||||
|
||||
patch: async (endpoint, body) => {
|
||||
return ApiUtils.call(endpoint, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
},
|
||||
|
||||
delete: async (endpoint) => {
|
||||
return ApiUtils.call(endpoint, { method: "DELETE" });
|
||||
},
|
||||
|
||||
uploadFile: async (endpoint, formData) => {
|
||||
const token = AuthUtils.getToken();
|
||||
|
||||
const response = await fetch(`${CONFIG.API_URL}${endpoint}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
AuthUtils.clearAuth();
|
||||
window.location.href = "/login";
|
||||
}
|
||||
throw new Error(data.error || "Upload failed");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
// assets/js/utils/auth.js
|
||||
const AuthUtils = {
|
||||
getToken: () => localStorage.getItem("token"),
|
||||
|
||||
setToken: (token) => localStorage.setItem("token", token),
|
||||
|
||||
removeToken: () => localStorage.removeItem("token"),
|
||||
|
||||
getCurrentUser: () => {
|
||||
const user = localStorage.getItem("user");
|
||||
return user ? JSON.parse(user) : null;
|
||||
},
|
||||
|
||||
setCurrentUser: (user) => {
|
||||
localStorage.setItem("user", JSON.stringify(user));
|
||||
},
|
||||
|
||||
removeCurrentUser: () => {
|
||||
localStorage.removeItem("user");
|
||||
},
|
||||
|
||||
clearAuth: () => {
|
||||
localStorage.clear();
|
||||
},
|
||||
|
||||
isAuthenticated: () => {
|
||||
return !!AuthUtils.getToken();
|
||||
},
|
||||
|
||||
hasRole: (role) => {
|
||||
const user = AuthUtils.getCurrentUser();
|
||||
return user && user.role === role;
|
||||
},
|
||||
|
||||
redirectBasedOnRole: (user) => {
|
||||
if (!user || !user.role) {
|
||||
window.location.href = "/login";
|
||||
return;
|
||||
}
|
||||
|
||||
const redirectMap = {
|
||||
admin: "/admin",
|
||||
manager: "/manager",
|
||||
user: "/user",
|
||||
};
|
||||
|
||||
window.location.href = redirectMap[user.role] || "/login";
|
||||
},
|
||||
|
||||
checkAuthAndRedirect: (requiredRole = null) => {
|
||||
const user = AuthUtils.getCurrentUser();
|
||||
|
||||
if (!AuthUtils.isAuthenticated() || !user) {
|
||||
window.location.href = "/login";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requiredRole && user.role !== requiredRole) {
|
||||
AuthUtils.redirectBasedOnRole(user);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
// assets/js/utils/helpers.js
|
||||
const Helpers = {
|
||||
formatDate: (dateString) => {
|
||||
return new Date(dateString).toLocaleDateString("id-ID", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
},
|
||||
|
||||
formatDateShort: (dateString) => {
|
||||
return new Date(dateString).toLocaleDateString("id-ID", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
},
|
||||
|
||||
formatDateTime: (dateString) => {
|
||||
return new Date(dateString).toLocaleString("id-ID", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
},
|
||||
|
||||
validateEmail: (email) => {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
},
|
||||
|
||||
validatePhone: (phone) => {
|
||||
const re = /^(08|62)[0-9]{9,12}$/;
|
||||
return re.test(phone);
|
||||
},
|
||||
|
||||
checkPasswordStrength: (password) => {
|
||||
let strength = 0;
|
||||
if (password.length >= 8) strength++;
|
||||
if (password.match(/[a-z]+/)) strength++;
|
||||
if (password.match(/[A-Z]+/)) strength++;
|
||||
if (password.match(/[0-9]+/)) strength++;
|
||||
if (password.match(/[$@#&!]+/)) strength++;
|
||||
|
||||
if (password.length < 8) {
|
||||
return "";
|
||||
} else if (strength <= 2) {
|
||||
return "weak";
|
||||
} else if (strength <= 4) {
|
||||
return "medium";
|
||||
} else {
|
||||
return "strong";
|
||||
}
|
||||
},
|
||||
|
||||
debounce: (func, wait) => {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
},
|
||||
|
||||
truncateText: (text, maxLength) => {
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.substring(0, maxLength) + "...";
|
||||
},
|
||||
|
||||
getStatusBadgeClass: (status) => {
|
||||
const statusClasses = {
|
||||
unclaimed: "bg-blue-500/20 text-blue-400 border border-blue-500/50",
|
||||
verified: "bg-green-500/20 text-green-400 border border-green-500/50",
|
||||
pending_claim:
|
||||
"bg-yellow-500/20 text-yellow-400 border border-yellow-500/50",
|
||||
case_closed: "bg-slate-500/20 text-slate-400 border border-slate-500/50",
|
||||
expired: "bg-red-500/20 text-red-400 border border-red-500/50",
|
||||
active: "bg-green-500/20 text-green-400 border border-green-500/50",
|
||||
blocked: "bg-red-500/20 text-red-400 border border-red-500/50",
|
||||
pending: "bg-yellow-500/20 text-yellow-400 border border-yellow-500/50",
|
||||
approved: "bg-green-500/20 text-green-400 border border-green-500/50",
|
||||
rejected: "bg-red-500/20 text-red-400 border border-red-500/50",
|
||||
};
|
||||
return (
|
||||
statusClasses[status] ||
|
||||
"bg-slate-500/20 text-slate-400 border border-slate-500/50"
|
||||
);
|
||||
},
|
||||
|
||||
getRoleBadgeClass: (role) => {
|
||||
const roleClasses = {
|
||||
admin: "bg-red-500/20 text-red-400 border border-red-500/50",
|
||||
manager: "bg-yellow-500/20 text-yellow-400 border border-yellow-500/50",
|
||||
user: "bg-blue-500/20 text-blue-400 border border-blue-500/50",
|
||||
};
|
||||
return (
|
||||
roleClasses[role] ||
|
||||
"bg-slate-500/20 text-slate-400 border border-slate-500/50"
|
||||
);
|
||||
},
|
||||
|
||||
getInitials: (name) => {
|
||||
if (!name) return "?";
|
||||
return name.charAt(0).toUpperCase();
|
||||
},
|
||||
|
||||
showConfirm: (message) => {
|
||||
return confirm(message);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user