fixup result, add totaltime in exam, add todo in readme
This commit is contained in:
		
							parent
							
								
									2d3854a4fe
								
							
						
					
					
						commit
						cf868f7d65
					
				
					 8 changed files with 125 additions and 53 deletions
				
			
		
							
								
								
									
										12
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
										
									
									
									
								
							|  | @ -8,7 +8,17 @@ This project utilizes `pnpm`, thus it is recommended | ||||||
| pnpm install | pnpm install | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| More information about setting up database will come here later | # To-do: | ||||||
|  | 
 | ||||||
|  | - [ ] re-forge database structure, script for processing, share appropriate files | ||||||
|  | - [ ] choose category | ||||||
|  | - [ ] beautify website | ||||||
|  | - [ ] better answer click recognition | ||||||
|  | - [ ] come up with how to show results appropriately | ||||||
|  | - [ ] i18n - pl, en, de, ua (not all questions are not available in ua, api handle) | ||||||
|  | - [ ] exam (maybe also results?) warning leave message on exit (refresh) | ||||||
|  | - [ ] lazy loading | ||||||
|  | - [ ] question timers, and at end of total timer show a message for a while before immediatly navigating to results (maybe sth similar also when normally ending exam) | ||||||
| 
 | 
 | ||||||
| ## Development Server | ## Development Server | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,8 +15,19 @@ defineProps<{ | ||||||
|   <div |   <div | ||||||
|     class="select-none z-[-1] flex-1 flex items-stretch w-full justify-center *:object-contain" |     class="select-none z-[-1] flex-1 flex items-stretch w-full justify-center *:object-contain" | ||||||
|   > |   > | ||||||
|     <img :src="cdnUrl + media.ogName" alt="" v-if="media.fileType == 'jpg'" /> |     <!-- Reserved for getting to know the question (20s) in basic questions section | ||||||
|     <video v-else-if="media.fileType == 'wmv'" :key="media.fileName" autoplay> |     src="~/public/placeholder.svg" --> | ||||||
|  |     <img | ||||||
|  |       :src="cdnUrl + media.ogName" | ||||||
|  |       alt="" | ||||||
|  |       v-if="media.fileType == 'jpg'" | ||||||
|  |       :key="`${media.ogName}-photo`" | ||||||
|  |     /> | ||||||
|  |     <video | ||||||
|  |       v-else-if="media.fileType == 'wmv'" | ||||||
|  |       :key="`${media.ogName}-video`" | ||||||
|  |       autoplay | ||||||
|  |     > | ||||||
|       <source |       <source | ||||||
|         :src="cdnUrl + [media.fileName, 'mp4'].join('.')" |         :src="cdnUrl + [media.fileName, 'mp4'].join('.')" | ||||||
|         type="video/mp4" |         type="video/mp4" | ||||||
|  |  | ||||||
|  | @ -23,18 +23,21 @@ const emit = defineEmits<{ | ||||||
|   > |   > | ||||||
|     <h1 class="text-[1.5rem]">{{ title }}</h1> |     <h1 class="text-[1.5rem]">{{ title }}</h1> | ||||||
|     <div class="*:inline">Punkty: <slot name="points" /> / 74</div> |     <div class="*:inline">Punkty: <slot name="points" /> / 74</div> | ||||||
|     <div class="*:inline">Wynik: pozytywny/negatywny</div> |     <div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div> | ||||||
|     <div |     <div | ||||||
|       class="flex flex-row gap-2 *:py-1 *:px-3 *:border *:border-1 *:border-slate-300 *:rounded-md *:bg-slate-100" |       class="flex flex-row gap-2 *:py-1 *:px-3 *:border *:border-1 *:border-slate-300 *:rounded-md *:bg-slate-100" | ||||||
|     > |     > | ||||||
|       <button class="text-slate-600" @click="emit('homepage')"> |       <button | ||||||
|  |         class="!border-slate-200 text-slate-600" | ||||||
|  |         @click="emit('homepage')" | ||||||
|  |       > | ||||||
|         Wróć na stronę główną |         Wróć na stronę główną | ||||||
|       </button> |       </button> | ||||||
|       <button class="border-slate-200 bg-slate-50" @click="emit('newExam')"> |       <button class="!bg-slate-200 text-slate-800" @click="emit('newExam')"> | ||||||
|         Rozpocznij jeszcze raz |         Rozpocznij jeszcze raz | ||||||
|       </button> |       </button> | ||||||
|       <button |       <button | ||||||
|         class="!border-slate-500 !bg-slate-700 text-white" |         class="!border-2 !border-slate-200 !bg-slate-700 text-white" | ||||||
|         @click="emit('close')" |         @click="emit('close')" | ||||||
|       > |       > | ||||||
|         Przejrzyj odpowiedzi |         Przejrzyj odpowiedzi | ||||||
|  | @ -42,14 +45,3 @@ const emit = defineEmits<{ | ||||||
|     </div> |     </div> | ||||||
|   </VueFinalModal> |   </VueFinalModal> | ||||||
| </template> | </template> | ||||||
| 
 |  | ||||||
| <style> |  | ||||||
| .confirm-modal-content button { |  | ||||||
|   padding: 0 8px; |  | ||||||
|   border: 1px solid; |  | ||||||
|   border-radius: 0.5rem; |  | ||||||
| } |  | ||||||
| .dark .confirm-modal-content { |  | ||||||
|   background: #000; |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import * as _ from "lodash"; | import { range } from "lodash"; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
|   // result: ResultEndType; |   result: ResultEndType; | ||||||
|   countBasic: number; |   countBasic: number; | ||||||
|   countAdvanced: number; |   countAdvanced: number; | ||||||
|   question: BasicQuestion | AdvancedQuestion | undefined; |   question: BasicQuestion | AdvancedQuestion | undefined; | ||||||
|  | @ -13,34 +13,48 @@ const props = defineProps<{ | ||||||
| 
 | 
 | ||||||
| const isBasic = computed(() => props.now == "basic"); | const isBasic = computed(() => props.now == "basic"); | ||||||
| const isAdvanced = computed(() => props.now == "advanced"); | const isAdvanced = computed(() => props.now == "advanced"); | ||||||
|  | 
 | ||||||
|  | const boxesAmount = computed(() => { | ||||||
|  |   if (isBasic.value) { | ||||||
|  |     return 20 + 1; | ||||||
|  |   } else if (isAdvanced.value) { | ||||||
|  |     return 12 + 1; | ||||||
|  |   } else { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <div |   <div | ||||||
|     class="flex flex-col items-center p-4 gap-10 border-l border-slate-300 bg-slate-100" |     class="flex flex-col items-center p-4 gap-6 border-l border-slate-300 bg-slate-100" | ||||||
|   > |   > | ||||||
|     <div> |     <div> | ||||||
|       <button @click="$emit('end-exam')" class="btn-major"> |       <button @click="" class="btn-major">Wróć na stronę główną</button> | ||||||
|         Wróć na stronę główną |       <button @click="" class="btn-major">Rozpocznij jeszcze raz</button> | ||||||
|       </button> |  | ||||||
|       <button @click="$emit('end-exam')" class="btn-major"> |  | ||||||
|         Rozpocznij jeszcze raz |  | ||||||
|       </button> |  | ||||||
|     </div> |     </div> | ||||||
|     <div class="flex flex-row gap-6 *:flex-1 w-full"> |     <div class="flex flex-row gap-6 *:flex-1 w-full"> | ||||||
|       <button class="text-md text-white bg-blue-400">Pytania podstawowe</button> |       <button | ||||||
|       <button class="text-md text-white bg-blue-400"> |         class="text-md text-white bg-blue-400" | ||||||
|  |         @click="$emit('change-now', 'basic')" | ||||||
|  |       > | ||||||
|  |         Pytania podstawowe | ||||||
|  |       </button> | ||||||
|  |       <button | ||||||
|  |         class="text-md text-white bg-blue-400" | ||||||
|  |         @click="$emit('change-now', 'advanced')" | ||||||
|  |       > | ||||||
|         Pytania specjalistyczne |         Pytania specjalistyczne | ||||||
|       </button> |       </button> | ||||||
|     </div> |     </div> | ||||||
|     <div class="w-full flex justify-center items-center"> |     <div | ||||||
|       <div class="flex flex-row flex-wrap gap-2 items-stretch !w-max"> |       class="grid grid-cols-[repeat(auto-fit,50px)] gap-2 justify-around w-full" | ||||||
|         <div |     > | ||||||
|           v-for="num in _.range(1, 21)" |       <div | ||||||
|           class="p-1 bg-blue-500 text-white w-8 aspect-square text-center" |         v-for="num in range(1, boxesAmount)" | ||||||
|         > |         class="p-1 bg-blue-500 text-white text-center" | ||||||
|           {{ num }} |       > | ||||||
|         </div> |         {{ num }} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <div class="flex flex-row gap-6 *:flex-1 w-full"> |     <div class="flex flex-row gap-6 *:flex-1 w-full"> | ||||||
|  | @ -51,7 +65,10 @@ const isAdvanced = computed(() => props.now == "advanced"); | ||||||
|         Pokaż poprawną odpowiedź |         Pokaż poprawną odpowiedź | ||||||
|       </button> |       </button> | ||||||
|     </div> |     </div> | ||||||
|  |     <div> | ||||||
|  |       Poprawna odpowiedź | ||||||
|  |       <br /> | ||||||
|  |       Zaznaczona odpowiedź | ||||||
|  |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 |  | ||||||
| <!-- <style></style> --> |  | ||||||
|  |  | ||||||
|  | @ -1,9 +1,20 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| defineProps<{ | import type { Duration } from "date-fns"; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|   points: number | undefined; |   points: number | undefined; | ||||||
|   category: string | undefined; |   category: string | undefined; | ||||||
|   timeRemaining?: string | undefined; |   timeRemaining?: Duration | undefined; | ||||||
| }>(); | }>(); | ||||||
|  | 
 | ||||||
|  | const timeRemainingFriendly = computed(() => { | ||||||
|  |   if (typeof props.timeRemaining !== "undefined") { | ||||||
|  |     let seconds = props.timeRemaining.seconds ?? 0; | ||||||
|  |     return `${props.timeRemaining.minutes ?? 0}:${ | ||||||
|  |       seconds >= 0 && seconds < 10 ? 0 : "" | ||||||
|  |     }${seconds ?? 0}`; | ||||||
|  |   } | ||||||
|  | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -20,9 +31,11 @@ defineProps<{ | ||||||
|       <span class="block">Aktualna kategoria (implement)</span> |       <span class="block">Aktualna kategoria (implement)</span> | ||||||
|       <div class="info-little-box">{{ category }}</div> |       <div class="info-little-box">{{ category }}</div> | ||||||
|     </div> |     </div> | ||||||
|     <div v-if="typeof timeRemaining === 'string'"> |     <div v-if="typeof timeRemaining !== 'undefined'"> | ||||||
|       <span class="block">Czas do końca egzaminu (implement)</span> |       <span class="block">Czas do końca egzaminu</span> | ||||||
|       <div class="info-little-box">{{ timeRemaining }}</div> |       <div class="info-little-box w-18 text-center"> | ||||||
|  |         {{ timeRemainingFriendly }} | ||||||
|  |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -6,6 +6,29 @@ definePageMeta({ | ||||||
| import "7.css/dist/7.scoped.css"; | import "7.css/dist/7.scoped.css"; | ||||||
| import { useExamStore } from "~/store/examResults"; | import { useExamStore } from "~/store/examResults"; | ||||||
| 
 | 
 | ||||||
|  | import { intervalToDuration, addMinutes, addSeconds, isEqual } from "date-fns"; | ||||||
|  | 
 | ||||||
|  | const nowTime = ref(new Date()); | ||||||
|  | const timeEnd = addMinutes(new Date(), 25); | ||||||
|  | 
 | ||||||
|  | const timeRemainingTotal = computed(() => | ||||||
|  |   intervalToDuration({ | ||||||
|  |     start: nowTime.value, | ||||||
|  |     end: timeEnd, | ||||||
|  |   }) | ||||||
|  | ); | ||||||
|  | // const timeRemainingQuestion - to implement | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   const endInterval = setInterval(() => { | ||||||
|  |     nowTime.value = addSeconds(nowTime.value, 1); | ||||||
|  |     if (isEqual(nowTime.value, timeEnd)) { | ||||||
|  |       clearInterval(endInterval); | ||||||
|  |       endExam(); | ||||||
|  |     } | ||||||
|  |   }, 1000); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| const examStore = useExamStore(); | const examStore = useExamStore(); | ||||||
| 
 | 
 | ||||||
| examStore.resetExam(); | examStore.resetExam(); | ||||||
|  | @ -90,7 +113,7 @@ function endExam() { | ||||||
|   next(); |   next(); | ||||||
|   examStore.result = result.value; |   examStore.result = result.value; | ||||||
|   examStore.end = true; |   examStore.end = true; | ||||||
|   return navigateTo("/result"); |   return navigateTo("/result", { replace: true }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const questionBasic = computed<BasicQuestion | undefined>(() => | const questionBasic = computed<BasicQuestion | undefined>(() => | ||||||
|  | @ -133,7 +156,7 @@ const result: Ref<ResultEndType> = ref({ | ||||||
|           <TopBar |           <TopBar | ||||||
|             :points="question?.liczba_pkt" |             :points="question?.liczba_pkt" | ||||||
|             :category="`B`" |             :category="`B`" | ||||||
|             :time-remaining="`25:00`" |             :time-remaining="timeRemainingTotal" | ||||||
|           /> |           /> | ||||||
|           <Media :media="media" /> |           <Media :media="media" /> | ||||||
|           <BasicQuestionBlock |           <BasicQuestionBlock | ||||||
|  |  | ||||||
|  | @ -8,26 +8,26 @@ definePageMeta({ | ||||||
| 
 | 
 | ||||||
| const examStore = useExamStore(); | const examStore = useExamStore(); | ||||||
| 
 | 
 | ||||||
| const points = ref<number>(); | const points = ref<number>(0); | ||||||
| 
 | 
 | ||||||
| if (!examStore.end) { | if (!examStore.end) { | ||||||
|   examStore.resetExam(); |   examStore.resetExam(); | ||||||
|   await navigateTo("/"); |   await navigateTo("/"); | ||||||
| } else { | } else { | ||||||
|   let sum = 0; |  | ||||||
|   examStore.result.basic.forEach((answer) => { |   examStore.result.basic.forEach((answer) => { | ||||||
|     if (answer.chosen_is_correct) { |     if (answer.chosen_is_correct) { | ||||||
|       sum += answer.question?.liczba_pkt ?? 0; |       points.value += answer.question?.liczba_pkt ?? 0; | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|   examStore.result.advanced.forEach((answer) => { |   examStore.result.advanced.forEach((answer) => { | ||||||
|     if (answer.chosen_is_correct) { |     if (answer.chosen_is_correct) { | ||||||
|       sum += answer.question?.liczba_pkt ?? 0; |       points.value += answer.question?.liczba_pkt ?? 0; | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|   points.value = sum; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const resultTrueFalse = ref(points.value >= 68 ? "pozytywny" : "negatywny"); | ||||||
|  | 
 | ||||||
| const countBasic = ref(0); | const countBasic = ref(0); | ||||||
| const countAdvanced = ref(0); | const countAdvanced = ref(0); | ||||||
| 
 | 
 | ||||||
|  | @ -89,9 +89,14 @@ const { open, close } = useModal({ | ||||||
|   }, |   }, | ||||||
|   slots: { |   slots: { | ||||||
|     points: `${points.value}`, |     points: `${points.value}`, | ||||||
|  |     resultTrueFalse: resultTrueFalse, | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
| open(); | open(); | ||||||
|  | 
 | ||||||
|  | function changeNow(to: string) { | ||||||
|  |   now.value = to; | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -117,15 +122,15 @@ open(); | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <RightBarResult |         <RightBarResult | ||||||
|  |           :result="examStore.result" | ||||||
|           :question="question" |           :question="question" | ||||||
|           :question-basic="questionBasic" |           :question-basic="questionBasic" | ||||||
|           :question-advanced="questionAdvanced" |           :question-advanced="questionAdvanced" | ||||||
|           :count-basic="countBasic" |           :count-basic="countBasic" | ||||||
|           :count-advanced="countAdvanced" |           :count-advanced="countAdvanced" | ||||||
|           :now="now" |           :now="now" | ||||||
|  |           @change-now="changeNow" | ||||||
|         /> |         /> | ||||||
|         <!-- :result="result" --> |  | ||||||
|         <!-- @next-question="next()" --> |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								public/placeholder.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								public/placeholder.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" width="680.764" height="528.354" viewBox="0 0 180.119 139.794"><g transform="translate(-13.59 -66.639)" paint-order="fill markers stroke"><path fill="#d0d0d0" d="M13.591 66.639H193.71v139.794H13.591z"/><path d="m118.507 133.514-34.249 34.249-15.968-15.968-41.938 41.937H178.726z" opacity=".675" fill="#fff"/><circle cx="58.217" cy="108.555" r="11.773" opacity=".675" fill="#fff"/><path fill="none" d="M26.111 77.634h152.614v116.099H26.111z"/></g></svg> | ||||||
| After Width: | Height: | Size: 492 B | 
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 NetMan
						NetMan