Add more details in group view, add editing group students
This commit is contained in:
parent
4f42e69a5d
commit
213a16743d
@ -5,7 +5,7 @@ from flask_sqlalchemy import get_debug_queries
|
||||
from ...students.models import Group, Student, YearGroup, ProjectGradeSheet
|
||||
from ...project_supervisor.models import ProjectSupervisor, YearGroupProjectSupervisors
|
||||
from ..schemas import GroupSchema, GroupEditSchema, GroupsPaginationSchema, \
|
||||
GroupCreateSchema, MessageSchema, GroupQuerySchema
|
||||
GroupCreateSchema, MessageSchema, GroupQuerySchema, DetailGroupSchema
|
||||
from ...dependencies import db
|
||||
from ...base.utils import paginate_models
|
||||
from ..utils import load_weight_for_project_grade_sheet, calculate_points_for_one_term
|
||||
@ -101,7 +101,7 @@ def create_group(year_group_id: int, data: dict) -> dict:
|
||||
|
||||
|
||||
@bp.get("/<int:id>/detail/")
|
||||
@bp.output(GroupSchema)
|
||||
@bp.output(DetailGroupSchema)
|
||||
def detail_group(id: int) -> Group:
|
||||
group = Group.query.filter_by(id=id).first()
|
||||
if group is None:
|
||||
@ -135,6 +135,10 @@ def edit_group(id: int, data: dict) -> dict:
|
||||
if group is None:
|
||||
abort(400, f"Group with id {id} doesn't exist!")
|
||||
|
||||
group_query.update(data)
|
||||
students = db.session.query(Student).filter(Student.index.in_(data['students'])).all()
|
||||
group.students = students
|
||||
group.name = data['name']
|
||||
group.project_supervisor_id = data['project_supervisor_id']
|
||||
|
||||
db.session.commit()
|
||||
return {"message": "Group was updated!"}
|
||||
|
@ -7,5 +7,5 @@ from .project_supervisor import ProjectSupervisorQuerySchema, ProjectSupervisors
|
||||
ProjectSupervisorCreateSchema, ProjectSupervisorEditSchema, ProjectSupervisorYearGroupSchema
|
||||
from .students import ProjectSupervisorSchema, GroupSchema, StudentSchema, StudentsPaginationSchema, \
|
||||
StudentListFileDownloaderSchema, StudentCreateSchema, StudentEditSchema, MessageSchema, FileSchema, \
|
||||
StudentQuerySchema, YearGroupInfoQuery
|
||||
StudentQuerySchema, YearGroupInfoQuery, DetailGroupSchema
|
||||
from .year_group import YearGroupSchema, YearGroupPaginationSchema, YearGroupQuerySchema
|
||||
|
@ -23,8 +23,8 @@ class GroupCreateSchema(Schema):
|
||||
|
||||
class GroupEditSchema(Schema):
|
||||
name = fields.Str(validate=validate.Length(min=1, max=255))
|
||||
project_supervisor_id = fields.Integer(validate=validate_index)
|
||||
students = fields.List(fields.Nested(StudentSchema), validate=validate.Length(min=1, max=255))
|
||||
project_supervisor_id = fields.Integer()
|
||||
students = fields.List(fields.Integer(validate=validate_index))
|
||||
|
||||
|
||||
class GroupIdSchema(Schema):
|
||||
|
@ -68,3 +68,9 @@ class StudentQuerySchema(ma.Schema):
|
||||
|
||||
class YearGroupInfoQuery(Schema):
|
||||
id = fields.Integer(required=True)
|
||||
|
||||
class DetailGroupSchema(ma.SQLAlchemyAutoSchema):
|
||||
project_supervisor = fields.Nested(ProjectSupervisorSchema)
|
||||
students = fields.List(fields.Nested(StudentSchema), validate=validate.Length(min=1, max=255))
|
||||
class Meta:
|
||||
model = Group
|
||||
|
@ -1,5 +1,6 @@
|
||||
import axiosInstance from './axiosInstance'
|
||||
import { Leader } from './leaders'
|
||||
import { Student } from './students'
|
||||
|
||||
export interface CreateGroup {
|
||||
name: string
|
||||
@ -16,6 +17,7 @@ export interface Group {
|
||||
project_supervisor: Leader
|
||||
prz_kod: string
|
||||
tzaj_kod: string
|
||||
students: Student[]
|
||||
}
|
||||
|
||||
export const getGroups = (
|
||||
@ -41,3 +43,6 @@ export const deleteGroup = (id: number) =>
|
||||
|
||||
export const getGroup = (id: number) =>
|
||||
axiosInstance.get<Group>(`coordinator/groups/${id}/detail/`)
|
||||
|
||||
export const editGroup = (id: number, payload: CreateGroup) =>
|
||||
axiosInstance.put(`coordinator/groups/${id}/`, payload)
|
||||
|
@ -13,7 +13,7 @@ export interface Student {
|
||||
index: number
|
||||
pesel: string
|
||||
mode?: boolean
|
||||
group?: any
|
||||
groups?: any[]
|
||||
}
|
||||
|
||||
export const getStudents = (
|
||||
|
@ -33,7 +33,7 @@ const AddGroup = () => {
|
||||
onSuccess: (data) => {
|
||||
setStudentOptions(
|
||||
data?.data.students
|
||||
// .filter((st) => st.group === null)
|
||||
.filter(({ groups }) => !groups?.length)
|
||||
.map(({ first_name, last_name, index }) => {
|
||||
return {
|
||||
value: index,
|
||||
|
@ -1,14 +1,69 @@
|
||||
import { useQuery } from 'react-query'
|
||||
import { useState } from 'react'
|
||||
import { useMutation, useQuery } from 'react-query'
|
||||
import { Link, useLocation, useParams } from 'react-router-dom'
|
||||
import { getGroup } from '../../api/groups'
|
||||
import ReactSelect from 'react-select'
|
||||
import useLocalStorageState from 'use-local-storage-state'
|
||||
import { CreateGroup, editGroup, getGroup } from '../../api/groups'
|
||||
import { getStudents } from '../../api/students'
|
||||
import { ReactComponent as IconRemove } from '../../assets/svg/icon-remove.svg'
|
||||
|
||||
type SelectValue = {
|
||||
value: string | number
|
||||
label: string
|
||||
}
|
||||
|
||||
const Group = () => {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const location = useLocation()
|
||||
|
||||
const { data: groups } = useQuery(['getGroup'], () => getGroup(Number(id)))
|
||||
const { name, project_supervisor } = groups?.data || {}
|
||||
const {
|
||||
data: groups,
|
||||
refetch,
|
||||
isLoading: isGroupLoading,
|
||||
} = useQuery(['getGroup', id], () => getGroup(Number(id)))
|
||||
const {
|
||||
name,
|
||||
project_supervisor,
|
||||
students,
|
||||
points_for_first_term,
|
||||
points_for_second_term,
|
||||
} = groups?.data || {}
|
||||
const [yearGroupId] = useLocalStorageState('yearGroupId')
|
||||
|
||||
const [studentOptions, setStudentOptions] = useState<SelectValue[]>([])
|
||||
const { isLoading: areStudentsLoading } = useQuery(
|
||||
'students',
|
||||
() => getStudents({ year_group_id: Number(yearGroupId), per_page: 1000 }),
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
setStudentOptions(
|
||||
data?.data.students
|
||||
.filter(({ groups }) => !groups?.length)
|
||||
.map(({ first_name, last_name, index }) => {
|
||||
return {
|
||||
value: index,
|
||||
label: `${first_name} ${last_name} (${index})`,
|
||||
}
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
)
|
||||
const [newStudent, setNewStudent] = useState(0)
|
||||
const { mutate: mutateEditGroup } = useMutation(
|
||||
['editGroup'],
|
||||
(data: { id: number; payload: CreateGroup }) =>
|
||||
editGroup(data.id, data.payload),
|
||||
{
|
||||
onSuccess: () => {
|
||||
refetch()
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if (isGroupLoading || areStudentsLoading) {
|
||||
return <div>Ładowanie</div>
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h1 className="font-bold text-xl">{name}</h1>
|
||||
@ -16,6 +71,9 @@ const Group = () => {
|
||||
Opiekun: {project_supervisor?.first_name}{' '}
|
||||
{project_supervisor?.last_name}
|
||||
</h2>
|
||||
<p>Punkty % za semestr 1: {points_for_first_term}</p>
|
||||
<p>Punkty % za semestr 2: {points_for_second_term}</p>
|
||||
|
||||
<Link
|
||||
to={`/${
|
||||
location.pathname.includes('coordinator')
|
||||
@ -25,6 +83,84 @@ const Group = () => {
|
||||
>
|
||||
<button className="btn btn-success mt-2">KARTA OCENY</button>
|
||||
</Link>
|
||||
|
||||
<div className="flex mx-auto mt-5 overflow-hidden overflow-x-auto border border-gray-100 rounded">
|
||||
<table className="min-w-full table table-compact">
|
||||
<thead>
|
||||
<tr className="bg-gray-50">
|
||||
<th>Imię</th>
|
||||
<th>Nazwisko</th>
|
||||
<th>Indeks</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100">
|
||||
{students?.map(({ first_name, last_name, index }) => (
|
||||
<tr key={index}>
|
||||
<td>{first_name}</td>
|
||||
<td>{last_name}</td>
|
||||
<td>{index}</td>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (students && name && project_supervisor)
|
||||
mutateEditGroup({
|
||||
id: Number(id),
|
||||
payload: {
|
||||
name,
|
||||
project_supervisor_id: project_supervisor.id,
|
||||
students: students
|
||||
.map((student) => student.index)
|
||||
.filter((i) => index !== i),
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<IconRemove />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<ReactSelect
|
||||
closeMenuOnSelect={true}
|
||||
options={studentOptions}
|
||||
placeholder="Wybierz studenta"
|
||||
styles={{
|
||||
control: (styles) => ({
|
||||
...styles,
|
||||
width: '270px',
|
||||
padding: '0.3rem',
|
||||
borderRadius: '0.5rem',
|
||||
}),
|
||||
}}
|
||||
onChange={(val) => {
|
||||
if (val?.value) setNewStudent(Number(val.value))
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
className="btn btn-success"
|
||||
onClick={() => {
|
||||
if (students && name && project_supervisor)
|
||||
mutateEditGroup({
|
||||
id: Number(id),
|
||||
payload: {
|
||||
name,
|
||||
project_supervisor_id: project_supervisor.id,
|
||||
students: [
|
||||
...students.map((student) => student.index),
|
||||
newStudent,
|
||||
],
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Dodaj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -177,13 +177,13 @@ const Students = () => {
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100">
|
||||
{students?.data?.students
|
||||
?.filter((st) => st.group === null || !showGroupless)
|
||||
.map(({ first_name, last_name, index, group, mode }) => (
|
||||
?.filter((st) => !st.groups?.length || !showGroupless)
|
||||
.map(({ first_name, last_name, index, groups, mode }) => (
|
||||
<tr key={index}>
|
||||
<td>{first_name}</td>
|
||||
<td>{last_name}</td>
|
||||
<td>{index}</td>
|
||||
<td>{group === null ? 'Nie' : 'Tak'}</td>
|
||||
<td>{groups?.length ? 'Tak' : 'Nie'}</td>
|
||||
<td>
|
||||
<button onClick={() => mutateDelete(index)}>
|
||||
<IconRemove />
|
||||
|
Loading…
Reference in New Issue
Block a user