From f02a8b21d0c397cc71d4a85672958b9d9207fb4c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 26 Feb 2026 05:49:02 +0000 Subject: [PATCH] =?UTF-8?q?INTEGRE=20DESENVOLVA=20O=20Telesc=C3=B3pio=20Ja?= =?UTF-8?q?mes=20webb,=20COMPLE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/NavBarItem.tsx | 5 +- frontend/src/layouts/Authenticated.tsx | 5 +- frontend/src/menuAside.ts | 8 +- frontend/src/pages/index.tsx | 212 ++++++++--------------- frontend/src/pages/observation.tsx | 229 +++++++++++++++++++++++++ 5 files changed, 309 insertions(+), 150 deletions(-) create mode 100644 frontend/src/pages/observation.tsx diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx index eb155e3..1986306 100644 --- a/frontend/src/components/NavBarItem.tsx +++ b/frontend/src/components/NavBarItem.tsx @@ -1,6 +1,5 @@ -import React, {useEffect, useRef} from 'react' +import React, {useEffect, useRef, useState} from 'react' import Link from 'next/link' -import { useState } from 'react' import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import BaseDivider from './BaseDivider' import BaseIcon from './BaseIcon' @@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) { } return
{NavBarItemComponentContents}
-} +} \ No newline at end of file diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx index 1b9907d..26c3572 100644 --- a/frontend/src/layouts/Authenticated.tsx +++ b/frontend/src/layouts/Authenticated.tsx @@ -1,5 +1,4 @@ -import React, { ReactNode, useEffect } from 'react' -import { useState } from 'react' +import React, { ReactNode, useEffect, useState } from 'react' import jwt from 'jsonwebtoken'; import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' import menuAside from '../menuAside' @@ -126,4 +125,4 @@ export default function LayoutAuthenticated({ ) -} +} \ No newline at end of file diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts index c6a06a5..0f61da0 100644 --- a/frontend/src/menuAside.ts +++ b/frontend/src/menuAside.ts @@ -7,7 +7,11 @@ const menuAside: MenuAsideItem[] = [ icon: icon.mdiViewDashboardOutline, label: 'Dashboard', }, - + { + href: '/observation', + icon: icon.mdiCamera, + label: 'Live Observation', + }, { href: '/users/users-list', label: 'Users', @@ -112,4 +116,4 @@ const menuAside: MenuAsideItem[] = [ }, ] -export default menuAside +export default menuAside \ No newline at end of file diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 3d1284d..ccb24ce 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,161 +1,90 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; import BaseButton from '../components/BaseButton'; -import CardBox from '../components/CardBox'; import SectionFullScreen from '../components/SectionFullScreen'; import LayoutGuest from '../layouts/Guest'; -import BaseDivider from '../components/BaseDivider'; -import BaseButtons from '../components/BaseButtons'; import { getPageTitle } from '../config'; -import { useAppSelector } from '../stores/hooks'; -import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; -import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'; - +import { mdiTelescope, mdiCamera, mdiDatabaseSearch } from '@mdi/js'; +import BaseIcon from '../components/BaseIcon'; export default function Starter() { - const [illustrationImage, setIllustrationImage] = useState({ - src: undefined, - photographer: undefined, - photographer_url: undefined, - }) - const [illustrationVideo, setIllustrationVideo] = useState({video_files: []}) - const [contentType, setContentType] = useState('image'); - const [contentPosition, setContentPosition] = useState('right'); - const textColor = useAppSelector((state) => state.style.linkColor); - - const title = 'Live Sky Viewer PWA' - - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); - - const imageBlock = (image) => ( -
-
- - Photo by {image?.photographer} on Pexels - -
-
- ); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- -
- - Video by {video.user.name} on Pexels - -
-
) - } - }; + const title = 'JWST Live Sky Explorer' return ( -
+
- {getPageTitle('Starter Page')} + {getPageTitle('JWST Explorer')} - -
- {contentType === 'image' && contentPosition !== 'background' - ? imageBlock(illustrationImage) - : null} - {contentType === 'video' && contentPosition !== 'background' - ? videoBlock(illustrationVideo) - : null} -
- - - -
-

This is a React.js/Node.js app generated by the Flatlogic Web App Generator

-

For guides and documentation please check - your local README.md and the Flatlogic documentation

-
- - - - - -
+ + {/* Background Decorative Elements */} +
+
+
+
-
- -
-

© 2026 {title}. All rights reserved

- - Privacy Policy - -
+
+
+
+ +
+
+ +

+ JWST LIVE EXPLORER +

+ +

+ Deploy the world's most powerful infrared eye. Observe the deep universe in real-time through the lens of a physical simulation. +

+ +
+ + +
+ +
+
+

IR Spectrum

+

Visualize wavelengths beyond the visible light, piercing through cosmic dust.

+
+
+

Deep Field

+

High-resolution scans of distant galaxies from NASA's latest data releases.

+
+
+

Live Sync

+

Sync with your device's camera for an augmented reality sky identification.

+
+
+
+ + +
+
+

© 2026 {title}. Deep Space Simulation Protocol active.

+
+ Security Manual + Mission Parameters +
+
+
); } @@ -163,4 +92,3 @@ export default function Starter() { Starter.getLayout = function getLayout(page: ReactElement) { return {page}; }; - diff --git a/frontend/src/pages/observation.tsx b/frontend/src/pages/observation.tsx new file mode 100644 index 0000000..f4a2a38 --- /dev/null +++ b/frontend/src/pages/observation.tsx @@ -0,0 +1,229 @@ + +import React, { useEffect, useRef, useState } from 'react'; +import type { ReactElement } from 'react'; +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import { mdiClose, mdiCamera, mdiTarget, mdiInformationOutline, mdiTelescope } from '@mdi/js'; +import BaseIcon from '../components/BaseIcon'; +import { useAppDispatch, useAppSelector } from '../stores/hooks'; +import { fetch as fetchSkyObjects } from '../stores/sky_objects/sky_objectsSlice'; +import LayoutAuthenticated from '../layouts/Authenticated'; + +const ObservationPage = () => { + const videoRef = useRef(null); + const [isCameraActive, setIsCameraActive] = useState(false); + const [mode, setMode] = useState<'normal' | 'ir' | 'deep'>('normal'); + const [scanning, setScanning] = useState(false); + const [identifiedObject, setIdentifiedObject] = useState(null); + const [telemetry, setTelemetry] = useState({ + temp: -233, + dist: 1.5, + focal: 131, + }); + + const dispatch = useAppDispatch(); + const { sky_objects } = useAppSelector((state) => state.sky_objects); + const router = useRouter(); + + useEffect(() => { + dispatch(fetchSkyObjects({})); + }, [dispatch]); + + const startCamera = async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: { facingMode: 'environment' } + }); + if (videoRef.current) { + videoRef.current.srcObject = stream; + setIsCameraActive(true); + } + } catch (err) { + console.error("Camera access denied:", err); + alert("Camera access is required for live observation simulation."); + } + }; + + const stopCamera = () => { + if (videoRef.current && videoRef.current.srcObject) { + const tracks = (videoRef.current.srcObject as MediaStream).getTracks(); + tracks.forEach(track => track.stop()); + setIsCameraActive(false); + } + }; + + useEffect(() => { + return () => stopCamera(); + }, []); + + useEffect(() => { + if (scanning) { + const timeout = setTimeout(() => { + if (sky_objects.length > 0) { + const randomObj = sky_objects[Math.floor(Math.random() * sky_objects.length)]; + setIdentifiedObject(randomObj); + } + setScanning(false); + }, 3000); + return () => clearTimeout(timeout); + } + }, [scanning, sky_objects]); + + // Simulate telemetry fluctuations + useEffect(() => { + const interval = setInterval(() => { + setTelemetry(prev => ({ + temp: prev.temp + (Math.random() - 0.5), + dist: 1.5 + (Math.random() - 0.5) * 0.01, + focal: 131.4 + (Math.random() - 0.5), + })); + }, 2000); + return () => clearInterval(interval); + }, []); + + const getFilter = () => { + switch (mode) { + case 'ir': return 'contrast(1.2) brightness(1.1) hue-rotate(180deg) saturate(1.5)'; + case 'deep': return 'contrast(1.5) brightness(0.8) saturate(0.5) blur(0.5px)'; + default: return 'none'; + } + }; + + return ( +
+ + JWST | Live Observation + + + {/* Camera View */} +
+ {!isCameraActive && ( +
+
+ +
+

Systems Offline

+ +
+ )} +
+ + {/* JWST Hexagonal Overlay */} + {isCameraActive && ( +
+ + + + + + + + +
+ )} + + {/* Telemetry & UI */} + {isCameraActive && ( +
+ {/* Top Bar */} +
+
+
System Status
+
+
+
Operational
+
+
+
TEMP: {telemetry.temp.toFixed(1)}K
+
L2 DIST: {telemetry.dist.toFixed(3)}M km
+
+
+ + +
+ + {/* Identification Box */} +
+
+ {scanning && ( +
+ )} +
+
+ +
+
+
Active Search
+
+ {scanning ? 'Analyzing Spectrum...' : identifiedObject ? identifiedObject.name : 'No Target Lock'} +
+
+
+ {identifiedObject && !scanning && ( +
+

COORDINATES: {identifiedObject.ra || 'N/A'}, {identifiedObject.dec || 'N/A'}

+

CLASSIFICATION: {identifiedObject.type || 'STEL-OBJ'}

+

{identifiedObject.description}

+
+ )} + {!scanning && ( + + )} +
+
+ + {/* Bottom Controls */} +
+ {[ + { id: 'normal', label: 'VIS', color: 'bg-white/10 text-white' }, + { id: 'ir', label: 'NIRSpec', color: 'bg-[#E3B341]/20 text-[#E3B341]' }, + { id: 'deep', label: 'MIRI', color: 'bg-purple-500/20 text-purple-300' } + ].map(btn => ( + + ))} +
+
+ )} + + {/* Static Footer (only visible when not active or on desktop) */} +
+ Mission Protocol: JWST-MAIN-SYS-V2.0.26 +
+
+ ); +}; + +ObservationPage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default ObservationPage;