Skip to content

Commit

Permalink
Merge pull request #273 from Shelf-nu/264-feature-request-improvement…
Browse files Browse the repository at this point in the history
…s-to-pagination

264 feature request improvements to pagination
  • Loading branch information
DonKoko authored Aug 1, 2023
2 parents 25f3477 + 3d02c20 commit 1ed4f9a
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 60 deletions.
25 changes: 13 additions & 12 deletions app/components/list/pagination/pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMemo } from "react";
import { useLoaderData } from "@remix-run/react";
import Input from "~/components/forms/input";

Check warning on line 3 in app/components/list/pagination/pagination.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

'Input' is defined but never used
import { Button } from "~/components/shared/button";
import type { IndexResponse } from "~/routes/_layout+/assets._index";
import { PageNumber } from "./page-number";

Check warning on line 6 in app/components/list/pagination/pagination.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

'PageNumber' is defined but never used
Expand All @@ -8,13 +9,13 @@ export const Pagination = () => {
const { page, totalItems, totalPages, perPage, next, prev } =
useLoaderData<IndexResponse>();

const pageNumbers = useMemo(() => {
const pages = [];
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
return pages;
}, [totalPages]);
// const pageNumbers = useMemo(() => {
// const pages = [];
// for (let i = 1; i <= totalPages; i++) {
// pages.push(i);
// }
// return pages;
// }, [totalPages]);

const { prevDisabled, nextDisabled } = useMemo(
() => ({
Expand All @@ -30,11 +31,11 @@ export const Pagination = () => {
{"< Previous"}
</Button>

<ul className="flex gap-[2px]">
{pageNumbers.map((pageNumber) => (
<PageNumber number={pageNumber} key={pageNumber} />
))}
</ul>
<div className="flex items-center gap-2 py-2 text-gray-400">
<span>{page === 0 ? 1 : page}</span>
<span>/</span>
<span>{totalPages}</span>
</div>

<Button variant="secondary" size="sm" to={next} disabled={nextDisabled}>
{"Next >"}
Expand Down
72 changes: 72 additions & 0 deletions app/components/user/delete-user.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { User } from "@prisma/client";
import { Form } from "@remix-run/react";
import { Button } from "~/components/shared/button";

import {
AlertDialog,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "~/components/shared/modal";
import { TrashIcon } from "../icons";

export const DeleteUser = ({
user,
}: {
user: {
id: User["id"];
firstName: User["firstName"];
lastName: User["lastName"];
};
}) => (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="secondary"
data-test-id="deleteUserButton"
className="justify-start px-6 py-3 text-sm font-semibold text-gray-700 outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-slate-100 hover:text-gray-700"
width="full"
>
Delete
</Button>
</AlertDialogTrigger>

<AlertDialogContent>
<AlertDialogHeader>
<div className="mx-auto md:m-0">
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-error-50 p-2 text-error-600">
<TrashIcon />
</span>
</div>
<AlertDialogTitle>
Delete {user.firstName} {user.lastName}
</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this user? This action cannot be
undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<div className="flex justify-center gap-2">
<AlertDialogCancel asChild>
<Button variant="secondary">Cancel</Button>
</AlertDialogCancel>

<Form method="delete">
<Button
className="border-error-600 bg-error-600 hover:border-error-800 hover:bg-error-800"
type="submit"
data-test-id="confirmdeleteUserButton"
>
Delete
</Button>
</Form>
</div>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
37 changes: 37 additions & 0 deletions app/modules/user/service.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Prisma, Roles } from "@prisma/client";
import type { Category, User } from "@prisma/client";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
import { json, type LoaderArgs } from "@remix-run/node";
import sharp from "sharp";
import { db } from "~/database";
Expand Down Expand Up @@ -319,3 +320,39 @@ export async function updateProfilePicture({
profilePicture: getPublicFileURL({ filename: profilePicture }),
});
}

export async function deleteUser(id: User["id"]) {
if (!id) {
throw new Error("User ID is required");
}

try {
const user = await db.user.findUnique({
where: { id },
include: { organizations: true },
});

/** Find the personal org of the user and delete it */
const personalOrg = user?.organizations.find(
(org) => org.type === "PERSONAL"
);

await db.organization.delete({
where: { id: personalOrg?.id },
});

await db.user.delete({ where: { id } });
} catch (error) {
if (
error instanceof PrismaClientKnownRequestError &&
error.code === "P2025"
) {
// eslint-disable-next-line no-console
console.log("User not found, so no need to delete");
} else {
throw error;
}
}

await deleteAuthAccount(id);
}
52 changes: 40 additions & 12 deletions app/routes/_layout+/admin-dashboard.$userId.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import type { Qr, User } from "@prisma/client";
import type { ActionArgs, LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form, useLoaderData, Link } from "@remix-run/react";
import { Button } from "~/components/shared";
import { Table, Td, Tr } from "~/components/table";
import { DeleteUser } from "~/components/user/delete-user";
import { db } from "~/database";
import { requireAuthSession } from "~/modules/auth";
import { generateOrphanedCodes } from "~/modules/qr";
import { deleteUser } from "~/modules/user";
import { isDelete } from "~/utils";
import { sendNotification } from "~/utils/emitter/send-notification.server";
import { requireAdmin } from "~/utils/roles.servers";

export type UserWithQrCodes = User & {
qrCodes: Qr[];
};

export const loader = async ({ request, params }: LoaderArgs) => {
requireAdmin(request);
const userId = params.userId as string;
const user = await db.user.findUnique({
const user = (await db.user.findUnique({
where: { id: userId },
include: {
qrCodes: {
orderBy: { createdAt: "desc" },
},
},
});
})) as UserWithQrCodes;

return json({ user });
};
Expand All @@ -27,27 +37,45 @@ export const handle = {
};

export const action = async ({ request, params }: ActionArgs) => {
const authSession = await requireAuthSession(request);
await requireAdmin(request);
const formData = await request.formData();
/** ID of the target user we are generating codes for */
const userId = params.userId as string;

await generateOrphanedCodes({
userId,
amount: Number(formData.get("amount")),
});
return json({ message: "Generated Orphaned QR codes" });
if (isDelete(request)) {
await deleteUser(userId);

sendNotification({
title: "User deleted",
message: "The user has been deleted successfully",
icon: { name: "trash", variant: "error" },
senderId: authSession.userId,
});
return redirect("/admin-dashboard");
} else {
await generateOrphanedCodes({
userId,
amount: Number(formData.get("amount")),
});
return json({ message: "Generated Orphaned QR codes" });
}
};

export default function Area51UserPage() {
const { user } = useLoaderData<typeof loader>();
return (
return user ? (
<div>
<div>
<div className="flex justify-between">
<h1>User: {user?.email}</h1>
<div>
<Button to={`/api/${user?.id}/orphaned-codes.zip`} reloadDocument>
<div className="flex gap-3">
<DeleteUser user={user} />
<Button
to={`/api/${user?.id}/orphaned-codes.zip`}
reloadDocument
className="whitespace-nowrap"
>
Print orphaned codes
</Button>
</div>
Expand Down Expand Up @@ -122,5 +150,5 @@ export default function Area51UserPage() {
</Table>
</div>
</div>
);
) : null;
}
36 changes: 0 additions & 36 deletions test/support/delete-user.ts

This file was deleted.

0 comments on commit 1ed4f9a

Please sign in to comment.