Exam generation rework, batch user tables, fastapi endpoint switch
This commit is contained in:
140
src/components/ExamEditor/SettingsEditor/index.tsx
Normal file
140
src/components/ExamEditor/SettingsEditor/index.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user