Add button to submit exam without approval process

This commit is contained in:
Joao Correia
2025-02-09 17:37:19 +00:00
parent 9ae6b8e894
commit 75b4643918
8 changed files with 47 additions and 25 deletions

View File

@@ -19,7 +19,7 @@ interface SettingsEditorProps {
children?: ReactNode; children?: ReactNode;
canPreview: boolean; canPreview: boolean;
canSubmit: boolean; canSubmit: boolean;
submitModule: () => void; submitModule: (requiresApproval: boolean) => void;
preview: () => void; preview: () => void;
} }
@@ -148,19 +148,31 @@ const SettingsEditor: React.FC<SettingsEditorProps> = ({
</div> </div>
</Dropdown> </Dropdown>
{children} {children}
<div className="flex flex-row justify-between mt-4"> <div className="flex flex-col gap-3 mt-4">
<button <button
className={clsx( className={clsx(
"flex items-center justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300", "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`, `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" "disabled:cursor-not-allowed disabled:text-gray-200"
)} )}
onClick={submitModule} onClick={() => submitModule(true)}
disabled={!canSubmit} disabled={!canSubmit}
> >
<FaFileUpload className="mr-2" size={18} /> <FaFileUpload className="mr-2" size={18} />
Submit Module as Exam Submit Module as Exam
</button> </button>
<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={() => submitModule(false)}
disabled={!canSubmit}
>
<FaFileUpload className="mr-2" size={18} />
Submit Module as Exam Without Approval Process
</button>
<button <button
className={clsx( className={clsx(
"flex items-center justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300", "flex items-center justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300",

View File

@@ -76,7 +76,7 @@ const LevelSettings: React.FC = () => {
}); });
}); });
const submitLevel = async () => { const submitLevel = async (requiresApproval: boolean) => {
if (title === "") { if (title === "") {
toast.error("Enter a title for the exam!"); toast.error("Enter a title for the exam!");
return; return;
@@ -195,7 +195,8 @@ const LevelSettings: React.FC = () => {
category: s.settings.category category: s.settings.category
}; };
}).filter(part => part.exercises.length > 0), }).filter(part => part.exercises.length > 0),
isDiagnostic: true, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed. requiresApproval: requiresApproval,
isDiagnostic: requiresApproval ? true : false, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed.
minTimer, minTimer,
module: "level", module: "level",
id: title, id: title,

View File

@@ -65,7 +65,7 @@ const ListeningSettings: React.FC = () => {
} }
]; ];
const submitListening = async () => { const submitListening = async (requiresApproval: boolean) => {
if (title === "") { if (title === "") {
toast.error("Enter a title for the exam!"); toast.error("Enter a title for the exam!");
return; return;
@@ -138,7 +138,8 @@ const ListeningSettings: React.FC = () => {
category: s.settings.category category: s.settings.category
}; };
}), }),
isDiagnostic: true, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed. requiresApproval: requiresApproval,
isDiagnostic: requiresApproval ? true : false, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed.
minTimer, minTimer,
module: "listening", module: "listening",
id: title, id: title,

View File

@@ -60,7 +60,7 @@ const ReadingSettings: React.FC = () => {
(s.state as ReadingPart).exercises.length > 0 (s.state as ReadingPart).exercises.length > 0
); );
const submitReading = () => { const submitReading = (requiresApproval: boolean) => {
if (title === "") { if (title === "") {
toast.error("Enter a title for the exam!"); toast.error("Enter a title for the exam!");
return; return;
@@ -74,7 +74,8 @@ const ReadingSettings: React.FC = () => {
category: localSettings.category, category: localSettings.category,
}; };
}), }),
isDiagnostic: true, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed. requiresApproval: requiresApproval,
isDiagnostic: requiresApproval ? true : false, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed.
minTimer, minTimer,
module: "reading", module: "reading",
id: title, id: title,

View File

