style: clean up code with prettier and eslint

This commit is contained in:
openhands committed 2026-07-12 20:31:05 +02:00
1 parent 60278b9e71
commit fb8e77bb68
378 files changed
+22407 -20689

No files matched your search

+2 -2
View File
@@ -1,4 +1,4 @@
import { Skeleton } from '@/components/ui/skeleton'
import { Skeleton } from "@/components/ui/skeleton";
export default function UserDetailLoading() {
return (
@@ -46,5 +46,5 @@ export default function UserDetailLoading() {
</div>
</div>
</div>
)
);
}
+4 -4
View File
@@ -1,11 +1,11 @@
import { redirect } from 'next/navigation'
import { redirect } from "next/navigation";
/** Legacy route — redirects to /admin/users/show/[id] */
export default async function UserLegacyPage({
params,
}: {
params: Promise<{ locale: string; id: string }>
params: Promise<{ locale: string; id: string }>;
}) {
const { id } = await params
redirect(`/admin/users/show/${id}`)
const { id } = await params;
redirect(`/admin/users/show/${id}`);
}
+27 -31
View File
@@ -1,37 +1,37 @@
import { MessageSquare, Terminal } from 'lucide-react'
import Link from 'next/link'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { MessageSquare, Terminal } from "lucide-react";
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
interface ChatMessage {
id: number
roomId: number
timestamp: number
message: string
id: number;
roomId: number;
timestamp: number;
message: string;
}
interface CommandEntry {
id: number
timestamp: number
command: string
params: string
succes: string
id: number;
timestamp: number;
command: string;
params: string;
succes: string;
}
interface UserActivityLogProps {
chatLogs: ChatMessage[]
commandLogs: CommandEntry[]
chatLogs: ChatMessage[];
commandLogs: CommandEntry[];
}
function formatTime(ts: number) {
if (!ts) return '-'
const d = new Date(ts * 1000)
if (!ts) return "-";
const d = new Date(ts * 1000);
return d.toLocaleString(undefined, {
day: '2-digit',
month: 'short',
hour: '2-digit',
minute: '2-digit',
})
day: "2-digit",
month: "short",
hour: "2-digit",
minute: "2-digit",
});
}
export function UserActivityLog({ chatLogs, commandLogs }: UserActivityLogProps) {
@@ -59,9 +59,7 @@ export function UserActivityLog({ chatLogs, commandLogs }: UserActivityLogProps)
>
Room #{log.roomId}
</Link>
<span className="text-xs text-muted-foreground">
{formatTime(log.timestamp)}
</span>
<span className="text-xs text-muted-foreground">{formatTime(log.timestamp)}</span>
</div>
<p className="break-words">{log.message}</p>
</div>
@@ -90,14 +88,12 @@ export function UserActivityLog({ chatLogs, commandLogs }: UserActivityLogProps)
<code className="font-mono text-xs font-bold">:{log.command}</code>
<div className="flex items-center gap-2">
<Badge
variant={log.succes === 'yes' ? 'default' : 'destructive'}
variant={log.succes === "yes" ? "default" : "destructive"}
className="text-[10px]"
>
{log.succes === 'yes' ? 'OK' : 'FAIL'}
{log.succes === "yes" ? "OK" : "FAIL"}
</Badge>
<span className="text-xs text-muted-foreground">
{formatTime(log.timestamp)}
</span>
<span className="text-xs text-muted-foreground">{formatTime(log.timestamp)}</span>
</div>
</div>
{log.params && (
@@ -110,5 +106,5 @@ export function UserActivityLog({ chatLogs, commandLogs }: UserActivityLogProps)
</CardContent>
</Card>
</div>
)
);
}
@@ -1,39 +1,39 @@
'use client'
"use client";
import { Plus, X } from 'lucide-react'
import { useState } from 'react'
import { giveBadge, removeBadge } from '@/actions/users'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { useServerAction } from '@/hooks/use-server-action'
import { Plus, X } from "lucide-react";
import { useState } from "react";
import { giveBadge, removeBadge } from "@/actions/users";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { useServerAction } from "@/hooks/use-server-action";
interface BadgeInfo {
id: number
badgeCode: string
id: number;
badgeCode: string;
}
interface UserBadgesSectionProps {
userId: number
badges: BadgeInfo[]
userId: number;
badges: BadgeInfo[];
}
export function UserBadgesSection({ userId, badges }: UserBadgesSectionProps) {
const { isPending, run } = useServerAction()
const [newBadge, setNewBadge] = useState('')
const { isPending, run } = useServerAction();
const [newBadge, setNewBadge] = useState("");
function handleAddBadge() {
if (!newBadge.trim()) return
if (!newBadge.trim()) return;
run(() => giveBadge({ userId, badgeCode: newBadge.trim() }), {
successMessage: `Badge "${newBadge.trim()}" added.`,
onSuccess: () => setNewBadge(''),
})
onSuccess: () => setNewBadge(""),
});
}
function handleRemoveBadge(badgeCode: string) {
run(() => removeBadge({ userId, badgeCode }), {
successMessage: `Badge "${badgeCode}" removed.`,
})
});
}
return (
@@ -47,7 +47,7 @@ export function UserBadgesSection({ userId, badges }: UserBadgesSectionProps) {
value={newBadge}
onChange={(e) => setNewBadge(e.target.value)}
placeholder="Badge code..."
onKeyDown={(e) => e.key === 'Enter' && handleAddBadge()}
onKeyDown={(e) => e.key === "Enter" && handleAddBadge()}
/>
<Button size="icon" onClick={handleAddBadge} disabled={isPending}>
<Plus className="h-4 w-4" />
@@ -69,7 +69,7 @@ export function UserBadgesSection({ userId, badges }: UserBadgesSectionProps) {
alt={badge.badgeCode}
className="h-5 w-5"
onError={(e) => {
;(e.target as HTMLImageElement).style.display = 'none'
(e.target as HTMLImageElement).style.display = "none";
}}
/>
<span>{badge.badgeCode}</span>
@@ -88,5 +88,5 @@ export function UserBadgesSection({ userId, badges }: UserBadgesSectionProps) {
</div>
</CardContent>
</Card>
)
);
}
+38 -44
View File
@@ -1,49 +1,43 @@
'use client'
"use client";
import { Ban, ShieldOff } from 'lucide-react'
import { useState } from 'react'
import { banUser, unbanUser } from '@/actions/users'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
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 { useServerAction } from '@/hooks/use-server-action'
import { Ban, ShieldOff } from "lucide-react";
import { useState } from "react";
import { banUser, unbanUser } from "@/actions/users";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
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 { useServerAction } from "@/hooks/use-server-action";
interface BanInfo {
id: number
reason: string
type: string
staffName: string
timestamp: number
banExpire: number
id: number;
reason: string;
type: string;
staffName: string;
timestamp: number;
banExpire: number;
}
interface UserBanSectionProps {
userId: number
username: string
bans: BanInfo[]
userId: number;
username: string;
bans: BanInfo[];
}
export function UserBanSection({ userId, username, bans }: UserBanSectionProps) {
const { isPending, run } = useServerAction()
const [showBanForm, setShowBanForm] = useState(false)
const [reason, setReason] = useState('')
const [duration, setDuration] = useState('24')
const [banType, setBanType] = useState('account')
const { isPending, run } = useServerAction();
const [showBanForm, setShowBanForm] = useState(false);
const [reason, setReason] = useState("");
const [duration, setDuration] = useState("24");
const [banType, setBanType] = useState("account");
const activeBan = bans.find((b) => {
const now = Math.floor(Date.now() / 1000)
return b.banExpire === 0 || b.banExpire > now
})
const now = Math.floor(Date.now() / 1000);
return b.banExpire === 0 || b.banExpire > now;
});
function handleBan() {
run(
@@ -52,22 +46,22 @@ export function UserBanSection({ userId, username, bans }: UserBanSectionProps)
userId,
reason,
duration: parseInt(duration, 10),
type: banType as 'account' | 'ip' | 'machine',
type: banType as "account" | "ip" | "machine",
}),
{
successMessage: `${username} has been banned.`,
onSuccess: () => {
setShowBanForm(false)
setReason('')
setShowBanForm(false);
setReason("");
},
},
)
);
}
function handleUnban() {
run(() => unbanUser({ userId }), {
successMessage: `${username} has been unbanned.`,
})
});
}
return (
@@ -94,9 +88,9 @@ export function UserBanSection({ userId, username, bans }: UserBanSectionProps)
<p className="text-sm font-medium text-destructive">Active Ban</p>
<p className="text-sm">{activeBan.reason}</p>
<p className="text-xs text-muted-foreground">
By {activeBan.staffName} &middot;{' '}
By {activeBan.staffName} &middot;{" "}
{activeBan.banExpire === 0
? 'Permanent'
? "Permanent"
: `Expires ${new Date(activeBan.banExpire * 1000).toLocaleString()}`}
</p>
</div>
@@ -137,7 +131,7 @@ export function UserBanSection({ userId, username, bans }: UserBanSectionProps)
</div>
</div>
<Button variant="destructive" onClick={handleBan} disabled={isPending || !reason}>
{isPending ? 'Banning...' : 'Confirm Ban'}
{isPending ? "Banning..." : "Confirm Ban"}
</Button>
</div>
)}
@@ -162,5 +156,5 @@ export function UserBanSection({ userId, username, bans }: UserBanSectionProps)
)}
</CardContent>
</Card>
)
);
}
+46 -58
View File
@@ -1,52 +1,46 @@
'use client'
"use client";
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { updateUser } from '@/actions/users'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { useServerAction } from '@/hooks/use-server-action'
import { type UpdateUserInput, updateUserSchema } from '@/lib/validators/user'
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { updateUser } from "@/actions/users";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useServerAction } from "@/hooks/use-server-action";
import { type UpdateUserInput, updateUserSchema } from "@/lib/validators/user";
interface UserEditFormProps {
user: {
id: number
username: string
mail: string
rank: number
motto: string
credits: number
pixels: number
diamonds: number
duckets: number
}
id: number;
username: string;
mail: string;
rank: number;
motto: string;
credits: number;
pixels: number;
diamonds: number;
duckets: number;
};
/** Ranks loaded from the `permissions` table (id + human-readable name). */
ranks?: Array<{ id: number; name: string }>
ranks?: Array<{ id: number; name: string }>;
}
// Fallback used if the page does not supply the DB-loaded rank list.
const FALLBACK_RANKS: Array<{ id: number; name: string }> = [
{ id: 1, name: 'User' },
{ id: 2, name: 'Bronze' },
{ id: 3, name: 'Silver' },
{ id: 4, name: 'Gold' },
{ id: 5, name: 'Moderator' },
{ id: 6, name: 'Admin' },
{ id: 7, name: 'Super Admin' },
]
{ id: 1, name: "User" },
{ id: 2, name: "Bronze" },
{ id: 3, name: "Silver" },
{ id: 4, name: "Gold" },
{ id: 5, name: "Moderator" },
{ id: 6, name: "Admin" },
{ id: 7, name: "Super Admin" },
];
export function UserEditForm({ user, ranks }: UserEditFormProps) {
const rankOptions = ranks && ranks.length > 0 ? ranks : FALLBACK_RANKS
const { isPending, run } = useServerAction()
const rankOptions = ranks && ranks.length > 0 ? ranks : FALLBACK_RANKS;
const { isPending, run } = useServerAction();
const {
register,
@@ -65,12 +59,12 @@ export function UserEditForm({ user, ranks }: UserEditFormProps) {
diamonds: user.diamonds,
duckets: user.duckets,
},
})
});
function onSubmit(data: UpdateUserInput) {
run(() => updateUser({ id: user.id, ...data }), {
successMessage: 'User updated successfully.',
})
successMessage: "User updated successfully.",
});
}
return (
@@ -83,14 +77,12 @@ export function UserEditForm({ user, ranks }: UserEditFormProps) {
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="username">Username</Label>
<Input id="username" {...register('username')} />
{errors.username && (
<p className="text-sm text-destructive">{errors.username.message}</p>
)}
<Input id="username" {...register("username")} />
{errors.username && <p className="text-sm text-destructive">{errors.username.message}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="mail">Email</Label>
<Input id="mail" type="email" {...register('mail')} />
<Input id="mail" type="email" {...register("mail")} />
{errors.mail && <p className="text-sm text-destructive">{errors.mail.message}</p>}
</div>
</div>
@@ -100,7 +92,7 @@ export function UserEditForm({ user, ranks }: UserEditFormProps) {
<Label htmlFor="rank">Rank</Label>
<Select
defaultValue={String(user.rank)}
onValueChange={(v) => setValue('rank', parseInt(v, 10))}
onValueChange={(v) => setValue("rank", parseInt(v, 10))}
>
<SelectTrigger>
<SelectValue placeholder="Select rank" />
@@ -116,38 +108,34 @@ export function UserEditForm({ user, ranks }: UserEditFormProps) {
</div>
<div className="space-y-2">
<Label htmlFor="motto">Motto</Label>
<Input id="motto" {...register('motto')} />
<Input id="motto" {...register("motto")} />
</div>
</div>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div className="space-y-2">
<Label htmlFor="credits">Credits</Label>
<Input id="credits" type="number" {...register('credits', { valueAsNumber: true })} />
<Input id="credits" type="number" {...register("credits", { valueAsNumber: true })} />
</div>
<div className="space-y-2">
<Label htmlFor="pixels">Pixels</Label>
<Input id="pixels" type="number" {...register('pixels', { valueAsNumber: true })} />
<Input id="pixels" type="number" {...register("pixels", { valueAsNumber: true })} />
</div>
<div className="space-y-2">
<Label htmlFor="diamonds">Diamonds</Label>
<Input
id="diamonds"
type="number"
{...register('diamonds', { valueAsNumber: true })}
/>
<Input id="diamonds" type="number" {...register("diamonds", { valueAsNumber: true })} />
</div>
<div className="space-y-2">
<Label htmlFor="duckets">Duckets</Label>
<Input id="duckets" type="number" {...register('duckets', { valueAsNumber: true })} />
<Input id="duckets" type="number" {...register("duckets", { valueAsNumber: true })} />
</div>
</div>
<Button type="submit" disabled={isPending}>
{isPending ? 'Saving...' : 'Save Changes'}
{isPending ? "Saving..." : "Save Changes"}
</Button>
</form>
</CardContent>
</Card>
)
);
}
+49 -62
View File
@@ -1,102 +1,89 @@
'use client'
"use client";
import { Check, Coins, Copy, KeyRound, MessageCircle, Unplug, Volume2, VolumeX } from 'lucide-react'
import { useState } from 'react'
import {
alertUser,
disconnectUser,
muteUser,
resetPassword,
sendCredits,
unmuteUser,
} from '@/actions/users'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useServerAction } from '@/hooks/use-server-action'
import { Check, Coins, Copy, KeyRound, MessageCircle, Unplug, Volume2, VolumeX } from "lucide-react";
import { useState } from "react";
import { alertUser, disconnectUser, muteUser, resetPassword, sendCredits, unmuteUser } from "@/actions/users";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useServerAction } from "@/hooks/use-server-action";
interface UserQuickActionsProps {
userId: number
username: string
isOnline: boolean
userId: number;
username: string;
isOnline: boolean;
}
export function UserQuickActions({ userId, username, isOnline }: UserQuickActionsProps) {
const { run, isPending } = useServerAction()
const { run, isPending } = useServerAction();
// Reset password state
const [newPassword, setNewPassword] = useState<string | null>(null)
const [copied, setCopied] = useState(false)
const [newPassword, setNewPassword] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
// Alert state
const [alertMessage, setAlertMessage] = useState('')
const [alertMessage, setAlertMessage] = useState("");
// Mute state
const [muteDuration, setMuteDuration] = useState('10')
const [muteDuration, setMuteDuration] = useState("10");
// Credits state
const [creditAmount, setCreditAmount] = useState('1000')
const [creditAmount, setCreditAmount] = useState("1000");
function handleResetPassword() {
if (!confirm(`Reset password for ${username}? A new random password will be generated.`)) return
if (!confirm(`Reset password for ${username}? A new random password will be generated.`)) return;
run(() => resetPassword({ userId }), {
successMessage: 'Password reset successfully.',
successMessage: "Password reset successfully.",
onSuccess: (data) => {
setNewPassword(data.newPassword as string)
setNewPassword(data.newPassword as string);
},
})
});
}
function handleDisconnect() {
run(() => disconnectUser({ userId }), {
successMessage: `${username} disconnected.`,
})
});
}
function handleAlert() {
if (!alertMessage.trim()) return
if (!alertMessage.trim()) return;
run(() => alertUser({ userId, message: alertMessage.trim() }), {
successMessage: 'Alert sent.',
onSuccess: () => setAlertMessage(''),
})
successMessage: "Alert sent.",
onSuccess: () => setAlertMessage(""),
});
}
function handleMute() {
const duration = Number(muteDuration)
if (Number.isNaN(duration) || duration < 0) return
const duration = Number(muteDuration);
if (Number.isNaN(duration) || duration < 0) return;
run(() => muteUser({ userId, duration }), {
successMessage: `${username} muted.`,
})
});
}
function handleUnmute() {
run(() => unmuteUser({ userId }), {
successMessage: `${username} unmuted.`,
})
});
}
function handleSendCredits() {
const amount = parseInt(creditAmount, 10)
if (!amount || amount <= 0) return
const amount = parseInt(creditAmount, 10);
if (!amount || amount <= 0) return;
run(() => sendCredits({ userId, amount }), {
successMessage: `${amount.toLocaleString()} credits sent to ${username}.`,
})
});
}
function copyPassword() {
if (newPassword) {
navigator.clipboard.writeText(newPassword)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
navigator.clipboard.writeText(newPassword);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
}
@@ -142,14 +129,10 @@ export function UserQuickActions({ userId, username, isOnline }: UserQuickAction
placeholder="Message to send..."
value={alertMessage}
onChange={(e) => setAlertMessage(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleAlert()}
onKeyDown={(e) => e.key === "Enter" && handleAlert()}
className="flex-1"
/>
<Button
onClick={handleAlert}
disabled={isPending || !alertMessage.trim()}
size="icon"
>
<Button onClick={handleAlert} disabled={isPending || !alertMessage.trim()} size="icon">
<MessageCircle className="h-4 w-4" />
</Button>
</div>
@@ -200,8 +183,8 @@ export function UserQuickActions({ userId, username, isOnline }: UserQuickAction
<DialogHeader>
<DialogTitle>Password Reset</DialogTitle>
<DialogDescription>
New password generated for <strong>{username}</strong>. Copy it now — it won&apos;t be
shown again.
New password generated for <strong>{username}</strong>. Copy it now — it won&apos;t be shown
again.
</DialogDescription>
</DialogHeader>
<div className="flex items-center gap-2 rounded-lg border bg-muted p-3">
@@ -209,11 +192,15 @@ export function UserQuickActions({ userId, username, isOnline }: UserQuickAction
{newPassword}
</code>
<Button variant="ghost" size="icon" onClick={copyPassword}>
{copied ? <Check className="h-4 w-4 text-[var(--admin-success)]" /> : <Copy className="h-4 w-4" />}
{copied ? (
<Check className="h-4 w-4 text-[var(--admin-success)]" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
</DialogContent>
</Dialog>
</>
)
);
}
+18 -18
View File
@@ -1,26 +1,26 @@
import { DoorOpen, Users } from 'lucide-react'
import Link from 'next/link'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { DoorOpen, Users } from "lucide-react";
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
interface UserRoom {
id: number
name: string
state: string
users: number
usersMax: number
id: number;
name: string;
state: string;
users: number;
usersMax: number;
}
interface UserRoomsProps {
rooms: UserRoom[]
rooms: UserRoom[];
}
const stateColors: Record<string, 'default' | 'secondary' | 'outline' | 'destructive'> = {
open: 'default',
locked: 'secondary',
password: 'destructive',
invisible: 'outline',
}
const stateColors: Record<string, "default" | "secondary" | "outline" | "destructive"> = {
open: "default",
locked: "secondary",
password: "destructive",
invisible: "outline",
};
export function UserRooms({ rooms }: UserRoomsProps) {
return (
@@ -45,7 +45,7 @@ export function UserRooms({ rooms }: UserRoomsProps) {
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">{room.name}</p>
<div className="flex items-center gap-2 mt-0.5">
<Badge variant={stateColors[room.state] || 'outline'} className="text-[10px]">
<Badge variant={stateColors[room.state] || "outline"} className="text-[10px]">
{room.state}
</Badge>
<span className="text-xs text-muted-foreground flex items-center gap-1">
@@ -61,5 +61,5 @@ export function UserRooms({ rooms }: UserRoomsProps) {
)}
</CardContent>
</Card>
)
);
}