Skip to content

Commit

Permalink
Merge pull request #448 from cornell-dti/andrew/admin-fixes
Browse files Browse the repository at this point in the history
Admin feature fixes, new admin privilege manager
  • Loading branch information
qiandrewj committed May 7, 2024
2 parents 9fae7cc + ae07087 commit d03e687
Show file tree
Hide file tree
Showing 22 changed files with 1,059 additions and 671 deletions.
444 changes: 203 additions & 241 deletions client/src/modules/Admin/Components/Admin.tsx

Large diffs are not rendered by default.

214 changes: 80 additions & 134 deletions client/src/modules/Admin/Components/AdminReview.tsx
Original file line number Diff line number Diff line change
@@ -1,157 +1,103 @@
import React, { Component } from 'react'
import React, { useEffect, useState } from 'react'

Check warning on line 1 in client/src/modules/Admin/Components/AdminReview.tsx

View workflow job for this annotation

GitHub Actions / build

'useEffect' is defined but never used
import axios from 'axios'
import styles from '../Styles/AdminReview.module.css'

type Props = {
info: any
approveHandler: (arg1: any) => any
removeHandler: (arg1: any, arg2: any) => any

//was origially optional
unReportHandler: (arg1: any) => any
review: any
approveHandler: (arg1: any) => any
removeHandler: (arg1: any, arg2: any) => any
unReportHandler: (arg1: any) => any
}

type State = {
shortName: string
longName: string
}
/*
Update Review Component.
Simple styling component that renders a single review (an li element)
to show on the Admin interface. Admin-visible reviews will be of 2 types:
- Unapproved: new reviews needing approval
- Reported: reviews that have been reprot and need review.
Unapproved Reivews will contain:
- Name of the course the review belongs to
- how long ago the review was added
- all review content
- button to approve the review
- button to delete the review
Reported Reviews will contain:
- Name of the course the review belongs to
- how long ago the review was added
- all review content
- button to un-report (restore) the review
- button to delete the review
- Reported: reviews that have been reported and require admin undo
*/

