added unlocked (deletion) function for aime_user_game_locks in admin panel

This commit is contained in:
Polaris 2024-09-15 13:43:04 -04:00
parent 04edbf3df0
commit d13e1561ae
6 changed files with 258 additions and 40 deletions

View File

@ -0,0 +1,69 @@
"use server";
import { artemis } from "@/lib/prisma";
import type * as Prisma from "@prisma/client";
type AimeUserGameLocks = Prisma.PrismaClient;
export async function getLockedUsers() {
try {
const lockedUsers = await artemis.aime_user_game_locks.findMany({
select: {
id: true,
user: true,
game: true,
expires_at: true,
extra: true,
},
});
// user = aime_user_game_locks id column
const userIds = lockedUsers.map((lockedUsers) => lockedUsers.user);
const usersWithNames = await artemis.chuni_profile_data.findMany({
where: {
user: {
in: userIds,
},
},
select: {
user: true,
userName: true,
},
});
const LockedUsernames = lockedUsers.map((lockedUsers) => {
const user = usersWithNames.find((u) => u.user === lockedUsers.user);
return {
...lockedUsers,
userName: user?.userName || null,
};
});
return LockedUsernames;
} catch (error) {
console.error("Error fetching locked users:", error);
return null;
}
}
export async function deleteUserGameLocks(userId: number) {
try {
const result = await artemis.aime_user_game_locks.deleteMany({
where: {
user: userId,
},
});
if (result.count > 0) {
console.log(
`Successfully deleted ${result.count} rows for user ${userId}.`,
);
return { success: true, count: result.count };
} else {
console.log(`No rows found for user ${userId}.`);
return { success: false, message: `No rows found for user ${userId}.` };
}
} catch (error) {
console.error("Error deleting user game locks:", error);
return { success: false, message: "Error deleting user game locks." };
}
}

View File

@ -0,0 +1,73 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { aime_user_game_locks } from "@/prisma/schemas/artemis/generated/artemis";
import { Button } from "@/components/ui/button";
import { Trash2, UnlockIcon } from "lucide-react";
import { deleteUserGameLocks } from "./action";
type chunithm = aime_user_game_locks & { userName: string | null };
const handleDelete = async (userId: number) => {
try {
const result = await deleteUserGameLocks(userId);
if (result.success) {
console.log(
`Successfully deleted ${result.count} rows for user ${userId}.`,
);
} else {
console.error(result.message);
}
} catch (error) {
console.error("Error deleting user game locks:", error);
}
};
export const columns: ColumnDef<chunithm>[] = [
{
accessorKey: "Username",
header: "Username",
cell: ({ row }) => (
<div className="text-bold flex items-center">
<div className="ml-2 flex flex-col">
{row.original.userName || `User ID: ${row.original.user}`}{" "}
</div>
</div>
),
},
{
accessorKey: "ExpiresDate",
header: "Expiration Date",
cell: ({ row }) => {
const expirationDate = row.original.expires_at;
const currentDate = new Date();
if (!expirationDate) {
return <div>N/A</div>;
}
const expirationDateTime = new Date(expirationDate).getTime();
const currentDateTime = currentDate.getTime();
const fifteenMinuteBuffer = 15 * 60 * 1000;
if (expirationDateTime < currentDateTime - fifteenMinuteBuffer) {
return <div>Expired</div>;
}
return <div>{new Date(expirationDate).toLocaleDateString()}</div>;
},
},
{
accessorKey: "deletelock",
header: "Remove Lock",
cell: ({ row }) => (
<div>
<Button
className="hover:bg-red-400"
variant="ghost"
size="icon"
onClick={() => handleDelete(row.original.user)}
>
<UnlockIcon className="h-4 w-4" />
</Button>
</div>
),
},
];

View File

@ -0,0 +1,106 @@
"use client";
// https://github.com/dracor-org/einakter/blob/466ca1663098a16cc1141129a6ba22628135b04c/src/components/Table.tsx#L26
// used the above for reference on how to fuzzy search
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
ColumnDef,
SortingState,
getSortedRowModel,
flexRender,
getFilteredRowModel,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import DebouncedInput from "@/components/scoreplaylog/DebouncedInput";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});
return (
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id} className="pb-2 pl-4 pt-2">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-2 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<div className="flex items-center justify-end space-x-2 p-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
);
}

View File

@ -1,6 +1,10 @@
import UnlockUser from "./unlock"; import { getLockedUsers } from "./action";
import { DataTable } from "./data-table";
import { columns } from "./columns";
const FileExtractionPage = async () => { const UnlockUserPage = async () => {
return <UnlockUser />; const songs = (await getLockedUsers()) || [];
return <DataTable columns={columns} data={songs} />;
}; };
export default FileExtractionPage; export default UnlockUserPage;

View File

@ -1,34 +0,0 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@radix-ui/react-dropdown-menu";
import React from "react";
const GenerateKeychip = () => {
return (
<Card x-chunk="aimecard">
<CardHeader>
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
</CardHeader>
<CardContent>
<div className="mb-4 text-lg">Current Access Code:</div>
<form className="grid gap-4">
<div className="grid gap-2">
<Label>Access Code</Label>
<Input
name="accessCode"
type="text"
placeholder="*******************"
/>
</div>
<Button type="submit" className="w-full">
Link
</Button>
</form>
</CardContent>
</Card>
);
};
export default GenerateKeychip;

View File

@ -8,8 +8,8 @@ const NAV_ITEMS = [
{ href: "/admin/generateKeychip", label: "Generate Keychip" }, { href: "/admin/generateKeychip", label: "Generate Keychip" },
{ href: "/admin/unlockUser", label: "Unlock User" }, { href: "/admin/unlockUser", label: "Unlock User" },
{ href: "/admin/extraction", label: "Extract Game Files" }, // { href: "/admin/extraction", label: "Extract Game Files" },
{ href: "/admin/gameversions", label: "Edit Game Version" }, // { href: "/admin/gameversions", label: "Edit Game Version" },
]; ];
const AdminSubNavigation = () => { const AdminSubNavigation = () => {