Skip to content

Commit

Permalink
Merge pull request #70 from stevebrownlee/develop
Browse files Browse the repository at this point in the history
Phase 1 of making Github Classroom obsolete
  • Loading branch information
stevebrownlee authored Jul 7, 2024
2 parents e2e181b + e66fd5e commit b7915ff
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 200 deletions.
2 changes: 1 addition & 1 deletion src/components/assessments/AssessmentProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const AssessmentProvider = (props) => {
return fetch(`${Settings.apiHost}/assessments?studentId=${studentId}`)
.then(response => response.json())
.then(data => {
data.sort((c, n) => c.status - n.status ? 1 : -1)

setAssessments(data)
})
}, [setAssessments])
Expand Down
62 changes: 56 additions & 6 deletions src/components/cohorts/StudentCardList.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import keyboardShortcut from "../ui/keyboardShortcut.js"

import "./CohortStudentList.css"
import "./Tooltip.css"
import { StudentAssessmentForm } from "../people/StudentAssessmentForm.js"

const persistSettings = (setting, value) => {
let settings = localStorage.getItem("lp_settings")
Expand Down Expand Up @@ -65,6 +66,7 @@ export const StudentCardList = ({ searchTerms }) => {
const [showTags, toggleTags] = useState(initial_show_tags_state)
const [groupedStudents, setGroupedStudents] = useState([])
const [showAvatars, toggleAvatars] = useState(initial_show_avatars_state)
const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false)

const history = useHistory()

Expand All @@ -86,6 +88,22 @@ export const StudentCardList = ({ searchTerms }) => {
enteringNoteStateRef.current = enteringNote
})

/*
This function, and the useEffect below, were added to prevent the
Radix Dialog element from blocking pointer events on the body
*/
const removePointerEventsStyle = () => {
document.body.style.pointerEvents = ''
}

useEffect(() => {
return () => {
if (!feedbackDialogOpen) {
removePointerEventsStyle()
}
}
}, [feedbackDialogOpen])

