commit 68f6fd41bdefabffcc8e851a9177ba43f0675682
parent af2d92012b646c6f1bb1ab1d3f4fd04b7451af5d
Author: MTRNord <mtrnord1@gmail.com>
Date: Thu, 24 Apr 2025 22:37:01 +0200
Split out things, abstract things and add filters for bans
Signed-off-by: MTRNord <mtrnord1@gmail.com>
Diffstat:
8 files changed, 277 insertions(+), 345 deletions(-)
diff --git a/src/app/dashboard/analytics-dashboard.tsx b/src/app/dashboard/analytics-dashboard.tsx
@@ -1,19 +1,44 @@
-"use client"
-
import { useState } from "react"
-import { Filter, TrendingUp, Users } from "lucide-react"
-import { useRouter } from "next/navigation"
+import { TrendingUp } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
-import { Button } from "@/components/ui/button"
import { PieChart } from "@/components/analytics/pie-chart"
import { Heatmap } from "@/components/analytics/heatmap"
import { BarChart } from "@/components/analytics/bar-chart"
import { HorizontalBarChart } from "@/components/analytics/horizontal-bar-chart"
+interface InfoCardWithTrendProps {
+ title: string;
+ changePercent: number;
+ value: string | number;
+ trendColorOverride?: string;
+}
+
+function InfoCardWithTrend({ title, changePercent, value, trendColorOverride }: InfoCardWithTrendProps) {
+ const trendColor = changePercent > 0 ? "text-green-400" : "text-red-400"
+ const trendIcon = changePercent > 0 ? <TrendingUp className="h-3.5 w-3.5 mr-1" /> : <TrendingUp className="h-3.5 w-3.5 mr-1 rotate-180" />
+ const trendText = changePercent > 0 ? `+${changePercent}% from previous period` : `${changePercent}% from previous period`
+ const trendClass = trendColorOverride || trendColor
+
+ return (
+ <Card className="border-gray-800 bg-gray-950">
+ <CardHeader className="pb-2">
+ <CardTitle className="text-sm font-medium">{title}</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <div className="text-2xl font-bold">{value}</div>
+ <div className={`flex items-center text-xs ${trendClass}`}>
+ {trendIcon}
+ <span>{trendText}</span>
+ </div>
+ </CardContent>
+ </Card>
+ )
+}
+
// Mock data for heatmaps
const generateHeatmapData = (intensity = 1, seed = 42) => {
const today = new Date()
@@ -102,12 +127,9 @@ interface AnalyticsDashboardProps {
export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
const [timeRange, setTimeRange] = useState("year")
const [heatmapType, setHeatmapType] = useState("reports")
- const router = useRouter()
- const handleViewAllBannedServers = () => {
- // Navigate to the bans tab
- router.push("/dashboard?tab=bans")
- }
+
+ const reportChangePercent = 12
return (
<div className="space-y-4">
@@ -125,62 +147,15 @@ export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
<SelectItem value="all">All Time</SelectItem>
</SelectContent>
</Select>
- <Button variant="outline" size="sm" className="h-8 border-gray-800 text-gray-400 hover:text-white">
- <Filter className="h-3.5 w-3.5 mr-1" />
- Filter
- </Button>
+
</div>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
- <Card className="border-gray-800 bg-gray-950">
- <CardHeader className="pb-2">
- <CardTitle className="text-sm font-medium">Total Reports</CardTitle>
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">247</div>
- <div className="flex items-center text-xs text-green-400">
- <TrendingUp className="h-3.5 w-3.5 mr-1" />
- <span>+12% from previous period</span>
- </div>
- </CardContent>
- </Card>
- <Card className="border-gray-800 bg-gray-950">
- <CardHeader className="pb-2">
- <CardTitle className="text-sm font-medium">Bans Issued</CardTitle>
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">98</div>
- <div className="flex items-center text-xs text-red-400">
- <TrendingUp className="h-3.5 w-3.5 mr-1" />
- <span>+5% from previous period</span>
- </div>
- </CardContent>
- </Card>
- <Card className="border-gray-800 bg-gray-950">
- <CardHeader className="pb-2">
- <CardTitle className="text-sm font-medium">Unique Reporters</CardTitle>
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">42</div>
- <div className="flex items-center text-xs text-green-400">
- <TrendingUp className="h-3.5 w-3.5 mr-1" />
- <span>+8% from previous period</span>
- </div>
- </CardContent>
- </Card>
- <Card className="border-gray-800 bg-gray-950">
- <CardHeader className="pb-2">
- <CardTitle className="text-sm font-medium">Avg. Response Time</CardTitle>
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">1.4h</div>
- <div className="flex items-center text-xs text-green-400">
- <TrendingUp className="h-3.5 w-3.5 mr-1 rotate-180" />
- <span>-15% from previous period</span>
- </div>
- </CardContent>
- </Card>
+ <InfoCardWithTrend title="Total Reports" changePercent={12} value={247} trendColorOverride={reportChangePercent > 0 ? "text-red-400" : "text-green-400"} />
+ <InfoCardWithTrend title="Bans Issued" changePercent={5} value={98} />
+ <InfoCardWithTrend title="Unique Reporters" changePercent={8} value={42} />
+ <InfoCardWithTrend title="Avg. Response Time" changePercent={-15} value="1.4h" />
</div>
<Card className="border-gray-800 bg-gray-950">
@@ -192,7 +167,7 @@ export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
Visualization of moderation activity over time
</CardDescription>
</div>
- <Tabs value={heatmapType} onValueChange={setHeatmapType} className="w-[240px]">
+ <Tabs value={heatmapType} onValueChange={setHeatmapType}>
<TabsList className="bg-gray-900">
<TabsTrigger value="reports" className="text-xs">
Reports
@@ -286,17 +261,6 @@ export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
</CardHeader>
<CardContent>
<HorizontalBarChart data={bannedServersData} color="#dc2626" />
- <div className="mt-4 flex items-center justify-center">
- <Button
- variant="outline"
- size="sm"
- className="border-gray-800 text-gray-400 hover:text-white"
- onClick={handleViewAllBannedServers}
- >
- <Users className="mr-2 h-4 w-4" />
- View All Banned Servers
- </Button>
- </div>
</CardContent>
</Card>
</div>
@@ -312,7 +276,7 @@ export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
<div className="grid gap-4 md:grid-cols-3">
<div className="space-y-2">
<h3 className="text-sm font-medium text-gray-400">Average Response Time</h3>
- <div className="flex items-end gap-2">
+ <div className="flex items-end gap-3">
<span className="text-2xl font-bold">1.4h</span>
<span className="text-xs text-green-400">-15% from last month</span>
</div>
@@ -321,7 +285,7 @@ export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
<div className="space-y-2">
<h3 className="text-sm font-medium text-gray-400">Resolution Rate</h3>
- <div className="flex items-end gap-2">
+ <div className="flex items-end gap-3">
<span className="text-2xl font-bold">92%</span>
<span className="text-xs text-green-400">+3% from last month</span>
</div>
@@ -330,7 +294,7 @@ export function AnalyticsDashboard({ selectedTeam }: AnalyticsDashboardProps) {
<div className="space-y-2">
<h3 className="text-sm font-medium text-gray-400">Moderator Activity</h3>
- <div className="flex items-end gap-2">
+ <div className="flex items-end gap-3">
<span className="text-2xl font-bold">4.2</span>
<span className="text-xs text-gray-400">actions per day</span>
</div>
diff --git a/src/app/dashboard/components/bans-pane.tsx b/src/app/dashboard/components/bans-pane.tsx
@@ -1,24 +1,13 @@
"use client";
-import { Checkbox } from "@/components/ui/checkbox"
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area"
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
-import { Ban, Badge } from "lucide-react";
+import { Ban, PenOff, Pen } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
-import { Textarea } from "@/components/ui/textarea";
-import { Label } from "@/components/ui/label"
import { useState } from "react";
-import { PolicyList } from "../page";
+import { EBanTypes, PolicyList } from "../page";
+import AddBan from "./modals/add-ban";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
interface BansProps {
policyLists: PolicyList[];
@@ -26,89 +15,22 @@ interface BansProps {
export default function Bans({ policyLists }: BansProps) {
const [selectedPolicyList, setSelectedPolicyList] = useState("global")
+ const [filter, setFilter] = useState("all")
const selectedList = policyLists.find((list) => list.id === selectedPolicyList) || policyLists[0] || { entries: [] }
+ const entries = selectedList.entries.filter((entry) => {
+ if (filter === "all") return true;
+ if (filter === "users") return entry.type === EBanTypes.User;
+ if (filter === "servers") return entry.type === EBanTypes.Server;
+ if (filter === "rooms") return entry.type === EBanTypes.Room;
+ return false;
+ });
return (
<Card className="border-gray-800 bg-gray-950">
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle>Policy Lists</CardTitle>
- <Dialog>
- <DialogTrigger asChild>
- <Button size="sm" className="bg-purple-600 text-white hover:bg-purple-700">
- <Ban className="mr-2 h-4 w-4" />
- Add Ban
- </Button>
- </DialogTrigger>
- <DialogContent className="bg-gray-950 border-gray-800">
- <DialogHeader>
- <DialogTitle>Add New Ban</DialogTitle>
- <DialogDescription className="text-gray-400">
- Ban a user or server from your Matrix rooms
- </DialogDescription>
- </DialogHeader>
- <div className="space-y-4 py-4">
- <div className="space-y-2">
- <Label htmlFor="ban-type">Ban Type</Label>
- <Select defaultValue="user">
- <SelectTrigger className="bg-gray-900 border-gray-800">
- <SelectValue placeholder="Select ban type" />
- </SelectTrigger>
- <SelectContent className="bg-gray-900 border-gray-800">
- <SelectItem value="user">User</SelectItem>
- <SelectItem value="server">Server</SelectItem>
- <SelectItem value="room">Room</SelectItem>
- </SelectContent>
- </Select>
- </div>
- <div className="space-y-2">
- <Label htmlFor="ban-target">Target</Label>
- <Input
- id="ban-target"
- placeholder="@user:matrix.org"
- className="bg-gray-900 border-gray-800"
- />
- </div>
- <div className="space-y-2">
- <Label htmlFor="ban-reason">Reason</Label>
- <Textarea
- id="ban-reason"
- placeholder="Reason for the ban"
- className="bg-gray-900 border-gray-800"
- />
- </div>
- <div className="space-y-2">
- <Label>Policy Lists</Label>
- <div className="space-y-2 mt-1">
- {policyLists
- .filter((list) => !list.readOnly)
- .map((list) => (
- <div key={list.id} className="flex items-center space-x-2">
- <Checkbox id={`list-${list.id}`} />
- <Label htmlFor={`list-${list.id}`} className="font-normal">
- {list.name}
- </Label>
- </div>
- ))}
- </div>
- </div>
- <div className="space-y-2">
- <p className="text-xs text-gray-400">
- Bans are applied to all protected rooms monitored by this bot.
- </p>
- </div>
- </div>
- <DialogFooter>
- <Button
- variant="outline"
- className="border-gray-700 text-gray-400 hover:bg-gray-900 hover:text-gray-300"
- >
- Cancel
- </Button>
- <Button className="bg-red-600 text-white hover:bg-red-700">Add Ban</Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
+ <AddBan policyLists={policyLists} filled />
</div>
<CardDescription className="text-gray-400">Manage ban lists across your Matrix rooms</CardDescription>
</CardHeader>
@@ -130,9 +52,9 @@ export default function Bans({ policyLists }: BansProps) {
<div className="flex items-center justify-between mb-1">
<span className="font-medium">{list.name}</span>
{list.readOnly ? (
- <Badge className="bg-gray-800 text-gray-400">Read Only</Badge>
+ <PenOff className=" text-gray-400">Read Only</PenOff>
) : (
- <Badge className="bg-green-900/50 text-green-300">Editable</Badge>
+ <Pen className="text-green-300">Editable</Pen>
)}
</div>
<p className="text-xs text-gray-400">{list.description}</p>
@@ -155,25 +77,26 @@ export default function Bans({ policyLists }: BansProps) {
</h3>
<div className="flex items-center gap-2">
<Input placeholder="Search entries..." className="h-8 w-[200px] bg-gray-900 border-gray-800" />
- {selectedList && !selectedList.readOnly && (
- <Dialog>
- <DialogTrigger asChild>
- <Button size="sm" className="bg-purple-600 text-white hover:bg-purple-700">
- Add Entry
- </Button>
- </DialogTrigger>
- <DialogContent className="bg-gray-950 border-gray-800">
- {/* Add entry dialog content */}
- </DialogContent>
- </Dialog>
- )}
+ <div className="flex items-center gap-2">
+ <Select defaultValue="all" onValueChange={setFilter}>
+ <SelectTrigger className="h-8 w-[120px] bg-gray-900 border-gray-800">
+ <SelectValue placeholder="Filter" />
+ </SelectTrigger>
+ <SelectContent className="bg-gray-900 border-gray-800">
+ <SelectItem value="all">All Bans</SelectItem>
+ <SelectItem value="users">Users</SelectItem>
+ <SelectItem value="servers">Servers</SelectItem>
+ <SelectItem value="rooms">Rooms</SelectItem>
+ </SelectContent>
+ </Select>
+ </div>
</div>
</div>
<ScrollArea className="h-[400px] rounded-md border border-gray-800">
<div className="p-4 space-y-3">
- {selectedList && selectedList.entries && selectedList.entries.length > 0 ? (
- selectedList.entries.map((entry) => (
+ {selectedList && entries && entries.length > 0 ? (
+ entries.map((entry) => (
<div key={entry.id} className="p-3 rounded-md bg-gray-900 border border-gray-800">
<div className="flex items-start justify-between">
<div>
diff --git a/src/app/dashboard/components/dashboard-header.tsx b/src/app/dashboard/components/dashboard-header.tsx
@@ -29,6 +29,8 @@ import {
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { ScrollArea } from "@/components/ui/scroll-area"
import { useSession } from "@/contexts/session-context"
+import AddBan from "./modals/add-ban"
+import { PolicyList } from "../page"
interface Notification {
id: string
@@ -47,14 +49,15 @@ interface Team {
}
interface DashboardHeaderProps {
- selectedTeam: Team
- onTeamChange: (teamId: string) => void
- activeTab: string
- setActiveTab: (tab: string) => void
- teams: Team[]
+ selectedTeam: Team;
+ onTeamChange: (teamId: string) => void;
+ activeTab: string;
+ setActiveTab: (tab: string) => void;
+ teams: Team[];
+ policyLists: PolicyList[];
}
-export function DashboardHeader({ selectedTeam, onTeamChange, activeTab, setActiveTab, teams }: DashboardHeaderProps) {
+export function DashboardHeader({ selectedTeam, onTeamChange, activeTab, setActiveTab, teams, policyLists }: DashboardHeaderProps) {
const { logout } = useSession()
const [notifications, setNotifications] = useState<Notification[]>([
{
@@ -117,7 +120,7 @@ export function DashboardHeader({ selectedTeam, onTeamChange, activeTab, setActi
{/* Global Team Selector */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
- <Button variant="outline" className="border-purple-500 text-purple-400 hover:bg-purple-950">
+ <Button size="sm" variant="outline" className="border-purple-500 text-purple-400 hover:bg-purple-950">
<Crown className="mr-2 h-4 w-4" />
{selectedTeam.name}
<ChevronDown className="ml-2 h-4 w-4" />
@@ -217,10 +220,10 @@ export function DashboardHeader({ selectedTeam, onTeamChange, activeTab, setActi
>
<div
className={`rounded-full p-1.5 mr-3 ${notification.type === "report"
- ? "bg-yellow-500/20"
- : notification.type === "ban"
- ? "bg-red-500/20"
- : "bg-blue-500/20"
+ ? "bg-yellow-500/20"
+ : notification.type === "ban"
+ ? "bg-red-500/20"
+ : "bg-blue-500/20"
}`}
>
{notification.type === "report" ? (
@@ -266,43 +269,7 @@ export function DashboardHeader({ selectedTeam, onTeamChange, activeTab, setActi
</Popover>
{/* Quick Action Buttons */}
- <Dialog>
- <DialogTrigger asChild>
- <Button
- variant="outline"
- size="sm"
- className="hidden md:flex border-red-500 text-red-400 hover:bg-red-950 hover:text-red-300"
- >
- <Ban className="mr-2 h-4 w-4" />
- Ban User
- </Button>
- </DialogTrigger>
- <DialogContent className="bg-gray-950 border-gray-800">
- <DialogHeader>
- <DialogTitle>Ban User</DialogTitle>
- <DialogDescription className="text-gray-400">Ban a user from your Matrix rooms</DialogDescription>
- </DialogHeader>
- <div className="space-y-4 py-4">
- <div className="space-y-2">
- <Label htmlFor="user-id">Matrix User ID</Label>
- <Input id="user-id" placeholder="@user:matrix.org" className="bg-gray-900 border-gray-800" />
- </div>
- <div className="space-y-2">
- <Label htmlFor="ban-reason">Reason</Label>
- <Input id="ban-reason" placeholder="Reason for the ban" className="bg-gray-900 border-gray-800" />
- </div>
- </div>
- <DialogFooter>
- <Button
- variant="outline"
- className="border-gray-700 text-gray-400 hover:bg-gray-900 hover:text-gray-300"
- >
- Cancel
- </Button>
- <Button className="bg-red-600 text-white hover:bg-red-700">Ban User</Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
+ <AddBan header policyLists={policyLists} />
<Dialog>
<DialogTrigger asChild>
@@ -340,6 +307,7 @@ export function DashboardHeader({ selectedTeam, onTeamChange, activeTab, setActi
<Button
variant="outline"
+ size="sm"
className="hidden md:flex border-purple-500 text-purple-400 hover:bg-purple-950 hover:text-purple-300"
onClick={handleLogout}
>
diff --git a/src/app/dashboard/components/modals/add-ban.tsx b/src/app/dashboard/components/modals/add-ban.tsx
@@ -0,0 +1,115 @@
+import { Button } from "@/components/ui/button";
+import { Checkbox } from "@/components/ui/checkbox";
+import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { Textarea } from "@/components/ui/textarea";
+import { Ban, Plus } from "lucide-react";
+import { PolicyList } from "../../page";
+
+interface AddBanProps {
+ policyLists: PolicyList[];
+ filled?: boolean;
+ header?: boolean;
+}
+
+export default function AddBan({ filled, policyLists, header }: AddBanProps) {
+ return (
+ <Dialog>
+ <DialogTrigger asChild>
+ {header ? (
+ <Button
+ variant="outline"
+ size="sm"
+ className="hidden md:flex border-red-500 text-red-400 hover:bg-red-950 hover:text-red-300"
+ >
+ <Ban className="mr-2 h-4 w-4" />
+ Add Ban
+ </Button>
+ ) : (
+ filled ? (
+ <Button size="sm" className="bg-purple-600 text-white hover:bg-purple-700">
+ <Ban className="mr-2 h-4 w-4" />
+ Add Ban
+ </Button>
+ ) : (
+ <Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-purple-400">
+ <Plus className="h-3 w-3 mr-1" />
+ Add Ban
+ </Button>
+ )
+ )}
+
+ </DialogTrigger>
+ <DialogContent className="bg-gray-950 border-gray-800">
+ <DialogHeader>
+ <DialogTitle>Add New Ban</DialogTitle>
+ <DialogDescription className="text-gray-400">
+ Ban a user or server from your Matrix rooms
+ </DialogDescription>
+ </DialogHeader>
+ <div className="space-y-4 py-4">
+ <div className="space-y-2">
+ <Label htmlFor="ban-type">Ban Type</Label>
+ <Select defaultValue="user">
+ <SelectTrigger className="bg-gray-900 border-gray-800">
+ <SelectValue placeholder="Select ban type" />
+ </SelectTrigger>
+ <SelectContent className="bg-gray-900 border-gray-800">
+ <SelectItem value="user">User</SelectItem>
+ <SelectItem value="server">Server</SelectItem>
+ <SelectItem value="room">Room</SelectItem>
+ </SelectContent>
+ </Select>
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="ban-target">Target</Label>
+ <Input
+ id="ban-target"
+ placeholder="@user:matrix.org"
+ className="bg-gray-900 border-gray-800"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="ban-reason">Reason</Label>
+ <Textarea
+ id="ban-reason"
+ placeholder="Reason for the ban"
+ className="bg-gray-900 border-gray-800"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>Policy Lists</Label>
+ <div className="space-y-2 mt-1">
+ {policyLists
+ .filter((list) => !list.readOnly)
+ .map((list) => (
+ <div key={list.id} className="flex items-center space-x-2">
+ <Checkbox id={`list-${list.id}`} />
+ <Label htmlFor={`list-${list.id}`} className="font-normal">
+ {list.name}
+ </Label>
+ </div>
+ ))}
+ </div>
+ </div>
+ <div className="space-y-2">
+ <p className="text-xs text-gray-400">
+ Bans are applied to all protected rooms monitored by this bot.
+ </p>
+ </div>
+ </div>
+ <DialogFooter>
+ <Button
+ variant="outline"
+ className="border-gray-700 text-gray-400 hover:bg-gray-900 hover:text-gray-300"
+ >
+ Cancel
+ </Button>
+ <Button className="bg-red-600 text-white hover:bg-red-700">Add Ban</Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog >
+ )
+}
+\ No newline at end of file
diff --git a/src/app/dashboard/components/overview-tab.tsx b/src/app/dashboard/components/overview-tab.tsx
@@ -1,32 +1,15 @@
-"use client"
-import { Plus, X, AlertTriangle, Ban, CheckCircle } from "lucide-react"
+import { AlertTriangle, Ban, CheckCircle } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
-import { Button } from "@/components/ui/button"
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog"
-import { Label } from "@/components/ui/label"
-import { Input } from "@/components/ui/input"
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { PolicyList, Report } from "../page"
-
-interface Team {
- id: string
- name: string
- rooms: string[]
-}
+import ProtectedRomsList from "./protected-rooms-list"
+import { Team } from "../team-management"
+import AddProtectedRoom from "./modals/add-protected-room"
interface OverviewTabProps {
- selectedTeam: Team
- policyLists: PolicyList[]
- reports: Report[]
+ selectedTeam: Team;
+ policyLists: PolicyList[];
+ reports: Report[];
}
export function OverviewTab({ selectedTeam, policyLists, reports }: OverviewTabProps) {
@@ -41,53 +24,7 @@ export function OverviewTab({ selectedTeam, policyLists, reports }: OverviewTabP
<div className="text-2xl font-bold">{selectedTeam.rooms.length}</div>
<div className="flex justify-between items-center">
<p className="text-xs text-gray-400">Monitored by this bot</p>
- <Dialog>
- <DialogTrigger asChild>
- <Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-purple-400">
- <Plus className="h-3 w-3 mr-1" />
- Add Room
- </Button>
- </DialogTrigger>
- <DialogContent className="bg-gray-950 border-gray-800">
- <DialogHeader>
- <DialogTitle>Add Protected Room</DialogTitle>
- <DialogDescription className="text-gray-400">
- Add a Matrix room to be monitored by this bot
- </DialogDescription>
- </DialogHeader>
- <div className="space-y-4 py-4">
- <div className="space-y-2">
- <Label htmlFor="room-id">Room ID or Alias</Label>
- <Input id="room-id" placeholder="#roomname:matrix.org" className="bg-gray-900 border-gray-800" />
- <p className="text-xs text-gray-400">
- Enter a room ID or alias. The bot must be invited to this room.
- </p>
- </div>
- <div className="space-y-2">
- <Label>Protection Level</Label>
- <Select defaultValue="standard">
- <SelectTrigger className="bg-gray-900 border-gray-800">
- <SelectValue placeholder="Select protection level" />
- </SelectTrigger>
- <SelectContent className="bg-gray-900 border-gray-800">
- <SelectItem value="minimal">Minimal - Report only</SelectItem>
- <SelectItem value="standard">Standard - Report and auto-moderate</SelectItem>
- <SelectItem value="strict">Strict - Aggressive moderation</SelectItem>
- </SelectContent>
- </Select>
- </div>
- </div>
- <DialogFooter>
- <Button
- variant="outline"
- className="border-gray-700 text-gray-400 hover:bg-gray-900 hover:text-gray-300"
- >
- Cancel
- </Button>
- <Button className="bg-purple-600 text-white hover:bg-purple-700">Add Room</Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
+ <AddProtectedRoom />
</div>
</CardContent>
</Card>
@@ -174,34 +111,11 @@ export function OverviewTab({ selectedTeam, policyLists, reports }: OverviewTabP
<CardTitle>Protected Rooms</CardTitle>
<CardDescription className="text-gray-400">Rooms monitored by this bot</CardDescription>
</div>
- <Dialog>
- <DialogTrigger asChild>
- <Button size="sm" className="bg-purple-600 text-white hover:bg-purple-700">
- <Plus className="mr-2 h-4 w-4" />
- Add Room
- </Button>
- </DialogTrigger>
- <DialogContent className="bg-gray-950 border-gray-800">
- {/* Same content as the other add room dialog */}
- </DialogContent>
- </Dialog>
+ <AddProtectedRoom filled />
</div>
</CardHeader>
<CardContent>
- <div className="space-y-4">
- {selectedTeam.rooms.map((room, index) => (
- <div key={index} className="flex items-center justify-between">
- <div className="flex items-center gap-2">
- <div className="h-2 w-2 rounded-full bg-green-500"></div>
- <p className="text-sm">{room}</p>
- </div>
- <Button variant="ghost" size="sm" className="h-7 w-7 p-0 text-gray-400 hover:text-red-400">
- <X className="h-4 w-4" />
- <span className="sr-only">Remove</span>
- </Button>
- </div>
- ))}
- </div>
+ <ProtectedRomsList team={selectedTeam} />
</CardContent>
</Card>
</div>
diff --git a/src/app/dashboard/components/protected-rooms-list.tsx b/src/app/dashboard/components/protected-rooms-list.tsx
@@ -0,0 +1,25 @@
+import { Button } from "@/components/ui/button";
+import { Trash2 } from "lucide-react";
+import { Team } from "../team-management";
+
+interface ProtectedRomsListProps {
+ team: Team;
+}
+
+export default function ProtectedRomsList({ team }: ProtectedRomsListProps) {
+ return (
+ <div className="p-4 space-y-3">
+ {team.rooms.map((room, index) => (
+ <div key={index} className="flex items-center justify-between p-3 px-4 rounded-md bg-gray-900">
+ <div>
+ <p>{room}</p>
+ </div>
+ <Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-gray-400 hover:text-red-400">
+ <Trash2 className="h-4 w-4" />
+ <span className="sr-only">Remove</span>
+ </Button>
+ </div>
+ ))}
+ </div>
+ )
+}
+\ No newline at end of file
diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
@@ -10,6 +10,13 @@ import { ReportsTab } from "./components/reports-tab"
import { DashboardHeader } from "./components/dashboard-header"
import { OverviewTab } from "./components/overview-tab"
+export const EBanTypes = {
+ User: "user",
+ Server: "server",
+ Room: "room",
+} as const;
+type BanTypes = typeof EBanTypes[keyof typeof EBanTypes];
+
export interface PolicyList {
id: string;
name: string;
@@ -21,6 +28,7 @@ export interface PolicyList {
target: string,
reason: string,
timestamp: string,
+ type: BanTypes;
}[];
};
@@ -163,14 +171,22 @@ export default function DashboardPage() {
target: "@spammer:badserver.org",
reason: "Spam across multiple rooms",
timestamp: "2025-04-20T10:30:00Z",
+ type: EBanTypes.User,
},
{
id: "ban_2",
target: "@troll:badserver.org",
reason: "Harassment and inappropriate content",
timestamp: "2025-04-19T15:45:00Z",
+ type: EBanTypes.User,
+ },
+ {
+ id: "ban_3",
+ target: "@phisher:scam.org",
+ reason: "Phishing attempts",
+ timestamp: "2025-04-18T09:20:00Z",
+ type: EBanTypes.User,
},
- { id: "ban_3", target: "@phisher:scam.org", reason: "Phishing attempts", timestamp: "2025-04-18T09:20:00Z" },
],
},
{
@@ -185,8 +201,23 @@ export default function DashboardPage() {
target: "@disruptor:matrix.org",
reason: "Disruptive behavior in #general",
timestamp: "2025-04-21T08:15:00Z",
+ type: EBanTypes.User,
+ },
+ {
+ id: "ban_5",
+ target: "@bot123:unknown.org",
+ reason: "Automated spam",
+ timestamp: "2025-04-20T14:30:00Z",
+ type: EBanTypes.User,
+ },
+ // Server ban
+ {
+ id: "ban_6",
+ target: "badserver.org",
+ reason: "Known spam server",
+ timestamp: "2025-04-19T11:00:00Z",
+ type: EBanTypes.Server,
},
- { id: "ban_5", target: "@bot123:unknown.org", reason: "Automated spam", timestamp: "2025-04-20T14:30:00Z" },
],
},
{
@@ -201,6 +232,7 @@ export default function DashboardPage() {
target: "@angryuser:matrix.org",
reason: "Abusive language to support staff",
timestamp: "2025-04-21T11:45:00Z",
+ type: EBanTypes.User,
},
],
},
@@ -216,6 +248,7 @@ export default function DashboardPage() {
target: "@spambot:matrix.org",
reason: "Code spam in development channels",
timestamp: "2025-04-19T09:30:00Z",
+ type: EBanTypes.User,
},
],
},
@@ -237,6 +270,7 @@ export default function DashboardPage() {
activeTab={activeTab}
setActiveTab={setActiveTab}
teams={mockTeams}
+ policyLists={policyLists}
/>
<main className="flex-1 container px-4 py-8 md:px-6 md:py-12">
<Tabs defaultValue="overview" value={activeTab} onValueChange={setActiveTab} className="space-y-8">
diff --git a/src/app/dashboard/team-management.tsx b/src/app/dashboard/team-management.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import { useState } from "react"
import { UserPlus, Settings, Crown, Trash2, LogOut } from "lucide-react"
@@ -29,6 +27,7 @@ import {
import { ScrollArea } from "@/components/ui/scroll-area"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import AddProtectedRoom from "./components/modals/add-protected-room";
+import ProtectedRomsList from "./components/protected-rooms-list";
export interface Team {
id: string;
@@ -324,19 +323,7 @@ export function TeamManagement({ selectedTeam: selectedTeamProp, onTeamChange }:
</div>
<ScrollArea className="h-[300px] rounded-md border border-gray-800">
- <div className="p-4 space-y-3">
- {selectedTeam.rooms.map((room, index) => (
- <div key={index} className="flex items-center justify-between p-3 px-4 rounded-md bg-gray-900">
- <div>
- <p>{room}</p>
- </div>
- <Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-gray-400 hover:text-red-400">
- <Trash2 className="h-4 w-4" />
- <span className="sr-only">Remove</span>
- </Button>
- </div>
- ))}
- </div>
+ <ProtectedRomsList team={selectedTeam} />
</ScrollArea>
</TabsContent>