@@ -84,7 +84,7 @@ const SpeakingSettings: React.FC = () => {
}); });
})(); })();
const submitSpeaking = async () => { const submitSpeaking = async (requiresApproval: boolean) => {
if (title === "") { if (title === "") {
toast.error("Enter a title for the exam!"); toast.error("Enter a title for the exam!");
return; return;
@@ -181,7 +181,8 @@ const SpeakingSettings: React.FC = () => {
minTimer, minTimer,
module: "speaking", module: "speaking",
id: title, id: title,
isDiagnostic: true, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed. requiresApproval: requiresApproval,
isDiagnostic: requiresApproval ? true : false, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed.
variant: undefined, variant: undefined,
difficulty, difficulty,
instructorGender: "varied", instructorGender: "varied",

View File

@@ -88,7 +88,7 @@ const WritingSettings: React.FC = () => {
openDetachedTab("popout?type=Exam&module=writing", router) openDetachedTab("popout?type=Exam&module=writing", router)
} }
const submitWriting = async () => { const submitWriting = async (requiresApproval: boolean) => {
if (title === "") { if (title === "") {
toast.error("Enter a title for the exam!"); toast.error("Enter a title for the exam!");
return; return;
@@ -131,7 +131,8 @@ const WritingSettings: React.FC = () => {
minTimer, minTimer,
module: "writing", module: "writing",
id: title, id: title,
isDiagnostic: true, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed. requiresApproval: requiresApproval,
isDiagnostic: requiresApproval ? true : false, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed.
variant: undefined, variant: undefined,
difficulty, difficulty,
access, access,

View File

@@ -28,6 +28,7 @@ export interface ExamBase {
createdAt?: string; // option as it has been added later createdAt?: string; // option as it has been added later
access: AccessType; access: AccessType;
label?: string; label?: string;
requiresApproval?: boolean;
} }
export interface ReadingExam extends ExamBase { export interface ReadingExam extends ExamBase {
module: "reading"; module: "reading";

View File

@@ -98,21 +98,25 @@ async function POST(req: NextApiRequest, res: NextApiResponse) {
// create workflow only if exam is being created for the first time // create workflow only if exam is being created for the first time
if (docSnap === null) { if (docSnap === null) {
try { try {
const { successCount, totalCount } = await createApprovalWorkflowOnExamCreation(exam.createdBy, exam.entities, exam.id, module); if (exam.requiresApproval === false) {
responseStatus = 200;
if (isAdmin(user)) { responseMessage = `Successfully created exam "${exam.id}" and skipped Approval Workflow due to user request.`;
} else if (isAdmin(user)) {
responseStatus = 200; responseStatus = 200;
responseMessage = `Successfully created exam "${exam.id}" and skipped Approval Workflow due to admin rights.`; responseMessage = `Successfully created exam "${exam.id}" and skipped Approval Workflow due to admin rights.`;
} else if (successCount === totalCount) {
responseStatus = 200;
responseMessage = `Successfully created exam "${exam.id}" and started its Approval Workflow.`;
/* responseMessage = `Successfully created exam "${exam.id}" and started its Approval Workflow(s).`; */
} else if (successCount > 0) {
responseStatus = 207;
responseMessage = `Successfully created exam with ID: "${exam.id}" but was not able to start/find an Approval Workflow for all the author's entities.`;
} else { } else {
responseStatus = 207; const { successCount, totalCount } = await createApprovalWorkflowOnExamCreation(exam.createdBy, exam.entities, exam.id, module);
responseMessage = `Successfully created exam with ID: "${exam.id}" but skipping approval process because no approval workflow was found configured for the exam author.`;
if (successCount === totalCount) {
responseStatus = 200;
responseMessage = `Successfully created exam "${exam.id}" and started its Approval Workflow.`;
} else if (successCount > 0) {
responseStatus = 207;
responseMessage = `Successfully created exam with ID: "${exam.id}" but was not able to start/find an Approval Workflow for all the author's entities.`;
} else {
responseStatus = 207;
responseMessage = `Successfully created exam with ID: "${exam.id}" but skipping approval process because no approval workflow was found configured for the exam author.`;
}
} }
} catch (error) { } catch (error) {
console.error("Workflow creation error:", error); console.error("Workflow creation error:", error);