Add HorizontalBar component
This commit is contained in:
parent
b7038c1a9c
commit
2ab8f580bc
|
|
@ -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"
|
||||
>
|
||||
<div class="w-full text-base font-bold">{{ label }}</div>
|
||||
<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>
|
||||
class="h-8 bg-sky-500"
|
||||
:style="{ width: `${percentage * 100 * 0.8}%` }"
|
||||
></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>
|
||||
|
|
|
|||
|
|
@ -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?",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue