feat: add rating colors

This commit is contained in:
Reto Aebersold 2023-10-31 11:20:21 +01:00
parent dfe1bfb845
commit 3664ddf6d7
5 changed files with 84 additions and 77 deletions

View File

@ -28,10 +28,6 @@ defineProps<{
<span>{{ $t("a.Durchführungen") }}</span>
</div>
</div>
<!-- TODO Figure out where this link should go -->
<!-- <router-link class="btn-secondary" to="#">-->
<!-- {{ $t("a.Liste anzeigen") }}-->
<!-- </router-link>-->
</div>
</template>

View File

@ -1,20 +1,29 @@
<script setup lang="ts">
import BaseBox from "@/components/dashboard/BaseBox.vue";
import { getBlendedColorForRating } from "@/utils/ratingToColor";
import { computed } from "vue";
const props = defineProps<{
feedbackCount: number;
statisfactionMax: number;
statisfactionAvg: number;
}>();
const satisfactionColor = computed(() => {
return getBlendedColorForRating(props.statisfactionAvg);
});
</script>
<template>
<BaseBox :details-link="'/statistic/feedback'">
<template #title>{{ $t("a.Feedback Teilnehmer") }}</template>
<template #content>
<div class="flex">
<div class="flex items-center">
<!-- Left Pane -->
<div class="flex h-16 w-32 items-center justify-center rounded bg-green-500">
<div
class="mr-3 flex items-center justify-center rounded p-4"
:style="{ backgroundColor: satisfactionColor }"
>
<i18next :translation="$t('a.{AVG} von {MAX}')">
<template #AVG>
<span class="pr-2 text-4xl font-bold">
@ -27,7 +36,7 @@ const props = defineProps<{
</i18next>
</div>
<!-- Right Pane -->
<div class="flex flex-col items-center space-y-1 p-2">
<div class="flex flex-col items-center space-y-1">
<span class="font-bold">{{ $t("a.Allgemeine Zufriedenheit") }}</span>
<div class="self-start">
<i18next :translation="$t('a.Total {NUMBER} Antworten')">

View File

@ -77,15 +77,10 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
import { computed } from "vue";
import { useTranslation } from "i18next-vue";
import log from "loglevel";
import { getBlendedColorForRating } from "@/utils/ratingToColor";
const { t } = useTranslation();
type RGB = [number, number, number];
const red: RGB = [221, 103, 81]; // red-600
const yellow: RGB = [250, 200, 82]; // yellow-500
const lightGreen: RGB = [120, 222, 163]; // green-500
const darkGreen: RGB = [91, 183, 130]; // green-600
const legends = [
{ index: 1, label: t("feedback.veryUnhappy") },
{ index: 2, label: t("feedback.unhappy") },
@ -101,19 +96,11 @@ const props = defineProps<{
log.debug("RatingScale created", props);
const rating = computed((): number => {
const rating = computed(() => {
const sum = props.ratings.reduce((a, b) => a + b, 0);
return sum / props.ratings.length;
});
const weight = computed(() => {
return rating.value % 1;
});
const scale = computed(() => {
return Math.floor(rating.value);
});
const answers = computed(() => props.ratings.length);
const numberOfRatings = computed(() => {
@ -122,57 +109,14 @@ const numberOfRatings = computed(() => {
);
});
const colors = computed(() => {
switch (scale.value) {
case 1:
return [red, yellow];
case 2:
return [yellow, lightGreen];
case 3:
default:
return [lightGreen, darkGreen];
}
});
const blendColorValue = (v1: number, v2: number, weight: number) => {
return v1 * (1 - weight) + v2 * weight;
};
const blendColors = (c1: RGB, c2: RGB, weight: number): RGB => {
const [r1, g1, b1] = c1;
const [r2, g2, b2] = c2;
return [
blendColorValue(r1, r2, weight),
blendColorValue(g1, g2, weight),
blendColorValue(b1, b2, weight),
];
};
const getRGBString = ([r, g, b]: RGB) => {
return `rgb(${Math.floor(r)}, ${Math.floor(g)}, ${Math.floor(b)})`;
};
// const getRGBStyle = (c1: RGB, c2: RGB, weight: number) => {
// return getRGBString(blendColors(c1, c2, weight));
// };
const percent = computed(() => {
return (scale.value - 1) * 33.33 + weight.value * 33.33;
return (Math.floor(rating.value) - 1) * 33.33 + (rating.value % 1) * 33.33;
});
const leftPosition = computed(() => {
return `${percent.value.toPrecision(3)}%`;
});
const leftPosition = computed(() => `${percent.value.toPrecision(3)}%`);
const rightClip = computed(() => `${Math.round(100 - percent.value)}%`);
const rightClip = computed(() => {
return `${Math.round(100 - percent.value)}%`;
});
const blendedColor = computed(() => {
return blendColors(colors.value[0], colors.value[1], weight.value);
});
const backgroundColor = getRGBString(blendedColor.value);
const backgroundColor = getBlendedColorForRating(rating.value);
const circleStyle = {
backgroundColor,

View File

@ -9,6 +9,7 @@ import type {
} from "@/gql/graphql";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import { useCourseStatistics } from "@/composables";
import { getBlendedColorForRating } from "@/utils/ratingToColor";
const dashboardStore = useDashboardStore();
@ -50,15 +51,25 @@ const { courseSessionName, circleMeta } = useCourseStatistics();
<div>
<div class="mb-4 flex items-center space-x-2">
<div
class="flex items-center justify-center rounded bg-green-500 px-2 py-1"
class="rounded px-2 py-1"
:style="{ backgroundColor: getBlendedColorForRating((item as FeedbackStatisticsRecordType).satisfaction_average) }"
>
<span class="font-bold">
{{ (item as FeedbackStatisticsRecordType).satisfaction_average }}
</span>
&nbsp;von&nbsp;
<span>
{{ (item as FeedbackStatisticsRecordType).satisfaction_max }}
</span>
<i18next :translation="$t('a.{AVG} von {MAX}')">
<template #AVG>
<span class="font-bold">
{{
(
item as FeedbackStatisticsRecordType
).satisfaction_average.toFixed(1)
}}
</span>
</template>
<template #MAX>
<span>
{{ (item as FeedbackStatisticsRecordType).satisfaction_max }}
</span>
</template>
</i18next>
</div>
<span>
{{ $t("a.Allgemeine Zufriedenheit") }}

View File

@ -0,0 +1,47 @@
type RGB = [number, number, number];
const red: RGB = [221, 103, 81]; // red-600
const yellow: RGB = [250, 200, 82]; // yellow-500
const lightGreen: RGB = [120, 222, 163]; // green-500
const darkGreen: RGB = [91, 183, 130]; // green-600
const blendColorValue = (v1: number, v2: number, weight: number): number => {
return v1 * (1 - weight) + v2 * weight;
};
const blendColors = (c1: RGB, c2: RGB, weight: number): RGB => {
const [r1, g1, b1] = c1;
const [r2, g2, b2] = c2;
return [
blendColorValue(r1, r2, weight),
blendColorValue(g1, g2, weight),
blendColorValue(b1, b2, weight),
];
};
const getRGBString = (color: RGB): string => {
const [r, g, b] = color;
return `rgb(${Math.floor(r)}, ${Math.floor(g)}, ${Math.floor(b)})`;
};
export const getBlendedColorForRating = (rating: number): string => {
const scale = Math.floor(rating);
const weight = rating % 1;
let colors: [RGB, RGB];
switch (scale) {
case 1:
colors = [red, yellow];
break;
case 2:
colors = [yellow, lightGreen];
break;
case 3:
default:
colors = [lightGreen, darkGreen];
break;
}
const blendedColor = blendColors(colors[0], colors[1], weight);
return getRGBString(blendedColor);
};