Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add first draft of Fra Kåre draw #562

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion components/dashboard/DashboardTimeSeriesChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const crosshairTemplate = (d: GetFraKaareStatisticsChurchStatisticsSnapshot) =>
:grid-line="false"
:tick-line="false"
:tick-format="
(v: number, i: number) => data[i]?.snapshotDate?.toLocaleDateString()
(timestamp: number) => new Date(timestamp).toLocaleDateString()
"
/>
<VisAxis
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
},
"dependencies": {
"@auth0/auth0-vue": "^2.3.3",
"@bcc-code/bmm-sdk-fetch": "^8.7.4",
"@bcc-code/bmm-sdk-fetch": "^8.7.10",
"@floating-ui/vue": "^1.0.6",
"@headlessui/vue": "^1.7.19",
"@microsoft/applicationinsights-web": "^3.0.8",
"@neoconfetti/vue": "^2.2.1",
"@pinia/nuxt": "^0.5.1",
"@sentry/vue": "^8.27.0",
"@tiptap/starter-kit": "^2.9.1",
Expand Down
343 changes: 21 additions & 322 deletions pages/dashboards/fra-kaare.vue
Original file line number Diff line number Diff line change
@@ -1,329 +1,28 @@
<script setup lang="ts">
import { StatisticsApi } from "@bcc-code/bmm-sdk-fetch";
import type { GetFraKaareStatisticsResponse } from "@bcc-code/bmm-sdk-fetch";
import { breakpointsTailwind } from "@vueuse/core";
import DashboardDataTable from "~/components/dashboard/DashboardDataTable.vue";
import type { SortDirection } from "~/components/dashboard/DashboardDataTable.vue";

const { t } = useI18n();
setTitle(() => t("dashboards.title"));

definePageMeta({
middleware: ["frakaare-dashboard-viewer"],
});

const sortDirection = ref<SortDirection>("descending");
const churchSize = ref<"small" | "large">("large");

const statistics = ref<GetFraKaareStatisticsResponse>();
onBeforeMount(async () => {
statistics.value = await new StatisticsApi().statisticsFraKaareGet();
});

watch(statistics, (stats) => {
if (!stats) return;
churchSize.value = (stats.largeChurches || []).some(
(item) => item.churchName === stats.highlightedChurchName,
)
? "large"
: "small";
});

const rows = computed(
() =>
(churchSize.value === "large"
? statistics.value?.largeChurches
: statistics.value?.smallChurches) ?? [],
);

function sortPercentageColumn(
a: number | null | undefined,
b: number | null | undefined,
) {
if (typeof a !== "number" || typeof b !== "number") return 0;
if (a === b) return 0;
if (sortDirection.value === "descending") {
return b - a;
}
return a - b;
}

function sortStringColumn(
a: string | null | undefined,
b: string | null | undefined,
) {
if (typeof a !== "string" || typeof b !== "string") return 0;
if (sortDirection.value === "ascending") {
return b.localeCompare(a);
}
return a.localeCompare(b);
}

const breakpoints = useBreakpoints(breakpointsTailwind);
const isSmallScreeen = breakpoints.smaller("lg");

const shouldHideColumns = computed(
() => churchSize.value === "small" || isSmallScreeen.value,
);

const columnGroupWidth = computed(() => {
if (churchSize.value === "small" && !isSmallScreeen.value) return 2;
if (shouldHideColumns.value) return 1;
return 5;
});
import { NuxtLink } from "#components";
</script>

<template>
<div class="space-y-12">
<PageHeading>Dashboard: Fra Kåre</PageHeading>

<section
id="chart"
class="flex min-h-[400px] flex-col rounded-2xl bg-background-2 p-8"
>
<h2 class="type-heading-3 mb-4">
{{ statistics?.highlightedChurchName }}
</h2>
<DashboardTimeSeriesChart
v-if="statistics?.timeSeries"
:data="statistics.timeSeries"
/>
</section>

<section id="table">
<div class="mb-4">
<DropdownMenu placement="bottom-start">
<ButtonStyled size="small" intent="tertiary">
{{ churchSize === "large" ? "Store menigheter" : "Små menigheter" }}
<NuxtIcon name="icon.chevron.down" class="ml-1" />
</ButtonStyled>
<template #items>
<DropdownMenuGroup>
<DropdownMenuItem @click="churchSize = 'large'">
Store menigheter
</DropdownMenuItem>
<DropdownMenuItem @click="churchSize = 'small'">
Små menigheter
</DropdownMenuItem>
</DropdownMenuGroup>
</template>
</DropdownMenu>
<div>
<header class="mb-8">
<PageHeading>Dashboard: Fra Kåre</PageHeading>
<div class="flex border-b border-label-separator">
<NuxtLink
to="/dashboards/fra-kaare/overview"
class="px-4 py-2"
active-class="border-b-4 border-tint"
>
Statistikk
</NuxtLink>
<NuxtLink
to="/dashboards/fra-kaare/raffle"
class="px-4 py-2"
active-class="border-b-4 border-tint"
>
Trekk en vinner
</NuxtLink>
</div>

<DashboardDataTable
v-if="statistics"
:key="`${churchSize}-${isSmallScreeen}`"
v-model:sort-direction="sortDirection"
:items="rows"
sort-by="oneEpisodePercentAverage"
:column-groups="[
{
key: 'oneEpisode',
text: 'Har hørt minst én episode i 2025',
start: 1,
span: columnGroupWidth,
},
{
key: 'allEpisodes',
text: 'Totale episoder hørt',
start: 5,
span: columnGroupWidth,
},
]"
:columns="[
{
key: 'churchName',
text: 'Menighet',
sortMethod: (a, b) => sortStringColumn(a.churchName, b.churchName),
},
{
key: 'oneEpisodePercent13To17',
text: '13-17',
sortMethod: (a, b) =>
sortPercentageColumn(
a.oneEpisodePercent13To17,
b.oneEpisodePercent13To17,
),
hide: shouldHideColumns,
},
{
key: 'oneEpisodePercent18To25',
text: '18-25',
sortMethod: (a, b) =>
sortPercentageColumn(
a.oneEpisodePercent18To25,
b.oneEpisodePercent18To25,
),
hide: shouldHideColumns,
},
{
key: 'oneEpisodePercent26To35',
text: '26-35',
sortMethod: (a, b) =>
sortPercentageColumn(
a.oneEpisodePercent26To35,
b.oneEpisodePercent26To35,
),
hide: shouldHideColumns,
},
{
key: 'oneEpisodePercentAverage',
text: 'Gjennomsnitt',
props: () =>
!shouldHideColumns ? { class: 'bg-background-2' } : {},
sortMethod: (a, b) =>
sortPercentageColumn(
a.oneEpisodePercentAverage,
b.oneEpisodePercentAverage,
),
},
{
key: 'oneEpisodeChange',
text: 'Endring 7d',
props: (item) => ({
class: [
'bg-background-2',
{
'text-[green]':
item?.oneEpisodeChange && item.oneEpisodeChange > 0,
'text-[red]':
item?.oneEpisodeChange && item.oneEpisodeChange < 0,
},
],
}),
sortMethod: (a, b) =>
sortPercentageColumn(a.oneEpisodeChange, b.oneEpisodeChange),
hide: isSmallScreeen,
},
{
key: 'allEpisodesPercent13To17',
text: '13-17',
sortMethod: (a, b) =>
sortPercentageColumn(
a.allEpisodesPercent13To17,
b.allEpisodesPercent13To17,
),
hide: shouldHideColumns,
},
{
key: 'allEpisodesPercent18To25',
text: '18-25',
sortMethod: (a, b) =>
sortPercentageColumn(
a.allEpisodesPercent18To25,
b.allEpisodesPercent18To25,
),
hide: shouldHideColumns,
},
{
key: 'allEpisodesPercent26To35',
text: '26-35',
sortMethod: (a, b) =>
sortPercentageColumn(
a.allEpisodesPercent26To35,
b.allEpisodesPercent26To35,
),
hide: shouldHideColumns,
},
{
key: 'allEpisodesPercentAverage',
text: 'Gjennomsnitt',
props: () =>
!shouldHideColumns ? { class: 'bg-background-2' } : {},
sortMethod: (a, b) =>
sortPercentageColumn(
a.allEpisodesPercentAverage,
b.allEpisodesPercentAverage,
),
},
{
key: 'allEpisodesChange',
text: 'Endring 7d',
props: (item) => ({
class: [
'bg-background-2',
{
'text-[green]':
item?.allEpisodesChange && item.allEpisodesChange > 0,
'text-[red]':
item?.allEpisodesChange && item.allEpisodesChange < 0,
},
],
}),
sortMethod: (a, b) =>
sortPercentageColumn(a.allEpisodesChange, b.allEpisodesChange),
hide: isSmallScreeen,
},
]"
:get-field="
(item, key) => {
const value = item[key];
if (value === null || value === undefined) return '';
if (typeof value === 'number') return Math.round(value * 100) + '%';
return value;
}
"
:highlight-row="
(item) => item.churchName === statistics!.highlightedChurchName
"
>
<template #oneEpisodeChange="{ item }">
<div
v-if="typeof item.oneEpisodeChange === 'number'"
class="flex grow items-center justify-end gap-1"
>
<NuxtIcon
v-if="item.oneEpisodeChange !== 0"
:name="
item.oneEpisodeChange > 0
? 'icon.chevron.up'
: 'icon.chevron.down'
"
/>
<span>{{ (item.oneEpisodeChange * 100).toFixed(1) }}%</span>
</div>
</template>
<template #allEpisodesChange="{ item }">
<div
v-if="typeof item.allEpisodesChange === 'number'"
class="flex grow items-center justify-end gap-1"
>
<NuxtIcon
v-if="item.allEpisodesChange !== 0"
:name="
item.allEpisodesChange > 0
? 'icon.chevron.up'
: 'icon.chevron.down'
"
/>
<span>{{ (item.allEpisodesChange * 100).toFixed(1) }}%</span>
</div>
</template>
</DashboardDataTable>
</section>

