diff --git a/.replit b/.replit index ba89ca5..a45f89c 100644 --- a/.replit +++ b/.replit @@ -22,10 +22,6 @@ externalPort = 3001 localPort = 43349 externalPort = 3000 -[[ports]] -localPort = 44197 -externalPort = 3002 - [env] PORT = "5000" diff --git a/attached_assets/스크린샷 2025-09-30 오전 2.12.13_1759171072115.png b/attached_assets/스크린샷 2025-09-30 오전 2.12.13_1759171072115.png new file mode 100644 index 0000000..bd7859d Binary files /dev/null and b/attached_assets/스크린샷 2025-09-30 오전 2.12.13_1759171072115.png differ diff --git a/client/src/components/MainContent.tsx b/client/src/components/MainContent.tsx index 473d2a9..8e56618 100644 --- a/client/src/components/MainContent.tsx +++ b/client/src/components/MainContent.tsx @@ -1,8 +1,11 @@ import { useQuery } from "@tanstack/react-query"; import { Card, CardContent } from "@/components/ui/card"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import type { MediaOutlet } from "@shared/schema"; import { useAuth } from "@/hooks/useAuth"; import Footer from "@/components/Footer"; +import { useState } from "react"; +import { ArrowUpDown } from "lucide-react"; const categories = [ { id: "people", name: "People", key: "People" }, @@ -12,6 +15,7 @@ const categories = [ export default function MainContent() { const { user } = useAuth(); + const [sortBy, setSortBy] = useState<"alphabetical" | "traffic">("alphabetical"); const { data: allOutlets = [], isLoading } = useQuery({ queryKey: ["/api/media-outlets"], @@ -26,11 +30,20 @@ export default function MainContent() { }, }); - // Group outlets by category + // Group outlets by category and sort const getOutletsByCategory = (category: string) => { - return allOutlets.filter(outlet => + const filtered = allOutlets.filter(outlet => outlet.category.toLowerCase() === category.toLowerCase() ); + + return filtered.sort((a, b) => { + if (sortBy === "alphabetical") { + return a.name.localeCompare(b.name); + } else { + // Sort by traffic score (descending - highest traffic first) + return (b.trafficScore || 0) - (a.trafficScore || 0); + } + }); }; const renderOutletCard = (outlet: MediaOutlet) => ( @@ -72,6 +85,23 @@ export default function MainContent() { return (
+ {/* Sorting Controls */} +
+

언론매체

+
+ + +
+
+ {isLoading ? (
{categories.map((category) => ( diff --git a/client/src/pages/AdminDashboard.tsx b/client/src/pages/AdminDashboard.tsx index e69cd78..b8c4025 100644 --- a/client/src/pages/AdminDashboard.tsx +++ b/client/src/pages/AdminDashboard.tsx @@ -6,7 +6,8 @@ import { useAuth } from "@/hooks/useAuth"; import { useEffect, useState } from "react"; import { useToast } from "@/hooks/use-toast"; import { isUnauthorizedError } from "@/lib/authUtils"; -import { Search, Settings } from "lucide-react"; +import { Search, Settings, ArrowUpDown } from "lucide-react"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import type { MediaOutlet } from "@shared/schema"; import MediaOutletManagement from "@/components/MediaOutletManagement"; @@ -16,6 +17,7 @@ export default function AdminDashboard() { const [searchTerm, setSearchTerm] = useState(""); const [selectedOutlet, setSelectedOutlet] = useState(null); const [managingOutlet, setManagingOutlet] = useState(null); + const [sortBy, setSortBy] = useState<"alphabetical" | "traffic">("alphabetical"); // Redirect if not authenticated or not admin/superadmin useEffect(() => { @@ -44,11 +46,20 @@ export default function AdminDashboard() { }, }); - // 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())) - ); + // Filter and sort outlets based on search term and sort option + const filteredOutlets = mediaOutlets + .filter(outlet => + outlet.name.toLowerCase().includes(searchTerm.toLowerCase()) || + (outlet.description && outlet.description.toLowerCase().includes(searchTerm.toLowerCase())) + ) + .sort((a, b) => { + if (sortBy === "alphabetical") { + return a.name.localeCompare(b.name); + } else { + // Sort by traffic score (descending - highest traffic first) + return (b.trafficScore || 0) - (a.trafficScore || 0); + } + }); const handleLogout = () => { window.location.href = "/api/logout"; @@ -131,9 +142,23 @@ export default function AdminDashboard() {
-
-

관리자 대시보드

-

관리할 언론매체를 검색하고 선택하세요

+
+
+

관리자 대시보드

+

관리할 언론매체를 검색하고 선택하세요

+
+
+ + +
{outletsLoading ? ( diff --git a/shared/schema.ts b/shared/schema.ts index b07d00d..4128fc3 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -47,6 +47,7 @@ export const mediaOutlets = pgTable("media_outlets", { description: text("description"), imageUrl: varchar("image_url"), tags: text("tags").array(), + trafficScore: integer("traffic_score").default(0), // For sorting by traffic isActive: boolean("is_active").default(true), createdAt: timestamp("created_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(),