Skip to content

Commit f6dcd31

Browse files
committed
feat: show error on question page
1 parent f2356b0 commit f6dcd31

File tree

3 files changed

+34
-14
lines changed

3 files changed

+34
-14
lines changed

frontend/src/components/question/QuestionCard.vue

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
<BCard :variant="cardVariant">
99
<BCardText>
1010
<!-- TODO: Show Question -->
11-
<CollapsibleCard v-if="isDetailedServerError(data)" variant="danger">
12-
<template #button-title>{{ data.error }}</template>
11+
<CollapsibleCard v-if="isDetailedServerError(error)" variant="danger">
12+
<template #button-title>{{ error.error }}</template>
1313
<p>The package could not parse the question state.</p>
1414
<code>
15-
<pre>{{ data.details }}</pre>
15+
<pre>{{ error.details }}</pre>
1616
</code>
1717
</CollapsibleCard>
18-
<pre v-else>{{ data }}</pre>
18+
<pre v-if="data">{{ data }}</pre>
1919
</BCardText>
2020
<ButtonGroup>
2121
<!-- TODO: Implement clone -->
@@ -59,9 +59,10 @@ import useAppStateStore from '@/stores/useAppStateStore'
5959
import { isDetailedServerError } from '@/types'
6060
import type { DetailedServerError, OptionsFormData } from '@/types'
6161
62-
const { questionId } = defineProps<{
62+
const { data, questionId } = defineProps<{
6363
questionId: string
64-
data: OptionsFormData | DetailedServerError
64+
data?: OptionsFormData
65+
error?: DetailedServerError
6566
}>()
6667
6768
const questionLocation = { name: 'question', params: { questionId } } as const

frontend/src/components/question/QuestionList.vue

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
<template #button-title>Saved questions ({{ questionCount }})</template>
1212
<InvalidQuestionStateError v-if="hasInvalidStates" class="mb-3" />
1313
<QuestionCard
14-
v-for="[questionId, data] in Object.entries(questions)"
14+
v-for="[questionId, question] in Object.entries(questions)"
1515
class="question-card"
1616
:id="`question-${questionId}`"
1717
:key="questionId"
1818
:questionId="questionId"
19-
:data="data"
19+
:data="question.data"
20+
:error="question.error"
2021
/>
2122
<BAlert v-if="questionCount === 0" :model-value="true" class="mb-0" variant="info"
2223
>This package has no questions yet.</BAlert
@@ -29,12 +30,23 @@ import { computed } from 'vue'
2930
3031
import { useQuestionStatesQuery } from '@/queries'
3132
import { isDetailedServerError } from '@/types'
33+
import type { DetailedServerError, OptionsFormData } from '@/types'
3234
3335
const { asyncStatus, error, data } = useQuestionStatesQuery()
3436
35-
const questions = computed(() => data.value ?? {})
37+
const questions = computed<Record<string, { data?: OptionsFormData; error?: DetailedServerError }>>(() => {
38+
if (data.value === undefined) {
39+
return {}
40+
}
41+
const entries = Object.entries(data.value).map(([questionId, value]) => [
42+
questionId,
43+
isDetailedServerError(value) ? { data: undefined, error: value } : { data: value, error: undefined },
44+
])
45+
return Object.fromEntries(entries)
46+
})
47+
3648
const questionCount = computed(() => Object.keys(questions.value).length)
37-
const hasInvalidStates = computed(() => Object.values(questions.value).some(isDetailedServerError))
49+
const hasInvalidStates = computed(() => Object.values(questions.value).some((q) => q.error))
3850
</script>
3951

4052
<style lang="scss" scoped>

frontend/src/pages/question/[questionId]/index.vue

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<IconButton :to="{ name: 'index' }" :icon-component="IMdiArrowLeft" class="ps-0 mb-2" variant="link"
99
>Back to Package Preview</IconButton
1010
>
11-
<QuestionCard class="mb-3" :question-id="params.questionId" :data="formData?.data ?? {}" />
11+
<QuestionCard class="mb-3" :question-id="params.questionId" :data="formData?.data" :error="detailedServerError" />
1212
<ButtonGroup class="mb-4">
1313
<IconButton :icon-component="IMdiImport" variant="link" @click="importAttempt">Import attempt</IconButton>
1414
<IconButton :icon-component="IMdiAdd" @click="createAttempt" variant="primary">New attempt</IconButton>
@@ -20,15 +20,16 @@
2020
import IMdiAdd from '~icons/mdi/add'
2121
import IMdiArrowLeft from '~icons/mdi/arrow-left'
2222
import IMdiImport from '~icons/mdi/import'
23-
import { watch } from 'vue'
23+
import { computed, watch } from 'vue'
2424
import { useRoute, useRouter } from 'vue-router'
2525
2626
import { useCreateAttempt } from '@/composables/attempt'
27-
import { useOptionsFormDataQuery } from '@/queries'
27+
import { FetchError, useOptionsFormDataQuery } from '@/queries'
28+
import type { DetailedServerError } from '@/types'
2829
2930
const route = useRouter()
3031
const { params } = useRoute('question')
31-
const { data: formData } = useOptionsFormDataQuery(params.questionId)
32+
const { data: formData, error } = useOptionsFormDataQuery(params.questionId)
3233
const createAttempt = useCreateAttempt(params.questionId)
3334
3435
// If question doesn't exist, show index instead
@@ -45,6 +46,12 @@ watch(
4546
function importAttempt() {
4647
// TODO: import question
4748
}
49+
50+
const detailedServerError = computed(() =>
51+
error.value instanceof FetchError && error.value.message === 'InvalidQuestionStateError'
52+
? ({ error: error.value.message, details: error.value.details } satisfies DetailedServerError)
53+
: undefined,
54+
)
4855
</script>
4956

5057
<route lang="json">

0 commit comments

Comments
 (0)