draupnir4all-web

git clone git://archive.git.mtrnord.blog/MTRNord/draupnir4all-web.git
Log | Files | Refs | README | LICENSE

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:
Msrc/app/dashboard/analytics-dashboard.tsx | 118++++++++++++++++++++++++++++---------------------------------------------------
Msrc/app/dashboard/components/bans-pane.tsx | 137++++++++++++++++++-------------------------------------------------------------
Msrc/app/dashboard/components/dashboard-header.tsx | 64++++++++++++++++------------------------------------------------
Asrc/app/dashboard/components/modals/add-ban.tsx | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/app/dashboard/components/overview-tab.tsx | 106++++++++-----------------------------------------------------------------------
Asrc/app/dashboard/components/protected-rooms-list.tsx | 26++++++++++++++++++++++++++
Msrc/app/dashboard/page.tsx | 38++++++++++++++++++++++++++++++++++++--
Msrc/app/dashboard/team-management.tsx | 17++---------------
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>