From d8ad2f80b8f6cdb6af3928fcfc4a05cdf432b372 Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Sun, 11 Feb 2024 17:20:41 +0000 Subject: [PATCH] Use server actions over swr for retrieving data --- package-lock.json | 116 ++++++++++-------------- package.json | 2 +- src/app/OverviewPage.tsx | 17 ++-- src/app/cards/calendarOverviewCard.tsx | 82 +++++++---------- src/app/cards/subjectComparisonCard.tsx | 66 +++++--------- src/app/cards/subjectOverviewCard.tsx | 56 +++--------- src/app/fetchData.ts | 45 +++++++++ 7 files changed, 169 insertions(+), 215 deletions(-) create mode 100644 src/app/fetchData.ts diff --git a/package-lock.json b/package-lock.json index 017b039..37d133d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@types/ramda": "^0.29.9", "date-fns": "^2.30.0", "heat-calendar": "^1.0.7", - "next": "14.0.4", + "next": "^14.1.0", "ramda": "^0.29.1", "react": "^18", "react-calendar-heatmap": "^1.9.0", @@ -1441,9 +1441,9 @@ } }, "node_modules/@next/env": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", - "integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==" + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.0.4", @@ -1455,9 +1455,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz", - "integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", "cpu": [ "arm64" ], @@ -1470,9 +1470,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz", - "integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", "cpu": [ "x64" ], @@ -1485,9 +1485,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz", - "integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", "cpu": [ "arm64" ], @@ -1500,9 +1500,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", - "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", "cpu": [ "arm64" ], @@ -1515,9 +1515,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", - "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", "cpu": [ "x64" ], @@ -1530,9 +1530,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", - "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", "cpu": [ "x64" ], @@ -1545,9 +1545,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", - "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", "cpu": [ "arm64" ], @@ -1560,9 +1560,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", - "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", "cpu": [ "ia32" ], @@ -1575,9 +1575,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", - "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", "cpu": [ "x64" ], @@ -3162,9 +3162,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "version": "1.0.30001585", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz", + "integrity": "sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q==", "funding": [ { "type": "opencollective", @@ -5103,11 +5103,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -7132,18 +7127,17 @@ "dev": true }, "node_modules/next": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz", - "integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "dependencies": { - "@next/env": "14.0.4", + "@next/env": "14.1.0", "@swc/helpers": "0.5.2", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -7152,15 +7146,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.4", - "@next/swc-darwin-x64": "14.0.4", - "@next/swc-linux-arm64-gnu": "14.0.4", - "@next/swc-linux-arm64-musl": "14.0.4", - "@next/swc-linux-x64-gnu": "14.0.4", - "@next/swc-linux-x64-musl": "14.0.4", - "@next/swc-win32-arm64-msvc": "14.0.4", - "@next/swc-win32-ia32-msvc": "14.0.4", - "@next/swc-win32-x64-msvc": "14.0.4" + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -9760,18 +9754,6 @@ "makeerror": "1.0.12" } }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index 8f05b19..53eab7f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@types/ramda": "^0.29.9", "date-fns": "^2.30.0", "heat-calendar": "^1.0.7", - "next": "14.0.4", + "next": "^14.1.0", "ramda": "^0.29.1", "react": "^18", "react-calendar-heatmap": "^1.9.0", diff --git a/src/app/OverviewPage.tsx b/src/app/OverviewPage.tsx index b010a37..747de59 100644 --- a/src/app/OverviewPage.tsx +++ b/src/app/OverviewPage.tsx @@ -2,10 +2,13 @@ import {SubjectComparisonCard} from "@/app/cards/subjectComparisonCard"; import {CalendarOverviewCard} from "@/app/cards/calendarOverviewCard"; import {OverviewConfig} from "@/app/overviewConfig"; import {SubjectOverviewCard} from "@/app/cards/subjectOverviewCard"; +import {getData} from "@/app/fetchData"; -export default function OverviewPage({config}: { +export default async function OverviewPage({config}: { config: OverviewConfig }) { + const data = await getData(config); + const projectIds = config.subjects.map((subject) => subject.projectId); return ( @@ -18,23 +21,17 @@ export default function OverviewPage({config}: { key={subject.projectId} projectId={subject.projectId} title={subject.title} - startTime={config.timePeriod.start} - endTime={config.timePeriod.end} + data={data} /> ))} - - + ) diff --git a/src/app/cards/calendarOverviewCard.tsx b/src/app/cards/calendarOverviewCard.tsx index 42480a0..82d1047 100644 --- a/src/app/cards/calendarOverviewCard.tsx +++ b/src/app/cards/calendarOverviewCard.tsx @@ -1,14 +1,12 @@ "use client"; import {Card, Title} from "@tremor/react"; -import {useEffect, useRef, useState} from "react"; -import useSWR from "swr"; -import {fetcher} from "@/app/utils"; import * as R from 'ramda'; import * as dFns from 'date-fns'; import CalendarHeatmap from 'react-calendar-heatmap'; import 'react-calendar-heatmap/dist/styles.css'; import '../calendar-styles.css' import {Tooltip} from 'react-tooltip'; +import {Data} from "@/app/fetchData"; const dailyGoal = 4; const granularity = 4; @@ -20,66 +18,48 @@ function computeCompletionShade(value: number) { return linearValue; } -function useCalendarData(projectIds: number[], initialDate: string, endDate: string) { - const { - data: timeEntries, - } = useSWR<{ - raw_json: { - start: string, - seconds: number, +function useCalendarData(data: Data, initialDate: Date, endDate: Date) { + const timeEntries = data.timeEntries; + + // Group by day, sum up seconds + const grouped = R.groupBy((entry) => { + return dFns.formatISO(dFns.startOfDay(dFns.parseISO(entry.start))); + }, timeEntries); + + const summed = R.mapObjIndexed((entries) => { + return R.sum((entries ?? []).map((entry) => entry.duration)) + }, grouped); + + // Fill in missing days, hacky + dFns.eachDayOfInterval({ + start: initialDate, + end: endDate, + }).forEach((date) => { + const key = dFns.formatISO(date); + if (summed[key] == undefined) { + summed[key] = 0; } - }[]>(`/api/time_entry?select=raw_json&start=gt.${initialDate}&start=lt.${endDate}&project_id=in.(${projectIds.join(',')})`, fetcher, {}); + }) - const [data, setData] = useState<{ - date: Date, - count: number, - }[]>([]); - - useEffect(() => { - if (timeEntries == undefined) return; - - // Group by day, sum up seconds - const grouped = R.groupBy((entry) => { - return dFns.formatISO(dFns.startOfDay(dFns.parseISO(entry.raw_json.start))); - }, timeEntries); - - const summed = R.mapObjIndexed((entries) => { - return R.sum((entries ?? []).map((entry) => entry.raw_json.seconds)) - }, grouped); - - // Fill in missing days, hacky - dFns.eachDayOfInterval({ - start: dFns.parseISO(initialDate), - end: dFns.parseISO(endDate), - }).forEach((date) => { - const key = dFns.formatISO(date); - if (summed[key] == undefined) { - summed[key] = 0; - } - }) - - setData(Object.entries(summed) - .map(([key, value]) => ({ - date: dFns.parseISO(key), - count: value / (60 * 60), - }))); - }, [timeEntries]); - - return data + return Object.entries(summed) + .map(([key, value]) => ({ + date: dFns.parseISO(key), + count: value / (60 * 60), + })) } export function CalendarOverviewCard({ - projectIds, + data, startTime, endTime }: { - projectIds: number[], + data: Data, startTime: string, endTime: string, }) { const initialDate = dFns.parseISO(startTime); const endDate = dFns.parseISO(endTime); - const data = useCalendarData(projectIds, startTime, endTime); + const calendarData = useCalendarData(data, initialDate, endDate); return @@ -88,7 +68,7 @@ export function CalendarOverviewCard({ showWeekdayLabels={true} startDate={initialDate} endDate={endDate} - values={data} + values={calendarData} classForValue={value => `color-github-${computeCompletionShade(value?.count ?? 0)}`} tooltipDataAttrs={(value: any) => { return value.date ? { diff --git a/src/app/cards/subjectComparisonCard.tsx b/src/app/cards/subjectComparisonCard.tsx index 571b6f9..bbdefdb 100644 --- a/src/app/cards/subjectComparisonCard.tsx +++ b/src/app/cards/subjectComparisonCard.tsx @@ -1,52 +1,36 @@ "use client"; -import useSWR from "swr"; -import {fetcher} from "@/app/utils"; -import {useEffect, useState} from "react"; import {Card, DonutChart, Title} from "@tremor/react"; import * as R from "ramda"; +import {Data} from "@/app/fetchData"; + +function useBreakdownData(data: Data): { + name: string, + value: number, + colour: string +}[] { + const sorted = R.sortBy(R.prop('projectId'), data.timeEntries); + const grouped = R.groupWith(R.eqBy(R.prop('projectId')), sorted); + + return grouped + .map((entries) => { + const project = data.projects.find(p => p.projectId === entries[0].projectId)!; + + return ({ + name: project.name, + value: (entries).map((entry) => entry.duration).reduce((a, b) => a + b, 0), + colour: project.color, + }); + }) +} export function SubjectComparisonCard({ - projectIds, - startTime, - endTime + data, }: { - projectIds: number[], - startTime: string, - endTime: string, + data: Data, }) { - const { - data: rawData, - error, - isLoading, - } = useSWR<{ - raw_json: { - seconds: number, - }, - project: { - name: string, - raw_json: any - } - }[]>(`/api/time_entry?select=raw_json,project:project_id(name,raw_json)&start=gt.${startTime}&start=lt.${endTime}&project_id=in.(${projectIds.join(',')})`, fetcher, {}); - - const [breakdownData, setBreakdownData] = useState<{ - name: string, - value: number - }[]>([]); - const [colours, setColours] = useState([]); - - useEffect(() => { - const breakdownData = R.toPairs(R.groupBy((entry) => entry.project.name, rawData ?? [])) - .map(([name, entries]) => ({ - name, - value: (entries ?? []).map((entry) => entry.raw_json.seconds).reduce((a, b) => a + b, 0), - colour: entries?.[0].project.raw_json.color - })) - - setBreakdownData(breakdownData); - - setColours(breakdownData.map((entry) => entry.colour)); - }, [rawData]); + const breakdownData = useBreakdownData(data); + const colours = breakdownData.map((entry) => entry.colour); const valueFormatter = (number: number) => `${(number / (60 * 60)).toFixed(2)} hours`; diff --git a/src/app/cards/subjectOverviewCard.tsx b/src/app/cards/subjectOverviewCard.tsx index 00e9bb9..fed12a3 100644 --- a/src/app/cards/subjectOverviewCard.tsx +++ b/src/app/cards/subjectOverviewCard.tsx @@ -1,61 +1,27 @@ -"use client"; - -import useSWR from "swr"; -import {fetcher} from "@/app/utils"; -import {useEffect, useState} from "react"; import {Card, Metric, Text, Title} from "@tremor/react"; +import {Data} from "@/app/fetchData"; export function SubjectOverviewCard({ - projectId, title, - startTime = '2023-12-15T00:00:00.000Z', - endTime = '2024-01-25T00:00:00.000Z', + data, + projectId, }: { + title?: string, + data: Data, projectId: number, - title?: string - startTime?: string, - endTime?: string, }) { - const { - data: _project - } = useSWR<{ - raw_json: { - name: string, - color: string, - } - }[]>(`/api/project?select=raw_json&toggl_id=eq.${projectId}`, fetcher); + const project = data.projects.find((project) => project.projectId === projectId)!; + const totalDuration = data.timeEntries + .filter((entry) => entry.projectId === projectId) + .map((entry) => entry.duration) + .reduce((a, b) => a + b, 0); - const [project, setProject] = useState({ - name: '', - color: '', - }); - - useEffect(() => { - if (_project) { - setProject(_project[0].raw_json); - } - }, [_project]); - - const { - data, - error, - isLoading, - } = useSWR(`/api/time_entry?select=raw_json&project_id=eq.${projectId}&start=gt.${startTime}&start=lt.${endTime}`, fetcher); - const [a, setA] = useState(0) - - useEffect(() => { - if (data) { - setA(data - .map((entry) => entry.raw_json.seconds) - .reduce((a, b) => a + b, 0)); - } - }, [data]); return ( {title ?? project?.name} Total - {(a / (60 * 60)).toFixed(2)} hours + {(totalDuration / (60 * 60)).toFixed(2)} hours ) } diff --git a/src/app/fetchData.ts b/src/app/fetchData.ts new file mode 100644 index 0000000..5ea131b --- /dev/null +++ b/src/app/fetchData.ts @@ -0,0 +1,45 @@ +"use server"; + +import {OverviewConfig} from "@/app/overviewConfig"; + +export interface Data { + projects: { + projectId: number, + name: string, + color: string, + }[], + + timeEntries: { + projectId: number, + start: string, + end: string, + duration: number, + // description: string, + }[], +} + +export async function getData(config: OverviewConfig) { + const projectIds = config.subjects.map((subject) => subject.projectId); + const projectResponse = await fetch(`https://revision.joshuacoles.me/api/project?select=raw_json&toggl_id=in.(${projectIds.join(',')})`); + const projects = await projectResponse.json(); + const projectLensed = projects.map((project: any) => ({ + projectId: project.raw_json.id, + name: project.raw_json.name, + color: project.raw_json.color, + })); + + const timeEntriesResponse = await fetch(`https://revision.joshuacoles.me/api/time_entry?select=project_id,raw_json&project_id=in.(${projectIds.join(',')})&start=gt.${config.timePeriod.start}&start=lt.${config.timePeriod.end}`); + const timeEntries = await timeEntriesResponse.json(); + const timeEntriesLensed = timeEntries.map((timeEntry: any) => ({ + projectId: timeEntry.project_id, + start: timeEntry.raw_json.start, + end: timeEntry.raw_json.end, + duration: timeEntry.raw_json.seconds, + // description: timeEntry.raw_json.description, + })); + + return { + projects: projectLensed, + timeEntries: timeEntriesLensed, + }; +}