整合資料庫、完成登入註冊忘記密碼功能
This commit is contained in:
@@ -77,17 +77,27 @@ const mockNotifications: Notification[] = []
|
||||
const mockSearchData: SearchResult[] = []
|
||||
|
||||
export function AdminLayout({ children, currentPage, onPageChange }: AdminLayoutProps) {
|
||||
const { user, logout, isLoading } = useAuth()
|
||||
|
||||
// Move ALL hooks to the top, before any conditional logic
|
||||
const { user, logout, isLoading, isInitialized } = useAuth()
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||||
const [isClient, setIsClient] = useState(false)
|
||||
|
||||
// Search state
|
||||
const [searchQuery, setSearchQuery] = useState("")
|
||||
const [searchResults, setSearchResults] = useState<SearchResult[]>([])
|
||||
const [showSearchResults, setShowSearchResults] = useState(false)
|
||||
|
||||
// Notification state
|
||||
const [notifications, setNotifications] = useState<Notification[]>(mockNotifications)
|
||||
const [showNotifications, setShowNotifications] = useState(false)
|
||||
|
||||
// Logout confirmation state
|
||||
const [showLogoutDialog, setShowLogoutDialog] = useState(false)
|
||||
|
||||
// Set client state after hydration
|
||||
useEffect(() => {
|
||||
setIsClient(true)
|
||||
}, [])
|
||||
|
||||
// Handle search
|
||||
useEffect(() => {
|
||||
if (searchQuery.trim()) {
|
||||
@@ -104,46 +114,6 @@ export function AdminLayout({ children, currentPage, onPageChange }: AdminLayout
|
||||
}
|
||||
}, [searchQuery])
|
||||
|
||||
// 認證檢查 - moved after all hooks
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
|
||||
<span className="text-gray-600">載入中...</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-4">需要登入</h1>
|
||||
<p className="text-gray-600 mb-6">請先登入才能訪問管理員頁面</p>
|
||||
<Button onClick={() => window.location.href = '/'}>
|
||||
返回首頁
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (user.role !== 'admin') {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-4">權限不足</h1>
|
||||
<p className="text-gray-600 mb-6">您沒有訪問管理員頁面的權限</p>
|
||||
<Button onClick={() => window.location.href = '/'}>
|
||||
返回首頁
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Get unread notification count
|
||||
const unreadCount = notifications.filter((n) => !n.read).length
|
||||
|
||||
@@ -207,13 +177,15 @@ export function AdminLayout({ children, currentPage, onPageChange }: AdminLayout
|
||||
setShowLogoutDialog(false)
|
||||
|
||||
// Check if this is a popup/new tab opened from main site
|
||||
if (typeof window !== 'undefined' && window.opener && !window.opener.closed) {
|
||||
// If opened from another window, close this tab and focus parent
|
||||
window.opener.focus()
|
||||
window.close()
|
||||
} else {
|
||||
// If this is the main window or standalone, redirect to homepage
|
||||
window.location.href = "/"
|
||||
if (isClient) {
|
||||
if (window.opener && !window.opener.closed) {
|
||||
// If opened from another window, close this tab and focus parent
|
||||
window.opener.focus()
|
||||
window.close()
|
||||
} else {
|
||||
// If this is the main window or standalone, redirect to homepage
|
||||
window.location.href = "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +208,19 @@ export function AdminLayout({ children, currentPage, onPageChange }: AdminLayout
|
||||
}
|
||||
}
|
||||
|
||||
// 如果還在載入中,顯示載入畫面
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-100">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div>
|
||||
<p className="text-gray-600">載入中...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 檢查用戶權限
|
||||
if (!user || user.role !== "admin") {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-100">
|
||||
@@ -246,11 +231,20 @@ export function AdminLayout({ children, currentPage, onPageChange }: AdminLayout
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">存取被拒</h2>
|
||||
<p className="text-gray-600 mb-4">您沒有管理員權限訪問此頁面</p>
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<div className="text-xs text-gray-500 mb-4">
|
||||
調試信息: 用戶={user ? '已登入' : '未登入'}, 角色={user?.role || '無'}
|
||||
</div>
|
||||
)}
|
||||
<div className="space-x-3">
|
||||
<Button onClick={() => (window.location.href = "/")} variant="outline">
|
||||
<Button onClick={() => {
|
||||
if (isClient) {
|
||||
window.location.href = "/"
|
||||
}
|
||||
}} variant="outline">
|
||||
返回首頁
|
||||
</Button>
|
||||
{typeof window !== 'undefined' && window.opener && !window.opener.closed && (
|
||||
{isClient && window.opener && !window.opener.closed && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.opener.focus()
|
||||
|
Reference in New Issue
Block a user