diff --git a/rails/react-components/src/library/components/permission-forms/common/types.ts b/rails/react-components/src/library/components/permission-forms/common/types.ts index e811114d0..756abac22 100644 --- a/rails/react-components/src/library/components/permission-forms/common/types.ts +++ b/rails/react-components/src/library/components/permission-forms/common/types.ts @@ -1,6 +1,7 @@ export type CurrentSelectedProject = number | null; + export interface IPermissionForm { - id: string; + id: number; name: string; is_archived: boolean; can_delete: boolean; @@ -9,7 +10,7 @@ export interface IPermissionForm { } export interface IProject { - id: string; + id: number; name: string; } diff --git a/rails/react-components/src/library/components/permission-forms/index.scss b/rails/react-components/src/library/components/permission-forms/index.scss index 994c5c6e4..c0702da25 100644 --- a/rails/react-components/src/library/components/permission-forms/index.scss +++ b/rails/react-components/src/library/components/permission-forms/index.scss @@ -1,7 +1,7 @@ @import "../../../shared/styles/variables/variables"; .permissionForms { - margin-bottom: 200px; // for dropdown menu + margin-bottom: 180px; // for dropdown menu .tabArea { .tabs { diff --git a/rails/react-components/src/library/components/permission-forms/manage-forms-tab/create-edit-permission-form.scss b/rails/react-components/src/library/components/permission-forms/manage-forms-tab/create-edit-permission-form.scss index 7576cc05d..360c83fc1 100644 --- a/rails/react-components/src/library/components/permission-forms/manage-forms-tab/create-edit-permission-form.scss +++ b/rails/react-components/src/library/components/permission-forms/manage-forms-tab/create-edit-permission-form.scss @@ -4,13 +4,14 @@ width: 600px; .formTop { - display: flex; - align-content: center; background-color: $col-link; color: white; font-weight: bold; padding: 10px; margin-bottom:10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .formRow { diff --git a/rails/react-components/src/library/components/permission-forms/manage-forms-tab/manage-forms-tab.tsx b/rails/react-components/src/library/components/permission-forms/manage-forms-tab/manage-forms-tab.tsx index d3509be89..3f7de496d 100644 --- a/rails/react-components/src/library/components/permission-forms/manage-forms-tab/manage-forms-tab.tsx +++ b/rails/react-components/src/library/components/permission-forms/manage-forms-tab/manage-forms-tab.tsx @@ -25,7 +25,7 @@ const editPermissionForm = async (formData: IPermissionFormFormData): Promise +const deletePermissionForm = async (permissionFormId: number) => request({ url: `${Portal.API_V1.PERMISSION_FORMS}/${permissionFormId}`, method: "DELETE" @@ -78,7 +78,7 @@ export default function ManageFormsTab() { } }; - const handleDeleteClick = async (permissionFormId: string) => { + const handleDeleteClick = async (permissionFormId: number) => { await deletePermissionForm(permissionFormId); refetchPermissions(); }; diff --git a/rails/react-components/src/library/components/permission-forms/manage-forms-tab/permission-form-row.tsx b/rails/react-components/src/library/components/permission-forms/manage-forms-tab/permission-form-row.tsx index 3e4c1bbe9..a2583db10 100644 --- a/rails/react-components/src/library/components/permission-forms/manage-forms-tab/permission-form-row.tsx +++ b/rails/react-components/src/library/components/permission-forms/manage-forms-tab/permission-form-row.tsx @@ -8,7 +8,7 @@ interface PermissionFormRowProps { permissionForm: IPermissionForm; onEditModalToggle: (permissionForm: IPermissionForm) => void; onEdit: (permissionForm: IPermissionForm) => void; - onDelete: (permissionFormId: string) => void; + onDelete: (permissionFormId: number) => void; } function ensureUrlProtocol(url: string): string { diff --git a/rails/react-components/src/library/components/permission-forms/students-tab/classes-table.tsx b/rails/react-components/src/library/components/permission-forms/students-tab/classes-table.tsx index 06b72e20c..c543a904a 100644 --- a/rails/react-components/src/library/components/permission-forms/students-tab/classes-table.tsx +++ b/rails/react-components/src/library/components/permission-forms/students-tab/classes-table.tsx @@ -8,13 +8,13 @@ import { CurrentSelectedProject, IClassBasicInfo } from "./types"; import css from "./classes-table.scss"; interface IProps { - teacherId: string; + teacherId: number; currentSelectedProject: CurrentSelectedProject; } export const ClassesTable = ({ teacherId, currentSelectedProject }: IProps) => { const { data: classesData, isLoading } = useFetch(Portal.API_V1.teacherClasses(teacherId), []); - const [selectedClassId, setSelectedClassId] = useState(null); + const [selectedClassId, setSelectedClassId] = useState(null); if (isLoading) { return (
Loading...
); @@ -23,8 +23,8 @@ export const ClassesTable = ({ teacherId, currentSelectedProject }: IProps) => { return (
No classes found
); } - const handleViewStudentsClick = (classId: string) => { - setSelectedClassId((prevSelectedClass: string | null) => prevSelectedClass === classId ? null : classId); + const handleViewStudentsClick = (classId: number) => { + setSelectedClassId((prevSelectedClass: number | null) => prevSelectedClass === classId ? null : classId); }; return ( diff --git a/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.scss b/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.scss index b70bc83a5..3fb822d89 100644 --- a/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.scss +++ b/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.scss @@ -1,6 +1,6 @@ @import "../../../../shared/styles/variables/variables"; -.editStudentPerimissionsForm { +.editStudentPermissionsForm { width: 540px; .formTop { @@ -10,7 +10,7 @@ background-color: $blue; color: white; font-weight: bold; - padding: 10px; + padding: 8px 10px; .studentName { align-content: center; @@ -22,13 +22,13 @@ .closeButton button { padding: 0px; border: none; - width: 40px; - height: 40px; + width: 27px; + height: 27px; text-align: center; display: flex; align-items: center; justify-content: center; - border-radius: 50px; + border-radius: 50%; background-color: white; &:hover { background-color: darken(white, 10%); @@ -36,14 +36,14 @@ i { color: $blue; margin: 0 auto; - font-size: 16px; + font-size: 15px; } } } .scrollableWrapper { max-height: 300px; - overflow-y: scroll; + overflow-y: auto; } .formRow { diff --git a/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.tsx b/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.tsx index 2ed49397d..5248bc7af 100644 --- a/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.tsx +++ b/rails/react-components/src/library/components/permission-forms/students-tab/edit-student-permissions-form.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from "react"; import { IPermissionForm, IStudent } from "./types"; -import { bulkUpdatePermissionForms } from "./students-table"; import css from "./edit-student-permissions-form.scss"; @@ -8,11 +7,10 @@ interface CreateEditPermissionFormProps { student: IStudent; permissionForms: IPermissionForm[]; onFormCancel: () => void; - onFormSave: () => void; - classId: string; + onFormSave: ({ studentId, idsToAdd, idsToRemove }: { studentId: number; idsToAdd: number[]; idsToRemove: number[] }) => void; } -export const EditStudentPermissionsForm = ({ student, permissionForms, onFormCancel, onFormSave, classId }: CreateEditPermissionFormProps) => { +export const EditStudentPermissionsForm = ({ student, permissionForms, onFormCancel, onFormSave }: CreateEditPermissionFormProps) => { const [localPermissions, setLocalPermissions] = useState(student.permission_forms || []); useEffect(() => { @@ -21,7 +19,7 @@ export const EditStudentPermissionsForm = ({ student, permissionForms, onFormCan // whenever a permission checkbox changes we figure out if it represents an add or a remove // we update the form state and the idsToAdd and idsToRemove arrays accordingly - const handlePermissionChange = (changedPermissionId: string) => { + const handlePermissionChange = (changedPermissionId: number) => { const shouldAddPermission = !localPermissions.some(lp => lp.id === changedPermissionId); const shouldRemovePermission = localPermissions.some(lp => lp.id === changedPermissionId); @@ -43,18 +41,8 @@ export const EditStudentPermissionsForm = ({ student, permissionForms, onFormCan const handleFormSave = async () => { const idsToAdd = localPermissions.filter(lp => !student.permission_forms.some(pf => pf.id === lp.id)).map(lp => lp.id); const idsToRemove = student.permission_forms.filter(pf => !localPermissions.some(lp => lp.id === pf.id)).map(pf => pf.id); - const response = await bulkUpdatePermissionForms({ - classId, - selectedStudentIds: [student.id.toString()], - addFormIds: idsToAdd, - removeFormIds: idsToRemove - }); - if (response) { - onFormSave(); - } else { - alert("Failed to update permission forms"); - } + onFormSave({ studentId: student.id, idsToAdd, idsToRemove }); }; // Check if there are any changes to the permissions so we know whether to enable the save button @@ -64,10 +52,10 @@ export const EditStudentPermissionsForm = ({ student, permissionForms, onFormCan const sortedPermissions = permissionForms.sort((a, b) => a.name.localeCompare(b.name)); return ( -
+
- { `EDIT Permission Forms for: ${student.name}` } + { `EDIT Permission Forms for: ${student.name}` }
diff --git a/rails/react-components/src/library/components/permission-forms/students-tab/students-table.scss b/rails/react-components/src/library/components/permission-forms/students-tab/students-table.scss index 8278da7d9..e91600917 100644 --- a/rails/react-components/src/library/components/permission-forms/students-tab/students-table.scss +++ b/rails/react-components/src/library/components/permission-forms/students-tab/students-table.scss @@ -21,9 +21,7 @@ table.studentsTable { &.expandedPermissions { td.permissionFormsColumn { - overflow: visible; white-space: normal; - text-overflow: none; } } @@ -32,10 +30,21 @@ table.studentsTable { text-overflow: ellipsis; } + .permissionFormSelectMenuList { + max-height: 250px; + } + .checkboxColumn { width: 35px; } + th.permissionFormsColumn { + cursor: pointer; + &:hover { + background-color: lighten($permission-gold, 5%); + } + } + .permissionFormsColumn { width: 320px; } diff --git a/rails/react-components/src/library/components/permission-forms/students-tab/students-table.tsx b/rails/react-components/src/library/components/permission-forms/students-tab/students-table.tsx index 6db64bfb7..73795fed2 100644 --- a/rails/react-components/src/library/components/permission-forms/students-tab/students-table.tsx +++ b/rails/react-components/src/library/components/permission-forms/students-tab/students-table.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import Select from "react-select"; +import clsx from "clsx"; import { useFetch } from "../../../hooks/use-fetch"; import { request } from "../../../helpers/api/request"; import { CurrentSelectedProject, IPermissionForm, IStudent } from "./types"; @@ -10,19 +11,19 @@ import ModalDialog from "../../shared/modal-dialog"; import css from "./students-table.scss"; interface IProps { - classId: string; + classId: number; currentSelectedProject: CurrentSelectedProject; } type PermissionFormOption = { - value: string; + value: number; label: string; }; export const bulkUpdatePermissionForms = async ( { classId, selectedStudentIds, addFormIds, removeFormIds }: - { classId: string; selectedStudentIds: string[]; addFormIds: string[]; removeFormIds: string[]; } + { classId: number; selectedStudentIds: number[]; addFormIds: number[]; removeFormIds: number[]; } ) => request({ url: Portal.API_V1.PERMISSION_FORMS_BULK_UPDATE, @@ -39,7 +40,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { const { data: studentsData, isLoading: studentsLoading, refetch: refetchStudentsData } = useFetch(Portal.API_V1.permissionFormsClassPermissionForms(classId), []); const { data: permissionForms, isLoading: permissionFormsLoading } = useFetch(Portal.API_V1.PERMISSION_FORMS, []); - const [isStudentSelected, setIsStudentSelected] = useState>({}); + const [isStudentSelected, setIsStudentSelected] = useState>({}); const [permissionFormsToAdd, setPermissionFormsToAdd] = useState([]); const [permissionFormsToRemove, setPermissionFormsToRemove] = useState([]); const [editStudent, setEditStudent] = useState(null); @@ -65,7 +66,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { } const handleStudentSelectedToggle = (e: React.ChangeEvent) => { - const studentId = e.target.name; + const studentId = Number(e.target.name); if (e.target.checked) { setIsStudentSelected(prevIsStudentSelected => ({ ...prevIsStudentSelected, [studentId]: true })); } else { @@ -98,7 +99,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { setPermissionFormsToRemove(selectedOptions); }; - const handleEditClick = (studentId: string) => { + const handleEditClick = (studentId: number) => { const student = studentsData.find(s => s.id === studentId); if (student) { setEditStudent(student); @@ -109,7 +110,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { setRequestInProgress(true); const response = await bulkUpdatePermissionForms({ classId, - selectedStudentIds: Object.keys(isStudentSelected).filter(id => isStudentSelected[id]), + selectedStudentIds: Object.keys(isStudentSelected).filter(id => isStudentSelected[Number(id)]).map(id => Number(id)), addFormIds: permissionFormsToAdd.map(form => form.value), removeFormIds: permissionFormsToRemove.map(form => form.value) }); @@ -124,11 +125,22 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { } }; - // this is passed and called from onFormSave in EditStudentPermissionsForm - // the actual API call happens there - const handleSaveStudentPermissionsSuccess = async () => { - setEditStudent(null); - refetchStudentsData(); + const handleSaveStudentPermissions = async ({ studentId, idsToAdd, idsToRemove }: { studentId: number; idsToAdd: number[]; idsToRemove: number[] }) => { + setRequestInProgress(true); + const response = await bulkUpdatePermissionForms({ + classId, + selectedStudentIds: [studentId], + addFormIds: idsToAdd, + removeFormIds: idsToRemove + }); + setRequestInProgress(false); + + if (response) { + setEditStudent(null); + refetchStudentsData(); + } else { + alert("Failed to update permission forms"); + } }; const handleClickPermissionExpandToggle = () => { @@ -138,15 +150,20 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { const selectedStudentsCount = Object.keys(isStudentSelected).length; const allStudentsSelected = Object.keys(isStudentSelected).length === studentsData.length; + const permissionFormsSelectClassNames = { + option: () => css.permissionFormSelectOption, + menuList: () => css.permissionFormSelectMenuList + }; + return ( <> - +
- + @@ -164,7 +182,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { return ( @@ -191,15 +209,13 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => {
Student Name Username +
Permission Forms { permissionsExpanded @@ -155,6 +172,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { }
- + { studentInfo.name } { studentInfo.login }
- { selectedStudentsCount } selected { selectedStudentsCount === 1 ? "student" : "students" } + { selectedStudentsCount } { selectedStudentsCount === 1 ? "student" : "students" } selected
Add: - classNames={{ - option: () => css.permissionFormSelectOption - }} + classNames={permissionFormsSelectClassNames} className={css.permissionFormSelect} options={permissionFormToAddOptions} isMulti={true} @@ -212,9 +228,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => {
Remove: - classNames={{ - option: () => css.permissionFormSelectOption - }} + classNames={permissionFormsSelectClassNames} className={css.permissionFormSelect} options={permissionFormToRemoveOptions} isMulti={true} @@ -248,8 +262,7 @@ export const StudentsTable = ({ classId, currentSelectedProject }: IProps) => { student={editStudent} permissionForms={currentForms} onFormCancel={() => setEditStudent(null)} - onFormSave={handleSaveStudentPermissionsSuccess} - classId={classId} + onFormSave={handleSaveStudentPermissions} /> } diff --git a/rails/react-components/src/library/components/permission-forms/students-tab/types.ts b/rails/react-components/src/library/components/permission-forms/students-tab/types.ts index bb68d2508..9f22aa769 100644 --- a/rails/react-components/src/library/components/permission-forms/students-tab/types.ts +++ b/rails/react-components/src/library/components/permission-forms/students-tab/types.ts @@ -3,21 +3,21 @@ import { IPermissionForm } from "./types"; export { IPermissionForm, IProject, CurrentSelectedProject } from "../common/types"; export interface ITeacher { - id: string; + id: number; name: string; email: string; login: string; } export interface IStudent { - id: string; + id: number; name: string; login: string; permission_forms: IPermissionForm[]; } export interface IClassBasicInfo { - id: string; + id: number; name: string; class_word: string; } diff --git a/rails/react-components/src/library/components/shared/modal-dialog.tsx b/rails/react-components/src/library/components/shared/modal-dialog.tsx index 6a4cdc44d..1297347f1 100644 --- a/rails/react-components/src/library/components/shared/modal-dialog.tsx +++ b/rails/react-components/src/library/components/shared/modal-dialog.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import Modal from "./modal"; import css from "./modal-dialog.scss"; @@ -7,20 +7,29 @@ interface IProps { title?: string; borderColor?: "orange" | "teal"; } -export default class ModalDialog extends React.Component { - render () { - const { title, children, borderColor } = this.props; - const themeClass = borderColor || "teal"; - return ( - -
- { title &&
{ title }
} -
- { children } -
+const ModalDialog = ({ title, children, borderColor }: IProps) => { + const themeClass = borderColor || "teal"; + + useEffect(() => { + const previousOverflowValue = document.body.style.getPropertyValue("overflow"); + const previousOverflowPriority = document.body.style.getPropertyPriority("overflow"); + document.body.style.setProperty("overflow", "hidden", "important"); + return () => { + document.body.style.setProperty("overflow", previousOverflowValue, previousOverflowPriority); + }; + }, []); + + return ( + +
+ { title &&
{ title }
} +
+ { children }
- - ); - } -} +
+
+ ); +}; + +export default ModalDialog;