fix: General bug fixes/small feature improvements

This commit is contained in:
LukeGus
2025-11-10 16:23:46 -06:00
parent 966758ecf8
commit 7e8105a938
31 changed files with 1774 additions and 634 deletions

View File

@@ -1072,109 +1072,113 @@ export function AdminSettings({
</div>
) : (
<div className="border rounded-md overflow-hidden">
<Table>
<TableHeader>
<TableRow>
<TableHead className="px-4">Device</TableHead>
<TableHead className="px-4">User</TableHead>
<TableHead className="px-4">Created</TableHead>
<TableHead className="px-4">Last Active</TableHead>
<TableHead className="px-4">Expires</TableHead>
<TableHead className="px-4">
{t("admin.actions")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{sessions.map((session) => {
const DeviceIcon =
session.deviceType === "desktop"
? Monitor
: session.deviceType === "mobile"
? Smartphone
: Globe;
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead className="px-4">Device</TableHead>
<TableHead className="px-4">User</TableHead>
<TableHead className="px-4">Created</TableHead>
<TableHead className="px-4">Last Active</TableHead>
<TableHead className="px-4">Expires</TableHead>
<TableHead className="px-4">
{t("admin.actions")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{sessions.map((session) => {
const DeviceIcon =
session.deviceType === "desktop"
? Monitor
: session.deviceType === "mobile"
? Smartphone
: Globe;
const createdDate = new Date(session.createdAt);
const lastActiveDate = new Date(session.lastActiveAt);
const expiresDate = new Date(session.expiresAt);
const createdDate = new Date(session.createdAt);
const lastActiveDate = new Date(
session.lastActiveAt,
);
const expiresDate = new Date(session.expiresAt);
const formatDate = (date: Date) =>
date.toLocaleDateString() +
" " +
date.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
});
const formatDate = (date: Date) =>
date.toLocaleDateString() +
" " +
date.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
});
return (
<TableRow
key={session.id}
className={
session.isRevoked ? "opacity-50" : undefined
}
>
<TableCell className="px-4">
<div className="flex items-center gap-2">
<DeviceIcon className="h-4 w-4" />
<div className="flex flex-col">
<span className="font-medium text-sm">
{session.deviceInfo}
</span>
{session.isRevoked && (
<span className="text-xs text-red-600">
Revoked
return (
<TableRow
key={session.id}
className={
session.isRevoked ? "opacity-50" : undefined
}
>
<TableCell className="px-4">
<div className="flex items-center gap-2">
<DeviceIcon className="h-4 w-4" />
<div className="flex flex-col">
<span className="font-medium text-sm">
{session.deviceInfo}
</span>
)}
{session.isRevoked && (
<span className="text-xs text-red-600">
Revoked
</span>
)}
</div>
</div>
</div>
</TableCell>
<TableCell className="px-4">
{session.username || session.userId}
</TableCell>
<TableCell className="px-4 text-sm text-muted-foreground">
{formatDate(createdDate)}
</TableCell>
<TableCell className="px-4 text-sm text-muted-foreground">
{formatDate(lastActiveDate)}
</TableCell>
<TableCell className="px-4 text-sm text-muted-foreground">
{formatDate(expiresDate)}
</TableCell>
<TableCell className="px-4">
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={() =>
handleRevokeSession(session.id)
}
className="text-red-600 hover:text-red-700 hover:bg-red-50"
disabled={session.isRevoked}
>
<Trash2 className="h-4 w-4" />
</Button>
{session.username && (
</TableCell>
<TableCell className="px-4">
{session.username || session.userId}
</TableCell>
<TableCell className="px-4 text-sm text-muted-foreground">
{formatDate(createdDate)}
</TableCell>
<TableCell className="px-4 text-sm text-muted-foreground">
{formatDate(lastActiveDate)}
</TableCell>
<TableCell className="px-4 text-sm text-muted-foreground">
{formatDate(expiresDate)}
</TableCell>
<TableCell className="px-4">
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={() =>
handleRevokeAllUserSessions(
session.userId,
)
handleRevokeSession(session.id)
}
className="text-orange-600 hover:text-orange-700 hover:bg-orange-50 text-xs"
title="Revoke all sessions for this user"
className="text-red-600 hover:text-red-700 hover:bg-red-50"
disabled={session.isRevoked}
>
Revoke All
<Trash2 className="h-4 w-4" />
</Button>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
{session.username && (
<Button
variant="ghost"
size="sm"
onClick={() =>
handleRevokeAllUserSessions(
session.userId,
)
}
className="text-orange-600 hover:text-orange-700 hover:bg-orange-50 text-xs"
title="Revoke all sessions for this user"
>
Revoke All
</Button>
)}
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</div>
)}
</div>
@@ -1185,8 +1189,8 @@ export function AdminSettings({
<h3 className="text-lg font-semibold">
{t("admin.adminManagement")}
</h3>
<div className="space-y-4 p-6 border rounded-md bg-muted/50">
<h4 className="font-medium">{t("admin.makeUserAdmin")}</h4>
<div className="space-y-4 p-4 border rounded-md bg-dark-bg-panel">
<h4 className="font-semibold">{t("admin.makeUserAdmin")}</h4>
<form onSubmit={handleMakeUserAdmin} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="new-admin-username">
@@ -1279,32 +1283,17 @@ export function AdminSettings({
<TabsContent value="security" className="space-y-6">
<div className="space-y-4">
<div className="flex items-center gap-3">
<Database className="h-5 w-5" />
<h3 className="text-lg font-semibold">
{t("admin.databaseSecurity")}
</h3>
</div>
<div className="p-4 border rounded bg-card">
<div className="flex items-center gap-2">
<Lock className="h-4 w-4 text-green-500" />
<div>
<div className="text-sm font-medium">
{t("admin.encryptionStatus")}
</div>
<div className="text-xs text-green-500">
{t("admin.encryptionEnabled")}
</div>
</div>
</div>
</div>
<div className="grid gap-3 md:grid-cols-2">
<div className="p-4 border rounded bg-card">
<div className="p-4 border rounded-lg bg-dark-bg-panel">
<div className="space-y-3">
<div className="flex items-center gap-2">
<Download className="h-4 w-4 text-blue-500" />
<h4 className="font-medium">{t("admin.export")}</h4>
<h4 className="font-semibold">{t("admin.export")}</h4>
</div>
<p className="text-xs text-muted-foreground">
{t("admin.exportDescription")}
@@ -1351,11 +1340,11 @@ export function AdminSettings({
</div>
</div>
<div className="p-4 border rounded bg-card">
<div className="p-4 border rounded-lg bg-dark-bg-panel">
<div className="space-y-3">
<div className="flex items-center gap-2">
<Upload className="h-4 w-4 text-green-500" />
<h4 className="font-medium">{t("admin.import")}</h4>
<h4 className="font-semibold">{t("admin.import")}</h4>
</div>
<p className="text-xs text-muted-foreground">
{t("admin.importDescription")}