Skip to content

Commit

Permalink
improved view components and create getUserStats logic b00tc4mp#182
Browse files Browse the repository at this point in the history
  • Loading branch information
AgusBirman committed Aug 25, 2024
1 parent a6f8cfb commit 30ab450
Show file tree
Hide file tree
Showing 19 changed files with 308 additions and 19 deletions.
2 changes: 1 addition & 1 deletion staff/agustin-birman/api/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
node_modules
.node_modules
33 changes: 33 additions & 0 deletions staff/agustin-birman/api/handlers/getUserStatsHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import jwt from '../util/jsonwebtoken-promised.js'

import logic from '../logic/index.js'

import { CredentialsError } from 'com/errors.js'

const { JWT_SECRET } = process.env

export default (req, res, next) => {
try {
const token = req.headers.authorization.slice(7)

jwt.verify(token, JWT_SECRET)
.then(payload => {
const { sub: userId } = payload

const { targetUserId } = req.params

try {
logic.getUserStats(userId, targetUserId)
.then(stats => res.json(stats))
.catch(error => next(error))
} catch (error) {
next(error)
}
})
.catch(error => {
next(new CredentialsError(error.message))
})
} catch (error) {
next(error)
}
}
2 changes: 2 additions & 0 deletions staff/agustin-birman/api/handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import getUserInfoHandler from './getUserInfoHandler.js'
import addStudentHandler from './addStudentHandler.js'
import removeStudentHandler from './removeStudentHandler.js'
import getStudentsHandler from './getStudentsHandler.js'
import getUserStatsHandler from './getUserStatsHandler.js'