export default class UpdateReview extends Component<Props, State> {
constructor(props: Props) {
super(props)

// state of app will contain details about the class this reivew is for
this.state = {
shortName: '',
longName: '',
}
const UpdateReview = ({review, approveHandler, removeHandler, unReportHandler}: Props) => {
const [shortName, setShortName] = useState<string>("")
const [fullName, setFullName] = useState<string>("")

axios
.post(`/api/getCourseById`, {
courseId: props.info.class,
})
.then((response) => {
const course = response.data.result
if (course) {
this.setState({
shortName: course.classSub.toUpperCase() + ' ' + course.classNum,
longName: course.classTitle,
})
} else {
// eslint-disable-next-line no-console
console.log(`Unable to find course by id = ${props.info.class}`)
}
})
}

// Decide which buttons to show, and what action the buttons take,
// based on the type of update (report or approval)
renderButtons(review: any) {
const reported = review.reported
if (reported === 1) {
return (
<div className="">
<button
type="button"
className=""
onClick={() => this.props.unReportHandler(review)}
>
{' '}
Restore Review
</button>
<button
type="button"
className=""
onClick={() => this.props.removeHandler(review, false)}
>
{' '}
Remove Review
</button>
</div>
)
} else {
return (
<div className="">
<button
type="button"
className=""
onClick={() => this.props.approveHandler(review)}
>
{' '}
Confirm Review
</button>
<button
type="button"
className=""
onClick={() => this.props.removeHandler(review, true)}
>
{' '}
Remove Review
</button>
</div>
)
}
}
.post(`/api/getCourseById`, {
courseId: review.class,
})
.then((response) => {
const course = response.data.result
if (course) {
setShortName(course.classSub.toUpperCase() + ' ' + course.classNum)
setFullName(course.classTitle)
}
})

render() {
const review = this.props.info
return (
<li id={review._id}>
<div className="">
<div className="">
<b>Course:</b> {this.state.shortName}: {this.state.longName}
<br></br>
{/* <b>Posted </b> {moment(review.date).fromNow()} */}
</div>
</div>
<div className="">
<div className="">
function renderButtons(review: any) {
const reported = review.reported
if (reported === 1) {
return (
<div className="">
<div className="">
<div className="">
<div className="">{review.rating}</div>
</div>
<div className="">
<div className="">{review.difficulty}</div>
</div>
<div className="">
<div className="">{review.professors}</div>
</div>
</div>
<div className="">
<div className=""> Overall Rating</div>
<div className=""> Difficulty</div>
<div className=""> Professor(s)</div>
</div>
<div className="">{review.text}</div>
<button
type="button"
className={styles.approvebutton}
onClick={() => unReportHandler(review)}
>
{' '}
Restore Review
</button>
<button
type="button"
className={styles.removebutton}
onClick={() => removeHandler(review, false)}
>
{' '}
Remove Review
</button>
</div>
)
} else {
return (
<div className="">
<div className="">{this.renderButtons(review)}</div>
<button
type="button"
className={styles.approvebutton}
onClick={() => approveHandler(review)}
>
{' '}
Confirm Review
</button>
<button
type="button"
className={styles.removebutton}
onClick={() => removeHandler(review, true)}
>
{' '}
Remove Review
</button>
</div>
</div>
)
}
}
return (
<div id = {review._id} className = {styles.pendingreview}>
<div className = {styles.titleinfo}>
<h4 className = "">
Course: {shortName}, {fullName}
</h4>
<p>{review.date}</p>
</div>
<div className = {styles.reviewinfo}>
<p>Professor(s): {review.professors}</p>
<p>Overall Rating: {review.rating}</p>
<p>Difficulty: {review.difficulty}</p>
<p>Workload: {review.workload}</p>
<br></br>
<p>{review.text}</p>
</div>
</li>
<div className="">{renderButtons(review)}</div>
</div>
)
}
}
export default UpdateReview
26 changes: 26 additions & 0 deletions client/src/modules/Admin/Components/AdminUser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useEffect, useState } from 'react'

Check warning on line 1 in client/src/modules/Admin/Components/AdminUser.tsx

View workflow job for this annotation

GitHub Actions / build

'useEffect' is defined but never used

Check warning on line 1 in client/src/modules/Admin/Components/AdminUser.tsx

View workflow job for this annotation

GitHub Actions / build

'useState' is defined but never used
import axios from 'axios'

Check warning on line 2 in client/src/modules/Admin/Components/AdminUser.tsx

View workflow job for this annotation

GitHub Actions / build

'axios' is defined but never used
import styles from '../Styles/AdminUser.module.css'

type Props = {
user: any
token: string
removeHandler: (arg1: any) => any
}

const AdminUser = ({user, token, removeHandler}: Props) => {

return (
<div className={styles.userEntry}>
{user.firstName} {user.lastName}, {user.netId}
<button
className={styles.removeButton}
onClick={() => removeHandler(user)}
>
Remove
</button>
</div>
)
}

export default AdminUser
126 changes: 126 additions & 0 deletions client/src/modules/Admin/Components/ManageAdminModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { useEffect, useState } from 'react'
import axios from 'axios'
import styles from '../Styles/ManageAdminModal.module.css'

import { Student } from 'common'
import AdminUser from './AdminUser'
import closeIcon from '../../../assets/icons/X.svg'

type Props = {
open: boolean
setOpen: (open: boolean) => void
token: string
}

const ManageAdminModal = ({token, open, setOpen}: Props) => {
const [admins, setAdmins] = useState<Student[]>([])
const [netId, setNetId] = useState<string>("")

function closeModal() {
setOpen(false)
}

/**
* Endpoint to get all admins
*/
useEffect(() => {
axios
.post('/api/admin/users/get', {token: token})
.then((response) => {
const result = response.data.result
if (response.status === 200) {
setAdmins(result)
} else {
console.log('Error at getAdmins')
}
})
}, [token])

/**
* Removes an admin from the list, giving that user 'regular' privilege
* @param user assumes that this user already has admin privilege
*/

function removeAdmin(user: Student) {
axios
.post('/api/admin/users/remove', {
userId: user.netId,
token: token
})
.then((response) => {
if (response.status === 200) {
const updatedAdmins = admins.filter((admin: Student) => {
return admin && admin._id !== user.netId
})
setAdmins(updatedAdmins)
}
}).catch((e) => console.log(`Unable to remove admin ${e}`))
}

/**
* Calls endpoint to add or update a user with admin privilege
* @param _netId the user's net id
*/

function addAdminByNetId(_netId: string) {
axios
.post('/api/admin/users/add', {
userId: _netId,
token: token
})
.then((response) => {
if (response.status === 200) {
console.log(`Successfully gave admin privilege to ${_netId}`)
}
}).catch((e) => console.log(`Unable to remove admin ${e}`))
}

function onTextChange(newText: string) {
setNetId(newText)
}

if (!open) {
return <></>
}

return (
<div className={styles.modalbg}>
<div className={styles.modal}>
<h2>Administrators</h2>
<img
className={styles.closeButton}
onClick={closeModal}
src={closeIcon}
alt="close-modal"
/>
<div className={styles.addAdmin}>
<input
className={styles.textInputBox}
value={netId}
onChange={(e) => onTextChange(e.target.value)}
name="new-admin"
id="new-admin"
placeholder="User net-id"
></input>
<button
className = {styles.addAdminButton}
onClick={() => addAdminByNetId(netId)}
>Add administrator</button>
</div>
<br>
</br>
{admins.map((admin) => {
return (
<AdminUser
user={admin}
token={token}
removeHandler={removeAdmin}
/>
)
})}
</div>
</div>
)
}

export default ManageAdminModal
Loading

0 comments on commit d03e687

Please sign in to comment.