238 lines
7.5 KiB
JavaScript
238 lines
7.5 KiB
JavaScript
const { useState, useEffect } = React;
|
|
|
|
const UserApp = () => {
|
|
const state = useUserState();
|
|
const handlers = useUserHandlers(state);
|
|
|
|
const {
|
|
user,
|
|
setUser,
|
|
activeTab,
|
|
setActiveTab,
|
|
stats,
|
|
items,
|
|
setFilteredItems,
|
|
searchTerm,
|
|
categoryFilter,
|
|
showDetailModal,
|
|
setShowDetailModal,
|
|
selectedItem,
|
|
showClaimModal,
|
|
setShowClaimModal,
|
|
showReportLostModal,
|
|
setShowReportLostModal,
|
|
showReportFoundModal,
|
|
setShowReportFoundModal,
|
|
toast,
|
|
setToast,
|
|
loading,
|
|
photoPreview,
|
|
setPhotoPreview,
|
|
showSelectLostItemModal,
|
|
setShowSelectLostItemModal,
|
|
suggestedLostItems,
|
|
claimInitialData,
|
|
categories,
|
|
} = state;
|
|
|
|
useEffect(() => {
|
|
if (!AuthUtils.checkAuthAndRedirect("user")) return;
|
|
const currentUser = AuthUtils.getCurrentUser();
|
|
setUser(currentUser);
|
|
handlers.loadData();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
filterItems();
|
|
}, [searchTerm, categoryFilter, items]);
|
|
|
|
const filterItems = () => {
|
|
let filtered = items.filter((item) => {
|
|
const matchesSearch =
|
|
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
item.location.toLowerCase().includes(searchTerm.toLowerCase());
|
|
const matchesCategory =
|
|
!categoryFilter ||
|
|
Helpers.getCategoryValue(item.category_id) === categoryFilter;
|
|
return matchesSearch && matchesCategory;
|
|
});
|
|
setFilteredItems(filtered);
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-blue-900 to-slate-900">
|
|
<Navbar user={user} onLogout={handlers.handleLogout} userType="user" />
|
|
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold text-white mb-2">Dashboard User</h1>
|
|
<p className="text-slate-400">
|
|
Selamat datang di Lost & Found System
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
|
<StatCard
|
|
title="Barang Hilang Saya"
|
|
value={stats.lost_items || 0}
|
|
icon="😢"
|
|
/>
|
|
<StatCard
|
|
title="Barang yang Saya Temukan"
|
|
value={stats.found_items || 0}
|
|
icon="🎉"
|
|
/>
|
|
<StatCard title="Klaim Saya" value={stats.claims || 0} icon="🤝" />
|
|
</div>
|
|
|
|
<div className="flex gap-2 mb-6 flex-wrap">
|
|
<button
|
|
onClick={() => setActiveTab("browse")}
|
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
|
activeTab === "browse"
|
|
? "bg-gradient-to-r from-blue-600 to-blue-700 text-white shadow-lg shadow-blue-500/50"
|
|
: "bg-slate-800 text-slate-300 hover:bg-slate-700 border border-slate-700"
|
|
}`}
|
|
>
|
|
📦 Barang Ditemukan
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveTab("public-lost")}
|
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
|
activeTab === "public-lost"
|
|
? "bg-gradient-to-r from-blue-600 to-blue-700 text-white shadow-lg shadow-blue-500/50"
|
|
: "bg-slate-800 text-slate-300 hover:bg-slate-700 border border-slate-700"
|
|
}`}
|
|
>
|
|
😢 Barang Hilang
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveTab("lost")}
|
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
|
activeTab === "lost"
|
|
? "bg-gradient-to-r from-blue-600 to-blue-700 text-white shadow-lg shadow-blue-500/50"
|
|
: "bg-slate-800 text-slate-300 hover:bg-slate-700 border border-slate-700"
|
|
}`}
|
|
>
|
|
😢 Barang Hilang Saya
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveTab("found")}
|
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
|
activeTab === "found"
|
|
? "bg-gradient-to-r from-blue-600 to-blue-700 text-white shadow-lg shadow-blue-500/50"
|
|
: "bg-slate-800 text-slate-300 hover:bg-slate-700 border border-slate-700"
|
|
}`}
|
|
>
|
|
🎉 Barang yang Saya Temukan
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveTab("claims")}
|
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
|
activeTab === "claims"
|
|
? "bg-gradient-to-r from-blue-600 to-blue-700 text-white shadow-lg shadow-blue-500/50"
|
|
: "bg-slate-800 text-slate-300 hover:bg-slate-700 border border-slate-700"
|
|
}`}
|
|
>
|
|
🤝 Klaim Saya
|
|
</button>
|
|
</div>
|
|
|
|
{activeTab === "browse" && (
|
|
<BrowseTab state={state} handlers={handlers} />
|
|
)}
|
|
|
|
{activeTab === "public-lost" && (
|
|
<PublicLostItemsTab state={state} handlers={handlers} />
|
|
)}
|
|
|
|
{activeTab === "lost" && (
|
|
<MyLostItemsTab
|
|
state={state}
|
|
handlers={handlers}
|
|
myClaims={state.myClaims}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === "found" && (
|
|
<MyFoundItemsTab state={state} handlers={handlers} />
|
|
)}
|
|
|
|
{activeTab === "claims" && (
|
|
<MyClaimsTab state={state} handlers={handlers} />
|
|
)}
|
|
</div>
|
|
|
|
<SelectLostItemModal
|
|
isOpen={showSelectLostItemModal}
|
|
onClose={() => state.setShowSelectLostItemModal(false)}
|
|
lostItems={suggestedLostItems}
|
|
onSelect={handlers.handleSelectLostItemForClaim}
|
|
onManual={handlers.handleManualClaimEntry}
|
|
/>
|
|
|
|
<FoundOptionModal
|
|
isOpen={state.showFoundOptionModal}
|
|
onClose={() => state.setShowFoundOptionModal(false)}
|
|
onSelectOption={handlers.handleSelectFoundOption}
|
|
/>
|
|
|
|
<DirectClaimModal
|
|
isOpen={state.showDirectClaimModal}
|
|
onClose={() => state.setShowDirectClaimModal(false)}
|
|
onSubmit={handlers.handleDirectClaimSubmit}
|
|
loading={loading}
|
|
lostItem={state.selectedLostItemForFound}
|
|
/>
|
|
|
|
<DetailModalUser
|
|
isOpen={state.showDetailModal}
|
|
onClose={() => state.setShowDetailModal(false)}
|
|
item={state.selectedItem}
|
|
user={state.user}
|
|
onClaim={handlers.handleClaim}
|
|
/>
|
|
|
|
<ClaimModalUser
|
|
isOpen={showClaimModal}
|
|
onClose={() => state.setShowClaimModal(false)}
|
|
onSubmit={handlers.submitClaim}
|
|
loading={loading}
|
|
initialData={claimInitialData}
|
|
categories={categories}
|
|
/>
|
|
|
|
<ReportLostModalUser
|
|
isOpen={showReportLostModal}
|
|
onClose={() => state.setShowReportLostModal(false)}
|
|
onSubmit={handlers.submitReportLost}
|
|
loading={loading}
|
|
categories={categories}
|
|
/>
|
|
|
|
<ReportFoundModalUser
|
|
isOpen={showReportFoundModal}
|
|
onClose={() => state.setShowReportFoundModal(false)}
|
|
onSubmit={handlers.submitReportFound}
|
|
loading={loading}
|
|
photoPreview={photoPreview}
|
|
onPhotoChange={handlers.handlePhotoChange}
|
|
user={user}
|
|
categories={categories}
|
|
/>
|
|
|
|
{state.toast && (
|
|
<Toast
|
|
message={state.toast.message}
|
|
type={state.toast.type}
|
|
onClose={() => state.setToast(null)}
|
|
/>
|
|
)}
|
|
|
|
<AIChatbot />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
ReactDOM.render(<UserApp />, document.getElementById("root"));
|