/** * CFF APP CONTEXT * Global State Management sederhana tanpa Redux/Zustand. * * Fungsi: * 1. Mengelola Data User (dari window.CFF_CONFIG yang disuntikkan PHP) * 2. Menyediakan Helper Role (isAdmin, isSineas) * 3. Menangani Notifikasi Global (Toast) * 4. Menyimpan State 'ActiveTab' untuk navigasi antar View */ const { createContext, useContext, useState, useEffect } = React; // 1. Buat Context const AppContext = createContext(); // 2. Provider Component const AppProvider = ({ children }) => { // --- STATE: USER & CONFIG --- // Ambil data inisial langsung dari PHP Injection (The Data Bridge) // Jika tidak ada (dev mode murni html), pakai fallback mock const initialConfig = window.CFF_CONFIG || { user: { id: 0, isLoggedIn: false, name: 'Guest', roles: [], canAccessConsole: false }, siteName: 'CFF Dev', apiUrl: '', nonce: '' }; const [user, setUser] = useState(initialConfig.user); const [config] = useState(initialConfig); // Config statis (nonce, url) // --- STATE: UI & NAVIGATION --- const [activeTab, setActiveTab] = useState('home'); // 'home', 'collab', 'market', 'console', 'auth' const [notification, setNotification] = useState(null); // Toast Message const [isLoading, setIsLoading] = useState(false); // Global Loading Spinner // --- EFFECT: ROUTE SYNC --- // Sinkronisasi URL Browser dengan State activeTab saat load pertama useEffect(() => { const path = window.location.pathname; if (path.includes('/console')) { // Proteksi Rute Console if (user.canAccessConsole) { setActiveTab('console'); } else { // Redirect paksa jika coba akses /console tanpa hak window.history.replaceState(null, '', '/'); setActiveTab('home'); showToast("Akses Ditolak: Area Terbatas", "error"); } } else if (path.includes('/auth')) { if (user.isLoggedIn) { window.history.replaceState(null, '', '/console'); // Sudah login -> ke console/home setActiveTab('console'); } else { setActiveTab('auth'); } } else { setActiveTab('home'); } }, []); // Run once on mount // --- HELPER: ROLE CHECKER --- // Memudahkan pengecekan di komponen UI: {hasRole('administrator') && } const hasRole = (roleSlug) => { if (!user || !user.roles) return false; return user.roles.includes(roleSlug); }; const isAdmin = () => hasRole('administrator'); const isSineas = () => hasRole('sineas'); // --- HELPER: NAVIGATION --- // Mengganti tab sekaligus URL browser (SPA feel) const navigateTo = (tabName, urlPath = null) => { setActiveTab(tabName); let path = urlPath; if (!path) { // Auto-generate path jika tidak disediakan if (tabName === 'home') path = '/'; else if (tabName === 'auth') path = '/auth'; else if (tabName === 'console') path = '/console'; else path = '/'; // Default fallback } // Gunakan PushState agar URL berubah tanpa refresh halaman if (path !== window.location.pathname) { window.history.pushState({ tab: tabName }, '', path); } }; // --- HELPER: TOAST NOTIFICATION --- const showToast = (message, type = 'info') => { setNotification({ message, type }); // Type: 'info', 'success', 'error' setTimeout(() => setNotification(null), 3000); }; // --- HELPER: API FETCHER (Wrapper fetch dengan Nonce WP) --- // Otomatis menyisipkan Nonce WP untuk keamanan request ke Backend const fetchAPI = async (endpoint, options = {}) => { setIsLoading(true); const url = `${config.apiUrl}${endpoint}`; const defaultHeaders = { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce // Header wajib untuk WP REST API }; try { const response = await fetch(url, { ...options, headers: { ...defaultHeaders, ...options.headers } }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || 'Terjadi kesalahan server'); } return data; } catch (error) { console.error("API Error:", error); showToast(error.message, 'error'); throw error; } finally { setIsLoading(false); } }; // --- VALUE EXPORT --- const value = { user, setUser, config, activeTab, setActiveTab, navigateTo, notification, showToast, isLoading, setIsLoading, hasRole, isAdmin, isSineas, fetchAPI }; return ( {children} {/* GLOBAL TOAST COMPONENT (Rendered here for global access) */} {notification && (
{notification.type === 'success' ? : notification.type === 'error' ? ⚠️ : ℹ️} {notification.message}
)} {/* GLOBAL LOADING OVERLAY (Optional, for heavy API calls) */} {isLoading && (
)}
); }; // 3. Custom Hook untuk mempermudah pemakaian const useApp = () => { const context = useContext(AppContext); if (!context) { throw new Error("useApp must be used within an AppProvider"); } return context; }; // Expose ke Global Scope agar bisa diakses file lain tanpa import/require window.AppProvider = AppProvider; window.useApp = useApp;