added unlocked (deletion) function for aime_user_game_locks in admin panel
This commit is contained in:
parent
04edbf3df0
commit
d13e1561ae
@ -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." };
|
||||
}
|
||||
}
|
@ -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>
|
||||
),
|
||||
},
|
||||
];
|
@ -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>
|
||||
);
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
import UnlockUser from "./unlock";
|
||||
import { getLockedUsers } from "./action";
|
||||
import { DataTable } from "./data-table";
|
||||
import { columns } from "./columns";
|
||||
|
||||
const FileExtractionPage = async () => {
|
||||
return <UnlockUser />;
|
||||
const UnlockUserPage = async () => {
|
||||
const songs = (await getLockedUsers()) || [];
|
||||
|
||||
return <DataTable columns={columns} data={songs} />;
|
||||
};
|
||||
export default FileExtractionPage;
|
||||
export default UnlockUserPage;
|
||||
|
@ -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;
|
@ -8,8 +8,8 @@ const NAV_ITEMS = [
|
||||
{ href: "/admin/generateKeychip", label: "Generate Keychip" },
|
||||
|
||||
{ href: "/admin/unlockUser", label: "Unlock User" },
|
||||
{ href: "/admin/extraction", label: "Extract Game Files" },
|
||||
{ href: "/admin/gameversions", label: "Edit Game Version" },
|
||||
// { href: "/admin/extraction", label: "Extract Game Files" },
|
||||
// { href: "/admin/gameversions", label: "Edit Game Version" },
|
||||
];
|
||||
|
||||
const AdminSubNavigation = () => {
|
||||
|
Loading…
Reference in New Issue
Block a user