Spaces:
Running
Running
| // src/App.jsx | |
| import React, { useState, useEffect } from 'react'; | |
| import Sidebar from './components/Sidebar/Sidebar'; | |
| // import { auth, db, FieldValue } from './firebase-config.js'; // Không import trực tiếp auth, db nữa | |
| import { generateAvatarUrl } from './utils'; | |
| import eventBus from '../public/js/utils/eventBus.js'; // Import eventBus | |
| import { showModal, hideModal } from '../public/js/modal.js'; // Import modal functions | |
| function App() { | |
| const [currentUser, setCurrentUser] = useState(null); | |
| const [loadingAuth, setLoadingAuth] = useState(true); | |
| const [departments, setDepartments] = useState([]); | |
| const [loadingDepartments, setLoadingDepartments] = useState(true); | |
| const getInitialSection = () => { | |
| const hash = window.location.hash.substring(1); | |
| const validSections = ['dashboard-section', 'tasks-section', 'personnel-section', 'departments-section', 'reports-section', 'calendar-section', 'settings-section']; | |
| return validSections.includes(hash) ? hash : 'dashboard-section'; | |
| }; | |
| const [activeSection, setActiveSection] = useState(getInitialSection()); | |
| const [isMobileMenuOpen, setMobileMenuOpen] = useState(false); | |
| // Listen for auth state changes from vanilla JS (via userManager and listeners.js) | |
| useEffect(() => { | |
| const unsubscribeUser = eventBus.subscribe('userDataUpdated', (userData) => { | |
| console.log('App.jsx: Received userDataUpdated event from EventBus:', userData); | |
| setCurrentUser(userData); | |
| setLoadingAuth(false); | |
| // If user is null, show login modal | |
| if (!userData) { | |
| showModal('loginModal'); | |
| // Ensure vanilla JS also handles hiding main content etc. | |
| // Or: React handles it completely by not rendering main UI | |
| } else { | |
| hideModal('loginModal'); | |
| } | |
| }); | |
| const unsubscribeDepartments = eventBus.subscribe('departmentsDataReady', (deptsData) => { | |
| console.log('App.jsx: Received departmentsDataReady event from EventBus:', deptsData); | |
| setDepartments(deptsData); | |
| setLoadingDepartments(false); | |
| }); | |
| // Set up reactBridge functions for vanilla JS to call | |
| window.reactBridge = { | |
| updateActiveSection: (sectionId) => { | |
| console.log("App.jsx: reactBridge.updateActiveSection called with", sectionId); | |
| setActiveSection(sectionId); | |
| }, | |
| setMobileMenuOpen: (isOpen) => { | |
| console.log("App.jsx: reactBridge.setMobileMenuOpen called with", isOpen); | |
| setMobileMenuOpen(isOpen); | |
| } | |
| // userData and departmentData are now passed via EventBus, not directly via reactBridge | |
| }; | |
| console.log("App.jsx: Bridge functions attached to window.reactBridge"); | |
| return () => { | |
| unsubscribeUser(); | |
| unsubscribeDepartments(); | |
| delete window.reactBridge; // Clean up bridge on unmount | |
| }; | |
| }, []); | |
| // Sync active section with URL hash | |
| useEffect(() => { | |
| if (currentUser) { | |
| window.location.hash = activeSection; | |
| } | |
| }, [activeSection, currentUser]); | |
| // Handle hashchange from browser (back/forward) | |
| useEffect(() => { | |
| const handleHashChange = () => { | |
| const hash = window.location.hash.substring(1); | |
| if (hash && hash !== activeSection) { | |
| setActiveSection(hash); | |
| } | |
| }; | |
| window.addEventListener('hashchange', handleHashChange); | |
| return () => { | |
| window.removeEventListener('hashchange', handleHashChange); | |
| }; | |
| }, [activeSection]); | |
| const handleNavigate = (sectionId) => { | |
| setActiveSection(sectionId); | |
| // This will trigger the useEffect above to update window.location.hash | |
| // And vanillaNavigate (in listeners.js) will listen to hashchange | |
| if (window.innerWidth < 768) { // Close sidebar on mobile after navigation | |
| setMobileMenuOpen(false); | |
| } | |
| }; | |
| const handleLogout = () => { | |
| // This will be handled by the vanilla JS auth.js via the logout button in the header | |
| // userManager.js will then publish 'userLoggedOut' which App.jsx will listen to. | |
| // You can optionally add a direct call to auth.signOut() here if you want React to initiate logout. | |
| // For now, let's keep the vanilla JS button as the initiator. | |
| console.log("App.jsx: Logout button clicked, vanilla JS should handle this."); | |
| }; | |
| // Render logic | |
| if (loadingAuth || (currentUser && loadingDepartments)) { | |
| return ( | |
| <div className="flex-1 flex items-center justify-center h-screen"> | |
| <p>Đang tải ứng dụng...</p> | |
| </div> | |
| ); | |
| } | |
| if (!currentUser) { | |
| // When not logged in, only render the login modal (which is part of vanilla HTML) | |
| // The main content area (main-content) will be display:none by vanilla JS. | |
| return ( | |
| <div className="flex-1 flex flex-col items-center justify-center p-6"> | |
| {/* Content hidden or login form shown by vanilla JS */} | |
| {/* <h1 className="text-2xl font-bold mb-4">Vui lòng đăng nhập</h1> */} | |
| {/* The login modal is rendered by vanilla JS, so no React component for it here */} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="flex h-screen"> | |
| <Sidebar | |
| initialUser={currentUser} | |
| initialDepartments={departments} | |
| initialActiveSection={activeSection} | |
| onNavigate={handleNavigate} | |
| isMobileMenuOpen={isMobileMenuOpen} | |
| setMobileMenuOpen={setMobileMenuOpen} | |
| /> | |
| {/* Main content area, controlled by vanilla JS for now, but React header can be here */} | |
| <div className="flex-1 flex flex-col overflow-hidden"> | |
| {/* Header is part of HTML, but React can manage its logout button etc. */} | |
| {/* For now, the logout button in HTML is handled by vanilla JS */} | |
| <header className="bg-white shadow h-16 flex items-center justify-between px-6 md:hidden"> | |
| {/* The mobile menu button for small screens */} | |
| <button | |
| className="text-gray-500 focus:outline-none" | |
| onClick={() => setMobileMenuOpen(true)} | |
| > | |
| <i className="fas fa-bars text-xl"></i> | |
| </button> | |
| <div className="flex-1"></div> | |
| <div> | |
| {/* This logout button is part of HTML, handled by vanilla JS */} | |
| {/* <button | |
| onClick={handleLogout} | |
| className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" | |
| > | |
| Đăng xuất | |
| </button> */} | |
| </div> | |
| </header> | |
| <main className="flex-1 overflow-x-hidden overflow-y-auto bg-gray-100"> | |
| {/* Nội dung chính sẽ được quản lý bởi JavaScript thuần thông qua class 'active' */} | |
| {/* Chúng ta chỉ cần đảm bảo các phần tử content-section tồn tại trong index.html */} | |
| {/* Và JavaScript thuần (listeners.js) sẽ thêm/bỏ class 'active' */} | |
| {/* Không cần render lại nội dung ở đây bằng React trừ khi chuyển hoàn toàn section đó sang React */} | |
| {/* Ví dụ, bạn có thể render: | |
| {activeSection === 'dashboard-section' && <DashboardComponent />} | |
| nhưng điều đó sẽ là bước tiếp theo của việc chuyển đổi từng section */} | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export default App; |