diff --git a/.replit b/.replit
index a45f89c..ba89ca5 100644
--- a/.replit
+++ b/.replit
@@ -22,6 +22,10 @@ externalPort = 3001
localPort = 43349
externalPort = 3000
+[[ports]]
+localPort = 44197
+externalPort = 3002
+
[env]
PORT = "5000"
diff --git a/attached_assets/Lautaro Martínez_1759170040559.jpeg b/attached_assets/Lautaro Martínez_1759170040559.jpeg
new file mode 100644
index 0000000..ce09dd4
Binary files /dev/null and b/attached_assets/Lautaro Martínez_1759170040559.jpeg differ
diff --git a/attached_assets/Nicolò Barella_1759170031318.jpeg b/attached_assets/Nicolò Barella_1759170031318.jpeg
new file mode 100644
index 0000000..41cb11f
Binary files /dev/null and b/attached_assets/Nicolò Barella_1759170031318.jpeg differ
diff --git a/attached_assets/Rodri Hernández (Rodrigo Hernández)_1759170035155.jpeg b/attached_assets/Rodri Hernández (Rodrigo Hernández)_1759170035155.jpeg
new file mode 100644
index 0000000..03bab8f
Binary files /dev/null and b/attached_assets/Rodri Hernández (Rodrigo Hernández)_1759170035155.jpeg differ
diff --git a/attached_assets/altcoin_1759170052209.jpeg b/attached_assets/altcoin_1759170052209.jpeg
new file mode 100644
index 0000000..8fad727
Binary files /dev/null and b/attached_assets/altcoin_1759170052209.jpeg differ
diff --git a/client/src/components/MediaOutletManagement.tsx b/client/src/components/MediaOutletManagement.tsx
new file mode 100644
index 0000000..4ce0cd2
--- /dev/null
+++ b/client/src/components/MediaOutletManagement.tsx
@@ -0,0 +1,293 @@
+import { useState } from "react";
+import { useQuery } from "@tanstack/react-query";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { ArrowLeft, Edit, Plus, BarChart3, Gavel, MessageSquare } from "lucide-react";
+import type { MediaOutlet } from "@shared/schema";
+
+interface MediaOutletManagementProps {
+ outlet: MediaOutlet;
+ onBack: () => void;
+}
+
+export default function MediaOutletManagement({ outlet, onBack }: MediaOutletManagementProps) {
+ const [activeTab, setActiveTab] = useState("overview");
+
+ // TODO: Add queries for articles, predictions, auctions, comments related to this outlet
+ const { data: articles = [] } = useQuery({
+ queryKey: ["/api/articles", outlet.id],
+ enabled: false, // Disabled for now as articles API is not implemented
+ });
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
+
+
+ {outlet.imageUrl ? (
+

+ ) : (
+
+
+ {outlet.name.charAt(0)}
+
+
+ )}
+
+
{outlet.name}
+
{outlet.description || "Media Outlet"}
+
+
+
+
+
+
+ {outlet.category}
+
+
+
+
+
+
+
+ {/* Main Content */}
+
+
+
+ 개요
+ 기사 관리
+ 예측시장
+ 경매
+ 댓글
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
기사 관리
+
+
+
+
+
+
아직 기사가 없습니다
+
첫 번째 기사를 작성해보세요
+
+
+
+
+
+
+
+
+
+
예측시장 관리
+
+
+
+
+
+
활성 예측시장이 없습니다
+
새로운 예측시장을 생성해보세요
+
+
+
+
+
+
+
+
+
+
경매 관리
+
+
+
+
+
+
진행중인 경매가 없습니다
+
새로운 경매를 시작해보세요
+
+
+
+
+
+
+
+
+ 댓글 관리
+
+
+
+
관리할 댓글이 없습니다
+
댓글이 등록되면 여기서 관리할 수 있습니다
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/pages/AdminDashboard.tsx b/client/src/pages/AdminDashboard.tsx
index 3014098..e69cd78 100644
--- a/client/src/pages/AdminDashboard.tsx
+++ b/client/src/pages/AdminDashboard.tsx
@@ -1,14 +1,21 @@
import { useQuery } from "@tanstack/react-query";
import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
import { Card, CardContent } from "@/components/ui/card";
import { useAuth } from "@/hooks/useAuth";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
import { useToast } from "@/hooks/use-toast";
import { isUnauthorizedError } from "@/lib/authUtils";
+import { Search, Settings } from "lucide-react";
+import type { MediaOutlet } from "@shared/schema";
+import MediaOutletManagement from "@/components/MediaOutletManagement";
export default function AdminDashboard() {
const { user, isLoading } = useAuth();
const { toast } = useToast();
+ const [searchTerm, setSearchTerm] = useState("");
+ const [selectedOutlet, setSelectedOutlet] = useState(null);
+ const [managingOutlet, setManagingOutlet] = useState(null);
// Redirect if not authenticated or not admin/superadmin
useEffect(() => {
@@ -24,24 +31,24 @@ export default function AdminDashboard() {
}
}, [isLoading, user, toast]);
- const { data: analytics, isLoading: analyticsLoading, error: analyticsError } = useQuery({
- queryKey: ["/api/analytics"],
- retry: false,
+ const { data: mediaOutlets = [], isLoading: outletsLoading } = useQuery({
+ queryKey: ["/api/media-outlets"],
+ queryFn: async () => {
+ const res = await fetch("/api/media-outlets", {
+ credentials: "include",
+ });
+ if (!res.ok) {
+ throw new Error(`${res.status}: ${res.statusText}`);
+ }
+ return res.json();
+ },
});
- // Handle analytics errors
- useEffect(() => {
- if (analyticsError && isUnauthorizedError(analyticsError as Error)) {
- toast({
- title: "Unauthorized",
- description: "You are logged out. Logging in again...",
- variant: "destructive",
- });
- setTimeout(() => {
- window.location.href = "/api/login";
- }, 500);
- }
- }, [analyticsError, toast]);
+ // Filter outlets based on search term
+ const filteredOutlets = mediaOutlets.filter(outlet =>
+ outlet.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ (outlet.description && outlet.description.toLowerCase().includes(searchTerm.toLowerCase()))
+ );
const handleLogout = () => {
window.location.href = "/api/logout";
@@ -55,25 +62,67 @@ export default function AdminDashboard() {
);
}
+ // If managing an outlet, show MediaOutletManagement
+ if (managingOutlet) {
+ return (
+ setManagingOutlet(null)}
+ />
+ );
+ }
+
return (
-
+
{/* Header */}
-
+
-
-
- S
-
-
SAPIENS
-
• Admin Dashboard
+
+
@@ -81,137 +130,135 @@ export default function AdminDashboard() {
-
-
-
-
Admin Dashboard
-
Manage media outlets and content
+
+
+
관리자 대시보드
+
관리할 언론매체를 검색하고 선택하세요
+
+
+ {outletsLoading ? (
+
+ {Array.from({ length: 12 }).map((_, i) => (
+
+
+
+
+
+ ))}
-
-
- {/* Analytics Cards */}
-
- {analyticsLoading ? (
- Array.from({ length: 4 }).map((_, i) => (
-
-
-
-
-
- ))
- ) : (
- <>
-
-
-
-
-
Total Articles
-
{(analytics as any)?.totalArticles || 0}
-
-
-
-
-
-
-
-
-
-
-
Active Predictions
-
{(analytics as any)?.activePredictions || 0}
-
-
-
-
-
-
-
-
-
-
-
Live Auctions
-
{(analytics as any)?.liveAuctions || 0}
-
-
-
-
-
-
-
-
-
-
-
Revenue
-
${((analytics as any)?.totalRevenue || 0).toLocaleString()}
-
-
-
-
-
- >
- )}
-
-
- {/* Admin Actions */}
-
-
-
- Content Management
-
-