diff --git a/src/app/(frontend)/intelligence/campaigns/page.tsx b/src/app/(frontend)/intelligence/campaigns/page.tsx
index 73800d6..21133e1 100644
--- a/src/app/(frontend)/intelligence/campaigns/page.tsx
+++ b/src/app/(frontend)/intelligence/campaigns/page.tsx
@@ -2,7 +2,7 @@ import config from "@payload-config";
import { getPayload } from "payload";
import { headers as nextHeaders } from "next/headers";
import { ShieldIcon } from "lucide-react";
-import { CampaignCard } from "@/components/frontend/intelligence/CampaignCard";
+import { CampaignsList } from "@/components/frontend/intelligence/CampaignsList";
import { CreateCampaignDialog } from "@/components/frontend/intelligence/CreateCampaignDialog";
import type { Media } from "@/payload-types";
import { Button } from "@/components/ui/button";
@@ -100,13 +100,6 @@ export default async function CampaignsPage() {
}),
);
- const officialCampaigns = campaignsWithStats.filter(
- (campaign) => campaign.campaignMode === "official",
- );
- const communityCampaigns = campaignsWithStats.filter(
- (campaign) => campaign.campaignMode !== "official",
- );
-
return (
@@ -125,51 +118,7 @@ export default async function CampaignsPage() {
- {campaignsWithStats.length > 0 ? (
- <>
- {officialCampaigns.length > 0 && (
-
-
-
- Official Campaigns
-
-
- Command-sanctioned operations at the heart of the unit's story.
-
-
-
- {officialCampaigns.map((campaign) => (
-
- ))}
-
-
- )}
- {officialCampaigns.length > 0 && communityCampaigns.length > 0 && (
-
- )}
- {communityCampaigns.length > 0 && (
-
-
-
- Community Campaigns
-
-
- Player-made side operations and stories, open to everyone.
-
-
-
- {communityCampaigns.map((campaign) => (
-
- ))}
-
-
- )}
- >
- ) : (
-
- )}
+
);
}
diff --git a/src/components/frontend/intelligence/CampaignsList.tsx b/src/components/frontend/intelligence/CampaignsList.tsx
new file mode 100644
index 0000000..bd97a68
--- /dev/null
+++ b/src/components/frontend/intelligence/CampaignsList.tsx
@@ -0,0 +1,113 @@
+"use client";
+
+import { useState } from "react";
+import { CampaignCard } from "@/components/frontend/intelligence/CampaignCard";
+import { Switch } from "@/components/ui/switch";
+
+export interface CampaignListEntry {
+ readonly id: number;
+ readonly name: string;
+ readonly summary: string;
+ readonly status: "concept" | "inProgress" | "completed";
+ readonly campaignMode: "official" | "custom";
+ readonly startDate?: string | null;
+ readonly endDate?: string | null;
+ readonly coverImageUrl: string | null;
+ readonly missionCount: number;
+ readonly completedMissions: number;
+ readonly avgPlayers: number;
+ readonly avgDurationMinutes: number;
+ readonly ownerName: string;
+}
+
+interface CampaignsListProps {
+ readonly campaigns: CampaignListEntry[];
+}
+
+export function CampaignsList({ campaigns }: CampaignsListProps) {
+ const [hideCompleted, setHideCompleted] = useState(false);
+
+ const visible = hideCompleted
+ ? campaigns.filter((campaign) => campaign.status !== "completed")
+ : campaigns;
+ const officialCampaigns = visible.filter(
+ (campaign) => campaign.campaignMode === "official",
+ );
+ const communityCampaigns = visible.filter(
+ (campaign) => campaign.campaignMode !== "official",
+ );
+
+ return (
+ <>
+
+
+
+
+ {visible.length > 0 ? (
+ <>
+ {officialCampaigns.length > 0 && (
+
+
+
+ Official Campaigns
+
+
+ Command-sanctioned operations at the heart of the unit's story.
+
+
+
+ {officialCampaigns.map((campaign) => (
+
+ ))}
+
+
+ )}
+ {officialCampaigns.length > 0 && communityCampaigns.length > 0 && (
+
+ )}
+ {communityCampaigns.length > 0 && (
+
+
+
+ Community Campaigns
+
+
+ Player-made side operations and stories, open to everyone.
+
+
+
+ {communityCampaigns.map((campaign) => (
+
+ ))}
+
+
+ )}
+ >
+ ) : campaigns.length > 0 ? (
+
+
No campaigns match the current filter.
+
+
+ ) : (
+
+ )}
+ >
+ );
+}