diff --git a/package-lock.json b/package-lock.json index bc87410..5cfda92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "dependencies": { "@heroicons/react": "^1.0.6", "@tremor/react": "^3.12.0", + "@types/ramda": "^0.29.9", "next": "14.0.4", + "ramda": "^0.29.1", "react": "^18", "react-dom": "^18", "reaviz": "^15.2.1", @@ -929,6 +931,14 @@ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" }, + "node_modules/@types/ramda": { + "version": "0.29.9", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.9.tgz", + "integrity": "sha512-X3yEG6tQCWBcUAql+RPC/O1Hm9BSU+MXu2wJnCETuAgUlrEDwTA1kIOdEEE4YXDtf0zfQLHa9CCE7WYp9kqPIQ==", + "dependencies": { + "types-ramda": "^0.29.6" + } + }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -2797,6 +2807,15 @@ "ramda": "0.29.0" } }, + "node_modules/file-system-cache/node_modules/ramda": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz", + "integrity": "sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4661,9 +4680,9 @@ ] }, "node_modules/ramda": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz", - "integrity": "sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==", + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/ramda" @@ -5798,6 +5817,11 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -5900,6 +5924,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/types-ramda": { + "version": "0.29.6", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.6.tgz", + "integrity": "sha512-VJoOk1uYNh9ZguGd3eZvqkdhD4hTGtnjRBUx5Zc0U9ftmnCgiWcSj/lsahzKunbiwRje1MxxNkEy1UdcXRCpYw==", + "dependencies": { + "ts-toolbelt": "^9.6.0" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", diff --git a/package.json b/package.json index 6ef85a5..7d69751 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "dependencies": { "@heroicons/react": "^1.0.6", "@tremor/react": "^3.12.0", + "@types/ramda": "^0.29.9", "next": "14.0.4", + "ramda": "^0.29.1", "react": "^18", "react-dom": "^18", "reaviz": "^15.2.1", diff --git a/src/app/a.client.tsx b/src/app/a.client.tsx index aa4a3b2..7ebf2e5 100644 --- a/src/app/a.client.tsx +++ b/src/app/a.client.tsx @@ -3,20 +3,35 @@ import useSWR from "swr"; import {fetcher} from "@/app/utils"; import {useEffect, useState} from "react"; -import {Card, Metric, Text, Title} from "@tremor/react"; +import {Card, DonutChart, Metric, Text, Title} from "@tremor/react"; +import * as R from "ramda"; -export function ClientComponent({ - projectId, - title - }: { +export function SubjectOverviewCard({ + projectId, + title + }: { projectId: number, title?: string }) { + const { + data: _project + } = useSWR(`http://cosmos:8074/project?select=raw_json&toggl_id=eq.${projectId}`, fetcher); + const [project, setProject] = useState({ + name: '', + color: '', + }); + + useEffect(() => { + if (_project) { + setProject(_project[0].raw_json); + } + }, [_project]); + const { data, error, - isLoading - } = useSWR(`http://cosmos:8074/time_entry?select=*,project:project_id(name)&project_id=eq.${projectId}`, fetcher); + isLoading, + } = useSWR(`http://cosmos:8074/time_entry?select=raw_json&project_id=eq.${projectId}&start=gt.2023-12-15T00:00:00.000Z`, fetcher); const [a, setA] = useState(0) useEffect(() => { @@ -28,10 +43,52 @@ export function ClientComponent({ }, [data]); return ( - - {title ?? (isLoading ? 'Loading' : data?.[0]['project']['name'])} + + {title ?? project?.name} Total {(a / (60 * 60)).toFixed(2)} hours ) } + +export function SubjectComparisonCard({projectIds}: { + projectIds: number[] +}) { + const { + data, + error, + isLoading, + } = useSWR(`http://cosmos:8074/time_entry?select=raw_json,project:project_id(name,raw_json)&project_id=in.(${projectIds.join(',')})`, fetcher, {}); + + const [a, setA] = useState<{ name: string, value: number }[]>([]); + const [colours, setColours] = useState([]); + + useEffect(() => { + const a = R.toPairs(R.groupBy((entry) => entry.project.name, data ?? [])) + .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 + })) + + setA(a); + + setColours(a.map((entry) => entry.colour)); + }, [data]); + + const valueFormatter = (number: number) => `${(number / (60 * 60)).toFixed(2)} hours`; + + return ( + + Relative Breakdown + + + ) +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 4540f2c..4868321 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,25 +1,38 @@ -import {ClientComponent} from "@/app/a.client"; +import {SubjectComparisonCard, SubjectOverviewCard} from "@/app/a.client"; +import {Title} from "@tremor/react"; export default function Home() { return ( -
- +
+

Revision Tracker

- +
+ - + - + + + + + +
+ ) }