add card deletion

This commit is contained in:
sk1982 2024-04-02 01:50:39 -04:00
parent 619c8bf2cd
commit 18271a7d69
6 changed files with 53 additions and 14 deletions

View File

@ -12,9 +12,9 @@ import { getGlobalConfig } from '@/config';
import { UserPayload } from '@/types/user';
import { DB } from '@/types/db';
export const getCards = async (user: UserPayload) => {
export const getCards = async (user: number) => {
return db.selectFrom('aime_card')
.where('user', '=', user.id)
.where('user', '=', user)
.selectAll()
.execute();
};
@ -65,11 +65,26 @@ export const adminAddCardToUser = async (user: number, code: string): Promise<Ac
return { data: await getUsers() };
};
export const deleteCard = async (user: number, id: number) => {
const requestingUser = await requireUser();
if (requestingUser.id !== user)
requirePermission(requestingUser.permissions, UserPermissions.USERMOD);
await db.deleteFrom('aime_card')
.where('id', '=', id)
.where('user', '=', user)
.executeTakeFirst();
revalidatePath('/settings', 'page');
revalidatePath('/admin/users', 'page');
};
export const userAddCard = async (code: string): Promise<ActionResult<{ card: DB['aime_card'] }>> => {
const user = await requireUser();
if (!hasPermission(user.permissions, UserPermissions.USERMOD)) {
const cards = await getCards(user);
const cards = await getCards(user.id);
if (!getGlobalConfig('allow_user_add_card'))
return { error: true, message: 'You do not have permissions to add a card' };

View File

@ -12,7 +12,7 @@ import { hasPermission } from '@/helpers/permissions';
import { AimeCard } from '@/components/aime-card';
import { useErrorModal } from '@/components/error-modal';
import { AdminUser } from '@/data/user';
import { adminAddCardToUser } from '@/actions/card';
import { adminAddCardToUser, deleteCard } from '@/actions/card';
import { TrashIcon } from '@heroicons/react/24/outline';
import { useConfirmModal } from '@/components/confirm-modal';
import Link from 'next/link';
@ -75,7 +75,7 @@ export const AdminUserList = ({ users: initialUsers }: { users: AdminUser[]; })
onSelectionChange={s => typeof s !== 'string' && setOpenUsers(s)}
className="my-1 border-b sm:border-b-0 border-divider sm:bg-content1 sm:rounded-lg sm:px-4 overflow-hidden">
{users.map(userEntry => (<AccordionItem key={userEntry.uuid}
{users.map(userEntry => (<AccordionItem key={userEntry.uuid ?? userEntry.id}
id={userEntry.uuid ?? undefined} indicator={({ isOpen }) => <Tooltip content="Show cards">
<div className="flex items-center">
<CreditCardIcon className="h-6 w-6 mr-1" />
@ -142,6 +142,14 @@ export const AdminUserList = ({ users: initialUsers }: { users: AdminUser[]; })
<section className="flex sm:p-4">
<div className="flex-grow flex flex-wrap items-center justify-center gap-2">
{userEntry.cards.map(c => <AimeCard key={c.access_code}
canDelete
onDelete={() => {
deleteCard(c.user!, c.id!);
setUsers(u => u.map(u => u.id === userEntry.id ? {
...u,
cards: u.cards.filter(card => card.id !== c.id)
} : u));
}}
card={{
...c,
created_date: new Date(c.created_date!),

View File

@ -11,7 +11,7 @@ import { UserPermissions } from '@/types/permissions';
import { hasPermission } from '@/helpers/permissions';
import { useErrorModal } from '@/components/error-modal';
import { useState } from 'react';
import { userAddCard } from '@/actions/card';
import { deleteCard, userAddCard } from '@/actions/card';
export type CardsProps = {
cards: DB['aime_card'][],
@ -21,7 +21,7 @@ export type CardsProps = {
export const Cards = ({ cards: initialCards, canAddCard, maxCard }: CardsProps) => {
const prompt = usePromptModal();
const user = useUser();
const user = useUser({ required: true });
const setError = useErrorModal();
const [cards, setCards] = useState(initialCards);
@ -29,7 +29,7 @@ export const Cards = ({ cards: initialCards, canAddCard, maxCard }: CardsProps)
<header className="text-2xl font-semibold flex px-4 h-16 items-center">
Cards
{(hasPermission(user?.permissions, UserPermissions.USERMOD) ||
{(hasPermission(user.permissions, UserPermissions.USERMOD) ||
(canAddCard && cards.length < (maxCard ?? Infinity))) && <Tooltip content="Add card">
<Button isIconOnly className="ml-auto" onPress={() => {
promptAccessCode(prompt, 'Enter an access code for this card', code => {
@ -47,7 +47,10 @@ export const Cards = ({ cards: initialCards, canAddCard, maxCard }: CardsProps)
</header>
<Divider className="mb-4 hidden sm:block" />
<div className="px-1 sm:px-4 sm:pb-4 flex flex-wrap items-center justify-center gap-4">
{cards.map(c => <AimeCard key={c.id} card={c} className="w-full" />)}
{cards.map(c => <AimeCard canDelete key={c.id} card={c} className="w-full" onDelete={() => {
deleteCard(user.id, c.id);
setCards(cards => cards.filter(card => card.id !== c.id));
}} />)}
</div>
</section>);
};

View File

@ -7,7 +7,7 @@ import { getGlobalConfig } from '@/config';
export default async function SettingsPage() {
const user = await requireUser();
const cards = await getCards(user);
const cards = await getCards(user.id);
return (<div className="w-full flex items-center justify-center">
<div className="w-full max-w-full sm:max-w-5xl flex flex-col gap-2 2xl:max-w-screen-4xl 2xl:grid grid-cols-12">

View File

@ -5,22 +5,27 @@ import { useState } from 'react';
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
import { Button, Tooltip } from '@nextui-org/react';
import { useUser } from '@/helpers/use-user';
import { TbHammer, TbHammerOff, TbLock, TbLockOpen } from 'react-icons/tb';
import { TbHammer, TbHammerOff, TbLock, TbLockOpen, TbTrashX } from 'react-icons/tb';
import { hasPermission } from '@/helpers/permissions';
import { UserPermissions } from '@/types/permissions';
import { banUnbanCard, lockUnlockCard } from '@/actions/card';
import { useConfirmModal } from './confirm-modal';
type AimeCardProps = {
card: DB['aime_card'],
className?: string,
canDelete?: boolean,
onDelete?: () => void
};
export const AimeCard = ({ className, card }: AimeCardProps) => {
export const AimeCard = ({ className, card, canDelete, onDelete }: AimeCardProps) => {
const [showCode, setShowCode] = useState(false);
const user = useUser();
const canBan = hasPermission(user?.permissions, UserPermissions.USERMOD);
const [locked, setLocked] = useState(!!card.is_locked ?? false);
const [banned, setBanned] = useState(!!card.is_banned ?? true);
const confirm = useConfirmModal();
const formatOptions = {
year: '2-digit',
month: 'numeric',
@ -73,9 +78,16 @@ export const AimeCard = ({ className, card }: AimeCardProps) => {
</>}
</div>
<div className="text-sm sm:text-medium">
<div className="text-sm sm:text-medium flex items-end">
{card.last_login_date ? `Last Used ${card.last_login_date.toLocaleTimeString(undefined, formatOptions)}` :
'Never Used'}
{canDelete && <Tooltip content={<span className="text-danger">Delete this card</span>}>
<Button className="ml-auto" isIconOnly color="danger" variant="faded" onPress={() => confirm('Do you want to delete this card?',
() => onDelete?.())}>
<TbTrashX className="w-7 h-7" />
</Button>
</Tooltip>}
</div>
{(locked || banned) && <div className="absolute flex items-center justify-center w-full left-0 top-[60%] backdrop-blur h-12 sm:h-16 bg-gray-600/50 font-bold sm:text-2xl">

View File

@ -88,7 +88,8 @@ export const getUsers = async () => {
] as const)
.execute();
return parseJsonResult(res, ['cards']);
const data = parseJsonResult(res, ['cards']);
return data.map(d => ({ ...d, cards: d.cards.filter(c => c.id !== null) }));
};
export type AdminUser = Awaited<ReturnType<typeof getUsers>>[number];