Skip to content

Commit

Permalink
Merge pull request #166 from avantifellows/feat/omr-mode-update
Browse files Browse the repository at this point in the history
Switch between OMR and Assessment Modes
  • Loading branch information
suryabulusu authored Nov 25, 2024
2 parents de286dc + 0340d14 commit f7f6bbf
Show file tree
Hide file tree
Showing 17 changed files with 205 additions and 38 deletions.
24 changes: 13 additions & 11 deletions src/components/Omr/OmrModal.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col bg-white w-full h-full overflow-auto justify-between">
<Header class="fixed top-0" v-if="isQuizAssessment" :hasQuizEnded="hasQuizEnded"
:hasTimeLimit="quizTimeLimit != null" :title="title" :userId="userId" :isOmrMode="isOmrMode"
:hasTimeLimit="quizTimeLimit != null" :title="title" :userId="userId" :isOmrMode=true
:isSessionAnswerRequestProcessing="isSessionAnswerRequestProcessing" v-model:isPaletteVisible="isPaletteVisible"
:timeRemaining="timeRemaining" :warningTimeLimit="timeLimitWarningThreshold"
@time-limit-warning="displayTimeLimitWarning" @end-test="endTest" @end-test-by-time="endTestByTime"
Expand All @@ -12,7 +12,7 @@
<div class="flex grow flex-col w-full h-full overflow-y-auto">
<QuestionPalette v-if="isPaletteVisible" :hasQuizEnded="hasQuizEnded" :questionSetStates="questionSetStates"
:currentQuestionIndex="currentQuestionIndex" :title="title" :subject="subject" :testFormat="testFormat"
:maxMarks="maxMarks" :numQuestions="numQuestions" :quizTimeLimit="quizTimeLimit" :isOmrMode="isOmrMode"
:maxMarks="maxMarks" :numQuestions="numQuestions" :quizTimeLimit="quizTimeLimit" :isOmrMode=true
class="absolute w-full h-full sm:w-2/3 lg:w-1/2 xl:w-1/3 z-10 bg-white overflow-y-scroll pb-[56px]"
data-test="questionPalette">
</QuestionPalette>
Expand All @@ -31,12 +31,18 @@
:questionType="$props.questions[questionState.index].type"
:isGradedQuestion="$props.questions[questionState.index].graded"
:maxCharLimit="$props.questions[questionState.index].max_char_limit"
:matrixSize="$props.questions[questionState.index].matrix_size" :isPortrait="isPortrait"
:quizType="quizType" :hasQuizEnded="hasQuizEnded" :submittedAnswer="draftResponses[questionState.index]"
:matrixSize="$props.questions[questionState.index].matrix_size"
:isPortrait="isPortrait"
:quizType="quizType"
:hasQuizEnded="hasQuizEnded"
:submittedAnswer="draftResponses[questionState.index]"
:isQuestionDisabled="questionDisabledArray[questionState.index]"
:currentQuestionIndex="questionState.index" @option-selected="questionOptionSelected"
@subjective-answer-entered="subjectiveAnswerUpdated" @numerical-answer-entered="numericalAnswerUpdated"
:key="questionState.index" :data-test="`OmrItem-${questionState.index}`"
:currentQuestionIndex="questionState.index"
@option-selected="questionOptionSelected"
@subjective-answer-entered="subjectiveAnswerUpdated"
@numerical-answer-entered="numericalAnswerUpdated"
:key="questionState.index"
:data-test="`OmrItem-${questionState.index}`"
:ref="`omritem-${questionState.index}`"></OmrItem>
</div>
</div>
Expand Down Expand Up @@ -147,10 +153,6 @@ export default defineComponent({
type: Number,
default: 0
},
isOmrMode: {
type: Boolean,
default: false,
},
userId: {
type: String,
default: ""
Expand Down
2 changes: 1 addition & 1 deletion src/components/Questions/Footer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export default defineComponent({
const isQuizAssessment = computed(() => props.quizType == "assessment" || props.quizType == "omr-assessment");
const isSmallScreen = ref(false);
const updateScreenSize = () => {
isSmallScreen.value = window.matchMedia("(max-width: 500px)").matches;
isSmallScreen.value = window.matchMedia("(max-width: 560px)").matches;
};
const state = reactive({
assessmentNavigationButtonIconClass: [
Expand Down
91 changes: 87 additions & 4 deletions src/components/Questions/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
data-test="togglePaletteButton"
></icon-button>
<div class="flex space-x-3">
<!-- toggle omr button -->
<icon-button
v-if="shouldShowOmrToggle"
:titleConfig="toggleButtonTextConfig"
:iconConfig="toggleButtonIconConfig"
:buttonClass="toggleButtonIconClass"
class="rounded-2xl shadow-lg"
data-test="toggleOmrMode"
@click="toggleOmrMode"
></icon-button>
<!-- countdown timer / can't click -->
<icon-button
v-if="!hasQuizEnded && hasTimeLimit"
Expand Down Expand Up @@ -47,8 +57,8 @@

<script lang="ts">
import IconButton from "../UI/Buttons/IconButton.vue";
import { defineComponent, reactive, toRefs, computed, watch, onMounted, PropType } from "vue";
import { quizTitleType } from "@/types";
import { ref, defineComponent, reactive, toRefs, computed, watch, onMounted, onBeforeUnmount, PropType } from "vue";
import { quizTitleType, quizType } from "@/types";
export default defineComponent({
name: "Header",
Expand Down Expand Up @@ -92,7 +102,11 @@ export default defineComponent({
isOmrMode: {
type: Boolean,
default: false,
}
},
quizType: {
type: String as PropType<quizType>,
default: "homework"
},
},
setup(props, context) {
const state = reactive({
Expand All @@ -105,6 +119,10 @@ export default defineComponent({
"bg-red-600 ring-red-500 p-2 px-4 bp-500:p-4 bp-500:px-6 rounded-lg sm:rounded-2xl shadow-xl hover:cursor-default",
timeRemaining: props.timeRemaining
});
const isSmallScreen = ref(false);
const updateScreenSize = () => {
isSmallScreen.value = window.matchMedia("(max-width: 560px)").matches;
};
onMounted(() => {
window.setInterval(() => {
Expand All @@ -121,6 +139,56 @@ export default defineComponent({
state.localIsPaletteVisible = !state.localIsPaletteVisible;
}
// repetitive code - make a component later
function toggleOmrMode() {
const url = new URL(window.location.href);
if (url.searchParams.has("omrMode")) {
url.searchParams.delete("omrMode");
} else {
url.searchParams.set("omrMode", "true");
}
window.location.href = url.toString();
}
// const shouldShowOmrToggle = computed(() => props.quizType == "assessment")
const shouldShowOmrToggle = computed(() => false)
const toggleButtonTextConfig = computed(() => {
const config = {
value: "",
class: ["text-orange-500 underline underline-offset-8", "text-base md:text-lg lg:text-xl font-semibold"],
};
if (props.isOmrMode) {
config.value = isSmallScreen.value ? "A" : "Switch to Assessment Mode";
} else {
config.value = isSmallScreen.value ? "O" : "Switch to OMR Mode";
}
return config;
});
const toggleButtonIconClass = computed(() => {
const iconClass = [
"border border-orange-300",
"bg-transparent hover:bg-gray-100",
"rounded-lg shadow-sm px-6 py-3",
"flex items-center justify-center",
"transition-all duration-200 ease-in-out"
]
return iconClass;
});
const toggleButtonIconConfig = computed(() => {
return {
iconName: props.isOmrMode ? "check-circle-solid" : "sync-alt-solid",
iconClass: "h-4 w-4 text-white",
};
});
const endTestButtonTitleConfig = computed(() => ({
value: props.hasQuizEnded ? "See Results" : "End Test",
class:
Expand Down Expand Up @@ -216,6 +284,16 @@ export default defineComponent({
}
);
// Setup listeners for screen size changes
onMounted(() => {
updateScreenSize();
window.addEventListener("resize", updateScreenSize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", updateScreenSize);
});
return {
...toRefs(state),
endTest,
Expand All @@ -225,7 +303,12 @@ export default defineComponent({
togglePaletteButtonIconConfig,
togglePaletteButtonClass,
countdownTimerClass,
countdownTimerTitleConfig
countdownTimerTitleConfig,
shouldShowOmrToggle,
toggleButtonIconClass,
toggleButtonIconConfig,
toggleButtonTextConfig,
toggleOmrMode
};
},
components: {
Expand Down
11 changes: 0 additions & 11 deletions src/components/Questions/Palette/QuestionPalette.vue
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,6 @@ export default defineComponent({
`w-1/2 font-bold text-white p-2 px-4 bp-500:p-4 bp-500:px-6 rounded-lg sm:rounded-2xl shadow-xl border shadow-lg ring-primary`,
]);
// const state = reactive({
// instructionTextClass:
// "text-lg md:text-xl lg:text-2xl mx-4 mt-2 leading-none text-slate-500",
// titleTextClass:
// "text-lg md:text-xl lg:text-2xl mx-4 mt-10 font-bold leading-tight whitespace-pre-wrap",
// instructionsButtonClass:
// "bg-gray-300 w-full font-bold ring-gray-500 p-2 px-4 bp-500:p-4 bp-500:px-6 rounded-lg sm:rounded-2xl shadow-xl",
// showInstructions: false,
// showPalette: true,
// });
return {
...state,
navigateToQuestion,
Expand Down
1 change: 1 addition & 0 deletions src/components/Questions/QuestionModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:warningTimeLimit="timeLimitWarningThreshold"
:title="title"
:userId="userId"
:quizType="quizType"
@time-limit-warning="displayTimeLimitWarning"
@end-test="endTest"
@end-test-by-time="endTestByTime"
Expand Down
1 change: 1 addition & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const routes = [
...route.params,
userId: route.query.userId,
apiKey: route.query.apiKey,
omrMode: route.query.omrMode
}),
// lazy-loading: https://router.vuejs.org/guide/advanced/lazy-loading.html
component: () =>
Expand Down
7 changes: 5 additions & 2 deletions src/services/API/Quiz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ export default {
/**
* returns the details for a quiz
* @param {string} quizId - uuid of the quiz to be fetched
* @param {boolean} omrMode - whether quiz should be displayed in omr mode
* @returns {Promise<QuizAPIResponse>} data corresponding to the quiz
*/
async getQuiz(quizId: string): Promise<QuizAPIResponse> {
const response = await apiClient().get(quizEndpoint + quizId);
async getQuiz({ quizId, omrMode = false }: { quizId: string, omrMode: boolean }): Promise<QuizAPIResponse> {
const response = await apiClient().get(quizEndpoint + quizId, {
params: { omr_mode: omrMode }
});
return response.data;
},
};
6 changes: 4 additions & 2 deletions src/services/API/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,24 @@ import {
QuestionSetMetricPayload,
SessionMetricsPayload
} from "@/types";
import axios, { AxiosError } from "axios";

export default {
/**
* returns the details for a quiz
* @param {string} quizId - id of the quiz for which the session is to be created
* @param {string} userId - id of the user for which the session is to be created
* @param {boolean} omrMode - whether the session is being accessed in omrMode
* @returns {Promise<SessionAPIResponse>} data corresponding to the session
*/
async createSession(
quizId: string,
userId: string
userId: string,
omrMode: boolean = false
): Promise<SessionAPIResponse> {
const response = await apiClient().post(sessionsEndpoint, {
user_id: userId,
quiz_id: quizId,
omr_mode: omrMode
});
return response.data;
},
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export interface SessionAPIResponse {
user_id: string;
quiz_id: string;
is_first: boolean;
omr_mode: boolean;
session_answers: SubmittedResponse[];
has_quiz_ended: boolean;
time_remaining?: number;
Expand Down
Loading

0 comments on commit f7f6bbf

Please sign in to comment.