import errorHandler from './errorHandler.js'
import createCompleteSentenceExerciseHandler from './createCompleteSentenceExerciseHandler.js'
Expand Down Expand Up @@ -36,6 +37,7 @@ export {
removeStudentHandler,
getStudentsHandler,
getTeachersHandler,
getUserStatsHandler,

errorHandler,

Expand Down
5 changes: 4 additions & 1 deletion staff/agustin-birman/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import {
getTeachersHandler,
checkCompleteActivityHandler,
getExercisesCountHandler,
getExerciseTypeHandler
getExerciseTypeHandler,
getUserStatsHandler
} from './handlers/index.js'
import removeTeacherHandler from './handlers/removeTeacherHandler.js'
import createOrderSentenceHandler from './handlers/createOrderSentenceHandler.js'
Expand Down Expand Up @@ -69,6 +70,8 @@ mongoose.connect(MONGODB_URL)

api.get('/users/:userId/teachers', getTeachersHandler)

api.get('/users/student/:targetUserId/stats', getUserStatsHandler)

api.post('/activity', jsonBodyParser, createActivityHandler)

api.get('/activity', getActivitiesHandler)
Expand Down
8 changes: 6 additions & 2 deletions staff/agustin-birman/api/logic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import addStudent from './user/addStudent.js'
import removeStudent from './user/removeStudent.js'
import getTeachers from './user/getTeachers.js'
import removeTeacher from './user/removeTeacher.js'
import getStudents from './user/getStudents.js'

import createActivity from './activity/createActivity.js'
import getActivities from './activity/getActivities.js'
import getActivity from './activity/getActivity.js'
import deleteActivity from './activity/deleteActivity.js'
import editActivity from './activity/editActivity.js'
import checkCompleteActivity from './activity/checkCompleteActivity.js'
import getTeachersActivities from './activity/getTeachersActivities.js'

import createCompleteSentenceExercise from './exercise/createCompleteSentenceExercise.js'
import getExercises from './exercise/getExercises.js'
Expand All @@ -21,11 +23,12 @@ import editExercise from './exercise/editExercise.js'
import getExercisesCount from './exercise/getExercisesCount.js'
import createOrderSentence from './exercise/createOrderSentence.js'
import getExerciseType from './exercise/getExerciseType.js'

import submitAnswer from './answer/submitAnswer.js'
import getAnswers from './answer/getAnswers.js'
import deleteAnswers from './answer/deleteAnswers.js'
import getStudents from './user/getStudents.js'
import getTeachersActivities from './activity/getTeachersActivities.js'
import getUserStats from './user/getUserStats.js'


const logic = {
registerUser,
Expand All @@ -36,6 +39,7 @@ const logic = {
removeStudent,
getStudents,
removeTeacher,
getUserStats,

createActivity,
getActivities,
Expand Down
63 changes: 63 additions & 0 deletions staff/agustin-birman/api/logic/user/getUserStats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import validate from 'com/validate.js'
import { Activity, Answer, Exercise, User } from '../../data/index.js'
import { SystemError, NotFoundError } from 'com/errors.js'

const getUserStats = (userId, targetUserId) => {
validate.id(userId, 'userId')
validate.id(targetUserId, 'targetUserId')

return User.findById(userId).lean()
.catch(error => { throw new SystemError(error.message) })
.then(user => {
if (!user) {
throw new NotFoundError('user not found')
}

return User.findById(targetUserId).lean()
.catch(error => { throw new SystemError(error.message) })
.then(user => {
if (!user)
throw new NotFoundError('targetUser not found')

return Answer.find({ student: targetUserId })
.catch(error => { throw new SystemError(error.message) })
.then(answers => {
const exerciseIds = answers.map(answer => answer.exercise)

return Exercise.find({ _id: { $in: exerciseIds } })
.catch(error => { throw new SystemError(error.message) })
.then(exercises => {

let countCorrectExercises = 0
exercises.forEach(exercise => {
answers.forEach(answer => {
if (answer.exercise.toString() === exercise._id.toString()) {
switch (exercise.type) {
case 'completeSentence':
if (answer.answer === exercise.answer) {
countCorrectExercises += 1
}
break
case 'orderSentence':
if (answer.answer === exercise.sentence) {
countCorrectExercises += 1
}
break
}
}
})
})
const activityIds = exercises.map(exercise => exercise.activity)
return Activity.countDocuments({ _id: { $in: activityIds } })
.catch(error => { throw new SystemError(error.message) })
.then(countActivities => {

return { countActivities, countExercises: exercises.length, countCorrectExercises }
})

})
})
})
})
}
export default getUserStats
16 changes: 16 additions & 0 deletions staff/agustin-birman/api/logic/user/getUserStats.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import getUserStats from './getUserStats.js'
import mongoose from 'mongoose'
import 'dotenv/config'

const { MONGODB_URL } = process.env

mongoose.connect(MONGODB_URL)
.then(() => {
try {
getUserStats('66a94dcb34505782bcd8cfd0', '66a94dcb34505782bcd8cfd0')
.then(userInfo => console.log(userInfo))
.catch(error => console.error(error))
} catch (error) {
console.error(error)
}
}).catch(error => { console.error(error) })
4 changes: 2 additions & 2 deletions staff/agustin-birman/app/src/logic/getUserName.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import extractPayloadFromJWT from '../utils/extractPayloadFromJWT'

const getUserName = () => {

const { sub: userId } = extractPayloadFromJWT(localStorage.token)
const { sub: userrId } = extractPayloadFromJWT(localStorage.token)

return fetch(`${import.meta.env.VITE_API_URL}/users/${userId}`, {
return fetch(`${import.meta.env.VITE_API_URL}/users/${userrId}`, {
headers: {
Authorization: `Bearer ${localStorage.token}`
}
Expand Down
33 changes: 33 additions & 0 deletions staff/agustin-birman/app/src/logic/getUserStats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import errors from 'com/errors'
import validate from 'com/validate'

const getUserStats = (targetUserId) => {
validate.id(targetUserId, 'targetUserId')

return fetch(`${import.meta.env.VITE_API_URL}/users/student/${targetUserId}/stats`, {
headers: {
Authorization: `Bearer ${localStorage.token}`
}
})
.catch(() => { throw new SystemError('server error') })
.then(response => {
if (response.status === 200) {
return response.json()
.catch(() => { throw new SystemError('server error') })
// .then(({ countActivities, countExercises, countCorrectExercises }) => ({ countActivities, countExercises, countCorrectExercises }))
.then(stats => stats)
}

return response.json()
.catch(() => { throw new SystemError('server error') })
.then(body => {
const { error, message } = body

const constructor = errors[error]

throw new constructor(message)
})
})
}

export default getUserStats
2 changes: 2 additions & 0 deletions staff/agustin-birman/app/src/logic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import getUserRole from './getUserRole'
import getTeachers from './getTeachers'
import removeTeacher from './removeTeacher'
import getUserId from './getUserId'
import getUserStats from './getUserStats'

import createActivity from './createActivity'
import getActivities from './getActivities'
Expand Down Expand Up @@ -47,6 +48,7 @@ const logic = {
getTeachers,
removeTeacher,
getUserId,
getUserStats,

createActivity,
getActivities,
Expand Down
2 changes: 2 additions & 0 deletions staff/agustin-birman/app/src/views/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ListTeachersActivities from './components/ListTeachersActivities'
import ListTeachers from './components/ListTeachers'
import OrderSentence from './components/OrderSentence'
import DoActivityOrderSentence from './components/DoActivityOrderSentence'
import ViewStudentStats from './components/ViewStudentStats'

function Home() {
const [name, setName] = useState('')
Expand Down Expand Up @@ -81,6 +82,7 @@ function Home() {
<Route path='/users/:userId' element={<ShareQR></ShareQR>} />
<Route path='/users/:userInfoId/add' element={<AddStudent></AddStudent>} />
<Route path='/users/students' element={<ListStudents></ListStudents>} />
<Route path='/users/student/:userId/info' element={<ViewStudentStats></ViewStudentStats>} />
<Route path='/users/teachers' element={<ListTeachers></ListTeachers>} />
</Routes>
</View>
Expand Down
17 changes: 17 additions & 0 deletions staff/agustin-birman/app/src/views/components/AddStudent/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@


.AddStudentText{
margin: 5%;
}

.AddStudentButton{
margin: 8%;
}

.AddStudentContainer{
margin-top: 30%;
width: 90%;
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3);
border-radius: 20px;
padding: 15px;
}
17 changes: 10 additions & 7 deletions staff/agustin-birman/app/src/views/components/AddStudent/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Text from '../../../components/core/Text'
import { useNavigate, useParams } from 'react-router-dom'
import { useEffect, useState } from 'react'
import Button from '../../../components/core/Button'
import './index.css'


function AddStudent() {
Expand Down Expand Up @@ -50,13 +51,15 @@ function AddStudent() {
}
}

return <View>
<Heading level='2'>Student's Information</Heading>
<Text>{userInfo.name}</Text>
<Text>{userInfo.surname}</Text>
<Button onClick={handleAddStudent}>Add Student</Button>

</View >
return <>
<View className='AddStudentContainer'>
<Heading level='2'>Student's Information</Heading>
<Text className='AddStudentText'>Name: {userInfo.name}</Text>
<Text className='AddStudentText'>Surname: {userInfo.surname}</Text>
<Text className='AddStudentText'>Username: {userInfo.username}</Text>
</View >
<Button className='AddStudentButton' onClick={handleAddStudent}>Add Student</Button>
</>
}

export default AddStudent
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
}

.StudentsList{
width: 95%;
height: auto;
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3);
border-radius: 10px;
margin: 10% 0;
width: 9%;
overflow-x: auto;
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { Link, useParams } from 'react-router-dom'
import { Link, useNavigate, useParams } from 'react-router-dom'
import './index.css'

import Button from '../../../components/core/Button'
Expand All @@ -12,6 +12,9 @@ import ConfirmDelete from '../../../components/library/ConfirmDelete'
function ListStudents() {
const [students, setStudents] = useState([])
const [confirmDeleteStudent, setConfirmDeleteStudent] = useState(false)
const navigate = useNavigate()


useEffect(() =>
loadStudents()
, [])
Expand Down Expand Up @@ -50,11 +53,13 @@ function ListStudents() {
}
}


const handleUserStats = (studentId) => navigate(`/users/student/${studentId}/info`)
const toggleDeleteStudent = () => setConfirmDeleteStudent(prevState => !prevState)
return <View>
<Heading level='2' className='StudentsListTitle'>Students List</Heading>
<View className='StudentsList'>
<table>
<View >
<table className='StudentsList'>
<thead>
<tr>
<th>Name</th>
Expand All @@ -70,6 +75,7 @@ function ListStudents() {
<td>{student.surname}</td>
<td><i
className="bi bi-info-circle"
onClick={() => handleUserStats(student.id)}
style={{ cursor: 'pointer', color: '#007bff' }}
></i></td>
<td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function MenuItem() {
<Button className='MenuItem'><Link to={`/activities/${userId}/list`}>View activities</Link></Button>
<Button className='MenuItem'><Link to='/users/teachers'>View teachers</Link></Button>
<Button className='MenuItem btn btn-primary'><Link to={`/users/${userId}`} >Share ID</Link></Button>
<Button className='MenuItem btn btn-primary'><Link to={`/users/student/${userId}/info`} >View Stats</Link></Button>
<i className="bi bi-info-circle">asd</i>
</>}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ function SelectActivity() {

return <View tag='main' className='SelectActivity'>
<Text>Select the activity</Text>
<Button type='button'><Link to='/activities/create/complete-sentence-exercise'>Complete the sentence</Link></Button>
<Button type='button'><Link to='/activities/create/order-sentence-exercise'>Order the sentence</Link></Button>
<Button type='button'><Link to='/activities/create/completeSentence'>Complete the sentence</Link></Button>
<Button type='button'><Link to='/activities/create/orderSentence'>Order the sentence</Link></Button>
<Button type='button'><Link to='/'>Back to home</Link></Button>
</View>
}
Expand Down
Loading

0 comments on commit 30ab450

Please sign in to comment.