Update the website header to match media outlets and expand search bar

Enhance UI for article pages by synchronizing headers with media outlets and increasing the search bar's length. Add search and user authentication modal components.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9a264234-c5d7-4dcc-adf3-a954b149b30d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/9a264234-c5d7-4dcc-adf3-a954b149b30d/IfFFLfD
This commit is contained in:
kimjaehyeon0101
2025-10-15 07:38:50 +00:00
parent 0e936187a3
commit bdb8369ae9
2 changed files with 115 additions and 22 deletions

View File

@ -5,19 +5,23 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { TrendingUp, TrendingDown, DollarSign, Clock, IdCard } from "lucide-react";
import { TrendingUp, TrendingDown, DollarSign, Clock, IdCard, Search, User, LogOut, Settings } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import { apiRequest, queryClient } from "@/lib/queryClient";
import Footer from "@/components/Footer";
import { useAuth } from "@/hooks/useAuth";
import LoginModal from "@/components/LoginModal";
import SearchModal from "@/components/SearchModal";
import type { Article, PredictionMarket, MediaOutlet } from "@shared/schema";
export default function Article() {
const [, params] = useRoute("/articles/:slug");
const [, setLocation] = useLocation();
const { toast } = useToast();
const { user } = useAuth();
const { user, isAuthenticated } = useAuth();
const [betAmounts, setBetAmounts] = useState<Record<string, string>>({});
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
const { data: articleData, isLoading: articleLoading } = useQuery<Article & { outlet?: MediaOutlet }>({
queryKey: ["/api/articles", params?.slug],
@ -32,6 +36,34 @@ export default function Article() {
enabled: !!params?.slug
});
const handleLogout = async () => {
try {
const response = await fetch("/api/logout", {
method: "POST",
credentials: "include",
});
if (response.ok) {
toast({
title: "Logout Successful",
description: "You have been successfully logged out.",
});
queryClient.invalidateQueries({ queryKey: ["/api/auth/user"] });
}
} catch (error) {
toast({
title: "Logout Error",
description: "An error occurred during logout.",
variant: "destructive",
});
}
};
const handleAdminPage = () => {
setLocation("/admin");
};
const placeBetMutation = useMutation({
mutationFn: async ({ marketId, side, amount }: { marketId: string; side: "yes" | "no"; amount: number }) => {
return apiRequest("POST", `/api/prediction-markets/${marketId}/bets`, { side, amount });
@ -160,29 +192,25 @@ export default function Article() {
<header className="bg-white border-b border-gray-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-3">
{outlet?.imageUrl ? (
<img
src={outlet.imageUrl}
alt={outlet.name}
className="w-10 h-10 rounded-full object-cover cursor-pointer"
alt={outlet.name}
className="w-10 h-10 rounded-full object-cover cursor-pointer hover:ring-2 hover:ring-blue-400 transition-all"
onClick={() => setLocation(`/media/${outlet.slug}`)}
data-testid="img-outlet-profile"
data-testid="image-outlet-header-profile"
/>
) : (
<div
className="w-10 h-10 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center cursor-pointer"
onClick={() => outlet && setLocation(`/media/${outlet.slug}`)}
data-testid="img-outlet-profile-fallback"
>
<span className="text-white font-bold text-lg">
<div className="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center">
<span className="text-gray-600 font-bold text-sm">
{outlet?.name.charAt(0)}
</span>
</div>
)}
{outlet && (
<div className="flex items-center space-x-2">
<span className="text-base font-bold text-gray-900" data-testid="text-outlet-name-header">
<span className="text-3xl font-bold text-gray-900" data-testid="text-outlet-name-header">
{outlet.name}
</span>
<Button
@ -202,14 +230,69 @@ export default function Article() {
</div>
)}
</div>
<div className="flex items-center">
<img
src="/attached_assets/logo_black_1759162717640.png"
alt="SAPIENS"
className="h-4 w-auto cursor-pointer opacity-60 hover:opacity-100 transition-opacity"
onClick={() => setLocation("/")}
data-testid="logo-sapiens"
/>
<div className="flex items-center space-x-4">
<div
className="relative cursor-pointer flex items-center"
onClick={() => setIsSearchModalOpen(true)}
data-testid="search-container"
>
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400 pointer-events-none" />
<div className="pl-10 pr-16 py-1.5 bg-gray-50 border border-gray-200 rounded-md flex items-center space-x-2 hover:bg-gray-100 transition-colors">
<span className="text-sm text-gray-500">search in</span>
<img
src="/attached_assets/logo_black_1759162717640.png"
alt="SAPIENS"
className="h-2 w-auto"
/>
</div>
</div>
{isAuthenticated && user ? (
<>
<Button
variant="ghost"
size="sm"
onClick={handleAdminPage}
data-testid="button-admin-dashboard"
>
Admin Dashboard
</Button>
<div className="flex items-center space-x-2 px-3 py-1 bg-gray-100 rounded-md">
<User className="h-4 w-4 text-gray-600" />
<span className="text-sm font-medium text-gray-700" data-testid="user-name">
{user.firstName} {user.lastName}
</span>
</div>
<Button
variant="ghost"
size="sm"
onClick={handleLogout}
data-testid="button-logout"
>
<LogOut className="h-4 w-4" />
</Button>
</>
) : (
<Button
variant="ghost"
size="sm"
onClick={() => setIsLoginModalOpen(true)}
data-testid="button-login"
>
Login
</Button>
)}
<Button
variant="ghost"
size="sm"
data-testid="button-settings"
>
<Settings className="h-4 w-4" />
</Button>
</div>
</div>
</div>
@ -368,6 +451,16 @@ export default function Article() {
</section>
</main>
{/* Modals */}
<LoginModal
isOpen={isLoginModalOpen}
onClose={() => setIsLoginModalOpen(false)}
/>
<SearchModal
isOpen={isSearchModalOpen}
onClose={() => setIsSearchModalOpen(false)}
/>
{/* Footer */}
<Footer />
</div>