From 44a7505774d53c63c8f61c13b101b9c13cc6a4ef Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Tue, 9 Jul 2024 09:31:33 +0200 Subject: [PATCH 01/18] Fetching data from Space-Track, fixing some bugs, adding cron tasks for updating database with tool functions --- backend/config/functions/cronTask.js | 27 ++++ backend/config/middlewares.js | 12 +- backend/config/server.js | 7 + .../content-types/satellite/schema.json | 3 + .../api/satellite/controllers/satellite.js | 15 ++- .../src/api/satellite/services/satellite.js | 66 +++++++++- frontend/package.json | 3 +- .../ScrollBarThumb.tsx | 83 ++++++++++++ .../[satelliteSlug]/launchDateCountDown.tsx | 2 +- .../[satelliteSlug]/orbitDataGraph.tsx | 121 ++++++++++++++++++ .../app/satellites/[satelliteSlug]/page.tsx | 13 ++ frontend/src/components/layout/Footer.tsx | 3 +- package.json | 2 +- start_project.sh | 0 14 files changed, 350 insertions(+), 7 deletions(-) create mode 100644 backend/config/functions/cronTask.js create mode 100644 frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx create mode 100644 frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx mode change 100644 => 100755 start_project.sh diff --git a/backend/config/functions/cronTask.js b/backend/config/functions/cronTask.js new file mode 100644 index 0000000..b39c59e --- /dev/null +++ b/backend/config/functions/cronTask.js @@ -0,0 +1,27 @@ +// backend/config/functions/cronTask.js +/* + * Function to fetch data from Space-Track such as Eccentricy, SMA, Inclination every month + * and update the database with the new data + */ +'use strict'; + +module.exports = { + updateAllSatellitesData: { + task: async ({ strapi }) => { + try { + // Fetching all satellites + const satellites = await strapi.entityService.findMany('api::satellite.satellite'); + + // Waiting for all promises to be resolved + await Promise.all( + satellites.map(async satellite => { + await strapi.service('api::satellite.satellite').fetchOrbitalData(satellite.id); + }) + ); + } catch (error) { + console.error(error); + } + }, + options: new Date(Date.now() + 10000), + }, +}; diff --git a/backend/config/middlewares.js b/backend/config/middlewares.js index 6eaf586..72ae482 100644 --- a/backend/config/middlewares.js +++ b/backend/config/middlewares.js @@ -5,7 +5,17 @@ module.exports = [ 'strapi::cors', 'strapi::poweredBy', 'strapi::query', - 'strapi::body', + { + name: "strapi::body", + config: { + formLimit: "256mb", // modify form body + jsonLimit: "256mb", // modify JSON body + textLimit: "256mb", // modify text body + formidable: { + maxFileSize: 200 * 1024 * 1024, // multipart data, modify here limit of uploaded file size + }, + }, + }, 'strapi::session', 'strapi::favicon', 'strapi::public', diff --git a/backend/config/server.js b/backend/config/server.js index fe927ab..2133f76 100644 --- a/backend/config/server.js +++ b/backend/config/server.js @@ -1,3 +1,5 @@ +const cronTask = require('./functions/cronTask'); + module.exports = ({ env }) => ({ host: env("HOST", "127.0.0.1"), port: env.int("PORT", 1337), @@ -7,4 +9,9 @@ module.exports = ({ env }) => ({ webhooks: { populateRelations: env.bool("WEBHOOKS_POPULATE_RELATIONS", false), }, + cron: { + // Enable or disable the cron tasks + enabled: env.bool("CRON_ENABLED", false), + tasks: cronTask, + }, }); diff --git a/backend/src/api/satellite/content-types/satellite/schema.json b/backend/src/api/satellite/content-types/satellite/schema.json index 166acba..f06a1ec 100644 --- a/backend/src/api/satellite/content-types/satellite/schema.json +++ b/backend/src/api/satellite/content-types/satellite/schema.json @@ -51,6 +51,9 @@ }, "massKg": { "type": "float" + }, + "historicalOrbitalData": { + "type": "json" } } } diff --git a/backend/src/api/satellite/controllers/satellite.js b/backend/src/api/satellite/controllers/satellite.js index d4d610a..58fba9d 100644 --- a/backend/src/api/satellite/controllers/satellite.js +++ b/backend/src/api/satellite/controllers/satellite.js @@ -6,4 +6,17 @@ const { createCoreController } = require('@strapi/strapi').factories; -module.exports = createCoreController('api::satellite.satellite'); +module.exports = createCoreController('api::satellite.satellite', ({ strapi }) => ({ + async findOne(ctx) { + // Fetching data from Space-Track + const entity = await strapi.entityService.findOne('api::satellite.satellite', ctx.params.id); + const updatedSatellite = await strapi.service('api::satellite.satellite').fetchOrbitalData(ctx.params.id); + + if (!entity.historicalOrbitalData) { + return updatedSatellite; + } + + return entity; + } +}) +); diff --git a/backend/src/api/satellite/services/satellite.js b/backend/src/api/satellite/services/satellite.js index 6dfd176..87a7669 100644 --- a/backend/src/api/satellite/services/satellite.js +++ b/backend/src/api/satellite/services/satellite.js @@ -4,6 +4,70 @@ * satellite service */ + const { createCoreService } = require('@strapi/strapi').factories; -module.exports = createCoreService('api::satellite.satellite'); +const axios = require('axios'); + +// Function to fetch data from Space-Track such as Eccentricy, SMA, Inclination +async function fetchOrbitalData(contextId) { + try { + // Fetching the satellite + const satellite = await strapi.entityService.findOne('api::satellite.satellite', contextId); + const noradId = satellite.catalogNumberNORAD; + + // Authentification to Space-Track + const authResponse = await axios.post('https://www.space-track.org/ajaxauth/login', { + identity: 'grauleflorian@gmail.com', + password : 'Vm5JxTtD3-hYBdq' + }); + + if (authResponse.status === 200) { + // Fetching data from Space-Track + const satelliteResponse = await axios.get(`https://www.space-track.org/basicspacedata/query/class/gp_history/NORAD_CAT_ID/${noradId}/orderby/TLE_LINE1%20ASC/EPOCH/1950-07-02--2024-07-02/format/json`, { + headers: { + Cookie: authResponse.headers['set-cookie'] + } + }); + + if (satelliteResponse.status === 200) { + // Collecting data + const satelliteData = satelliteResponse.data; + // Parsing correctly the data with wanted data : Inclination, Eccentricity, SMA, and Epoch + const historicalOrbitalData = satelliteData.map(data => { + return { + epoch: data.EPOCH, + inclination: data.INCLINATION, + eccentricity: data.ECCENTRICITY, + semiMajorAxis: data.SEMIMAJOR_AXIS + } + }); + + if (satellite) { + // Updating the satellite with the new data + const updatedSatellite = await strapi.entityService.update('api::satellite.satellite', contextId, { + data: { + historicalOrbitalData: historicalOrbitalData, + }, + }); + return updatedSatellite; + } else { + throw new Error('Satellite not found while updating orbit data'); + } + + + } else { + throw new Error('Error while fetching data from Space-Track'); + } + } else { + throw new Error('Authentication failed'); + } + } catch (error) { + console.error('Error while fetching data to Space-Track: ', error); + } +} + +module.exports = { + ...createCoreService('api::satellite.satellite'), + fetchOrbitalData, +}; diff --git a/frontend/package.json b/frontend/package.json index ab91f44..351521e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,7 +37,7 @@ "@visx/shape": "^3.5.0", "@visx/vendor": "^3.5.0", "add": "^2.0.6", - "chart.js": "^4.4.1", + "chart.js": "^4.4.3", "chartjs-adapter-luxon": "^1.3.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -59,6 +59,7 @@ "playwright": "^1.43.1", "qs": "^6.11.2", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.13", "react-markdown": "^9.0.1", diff --git a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx new file mode 100644 index 0000000..b412ff6 --- /dev/null +++ b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx @@ -0,0 +1,83 @@ +"use client"; + +import React, { useState, useEffect, useRef } from 'react'; + +interface ScrollBarThumbProps { + scrollBarThumbWidth: number; + svgContainerRect: {topLeft: number, width: number, height: number}; +} + +const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, svgContainerRect}) => { + const isDragging = useRef(false); + {/* SB is used for ScrollBar */} + const [sBThumbX, setSBThumbX] = useState(0); + const thumbRef = useRef(null); + // Distance between the left of the thumb and the mouse click + const distThumbClick = useRef(null); + + /* Be careful useEffect runs before parent props are received */ + useEffect(() => { + const handleMouseUp = (e: any) => { + if (thumbRef.current) { + isDragging.current = false; + } + } + + const handleMouseMove = (e: MouseEvent) => { + if (isDragging.current && thumbRef.current) { + // Scrollbar starts at the right of the svg container and goes to the left by increasing SBThumbX + setSBThumbX(() => { + // Calculating min and max x positions following the SBThumbX axis for moving the thumb with mouse movement + const minX= 0.5; + const maxX = svgContainerRect.width - scrollBarThumbWidth; + // newPos represents left border of the thumb + const newPos = svgContainerRect.topLeft + svgContainerRect.width - scrollBarThumbWidth - (e.clientX - (distThumbClick.current? distThumbClick.current : 0)); + + // If mouse movement isn't in the scrollable area + if (newPos <= minX) { + return minX; + } else if (newPos >= maxX) { + return maxX; + } + return newPos; + }); + } + } + + window.addEventListener('mouseup', handleMouseUp); + window.addEventListener('mousemove', handleMouseMove); + + // Managing the resize of the thumb + if (thumbRef.current && thumbRef.current.getBoundingClientRect().x < svgContainerRect.topLeft) { + setSBThumbX(svgContainerRect.width - scrollBarThumbWidth); + } + + return () => { + window.removeEventListener('mouseup', handleMouseUp); + window.removeEventListener('mousemove', handleMouseMove); + } + }, [scrollBarThumbWidth, svgContainerRect]); + + const handleMouseDown = (e: React.MouseEvent) => { + // If the thumb is clicked with left mouse button, we start dragging + if (thumbRef.current && e.button === 0) { + isDragging.current = true; + distThumbClick.current = e.clientX - thumbRef.current.getBoundingClientRect().left; + } + } + + return ( + <> + + + + + + + ); +} + +export default ScrollBarThumb; \ No newline at end of file diff --git a/frontend/src/app/satellites/[satelliteSlug]/launchDateCountDown.tsx b/frontend/src/app/satellites/[satelliteSlug]/launchDateCountDown.tsx index e791be4..eef6d79 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/launchDateCountDown.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/launchDateCountDown.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useState, useEffect } from "react"; -type LaunchDateCountDownProps = { +export type LaunchDateCountDownProps = { launchDate: string | Date | undefined; }; diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx new file mode 100644 index 0000000..7fc4b8e --- /dev/null +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -0,0 +1,121 @@ +"use client"; + +import React, { useState, useLayoutEffect, useRef, SyntheticEvent } from 'react' +import { SatelliteNumber } from '@/lib/store' +import { Line } from 'react-chartjs-2' +import Chart from 'chart.js' +import { LaunchDateCountDownProps } from './launchDateCountDown'; +import ScrollBarThumb from './_orbitDataGraphComponents/ScrollBarThumb'; + +type OrbitDataProps = { + satNum : SatelliteNumber; + launchDateString: LaunchDateCountDownProps['launchDate']; + orbitalData: any; +} + +const OrbitDataGraph : React.FC = ({ satNum, launchDateString, orbitalData }) => { + + // href for the svg component, tracking the size of the container + const svgContainer = useRef(null); + const [svgSize, setSvgSize] = useState({width: 0, height: 0}); + {/* SB use for ScrollBar*/} + const [scrollBarThumbWidth, setSBThumbWidth] = useState(0); + + // Handling button for zooming in and out of the graph on a time scale + const launchDate = launchDateString? new Date(launchDateString) : new Date(); + const calculateMonthsDiff = () => { + const currentDate = new Date(); + return currentDate.getMonth() - launchDate.getMonth() + (12 * (currentDate.getFullYear() - launchDate.getFullYear())); + } + + const months = calculateMonthsDiff(); + + const handleZoomClick = (e: React.MouseEvent) => { + // We round the width of the scrollbar thumb at one decimal + const period = e.currentTarget.textContent; + const regExp = new RegExp('^([1234567890]+)([my])$'); + const match = period?.match(regExp); + let SBThumbWidth = 0; + + if (period === "All") { + SBThumbWidth = svgSize.width*0.8-40.5; + setSBThumbWidth(SBThumbWidth); + } else { + const number = match ? parseInt(match[1]) : 0; + const periodType = match? match[2] : ""; + + if (periodType === "m") { + SBThumbWidth = Math.round((svgSize.width*0.8 * number *10) / months) / 10; + setSBThumbWidth(SBThumbWidth); + } else if (periodType === "y") { + SBThumbWidth = Math.round((svgSize.width*0.8 * number * 12 *10) / months) / 10 + setSBThumbWidth(SBThumbWidth); + } + } + } + + // Layout effect to track the size of the container and update the svg size + useLayoutEffect(() => { + const updateSize = () => { + if (svgContainer.current) { + const width = svgContainer.current.offsetWidth; + const height = width / 2; + setSvgSize({width, height}); + setSBThumbWidth( width*0.1 ); + } + } + window.addEventListener('resize', updateSize); + updateSize(); + + return () => window.removeEventListener('resize', updateSize); + }, []); + + return ( + <> +

