Compare commits
8 Commits
fb9547991c
...
0314ccc2b3
| Author | SHA1 | Date | |
|---|---|---|---|
| 0314ccc2b3 | |||
| 44df8a6fc6 | |||
| 0a89712965 | |||
| c7fcb0990f | |||
| 0ce2096fac | |||
| 52b6b0eaee | |||
| a61677fe35 | |||
| c85776a499 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,8 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# IDEA
|
||||||
|
/.idea
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
|
|||||||
5
.idea/.gitignore
generated
vendored
5
.idea/.gitignore
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
6
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/tremor-dashboard.iml" filepath="$PROJECT_DIR$/.idea/tremor-dashboard.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
12
.idea/tremor-dashboard.iml
generated
12
.idea/tremor-dashboard.iml
generated
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="WEB_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
18
jest.config.ts
Normal file
18
jest.config.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { Config } from 'jest'
|
||||||
|
import nextJest from 'next/jest.js'
|
||||||
|
|
||||||
|
const createJestConfig = nextJest({
|
||||||
|
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||||
|
dir: './',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add any custom config to be passed to Jest
|
||||||
|
const config: Config = {
|
||||||
|
coverageProvider: 'v8',
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
// Add more setup options before each test is run
|
||||||
|
// setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||||
|
}
|
||||||
|
|
||||||
|
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
||||||
|
export default createJestConfig(config)
|
||||||
3724
package-lock.json
generated
3724
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,8 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
@ -25,6 +26,8 @@
|
|||||||
"swr": "^2.2.4"
|
"swr": "^2.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@testing-library/jest-dom": "^6.1.5",
|
||||||
|
"@testing-library/react": "^14.1.2",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-calendar-heatmap": "^1.6.6",
|
"@types/react-calendar-heatmap": "^1.6.6",
|
||||||
@ -32,8 +35,11 @@
|
|||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.0.4",
|
"eslint-config-next": "14.0.4",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.3.0",
|
"tailwindcss": "^3.3.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
src/app/OverviewPage.tsx
Normal file
40
src/app/OverviewPage.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import {SubjectComparisonCard, SubjectOverviewCard} from "@/app/a.client";
|
||||||
|
import {CalendarOverviewCard} from "@/app/calendarOverviewCard";
|
||||||
|
import {OverviewConfig} from "@/app/overviewConfig";
|
||||||
|
|
||||||
|
export default function OverviewPage({config}: {
|
||||||
|
config: OverviewConfig
|
||||||
|
}) {
|
||||||
|
const projectIds = config.subjects.map((subject) => subject.projectId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="m-6">
|
||||||
|
<h1 className="text-3xl font-semibold text-slate-900 dark:text-white my-2">{config.title}</h1>
|
||||||
|
|
||||||
|
<div className="grid gap-5 grid-cols-1 sm:grid-cols-4">
|
||||||
|
{config.subjects.map((subject) => (
|
||||||
|
<SubjectOverviewCard
|
||||||
|
key={subject.projectId}
|
||||||
|
projectId={subject.projectId}
|
||||||
|
title={subject.title}
|
||||||
|
startTime={config.timePeriod.start}
|
||||||
|
endTime={config.timePeriod.end}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
|
||||||
|
<CalendarOverviewCard
|
||||||
|
startTime={config.timePeriod.start}
|
||||||
|
endTime={config.timePeriod.end}
|
||||||
|
projectIds={projectIds}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SubjectComparisonCard
|
||||||
|
startTime={config.timePeriod.start}
|
||||||
|
endTime={config.timePeriod.end}
|
||||||
|
projectIds={projectIds}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -8,14 +8,24 @@ import * as R from "ramda";
|
|||||||
|
|
||||||
export function SubjectOverviewCard({
|
export function SubjectOverviewCard({
|
||||||
projectId,
|
projectId,
|
||||||
title
|
title,
|
||||||
|
startTime = '2023-12-15T00:00:00.000Z',
|
||||||
|
endTime = '2024-01-25T00:00:00.000Z',
|
||||||
}: {
|
}: {
|
||||||
projectId: number,
|
projectId: number,
|
||||||
title?: string
|
title?: string
|
||||||
|
startTime?: string,
|
||||||
|
endTime?: string,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
data: _project
|
data: _project
|
||||||
} = useSWR<any>(`/api/project?select=raw_json&toggl_id=eq.${projectId}`, fetcher);
|
} = useSWR<{
|
||||||
|
raw_json: {
|
||||||
|
name: string,
|
||||||
|
color: string,
|
||||||
|
}
|
||||||
|
}[]>(`/api/project?select=raw_json&toggl_id=eq.${projectId}`, fetcher);
|
||||||
|
|
||||||
const [project, setProject] = useState({
|
const [project, setProject] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
color: '',
|
color: '',
|
||||||
@ -31,7 +41,7 @@ export function SubjectOverviewCard({
|
|||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
isLoading,
|
isLoading,
|
||||||
} = useSWR<any[]>(`/api/time_entry?select=raw_json&project_id=eq.${projectId}&start=gt.2023-12-15T00:00:00.000Z`, fetcher);
|
} = useSWR<any[]>(`/api/time_entry?select=raw_json&project_id=eq.${projectId}&start=gt.${startTime}&start=lt.${endTime}`, fetcher);
|
||||||
const [a, setA] = useState(0)
|
const [a, setA] = useState(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -43,7 +53,7 @@ export function SubjectOverviewCard({
|
|||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card style={{ borderColor: project?.color }} decoration={'left'}>
|
<Card style={{borderColor: project?.color}} decoration={'left'}>
|
||||||
<Title>{title ?? project?.name}</Title>
|
<Title>{title ?? project?.name}</Title>
|
||||||
<Text>Total</Text>
|
<Text>Total</Text>
|
||||||
<Metric>{(a / (60 * 60)).toFixed(2)} hours</Metric>
|
<Metric>{(a / (60 * 60)).toFixed(2)} hours</Metric>
|
||||||
@ -51,30 +61,47 @@ export function SubjectOverviewCard({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SubjectComparisonCard({projectIds}: {
|
export function SubjectComparisonCard({
|
||||||
projectIds: number[]
|
projectIds,
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
}: {
|
||||||
|
projectIds: number[],
|
||||||
|
startTime: string,
|
||||||
|
endTime: string,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
data,
|
data: rawData,
|
||||||
error,
|
error,
|
||||||
isLoading,
|
isLoading,
|
||||||
} = useSWR<any[]>(`/api/time_entry?select=raw_json,project:project_id(name,raw_json)&project_id=in.(${projectIds.join(',')})`, fetcher, {});
|
} = 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 [a, setA] = useState<{ name: string, value: number }[]>([]);
|
const [breakdownData, setBreakdownData] = useState<{
|
||||||
|
name: string,
|
||||||
|
value: number
|
||||||
|
}[]>([]);
|
||||||
const [colours, setColours] = useState<string[]>([]);
|
const [colours, setColours] = useState<string[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const a = R.toPairs(R.groupBy((entry) => entry.project.name, data ?? []))
|
const breakdownData = R.toPairs(R.groupBy((entry) => entry.project.name, rawData ?? []))
|
||||||
.map(([name, entries]) => ({
|
.map(([name, entries]) => ({
|
||||||
name,
|
name,
|
||||||
value: (entries ?? []).map((entry) => entry.raw_json.seconds).reduce((a, b) => a + b, 0),
|
value: (entries ?? []).map((entry) => entry.raw_json.seconds).reduce((a, b) => a + b, 0),
|
||||||
colour: entries?.[0].project.raw_json.color
|
colour: entries?.[0].project.raw_json.color
|
||||||
}))
|
}))
|
||||||
|
|
||||||
setA(a);
|
setBreakdownData(breakdownData);
|
||||||
|
|
||||||
setColours(a.map((entry) => entry.colour));
|
setColours(breakdownData.map((entry) => entry.colour));
|
||||||
}, [data]);
|
}, [rawData]);
|
||||||
|
|
||||||
const valueFormatter = (number: number) => `${(number / (60 * 60)).toFixed(2)} hours`;
|
const valueFormatter = (number: number) => `${(number / (60 * 60)).toFixed(2)} hours`;
|
||||||
|
|
||||||
@ -83,7 +110,7 @@ export function SubjectComparisonCard({projectIds}: {
|
|||||||
<Title>Relative Breakdown</Title>
|
<Title>Relative Breakdown</Title>
|
||||||
<DonutChart
|
<DonutChart
|
||||||
className="mt-6"
|
className="mt-6"
|
||||||
data={a ?? []}
|
data={breakdownData ?? []}
|
||||||
category="value"
|
category="value"
|
||||||
index="name"
|
index="name"
|
||||||
valueFormatter={valueFormatter}
|
valueFormatter={valueFormatter}
|
||||||
|
|||||||
@ -8,16 +8,7 @@ import * as dFns from 'date-fns';
|
|||||||
import CalendarHeatmap from 'react-calendar-heatmap';
|
import CalendarHeatmap from 'react-calendar-heatmap';
|
||||||
import 'react-calendar-heatmap/dist/styles.css';
|
import 'react-calendar-heatmap/dist/styles.css';
|
||||||
import './calendar-styles.css'
|
import './calendar-styles.css'
|
||||||
import { Tooltip } from 'react-tooltip';
|
import {Tooltip} from 'react-tooltip';
|
||||||
|
|
||||||
const initialDate = dFns.parseISO('2023-12-15T00:00:00.000Z')
|
|
||||||
const endDate = dFns.parseISO('2024-01-25T00:00:00.000Z')
|
|
||||||
const projectIds = [
|
|
||||||
195482340,
|
|
||||||
195519024,
|
|
||||||
195518593,
|
|
||||||
195754611,
|
|
||||||
];
|
|
||||||
|
|
||||||
const dailyGoal = 4;
|
const dailyGoal = 4;
|
||||||
const granularity = 4;
|
const granularity = 4;
|
||||||
@ -29,17 +20,15 @@ function computeCompletionShade(value: number) {
|
|||||||
return linearValue;
|
return linearValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useCalendarData() {
|
function useCalendarData(projectIds: number[], initialDate: string, endDate: string) {
|
||||||
const {
|
const {
|
||||||
data: timeEntries,
|
data: timeEntries,
|
||||||
error,
|
|
||||||
isLoading,
|
|
||||||
} = useSWR<{
|
} = useSWR<{
|
||||||
raw_json: {
|
raw_json: {
|
||||||
start: string,
|
start: string,
|
||||||
seconds: number,
|
seconds: number,
|
||||||
}
|
}
|
||||||
}[]>(`/api/time_entry?select=raw_json&start=gt.${dFns.formatISO(initialDate)}&project_id=in.(${projectIds.join(',')})`, fetcher, {});
|
}[]>(`/api/time_entry?select=raw_json&start=gt.${initialDate}&start=lt.${endDate}&project_id=in.(${projectIds.join(',')})`, fetcher, {});
|
||||||
|
|
||||||
const [data, setData] = useState<{
|
const [data, setData] = useState<{
|
||||||
date: Date,
|
date: Date,
|
||||||
@ -60,8 +49,8 @@ function useCalendarData() {
|
|||||||
|
|
||||||
// Fill in missing days, hacky
|
// Fill in missing days, hacky
|
||||||
dFns.eachDayOfInterval({
|
dFns.eachDayOfInterval({
|
||||||
start: initialDate,
|
start: dFns.parseISO(initialDate),
|
||||||
end: endDate,
|
end: dFns.parseISO(endDate),
|
||||||
}).forEach((date) => {
|
}).forEach((date) => {
|
||||||
const key = dFns.formatISO(date);
|
const key = dFns.formatISO(date);
|
||||||
if (summed[key] == undefined) {
|
if (summed[key] == undefined) {
|
||||||
@ -69,8 +58,6 @@ function useCalendarData() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
debugger
|
|
||||||
|
|
||||||
setData(Object.entries(summed)
|
setData(Object.entries(summed)
|
||||||
.map(([key, value]) => ({
|
.map(([key, value]) => ({
|
||||||
date: dFns.parseISO(key),
|
date: dFns.parseISO(key),
|
||||||
@ -81,8 +68,18 @@ function useCalendarData() {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CalendarOverviewCard() {
|
export function CalendarOverviewCard({
|
||||||
const data = useCalendarData();
|
projectIds,
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
}: {
|
||||||
|
projectIds: number[],
|
||||||
|
startTime: string,
|
||||||
|
endTime: string,
|
||||||
|
}) {
|
||||||
|
const initialDate = dFns.parseISO(startTime);
|
||||||
|
const endDate = dFns.parseISO(endTime);
|
||||||
|
const data = useCalendarData(projectIds, startTime, endTime);
|
||||||
|
|
||||||
return <Card className="col-span-1">
|
return <Card className="col-span-1">
|
||||||
<Tooltip id="calendar-tooltip"/>
|
<Tooltip id="calendar-tooltip"/>
|
||||||
@ -96,7 +93,7 @@ export function CalendarOverviewCard() {
|
|||||||
tooltipDataAttrs={(value: any) => {
|
tooltipDataAttrs={(value: any) => {
|
||||||
return value.date ? {
|
return value.date ? {
|
||||||
'data-tooltip-id': `calendar-tooltip`,
|
'data-tooltip-id': `calendar-tooltip`,
|
||||||
'data-tooltip-content': value.count ?`${dFns.format(value.date, 'EEE do')}: ${value.count.toFixed(2)} hours` : `${dFns.format(value.date, 'EEE do')}`
|
'data-tooltip-content': value.count ? `${dFns.format(value.date, 'EEE do')}: ${value.count.toFixed(2)} hours` : `${dFns.format(value.date, 'EEE do')}`
|
||||||
} : undefined
|
} : undefined
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import './globals.css'
|
|||||||
const inter = Inter({ subsets: ['latin'] })
|
const inter = Inter({ subsets: ['latin'] })
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Create Next App',
|
title: 'Work Tracker',
|
||||||
description: 'Generated by create next app',
|
description: 'Track time spent on different subjects',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|||||||
40
src/app/overviewConfig.ts
Normal file
40
src/app/overviewConfig.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export interface OverviewConfig {
|
||||||
|
title: string,
|
||||||
|
|
||||||
|
subjects: {
|
||||||
|
title?: string,
|
||||||
|
projectId: number,
|
||||||
|
}[],
|
||||||
|
|
||||||
|
goalHours: number,
|
||||||
|
|
||||||
|
timePeriod: {
|
||||||
|
start: string,
|
||||||
|
end: string
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const semester1Revision: OverviewConfig = {
|
||||||
|
title: 'Semester 1 Revision',
|
||||||
|
goalHours: 4,
|
||||||
|
subjects: [
|
||||||
|
{
|
||||||
|
projectId: 195482340,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Measure Theory',
|
||||||
|
projectId: 195519024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Quantum Mechanics',
|
||||||
|
projectId: 195518593,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectId: 195754611,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
timePeriod: {
|
||||||
|
start: "2023-12-15T00:00:00.000Z",
|
||||||
|
end: "2024-01-25T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,33 +1,6 @@
|
|||||||
import {SubjectComparisonCard, SubjectOverviewCard} from "@/app/a.client";
|
import OverviewPage from "@/app/OverviewPage";
|
||||||
import {CalendarOverviewCard} from "@/app/calendarOverviewCard";
|
import {semester1Revision} from "@/app/overviewConfig";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return <OverviewPage config={semester1Revision}/>
|
||||||
<main className="m-6">
|
|
||||||
<h1 className="text-3xl font-semibold text-slate-900 dark:text-white my-2">Revision Tracker</h1>
|
|
||||||
|
|
||||||
<div className="grid gap-5 grid-cols-1 sm:grid-cols-4">
|
|
||||||
<SubjectOverviewCard
|
|
||||||
projectId={195482340}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SubjectOverviewCard
|
|
||||||
title='Measure Theory'
|
|
||||||
projectId={195519024}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SubjectOverviewCard
|
|
||||||
title='Quantum Mechanics'
|
|
||||||
projectId={195518593}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SubjectOverviewCard
|
|
||||||
projectId={195754611}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CalendarOverviewCard/>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/app/sem2/page.tsx
Normal file
32
src/app/sem2/page.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import OverviewPage from "@/app/OverviewPage";
|
||||||
|
import {OverviewConfig} from "@/app/overviewConfig";
|
||||||
|
|
||||||
|
const semester2: OverviewConfig = {
|
||||||
|
title: 'Semester 2',
|
||||||
|
goalHours: 4,
|
||||||
|
subjects: [
|
||||||
|
{
|
||||||
|
projectId: 195754611,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectId: 199383703,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectId: 199383691,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectId: 198859760,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
projectId: 199383698,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timePeriod: {
|
||||||
|
start: "2024-02-03T00:00:00.000Z",
|
||||||
|
end: "2024-05-10T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return <OverviewPage config={semester2}/>
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user