### Local Development Setup and Build Source: https://github.com/leaksopan/sipandai/blob/master/README.md Instructions for setting up the project locally, including installing dependencies, configuring environment variables for Firebase, and running the development server. Also includes the command for building the production-ready application. ```bash # Install dependencies npm install # Create a .env file in the root directory with your Firebase credentials # REACT_APP_FIREBASE_API_KEY=xxx # REACT_APP_FIREBASE_AUTH_DOMAIN=xxx # REACT_APP_FIREBASE_PROJECT_ID=xxx # REACT_APP_FIREBASE_STORAGE_BUCKET=xxx # REACT_APP_FIREBASE_MESSAGING_SENDER_ID=xxx # REACT_APP_FIREBASE_APP_ID=xxx # REACT_APP_FIREBASE_MEASUREMENT_ID=xxx # Start the development server npm start # Build for production npm run build ``` -------------------------------- ### React File Search Component Example Source: https://context7.com/leaksopan/sipandai/llms.txt A React component demonstrating how to use the file search and filtering functions. It manages state for search terms, dates, and results, and provides buttons to trigger different search modes. This component relies on the `useState` hook and the previously defined search/filter functions. ```javascript // Example: Search component function FileSearchComponent() { const [searchTerm, setSearchTerm] = useState(''); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); const [results, setResults] = useState([]); const [searchMode, setSearchMode] = useState('simple'); const handleSimpleSearch = async () => { const files = await searchFiles(searchTerm); setResults(files); }; const handleDateFilter = async () => { const files = await filterFilesByDateRange(startDate, endDate); setResults(files); }; const handleAdvancedSearch = async () => { const filters = { name: searchTerm, startDate, endDate, type: 'pdf', minSize: 0, maxSize: 10 * 1024 * 1024 // 10 MB }; const files = await advancedSearch(filters); setResults(files); }; return (
| Name | Role | Status | Actions | |
|---|---|---|---|---|
| {user.displayName} | {user.email} | {user.isActive ? 'Active' : 'Inactive'} |
Email: {app.userEmail}
Section: {app.section}
``` -------------------------------- ### Firebase Authentication Hook Source: https://github.com/leaksopan/sipandai/blob/master/README.md Manages user authentication state, including login (Google, email/password), registration, logout, and synchronization of user data with Firestore. It provides authentication status and user information. ```javascript import { useState, useEffect } from 'react'; import { auth, db } from '../firebase/config'; import { onAuthStateChanged, GoogleAuthProvider, signInWithPopup, signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut } from 'firebase/auth'; import { doc, setDoc, getDoc, updateDoc } from 'firebase/firestore'; const useAuth = () => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => { if (firebaseUser) { const userDocRef = doc(db, 'users', firebaseUser.uid); const userDocSnap = await getDoc(userDocRef); if (userDocSnap.exists()) { setUser({ ...firebaseUser, ...userDocSnap.data() }); } else { // New user, create profile with default role await setDoc(userDocRef, { email: firebaseUser.email, role: 'guest', isActive: true }); setUser({ ...firebaseUser, role: 'guest', isActive: true }); } } else { setUser(null); } setLoading(false); }); return unsubscribe; }, []); const loginWithGoogle = async () => { const provider = new GoogleAuthProvider(); try { const result = await signInWithPopup(auth, provider); // User logged in, effect handles profile sync return result.user; } catch (error) { console.error('Google Sign-In Error:', error); throw error; } }; const loginWithEmailPassword = async (email, password) => { try { const userCredential = await signInWithEmailAndPassword(auth, email, password); // User logged in, effect handles profile sync return userCredential.user; } catch (error) { console.error('Email/Password Login Error:', error); throw error; } }; const registerWithEmailPassword = async (email, password) => { try { const userCredential = await createUserWithEmailAndPassword(auth, email, password); // User registered, create profile with default role const userDocRef = doc(db, 'users', userCredential.user.uid); await setDoc(userDocRef, { email: email, role: 'guest', isActive: true }); setUser({ ...userCredential.user, role: 'guest', isActive: true }); return userCredential.user; } catch (error) { console.error('Email/Password Registration Error:', error); throw error; } }; const logout = async () => { await signOut(auth); setUser(null); }; const updateUserRole = async (uid, newRole) => { try { const userDocRef = doc(db, 'users', uid); await updateDoc(userDocRef, { role: newRole }); setUser(prevUser => prevUser ? { ...prevUser, role: newRole } : null); return true; } catch (error) { console.error('Error updating user role:', error); return false; } }; const toggleUserActiveStatus = async (uid, isActive) => { try { const userDocRef = doc(db, 'users', uid); await updateDoc(userDocRef, { isActive: isActive }); setUser(prevUser => prevUser ? { ...prevUser, isActive: isActive } : null); return true; } catch (error) { console.error('Error toggling user active status:', error); return false; } }; return { user, loading, loginWithGoogle, loginWithEmailPassword, registerWithEmailPassword, logout, updateUserRole, toggleUserActiveStatus }; }; export default useAuth; ``` -------------------------------- ### User Role and Permissions Hook Source: https://github.com/leaksopan/sipandai/blob/master/README.md Fetches user roles from Firestore and provides methods to check permissions for specific actions or UI elements. It maps roles to granular permissions and offers helper functions for displaying role labels. ```javascript import { useState, useEffect, useContext } from 'react'; import { db } from '../firebase/config'; import { doc, onSnapshot } from 'firebase/firestore'; import { AuthContext } from './AuthContext'; // Assuming you have an AuthContext const rolePermissions = { super_admin: { canViewDashboard: true, canManageUsers: true, canAccessFileManager: true, canUploadFiles: true, canDownloadFiles: true, canApproveStaff: true, canDeleteFiles: true, canCreateFolders: true, }, Irban: { canViewDashboard: true, canManageUsers: false, canAccessFileManager: true, canUploadFiles: true, canDownloadFiles: true, canApproveStaff: true, canDeleteFiles: true, canCreateFolders: true, }, Auditor: { canViewDashboard: true, canManageUsers: false, canAccessFileManager: true, canUploadFiles: true, canDownloadFiles: false, // Auditors have limited download permissions canApproveStaff: false, canDeleteFiles: false, canCreateFolders: false, }, guest: { canViewDashboard: true, canManageUsers: false, canAccessFileManager: false, canUploadFiles: false, canDownloadFiles: false, canApproveStaff: false, canDeleteFiles: false, canCreateFolders: false, }, }; const roleLabels = { super_admin: 'Super Admin', Irban: 'Irban', Auditor: 'Auditor', guest: 'Guest', }; const useUserRole = () => { const { user } = useContext(AuthContext); // Get user from AuthContext const [role, setRole] = useState('guest'); const [loadingRole, setLoadingRole] = useState(true); useEffect(() => { if (user) { const userDocRef = doc(db, 'users', user.uid); const unsubscribe = onSnapshot(userDocRef, (docSnap) => { if (docSnap.exists()) { const userData = docSnap.data(); setRole(userData.role || 'guest'); } else { setRole('guest'); // Default if user doc somehow missing } setLoadingRole(false); }); return () => unsubscribe(); // Cleanup subscription on unmount } else { setRole('guest'); // Reset role if user logs out setLoadingRole(false); } }, [user]); // Re-run effect if user changes const hasPermission = (permission) => { const currentRolePermissions = rolePermissions[role] || {}; return currentRolePermissions[permission] || false; }; const getRoleLabel = () => { return roleLabels[role] || 'Unknown Role'; }; return { role, loadingRole, hasPermission, getRoleLabel, permissions: rolePermissions[role] || {} }; }; export default useUserRole; ``` -------------------------------- ### Batch Upload Files with Progress Tracking (JavaScript) Source: https://context7.com/leaksopan/sipandai/llms.txt This function handles uploading multiple files sequentially, providing progress updates for the entire batch. It iterates through an array of files, calling the single upload function for each and aggregating results. A progress callback function is essential for real-time UI updates. It depends on the `uploadFileWithMetadata` function and requires user authentication. ```javascript async function uploadMultipleFiles(files, currentFolder, user, onProgress) { const totalFiles = files.length; let completed = 0; const results = []; for (const file of files) { try { const result = await uploadFileWithMetadata(file, currentFolder, user); results.push(result); completed++; // Update progress const progress = (completed / totalFiles) * 100; onProgress(progress, completed, totalFiles); } catch (error) { console.error(`Failed to upload ${file.name}:`, error); results.push({ success: false, fileName: file.name, error }); } } return results; } ``` -------------------------------- ### File Upload Component with Progress Display (JavaScript) Source: https://context7.com/leaksopan/sipandai/llms.txt A React component demonstrating the usage of the batch file upload functionality. It includes an input element for file selection and displays the upload progress percentage. This component requires React, `useState` hook for state management, and a `useAuth` hook for user authentication. It integrates with `uploadMultipleFiles` to provide user feedback during uploads. ```javascript import { useState } from 'react'; import { useAuth } from './auth'; // Assuming useAuth is defined elsewhere // Assume uploadMultipleFiles is imported or defined in the same scope function FileUploadComponent() { const [uploadProgress, setUploadProgress] = useState(0); const { user } = useAuth(); const handleFileSelect = async (event) => { const files = Array.from(event.target.files); await uploadMultipleFiles( files, '/documents/2024', user, (progress, completed, total) => { setUploadProgress(progress); console.log(`Uploaded ${completed} of ${total} files (${progress.toFixed(1)}%)`); } ); console.log('All uploads completed'); }; return (You have limited access. Apply for staff role to unlock features.
Your application has been submitted and is pending approval.
Welcome, {user.displayName}
| Timestamp | User | Action | Target | Details |
|---|---|---|---|---|
| {new Date(log.createdAt).toLocaleString()} | {log.userName} ({log.userEmail}) | {log.action} | {log.targetType}: {log.targetName} | {log.details} |