+ Graph goes here. +

+
+
+

Zoom :

+ {/* Scrollbar thumb represents the zoom period selected, in case it fits bad we don't display + ie. containerSize > SBThumbWidth > 20px */} + { (Math.round((svgSize.width*0.8 * 1 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 1 * 10) / months) / 10 > 20) && } + { (Math.round((svgSize.width*0.8 * 3 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 3 * 10) / months) / 10 > 40) && } + { (Math.round((svgSize.width*0.8 * 6 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 6 * 10) / months) / 10 > 40) && } + { (Math.round((svgSize.width*0.8 * 12 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 10) / months) / 10 > 40) && } + { (Math.round((svgSize.width*0.8 * 12 * 5 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 5 * 10) / months) / 10 > 40) && } + { (Math.round((svgSize.width*0.8 * 12 * 10 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 10 * 10) / months) / 10 > 40) && } + { (Math.round((svgSize.width*0.8 * 12 * 20 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 20 * 10) / months) / 10 > 40) && } + + +
+
+ + {/* Scrollbar for time navigation */} + + + + + + {/* Scrollbar left navigation arrow */} + + + + + {/* Scrollbar right navigation arrow */} + + + + + {/* Scrollbar thumb */} + + + +
+
+ + ) +} + +export default OrbitDataGraph; \ No newline at end of file diff --git a/frontend/src/app/satellites/[satelliteSlug]/page.tsx b/frontend/src/app/satellites/[satelliteSlug]/page.tsx index 21857ed..8a6e10a 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/page.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/page.tsx @@ -13,6 +13,7 @@ import Image from "next/image"; import { SatelliteNumber } from "@/lib/store"; import { graphql } from "@/lib/tada/graphql"; import { getClient } from "@/lib/ApolloClient"; +import OrbitDataGraph from "./orbitDataGraph"; export interface ProjectOrSatellite { id: string; @@ -154,6 +155,17 @@ export default async function SatelliteInfoPage({ + {/* Orbit data */} +
+ {/*Pass the satNum and the launchDate as props to OrbitDataGraph*/} + { noradId? ( + satAttributes?.launchDate ? ( + + ) : null + + ) : null} +
+ {/* Related projects */}
{relatedProjects?.length != 0 ? ( @@ -189,6 +201,7 @@ const GET_SATELLITE_INFO = graphql(` name massKg missionStatus + historicalOrbitalData satelliteImage { data { attributes { diff --git a/frontend/src/components/layout/Footer.tsx b/frontend/src/components/layout/Footer.tsx index 4d6f1fc..846f34a 100644 --- a/frontend/src/components/layout/Footer.tsx +++ b/frontend/src/components/layout/Footer.tsx @@ -6,6 +6,7 @@ import NTNULogo from "./NTNULogo"; * It includes the NTNU logo, social media links, and contact information. */ export default function Footer() { + const now = new Date(); return (
@@ -50,7 +51,7 @@ export default function Footer() {

- © 2021 NTNU Small Satellite Lab + © 2017 - {now.getFullYear()} NTNU Small Satellite Lab

diff --git a/package.json b/package.json index d65ffd2..f4635ff 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "frontend": "cd frontend && npm run dev", "backend": "cd backend && npm run develop", - "dev": "concurrently \"npm run server\" \"npm run client\"" + "dev": "concurrently \"npm run backend\" \"npm run frontend\"" }, "author": "", "license": "", diff --git a/start_project.sh b/start_project.sh old mode 100644 new mode 100755 From 2dcdcf8362bfdddec78bda45a0b06bb834cf6ea8 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Mon, 15 Jul 2024 13:06:25 +0200 Subject: [PATCH 02/18] Orbital chart done, using backend data without refetching from the backend server --- .../ScrollBarThumb.tsx | 10 +- .../[satelliteSlug]/orbitDataGraph.tsx | 148 ++++++++++++++---- package.json | 2 + 3 files changed, 123 insertions(+), 37 deletions(-) diff --git a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx index b412ff6..a779b29 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx @@ -2,12 +2,13 @@ import React, { useState, useEffect, useRef } from 'react'; -interface ScrollBarThumbProps { +export interface ScrollBarThumbProps { scrollBarThumbWidth: number; svgContainerRect: {topLeft: number, width: number, height: number}; + handleChartScroll: (thumbX: number, svgContainerRect: ScrollBarThumbProps['svgContainerRect']) => void; } -const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, svgContainerRect}) => { +const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, svgContainerRect, handleChartScroll}) => { const isDragging = useRef(false); {/* SB is used for ScrollBar */} const [sBThumbX, setSBThumbX] = useState(0); @@ -38,9 +39,12 @@ const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, s return minX; } else if (newPos >= maxX) { return maxX; - } + } return newPos; }); + + // Change the displayed data on the chart + handleChartScroll(Math.round(thumbRef.current.getBoundingClientRect().x * 10) / 10, svgContainerRect); } } diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index 7fc4b8e..bf83305 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -1,25 +1,35 @@ "use client"; -import React, { useState, useLayoutEffect, useRef, SyntheticEvent } from 'react' +import React, { useState, useLayoutEffect, useRef } from 'react' import { SatelliteNumber } from '@/lib/store' -import { Line } from 'react-chartjs-2' -import Chart from 'chart.js' +import { XAxis, CartesianGrid, Line, LineChart, Tooltip, YAxis, Label, ResponsiveContainer, Legend } from 'recharts'; import { LaunchDateCountDownProps } from './launchDateCountDown'; -import ScrollBarThumb from './_orbitDataGraphComponents/ScrollBarThumb'; +import ScrollBarThumb, { ScrollBarThumbProps } from './_orbitDataGraphComponents/ScrollBarThumb'; type OrbitDataProps = { satNum : SatelliteNumber; launchDateString: LaunchDateCountDownProps['launchDate']; orbitalData: any; -} +} + +type ChartData = { + epoch: Date; + inclination: number; + eccentricity: number; + semiMajorAxis: number; +} const OrbitDataGraph : React.FC = ({ satNum, launchDateString, orbitalData }) => { // href for the svg component, tracking the size of the container const svgContainer = useRef(null); - const [svgSize, setSvgSize] = useState({width: 0, height: 0}); + const [svgSize, setSvgSize] = useState({width: 0, height: 200}); {/* SB use for ScrollBar*/} const [scrollBarThumbWidth, setSBThumbWidth] = useState(0); + // scrollBarTimeFrame is how many months the scrollbar thumb represents + const scrollBarTimeFrame = useRef(0); + // Chart Data + const [chartData, setChartData] = useState([]); // Handling button for zooming in and out of the graph on a time scale const launchDate = launchDateString? new Date(launchDateString) : new Date(); @@ -39,29 +49,76 @@ const OrbitDataGraph : React.FC = ({ satNum, launchDateString, o if (period === "All") { SBThumbWidth = svgSize.width*0.8-40.5; + scrollBarTimeFrame.current = months; setSBThumbWidth(SBThumbWidth); } else { const number = match ? parseInt(match[1]) : 0; const periodType = match? match[2] : ""; if (periodType === "m") { + scrollBarTimeFrame.current = number; SBThumbWidth = Math.round((svgSize.width*0.8 * number *10) / months) / 10; setSBThumbWidth(SBThumbWidth); } else if (periodType === "y") { + scrollBarTimeFrame.current = number * 12; SBThumbWidth = Math.round((svgSize.width*0.8 * number * 12 *10) / months) / 10 setSBThumbWidth(SBThumbWidth); } } + // Updating chart due to resizing the scrollbar thumb + if (svgContainer.current) { + handleChartScroll( + svgContainer.current.getBoundingClientRect().x + 0.9*svgContainer.current.getBoundingClientRect().width - 20.5 - SBThumbWidth, + {topLeft: svgContainer.current.getBoundingClientRect().x + svgSize.width*0.1 + 20.5, width: svgSize.width*0.8-40.5, height: 20} + ); + } } + + // Callback function for updating the chart when the scrollbar thumb is moved + const handleChartScroll = (thumbX: number, svgContainerRect: ScrollBarThumbProps['svgContainerRect']) => { + + // Ratio of the thumb left border position to the svg container width + const dateRatio = (thumbX - svgContainerRect.topLeft)/svgContainerRect.width; + // Taking the last date of the data period (first date is the launch date) + const lastDataDate = new Date(orbitalData[orbitalData.length-1].epoch.slice(0, 23) + "Z"); + // Calculating the displayed period in milliseconds + const displayedPeriodMs = lastDataDate.getTime() - launchDate.getTime(); + // Calculating the first and last date of the chart + const firstChartDate = new Date(launchDate.getTime() + displayedPeriodMs * dateRatio); + const lastChartDate = new Date(firstChartDate); + lastChartDate.setMonth(firstChartDate.getMonth() + scrollBarTimeFrame.current); + + // Filtering the data to display only the data in the selected period + const filteredData = orbitalData.filter((data: any) => { + const dataDate = new Date(data.epoch.slice(0, 23) + "Z"); + return dataDate >= firstChartDate && dataDate <= lastChartDate; + }).map((data: any) => { + return { + ...data, + epoch: new Date(data.epoch.slice(0, 23) + "Z") + }; + }); + + setChartData(filteredData); + }; + // Layout effect to track the size of the container and update the svg size useLayoutEffect(() => { const updateSize = () => { if (svgContainer.current) { + // Update svg container size const width = svgContainer.current.offsetWidth; - const height = width / 2; - setSvgSize({width, height}); - setSBThumbWidth( width*0.1 ); + setSvgSize((prevSvgSize) => {return { width: width, height: prevSvgSize.height }}); + // Initially set the scrollbar thumb width to represent 1/10 of the total period + !scrollBarTimeFrame.current? scrollBarTimeFrame.current = Math.round(months/10) : null; + // Setting the scrollbar thumb width + const newSBThumbWidth = Math.round((width*0.8 * scrollBarTimeFrame.current *10) / months) / 10; + setSBThumbWidth(newSBThumbWidth); + handleChartScroll( + svgContainer.current.getBoundingClientRect().x + 0.9*svgContainer.current.getBoundingClientRect().width - 20.5 - newSBThumbWidth, + {topLeft: svgContainer.current.getBoundingClientRect().x + width*0.1 + 20.5, width: width*0.8-40.5, height: 20} + ); } } window.addEventListener('resize', updateSize); @@ -72,12 +129,9 @@ const OrbitDataGraph : React.FC = ({ satNum, launchDateString, o return ( <> -

- Graph goes here. -

-
-

Zoom :

+
+

Zoom :

{/* Scrollbar thumb represents the zoom period selected, in case it fits bad we don't display ie. containerSize > SBThumbWidth > 20px */} { (Math.round((svgSize.width*0.8 * 1 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 1 * 10) / months) / 10 > 20) && } @@ -90,27 +144,53 @@ const OrbitDataGraph : React.FC = ({ satNum, launchDateString, o
-
- - {/* Scrollbar for time navigation */} - - - - - - {/* Scrollbar left navigation arrow */} - - - - - {/* Scrollbar right navigation arrow */} - - - - - {/* Scrollbar thumb */} - +
+ {/* Chart */} + + + + + + {return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' }).format(date);}} interval={Math.floor(chartData.length / 6)-1} /> + (0.999*dataMin), (dataMax: number) => (dataMax * 1.001)]} tick={{ fill: '#8884d8' }} tickFormatter={(tick: number) => tick.toFixed(1)} tickLine={false}> + + (0.9*dataMin), (dataMax: number) => (dataMax * 1.1)]} tick={{ fill: '#82ca9d'}} tickFormatter={(tick: number) => tick.toFixed(4)} tickLine={false}> + + (0.999*dataMin), (dataMax: number) => (dataMax * 1.001)]} tick={{ fill: '#ff0000'}} tickFormatter={(tick: number) => tick.toFixed(0)} tickLine={false}> + + + {return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year:'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' }).format(date)}} + contentStyle={{ backgroundColor: 'black' }} + /> + + + + + {/* Scrollbar for time navigation */} + + + + + + + {/* Scrollbar left navigation arrow */} + + + + + {/* Scrollbar right navigation arrow */} + + + + {/* Scrollbar thumb */} + +
diff --git a/package.json b/package.json index f4635ff..4f7d6e4 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "author": "", "license": "", "dependencies": { + "@types/recharts": "^1.8.29", "concurrently": "^8.2.2", "openapi-typescript": "^6.7.4", + "recharts": "^2.12.7", "three-glow-mesh": "^0.1.2" } } From 0d9dbb4cd238b4b0e4406ab5eb26e1149eb951cc Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Tue, 16 Jul 2024 11:21:59 +0200 Subject: [PATCH 03/18] Clean code of orbitalDataGraph, restore strapi files, adding cronTask to the 3rd of each month. --- backend/config/functions/cronTask.js | 14 ++++- backend/config/server.js | 2 +- .../api/satellite/controllers/satellite.js | 15 +---- .../src/api/satellite/services/satellite.js | 63 +------------------ .../[satelliteSlug]/orbitDataGraph.tsx | 7 +-- .../app/satellites/[satelliteSlug]/page.tsx | 2 +- package.json | 1 + 7 files changed, 20 insertions(+), 84 deletions(-) diff --git a/backend/config/functions/cronTask.js b/backend/config/functions/cronTask.js index b39c59e..da4c945 100644 --- a/backend/config/functions/cronTask.js +++ b/backend/config/functions/cronTask.js @@ -5,6 +5,8 @@ */ 'use strict'; +const { fetchOrbitalData } = require('./satelliteUtils'); + module.exports = { updateAllSatellitesData: { task: async ({ strapi }) => { @@ -15,13 +17,21 @@ module.exports = { // Waiting for all promises to be resolved await Promise.all( satellites.map(async satellite => { - await strapi.service('api::satellite.satellite').fetchOrbitalData(satellite.id); + try { + setTimeout(async () => { + await fetchOrbitalData(strapi, satellite.id); + }, 10000); + } catch (error) { + console.error(error); + } }) ); } catch (error) { console.error(error); } }, - options: new Date(Date.now() + 10000), + options: { + rule: "0 0 0 3 * *", // Every month on the 3rd at midnight + }, }, }; diff --git a/backend/config/server.js b/backend/config/server.js index 2133f76..796c641 100644 --- a/backend/config/server.js +++ b/backend/config/server.js @@ -11,7 +11,7 @@ module.exports = ({ env }) => ({ }, cron: { // Enable or disable the cron tasks - enabled: env.bool("CRON_ENABLED", false), + enabled: env.bool("CRON_ENABLED", true), tasks: cronTask, }, }); diff --git a/backend/src/api/satellite/controllers/satellite.js b/backend/src/api/satellite/controllers/satellite.js index 58fba9d..0f4851d 100644 --- a/backend/src/api/satellite/controllers/satellite.js +++ b/backend/src/api/satellite/controllers/satellite.js @@ -6,17 +6,4 @@ const { createCoreController } = require('@strapi/strapi').factories; -module.exports = createCoreController('api::satellite.satellite', ({ strapi }) => ({ - async findOne(ctx) { - // Fetching data from Space-Track - const entity = await strapi.entityService.findOne('api::satellite.satellite', ctx.params.id); - const updatedSatellite = await strapi.service('api::satellite.satellite').fetchOrbitalData(ctx.params.id); - - if (!entity.historicalOrbitalData) { - return updatedSatellite; - } - - return entity; - } -}) -); +module.exports = createCoreController('api::satellite.satellite'); \ No newline at end of file diff --git a/backend/src/api/satellite/services/satellite.js b/backend/src/api/satellite/services/satellite.js index 87a7669..409d67a 100644 --- a/backend/src/api/satellite/services/satellite.js +++ b/backend/src/api/satellite/services/satellite.js @@ -7,67 +7,6 @@ const { createCoreService } = require('@strapi/strapi').factories; -const axios = require('axios'); - -// Function to fetch data from Space-Track such as Eccentricy, SMA, Inclination -async function fetchOrbitalData(contextId) { - try { - // Fetching the satellite - const satellite = await strapi.entityService.findOne('api::satellite.satellite', contextId); - const noradId = satellite.catalogNumberNORAD; - - // Authentification to Space-Track - const authResponse = await axios.post('https://www.space-track.org/ajaxauth/login', { - identity: 'grauleflorian@gmail.com', - password : 'Vm5JxTtD3-hYBdq' - }); - - if (authResponse.status === 200) { - // Fetching data from Space-Track - const satelliteResponse = await axios.get(`https://www.space-track.org/basicspacedata/query/class/gp_history/NORAD_CAT_ID/${noradId}/orderby/TLE_LINE1%20ASC/EPOCH/1950-07-02--2024-07-02/format/json`, { - headers: { - Cookie: authResponse.headers['set-cookie'] - } - }); - - if (satelliteResponse.status === 200) { - // Collecting data - const satelliteData = satelliteResponse.data; - // Parsing correctly the data with wanted data : Inclination, Eccentricity, SMA, and Epoch - const historicalOrbitalData = satelliteData.map(data => { - return { - epoch: data.EPOCH, - inclination: data.INCLINATION, - eccentricity: data.ECCENTRICITY, - semiMajorAxis: data.SEMIMAJOR_AXIS - } - }); - - if (satellite) { - // Updating the satellite with the new data - const updatedSatellite = await strapi.entityService.update('api::satellite.satellite', contextId, { - data: { - historicalOrbitalData: historicalOrbitalData, - }, - }); - return updatedSatellite; - } else { - throw new Error('Satellite not found while updating orbit data'); - } - - - } else { - throw new Error('Error while fetching data from Space-Track'); - } - } else { - throw new Error('Authentication failed'); - } - } catch (error) { - console.error('Error while fetching data to Space-Track: ', error); - } -} - module.exports = { - ...createCoreService('api::satellite.satellite'), - fetchOrbitalData, + ...createCoreService('api::satellite.satellite') }; diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index bf83305..a1fc501 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -7,7 +7,6 @@ import { LaunchDateCountDownProps } from './launchDateCountDown'; import ScrollBarThumb, { ScrollBarThumbProps } from './_orbitDataGraphComponents/ScrollBarThumb'; type OrbitDataProps = { - satNum : SatelliteNumber; launchDateString: LaunchDateCountDownProps['launchDate']; orbitalData: any; } @@ -19,7 +18,7 @@ type ChartData = { semiMajorAxis: number; } -const OrbitDataGraph : React.FC = ({ satNum, launchDateString, orbitalData }) => { +const OrbitDataGraph : React.FC = ({ launchDateString, orbitalData }) => { // href for the svg component, tracking the size of the container const svgContainer = useRef(null); @@ -129,7 +128,7 @@ const OrbitDataGraph : React.FC = ({ satNum, launchDateString, o return ( <> -
+ {orbitalData &&

Zoom :

{/* Scrollbar thumb represents the zoom period selected, in case it fits bad we don't display @@ -193,7 +192,7 @@ const OrbitDataGraph : React.FC = ({ satNum, launchDateString, o
-
+
} ) } diff --git a/frontend/src/app/satellites/[satelliteSlug]/page.tsx b/frontend/src/app/satellites/[satelliteSlug]/page.tsx index 8a6e10a..7b47630 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/page.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/page.tsx @@ -160,7 +160,7 @@ export default async function SatelliteInfoPage({ {/*Pass the satNum and the launchDate as props to OrbitDataGraph*/} { noradId? ( satAttributes?.launchDate ? ( - + ) : null ) : null} diff --git a/package.json b/package.json index 4f7d6e4..ec92778 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "@types/recharts": "^1.8.29", "concurrently": "^8.2.2", + "node-schedule": "^2.1.1", "openapi-typescript": "^6.7.4", "recharts": "^2.12.7", "three-glow-mesh": "^0.1.2" From bddf971c4897a877b7ec000c3ba45a8659ebc3db Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 17 Jul 2024 12:39:00 +0200 Subject: [PATCH 04/18] Adding package*.json files --- backend/config/functions/satelliteUtils.js | 54 +++ frontend/package-lock.json | 36 +- package-lock.json | 463 +++++++++++++++++++++ package.json | 1 + 4 files changed, 533 insertions(+), 21 deletions(-) create mode 100644 backend/config/functions/satelliteUtils.js diff --git a/backend/config/functions/satelliteUtils.js b/backend/config/functions/satelliteUtils.js new file mode 100644 index 0000000..bc9abdc --- /dev/null +++ b/backend/config/functions/satelliteUtils.js @@ -0,0 +1,54 @@ +// backend/utils/satelliteUtils.js +const axios = require('axios'); + +async function fetchOrbitalData(strapi, contextId) { + try { + // Fetching the satellite + const satellite = await strapi.entityService.findOne('api::satellite.satellite', contextId); + const noradId = satellite.catalogNumberNORAD; + + // Authentication to Space-Track + const authResponse = await axios.post('https://www.space-track.org/ajaxauth/login', { + identity: 'floridg@stud.ntnu.no', + password: 'Vm5JxTtD3-hYBdq' + }); + + if (authResponse.status === 200) { + // Fetching data from Space-Track + const satelliteResponse = await axios.get(`https://www.space-track.org/basicspacedata/query/class/gp_history/NORAD_CAT_ID/${noradId}/orderby/TLE_LINE1%20ASC/EPOCH/1950-07-02--2024-07-02/format/json`, { + headers: { + Cookie: authResponse.headers['set-cookie'] + } + }); + + if (satelliteResponse.status === 200) { + // Collecting data + const satelliteData = satelliteResponse.data; + const historicalOrbitalData = satelliteData.map(data => ({ + epoch: data.EPOCH, + inclination: data.INCLINATION, + eccentricity: data.ECCENTRICITY, + semiMajorAxis: data.SEMIMAJOR_AXIS + })); + + // Updating the satellite with the new data + const updatedSatellite = await strapi.entityService.update('api::satellite.satellite', contextId, { + data: { + historicalOrbitalData: historicalOrbitalData, + }, + }); + return updatedSatellite; + } else { + throw new Error('Error while fetching data from Space-Track'); + } + } else { + throw new Error('Authentication failed'); + } + } catch (error) { + console.error('Error while fetching data to Space-Track: ', error); + } +} + +module.exports = { + fetchOrbitalData, +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b9e6f82..a15ae2d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,6 @@ "dependencies": { "@apollo/client": "^3.9.0-alpha.5", "@apollo/experimental-nextjs-app-support": "^0.7.0", - "@gsap/react": "^2.1.0", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", @@ -29,7 +28,7 @@ "@visx/shape": "^3.5.0", "@visx/vendor": "^3.5.0", "add": "^2.0.6", - "chart.js": "^4.4.1", + "chart.js": "^4.4.3", "chartjs-adapter-luxon": "^1.3.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -38,7 +37,6 @@ "framer-motion": "^11.0.24", "globe.gl": "^2.32.2", "gql.tada": "^1.6.2", - "gsap": "^3.12.5", "lucide-react": "^0.314.0", "luxon": "^3.4.4", "next": "14.1.0", @@ -52,6 +50,7 @@ "playwright": "^1.43.1", "qs": "^6.11.2", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.13", "react-markdown": "^9.0.1", @@ -1670,15 +1669,6 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@gsap/react": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@gsap/react/-/react-2.1.0.tgz", - "integrity": "sha512-pwdFXvOM5IsRZXpWTKkQoEjb3/iUjDCU1BCJDlE6pHgVjG+7Ep/7+sszUgqVZ2Jc0mR8gnhtDWyx5cQAT4kwQw==", - "dependencies": { - "gsap": "^3.12.4", - "react": ">=16" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -8061,14 +8051,14 @@ } }, "node_modules/chart.js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz", - "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", "dependencies": { "@kurkle/color": "^0.3.0" }, "engines": { - "pnpm": ">=7" + "pnpm": ">=8" } }, "node_modules/chartjs-adapter-luxon": { @@ -11969,11 +11959,6 @@ "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", "peer": true }, - "node_modules/gsap": { - "version": "3.12.5", - "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz", - "integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ==" - }, "node_modules/h3-js": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/h3-js/-/h3-js-4.1.0.tgz", @@ -16009,6 +15994,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", diff --git a/package-lock.json b/package-lock.json index 35c0c79..3badb7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,12 @@ "name": "it2901-smallsatlab-hypso", "version": "1.0.0", "dependencies": { + "@types/recharts": "^1.8.29", + "ci": "^2.3.0", "concurrently": "^8.2.2", + "node-schedule": "^2.1.1", "openapi-typescript": "^6.7.4", + "recharts": "^2.12.7", "three-glow-mesh": "^0.1.2" } }, @@ -64,6 +68,96 @@ "node": ">= 8" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + }, + "node_modules/@types/react": { + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/recharts": { + "version": "1.8.29", + "resolved": "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.29.tgz", + "integrity": "sha512-ulKklaVsnFIIhTQsQw226TnOibrddW1qUQNFVhoQEyY1Z7FRQrNecFCGt7msRuJseudzE9czVawZb17dK/aPXw==", + "dependencies": { + "@types/d3-shape": "^1", + "@types/react": "*" + } + }, + "node_modules/@types/recharts/node_modules/@types/d3-path": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.11.tgz", + "integrity": "sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==" + }, + "node_modules/@types/recharts/node_modules/@types/d3-shape": { + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.12.tgz", + "integrity": "sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==", + "dependencies": { + "@types/d3-path": "^1" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -136,6 +230,17 @@ "node": ">=8" } }, + "node_modules/ci": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ci/-/ci-2.3.0.tgz", + "integrity": "sha512-0MGXkzJKkwV3enG7RUxjJKdiAkbaZ7visCjitfpCN2BQjv02KGRMxCHLv4RPokkjJ4xR33FLMAXweS+aQ0pFSQ==", + "bin": { + "ci": "dist/cli.js" + }, + "funding": { + "url": "https://github.com/privatenumber/ci?sponsor=1" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -149,6 +254,14 @@ "node": ">=12" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -205,6 +318,132 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -220,6 +459,20 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -233,6 +486,19 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -294,6 +560,14 @@ "node": ">=8" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -329,6 +603,11 @@ "node": ">=0.12.0" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -345,6 +624,30 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -365,6 +668,27 @@ "node": ">=8.6" } }, + "node_modules/node-schedule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", + "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", + "dependencies": { + "cron-parser": "^4.2.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/openapi-typescript": { "version": "6.7.4", "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.4.tgz", @@ -392,6 +716,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -411,6 +745,95 @@ } ] }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/recharts": { + "version": "2.12.7", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.7.tgz", + "integrity": "sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -463,6 +886,15 @@ "tslib": "^2.1.0" } }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/shell-quote": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", @@ -471,6 +903,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, "node_modules/spawn-command": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", @@ -525,6 +962,11 @@ "three": ">= 0.102.0" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -560,6 +1002,27 @@ "node": ">=14.0" } }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index ec92778..27d4d0e 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "license": "", "dependencies": { "@types/recharts": "^1.8.29", + "ci": "^2.3.0", "concurrently": "^8.2.2", "node-schedule": "^2.1.1", "openapi-typescript": "^6.7.4", From d02faf52ac8b417079840975a2e71148de6ae2ef Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 17 Jul 2024 13:09:27 +0200 Subject: [PATCH 05/18] ESLint fixed --- .../_orbitDataGraphComponents/ScrollBarThumb.tsx | 3 ++- frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx index a779b29..1c30f36 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx @@ -5,6 +5,7 @@ import React, { useState, useEffect, useRef } from 'react'; export interface ScrollBarThumbProps { scrollBarThumbWidth: number; svgContainerRect: {topLeft: number, width: number, height: number}; + // eslint-disable-next-line no-unused-vars handleChartScroll: (thumbX: number, svgContainerRect: ScrollBarThumbProps['svgContainerRect']) => void; } @@ -18,7 +19,7 @@ const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, s /* Be careful useEffect runs before parent props are received */ useEffect(() => { - const handleMouseUp = (e: any) => { + const handleMouseUp = () => { if (thumbRef.current) { isDragging.current = false; } diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index a1fc501..1b3f892 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -1,8 +1,7 @@ "use client"; import React, { useState, useLayoutEffect, useRef } from 'react' -import { SatelliteNumber } from '@/lib/store' -import { XAxis, CartesianGrid, Line, LineChart, Tooltip, YAxis, Label, ResponsiveContainer, Legend } from 'recharts'; +import { XAxis, CartesianGrid, Line, LineChart, Tooltip, YAxis, ResponsiveContainer, Legend } from 'recharts'; import { LaunchDateCountDownProps } from './launchDateCountDown'; import ScrollBarThumb, { ScrollBarThumbProps } from './_orbitDataGraphComponents/ScrollBarThumb'; From 7e04e39d2fca729d16d83950c894101ec948c618 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 17 Jul 2024 13:11:54 +0200 Subject: [PATCH 06/18] Updating strapi env --- backend/types/generated/contentTypes.d.ts | 1 + frontend/src/lib/tada/graphql-env.d.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/types/generated/contentTypes.d.ts b/backend/types/generated/contentTypes.d.ts index 8b89107..d247214 100644 --- a/backend/types/generated/contentTypes.d.ts +++ b/backend/types/generated/contentTypes.d.ts @@ -1073,6 +1073,7 @@ export interface ApiSatelliteSatellite extends Schema.CollectionType { slug: Attribute.UID<'api::satellite.satellite', 'name'> & Attribute.Required; massKg: Attribute.Float; + historicalOrbitalData: Attribute.JSON; createdAt: Attribute.DateTime; updatedAt: Attribute.DateTime; publishedAt: Attribute.DateTime; diff --git a/frontend/src/lib/tada/graphql-env.d.ts b/frontend/src/lib/tada/graphql-env.d.ts index 942501c..09074e1 100644 --- a/frontend/src/lib/tada/graphql-env.d.ts +++ b/frontend/src/lib/tada/graphql-env.d.ts @@ -79,12 +79,12 @@ export type introspection = { 'PublicationState': { name: 'PublicationState'; enumValues: 'LIVE' | 'PREVIEW'; }; 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'article': { name: 'article'; type: { kind: 'OBJECT'; name: 'ArticleEntityResponse'; ofType: null; } }; 'articles': { name: 'articles'; type: { kind: 'OBJECT'; name: 'ArticleEntityResponseCollection'; ofType: null; } }; 'author': { name: 'author'; type: { kind: 'OBJECT'; name: 'AuthorEntityResponse'; ofType: null; } }; 'authors': { name: 'authors'; type: { kind: 'OBJECT'; name: 'AuthorEntityResponseCollection'; ofType: null; } }; 'featuredImage': { name: 'featuredImage'; type: { kind: 'OBJECT'; name: 'FeaturedImageEntityResponse'; ofType: null; } }; 'hero': { name: 'hero'; type: { kind: 'OBJECT'; name: 'HeroEntityResponse'; ofType: null; } }; 'homeFeaturedProjects': { name: 'homeFeaturedProjects'; type: { kind: 'OBJECT'; name: 'HomeFeaturedProjectsEntityResponse'; ofType: null; } }; 'homeMissionStatement': { name: 'homeMissionStatement'; type: { kind: 'OBJECT'; name: 'HomeMissionStatementEntityResponse'; ofType: null; } }; 'i18NLocale': { name: 'i18NLocale'; type: { kind: 'OBJECT'; name: 'I18NLocaleEntityResponse'; ofType: null; } }; 'i18NLocales': { name: 'i18NLocales'; type: { kind: 'OBJECT'; name: 'I18NLocaleEntityResponseCollection'; ofType: null; } }; 'me': { name: 'me'; type: { kind: 'OBJECT'; name: 'UsersPermissionsMe'; ofType: null; } }; 'project': { name: 'project'; type: { kind: 'OBJECT'; name: 'ProjectEntityResponse'; ofType: null; } }; 'projects': { name: 'projects'; type: { kind: 'OBJECT'; name: 'ProjectEntityResponseCollection'; ofType: null; } }; 'satellite': { name: 'satellite'; type: { kind: 'OBJECT'; name: 'SatelliteEntityResponse'; ofType: null; } }; 'satellites': { name: 'satellites'; type: { kind: 'OBJECT'; name: 'SatelliteEntityResponseCollection'; ofType: null; } }; 'uploadFile': { name: 'uploadFile'; type: { kind: 'OBJECT'; name: 'UploadFileEntityResponse'; ofType: null; } }; 'uploadFiles': { name: 'uploadFiles'; type: { kind: 'OBJECT'; name: 'UploadFileEntityResponseCollection'; ofType: null; } }; 'uploadFolder': { name: 'uploadFolder'; type: { kind: 'OBJECT'; name: 'UploadFolderEntityResponse'; ofType: null; } }; 'uploadFolders': { name: 'uploadFolders'; type: { kind: 'OBJECT'; name: 'UploadFolderEntityResponseCollection'; ofType: null; } }; 'usersPermissionsRole': { name: 'usersPermissionsRole'; type: { kind: 'OBJECT'; name: 'UsersPermissionsRoleEntityResponse'; ofType: null; } }; 'usersPermissionsRoles': { name: 'usersPermissionsRoles'; type: { kind: 'OBJECT'; name: 'UsersPermissionsRoleEntityResponseCollection'; ofType: null; } }; 'usersPermissionsUser': { name: 'usersPermissionsUser'; type: { kind: 'OBJECT'; name: 'UsersPermissionsUserEntityResponse'; ofType: null; } }; 'usersPermissionsUsers': { name: 'usersPermissionsUsers'; type: { kind: 'OBJECT'; name: 'UsersPermissionsUserEntityResponseCollection'; ofType: null; } }; }; }; 'ResponseCollectionMeta': { kind: 'OBJECT'; name: 'ResponseCollectionMeta'; fields: { 'pagination': { name: 'pagination'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Pagination'; ofType: null; }; } }; }; }; - 'Satellite': { kind: 'OBJECT'; name: 'Satellite'; fields: { 'catalogNumberNORAD': { name: 'catalogNumberNORAD'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'content': { name: 'content'; type: { kind: 'SCALAR'; name: 'JSON'; ofType: null; } }; 'createdAt': { name: 'createdAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'launchDate': { name: 'launchDate'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'massKg': { name: 'massKg'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'missionStatus': { name: 'missionStatus'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'projects': { name: 'projects'; type: { kind: 'OBJECT'; name: 'ProjectRelationResponseCollection'; ofType: null; } }; 'publishedAt': { name: 'publishedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'satelliteImage': { name: 'satelliteImage'; type: { kind: 'OBJECT'; name: 'UploadFileEntityResponse'; ofType: null; } }; 'slug': { name: 'slug'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; }; }; + 'Satellite': { kind: 'OBJECT'; name: 'Satellite'; fields: { 'catalogNumberNORAD': { name: 'catalogNumberNORAD'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'content': { name: 'content'; type: { kind: 'SCALAR'; name: 'JSON'; ofType: null; } }; 'createdAt': { name: 'createdAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'historicalOrbitalData': { name: 'historicalOrbitalData'; type: { kind: 'SCALAR'; name: 'JSON'; ofType: null; } }; 'launchDate': { name: 'launchDate'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'massKg': { name: 'massKg'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'missionStatus': { name: 'missionStatus'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'projects': { name: 'projects'; type: { kind: 'OBJECT'; name: 'ProjectRelationResponseCollection'; ofType: null; } }; 'publishedAt': { name: 'publishedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'satelliteImage': { name: 'satelliteImage'; type: { kind: 'OBJECT'; name: 'UploadFileEntityResponse'; ofType: null; } }; 'slug': { name: 'slug'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; }; }; 'SatelliteEntity': { kind: 'OBJECT'; name: 'SatelliteEntity'; fields: { 'attributes': { name: 'attributes'; type: { kind: 'OBJECT'; name: 'Satellite'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; }; }; 'SatelliteEntityResponse': { kind: 'OBJECT'; name: 'SatelliteEntityResponse'; fields: { 'data': { name: 'data'; type: { kind: 'OBJECT'; name: 'SatelliteEntity'; ofType: null; } }; }; }; 'SatelliteEntityResponseCollection': { kind: 'OBJECT'; name: 'SatelliteEntityResponseCollection'; fields: { 'data': { name: 'data'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SatelliteEntity'; ofType: null; }; }; }; } }; 'meta': { name: 'meta'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ResponseCollectionMeta'; ofType: null; }; } }; }; }; - 'SatelliteFiltersInput': { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; inputFields: [{ name: 'id'; type: { kind: 'INPUT_OBJECT'; name: 'IDFilterInput'; ofType: null; }; defaultValue: null }, { name: 'name'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'catalogNumberNORAD'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'content'; type: { kind: 'INPUT_OBJECT'; name: 'JSONFilterInput'; ofType: null; }; defaultValue: null }, { name: 'projects'; type: { kind: 'INPUT_OBJECT'; name: 'ProjectFiltersInput'; ofType: null; }; defaultValue: null }, { name: 'missionStatus'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'launchDate'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'slug'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'massKg'; type: { kind: 'INPUT_OBJECT'; name: 'FloatFilterInput'; ofType: null; }; defaultValue: null }, { name: 'createdAt'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'updatedAt'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'publishedAt'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'and'; type: { kind: 'LIST'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; ofType: null; }; }; defaultValue: null }, { name: 'or'; type: { kind: 'LIST'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; ofType: null; }; }; defaultValue: null }, { name: 'not'; type: { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; ofType: null; }; defaultValue: null }]; }; - 'SatelliteInput': { kind: 'INPUT_OBJECT'; name: 'SatelliteInput'; inputFields: [{ name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'catalogNumberNORAD'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'content'; type: { kind: 'SCALAR'; name: 'JSON'; ofType: null; }; defaultValue: null }, { name: 'satelliteImage'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'projects'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'missionStatus'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'launchDate'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }, { name: 'slug'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'massKg'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; }; defaultValue: null }, { name: 'publishedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }]; }; + 'SatelliteFiltersInput': { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; inputFields: [{ name: 'id'; type: { kind: 'INPUT_OBJECT'; name: 'IDFilterInput'; ofType: null; }; defaultValue: null }, { name: 'name'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'catalogNumberNORAD'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'content'; type: { kind: 'INPUT_OBJECT'; name: 'JSONFilterInput'; ofType: null; }; defaultValue: null }, { name: 'projects'; type: { kind: 'INPUT_OBJECT'; name: 'ProjectFiltersInput'; ofType: null; }; defaultValue: null }, { name: 'missionStatus'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'launchDate'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'slug'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'massKg'; type: { kind: 'INPUT_OBJECT'; name: 'FloatFilterInput'; ofType: null; }; defaultValue: null }, { name: 'historicalOrbitalData'; type: { kind: 'INPUT_OBJECT'; name: 'JSONFilterInput'; ofType: null; }; defaultValue: null }, { name: 'createdAt'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'updatedAt'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'publishedAt'; type: { kind: 'INPUT_OBJECT'; name: 'DateTimeFilterInput'; ofType: null; }; defaultValue: null }, { name: 'and'; type: { kind: 'LIST'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; ofType: null; }; }; defaultValue: null }, { name: 'or'; type: { kind: 'LIST'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; ofType: null; }; }; defaultValue: null }, { name: 'not'; type: { kind: 'INPUT_OBJECT'; name: 'SatelliteFiltersInput'; ofType: null; }; defaultValue: null }]; }; + 'SatelliteInput': { kind: 'INPUT_OBJECT'; name: 'SatelliteInput'; inputFields: [{ name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'catalogNumberNORAD'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'content'; type: { kind: 'SCALAR'; name: 'JSON'; ofType: null; }; defaultValue: null }, { name: 'satelliteImage'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'projects'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'missionStatus'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'launchDate'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }, { name: 'slug'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'massKg'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; }; defaultValue: null }, { name: 'historicalOrbitalData'; type: { kind: 'SCALAR'; name: 'JSON'; ofType: null; }; defaultValue: null }, { name: 'publishedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }]; }; 'SatelliteRelationResponseCollection': { kind: 'OBJECT'; name: 'SatelliteRelationResponseCollection'; fields: { 'data': { name: 'data'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SatelliteEntity'; ofType: null; }; }; }; } }; }; }; 'String': unknown; 'StringFilterInput': { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; inputFields: [{ name: 'and'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'or'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'not'; type: { kind: 'INPUT_OBJECT'; name: 'StringFilterInput'; ofType: null; }; defaultValue: null }, { name: 'eq'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'eqi'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'ne'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'nei'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'startsWith'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'endsWith'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'contains'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'notContains'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'containsi'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'notContainsi'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'gt'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'gte'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'lt'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'lte'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'null'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }, { name: 'notNull'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; defaultValue: null }, { name: 'in'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'notIn'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'between'; type: { kind: 'LIST'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; From 5869c1e2189b5db561bc712521c5c53939e9dc99 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 17 Jul 2024 14:53:20 +0200 Subject: [PATCH 07/18] Reseting frontend/package.json --- frontend/package-lock.json | 161 +++++++++++++++++++++++++++++++++++++ frontend/package.json | 1 + 2 files changed, 162 insertions(+) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a15ae2d..d5610dc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -55,6 +55,7 @@ "react-error-boundary": "^4.0.13", "react-markdown": "^9.0.1", "react-plotly.js": "^2.6.0", + "recharts": "^2.12.7", "satellite.js": "^5.0.0", "shadcn-ui": "^0.8.0", "tailwind-merge": "^2.2.0", @@ -5327,6 +5328,11 @@ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==" }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, "node_modules/@types/d3-format": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", @@ -5379,6 +5385,11 @@ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", "integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==" }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -8959,6 +8970,14 @@ "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", "peer": true }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-force": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", @@ -9196,6 +9215,11 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", @@ -9411,6 +9435,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dompurify": { "version": "2.4.7", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", @@ -10701,6 +10734,11 @@ "es5-ext": "~0.10.14" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -10809,6 +10847,14 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -16122,6 +16168,20 @@ } } }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -16144,6 +16204,21 @@ } } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -16202,6 +16277,36 @@ "node": ">=4" } }, + "node_modules/recharts": { + "version": "2.12.7", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.7.tgz", + "integrity": "sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -19256,6 +19361,62 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-vendor/node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/victory-vendor/node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/vite": { "version": "5.2.9", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz", diff --git a/frontend/package.json b/frontend/package.json index 351521e..c2e1a78 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -64,6 +64,7 @@ "react-error-boundary": "^4.0.13", "react-markdown": "^9.0.1", "react-plotly.js": "^2.6.0", + "recharts": "^2.12.7", "satellite.js": "^5.0.0", "shadcn-ui": "^0.8.0", "tailwind-merge": "^2.2.0", From 535cc084ad741ed2c891da73881aae733b88f631 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 17 Jul 2024 15:00:46 +0200 Subject: [PATCH 08/18] Prettier checked --- .../ScrollBarThumb.tsx | 110 ++- .../[satelliteSlug]/orbitDataGraph.tsx | 677 +++++++++++++----- .../app/satellites/[satelliteSlug]/page.tsx | 8 +- frontend/src/components/layout/Footer.tsx | 3 +- 4 files changed, 575 insertions(+), 223 deletions(-) diff --git a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx index 1c30f36..65223ce 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx @@ -1,17 +1,26 @@ "use client"; -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef } from "react"; export interface ScrollBarThumbProps { scrollBarThumbWidth: number; - svgContainerRect: {topLeft: number, width: number, height: number}; + svgContainerRect: { topLeft: number; width: number; height: number }; // eslint-disable-next-line no-unused-vars - handleChartScroll: (thumbX: number, svgContainerRect: ScrollBarThumbProps['svgContainerRect']) => void; + handleChartScroll: ( + thumbX: number, + svgContainerRect: ScrollBarThumbProps["svgContainerRect"], + ) => void; } -const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, svgContainerRect, handleChartScroll}) => { +const ScrollBarThumb: React.FC = ({ + scrollBarThumbWidth, + svgContainerRect, + handleChartScroll, +}) => { const isDragging = useRef(false); - {/* SB is used for ScrollBar */} + { + /* SB is used for ScrollBar */ + } const [sBThumbX, setSBThumbX] = useState(0); const thumbRef = useRef(null); // Distance between the left of the thumb and the mouse click @@ -23,66 +32,109 @@ const ScrollBarThumb : React.FC = ({ scrollBarThumbWidth, s if (thumbRef.current) { isDragging.current = false; } - } + }; const handleMouseMove = (e: MouseEvent) => { if (isDragging.current && thumbRef.current) { // Scrollbar starts at the right of the svg container and goes to the left by increasing SBThumbX setSBThumbX(() => { // Calculating min and max x positions following the SBThumbX axis for moving the thumb with mouse movement - const minX= 0.5; + const minX = 0.5; const maxX = svgContainerRect.width - scrollBarThumbWidth; // newPos represents left border of the thumb - const newPos = svgContainerRect.topLeft + svgContainerRect.width - scrollBarThumbWidth - (e.clientX - (distThumbClick.current? distThumbClick.current : 0)); + const newPos = + svgContainerRect.topLeft + + svgContainerRect.width - + scrollBarThumbWidth - + (e.clientX - + (distThumbClick.current + ? distThumbClick.current + : 0)); // If mouse movement isn't in the scrollable area if (newPos <= minX) { return minX; } else if (newPos >= maxX) { return maxX; - } + } return newPos; }); // Change the displayed data on the chart - handleChartScroll(Math.round(thumbRef.current.getBoundingClientRect().x * 10) / 10, svgContainerRect); + handleChartScroll( + Math.round( + thumbRef.current.getBoundingClientRect().x * 10, + ) / 10, + svgContainerRect, + ); } - } + }; - window.addEventListener('mouseup', handleMouseUp); - window.addEventListener('mousemove', handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + window.addEventListener("mousemove", handleMouseMove); // Managing the resize of the thumb - if (thumbRef.current && thumbRef.current.getBoundingClientRect().x < svgContainerRect.topLeft) { + if ( + thumbRef.current && + thumbRef.current.getBoundingClientRect().x < + svgContainerRect.topLeft + ) { setSBThumbX(svgContainerRect.width - scrollBarThumbWidth); } return () => { - window.removeEventListener('mouseup', handleMouseUp); - window.removeEventListener('mousemove', handleMouseMove); - } + window.removeEventListener("mouseup", handleMouseUp); + window.removeEventListener("mousemove", handleMouseMove); + }; }, [scrollBarThumbWidth, svgContainerRect]); - const handleMouseDown = (e: React.MouseEvent) => { + const handleMouseDown = ( + e: React.MouseEvent, + ) => { // If the thumb is clicked with left mouse button, we start dragging if (thumbRef.current && e.button === 0) { isDragging.current = true; - distThumbClick.current = e.clientX - thumbRef.current.getBoundingClientRect().left; + distThumbClick.current = + e.clientX - thumbRef.current.getBoundingClientRect().left; } - } + }; return ( <> - - - - + + + + ); -} +}; -export default ScrollBarThumb; \ No newline at end of file +export default ScrollBarThumb; diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index 1b3f892..5c6295f 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -1,199 +1,496 @@ "use client"; -import React, { useState, useLayoutEffect, useRef } from 'react' -import { XAxis, CartesianGrid, Line, LineChart, Tooltip, YAxis, ResponsiveContainer, Legend } from 'recharts'; -import { LaunchDateCountDownProps } from './launchDateCountDown'; -import ScrollBarThumb, { ScrollBarThumbProps } from './_orbitDataGraphComponents/ScrollBarThumb'; +import React, { useState, useLayoutEffect, useRef } from "react"; +import { + XAxis, + CartesianGrid, + Line, + LineChart, + Tooltip, + YAxis, + ResponsiveContainer, + Legend, +} from "recharts"; +import { LaunchDateCountDownProps } from "./launchDateCountDown"; +import ScrollBarThumb, { + ScrollBarThumbProps, +} from "./_orbitDataGraphComponents/ScrollBarThumb"; type OrbitDataProps = { - launchDateString: LaunchDateCountDownProps['launchDate']; - orbitalData: any; -} + launchDateString: LaunchDateCountDownProps["launchDate"]; + orbitalData: any; +}; type ChartData = { - epoch: Date; - inclination: number; - eccentricity: number; - semiMajorAxis: number; -} - -const OrbitDataGraph : React.FC = ({ launchDateString, orbitalData }) => { - - // href for the svg component, tracking the size of the container - const svgContainer = useRef(null); - const [svgSize, setSvgSize] = useState({width: 0, height: 200}); - {/* SB use for ScrollBar*/} - const [scrollBarThumbWidth, setSBThumbWidth] = useState(0); - // scrollBarTimeFrame is how many months the scrollbar thumb represents - const scrollBarTimeFrame = useRef(0); - // Chart Data - const [chartData, setChartData] = useState([]); - - // Handling button for zooming in and out of the graph on a time scale - const launchDate = launchDateString? new Date(launchDateString) : new Date(); - const calculateMonthsDiff = () => { - const currentDate = new Date(); - return currentDate.getMonth() - launchDate.getMonth() + (12 * (currentDate.getFullYear() - launchDate.getFullYear())); - } - - const months = calculateMonthsDiff(); - - const handleZoomClick = (e: React.MouseEvent) => { - // We round the width of the scrollbar thumb at one decimal - const period = e.currentTarget.textContent; - const regExp = new RegExp('^([1234567890]+)([my])$'); - const match = period?.match(regExp); - let SBThumbWidth = 0; - - if (period === "All") { - SBThumbWidth = svgSize.width*0.8-40.5; - scrollBarTimeFrame.current = months; - setSBThumbWidth(SBThumbWidth); - } else { - const number = match ? parseInt(match[1]) : 0; - const periodType = match? match[2] : ""; - - if (periodType === "m") { - scrollBarTimeFrame.current = number; - SBThumbWidth = Math.round((svgSize.width*0.8 * number *10) / months) / 10; - setSBThumbWidth(SBThumbWidth); - } else if (periodType === "y") { - scrollBarTimeFrame.current = number * 12; - SBThumbWidth = Math.round((svgSize.width*0.8 * number * 12 *10) / months) / 10 - setSBThumbWidth(SBThumbWidth); - } - } - // Updating chart due to resizing the scrollbar thumb - if (svgContainer.current) { - handleChartScroll( - svgContainer.current.getBoundingClientRect().x + 0.9*svgContainer.current.getBoundingClientRect().width - 20.5 - SBThumbWidth, - {topLeft: svgContainer.current.getBoundingClientRect().x + svgSize.width*0.1 + 20.5, width: svgSize.width*0.8-40.5, height: 20} - ); + epoch: Date; + inclination: number; + eccentricity: number; + semiMajorAxis: number; +}; + +const OrbitDataGraph: React.FC = ({ + launchDateString, + orbitalData, +}) => { + // href for the svg component, tracking the size of the container + const svgContainer = useRef(null); + const [svgSize, setSvgSize] = useState({ width: 0, height: 200 }); + { + /* SB use for ScrollBar*/ } - } - - - // Callback function for updating the chart when the scrollbar thumb is moved - const handleChartScroll = (thumbX: number, svgContainerRect: ScrollBarThumbProps['svgContainerRect']) => { - - // Ratio of the thumb left border position to the svg container width - const dateRatio = (thumbX - svgContainerRect.topLeft)/svgContainerRect.width; - // Taking the last date of the data period (first date is the launch date) - const lastDataDate = new Date(orbitalData[orbitalData.length-1].epoch.slice(0, 23) + "Z"); - // Calculating the displayed period in milliseconds - const displayedPeriodMs = lastDataDate.getTime() - launchDate.getTime(); - // Calculating the first and last date of the chart - const firstChartDate = new Date(launchDate.getTime() + displayedPeriodMs * dateRatio); - const lastChartDate = new Date(firstChartDate); - lastChartDate.setMonth(firstChartDate.getMonth() + scrollBarTimeFrame.current); - - // Filtering the data to display only the data in the selected period - const filteredData = orbitalData.filter((data: any) => { - const dataDate = new Date(data.epoch.slice(0, 23) + "Z"); - return dataDate >= firstChartDate && dataDate <= lastChartDate; - }).map((data: any) => { - return { - ...data, - epoch: new Date(data.epoch.slice(0, 23) + "Z") - }; - }); - - setChartData(filteredData); - }; - - // Layout effect to track the size of the container and update the svg size - useLayoutEffect(() => { - const updateSize = () => { - if (svgContainer.current) { - // Update svg container size - const width = svgContainer.current.offsetWidth; - setSvgSize((prevSvgSize) => {return { width: width, height: prevSvgSize.height }}); - // Initially set the scrollbar thumb width to represent 1/10 of the total period - !scrollBarTimeFrame.current? scrollBarTimeFrame.current = Math.round(months/10) : null; - // Setting the scrollbar thumb width - const newSBThumbWidth = Math.round((width*0.8 * scrollBarTimeFrame.current *10) / months) / 10; - setSBThumbWidth(newSBThumbWidth); - handleChartScroll( - svgContainer.current.getBoundingClientRect().x + 0.9*svgContainer.current.getBoundingClientRect().width - 20.5 - newSBThumbWidth, - {topLeft: svgContainer.current.getBoundingClientRect().x + width*0.1 + 20.5, width: width*0.8-40.5, height: 20} + const [scrollBarThumbWidth, setSBThumbWidth] = useState(0); + // scrollBarTimeFrame is how many months the scrollbar thumb represents + const scrollBarTimeFrame = useRef(0); + // Chart Data + const [chartData, setChartData] = useState([]); + + // Handling button for zooming in and out of the graph on a time scale + const launchDate = launchDateString + ? new Date(launchDateString) + : new Date(); + const calculateMonthsDiff = () => { + const currentDate = new Date(); + return ( + currentDate.getMonth() - + launchDate.getMonth() + + 12 * (currentDate.getFullYear() - launchDate.getFullYear()) ); - } - } - window.addEventListener('resize', updateSize); - updateSize(); - - return () => window.removeEventListener('resize', updateSize); - }, []); - - return ( - <> - {orbitalData &&
-
-

Zoom :

- {/* Scrollbar thumb represents the zoom period selected, in case it fits bad we don't display + }; + + const months = calculateMonthsDiff(); + + const handleZoomClick = (e: React.MouseEvent) => { + // We round the width of the scrollbar thumb at one decimal + const period = e.currentTarget.textContent; + const regExp = new RegExp("^([1234567890]+)([my])$"); + const match = period?.match(regExp); + let SBThumbWidth = 0; + + if (period === "All") { + SBThumbWidth = svgSize.width * 0.8 - 40.5; + scrollBarTimeFrame.current = months; + setSBThumbWidth(SBThumbWidth); + } else { + const number = match ? parseInt(match[1]) : 0; + const periodType = match ? match[2] : ""; + + if (periodType === "m") { + scrollBarTimeFrame.current = number; + SBThumbWidth = + Math.round((svgSize.width * 0.8 * number * 10) / months) / + 10; + setSBThumbWidth(SBThumbWidth); + } else if (periodType === "y") { + scrollBarTimeFrame.current = number * 12; + SBThumbWidth = + Math.round( + (svgSize.width * 0.8 * number * 12 * 10) / months, + ) / 10; + setSBThumbWidth(SBThumbWidth); + } + } + // Updating chart due to resizing the scrollbar thumb + if (svgContainer.current) { + handleChartScroll( + svgContainer.current.getBoundingClientRect().x + + 0.9 * svgContainer.current.getBoundingClientRect().width - + 20.5 - + SBThumbWidth, + { + topLeft: + svgContainer.current.getBoundingClientRect().x + + svgSize.width * 0.1 + + 20.5, + width: svgSize.width * 0.8 - 40.5, + height: 20, + }, + ); + } + }; + + // Callback function for updating the chart when the scrollbar thumb is moved + const handleChartScroll = ( + thumbX: number, + svgContainerRect: ScrollBarThumbProps["svgContainerRect"], + ) => { + // Ratio of the thumb left border position to the svg container width + const dateRatio = + (thumbX - svgContainerRect.topLeft) / svgContainerRect.width; + // Taking the last date of the data period (first date is the launch date) + const lastDataDate = new Date( + orbitalData[orbitalData.length - 1].epoch.slice(0, 23) + "Z", + ); + // Calculating the displayed period in milliseconds + const displayedPeriodMs = lastDataDate.getTime() - launchDate.getTime(); + // Calculating the first and last date of the chart + const firstChartDate = new Date( + launchDate.getTime() + displayedPeriodMs * dateRatio, + ); + const lastChartDate = new Date(firstChartDate); + lastChartDate.setMonth( + firstChartDate.getMonth() + scrollBarTimeFrame.current, + ); + + // Filtering the data to display only the data in the selected period + const filteredData = orbitalData + .filter((data: any) => { + const dataDate = new Date(data.epoch.slice(0, 23) + "Z"); + return dataDate >= firstChartDate && dataDate <= lastChartDate; + }) + .map((data: any) => { + return { + ...data, + epoch: new Date(data.epoch.slice(0, 23) + "Z"), + }; + }); + + setChartData(filteredData); + }; + + // Layout effect to track the size of the container and update the svg size + useLayoutEffect(() => { + const updateSize = () => { + if (svgContainer.current) { + // Update svg container size + const width = svgContainer.current.offsetWidth; + setSvgSize((prevSvgSize) => { + return { width: width, height: prevSvgSize.height }; + }); + // Initially set the scrollbar thumb width to represent 1/10 of the total period + !scrollBarTimeFrame.current + ? (scrollBarTimeFrame.current = Math.round(months / 10)) + : null; + // Setting the scrollbar thumb width + const newSBThumbWidth = + Math.round( + (width * 0.8 * scrollBarTimeFrame.current * 10) / + months, + ) / 10; + setSBThumbWidth(newSBThumbWidth); + handleChartScroll( + svgContainer.current.getBoundingClientRect().x + + 0.9 * + svgContainer.current.getBoundingClientRect().width - + 20.5 - + newSBThumbWidth, + { + topLeft: + svgContainer.current.getBoundingClientRect().x + + width * 0.1 + + 20.5, + width: width * 0.8 - 40.5, + height: 20, + }, + ); + } + }; + window.addEventListener("resize", updateSize); + updateSize(); + + return () => window.removeEventListener("resize", updateSize); + }, []); + + return ( + <> + {orbitalData && ( +
+
+

Zoom :

+ {/* Scrollbar thumb represents the zoom period selected, in case it fits bad we don't display ie. containerSize > SBThumbWidth > 20px */} - { (Math.round((svgSize.width*0.8 * 1 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 1 * 10) / months) / 10 > 20) && } - { (Math.round((svgSize.width*0.8 * 3 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 3 * 10) / months) / 10 > 40) && } - { (Math.round((svgSize.width*0.8 * 6 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 6 * 10) / months) / 10 > 40) && } - { (Math.round((svgSize.width*0.8 * 12 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 10) / months) / 10 > 40) && } - { (Math.round((svgSize.width*0.8 * 12 * 5 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 5 * 10) / months) / 10 > 40) && } - { (Math.round((svgSize.width*0.8 * 12 * 10 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 10 * 10) / months) / 10 > 40) && } - { (Math.round((svgSize.width*0.8 * 12 * 20 * 10) / months) / 10 < svgSize.width) && (Math.round((svgSize.width*0.8 * 12 * 20 * 10) / months) / 10 > 40) && } - - -
-
- {/* Chart */} - - - - - - {return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric' }).format(date);}} interval={Math.floor(chartData.length / 6)-1} /> - (0.999*dataMin), (dataMax: number) => (dataMax * 1.001)]} tick={{ fill: '#8884d8' }} tickFormatter={(tick: number) => tick.toFixed(1)} tickLine={false}> - - (0.9*dataMin), (dataMax: number) => (dataMax * 1.1)]} tick={{ fill: '#82ca9d'}} tickFormatter={(tick: number) => tick.toFixed(4)} tickLine={false}> - - (0.999*dataMin), (dataMax: number) => (dataMax * 1.001)]} tick={{ fill: '#ff0000'}} tickFormatter={(tick: number) => tick.toFixed(0)} tickLine={false}> - - - {return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year:'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' }).format(date)}} - contentStyle={{ backgroundColor: 'black' }} - /> - - - - - {/* Scrollbar for time navigation */} - - - - - - - {/* Scrollbar left navigation arrow */} - - - - - {/* Scrollbar right navigation arrow */} - - - - - {/* Scrollbar thumb */} - - - -
-
} - - ) -} - -export default OrbitDataGraph; \ No newline at end of file + {Math.round((svgSize.width * 0.8 * 1 * 10) / months) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 1 * 10) / months, + ) / + 10 > + 20 && ( + + )} + {Math.round((svgSize.width * 0.8 * 3 * 10) / months) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 3 * 10) / months, + ) / + 10 > + 40 && ( + + )} + {Math.round((svgSize.width * 0.8 * 6 * 10) / months) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 6 * 10) / months, + ) / + 10 > + 40 && ( + + )} + {Math.round((svgSize.width * 0.8 * 12 * 10) / months) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 12 * 10) / months, + ) / + 10 > + 40 && ( + + )} + {Math.round( + (svgSize.width * 0.8 * 12 * 5 * 10) / months, + ) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 12 * 5 * 10) / months, + ) / + 10 > + 40 && ( + + )} + {Math.round( + (svgSize.width * 0.8 * 12 * 10 * 10) / months, + ) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 12 * 10 * 10) / months, + ) / + 10 > + 40 && ( + + )} + {Math.round( + (svgSize.width * 0.8 * 12 * 20 * 10) / months, + ) / + 10 < + svgSize.width && + Math.round( + (svgSize.width * 0.8 * 12 * 20 * 10) / months, + ) / + 10 > + 40 && ( + + )} + + +
+
+ {/* Chart */} + + + + + + { + return new Intl.DateTimeFormat( + "en-US", + { month: "short", day: "numeric" }, + ).format(date); + }} + interval={ + Math.floor(chartData.length / 6) - 1 + } + /> + 0.999 * dataMin, + (dataMax: number) => dataMax * 1.001, + ]} + tick={{ fill: "#8884d8" }} + tickFormatter={(tick: number) => + tick.toFixed(1) + } + tickLine={false} + > + 0.9 * dataMin, + (dataMax: number) => dataMax * 1.1, + ]} + tick={{ fill: "#82ca9d" }} + tickFormatter={(tick: number) => + tick.toFixed(4) + } + tickLine={false} + > + 0.999 * dataMin, + (dataMax: number) => dataMax * 1.001, + ]} + tick={{ fill: "#ff0000" }} + tickFormatter={(tick: number) => + tick.toFixed(0) + } + tickLine={false} + > + + { + return new Intl.DateTimeFormat( + "en-US", + { + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + }, + ).format(date); + }} + contentStyle={{ backgroundColor: "black" }} + /> + + + + + {/* Scrollbar for time navigation */} + + + + + + + {/* Scrollbar left navigation arrow */} + + + + + {/* Scrollbar right navigation arrow */} + + + + + {/* Scrollbar thumb */} + + + +
+
+ )} + + ); +}; + +export default OrbitDataGraph; diff --git a/frontend/src/app/satellites/[satelliteSlug]/page.tsx b/frontend/src/app/satellites/[satelliteSlug]/page.tsx index 7b47630..5ee8338 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/page.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/page.tsx @@ -158,11 +158,13 @@ export default async function SatelliteInfoPage({ {/* Orbit data */}
{/*Pass the satNum and the launchDate as props to OrbitDataGraph*/} - { noradId? ( + {noradId ? ( satAttributes?.launchDate ? ( - + ) : null - ) : null}
diff --git a/frontend/src/components/layout/Footer.tsx b/frontend/src/components/layout/Footer.tsx index 846f34a..05759fe 100644 --- a/frontend/src/components/layout/Footer.tsx +++ b/frontend/src/components/layout/Footer.tsx @@ -51,7 +51,8 @@ export default function Footer() {

- © 2017 - {now.getFullYear()} NTNU Small Satellite Lab + © 2017 - {now.getFullYear()} NTNU Small + Satellite Lab

From 7cdad3262a333855fe652703bbb2e9b0d4dedf02 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 17 Jul 2024 15:09:56 +0200 Subject: [PATCH 09/18] Updating for ESlint check --- .../_orbitDataGraphComponents/ScrollBarThumb.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx index 65223ce..c936914 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/_orbitDataGraphComponents/ScrollBarThumb.tsx @@ -5,11 +5,12 @@ import React, { useState, useEffect, useRef } from "react"; export interface ScrollBarThumbProps { scrollBarThumbWidth: number; svgContainerRect: { topLeft: number; width: number; height: number }; - // eslint-disable-next-line no-unused-vars + /* eslint-disable no-unused-vars*/ handleChartScroll: ( thumbX: number, svgContainerRect: ScrollBarThumbProps["svgContainerRect"], ) => void; + /* eslint-enable no-unused-vars*/ } const ScrollBarThumb: React.FC = ({ From 88a46fb157ab99a06248e064b1c228fdde788dd0 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 24 Jul 2024 10:21:36 +0200 Subject: [PATCH 10/18] Adding backend env file --- backend/.env | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 backend/.env diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..83d5b8d --- /dev/null +++ b/backend/.env @@ -0,0 +1,14 @@ +# All these should be set in the GitHub Repository actions and secrets at https://github.com/NTNU-SmallSat-Lab/outreach-website/settings/variables/actions +# Any changes done here will not be reflected in the production environment +# All variables named toBeGenerated should be generated with the command `openssl rand -base64 32` +HOST=0.0.0.0 +PORT=1337 +STRAPI_URL="https://hypso.space/strapi/" +APP_KEYS="LlikgA/Dce0U0qy3b3jp5FwdipVsgLrUlbC4EiJczsg=,rweQcOIfQvkiQAcOPjRujpitslVAEesx4fkT09qMhNY=" +API_TOKEN_SALT="Nh97EoxmLk6zqzG5HXn/9NvpPHeTNs8Sz2XHIXGry1A=" +ADMIN_JWT_SECRET="jqFr2Ocz4j7xqK/wJMdfq+QSIlEnIuNchX3l0PD9bqk=" +TRANSFER_TOKEN_SALT="3JKja5Orq7jWr+HOfF/VKLgL9o2rvgX14NnOfskzo/k=" +JWT_SECRET="VHe12SBqQRQcnIAhUMbMfCbBLj8EvIkmcGRO7FZKMPM=" + +DATABASE_CLIENT=sqlite +DATABASE_FILENAME=dbLocation.sqlite # in dev, will use backend root dir, in prod, will be from server os root dir so should be /var/data/strapi.db or similar From 44f3712195aa71470321552b3bc6b85f3bd13491 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Mon, 29 Jul 2024 10:59:59 +0200 Subject: [PATCH 11/18] Revert "Adding backend env file" This reverts commit 88a46fb157ab99a06248e064b1c228fdde788dd0. --- backend/.env | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 backend/.env diff --git a/backend/.env b/backend/.env deleted file mode 100644 index 83d5b8d..0000000 --- a/backend/.env +++ /dev/null @@ -1,14 +0,0 @@ -# All these should be set in the GitHub Repository actions and secrets at https://github.com/NTNU-SmallSat-Lab/outreach-website/settings/variables/actions -# Any changes done here will not be reflected in the production environment -# All variables named toBeGenerated should be generated with the command `openssl rand -base64 32` -HOST=0.0.0.0 -PORT=1337 -STRAPI_URL="https://hypso.space/strapi/" -APP_KEYS="LlikgA/Dce0U0qy3b3jp5FwdipVsgLrUlbC4EiJczsg=,rweQcOIfQvkiQAcOPjRujpitslVAEesx4fkT09qMhNY=" -API_TOKEN_SALT="Nh97EoxmLk6zqzG5HXn/9NvpPHeTNs8Sz2XHIXGry1A=" -ADMIN_JWT_SECRET="jqFr2Ocz4j7xqK/wJMdfq+QSIlEnIuNchX3l0PD9bqk=" -TRANSFER_TOKEN_SALT="3JKja5Orq7jWr+HOfF/VKLgL9o2rvgX14NnOfskzo/k=" -JWT_SECRET="VHe12SBqQRQcnIAhUMbMfCbBLj8EvIkmcGRO7FZKMPM=" - -DATABASE_CLIENT=sqlite -DATABASE_FILENAME=dbLocation.sqlite # in dev, will use backend root dir, in prod, will be from server os root dir so should be /var/data/strapi.db or similar From 37456ada5dbee1c6822b2b07bfafe44777623465 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 7 Aug 2024 16:42:32 +0200 Subject: [PATCH 12/18] Try fixing cron job, error caused by Strapi reloading after updating db --- backend/config/functions/cronTask.js | 29 +++++++++++----------- backend/config/functions/satelliteUtils.js | 22 +++------------- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/backend/config/functions/cronTask.js b/backend/config/functions/cronTask.js index da4c945..915ca03 100644 --- a/backend/config/functions/cronTask.js +++ b/backend/config/functions/cronTask.js @@ -11,27 +11,28 @@ module.exports = { updateAllSatellitesData: { task: async ({ strapi }) => { try { - // Fetching all satellites - const satellites = await strapi.entityService.findMany('api::satellite.satellite'); - - // Waiting for all promises to be resolved - await Promise.all( - satellites.map(async satellite => { - try { - setTimeout(async () => { - await fetchOrbitalData(strapi, satellite.id); - }, 10000); - } catch (error) { - console.error(error); + // Fetch all satellites + const satellites = await strapi.entityService.findMany('api::satellite.satellite', { + fields: ['id', 'catalogNumberNORAD'], + filters: { + catalogNumberNORAD: { $ne: null }, + } + }); + await Promise.all(satellites.map(async satellite => { + const historicalOrbitalData = await fetchOrbitalData(strapi, satellite.catalogNumberNORAD); + await strapi.entityService.update('api::satellite.satellite', satellite.id, { + data: { + historicalOrbitalData: historicalOrbitalData, } }) - ); + })); } catch (error) { console.error(error); + return; } }, options: { - rule: "0 0 0 3 * *", // Every month on the 3rd at midnight + rule: "0 0 0 8 * *", // Every month on the 3rd at midnight }, }, }; diff --git a/backend/config/functions/satelliteUtils.js b/backend/config/functions/satelliteUtils.js index bc9abdc..65d233c 100644 --- a/backend/config/functions/satelliteUtils.js +++ b/backend/config/functions/satelliteUtils.js @@ -1,12 +1,8 @@ // backend/utils/satelliteUtils.js const axios = require('axios'); -async function fetchOrbitalData(strapi, contextId) { +async function fetchOrbitalData(strapi, noradId) { try { - // Fetching the satellite - const satellite = await strapi.entityService.findOne('api::satellite.satellite', contextId); - const noradId = satellite.catalogNumberNORAD; - // Authentication to Space-Track const authResponse = await axios.post('https://www.space-track.org/ajaxauth/login', { identity: 'floridg@stud.ntnu.no', @@ -15,7 +11,8 @@ async function fetchOrbitalData(strapi, contextId) { if (authResponse.status === 200) { // Fetching data from Space-Track - const satelliteResponse = await axios.get(`https://www.space-track.org/basicspacedata/query/class/gp_history/NORAD_CAT_ID/${noradId}/orderby/TLE_LINE1%20ASC/EPOCH/1950-07-02--2024-07-02/format/json`, { + const today = new Date(); + const satelliteResponse = await axios.get(`https://www.space-track.org/basicspacedata/query/class/gp_history/NORAD_CAT_ID/${noradId}/orderby/TLE_LINE1%20ASC/orderby/TLE_LINE1%20ASC/format/json`, { headers: { Cookie: authResponse.headers['set-cookie'] } @@ -30,19 +27,8 @@ async function fetchOrbitalData(strapi, contextId) { eccentricity: data.ECCENTRICITY, semiMajorAxis: data.SEMIMAJOR_AXIS })); - - // Updating the satellite with the new data - const updatedSatellite = await strapi.entityService.update('api::satellite.satellite', contextId, { - data: { - historicalOrbitalData: historicalOrbitalData, - }, - }); - return updatedSatellite; - } else { - throw new Error('Error while fetching data from Space-Track'); + return historicalOrbitalData; } - } else { - throw new Error('Authentication failed'); } } catch (error) { console.error('Error while fetching data to Space-Track: ', error); From 5dfb7e5bccd9a3ccb9aa3ee3e07418786a52f6bc Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Mon, 12 Aug 2024 14:50:57 +0200 Subject: [PATCH 13/18] Fix cron jobs issue and update Footer info --- backend/config/functions/cronTask.js | 27 ++++++++++++++-------- backend/config/functions/satelliteUtils.js | 2 +- frontend/src/components/layout/Footer.tsx | 14 ++++------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/backend/config/functions/cronTask.js b/backend/config/functions/cronTask.js index 915ca03..55d1b98 100644 --- a/backend/config/functions/cronTask.js +++ b/backend/config/functions/cronTask.js @@ -18,21 +18,30 @@ module.exports = { catalogNumberNORAD: { $ne: null }, } }); - await Promise.all(satellites.map(async satellite => { - const historicalOrbitalData = await fetchOrbitalData(strapi, satellite.catalogNumberNORAD); - await strapi.entityService.update('api::satellite.satellite', satellite.id, { - data: { - historicalOrbitalData: historicalOrbitalData, - } - }) - })); + await Promise.all( + satellites.map(async (satellite) => { + const fetchedData = await fetchOrbitalData(satellite.catalogNumberNORAD); + return { id: satellite.id, historicalOrbitalData: fetchedData }; + })).then(async (historicalOrbitalData) => { + await strapi.db.transaction(async (trx) => { + // Fetch data for each satellite + historicalOrbitalData.map(async (satellite) => { + // Update the database with the new data + const updatedSat = await strapi.entityService.update('api::satellite.satellite', satellite.id, { + data: { + historicalOrbitalData: satellite.historicalOrbitalData, + }, + }, { trx }); + }) + }); + }) } catch (error) { console.error(error); return; } }, options: { - rule: "0 0 0 8 * *", // Every month on the 3rd at midnight + rule: "0 0 0 3 * *", // Every month on the 3rd at midnight }, }, }; diff --git a/backend/config/functions/satelliteUtils.js b/backend/config/functions/satelliteUtils.js index 65d233c..a43f2af 100644 --- a/backend/config/functions/satelliteUtils.js +++ b/backend/config/functions/satelliteUtils.js @@ -1,7 +1,7 @@ // backend/utils/satelliteUtils.js const axios = require('axios'); -async function fetchOrbitalData(strapi, noradId) { +async function fetchOrbitalData(noradId) { try { // Authentication to Space-Track const authResponse = await axios.post('https://www.space-track.org/ajaxauth/login', { diff --git a/frontend/src/components/layout/Footer.tsx b/frontend/src/components/layout/Footer.tsx index 05759fe..d91613b 100644 --- a/frontend/src/components/layout/Footer.tsx +++ b/frontend/src/components/layout/Footer.tsx @@ -18,28 +18,22 @@ export default function Footer() {
From c2c504653204509afccdd567719f1f893979ef82 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Tue, 13 Aug 2024 19:24:03 +0200 Subject: [PATCH 14/18] Fixing not available satellites --- .../app/satellites/SatelliteResponsiveTable.tsx | 17 ++++++++--------- .../[satelliteSlug]/orbitDataGraph.tsx | 5 +++++ .../src/app/satellites/[satelliteSlug]/page.tsx | 4 ++-- frontend/src/components/layout/Footer.tsx | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/satellites/SatelliteResponsiveTable.tsx b/frontend/src/app/satellites/SatelliteResponsiveTable.tsx index 72bd56d..484472e 100644 --- a/frontend/src/app/satellites/SatelliteResponsiveTable.tsx +++ b/frontend/src/app/satellites/SatelliteResponsiveTable.tsx @@ -99,15 +99,14 @@ export default function SatelliteResponsiveTable({ /> )) : (satellites ?? []).map((satellite) => ( - - handleRowClick( - satellite.attributes?.slug ?? "", - ) - } - > + + handleRowClick(satellite.attributes?.slug ?? "") + } : {})} + > {satellite.attributes?.name} diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index 5c6295f..b97b3b9 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -194,6 +194,11 @@ const OrbitDataGraph: React.FC = ({ <> {orbitalData && (
+
+

Orbital parameters history (source :

+ Space Track +

)

+

Zoom :

{/* Scrollbar thumb represents the zoom period selected, in case it fits bad we don't display diff --git a/frontend/src/app/satellites/[satelliteSlug]/page.tsx b/frontend/src/app/satellites/[satelliteSlug]/page.tsx index 5ee8338..e0ed7d1 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/page.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/page.tsx @@ -155,9 +155,9 @@ export default async function SatelliteInfoPage({
- {/* Orbit data */} + {/* Container for graph of historical orbital data */}
- {/*Pass the satNum and the launchDate as props to OrbitDataGraph*/} + {/*Pass the historicalData and the launchDate as props to OrbitDataGraph*/} {noradId ? ( satAttributes?.launchDate ? (
- NTNU Small Satellite Lab + NTNU Small Satellite Lab Trondheim, Norway

Norwegian University of Science and Technology

From b6861515e16c62de992e192ac65d59cec45a6450 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 14 Aug 2024 11:18:33 +0200 Subject: [PATCH 15/18] Fix FeaturedProjects preview images bug --- .../app/_homeComponents/FeaturedProjects.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/_homeComponents/FeaturedProjects.tsx b/frontend/src/app/_homeComponents/FeaturedProjects.tsx index 11af6d0..b171c40 100644 --- a/frontend/src/app/_homeComponents/FeaturedProjects.tsx +++ b/frontend/src/app/_homeComponents/FeaturedProjects.tsx @@ -19,14 +19,6 @@ export default async function FeaturedProjects() { featuredProjects?.featuredProject3, ]; - let image_url = undefined; - if (STRAPI_URL) { - image_url = - STRAPI_URL + - featuredProjects?.featuredProject1?.data?.attributes?.previewImage - ?.data?.attributes?.url; - } - return (

@@ -50,6 +42,13 @@ export default async function FeaturedProjects() { if (!project?.data?.attributes?.title) { return null; } + let previewImage = + project?.data?.attributes?.previewImage?.data?.attributes + ?.url; + + if (STRAPI_URL && previewImage != undefined) { + previewImage = STRAPI_URL + previewImage; + } return ( ); })} From 1fce5cb1a60686f8bbed7251df1d2d5f137c89c4 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 14 Aug 2024 11:22:40 +0200 Subject: [PATCH 16/18] Prettier updated --- .../app/_homeComponents/FeaturedProjects.tsx | 4 ++-- .../satellites/SatelliteResponsiveTable.tsx | 21 ++++++++++++------- .../[satelliteSlug]/orbitDataGraph.tsx | 9 ++++++-- frontend/src/components/layout/Footer.tsx | 7 ++++++- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/_homeComponents/FeaturedProjects.tsx b/frontend/src/app/_homeComponents/FeaturedProjects.tsx index b171c40..69fff6b 100644 --- a/frontend/src/app/_homeComponents/FeaturedProjects.tsx +++ b/frontend/src/app/_homeComponents/FeaturedProjects.tsx @@ -43,8 +43,8 @@ export default async function FeaturedProjects() { return null; } let previewImage = - project?.data?.attributes?.previewImage?.data?.attributes - ?.url; + project?.data?.attributes?.previewImage?.data + ?.attributes?.url; if (STRAPI_URL && previewImage != undefined) { previewImage = STRAPI_URL + previewImage; diff --git a/frontend/src/app/satellites/SatelliteResponsiveTable.tsx b/frontend/src/app/satellites/SatelliteResponsiveTable.tsx index 484472e..c77a568 100644 --- a/frontend/src/app/satellites/SatelliteResponsiveTable.tsx +++ b/frontend/src/app/satellites/SatelliteResponsiveTable.tsx @@ -99,14 +99,19 @@ export default function SatelliteResponsiveTable({ /> )) : (satellites ?? []).map((satellite) => ( - - handleRowClick(satellite.attributes?.slug ?? "") - } : {})} - > + + handleRowClick( + satellite.attributes + ?.slug ?? "", + ), + } + : {})} + > {satellite.attributes?.name} diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index b97b3b9..3d368be 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -194,9 +194,14 @@ const OrbitDataGraph: React.FC = ({ <> {orbitalData && (
-
+

Orbital parameters history (source :

- Space Track + + Space Track +

)

diff --git a/frontend/src/components/layout/Footer.tsx b/frontend/src/components/layout/Footer.tsx index 87b85a0..bdb23f9 100644 --- a/frontend/src/components/layout/Footer.tsx +++ b/frontend/src/components/layout/Footer.tsx @@ -53,7 +53,12 @@ export default function Footer() {
- NTNU Small Satellite Lab + + NTNU Small Satellite Lab + Trondheim, Norway

Norwegian University of Science and Technology

From 28197fbaeb025fe485396dc46f0ee597625c0767 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Mon, 19 Aug 2024 20:31:06 +0200 Subject: [PATCH 17/18] Updating the semimajor axis in the graph --- frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index 3d368be..aed4a23 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -130,7 +130,6 @@ const OrbitDataGraph: React.FC = ({ lastChartDate.setMonth( firstChartDate.getMonth() + scrollBarTimeFrame.current, ); - // Filtering the data to display only the data in the selected period const filteredData = orbitalData .filter((data: any) => { @@ -140,6 +139,7 @@ const OrbitDataGraph: React.FC = ({ .map((data: any) => { return { ...data, + semiMajorAxis: data.semiMajorAxis - 6371, epoch: new Date(data.epoch.slice(0, 23) + "Z"), }; }); From ce866d03873b25045842024cfe0990c1b8d41219 Mon Sep 17 00:00:00 2001 From: Florian Graule Date: Wed, 25 Sep 2024 19:25:49 +0200 Subject: [PATCH 18/18] Updating some bugs, click for not in orbit satellites, displaying valid satellites --- .../app/satellites/SatelliteResponsiveTable.tsx | 15 +++++---------- .../satellites/[satelliteSlug]/orbitDataGraph.tsx | 4 +++- .../src/app/satellites/[satelliteSlug]/page.tsx | 2 ++ frontend/src/app/satellites/page.tsx | 4 ++-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/satellites/SatelliteResponsiveTable.tsx b/frontend/src/app/satellites/SatelliteResponsiveTable.tsx index 8010028..72bd56d 100644 --- a/frontend/src/app/satellites/SatelliteResponsiveTable.tsx +++ b/frontend/src/app/satellites/SatelliteResponsiveTable.tsx @@ -102,17 +102,12 @@ export default function SatelliteResponsiveTable({ - handleRowClick( - satellite.attributes - ?.slug ?? "", - ), - } - : {})} + onClick={() => + handleRowClick( + satellite.attributes?.slug ?? "", + ) + } > - {satellite.attributes?.name} diff --git a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx index aed4a23..5d0eb02 100644 --- a/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx +++ b/frontend/src/app/satellites/[satelliteSlug]/orbitDataGraph.tsx @@ -190,12 +190,14 @@ const OrbitDataGraph: React.FC = ({ return () => window.removeEventListener("resize", updateSize); }, []); + console.log("orbitalData", orbitalData); + return ( <> {orbitalData && (