Add HorizontalBar component

This commit is contained in:
Christian Cueni 2023-01-30 09:36:29 +01:00
parent b7038c1a9c
commit 2ab8f580bc
5 changed files with 72 additions and 44 deletions

View File

@ -1,24 +1,42 @@
<template>
<QuestionSummary :title="props.title" :text="props.text">
<h5 class="mb-6 text-base">{{ props.items.length }} {{ $t("feedback.answers") }}</h5>
<div
v-for="{ label, percentage } in props.items"
v-for="{ label, percentage } in chartItems"
:key="label"
class="mb-6 flex flex-row flex-wrap items-center gap-3 gap-y-2"
>
<Popover class="relative w-full">
<PopoverButton class="focus:outline-none">
<div class="w-full text-base font-bold">{{ label }}</div>
</PopoverButton>
<PopoverPanel
class="absolute top-[-200%] z-10 w-[120px] border border-gray-500 bg-white p-1 text-left text-sm font-normal"
>
<p>
{{
`"${label}" ${percentage * props.items.length} ${$t(
"feedback.answers"
)}`
}}
</p>
</PopoverPanel>
</Popover>
<div
class="h-8 bg-sky-500"
:style="{ width: `${percentage * 100 * 0.8}%` }"
></div>
<div class="text-sm">{{ percentage * 100 }}%</div>
<div class="text-sm">{{ (percentage * 100).toFixed(1) }}%</div>
</div>
</QuestionSummary>
</template>
<script setup lang="ts">
import QuestionSummary from "@/components/ui/QuestionSummary.vue";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
import { computed } from "vue";
export interface ChartItem {
interface ChartItem {
percentage: number;
label: string;
}
@ -26,6 +44,29 @@ export interface ChartItem {
const props = defineProps<{
title: string;
text: string;
items: ChartItem[];
items: string[];
}>();
const chartItems = computed<ChartItem[]>(() => {
const chartItems = props.items.reduce((acc, item) => {
const itemIndex = acc.findIndex((i) => i.label === item);
if (itemIndex === -1) {
acc.push({ label: item, percentage: 1 / props.items.length });
} else {
acc[itemIndex].percentage += 1 / props.items.length;
}
return acc;
}, [] as ChartItem[]);
return chartItems.sort(sort);
});
function sort(a: ChartItem, b: ChartItem) {
if (a.label > b.label) {
return -1;
}
if (a.label < b.label) {
return 1;
}
return 0;
}
</script>

View File

@ -126,7 +126,7 @@
"receivedMaterialsLabel": "Haben Sie Vorbereitungsunterlagen (z.B. eLearning) erhalten?",
"materialsRatingLabel": "Falls ja: Wie beurteilen Sie die Vorbereitungsunterlagen (z.B. eLearning)?",
"instructorCompetenceLabel": "Der Kursleiter war themenstark, fachkompetent.",
"instructorRespectLabel": "Fragen und Anregungen der Kursteilnehmenden wurden ernst genommen u. aufgegriffen.",
"instructorRespectLabel": "Fragen und Anregungen der Kursteilnehmenden wurden ernst genommen und aufgegriffen.",
"instructorOpenFeedbackLabel": "Was ich dem Kursleiter sonst noch sagen wollte:",
"courseNegativeFeedbackLabel": "Wo sehen Sie Verbesserungspotenzial?",
"coursePositiveFeedbackLabel": "Was hat Ihnen besonders gut gefallen?",

View File

@ -1,9 +1,6 @@
<script setup lang="ts">
import IconLogout from "@/components/icons/IconLogout.vue";
import IconSettings from "@/components/icons/IconSettings.vue";
import HorizontalBarChart, {
type ChartItem,
} from "@/components/ui/HorizontalBarChart.vue";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import ItCheckboxGroup from "@/components/ui/ItCheckboxGroup.vue";
import ItDropdown from "@/components/ui/ItDropdown.vue";
@ -12,6 +9,7 @@ import ItRadioGroup from "@/components/ui/ItRadioGroup.vue";
import ItTextarea from "@/components/ui/ItTextarea.vue";
import RatingScale from "@/components/ui/RatingScale.vue";
import VerticalBarChart from "@/components/ui/VerticalBarChart.vue";
import HorizontalBarChart from "@/components/ui/HorizontalBarChart.vue";
import logger from "loglevel";
import { reactive, ref } from "vue";
@ -102,23 +100,17 @@ const sourceItems = [
const textValue = ref("abc");
const barChartItems: ChartItem[] = [
{
percentage: 1,
label: "100%",
},
{
percentage: 0.9,
label: "Internet",
},
{
percentage: 0.1,
label: "Anderes",
},
{
percentage: 0,
label: "Anderes",
},
const barChartItems = [
"Internet",
"Internet",
"Internet",
"Internet",
"Internet",
"Internet",
"TV",
"TV",
"TV",
"Anderes"
];
function log(data: any) {
@ -455,7 +447,7 @@ function log(data: any) {
title="Frage 3"
text="Wie zufrieden bist du mit dem Kurs “Überbetriebliche Kurse” im Allgemeinen?"
/>
<VerticalBarChart title="Frage X" text="Fragentext" :ratio="0.2" />
<VerticalBarChart title="Frage X" text="Fragentext" :ratings="[true, true, false, true, true, false, true, false]" />
<HorizontalBarChart title="Frage X" text="Fragentext" :items="barChartItems" />
</div>
</main>

View File

@ -35,16 +35,17 @@
/>
<OpenFeedback
class="mb-8 bg-white"
v-if="openKeys.includes(question.key)"
v-else-if="openKeys.includes(question.key)"
:title="`${$t('feedback.questionTitle')} ${i + 1}`"
:text="question.question"
:answers="feedbackData.questions[question.key].filter((a) => a !== '')"
:answers="feedbackData.questions[question.key].filter((a: string) => a !== '')"
></OpenFeedback>
<!-- HorizontalBarChart
v-else
<HorizontalBarChart
class="mb-8 bg-white"
v-else-if="horizontalChartKeys.includes(question.key)"
:title="`${$t('feedback.questionTitle')} ${i}`"
:text="question.question"
:items="barChartItems" /-->
:items="feedbackData.questions[question.key].map((a: string) => `${a}%`)" />
</li>
</ol>
</main>
@ -87,7 +88,7 @@ const orderedQuestions = [
question: t("feedback.goalAttainmentLabel"),
},
{
key: "proficieny",
key: "proficiency",
question: t("feedback.proficiencyLabel"),
},
{
@ -128,6 +129,7 @@ const ratingKeys = [
"instructor_respect",
];
const verticalChartKyes = ["received_materials", "would_recommend"];
const horizontalChartKeys = ["proficiency"];
const openKeys = [
"course_negative_feedback",
"course_positive_feedback",
@ -144,13 +146,6 @@ onMounted(async () => {
Object.assign(feedbackData, data);
});
function calculateAverage(ratings: number[]): number {
return ratings.reduce((a, b) => a + b, 0) / ratings.length;
}
function getNumberOfAnswers(ratings: number[]): number {
return ratings.filter((r) => typeof r !== "string" || r !== "").length;
}
</script>
<style scoped></style>

View File

@ -26,7 +26,7 @@ class FeedbackFactory(DjangoModelFactory):
course_positive_feedback = FuzzyChoice(
[
"Die Präsentation war super",
"Das Beispiel mit der Katze fand ich sehr veranschaulicht!",
"Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
]
)
course_negative_feedback = FuzzyChoice(