Update site navigation and logout functionality

Refactors client-side navigation to use `wouter`'s `setLocation` instead of `window.location.href` for smoother transitions and improves the logout process by making it an asynchronous POST request with proper error handling and state invalidation. Also adds an "Auctions" button to the main navigation bar on multiple pages.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 069d4324-6c40-4355-955e-c714a50de1ea
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/069d4324-6c40-4355-955e-c714a50de1ea/bLfICpO
This commit is contained in:
kimjaehyeon0101
2025-09-29 21:18:33 +00:00
parent 00b92a8a45
commit d99a0580e6
7 changed files with 113 additions and 46 deletions

View File

@ -22,10 +22,6 @@ externalPort = 3002
localPort = 37531 localPort = 37531
externalPort = 3001 externalPort = 3001
[[ports]]
localPort = 40799
externalPort = 3003
[[ports]] [[ports]]
localPort = 43349 localPort = 43349
externalPort = 3000 externalPort = 3000

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -11,10 +11,13 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import type { MediaOutlet } from "@shared/schema"; import type { MediaOutlet } from "@shared/schema";
import MediaOutletManagement from "@/components/MediaOutletManagement"; import MediaOutletManagement from "@/components/MediaOutletManagement";
import Footer from "@/components/Footer"; import Footer from "@/components/Footer";
import { useLocation } from "wouter";
import { queryClient } from "@/lib/queryClient";
export default function AdminDashboard() { export default function AdminDashboard() {
const { user, isLoading } = useAuth(); const { user, isLoading } = useAuth();
const { toast } = useToast(); const { toast } = useToast();
const [, setLocation] = useLocation();
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [selectedOutlet, setSelectedOutlet] = useState<MediaOutlet | null>(null); const [selectedOutlet, setSelectedOutlet] = useState<MediaOutlet | null>(null);
const [managingOutlet, setManagingOutlet] = useState<MediaOutlet | null>(null); const [managingOutlet, setManagingOutlet] = useState<MediaOutlet | null>(null);
@ -29,7 +32,7 @@ export default function AdminDashboard() {
variant: "destructive", variant: "destructive",
}); });
setTimeout(() => { setTimeout(() => {
window.location.href = "/"; setLocation("/");
}, 500); }, 500);
} }
}, [isLoading, user, toast]); }, [isLoading, user, toast]);
@ -78,8 +81,29 @@ export default function AdminDashboard() {
}); });
}; };
const handleLogout = () => { const handleLogout = async () => {
window.location.href = "/api/logout"; try {
const response = await fetch("/api/logout", {
method: "POST",
credentials: "include",
});
if (response.ok) {
toast({
title: "Logged Out",
description: "You have been successfully logged out.",
});
queryClient.invalidateQueries({ queryKey: ["/api/auth/user"] });
setLocation("/");
}
} catch (error) {
toast({
title: "Logout Error",
description: "An error occurred while logging out.",
variant: "destructive",
});
}
}; };
if (isLoading || !user || (user.role !== 'admin' && user.role !== 'superadmin')) { if (isLoading || !user || (user.role !== 'admin' && user.role !== 'superadmin')) {
@ -107,14 +131,13 @@ export default function AdminDashboard() {
<div className="max-w-7xl mx-auto px-6 py-4"> <div className="max-w-7xl mx-auto px-6 py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
<a href="/" className="cursor-pointer" data-testid="logo-link"> <img
<img src="/attached_assets/logo_black_1759162717640.png"
src="/attached_assets/logo_black_1759162717640.png" alt="SAPIENS"
alt="SAPIENS" className="h-6 w-auto hover:opacity-80 transition-opacity cursor-pointer"
className="h-6 w-auto hover:opacity-80 transition-opacity" data-testid="logo-sapiens"
data-testid="logo-sapiens" onClick={() => setLocation("/")}
/> />
</a>
<div className="border-l border-gray-300 h-6"></div> <div className="border-l border-gray-300 h-6"></div>
<div> <div>
<h1 className="text-lg font-bold text-gray-900">Admin Dashboard</h1> <h1 className="text-lg font-bold text-gray-900">Admin Dashboard</h1>
@ -135,6 +158,15 @@ export default function AdminDashboard() {
/> />
</div> </div>
<Button
variant="ghost"
size="sm"
onClick={() => setLocation("/auctions")}
data-testid="button-auctions"
>
Auctions
</Button>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"

View File

@ -14,6 +14,7 @@ import AuctionCard from "@/components/AuctionCard";
import type { Auction, MediaOutlet } from "@shared/schema"; import type { Auction, MediaOutlet } from "@shared/schema";
import { BookOpen } from "lucide-react"; import { BookOpen } from "lucide-react";
import { Link } from "wouter"; import { Link } from "wouter";
import Footer from "@/components/Footer";
export default function Auctions() { export default function Auctions() {
const { user, isAuthenticated } = useAuth(); const { user, isAuthenticated } = useAuth();
@ -157,7 +158,7 @@ export default function Auctions() {
}; };
return ( return (
<div className="min-h-screen bg-background"> <div className="flex flex-col min-h-screen bg-background">
{/* Header */} {/* Header */}
<header className="bg-card border-b border-border sticky top-0 z-50"> <header className="bg-card border-b border-border sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-6 py-4"> <div className="max-w-7xl mx-auto px-6 py-4">
@ -215,7 +216,7 @@ export default function Auctions() {
</div> </div>
</header> </header>
<main className="max-w-7xl mx-auto px-6 py-8"> <main className="flex-1 max-w-7xl mx-auto px-6 py-8 w-full">
{/* Page Header */} {/* Page Header */}
<div className="mb-8"> <div className="mb-8">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
@ -418,6 +419,7 @@ export default function Auctions() {
)} )}
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<Footer />
</div> </div>
); );
} }

View File

@ -8,12 +8,14 @@ import LoginModal from "@/components/LoginModal";
import SearchModal from "@/components/SearchModal"; import SearchModal from "@/components/SearchModal";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { queryClient } from "@/lib/queryClient"; import { queryClient } from "@/lib/queryClient";
import { useLocation } from "wouter";
export default function Home() { export default function Home() {
const { user, isAuthenticated } = useAuth(); const { user, isAuthenticated } = useAuth();
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
const { toast } = useToast(); const { toast } = useToast();
const [, setLocation] = useLocation();
const handleLogout = async () => { const handleLogout = async () => {
try { try {
@ -24,8 +26,8 @@ export default function Home() {
if (response.ok) { if (response.ok) {
toast({ toast({
title: "로그아웃 완료", title: "Logged Out",
description: "성공적으로 로그아웃되었습니다.", description: "You have been successfully logged out.",
}); });
// Invalidate auth queries to refresh user state // Invalidate auth queries to refresh user state
@ -33,15 +35,15 @@ export default function Home() {
} }
} catch (error) { } catch (error) {
toast({ toast({
title: "로그아웃 오류", title: "Logout Error",
description: "로그아웃 중 오류가 발생했습니다.", description: "An error occurred while logging out.",
variant: "destructive", variant: "destructive",
}); });
} }
}; };
const handleAdminPage = () => { const handleAdminPage = () => {
window.location.href = "/admin"; setLocation("/admin");
}; };
return ( return (
@ -56,7 +58,7 @@ export default function Home() {
alt="SAPIENS" alt="SAPIENS"
className="h-6 w-auto cursor-pointer" className="h-6 w-auto cursor-pointer"
data-testid="logo-sapiens" data-testid="logo-sapiens"
onClick={() => window.location.href = "/"} onClick={() => setLocation("/")}
/> />
</div> </div>
@ -79,6 +81,15 @@ export default function Home() {
{isAuthenticated && user ? ( {isAuthenticated && user ? (
<> <>
<Button
variant="ghost"
size="sm"
onClick={() => setLocation("/auctions")}
data-testid="button-auctions"
>
Auctions
</Button>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
@ -105,14 +116,24 @@ export default function Home() {
</Button> </Button>
</> </>
) : ( ) : (
<Button <>
variant="ghost" <Button
size="sm" variant="ghost"
onClick={() => setIsLoginModalOpen(true)} size="sm"
data-testid="button-login" onClick={() => setLocation("/auctions")}
> data-testid="button-auctions"
Login >
</Button> Auctions
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setIsLoginModalOpen(true)}
data-testid="button-login"
>
Login
</Button>
</>
)} )}
<Button <Button

View File

@ -64,7 +64,7 @@ export default function MediaOutlet() {
}; };
const handleAdminPage = () => { const handleAdminPage = () => {
window.location.href = "/admin"; setLocation("/admin");
}; };
const formatCurrency = (amount: string | null) => { const formatCurrency = (amount: string | null) => {
@ -150,14 +150,24 @@ export default function MediaOutlet() {
</Button> </Button>
</> </>
) : ( ) : (
<Button <>
variant="ghost" <Button
size="sm" variant="ghost"
onClick={() => setIsLoginModalOpen(true)} size="sm"
data-testid="button-login" onClick={() => setLocation("/auctions")}
> data-testid="button-auctions"
Login >
</Button> Auctions
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setIsLoginModalOpen(true)}
data-testid="button-login"
>
Login
</Button>
</>
)} )}
<Button <Button
@ -194,7 +204,7 @@ export default function MediaOutlet() {
<div className="text-center"> <div className="text-center">
<h1 className="text-2xl font-bold mb-2">Media Outlet Not Found</h1> <h1 className="text-2xl font-bold mb-2">Media Outlet Not Found</h1>
<p className="text-gray-600 mb-4">The media outlet you're looking for doesn't exist.</p> <p className="text-gray-600 mb-4">The media outlet you're looking for doesn't exist.</p>
<Button onClick={() => window.location.href = "/"}>Go to Homepage</Button> <Button onClick={() => setLocation("/")}>Go to Homepage</Button>
</div> </div>
</div> </div>
); );
@ -212,7 +222,7 @@ export default function MediaOutlet() {
alt="SAPIENS" alt="SAPIENS"
className="h-6 w-auto cursor-pointer" className="h-6 w-auto cursor-pointer"
data-testid="logo-sapiens" data-testid="logo-sapiens"
onClick={() => window.location.href = "/"} onClick={() => setLocation("/")}
/> />
</div> </div>
@ -235,6 +245,15 @@ export default function MediaOutlet() {
{isAuthenticated && user ? ( {isAuthenticated && user ? (
<> <>
<Button
variant="ghost"
size="sm"
onClick={() => setLocation("/auctions")}
data-testid="button-auctions"
>
Auctions
</Button>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"

View File

@ -158,10 +158,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
return res.status(404).json({ message: "Media outlet not found" }); return res.status(404).json({ message: "Media outlet not found" });
} }
const auction = await storage.getAuctionByMediaOutlet(outlet.id); const auction = await storage.getAuctionByMediaOutlet(outlet.id);
if (!auction) { res.json(auction || null);
return res.status(404).json({ message: "No active auction found for this media outlet" });
}
res.json(auction);
} catch (error) { } catch (error) {
console.error("Error fetching auction:", error); console.error("Error fetching auction:", error);
res.status(500).json({ message: "Failed to fetch auction" }); res.status(500).json({ message: "Failed to fetch auction" });