Skip to content

Commit

Permalink
add server side fetching to performance display
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrir committed Nov 29, 2024
1 parent f7c0edc commit b4d6ec4
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 86 deletions.
184 changes: 120 additions & 64 deletions components/graphs/LineChart.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';
import React, { useState } from 'react';
import { useState, useMemo } from 'react';
import { Line } from 'react-chartjs-2';
import useSWR from 'swr';
import {
Chart as ChartJS,
CategoryScale,
Expand All @@ -9,7 +10,8 @@ import {
LineElement,
Title,
Tooltip,
Legend, ChartOptions,
Legend,
ChartOptions,
} from 'chart.js';
import { GraphType } from '@/lib/types';

Expand All @@ -34,13 +36,23 @@ const timeRanges = {

interface Props {
onlineFondet: GraphType[];
osebx: GraphType[];
}

const LineChart = ({ onlineFondet, osebx }: Props) => {
interface OsebxResponse {
data: GraphType[];
}

const fetcher = (url: string) => fetch(url).then((res) => res.json());

const LineChart = ({ onlineFondet }: Props) => {
const [selectedRange, setSelectedRange] =
useState<keyof typeof timeRanges>('5 år');

const { data: osebxData, error: osebxError } = useSWR<OsebxResponse>(
'/api/osebx',
fetcher,
);

const filterDataByRange = (data: GraphType[], rangeDays: number) => {
const today = new Date();
const cutoffDate = new Date(today);
Expand All @@ -63,66 +75,96 @@ const LineChart = ({ onlineFondet, osebx }: Props) => {
}));
};

const filteredOnlineFondet = filterDataByRange(
onlineFondet,
timeRanges[selectedRange],
const filteredOnlineFondet = useMemo(
() => filterDataByRange(onlineFondet, timeRanges[selectedRange]),
[onlineFondet, selectedRange],
);

const filteredOsebx = useMemo(
() =>
osebxData
? filterDataByRange(osebxData.data, timeRanges[selectedRange])
: [],
[osebxData, selectedRange],
);

const normalizedOnlineFondet = useMemo(
() => normalizeData(filteredOnlineFondet),
[filteredOnlineFondet],
);

const normalizedOsebx = useMemo(
() => normalizeData(filteredOsebx),
[filteredOsebx],
);

const allDatesSet = useMemo(
() =>
new Set([
...normalizedOnlineFondet.map((item) => item.date),
...normalizedOsebx.map((item) => item.date),
]),
[normalizedOnlineFondet, normalizedOsebx],
);
const filteredOsebx = filterDataByRange(osebx, timeRanges[selectedRange]);

const normalizedOnlineFondet = normalizeData(filteredOnlineFondet);
const normalizedOsebx = normalizeData(filteredOsebx);

const allDatesSet = new Set([
...normalizedOnlineFondet.map((item) => item.date),
...normalizedOsebx.map((item) => item.date),
]);
const allDates = Array.from(allDatesSet)
.map((date) => new Date(date))
.sort((a, b) => a.getTime() - b.getTime())
.map((date) => date.toISOString().split('T')[0]);

const mergedData = allDates.map((date) => {
const onlineFondetItem = normalizedOnlineFondet.find(
(item) => (item.date as unknown as string).split('T')[0] === date,
);
const osebxItem = normalizedOsebx.find(
(item) => (item.date as unknown as string).split('T')[0] === date,
);

return {
date,
onlineFondetValue: onlineFondetItem ? onlineFondetItem.value : null,
osebxValue: osebxItem ? osebxItem.value : null,
};
});

const data = {
labels: mergedData.map((item) => {
const date = new Date(item.date);
return date.toLocaleDateString('nb-NO', {
year: 'numeric',
month: 'short',
});
const allDates = useMemo(
() =>
Array.from(allDatesSet)
.map((date) => new Date(date))
.sort((a, b) => a.getTime() - b.getTime())
.map((date) => date.toISOString().split('T')[0]),
[allDatesSet],
);

const mergedData = useMemo(
() =>
allDates.map((date) => {
const onlineFondetItem = normalizedOnlineFondet.find(
(item) => new Date(item.date).toISOString().split('T')[0] === date,
);
const osebxItem = normalizedOsebx.find(
(item) => new Date(item.date).toISOString().split('T')[0] === date,
);

return {
date,
onlineFondetValue: onlineFondetItem ? onlineFondetItem.value : null,
osebxValue: osebxItem ? osebxItem.value : null,
};
}),
[allDates, normalizedOnlineFondet, normalizedOsebx],
);

const chartData = useMemo(
() => ({
labels: mergedData.map((item) => {
const date = new Date(item.date);
return date.toLocaleDateString('nb-NO', {
year: 'numeric',
month: 'short',
});
}),
datasets: [
{
label: 'Onlinefondet',
data: mergedData.map((item) => item.onlineFondetValue),
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
fill: true,
spanGaps: true,
},
{
label: 'OSEBX',
data: mergedData.map((item) => item.osebxValue),
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
fill: true,
spanGaps: true,
},
],
}),
datasets: [
{
label: 'Onlinefondet',
data: mergedData.map((item) => item.onlineFondetValue),
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
fill: true,
spanGaps: true,
},
{
label: 'OSEBX',
data: mergedData.map((item) => item.osebxValue),
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
fill: true,
spanGaps: true,
},
],
};
[mergedData],
);

const options: ChartOptions<'line'> = {
responsive: true,
Expand All @@ -132,8 +174,8 @@ const LineChart = ({ onlineFondet, osebx }: Props) => {
},
tooltip: {
callbacks: {
label: (ctx) => `${Math.round(ctx.raw as number * 100) / 100}%`
}
label: (ctx) => `${Math.round((ctx.raw as number) * 100) / 100}%`,
},
},
},
scales: {
Expand All @@ -154,10 +196,24 @@ const LineChart = ({ onlineFondet, osebx }: Props) => {
},
};

if (osebxError) {
return <div>Noe gikk galt: {osebxError.message}</div>;
}

if (!osebxData) {
return (
<div className="px-5">
<div className="flex flex-col items-center justify-center gap-4 text-center w-full h-full animate-pulse">
<div className="overflow-hidden bg-gray-700 h-48 w-64 lg:w-96 lg:h-64" />
</div>
</div>
);
}

return (
<div className="overflow-x-auto w-full">
<div className="min-w-[250px] h-64 md:h-96 lg:h-fit">
<Line data={data} options={options} />
<Line data={chartData} options={options} />
</div>
<div className="flex justify-center mb-4 pt-4 space-x-4 md:hidden overflow-x-auto">
<div className="flex space-x-4 px-2">
Expand Down
40 changes: 18 additions & 22 deletions components/graphs/PerformanceDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
'use client';
import useSWR from 'swr';
import ErrorPage from '../all/Error';
import Table from '../form/Table';
import LineChart from './LineChart';
import PieChart from './PieChart';
import { prisma } from '@/lib/prisma';

import { CompositionType } from '@/lib/types';

const fetcher = (url: string) => fetch(url).then((res) => res.json());

const PerformanceDisplay = () => {
const { data: osebxData, error: osebxError } = useSWR('/api/osebx', fetcher);
const { data: compositionData, error: compositionError } = useSWR(
'/api/admin/composition',
fetcher,
);
const PerformanceDisplay = async () => {
const compositionData = await prisma.composition.findMany({
orderBy: {
date: 'desc',
},
});

const { data: onlineFondetData, error: onlineFondetError } = useSWR(
'/api/admin/portfolio',
fetcher,
);
const onlineFondetData = await prisma.performance.findMany({
orderBy: {
date: 'desc',
},
});

if (osebxError || compositionError || onlineFondetError)
return <ErrorPage error="Portfølje" />;
if (!compositionData || !onlineFondetData) {
return <ErrorPage error="Data not found" />;
}

const columns = [
{
Expand All @@ -46,7 +45,7 @@ const PerformanceDisplay = () => {
Denne smultringen gir en oversikt over fondets sammensetning
</div>
{compositionData ? (
<PieChart composition={compositionData.composition} />
<PieChart composition={compositionData} />
) : (
<div className="flex flex-col items-center justify-center gap-4 text-center w-full h-full animate-pulse">
<div className="overflow-hidden rounded-full bg-gray-700 h-56 w-56 lg:w-72 lg:h-72" />
Expand All @@ -58,10 +57,7 @@ const PerformanceDisplay = () => {
Fondets prestasjon over tid
</div>
{onlineFondetData ? (
<LineChart
onlineFondet={onlineFondetData.performance}
osebx={osebxData.data}
/>
<LineChart onlineFondet={onlineFondetData} />
) : (
<div className="px-5">
<div className="flex flex-col items-center justify-center gap-4 text-center w-full h-full animate-pulse">
Expand All @@ -76,7 +72,7 @@ const PerformanceDisplay = () => {
Tabellen viser fond, andel og kategori
</div>
{compositionData ? (
<Table columns={columns} data={compositionData.composition} />
<Table columns={columns} data={compositionData} />
) : (
<div className="px-5">
<div className="flex flex-col items-center justify-center gap-4 text-center w-full h-full animate-pulse">
Expand Down

0 comments on commit b4d6ec4

Please sign in to comment.