Add media outlet header to comprehensive report view
Integrates the media outlet's header information, including its logo and name, into the Comprehensive Report page. The header is now sticky and displays dynamically based on the current report's media outlet. Includes new navigation and utility icons in the header, along with search modal functionality and user authentication hooks. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 0fb68265-c270-4198-9584-3d9be9bddb41 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/0fb68265-c270-4198-9584-3d9be9bddb41/16cZmxV
This commit is contained in:
@ -53,13 +53,6 @@ export default function MainContent() {
|
|||||||
acc[article.mediaOutletId] = (acc[article.mediaOutletId] || 0) + 1;
|
acc[article.mediaOutletId] = (acc[article.mediaOutletId] || 0) + 1;
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>);
|
}, {} as Record<string, number>);
|
||||||
|
|
||||||
console.log('📊 Article Count Debug:', {
|
|
||||||
totalArticles: allArticles.length,
|
|
||||||
totalOutlets: allOutlets.length,
|
|
||||||
articleCountByOutlet,
|
|
||||||
outletsWithArticles: Object.keys(articleCountByOutlet).length
|
|
||||||
});
|
|
||||||
|
|
||||||
// Group outlets by category and sort
|
// Group outlets by category and sort
|
||||||
const getOutletsByCategory = (category: string) => {
|
const getOutletsByCategory = (category: string) => {
|
||||||
|
|||||||
@ -1,8 +1,16 @@
|
|||||||
import { useRoute } from "wouter";
|
import { useRoute, useLocation } from "wouter";
|
||||||
import { FileText, Presentation } from "lucide-react";
|
import { FileText, Presentation, Info, Search, Settings, User, LogOut } from "lucide-react";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
|
import { useState } from "react";
|
||||||
|
import type { MediaOutlet } from "@shared/schema";
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
import SearchModal from "@/components/SearchModal";
|
||||||
|
|
||||||
const reportContent: Record<string, { htmlPath: string; pptPath: string }> = {
|
const reportContent: Record<string, { htmlPath: string; pptPath: string }> = {
|
||||||
'chayan-asli': {
|
'chayan-asli': {
|
||||||
@ -17,9 +25,38 @@ const reportContent: Record<string, { htmlPath: string; pptPath: string }> = {
|
|||||||
|
|
||||||
export default function Report() {
|
export default function Report() {
|
||||||
const [, params] = useRoute("/media/:slug/report");
|
const [, params] = useRoute("/media/:slug/report");
|
||||||
|
const [, setLocation] = useLocation();
|
||||||
|
const [enlargedImage, setEnlargedImage] = useState<string | null>(null);
|
||||||
|
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
|
||||||
|
const { user, isAuthenticated } = useAuth();
|
||||||
|
|
||||||
const slug = params?.slug || '';
|
const slug = params?.slug || '';
|
||||||
const content = reportContent[slug] || reportContent['chayan-asli'];
|
const content = reportContent[slug] || reportContent['chayan-asli'];
|
||||||
|
|
||||||
|
const { data: outlet, isLoading: outletLoading } = useQuery<MediaOutlet>({
|
||||||
|
queryKey: ["/api/media-outlets", slug],
|
||||||
|
enabled: !!slug
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLogout = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/logout", {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.href = "/";
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Logout failed:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAdminPage = () => {
|
||||||
|
if (user?.role === "admin" || user?.role === "superadmin") {
|
||||||
|
setLocation("/admin");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Get full URL for PPT file
|
// Get full URL for PPT file
|
||||||
const getFullPptUrl = () => {
|
const getFullPptUrl = () => {
|
||||||
@ -63,10 +100,151 @@ export default function Report() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="bg-white border-b border-gray-200 sticky top-0 z-10">
|
<header className="bg-white border-b border-gray-200 sticky top-0 z-50">
|
||||||
<div className="max-w-7xl mx-auto px-4 py-4">
|
<div className="max-w-7xl mx-auto px-6 py-4">
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-bold text-gray-900">Comprehensive Report</h1>
|
<div className="flex items-center space-x-3">
|
||||||
|
{outletLoading ? (
|
||||||
|
<>
|
||||||
|
<div className="w-10 h-10 bg-gray-200 rounded-full animate-pulse"></div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="h-3 w-20 bg-gray-200 rounded animate-pulse mb-1"></div>
|
||||||
|
<div className="h-3 w-16 bg-gray-200 rounded animate-pulse"></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : outlet ? (
|
||||||
|
<>
|
||||||
|
{outlet.imageUrl ? (
|
||||||
|
<img
|
||||||
|
src={outlet.imageUrl}
|
||||||
|
alt={outlet.name}
|
||||||
|
className="w-10 h-10 rounded-full object-cover cursor-pointer hover:ring-2 hover:ring-blue-400 transition-all"
|
||||||
|
onClick={() => setEnlargedImage(outlet.imageUrl!)}
|
||||||
|
data-testid="image-outlet-header-profile"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col cursor-pointer hover:opacity-80 transition-opacity" onClick={() => setLocation("/")}>
|
||||||
|
<img
|
||||||
|
src="/attached_assets/logo_black_1759162717640.png"
|
||||||
|
alt="SAPIENS"
|
||||||
|
className="h-3 w-auto mb-0.5"
|
||||||
|
data-testid="logo-sapiens"
|
||||||
|
/>
|
||||||
|
<div className="flex items-center space-x-1.5">
|
||||||
|
<span className="text-sm font-semibold text-gray-900" data-testid="text-outlet-name-header">
|
||||||
|
{outlet.name}
|
||||||
|
</span>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
className="inline-flex items-center"
|
||||||
|
aria-label="View outlet information"
|
||||||
|
>
|
||||||
|
<Info
|
||||||
|
className="h-3.5 w-3.5 text-gray-500 cursor-pointer hover:text-gray-700"
|
||||||
|
data-testid="icon-info-header"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p className="max-w-xs">{outlet.description || "Media Outlet"}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src="/attached_assets/logo_black_1759162717640.png"
|
||||||
|
alt="SAPIENS"
|
||||||
|
className="h-6 w-auto cursor-pointer"
|
||||||
|
onClick={() => setLocation("/")}
|
||||||
|
data-testid="logo-sapiens"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div
|
||||||
|
className="relative cursor-pointer"
|
||||||
|
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" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search the website"
|
||||||
|
className="w-80 pl-10 bg-gray-50 border-gray-200 cursor-pointer"
|
||||||
|
data-testid="input-search"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isAuthenticated && user ? (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setLocation("/auctions")}
|
||||||
|
data-testid="button-auctions"
|
||||||
|
>
|
||||||
|
Auctions
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<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={() => setLocation("/login")}
|
||||||
|
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>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -121,6 +299,12 @@ export default function Report() {
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
||||||
|
{/* Search Modal */}
|
||||||
|
<SearchModal
|
||||||
|
isOpen={isSearchModalOpen}
|
||||||
|
onClose={() => setIsSearchModalOpen(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user