<section
id="info"
class="type-paragraph-2 max-w-6xl space-y-3 px-6 text-label-2"
>
<p>
Tilgangen til denne statistikken er kun ment for lokale ungdomsledere og
BUK-kontakter. Disse dataene inneholder informasjon om individuell
deltagelse i bibelstudieprosjektet, og det er derfor viktig at de ikke
deles fritt med andre. Bruk denne statistikken ansvarlig for å skape
positivt engasjement rundt prosjektet i din lokalforening. Dersom du har
spørsmål til hvordan tallene kan brukes, vennligst kontakt oss på
<a href="mailto:[email protected]" class="underline"
>[email protected]</a
>.
</p>
<p>
Alderskategoriene er basert på alderen i begynnelsen av året. Det betyr
at hvis du fyller 18 i år, regnes du med i kategorien 13-17.
Alderskategoriene er basert på personlige brukerkontoer, og brukere som
f.eks. låner foreldres innlogging vil ikke telle med i statistikken for
sin aldersgruppe.
</p>
</section>
</header>
<NuxtPage />
</div>
</template>
6 changes: 6 additions & 0 deletions pages/dashboards/fra-kaare/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script setup lang="ts">
definePageMeta({
redirect: "/dashboards/fra-kaare/overview",
middleware: ["frakaare-dashboard-viewer"],
});
</script>
Loading
Loading