fix: fixed search in students table

This commit is contained in:
Sergey Yarkov 2021-12-16 20:52:14 +03:00
parent 1dff6be5e4
commit d133b69982
5 changed files with 64 additions and 55 deletions

View File

@ -16,7 +16,7 @@ import { IconType } from 'react-icons';
export interface IBaseModalProps extends ModalProps { export interface IBaseModalProps extends ModalProps {
isOpen: boolean; isOpen: boolean;
isDisabled: boolean; isDisabled?: boolean | undefined;
onClose: () => void; onClose: () => void;
onProceed?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void; onProceed?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
heading: string; heading: string;

View File

@ -34,6 +34,7 @@ const AddStudentsModal: React.FC<AddStudentsModalPropsType> = ({
const [selected, setSelected] = useState<Array<IUser>>([]); const [selected, setSelected] = useState<Array<IUser>>([]);
const handleSelectStudents = (students: Array<IUser>) => setSelected(students); const handleSelectStudents = (students: Array<IUser>) => setSelected(students);
const handleAttachStudents = async () => { const handleAttachStudents = async () => {
if (selected.length !== 0) { if (selected.length !== 0) {
try { try {

View File

@ -60,7 +60,13 @@ const AuthForm: React.FC = () => {
<InputLeftElement> <InputLeftElement>
<Icon as={MdAccountCircle} /> <Icon as={MdAccountCircle} />
</InputLeftElement> </InputLeftElement>
<Input type='text' placeholder='Login' {...register('login')} isInvalid={!!errors.login} /> <Input
type='text'
placeholder='Login'
{...register('login')}
autoComplete='login'
isInvalid={!!errors.login}
/>
</InputGroup> </InputGroup>
<Text as='small' color='red.500'> <Text as='small' color='red.500'>
{errors.login?.message} {errors.login?.message}
@ -72,6 +78,7 @@ const AuthForm: React.FC = () => {
placeholder='******' placeholder='******'
isInvalid={!!errors.password} isInvalid={!!errors.password}
errorMessage={errors.password?.message} errorMessage={errors.password?.message}
autoComplete='current-password'
/> />
<Button <Button

View File

@ -32,16 +32,26 @@ type StudentTableListPropsType = {
const StudentTableList: React.FC<StudentTableListPropsType> = props => { const StudentTableList: React.FC<StudentTableListPropsType> = props => {
const { render: Row, course } = props; const { render: Row, course } = props;
const [rows, setRows] = useState<ICourse['students']>(props.course.students); const [rows, setRows] = useState<ICourse['students']>(props.course.students);
const [selected, setSelected] = useState<ICourse['students']>([]); const [selected, setSelected] = useState<ICourse['students']>([]);
const [search, setSearch] = useState<string>(''); const [search, setSearch] = useState<string>('');
const [editing, setEditing] = useState<ICourse['students'][number] | null>(null); const [editing, setEditing] = useState<ICourse['students'][number] | null>(null);
const filteredRows = !search
? rows
: rows.filter(s => s.fullname.toLowerCase().includes(search) || s.email.toLowerCase().includes(search));
/**
* Add students modal
*/
const { const {
isOpen: isOpenAddStudentModal, isOpen: isOpenAddStudentModal,
onOpen: onOpenAddStudentModal, onOpen: onOpenAddStudentModal,
onClose: onCloseAddStudentModal, onClose: onCloseAddStudentModal,
} = useDisclosure(); } = useDisclosure();
/**
* Edit student modal
*/
const { const {
isOpen: isOpenEditStudentModal, isOpen: isOpenEditStudentModal,
onOpen: onOpenEditStudentModal, onOpen: onOpenEditStudentModal,
@ -50,39 +60,18 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
const { deleteUser } = useDeleteUser(); const { deleteUser } = useDeleteUser();
const { detachStudents } = useDetachStudents(); const { detachStudents } = useDetachStudents();
const isEmptyRows = rows.length === 0; const isEmptyRows = rows.length === 0 || filteredRows.length === 0;
const isEmptySelected = selected.length === 0; const isEmptySelected = selected.length === 0;
const isRowSelected = (student: ICourse['students'][number]) => !!selected.find(s => s.id === student.id); const isRowSelected = (student: ICourse['students'][number]) => {
return !!selected.find(selected => selected.id === student.id);
/**
* Search for a student by input
*/
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value);
setRows(
props.course.students.filter(
s => s.fullname.toLowerCase().includes(e.target.value) || s.email.toLowerCase().includes(e.target.value)
)
);
}; };
const onRemoved = (removedStudents: ICourse['students']) => { const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelected(prev => prev.filter(s => removedStudents.every(r => s.id !== r.id))); setSearch(event.target.value);
}; };
const onAdded = (users: IUser[]) => { const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
setRows(prev => [...prev, ...users]); if (event.target.checked) {
};
const onEdited = (user: IUser) => {
setRows(prev => prev.map(u => (u.id === user.id ? { ...user } : u)));
};
/**
* Select all students in a table
*/
const handleSelectAll = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
setSelected(rows); setSelected(rows);
return; return;
} }
@ -90,35 +79,30 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
setSelected([]); setSelected([]);
}; };
/** const handleSelect = useCallback(
* Select a student with checkbox (event: React.ChangeEvent<HTMLInputElement>, student: ICourse['students'][number]) => {
*/ if (event.target.checked) {
const handleSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>, student: ICourse['students'][number]) => { setSelected(prev => [...prev, student]);
if (e.target.checked) { return;
setSelected(prev => [...prev, student]); }
return;
}
setSelected(prev => prev.filter(s => s.id !== student.id)); setSelected(prev => prev.filter(selected => selected.id !== student.id));
}, []); },
[]
);
/**
* Delete student handler
*/
const handleDelete = (id: string) => { const handleDelete = (id: string) => {
return async () => { return async () => {
try { try {
const deleted = await deleteUser(id); const deletedStudent = await deleteUser(id);
setRows(prev => prev.filter(s => s.id !== deleted.id)); setRows(prev => prev.filter(student => student.id !== deletedStudent.id));
setSelected(prev => prev.filter(selected => selected.id !== id));
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
}; };
}; };
/**
* Edit student handler
*/
const handleEdit = (student: ICourse['students'][number]) => { const handleEdit = (student: ICourse['students'][number]) => {
return () => { return () => {
setEditing(student); setEditing(student);
@ -126,20 +110,32 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
}; };
}; };
/**
* Remove from course student handler
*/
const handleRemove = (id: string) => { const handleRemove = (id: string) => {
return async () => { return async () => {
try { try {
await detachStudents(course.id, [id]); await detachStudents(course.id, [id]);
setRows(prev => prev.filter(s => s.id !== id)); setRows(prev => prev.filter(student => student.id !== id));
setSelected(prev => prev.filter(selected => selected.id !== id));
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
}; };
}; };
const onRemoved = (removedStudents: ICourse['students']) => {
setSelected(prev =>
prev.filter(selected => removedStudents.every(removedStudent => selected.id !== removedStudent.id))
);
};
const onAdded = (users: IUser[]) => {
setRows(prev => [...prev, ...users]);
};
const onEdited = (editedUser: IUser) => {
setRows(prev => prev.map(student => (student.id === editedUser.id ? { ...editedUser } : student)));
};
return ( return (
<Box> <Box>
<AddStudentsModal <AddStudentsModal
@ -189,7 +185,7 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
<Table borderRadius='lg' mt='2'> <Table borderRadius='lg' mt='2'>
<StudentTableHead onSelectAll={handleSelectAll} /> <StudentTableHead onSelectAll={handleSelectAll} />
<Tbody> <Tbody>
{rows.map(student => ( {filteredRows.map(student => (
<Row <Row
key={student.id} key={student.id}
student={student} student={student}

View File

@ -1,7 +1,7 @@
import { AxiosInstance } from 'axios'; import { AxiosInstance } from 'axios';
import { ApiServiceInstance } from '.'; import { ApiServiceInstance } from '.';
import * as helpers from '@educt/helpers'; import * as helpers from '@educt/helpers';
import { IApiRespose, ICourse } from '@educt/interfaces'; import { IApiRespose, ICourse, IUser } from '@educt/interfaces';
import { CourseStatusEnum } from '@educt/enums'; import { CourseStatusEnum } from '@educt/enums';
import { CreateCourseParamsType, FetchCoursesParams, UpdateCourseParamsType } from '@educt/types'; import { CreateCourseParamsType, FetchCoursesParams, UpdateCourseParamsType } from '@educt/types';
@ -119,6 +119,11 @@ class CourseService {
); );
return result.data; return result.data;
} }
public async fetchStudentsById(courseId: string): Promise<IApiRespose<Array<IUser>>> {
const result = await this.api.get(`/v1/courses/${courseId}/students`);
return result.data;
}
} }
export const CourseServiceInstance = new CourseService(ApiServiceInstance.api); export const CourseServiceInstance = new CourseService(ApiServiceInstance.api);