Ts changes weren't staged, it compiles still doesnt build building
This commit is contained in:
@@ -78,6 +78,7 @@ export const UnderlineQuestion: React.FC<UnderlineQuestionProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
validateQuestion(question);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [question]);
|
||||
|
||||
const handlePromptChange = (value: string) => {
|
||||
|
||||
@@ -32,6 +32,7 @@ const useSectionEdit = ({
|
||||
|
||||
const handleEdit = useCallback(() => {
|
||||
setEditing(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sectionId, setEditing, updateRoot]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
@@ -41,17 +42,20 @@ const useSectionEdit = ({
|
||||
setEditing(false);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [setEditing, updateRoot, onSave, sectionId]);
|
||||
|
||||
const handleDiscard = useCallback(() => {
|
||||
setEditing(false);
|
||||
onDiscard?.();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [setEditing, updateRoot, onDiscard, sectionId]);
|
||||
|
||||
const modeHandle = useCallback(() => {
|
||||
setEditing(!editing);
|
||||
onMode?.();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [setEditing, editing, updateRoot, onMode, sectionId]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ReadingPart } from '@/interfaces/exam';
|
||||
import { defaultSectionSettings } from '@/stores/examEditor/defaults';
|
||||
|
||||
const WordUploader: React.FC<{ module: Module }> = ({ module }) => {
|
||||
const {currentModule, dispatch} = useExamEditorStore();
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
|
||||
const examInputRef = useRef<HTMLInputElement>(null);
|
||||
const solutionsInputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -38,14 +38,14 @@ const WordUploader: React.FC<{ module: Module }> = ({ module }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleImport = useCallback( async () => {
|
||||
const handleImport = useCallback(async () => {
|
||||
try {
|
||||
if (!examFile) {
|
||||
toast.error('Exam file is required');
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({type: "UPDATE_MODULE", payload: {updates: {importing: true}, module}})
|
||||
dispatch({ type: "UPDATE_MODULE", payload: { updates: { importing: true }, module } })
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('exercises', examFile);
|
||||
@@ -76,27 +76,30 @@ const WordUploader: React.FC<{ module: Module }> = ({ module }) => {
|
||||
const newSectionsStates = data.parts.map(
|
||||
(part: ReadingPart, index: number) => defaultSectionSettings(module, index + 1, part)
|
||||
);
|
||||
dispatch({type: "UPDATE_MODULE", payload: {
|
||||
updates: {
|
||||
sections: newSectionsStates,
|
||||
minTimer: data.minTimer,
|
||||
importModule: false,
|
||||
importing: false,
|
||||
},
|
||||
module
|
||||
}});
|
||||
dispatch({
|
||||
type: "UPDATE_MODULE", payload: {
|
||||
updates: {
|
||||
sections: newSectionsStates,
|
||||
minTimer: data.minTimer,
|
||||
importModule: false,
|
||||
importing: false,
|
||||
},
|
||||
module
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(`An unknown error has occured while import ${module} exam!`);
|
||||
} finally {
|
||||
dispatch({type: "UPDATE_MODULE", payload: {updates: {importing: false}, module}})
|
||||
dispatch({ type: "UPDATE_MODULE", payload: { updates: { importing: false }, module } })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
examFile,
|
||||
solutionsFile,
|
||||
dispatch,
|
||||
module
|
||||
currentModule
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,7 +7,7 @@ import { capitalize } from "lodash";
|
||||
import { Difficulty } from "@/interfaces/exam";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { ModuleState } from "@/stores/examEditor/types";
|
||||
import { ModuleState, SectionState } from "@/stores/examEditor/types";
|
||||
import { Module } from "@/interfaces";
|
||||
import useExamEditorStore from "@/stores/examEditor";
|
||||
import WritingSettings from "./SettingsEditor/writing";
|
||||
@@ -38,8 +38,8 @@ const ExamEditor: React.FC = () => {
|
||||
useEffect(() => {
|
||||
const currentSections = sections;
|
||||
const currentLabels = sectionLabels;
|
||||
let updatedSections;
|
||||
let updatedLabels;
|
||||
let updatedSections: SectionState[];
|
||||
let updatedLabels: any;
|
||||
|
||||
if (numberOfParts > currentSections.length) {
|
||||
const newSections = [...currentSections];
|
||||
@@ -76,6 +76,7 @@ const ExamEditor: React.FC = () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [numberOfParts]);
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function TrueFalseSolution({prompt, type, id, questions, userSolu
|
||||
{userSolutions &&
|
||||
questions.map((question, index) => {
|
||||
const userSolution = userSolutions.find((x) => x.id === question.id.toString());
|
||||
const solution = question.solution.toString().toLowerCase() as Solution;
|
||||
const solution = question?.solution?.toString().toLowerCase() as Solution;
|
||||
|
||||
return (
|
||||
<div key={question.id.toString()} className="flex flex-col gap-4">
|
||||
|
||||
@@ -8,6 +8,7 @@ interface Props {
|
||||
|
||||
const UserDisplay = (displayUser: User) => (
|
||||
<div className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={displayUser.profilePicture} alt={displayUser.name} className="rounded-full w-10 h-10" />
|
||||
<div className="flex flex-col gap-1 items-start">
|
||||
<span>{displayUser.name}</span>
|
||||
|
||||
196
src/components/UserTable.tsx
Normal file
196
src/components/UserTable.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
createColumnHelper,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
getFilteredRowModel,
|
||||
FilterFn,
|
||||
} from '@tanstack/react-table';
|
||||
import { UserImport } from "../interfaces/IUserImport";
|
||||
|
||||
const globalFilterFn: FilterFn<any> = (row, columnId, filterValue: string) => {
|
||||
const value = row.getValue(columnId);
|
||||
return String(value).toLowerCase().includes(filterValue.toLowerCase());
|
||||
};
|
||||
|
||||
const columnHelper = createColumnHelper<UserImport>();
|
||||
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor('name', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Name',
|
||||
}),
|
||||
columnHelper.accessor('studentID', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Student ID',
|
||||
}),
|
||||
columnHelper.accessor('demographicInformation.passport_id', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Passport/National ID',
|
||||
}),
|
||||
columnHelper.accessor('email', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Email',
|
||||
}),
|
||||
columnHelper.accessor('demographicInformation.phone', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Phone Number',
|
||||
}),
|
||||
columnHelper.accessor('groupName', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Classroom Name',
|
||||
}),
|
||||
columnHelper.accessor('demographicInformation.country', {
|
||||
cell: info => info.getValue(),
|
||||
header: () => 'Country',
|
||||
}),
|
||||
];
|
||||
|
||||
|
||||
const UserTable: React.FC<{ users: UserImport[] }> = ({ users }) => {
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
|
||||
const table = useReactTable({
|
||||
data: users,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
globalFilterFn: globalFilterFn,
|
||||
state: {
|
||||
globalFilter,
|
||||
},
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 5,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='flex flex-col'>
|
||||
<div className="flex flew-row w-full mb-4 justify-between gap-4">
|
||||
<input
|
||||
type="text"
|
||||
value={globalFilter ?? ''}
|
||||
onChange={e => setGlobalFilter(e.target.value)}
|
||||
placeholder="Search ..."
|
||||
className="p-2 border rounded flex-grow"
|
||||
/>
|
||||
<select
|
||||
value={table.getState().pagination.pageSize}
|
||||
onChange={e => {
|
||||
table.setPageSize(Number(e.target.value));
|
||||
}}
|
||||
className="p-2 border rounded"
|
||||
>
|
||||
{[5, 10, 15, 20].map(pageSize => (
|
||||
<option key={pageSize} value={pageSize}>
|
||||
Show {pageSize}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th
|
||||
key={header.id}
|
||||
className='bg-mti-purple-ultralight/80 first:rounded-tl-3xl last:rounded-tr-3xl py-4 first:pl-6 text-mti-purple-light cursor-pointer'
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
{header.isPlaceholder ? null : (
|
||||
<div className='flex flex-row justify-between'>
|
||||
<span>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</span>
|
||||
<span className='pr-6'>
|
||||
{{
|
||||
asc: ' 🔼',
|
||||
desc: ' 🔽',
|
||||
}[header.column.getIsSorted() as string] ?? null}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row, index, array) => {
|
||||
const isLastRow = index === array.length - 1;
|
||||
return (
|
||||
<tr key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
return (
|
||||
<td
|
||||
key={cell.id}
|
||||
className={
|
||||
isLastRow
|
||||
? `first:rounded-bl-3xl last:rounded-br-3xl py-4 first:pl-6 bg-mti-purple-ultralight/40`
|
||||
: `first:pl-6 py-4 border-b bg-mti-purple-ultralight/40`
|
||||
}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="mt-4 flex items-center gap-4 mx-auto">
|
||||
<button
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
className="px-4 py-2 bg-mti-purple-light text-white rounded disabled:opacity-50"
|
||||
>
|
||||
{'<<'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
className="px-4 py-2 bg-mti-purple-light text-white rounded disabled:opacity-50"
|
||||
>
|
||||
{'<'}
|
||||
</button>
|
||||
<span>
|
||||
Page{' '}
|
||||
<strong>
|
||||
{table.getState().pagination.pageIndex + 1} of{' '}
|
||||
{table.getPageCount()}
|
||||
</strong>
|
||||
</span>
|
||||
<button
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
className="px-4 py-2 bg-mti-purple-light text-white rounded disabled:opacity-50"
|
||||
>
|
||||
{'>'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
className="px-4 py-2 bg-mti-purple-light text-white rounded disabled:opacity-50"
|
||||
>
|
||||
{'>>'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserTable;
|
||||
Reference in New Issue
Block a user