Exam generation rework, batch user tables, fastapi endpoint switch

This commit is contained in:
Carlos-Mesquita
2024-11-04 23:29:14 +00:00
parent a2bc997e8f
commit 15c9c4d4bd
148 changed files with 11348 additions and 3901 deletions

View File

@@ -0,0 +1,140 @@
import React, { ReactNode, useCallback, useEffect, useMemo, useState, useRef } from "react";
import { FaEye } from "react-icons/fa";
import clsx from "clsx";
import Select from "@/components/Low/Select";
import Input from "@/components/Low/Input";
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import Option from '@/interfaces/option'
import Dropdown from "./Shared/SettingsDropdown";
import useSettingsState from "../Hooks/useSettingsState";
import { Module } from "@/interfaces";
import { SectionSettings } from "@/stores/examEditor/types";
import useExamEditorStore from "@/stores/examEditor";
interface SettingsEditorProps {
sectionId: number,
sectionLabel: string;
module: Module,
introPresets: Option[];
children?: ReactNode;
canPreview: boolean;
preview: () => void;
}
const SettingsEditor: React.FC<SettingsEditorProps> = ({
sectionId,
sectionLabel,
module,
introPresets,
children,
preview,
canPreview,
}) => {
const examLabel = useExamEditorStore((state) => state.modules[module].examLabel) || '';
const { localSettings, updateLocalAndScheduleGlobal } = useSettingsState<SectionSettings>(
module,
sectionId
);
const options = useMemo(() => [
{ value: 'None', label: 'None' },
...introPresets,
{ value: 'Custom', label: 'Custom' }
], [introPresets]);
const onCategoryChange = useCallback((text: string) => {
updateLocalAndScheduleGlobal({ category: text });
}, [updateLocalAndScheduleGlobal]);
const onIntroOptionChange = useCallback((option: { value: string | null, label: string }) => {
let updates: Partial<SectionSettings> = { introOption: option };
switch (option.label) {
case 'None':
updates.currentIntro = undefined;
break;
case 'Custom':
updates.currentIntro = localSettings.customIntro;
break;
default:
const selectedPreset = introPresets.find(preset => preset.label === option.label);
if (selectedPreset) {
updates.currentIntro = selectedPreset.value!
.replace('{part}', sectionLabel)
.replace('{label}', examLabel);
}
}
updateLocalAndScheduleGlobal(updates);
}, [updateLocalAndScheduleGlobal, localSettings.customIntro, introPresets, sectionLabel, examLabel]);
const onCustomIntroChange = useCallback((text: string) => {
updateLocalAndScheduleGlobal({
introOption: { value: 'Custom', label: 'Custom' },
customIntro: text,
currentIntro: text
});
}, [updateLocalAndScheduleGlobal]);
return (
<div className={`flex flex-col gap-8 border bg-ielts-${module}/20 rounded-3xl p-8 w-1/3 h-fit`}>
<div className={`w-full flex justify-center text-ielts-${module} font-bold text-xl`}>{sectionLabel} Settings</div>
<div className="flex flex-col gap-4">
<Dropdown
title="Category"
module={module}
open={localSettings.isCategoryDropdownOpen}
setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isCategoryDropdownOpen: isOpen })}
>
<Input
key={`section-${sectionId}`}
type="text"
placeholder="Category"
name="category"
onChange={onCategoryChange}
roundness="full"
value={localSettings.category || ''}
/>
</Dropdown>
<Dropdown
title="Divider"
module={module}
open={localSettings.isIntroDropdownOpen}
setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isIntroDropdownOpen: isOpen })}
>
<div className="flex flex-col gap-3 w-full">
<Select
options={options}
onChange={(o) => onIntroOptionChange({ value: o!.value, label: o!.label })}
value={localSettings.introOption}
/>
{localSettings.introOption.value !== "None" && (
<AutoExpandingTextArea
key={`section-${sectionId}`}
value={localSettings.currentIntro || ''}
onChange={onCustomIntroChange}
/>
)}
</div>
</Dropdown>
{children}
<div className="flex flex-row justify-center mt-4">
<button
className={clsx(
"flex items-center justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300",
`bg-ielts-${module}/70 border border-ielts-${module} hover:bg-ielts-${module} disabled:bg-ielts-${module}/30`,
"disabled:cursor-not-allowed disabled:text-gray-200"
)}
onClick={preview}
disabled={!canPreview}
>
<FaEye className="mr-2" size={18} />
Preview Module
</button>
</div>
</div>
</div>
);
};
export default SettingsEditor;