328 lines
11 KiB
TypeScript
328 lines
11 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { LandingPage } from './pages/LandingPage';
|
|
import { ExploreMap } from './pages/ExploreMap';
|
|
import { TourDetailPage } from './pages/TourDetailPage';
|
|
import SignupPage from './pages/SignupPage';
|
|
import { MyPhotosPage } from './pages/MyPhotosPage';
|
|
import { MyNotePage } from './pages/MyNotePage';
|
|
import { JoinTourPage } from './pages/JoinTourPage';
|
|
import { MemberDashboard } from './pages/MemberDashboard';
|
|
import { AdminDashboard } from './pages/AdminDashboard';
|
|
import { ShareJourneyPage } from './pages/ShareJourneyPage';
|
|
import { ConfirmProvider } from './hooks/useConfirm';
|
|
import { NotificationProvider } from './hooks/useNotification';
|
|
|
|
function App() {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const viewTourId = params.get('viewTour');
|
|
const pathParts = window.location.pathname.split('/');
|
|
const isJourneyShare = pathParts[1] === 'journey' && pathParts[2];
|
|
const journeyTokenVal = isJourneyShare ? pathParts[2] : null;
|
|
|
|
const [user, setUser] = useState<any>(null);
|
|
const [shareJourneyToken, setShareJourneyToken] = useState<string | null>(journeyTokenVal);
|
|
const [currentPage, setCurrentPage] = useState<'landing' | 'dashboard' | 'admin' | 'explore' | 'tourDetail' | 'signup' | 'myPhotos' | 'notes' | 'joinTour' | 'shareJourney'>(
|
|
journeyTokenVal ? 'shareJourney' : (viewTourId ? 'tourDetail' : (window.location.pathname === '/join-tour' || params.has('token') ? 'joinTour' : 'landing'))
|
|
);
|
|
const [currentTourId, setCurrentTourId] = useState<string | null>(viewTourId);
|
|
const [isPublicTourView, setIsPublicTourView] = useState(!!viewTourId);
|
|
const [previousPage, setPreviousPage] = useState<'explore' | 'dashboard' | 'landing'>('explore');
|
|
|
|
useEffect(() => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const viewTourId = params.get('viewTour');
|
|
const isJoinTour = window.location.pathname === '/join-tour' || params.has('token');
|
|
const pathParts = window.location.pathname.split('/');
|
|
const journeyToken = pathParts[1] === 'journey' && pathParts[2] ? pathParts[2] : null;
|
|
|
|
// Check if user just finished uploading from a public tour
|
|
const fromPublicUpload = localStorage.getItem('fromPublicUpload');
|
|
if (fromPublicUpload) {
|
|
localStorage.removeItem('fromPublicUpload');
|
|
setCurrentPage('landing');
|
|
return;
|
|
}
|
|
|
|
// Khôi phục thông tin đăng nhập nếu có
|
|
const token = localStorage.getItem('token');
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
const storedUser = localStorage.getItem('user');
|
|
const storedGuestUser = localStorage.getItem('guest_user');
|
|
|
|
let loggedInUser = null;
|
|
if (token && storedUser) {
|
|
try {
|
|
loggedInUser = JSON.parse(storedUser);
|
|
setUser(loggedInUser);
|
|
} catch (e) {
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('user');
|
|
}
|
|
}
|
|
|
|
if (journeyToken) {
|
|
setShareJourneyToken(journeyToken);
|
|
setCurrentPage('shareJourney');
|
|
} else if (isJoinTour) {
|
|
setCurrentPage('joinTour');
|
|
} else if (viewTourId) {
|
|
setCurrentPage('tourDetail');
|
|
} else {
|
|
// CRITICAL: Check for guest token FIRST - guests should NEVER access dashboard
|
|
// regardless of whether they also have other tokens
|
|
if (guestToken) {
|
|
setCurrentPage('landing');
|
|
} else if (loggedInUser) {
|
|
// Real authenticated user (no guest token)
|
|
if (loggedInUser.isAdmin) {
|
|
setCurrentPage('admin');
|
|
} else {
|
|
setCurrentPage('dashboard');
|
|
}
|
|
} else {
|
|
setCurrentPage('landing');
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
const handleLoginSuccess = (loggedInUser: any) => {
|
|
setUser(loggedInUser);
|
|
|
|
// Nếu có pending token, ta vẫn giữ ở trang joinTour để nó tự động thực hiện join
|
|
const pendingInviteToken = localStorage.getItem('pendingInviteToken');
|
|
if (pendingInviteToken) {
|
|
setCurrentPage('joinTour');
|
|
} else if (loggedInUser.isAdmin) {
|
|
// Nếu là admin, chuyển đến admin dashboard
|
|
setCurrentPage('admin');
|
|
} else {
|
|
// Only set to dashboard if this is a real user (has token), not a guest
|
|
const token = localStorage.getItem('token');
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
if (token && !guestToken) {
|
|
setCurrentPage('dashboard');
|
|
} else {
|
|
setCurrentPage('landing');
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('user');
|
|
setUser(null);
|
|
setCurrentPage('landing');
|
|
};
|
|
|
|
const handleViewTour = (tourId: string, fromPage?: 'explore' | 'dashboard') => {
|
|
setCurrentTourId(tourId);
|
|
setIsPublicTourView(false); // Không phải chế độ công khai nếu đến từ bản đồ khám phá
|
|
setPreviousPage(fromPage || (currentPage === 'dashboard' ? 'dashboard' : 'explore'));
|
|
setCurrentPage('tourDetail');
|
|
};
|
|
|
|
const handleBackFromTourDetail = () => {
|
|
// Check if this was a public view BEFORE clearing the flag
|
|
const wasPublicView = isPublicTourView;
|
|
|
|
setCurrentTourId(null);
|
|
setIsPublicTourView(false);
|
|
|
|
// If user was viewing a public tour, redirect to index/landing page
|
|
// Otherwise redirect based on authentication and previous page
|
|
if (wasPublicView) {
|
|
// Public tour view - always redirect to index/landing
|
|
setCurrentPage('landing');
|
|
} else if (user) {
|
|
// Authenticated user viewing their own tour - go back to previous page
|
|
setCurrentPage(previousPage);
|
|
} else {
|
|
// Not authenticated and not public view - go to landing
|
|
setCurrentPage('landing');
|
|
}
|
|
};
|
|
|
|
const handleBackFromSignup = () => {
|
|
const pendingInviteToken = localStorage.getItem('pendingInviteToken');
|
|
if (pendingInviteToken) {
|
|
setCurrentPage('joinTour');
|
|
} else {
|
|
setCurrentPage('landing');
|
|
}
|
|
};
|
|
|
|
const handleSignupSuccess = () => {
|
|
// Sau khi đăng ký, ta có thể tự động đăng nhập hoặc quay lại landing/joinTour
|
|
const pendingInviteToken = localStorage.getItem('pendingInviteToken');
|
|
const token = localStorage.getItem('token');
|
|
const storedUser = localStorage.getItem('user');
|
|
|
|
if (token && storedUser) {
|
|
try {
|
|
const loggedInUser = JSON.parse(storedUser);
|
|
setUser(loggedInUser);
|
|
if (pendingInviteToken) {
|
|
setCurrentPage('joinTour');
|
|
} else {
|
|
setCurrentPage('dashboard');
|
|
}
|
|
return;
|
|
} catch (e) {}
|
|
}
|
|
|
|
if (pendingInviteToken) {
|
|
setCurrentPage('joinTour');
|
|
} else {
|
|
setCurrentPage('landing');
|
|
}
|
|
};
|
|
|
|
const handleBackFromExplore = () => {
|
|
// Only allow real users (with token, not guest_token)
|
|
const token = localStorage.getItem('token');
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
const isRealUser = token && !guestToken;
|
|
|
|
if (user && isRealUser) {
|
|
setCurrentPage('dashboard');
|
|
} else {
|
|
setCurrentPage('landing');
|
|
}
|
|
};
|
|
|
|
const handleGoToDashboard = () => {
|
|
// Only allow real users (with token, not guest_token)
|
|
const token = localStorage.getItem('token');
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
const isRealUser = token && !guestToken;
|
|
|
|
if (user && isRealUser) {
|
|
setPreviousPage('explore');
|
|
setCurrentPage('dashboard');
|
|
} else {
|
|
setCurrentPage('landing');
|
|
}
|
|
};
|
|
|
|
const handleGoToHome = () => {
|
|
window.history.pushState({}, '', '/');
|
|
setShareJourneyToken(null);
|
|
const token = localStorage.getItem('token');
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
// Only real authenticated users can access dashboard, not guests
|
|
const isRealUser = token && !guestToken;
|
|
setCurrentPage(isRealUser ? 'dashboard' : 'landing');
|
|
};
|
|
|
|
return (
|
|
<ConfirmProvider>
|
|
<NotificationProvider>
|
|
{(() => {
|
|
if (currentPage === 'admin') {
|
|
return (
|
|
<AdminDashboard
|
|
user={user}
|
|
onNavigate={setCurrentPage}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (currentPage === 'dashboard') {
|
|
// SECURITY: Prevent any guest from accessing dashboard
|
|
const guestToken = localStorage.getItem('guest_token');
|
|
if (guestToken) {
|
|
console.warn('[App] Guest user attempted to access dashboard - forcing redirect to landing');
|
|
setCurrentPage('landing');
|
|
return (
|
|
<LandingPage
|
|
onLoginSuccess={handleLoginSuccess}
|
|
onGoToSignup={() => setCurrentPage('signup')}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<MemberDashboard
|
|
user={user}
|
|
onLogout={handleLogout}
|
|
onExploreTours={() => setCurrentPage('explore')}
|
|
onViewTour={handleViewTour}
|
|
onOpenMyPhotos={() => setCurrentPage('myPhotos')}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (currentPage === 'tourDetail') {
|
|
return (
|
|
<TourDetailPage
|
|
tourId={currentTourId!}
|
|
onBack={handleBackFromTourDetail}
|
|
isPublicView={isPublicTourView}
|
|
onOpenNotes={() => setCurrentPage('notes')}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (currentPage === 'notes') {
|
|
return <MyNotePage tourId={currentTourId!} onBack={() => setCurrentPage('tourDetail')} />;
|
|
}
|
|
|
|
if (currentPage === 'explore') {
|
|
return (
|
|
<ExploreMap
|
|
onBack={handleBackFromExplore}
|
|
onLogout={handleLogout}
|
|
user={user}
|
|
onViewTour={handleViewTour}
|
|
onOpenMyPhotos={() => setCurrentPage('myPhotos')}
|
|
onLoginSuccess={handleLoginSuccess}
|
|
onGoToDashboard={handleGoToDashboard}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (currentPage === 'myPhotos') {
|
|
return (
|
|
<MyPhotosPage onBack={() => setCurrentPage('explore')} />
|
|
);
|
|
}
|
|
|
|
if (currentPage === 'signup') {
|
|
return <SignupPage onBack={handleBackFromSignup} onSuccess={handleSignupSuccess} />;
|
|
}
|
|
|
|
if (currentPage === 'joinTour') {
|
|
return (
|
|
<JoinTourPage
|
|
onLoginSuccess={handleLoginSuccess}
|
|
onGoToSignup={() => setCurrentPage('signup')}
|
|
onViewTour={(tourId) => {
|
|
setCurrentTourId(tourId);
|
|
setIsPublicTourView(false);
|
|
setCurrentPage('tourDetail');
|
|
}}
|
|
onGoToHome={() => {
|
|
const loggedIn = !!localStorage.getItem('token');
|
|
setCurrentPage(loggedIn ? 'explore' : 'landing');
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (currentPage === 'shareJourney') {
|
|
return (
|
|
<ShareJourneyPage
|
|
token={shareJourneyToken!}
|
|
onGoToHome={handleGoToHome}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return <LandingPage onContinue={() => setCurrentPage('explore')} onGoToSignup={() => setCurrentPage('signup')} onGoToMap={() => setCurrentPage('explore')} onLoginSuccess={handleLoginSuccess} isInitialSetup={false} />;
|
|
})()}
|
|
</NotificationProvider>
|
|
</ConfirmProvider>
|
|
);
|
|
}
|
|
|
|
export default App; |