const toggleTagsShortcut = keyboardShortcut('t', 'g', () => {
if (!noteOpenStateRef.current && !enteringNoteStateRef.current) {
persistSettings('tags', !tagsStateRef.current)
Expand Down Expand Up @@ -124,7 +142,7 @@ export const StudentCardList = ({ searchTerms }) => {
}
else {
history.push("/cohorts")
new Toast("You have not joined a cohort. Please choose one.", Toast.TYPE_WARNING, Toast.TIME_NORMAL);
new Toast("You have not joined a cohort. Please choose one.", Toast.TYPE_WARNING, Toast.TIME_NORMAL)
}

document.addEventListener("keyup", toggleTagsShortcut)
Expand All @@ -135,6 +153,28 @@ export const StudentCardList = ({ searchTerms }) => {
}
}, [])

/*
The purpose of this useEffect is to restructure the student data into a format that
can be easily rendered in the component. This is done by grouping students by book and project.
Assessments are treated as projects with an index of 99.
The data structure is as follows:
[
{
name: "Book 1",
studentCount: 0,
display: false,
projects: [
{
name: "Project 1",
students: [],
display: false,
droppable: false
}
]
}
]
*/
useEffect(() => {
/* eslint-disable no-undef */
let copy = structuredClone(cohortStudents)
Expand All @@ -152,7 +192,7 @@ export const StudentCardList = ({ searchTerms }) => {
book.projects = [...book.projects, ...maxedAssessments]
}

copy = copy.map(s => ({...s, inGroupProject: false}))
copy = copy.map(s => ({ ...s, inGroupProject: false }))

for (const project of book.projects) {
project.display = false
Expand Down Expand Up @@ -233,7 +273,7 @@ export const StudentCardList = ({ searchTerms }) => {
if (project.index === 99 && student.assessment_status === 0) {
student.book_id = student.bookId // Snake case needed for the API
setStudentCurrentAssessment(student).then(() => getCohortStudents(activeCohort))
new Toast("Student assigned to assessment", Toast.TYPE_ERROR, Toast.TIME_NORMAL);
new Toast("Student assigned to assessment", Toast.TYPE_ERROR, Toast.TIME_NORMAL)
return null
}
setStudentCurrentProject(student.id, project.id)
Expand Down Expand Up @@ -263,7 +303,7 @@ export const StudentCardList = ({ searchTerms }) => {
book.id !== rawStudent.bookId &&
rawStudent.assessment_status !== 4
) {
new Toast("Self-assessment for this book not marked as reviewed and complete.", Toast.TYPE_WARNING, Toast.TIME_NORMAL);
new Toast("Self-assessment for this book not marked as reviewed and complete.", Toast.TYPE_WARNING, Toast.TIME_NORMAL)
}
else {
// Assign to assessment
Expand All @@ -272,7 +312,7 @@ export const StudentCardList = ({ searchTerms }) => {
setStudentCurrentAssessment(rawStudent)
}
else {
new Toast("Student already assigned to assessment.", Toast.TYPE_ERROR, Toast.TIME_NORMAL);
new Toast("Student already assigned to assessment.", Toast.TYPE_ERROR, Toast.TIME_NORMAL)
}
}
// Assign to core project
Expand All @@ -289,6 +329,7 @@ export const StudentCardList = ({ searchTerms }) => {
toggleStatuses={toggleStatuses}
toggleTags={toggleTagDialog}
toggleNote={toggleNote}
setFeedbackDialogOpen={setFeedbackDialogOpen}
assignStudentToProject={assignStudentToProject}
hasAssessment={book.assessments.length > 0}
toggleCohorts={toggleCohorts}
Expand All @@ -304,7 +345,7 @@ export const StudentCardList = ({ searchTerms }) => {
? <article key={`book--${book.id}--${showTags}--${showAvatars}`} className="bookColumn">
<header className="bookColumn__header">
<div className="bookColumn__name">
<div className="bookColumn__studentCount">&nbsp;</div>
<div className="bookColumn__studentCount">&nbsp</div>
<div> {book.name} </div>
<div className="bookColumn__studentCount">
<PeopleIcon />
Expand Down Expand Up @@ -351,10 +392,19 @@ export const StudentCardList = ({ searchTerms }) => {
})
}

{/*
All student-specific dialogs are rendered here. Each one should pull `activeStudent` from
the PeopleContext to access the student that was clicked on. This prevents multiple instances
of the dialog from being rendered at the same time.
Make sure that you invoke `activateStudent(student)` where needed before you display the
dialog. This will set the `activeStudent` in the PeopleContext to the student that was clicked on.
*/}
<StudentDetails toggleCohorts={toggleCohorts} />
<TagDialog toggleTags={toggleTagDialog} tagIsOpen={tagIsOpen} />
<StudentNoteDialog toggleNote={toggleNote} noteIsOpen={noteIsOpen} />
<CohortDialog toggleCohorts={toggleCohorts} cohortIsOpen={cohortIsOpen} />
<StudentAssessmentForm dialogOpen={feedbackDialogOpen} setDialogOpen={setFeedbackDialogOpen} />
</section>
}

Expand Down
10 changes: 6 additions & 4 deletions src/components/dashboard/StudentNoteDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ export const StudentNoteDialog = ({ toggleNote, noteIsOpen }) => {
onKeyDown={
e => {
if (e.key === "Enter") {
createStudentNote().then(getNotes).then(() => {
setMessage("")
})
createStudentNote().then(getNotes).then(() => setMessage(""))
} else if (e.key === "Escape") {
toggleNote()
setNotes([])
}
}
}
Expand All @@ -68,7 +67,10 @@ export const StudentNoteDialog = ({ toggleNote, noteIsOpen }) => {
fontSize: "0.75rem"
}}
id="closeBtn"
onClick={() => toggleNote()}>[ close ]</button>
onClick={() => {
toggleNote()
setNotes([])
}}>[ close ]</button>

<StudentNoteList notes={notes} deleteStudentNote={deleteStudentNote} />
</dialog>
Expand Down
59 changes: 39 additions & 20 deletions src/components/dashboard/student/StudentInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Settings from "../../Settings.js"
import { fetchIt } from "../../utils/Fetch.js"

export const StudentInfo = ({ profile }) => {

const history = useHistory()
const [githubUrl, setGithubUrl] = useState('')
const [vocab, setVocab] = useState(false)
Expand All @@ -31,23 +32,51 @@ export const StudentInfo = ({ profile }) => {
}
}, [])

useEffect(() => {
if (profile && profile.assessment_overview && profile.assessment_overview.length > 0) {
const latestAssessment = profile.assessment_overview[0]
setGithubUrl(latestAssessment.github_url)
}
}, [profile])

const createAssessmentRepo = () => {
fetchIt(`${Settings.apiHost}/students/${profile.id}/assess`, {
method: "POST",
body: JSON.stringify({ bookId: profile?.project?.book_id }),
autoHandleResponse: false
})
.then(res => {
if (res.status === 409) {
fetchIt(res.headers.get("Location")).then(sa => {
new Toast(
`You have already started the ${sa.assessment.name} self-assessment. Its status is ${sa.status}.`,
Toast.TYPE_WARNING, Toast.TIME_NORMAL
)
})
}
else {
new Toast(
"Your self-assessment project has been created. You will receive a notification in Slack with the link to the project.",
Toast.TYPE_DONE, Toast.TIME_NORMAL
)
}
})
}

const validateSubmission = () => {
if (pushed && vocab) {
fetchIt(`${Settings.apiHost}/notify`, {
method: "POST",
body: JSON.stringify({
message: `:speaking_head_in_silhouette: ${profile.name ?? "Testing"} has completed the ${profile?.project?.book_name} self-assessment.\n\nRepository: ${githubUrl}`
})
fetchIt(`${Settings.apiHost}/students/${profile.id}/assess`, {
method: "PUT",
body: JSON.stringify({ statusId: 2 })
})
.then(() => {
setPushed(false)
setVocab(false)
setGithubUrl('')
setValidationMessage('')

setDialogOpen(false)

new Toast( toasterElement.current, Toast.TYPE_DONE, Toast.TIME_LONG );
new Toast(toasterElement.current, Toast.TYPE_DONE, Toast.TIME_LONG);
})
}
else {
Expand Down Expand Up @@ -99,17 +128,7 @@ export const StudentInfo = ({ profile }) => {
You are done with the core projects in a book and need the link from an instructor to start your self-assessment project.
</p>
<section>
<Button color="iris"
onClick={
() => fetchIt(`${Settings.apiHost}/notify`, {
method: "POST",
body: JSON.stringify({
message: `:orange_book: ${profile.name} is ready for the ${profile?.project?.book_name} self-assessment`
})
}).then(() => window.alert("Your instructors have been notified"))
}>
Ready for Self-Assessment
</Button>
<Button color="iris" onClick={createAssessmentRepo}> Ready for Self-Assessment </Button>
</section>
<p>
When you have completed the project code, completed the Vocabulary &amp; Understanding questions, and pushed your repository to Github, click the button below to notify your coaches.
Expand All @@ -123,15 +142,15 @@ export const StudentInfo = ({ profile }) => {
<Dialog.Content>
<Dialog.Title>Complete Self-Assessment</Dialog.Title>
<Dialog.Description size="2" mb="4">
Share the URL of your Github repository with your coaches for review.
Verify that you have completed the project code, the Vocabulary &amp; Understanding questions, and pushed your repository to Github.
</Dialog.Description>

<Flex direction="column" gap="3">
<label>
<Text as="div" size="2" mb="1" weight="bold">
Github Repository URL
</Text>
<TextArea onChange={(e) => setGithubUrl(e.target.value)}
<TextArea readOnly={true} value={githubUrl} onChange={(e) => setGithubUrl(e.target.value)}
placeholder="https://github.com/your-username/repository"
/>
</label>
Expand Down
31 changes: 28 additions & 3 deletions src/components/people/Student.css
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,14 @@ button {
}

.section--notes {
padding: 0 2rem;
max-height: 20rem;
height: fit-content;
min-height: 15rem;
overflow: scroll;
border: 1px dotted lightsteelblue;
}

.dialog--note {
min-height: 20rem;
height: fit-content;
}

Expand All @@ -347,8 +348,22 @@ button {
display: flex;
}

.note__text {
font-size: 0.9rem;
flex: 20;
text-align: left;
}

.note__date {
font-size: small;
font-size: 0.7rem;
flex: 4;
text-align: center;
}

.note__author {
font-size: 0.7rem;
flex: 4;
text-align: center;
}

.note:nth-child(even) {
Expand All @@ -363,3 +378,13 @@ button {
background-color: rgb(187, 187, 249);
}

.student__assessmenticon {
position: absolute;
bottom: 0;
right: 0;
text-decoration: none;
}

.student__assessmenticon:hover {
cursor: pointer;
}
Loading

0 comments on commit b7915ff

Please sign in to comment.