Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12 batch user update #77

Merged
merged 7 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions components/user/SelectionActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import PlusOneIcon from "@mui/icons-material/PlusOne";
import { IconButton, Tooltip } from "@mui/material";

type SelectionActionsPropsType = {
checked: any;
increaseGrade: any;
};

export default function SelectionActions({
checked,
increaseGrade,
}: SelectionActionsPropsType) {
const checkedItems = Object.values(checked).filter((item) => item != false);
if (checkedItems.length > 0) {
//console.log("Checked Items", checkedItems);
return (
<Tooltip title="Klasse erhöhen">
<IconButton
type="button"
sx={{ p: "10px" }}
aria-label="klasse erhöhen"
onClick={increaseGrade}
>
<PlusOneIcon />
</IconButton>
</Tooltip>
);
} else return <div />;
}
138 changes: 98 additions & 40 deletions components/user/UserAdminList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Typography from "@mui/material/Typography";

import { Avatar, Grid } from "@mui/material";
import { Avatar, Checkbox, Grid, Paper } from "@mui/material";

import { UserType } from "@/entities/UserType";
import palette from "@/styles/palette";
Expand All @@ -12,22 +12,40 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";

import { RentalsUserType } from "@/entities/RentalsUserType";
import { useEffect } from "react";
import UserDetailsCard from "./UserDetailsCard";

type UserAdminListPropsType = {
users: Array<UserType>;
rentals: Array<RentalsUserType>;
searchString: string;
checked: any;
setChecked: any;
};

export default function UserAdminList({
users,
rentals,
searchString,
checked,
setChecked,
}: UserAdminListPropsType) {
//attach amount of rented books to the user

const rentalAmount: { [key: number]: number } = {};

//initialise user checked array

useEffect(() => {
const checkSet: { [key: string]: boolean } = users.reduce((acc, obj) => {
const userID = obj.id!.toString();
acc[userID] = false; // Initialize each key-value pair using the "id" field
return acc;
}, {} as { [key: string]: boolean });
setChecked(checkSet);
//console.log("Initialised user checkboxes", checkSet);
}, [users]);

rentals.map((r: any) => {
if (r.userid in rentalAmount) {
rentalAmount[r.userid] = rentalAmount[r.userid] + 1;
Expand All @@ -38,53 +56,93 @@ export default function UserAdminList({
<div>
{users.map((u: UserType) => {
const lowerCaseSearch = searchString.toLowerCase();
//console.log("Checked", checked);
const userID = u.id!.toString();
const checkBoxValue =
userID in checked ? (checked[userID] as boolean) : false;
if (
u.lastName.toLowerCase().includes(lowerCaseSearch) ||
u.firstName.toLowerCase().includes(lowerCaseSearch) ||
u.id!.toString().includes(lowerCaseSearch)
)
return (
<Accordion key={u.id}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
<Paper key={u.id} sx={{ mt: 0.5 }}>
<Grid
container
direction="row"
alignItems="center"
justifyContent="flex-start"
>
<Grid
container
direction="row"
alignItems="center"
justifyContent="flex-start "
sx={{ px: 10 }}
>
<Grid item sx={{ px: 3 }}>
{rentalAmount[u.id!] != undefined ? (
<Avatar sx={{ bgcolor: palette.secondary.dark }}>
{rentalAmount[u.id!]}
</Avatar>
) : (
<Avatar sx={{ bgcolor: palette.secondary.light }}>
0
</Avatar>
)}
</Grid>
<Grid>
<Typography>{u.lastName + ", " + u.firstName}</Typography>
<Typography variant="caption">
{"Klasse " + u.schoolGrade + " - " + u.schoolTeacherName}
</Typography>
</Grid>
<Grid item xs={1} sx={{ width: "100%", height: "100%" }}>
<Checkbox
checked={checkBoxValue ? checkBoxValue : false}
id={userID}
key={userID}
onChange={() => {
setChecked({ ...checked, [userID]: !checkBoxValue });
}}
inputProps={{ "aria-label": "controlled" }}
/>
</Grid>
<Grid item xs={11} sx={{ width: "100%", height: "100%" }}>
<Accordion
elevation={0}
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
}}
key={u.id}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
{" "}
<Grid
container
direction="row"
alignItems="center"
justifyContent="flex-start "
sx={{ px: 10 }}
>
<Grid item sx={{ px: 3 }}>
{rentalAmount[u.id!] != undefined ? (
<Avatar sx={{ bgcolor: palette.secondary.dark }}>
{rentalAmount[u.id!]}
</Avatar>
) : (
<Avatar sx={{ bgcolor: palette.secondary.light }}>
0
</Avatar>
)}
</Grid>
<Grid>
<Typography>
{u.lastName + ", " + u.firstName}
</Typography>
<Typography variant="caption">
{"Klasse " +
u.schoolGrade +
" - " +
u.schoolTeacherName}
</Typography>
</Grid>
</Grid>
</AccordionSummary>
<AccordionDetails>
<UserDetailsCard
user={u}
rentals={rentals.filter(
(r: any) => parseInt(r.userid) == u.id
)}
/>
</AccordionDetails>
</Accordion>
</Grid>
</AccordionSummary>
<AccordionDetails>
<UserDetailsCard
user={u}
rentals={rentals.filter(
(r: any) => parseInt(r.userid) == u.id
)}
/>
</AccordionDetails>
</Accordion>
</Grid>
</Paper>
);
})}
</div>
Expand Down
32 changes: 32 additions & 0 deletions entities/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,38 @@ export async function updateUser(
}
}

export async function increaseUserGrade(
client: PrismaClient,
newGrades: Array<{ id: number; grade: string }>
) {
try {
//create a transaction otherwise for single API calls, there's a connection pool issue
const transaction = [] as Array<any>;
newGrades.map((i: { id: number; grade: string }) => {
transaction.push(
client.user.update({
where: {
id: i.id,
},
data: { schoolGrade: i.grade },
})
);
});

const result = await client.$transaction(transaction);
console.log("Batch update database operation succeeded: ", result);
return result;
} catch (e) {
if (
e instanceof Prisma.PrismaClientKnownRequestError ||
e instanceof Prisma.PrismaClientValidationError
) {
console.log("ERROR in updating batch grades for user : ", e);
}
throw e;
}
}

export async function disableUser(client: PrismaClient, id: number) {
await addAudit(client, "Disable user", id.toString(), 0, id);
return await client.user.update({
Expand Down
31 changes: 31 additions & 0 deletions pages/api/batch/grade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { increaseUserGrade } from "@/entities/user";
import { PrismaClient } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";

const prisma = new PrismaClient();

export default async function handle(
req: NextApiRequest,
res: NextApiResponse
) {
switch (req.method) {
case "POST":
try {
if (!req.body) return res.status(404).end("No data provided");
//gets a list of user IDs to update the grade
const userdata = req.body;

const updateResult = await increaseUserGrade(prisma, userdata);

res.status(200).json(updateResult);
} catch (error) {
console.log(error);
res.status(400).json({ data: "ERROR DELETE: " + error });
}
break;

default:
res.status(405).end(`${req.method} Not Allowed`);
break;
}
}
Loading