Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a72885e
feat: add scale question type to quiz functionality
saadman30 Feb 17, 2026
90bdc00
Merge branch '4.0.0-dev' into feat/quiz-type-scale
saadman30 Mar 13, 2026
ec27e0c
feat: Add Scale question type support in quiz templates
saadman30 Mar 13, 2026
2c31446
feat: Add hand swipe right icon and update scale question instructions
saadman30 Mar 16, 2026
06bba5a
fix: Update step input for FormScale component to use integer values
saadman30 Mar 16, 2026
a7ac2ca
feat: Enhance quiz attempt details with improved scale question UI an…
saadman30 Mar 16, 2026
c84e990
feat: Add support for scale question type in quiz reveal mode and enh…
saadman30 Mar 17, 2026
7b89522
feat: Implement scale context decoding for quiz reveal mode and updat…
saadman30 Mar 17, 2026
6a6799e
Merge branch '4.0.0-dev' into feat/quiz-type-scale
saadman30 Apr 2, 2026
9828b17
Merge branch 'feat/quiz-type-pin-image' into feat/quiz-type-scale
saadman30 Apr 2, 2026
57a11c2
Enhance legacy learning mode handling for question types
saadman30 Apr 2, 2026
b340e17
Merge branch 'feat/quiz-type-pin-image' into feat/quiz-type-scale
saadman30 Apr 3, 2026
d884ab0
Remove Scale question template files and update Quiz class to include…
saadman30 Apr 3, 2026
b803b85
Update visibility handling for scale reference in spotlight quiz
saadman30 Apr 3, 2026
84f01af
Enhance quiz layout and attempt UX across tutor frontend.
saadman30 Apr 3, 2026
2b07c80
fix(quiz): update quiz options reference in spotlight quiz script
saadman30 Apr 3, 2026
1c7e533
feat(quiz): add action hook before question wrapper closes in attempt…
saadman30 Apr 3, 2026
d7f414c
fix(quiz): add missing newline at end of spotlight quiz script
saadman30 Apr 3, 2026
b9b7496
refactor(quiz): remove scale question handling from quiz logic
saadman30 Apr 3, 2026
77e6680
fix(quiz): prevent revealing answers when no answer IDs are available
saadman30 Apr 3, 2026
b359556
fix(quiz): streamline answer reveal logic in quiz layout
saadman30 Apr 3, 2026
04efefd
refactor(quiz): update quiz attempt details styles and remove unused …
saadman30 Apr 3, 2026
59b6ce2
refactor(quiz): clean up quiz attempt details styles by removing redu…
saadman30 Apr 3, 2026
a11baed
refactor(quiz): simplify quiz body logic by removing unused scale que…
saadman30 Apr 3, 2026
80d10ab
fix(quiz): correct answer reveal logic for supported question types
saadman30 Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions assets/icons/hand-swipe-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion assets/src/js/frontend/dashboard/pages/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ interface ResetPasswordResponse {
const settings = () => {
const query = window.TutorCore.query;
const form = window.TutorCore.form;
const modal = window.TutorCore.modal;
const toast = window.TutorCore.toast;

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const questionTypeIconMap: Record<Exclude<QuizQuestionType, 'single_choice' | 'i
image_answering: 'quizImageAnswer',
ordering: 'quizOrdering',
draw_image: 'quizImageAnswer',
scale: 'quizImageAnswer',
pin_image: 'quizImageAnswer',
h5p: 'quizH5p',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const questionTypes = {
label: __('Draw on Image', 'tutor'),
icon: 'quizImageAnswer',
},
scale: {
label: __('Scale', 'tutor'),
icon: 'quizImageAnswer',
},
pin_image: {
label: __('Pin on Image', 'tutor'),
icon: 'quizImageAnswer',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import MultipleChoiceAndOrdering from '@CourseBuilderComponents/curriculum/quest
import OpenEndedAndShortAnswer from '@CourseBuilderComponents/curriculum/question-types/OpenEndedAndShortAnswer';
import TrueFalse from '@CourseBuilderComponents/curriculum/question-types/TrueFalse';
import DrawImage from '@CourseBuilderComponents/curriculum/question-types/DrawImage';
import Scale from '@CourseBuilderComponents/curriculum/question-types/Scale';
import PinImage from '@CourseBuilderComponents/curriculum/question-types/PinImage';
import { useQuizModalContext } from '@CourseBuilderContexts/QuizModalContext';

Expand Down Expand Up @@ -57,6 +58,7 @@ const QuestionForm = () => {
image_answering: <ImageAnswering key={activeQuestionId} />,
ordering: <MultipleChoiceAndOrdering key={activeQuestionId} />,
draw_image: <DrawImage key={activeQuestionId} />,
scale: <Scale key={activeQuestionId} />,
pin_image: <PinImage key={activeQuestionId} />,
} as const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ const questionTypeOptions: {
icon: 'quizImageAnswer',
isPro: true,
},
{
label: __('Scale', 'tutor'),
value: 'scale',
icon: 'quizImageAnswer',
isPro: true,
},
{
label: __('Pin on Image', 'tutor'),
value: 'pin_image',
Expand All @@ -124,7 +130,9 @@ const isTutorPro = !!tutorConfig.tutor_pro_url;
const QuestionList = ({ isEditing }: { isEditing: boolean }) => {
const questionTypeOptionsForUi = useMemo(() => {
if (tutorConfig.is_legacy_learning_mode) {
return questionTypeOptions.filter((option) => option.value !== 'draw_image' && option.value !== 'pin_image');
return questionTypeOptions.filter(
(option) => option.value !== 'draw_image' && option.value !== 'pin_image' && option.value !== 'scale',
);
}
return questionTypeOptions;
}, []);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { css } from '@emotion/react';
import { useEffect } from 'react';
import { Controller, useFieldArray, useFormContext } from 'react-hook-form';

import { useQuizModalContext } from '@CourseBuilderContexts/QuizModalContext';
import type { QuizForm } from '@CourseBuilderServices/quiz';
import FormScale from '@TutorShared/components/fields/quiz/questions/FormScale';
import { spacing } from '@TutorShared/config/styles';
import { styleUtils } from '@TutorShared/utils/style-utils';
import { QuizDataStatus, type QuizQuestionOption } from '@TutorShared/utils/types';
import { nanoid } from '@TutorShared/utils/util';

const Scale = () => {
const form = useFormContext<QuizForm>();
const { activeQuestionId, activeQuestionIndex, validationError, setValidationError } = useQuizModalContext();

const answersPath = `questions.${activeQuestionIndex}.question_answers` as 'questions.0.question_answers';

const { fields: optionsFields } = useFieldArray({
control: form.control,
name: answersPath,
});

useEffect(() => {
if (!activeQuestionId) {
return;
}
if (optionsFields.length > 0) {
return;
}
const baseAnswer: QuizQuestionOption = {
_data_status: QuizDataStatus.NEW,
// Treat the initial default configuration as already saved so that
// validation doesn’t block adding another question when the instructor
// hasn’t interacted with the scale form yet.
is_saved: true,
answer_id: nanoid(),
belongs_question_id: activeQuestionId,
belongs_question_type: 'scale' as QuizQuestionOption['belongs_question_type'],
answer_title: '',
is_correct: '1',
image_id: undefined,
image_url: '',
answer_two_gap_match: JSON.stringify({
value: 50,
config: {
min: 0,
max: 100,
step: 1,
defaultValue: 50,
pxPerUnit: 10,
labelEvery: 10,
minorTickEvery: 5,
precision: 0,
},
}),
answer_view_format: 'scale',
answer_order: 0,
};
form.setValue(answersPath, [baseAnswer]);
}, [activeQuestionId, optionsFields.length, answersPath, form]);

if (optionsFields.length === 0) {
return null;
}

return (
<div css={styles.optionWrapper}>
<Controller
key={JSON.stringify(optionsFields[0])}
control={form.control}
name={`questions.${activeQuestionIndex}.question_answers.0` as 'questions.0.question_answers.0'}
render={(controllerProps) => (
<FormScale
{...controllerProps}
questionId={activeQuestionId}
validationError={validationError}
setValidationError={setValidationError}
/>
)}
/>
</div>
);
};

export default Scale;

const styles = {
optionWrapper: css`
${styleUtils.display.flex('column')};
padding-left: ${spacing[40]};
`,
};
Loading
Loading