Skip to content

Commit

Permalink
feat: charts
Browse files Browse the repository at this point in the history
  • Loading branch information
qin-guan committed Jul 14, 2023
1 parent d1d318c commit f15961e
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 21 deletions.
47 changes: 39 additions & 8 deletions pages/dashboard/surveys/[id]/analytics.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script setup lang="ts">
import RadioButton from 'primevue/radiobutton'
import Dialog from 'primevue/dialog'
import TabView from 'primevue/tabview'
import TabPanel from 'primevue/tabpanel'
import DataTable from 'primevue/datatable'
import Button from 'primevue/button'
import Textarea from 'primevue/textarea'
import Column from 'primevue/column'
import Chart from 'primevue/chart'
import RadioButton from 'primevue/radiobutton'
const route = useRoute()
const { $client } = useNuxtApp()
Expand All @@ -15,12 +16,9 @@ const responsePreview = reactive({
visible: false,
idx: -1,
})
const { data } = await $client.analytics.listResponses.useQuery({ id: route.params.id as string })
// const qnData = computed(() => {
// return data.value.map((d) => d.data).reduce((acc, curr) => {
// }, [])
// })
const { data } = await $client.analytics.listResponses.useQuery({ id: route.params.id as string })
const { data: allChartData } = await $client.analytics.chartResponses.useQuery({ id: route.params.id as string })
</script>

<template>
Expand All @@ -29,7 +27,13 @@ const { data } = await $client.analytics.listResponses.useQuery({ id: route.para
<div flex flex-col divide-y divide-gray>
<div v-for="(question, idx) in data.schema" :key="idx" flex flex-col gap3 py6>
<span font-semibold>{{ question.title }}</span>
<div>

<div
v-if="
// @ts-expect-error This exists
data.responses[responsePreview.idx].data[idx]
"
>
<Textarea
v-if="question.type === 'text'" disabled :value="
// @ts-expect-error Answer exists on text type
Expand All @@ -51,6 +55,11 @@ const { data } = await $client.analytics.listResponses.useQuery({ id: route.para
</div>
</div>
</div>
<div v-else>
<span text-red-600>
No response as this question is new!
</span>
</div>
</div>
</div>
</Dialog>
Expand All @@ -77,7 +86,29 @@ const { data } = await $client.analytics.listResponses.useQuery({ id: route.para
</DataTable>
</TabPanel>
<TabPanel header="Charts">
<div />
<div flex flex-col gap6>
<div
v-for="(qnChartData, idx) in allChartData"
:key="idx"
>
<div
v-if="qnChartData.labels.length > 0"
>
<span font-semibold>
{{ data.schema[idx].title }}
</span>
<Chart
type="bar" :data="qnChartData" :options="{
scales: {
y: {
beginAtZero: true,
},
},
}"
/>
</div>
</div>
</div>
</TabPanel>
</TabView>
</main>
Expand Down
3 changes: 1 addition & 2 deletions server/trpc/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { inferAsyncReturnType } from '@trpc/server'
import type { H3Event } from 'h3'
import { logger } from '../utils/logger'
import { createSessionContext } from '../utils/session'

/**
Expand All @@ -9,7 +8,7 @@ import { createSessionContext } from '../utils/session'
*/
export function createContext(_event: H3Event) {
// TODO better logging for this
logger(_event.node.req, _event.node.res)
// logger(_event.node.req, _event.node.res)

const session = createSessionContext(_event)

Expand Down
71 changes: 60 additions & 11 deletions server/trpc/routers/analytics/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { protectedProcedure, router } from '../../trpc'
import { surveySchema } from '../../../../shared/survey'
import type { SurveyResponseSchema, SurveySchema } from '../../../../shared/survey'

// TODO probably need a better name for all the functions here
export const analyticsRouter = router({
myResponses: protectedProcedure.query(async ({ ctx }) => {
return await ctx.prisma.survey.findMany({
Expand Down Expand Up @@ -33,17 +33,66 @@ export const analyticsRouter = router({
},
})

const result = await surveySchema.safeParseAsync(data.schema)
if (!result.success) {
throw new TRPCError({
code: 'BAD_REQUEST',
cause: result.error,
})
}

return {
...data,
schema: result.data,
schema: data.schema as SurveySchema,
}
}),
chartResponses: protectedProcedure.input(
z.object({
id: z.string(),
}),
).query(async ({ ctx, input }) => {
const [responses, survey] = await Promise.all([ctx.prisma.response.findMany({
where: {
surveyId: input.id,
},
}), ctx.prisma.survey.findUniqueOrThrow({
where: {
id: input.id,
},
})])

const surveySchema = survey.schema as SurveySchema

/**
* [
* { // Question 1
* "A label": 10,
* "Another label": 20,
* }
* ]
*/
const labels: (Record<string, number>)[] = Array.from({ length: surveySchema.length }).map(() => ({}))

for (const idx in responses) { // For every response
const responseData = responses[idx].data as SurveyResponseSchema

for (const qnIdx in responseData) { // For every question in response
const qnData = responseData[qnIdx]

if (qnData.type === 'mcq') {
const textOption = surveySchema[qnIdx].options[qnData.option]
if (!labels[qnIdx][textOption])
labels[qnIdx][textOption] = 0

labels[qnIdx][textOption]++
}
}
}

const chartData = labels.map((qn) => {
return {
labels: Object.keys(qn),
datasets: [
{
label: 'Responses',
data: Object.values(qn),
},
],
}
})

return chartData
}),
})
1 change: 1 addition & 0 deletions shared/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export const surveyResponseSchema = z.array(questionResponseSchema)

export type QuestionResponseSchema = z.infer<typeof questionResponseSchema>
export type SurveyResponseSchema = z.infer<typeof surveyResponseSchema>
export type SurveySchema = z.infer<typeof surveySchema>

0 comments on commit f15961e

Please sign in to comment.