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

feat: teacher onboarding #83

Merged
merged 14 commits into from
Jan 17, 2025
Merged
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
],
"dependencies": {
"@react-pdf/renderer": "^4.0.0",
"codeforlife": "2.6.3",
"codeforlife": "2.6.5",
"crypto-js": "^4.2.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/api/klass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export type {
RetrieveClassResult,
}

export type CreateClassResult = CreateResult<Class>
export type CreateClassResult = CreateResult<Class, "name">
export type CreateClassArg = CreateArg<
Class,
"name" | "read_classmates_data",
Expand Down
125 changes: 125 additions & 0 deletions src/components/PrintPasswordReminderCardsButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import * as pdf from "@react-pdf/renderer"
import { Button, type ButtonProps } from "@mui/material"
import { type FC, useRef } from "react"
import { type Student, type User } from "codeforlife/api"
import { Print as PrintIcon } from "@mui/icons-material"
import { generatePath } from "react-router-dom"

import CflLogoImage from "../images/logo_cfl.png"
import { makeAutoLoginLink } from "../utils/student"
import { paths } from "../routes"

interface PDFProps {
classId: string
students: Array<
Pick<Student, "id" | "auto_gen_password"> & {
user: Pick<User, "id" | "first_name" | "password">
}
>
}

const PDF: FC<PDFProps> = ({ classId, students }) => {
const classLoginLink = generatePath(paths.login.student.class._, { classId })

const styles = pdf.StyleSheet.create({
mainView: {
border: "2px solid black",
display: "flex",
flexDirection: "row",
gap: 5,
padding: 10,
},
page: {
padding: 20,
},
text: {
textAlign: "justify",
marginBottom: 5,
fontSize: 12,
},
image: {
width: 85,
height: 70,
},
})

return (
<pdf.Document>
<pdf.Page size="A4" style={styles.page}>
<pdf.Text style={styles.text}>
Please ensure students keep login details in a secure place
</pdf.Text>
{students.map(student => (
<pdf.View key={`${student.user.id}-pdf`} style={styles.mainView}>
<pdf.Image
source={CflLogoImage}
src={CflLogoImage}
style={styles.image}
/>
<pdf.View>
{/*TODO: Improve overall styles for this.*/}
<pdf.Text style={styles.text}>
Directly log in with:{"\n"}
{makeAutoLoginLink(classLoginLink, student)}
</pdf.Text>
<pdf.Text style={styles.text}>
OR class link: {classLoginLink}
</pdf.Text>
<pdf.Text style={styles.text}>
Name: {student.user.first_name} Password:{" "}
{student.user.password}
</pdf.Text>
</pdf.View>
</pdf.View>
))}
</pdf.Page>
</pdf.Document>
)
}

export type PrintPasswordReminderCardsButtonProps = PDFProps &
Omit<ButtonProps, "endIcon" | "onClick" | "className">

const PrintPasswordReminderCardsButton: FC<
PrintPasswordReminderCardsButtonProps
> = ({ classId, students, ...buttonProps }) => {
const linkRef = useRef<HTMLAnchorElement | null>(null)

const downloadPdf = async (): Promise<void> => {
try {
const blob = await pdf
.pdf(<PDF classId={classId} students={students} />)
.toBlob()

const url = URL.createObjectURL(blob)

if (linkRef.current) {
linkRef.current.href = url
linkRef.current.click()
}

URL.revokeObjectURL(url)
} catch (error) {
console.error(error)
}
}

return (
<>
<Button
endIcon={<PrintIcon />}
onClick={() => {
void downloadPdf()
}}
className="body"
{...buttonProps}
>
Print reminder cards
</Button>
{/* Invisible anchor tag to trigger the download */}
<a ref={linkRef} target="_blank" style={{ display: "none" }}></a>
</>
)
}

export default PrintPasswordReminderCardsButton
32 changes: 32 additions & 0 deletions src/components/PrintStudentCredentialsNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Stack, Typography } from "@mui/material"
import { type FC } from "react"
import { Section } from "codeforlife/components/page"
import { WarningAmber as WarningAmberIcon } from "@mui/icons-material"

import PrintPasswordReminderCardsButton, {
type PrintPasswordReminderCardsButtonProps,
} from "./PrintPasswordReminderCardsButton"

export interface PrintStudentCredentialsNotificationProps
extends PrintPasswordReminderCardsButtonProps {}

const PrintStudentCredentialsNotification: FC<
PrintStudentCredentialsNotificationProps
> = props => (
<Section boxProps={{ bgcolor: "#ffd23b" }} sx={{ paddingY: "5px" }}>
<Stack direction="row" alignItems="center" gap={2}>
<WarningAmberIcon htmlColor="#000" />
<Typography variant="body2" color="#000" mb={0}>
This is the only time you will be able to view this page. You can print
reminder cards or download as a CSV file.
</Typography>
<PrintPasswordReminderCardsButton
{...props}
style={{ marginLeft: "auto" }}
variant="outlined"
/>
</Stack>
</Section>
)

export default PrintStudentCredentialsNotification
Loading