From dd56562031eb8213d7f25117b6c7aabd6635af2e Mon Sep 17 00:00:00 2001 From: Valentin Gimonnet Date: Mon, 19 Feb 2024 15:51:18 +0100 Subject: [PATCH] fix typescript and eslint errors --- server/app.ts | 2 +- server/authentication/login.ts | 2 +- server/authentication/loginStudent.ts | 2 + server/controllers/projects.ts | 5 +- server/controllers/questions.ts | 2 + server/controllers/users.ts | 9 +-- server/lib/database.ts | 3 +- server/middlewares/authenticate.ts | 2 +- server/socket/index.ts | 3 +- src/api/projects/projects.pdf.ts | 4 +- src/api/projects/projects.put.ts | 2 +- src/api/questions/questions.put.ts | 2 + src/components/collaboration/AlertModal.tsx | 76 ++++++++++--------- .../collaboration/ButtonShowFeedback.tsx | 2 +- .../collaboration/FeedbackModal.tsx | 9 +-- src/components/collaboration/FormFeedback.tsx | 8 +- .../collaboration/NextStepButton.tsx | 5 +- src/components/collaboration/StatusModal.tsx | 4 +- .../DiaporamaPlayer/DiaporamaPlayer.tsx | 2 + src/components/layout/Modal/Modal.tsx | 3 +- src/hooks/useCurrentProject.ts | 5 ++ src/hooks/useSocket.ts | 8 +- src/lib/get-sequence-duration.ts | 2 +- src/pages/admin/scenarios/edit/[id].tsx | 2 +- src/pages/admin/scenarios/new.tsx | 2 +- src/pages/admin/users/index.tsx | 2 +- src/pages/create/1-scenario/index.tsx | 22 +++--- src/pages/create/2-questions/index.tsx | 52 +++++++------ src/pages/create/3-storyboard/edit.tsx | 2 +- src/pages/create/3-storyboard/index.tsx | 7 +- src/pages/create/3-storyboard/title.tsx | 2 +- src/pages/create/4-pre-mounting/edit.tsx | 2 +- src/pages/create/4-pre-mounting/index.tsx | 4 +- src/pages/create/5-music/index.tsx | 2 +- src/pages/join.tsx | 22 ++++-- types/models/question.type.ts | 4 +- 36 files changed, 159 insertions(+), 128 deletions(-) diff --git a/server/app.ts b/server/app.ts index 133e635e..6349c9aa 100644 --- a/server/app.ts +++ b/server/app.ts @@ -134,7 +134,7 @@ async function startApp() { if (port === false) { logger.error(`Exiting. Invalid port to use: %s`, port); } else { - const server = Server(app); + const server = new Server(app); // --- Start socket server --- startSocketServer(server); server.listen(port); diff --git a/server/authentication/login.ts b/server/authentication/login.ts index 29f53b3b..1620ed76 100644 --- a/server/authentication/login.ts +++ b/server/authentication/login.ts @@ -73,7 +73,7 @@ export async function login(req: Request, res: Response): Promise { await getConnection() .createQueryBuilder() .update(Project) - .set({ isCollaborationActive: false, joinCode: false }) + .set({ isCollaborationActive: false, joinCode: null }) .where({ userId: user.id, isCollaborationActive: true }) .execute(); diff --git a/server/authentication/loginStudent.ts b/server/authentication/loginStudent.ts index c19dabd9..1fc79708 100644 --- a/server/authentication/loginStudent.ts +++ b/server/authentication/loginStudent.ts @@ -52,6 +52,8 @@ export async function loginStudent(req: Request, res: Response): Promise { project === undefined || teacher === undefined || sequency === undefined || + sequency.project === undefined || + sequency.project.user === undefined || project.id !== sequency.project.id || teacher.id !== sequency.project.user.id || project.isCollaborationActive !== true diff --git a/server/controllers/projects.ts b/server/controllers/projects.ts index 05ab5ba4..48806487 100644 --- a/server/controllers/projects.ts +++ b/server/controllers/projects.ts @@ -206,6 +206,7 @@ type PostProjectData = { soundUrl?: string | null; soundVolume?: number | null; musicBeginTime?: number; + videoJobId?: string | null; }; const POST_PROJECT_SCHEMA: JSONSchemaType = { type: 'object', @@ -265,7 +266,7 @@ projectController.post({ path: '/', userType: UserType.CLASS }, async (req, res, newProject.soundUrl = data.soundUrl || null; newProject.soundVolume = data.soundVolume || null; newProject.musicBeginTime = data.musicBeginTime || 0; - newProject.videoJobId = data.videoJobId || 0; + newProject.videoJobId = data.videoJobId || null; const languageCode = getQueryString(req.query.languageCode) || req.cookies?.['app-language'] || 'fr'; newProject.languageCode = languageCode; const newQuestions: Question[] = []; @@ -370,7 +371,7 @@ projectController.put({ path: '/:id', userType: UserType.CLASS }, async (req, re project.joinCode = null; } else if (data.isCollaborationActive === true) { project.isCollaborationActive = true; - project.joinCode = data.joinCode; + project.joinCode = data.joinCode || null; // set collaboration mode to false on each user project await getConnection() .createQueryBuilder() diff --git a/server/controllers/questions.ts b/server/controllers/questions.ts index f348f6ce..307cd80a 100644 --- a/server/controllers/questions.ts +++ b/server/controllers/questions.ts @@ -7,6 +7,7 @@ import type { Title } from '../../types/models/title.type'; import { Question } from '../entities/question'; import { UserType } from '../entities/user'; import { ajv, sendInvalidDataError } from '../lib/json-schema-validator'; +import { logger } from '../lib/logger'; import { getQueryString } from '../utils/get-query-string'; import { Controller } from './controller'; @@ -253,6 +254,7 @@ questionController.put({ path: '/:id', userType: UserType.CLASS }, async (req, r question.soundUrl = data.soundUrl !== undefined ? data.soundUrl : question.soundUrl; question.soundVolume = data.soundVolume !== undefined ? data.soundVolume : question.soundVolume; const dataStatus = data.status; + logger.info(`dataStatus: ${dataStatus}`); if (dataStatus !== undefined && dataStatus !== null) { question.status = dataStatus; question.feedback = [QuestionStatus.ONGOING, QuestionStatus.PREMOUNTING].includes(dataStatus) && data.feedback ? data.feedback : null; diff --git a/server/controllers/users.ts b/server/controllers/users.ts index 9bef3c47..f2829a0b 100644 --- a/server/controllers/users.ts +++ b/server/controllers/users.ts @@ -133,13 +133,12 @@ userController.post({ path: '' }, async (req, res) => { const fromAdmin = req.user !== undefined && req.user.type === UserType.PLMO_ADMIN; if (!fromAdmin) { - const isValid: boolean = (await getRepository(Invite).count({ where: { token: data.inviteCode } })) > 0; - if (!isValid) { - throw new AppError('forbidden', ['Invite code provided is invalid.']); - } else { - if (data.expired_at < new Date().toISOString()) { + const invite = await getRepository(Invite).findOne({ where: { token: data.inviteCode } }); + if (invite === undefined || invite.expired_at < new Date()) { + if (invite !== undefined) { await getRepository(Invite).delete({ token: data.inviteCode }); } + throw new AppError('forbidden', ['Invite code provided is invalid.']); } } diff --git a/server/lib/database.ts b/server/lib/database.ts index 0454574b..a4cf7fcf 100644 --- a/server/lib/database.ts +++ b/server/lib/database.ts @@ -36,7 +36,8 @@ const getDBConfig = (): ConnectionOptions | null => { } const options = { - logging: process.env.NODE_ENV !== 'production', + logging: false, + // logging: process.env.NODE_ENV !== 'production', entities: [path.join(__dirname, '../entities/*.js')], migrations: [path.join(__dirname, '../migration/**/*.js')], synchronize: true, diff --git a/server/middlewares/authenticate.ts b/server/middlewares/authenticate.ts index ba216aff..d55e3f8f 100644 --- a/server/middlewares/authenticate.ts +++ b/server/middlewares/authenticate.ts @@ -82,7 +82,6 @@ export function authenticate(userType: UserType | undefined = undefined): Reques if (data.isStudent) { const user: User = ANONYMOUS_USER; const teacher = await getRepository(User).findOne({ where: { id: data.teacherId } }); - user.teacherId = teacher.id; const project = await getRepository(Project).findOne({ where: { id: data.projectId } }); const sequency = await getRepository(Question).findOne({ where: { id: data.sequencyId }, relations: ['project', 'project.user'] }); @@ -97,6 +96,7 @@ export function authenticate(userType: UserType | undefined = undefined): Reques ) { throw new AppError('forbidden'); } + user.teacherId = teacher.id; req.user = user; next(); } else { diff --git a/server/socket/index.ts b/server/socket/index.ts index d82d7246..9da5cc2a 100644 --- a/server/socket/index.ts +++ b/server/socket/index.ts @@ -1,3 +1,4 @@ +import type { Server as HttpServer } from 'http'; import type { Project } from 'server/entities/project'; import type { Question } from 'server/entities/question'; import type { Socket } from 'socket.io'; @@ -5,7 +6,7 @@ import { Server } from 'socket.io'; import type { AlertStudentData, AlertTeacherData } from '../../types/models/socket.type'; -export function startSocketServer(server) { +export function startSocketServer(server: HttpServer) { const io = new Server(server, { cors: { origin: '*', diff --git a/src/api/projects/projects.pdf.ts b/src/api/projects/projects.pdf.ts index a3b0a2ff..80d9f7c6 100644 --- a/src/api/projects/projects.pdf.ts +++ b/src/api/projects/projects.pdf.ts @@ -11,8 +11,8 @@ export type GetPDFParams = { scenarioDescription: string; questions: Question[]; languageCode: string; - soundUrl: string | null; - soundVolume: number | null; + soundUrl?: string | null; + soundVolume?: number | null; musicBeginTime?: number; }; diff --git a/src/api/projects/projects.put.ts b/src/api/projects/projects.put.ts index 44448bae..b55faa93 100644 --- a/src/api/projects/projects.put.ts +++ b/src/api/projects/projects.put.ts @@ -13,7 +13,7 @@ type PUTParams = { soundVolume?: number | null; musicBeginTime?: number; isCollaborationActive?: boolean; - joinCode?: number; + joinCode?: number | null; }; export const updateProject = async ({ projectId, ...data }: PUTParams): Promise => { diff --git a/src/api/questions/questions.put.ts b/src/api/questions/questions.put.ts index 2bd3e072..29f5f7a7 100644 --- a/src/api/questions/questions.put.ts +++ b/src/api/questions/questions.put.ts @@ -1,6 +1,7 @@ import type { UseMutationOptions } from 'react-query'; import { useMutation, useQueryClient } from 'react-query'; +import type { HttpRequestError } from 'src/utils/http-request'; import { httpRequest } from 'src/utils/http-request'; import type { Question, QuestionStatus } from 'types/models/question.type'; import type { Title } from 'types/models/title.type'; @@ -15,6 +16,7 @@ type PUTParams = { soundUrl?: string | null; soundVolume?: number | null; status?: QuestionStatus | null; + feedback?: string | null; }; export const updateQuestion = async ({ questionId, ...data }: PUTParams): Promise => { diff --git a/src/components/collaboration/AlertModal.tsx b/src/components/collaboration/AlertModal.tsx index 191e72f4..683453da 100644 --- a/src/components/collaboration/AlertModal.tsx +++ b/src/components/collaboration/AlertModal.tsx @@ -2,9 +2,10 @@ import { useRouter } from 'next/router'; import React from 'react'; import { Modal } from '../layout/Modal'; -import useQuery from 'src/hooks/useQuery'; +import type { ModalProps } from '../layout/Modal/Modal'; import { useTranslation } from 'src/i18n/useTranslation'; import { COLORS } from 'src/utils/colors'; +import { useQueryNumber, useQueryString } from 'src/utils/useQueryId'; import { QuestionStatus } from 'types/models/question.type'; export const AlertModal: React.FunctionComponent = () => { @@ -14,44 +15,53 @@ export const AlertModal: React.FunctionComponent = () => { const [modalTitle, setModalTitle] = React.useState(''); const [modalContent, setModalContent] = React.useState(''); const [hasConfirmButton, setHasConfirmButton] = React.useState(false); - const [route, setRoute] = React.useState(null); + const [route, setRoute] = React.useState(''); const router = useRouter(); - const query = useQuery(); + // retrieve query params + const alert: string | undefined = useQueryString('alert'); + const sequency: number | undefined = useQueryNumber('sequency'); + const type: string | undefined = useQueryString('type'); + const status: number | undefined = useQueryNumber('status'); + const projectId: string | undefined = useQueryString('projectId'); React.useEffect(() => { - if (!query) return; - setModal(query); - }, [query]); + if (alert !== undefined && alert === 'teacher' && sequency !== undefined && projectId !== undefined && status !== undefined) { + setModalForTeacher(); + } else if (alert !== undefined && alert == 'student' && type !== undefined) { + setModalForStudent(); + } + + return; + }, [alert]); + + const setModalForStudent = () => { + const isFeedback = type === 'feedback'; + setModalTitle(t(`collaboration_alert_modal_student_title_${isFeedback ? 'feedback' : 'ok'}`)); + setModalContent(t(`collaboration_alert_modal_student_content_${isFeedback ? 'feedback' : 'ok'}`)); + setShowModal(true); + }; - const setModal = ({ alert, sequency, type, status, projectId }) => { - if (alert && alert === 'teacher') { - setModalTitle(t('collaboration_alert_modal_teacher_title')); - if (sequency) { - setModalContent(t('collaboration_alert_modal_teacher_content', { color: COLORS[sequency], sequency: parseInt(sequency) + 1 })); - } else { - setModalContent(t('collaboration_alert_modal_teacher_content_empty')); - } + const setModalForTeacher = () => { + setModalTitle(t('collaboration_alert_modal_teacher_title')); - if (sequency && projectId && status && [QuestionStatus.STORYBOARD, QuestionStatus.SUBMITTED].includes(parseInt(status))) { - setHasConfirmButton(true); - setRoute( - parseInt(status) === QuestionStatus.STORYBOARD - ? `/create/3-storyboard?projectId=${projectId}` - : `/create/4-pre-mounting/edit?question=${sequency}&projectId=${projectId}`, - ); - } - setShowModal(true); - } else if (alert && alert === 'student') { - const isFeedback = type === 'feedback'; - setModalTitle(t(`collaboration_alert_modal_student_title_${isFeedback ? 'feedback' : 'ok'}`)); - setModalContent(t(`collaboration_alert_modal_student_content_${isFeedback ? 'feedback' : 'ok'}`)); - setShowModal(true); + if (sequency !== undefined) { + setModalContent(t('collaboration_alert_modal_teacher_content', { color: COLORS[sequency], sequency: sequency + 1 })); + } else { + setModalContent(t('collaboration_alert_modal_teacher_content_empty')); } + + setHasConfirmButton(true); + setRoute( + status === QuestionStatus.STORYBOARD + ? `/create/3-storyboard?projectId=${projectId}` + : `/create/4-pre-mounting/edit?question=${sequency}&projectId=${projectId}`, + ); + setShowModal(true); }; const closeModal = (goToPage: boolean = false) => { - if (goToPage) { + if (goToPage && route) { router.push(route); } else { delete router.query.alert; @@ -64,19 +74,17 @@ export const AlertModal: React.FunctionComponent = () => { setModalContent(''); setModalTitle(''); setHasConfirmButton(false); - setRoute(null); + setRoute(''); setShowModal(false); }; - const modalProps = { + const modalProps: ModalProps = { isOpen: showModal, onClose: () => closeModal(), - onConfirm: hasConfirmButton ? () => closeModal(true) : null, + onConfirm: hasConfirmButton ? () => closeModal(true) : undefined, title: modalTitle, cancelLabel: t('close'), confirmLabel: t('see'), - ariaLabelledBy: '', - ariaDescribedBy: '', }; return ( diff --git a/src/components/collaboration/ButtonShowFeedback.tsx b/src/components/collaboration/ButtonShowFeedback.tsx index d761a111..df4e2fd9 100644 --- a/src/components/collaboration/ButtonShowFeedback.tsx +++ b/src/components/collaboration/ButtonShowFeedback.tsx @@ -12,7 +12,7 @@ export const ButtonShowFeedback: React.FunctionComponent = ({ fee const { t } = useTranslation(); return ( - + {feedback} ); diff --git a/src/components/collaboration/FormFeedback.tsx b/src/components/collaboration/FormFeedback.tsx index 6dede0c7..12de5367 100644 --- a/src/components/collaboration/FormFeedback.tsx +++ b/src/components/collaboration/FormFeedback.tsx @@ -44,7 +44,9 @@ export const FormFeedback: React.FunctionComponent = ({ quest updateProjectSocket(updatedProject); } - alertStudentSocket({ room: `project-${project.id}_question-${question.id}`, feedback: feedbackData, projectId: project.id }); + if (project) { + alertStudentSocket({ room: `project-${project.id}_question-${question.id}`, feedback: feedbackData, projectId: project.id }); + } } catch (err) { console.error(err); sendToast({ message: t('unknown_error'), type: 'error' }); @@ -97,7 +99,7 @@ export const FormFeedback: React.FunctionComponent = ({ quest }} > )} - {isStudent && studentQuestion && studentQuestion.status === QuestionStatus.ONGOING && ( + {isStudent && studentQuestion && studentQuestion.status === QuestionStatus.ONGOING && sequencyId && ( )} { }} /> + ); }; diff --git a/src/pages/create/3-storyboard/title.tsx b/src/pages/create/3-storyboard/title.tsx index 5a10e5ad..da5cb4e8 100644 --- a/src/pages/create/3-storyboard/title.tsx +++ b/src/pages/create/3-storyboard/title.tsx @@ -70,7 +70,7 @@ const TitlePlan = () => { const sequence = React.useMemo(() => (questionIndex !== -1 ? questions[questionIndex] : undefined), [questions, questionIndex]); const isStudent = user?.type === UserType.STUDENT; - const [showButtonFeedback, setShowButtonFeedback] = React.useState(isStudent && sequence && sequence.feedback); + const [showButtonFeedback, setShowButtonFeedback] = React.useState((isStudent && sequence && sequence.feedback) as boolean); const [showFeedback, setShowFeedback] = React.useState(false); React.useEffect(() => { diff --git a/src/pages/create/4-pre-mounting/edit.tsx b/src/pages/create/4-pre-mounting/edit.tsx index 768740f5..2ed579ce 100644 --- a/src/pages/create/4-pre-mounting/edit.tsx +++ b/src/pages/create/4-pre-mounting/edit.tsx @@ -82,7 +82,7 @@ const PreMountSequence = () => { const { user } = React.useContext(userContext); const isStudent = user?.type === UserType.STUDENT; - const [showButtonFeedback, setShowButtonFeedback] = React.useState(isStudent && sequence && sequence.feedback); + const [showButtonFeedback, setShowButtonFeedback] = React.useState((isStudent && sequence && sequence.feedback) as boolean); const [showFeedback, setShowFeedback] = React.useState(false); React.useEffect(() => { diff --git a/src/pages/create/4-pre-mounting/index.tsx b/src/pages/create/4-pre-mounting/index.tsx index c357a016..a3fb9d73 100644 --- a/src/pages/create/4-pre-mounting/index.tsx +++ b/src/pages/create/4-pre-mounting/index.tsx @@ -41,7 +41,7 @@ const PreMountingPage = () => { const { socket, connectStudent, connectTeacher } = useSocket(); const [showFeedback, setShowFeedback] = React.useState(false); - const [studentQuestion, setStudentQuestion] = React.useState(null); + const [studentQuestion, setStudentQuestion] = React.useState(null); const { user } = React.useContext(userContext); const isStudent = user?.type === UserType.STUDENT; @@ -112,7 +112,7 @@ const PreMountingPage = () => { ); })} - {isStudent && studentQuestion && studentQuestion.status === QuestionStatus.PREMOUNTING && ( + {isStudent && studentQuestion && studentQuestion.status === QuestionStatus.PREMOUNTING && sequencyId && ( )} {!isStudent && ( diff --git a/src/pages/create/5-music/index.tsx b/src/pages/create/5-music/index.tsx index 04b4d660..b03600ed 100644 --- a/src/pages/create/5-music/index.tsx +++ b/src/pages/create/5-music/index.tsx @@ -37,7 +37,7 @@ const MusicPage = () => { enabled: !isProjectLoading && project !== undefined, }); const { isCollaborationActive } = useCollaboration(); - const { socket, connectTeacher, updateProject: updateProjectSocket } = useSocket(); + const { updateProject: updateProjectSocket } = useSocket(); const [soundBlob, setSoundBlob] = React.useState(null); const [volume, setVolume] = React.useState(project?.soundVolume ?? 100); diff --git a/src/pages/join.tsx b/src/pages/join.tsx index 38627813..0e98c672 100644 --- a/src/pages/join.tsx +++ b/src/pages/join.tsx @@ -28,7 +28,7 @@ const LoginPage = () => { const [isLoading, setIsLoading] = React.useState(false); const [showQrReader, setShowQrReader] = React.useState(false); const [joinCode, setJoinCode] = React.useState(''); - const [project, setProject] = React.useState(undefined); + const [project, setProject] = React.useState(undefined); const { connectStudent } = useSocket(); @@ -44,9 +44,14 @@ const LoginPage = () => { color: string; user: User; }; - const postLoginStudent = async (data) => { + type LoginStudentData = { + projectId: number; + sequencyId: number; + teacherId: number; + }; + const postLoginStudent = async (data: LoginStudentData) => { setIsLoading(true); - const response = await httpRequest({ + const response = await httpRequest({ method: 'POST', url: `/login/student`, data, @@ -68,7 +73,8 @@ const LoginPage = () => { setIsLoading(false); }; - const QrReaderResult = async (result) => { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const QrReaderResult = async (result: any) => { if (result) { const loginData = JSON.parse(result); @@ -130,7 +136,7 @@ const LoginPage = () => { id="joinCode" name="joinCode" type="number" - min={10000} + min={0} max={999999} color="secondary" value={joinCode} @@ -161,17 +167,17 @@ const LoginPage = () => { onClick={toggleShowQrReader} label={t(`collaboration_qrcode_scan_${showQrReader ? 'hide' : 'show'}`)} > - {showQrReader && } + {showQrReader && } )} - {project !== undefined && ( + {project !== undefined && project.questions && (
{project.questions.map((q, index) => { return (
postLoginStudent({ projectId: project.id, sequencyId: q.id, teacherId: project.userId })} + onClick={() => postLoginStudent({ projectId: project.id, sequencyId: q.id, teacherId: project?.userId || 0 })} key={index} >

{t('collaboration_join_sequency_number', { sequency: index + 1 })}

diff --git a/types/models/question.type.ts b/types/models/question.type.ts index c7a58ed8..e6029f19 100644 --- a/types/models/question.type.ts +++ b/types/models/question.type.ts @@ -21,8 +21,8 @@ export interface Question { voiceOffBeginTime: number; soundUrl: string | null; soundVolume: number | null; - status: QuestionStatus; - feedback: string | null; + status?: QuestionStatus; + feedback?: string | null; } export interface QuestionTemplate {