diff --git a/.prettierignore b/.prettierignore index eab39f0f..eb6073d2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,4 +11,5 @@ public/_redirects package.json package-lock.json bun.lockb -*.xcf \ No newline at end of file +*.xcf +*.glb diff --git a/public/static/models/Brain.glb b/public/static/models/Brain.glb new file mode 100644 index 00000000..427173d9 Binary files /dev/null and b/public/static/models/Brain.glb differ diff --git a/src/locales/en.json b/src/locales/en.json index 5c59e3f1..f53eabd4 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -555,6 +555,10 @@ "unschedulable": "Unschedulable", "schedulable": "Schedulable", "schedulable-description": "The last time this node reported its status, it was schedulable in the Kubernetes cluster", - "unschedulable-description": "The last time this node reported its status, it was unschedulable in the Kubernetes cluster" + "unschedulable-description": "The last time this node reported its status, it was unschedulable in the Kubernetes cluster", + "maia-intro-body": "Are you a researcher or student in biomedical engineering at KTH who needs state-of-the-art deep learning resources and compute? MAIA is the platform for developing, testing, and deploying medical AI, from early prototypes to real clinical workflows. You get straightforward access to compute via JupyterHub, SSH, or a virtual desktop. Our infrastructure, provided jointly with KTH Cloud, scales to different computational needs. Designed for collaboration and integration with hospital systems, MAIA lets you validate ideas and pilot your solutions in real-world clinical settings.", + "maia-intro-footer": "Request a MAIA account today and register a project to start building your medical AI solution.", + "maia-intro-header": "Discover ", + "button-get-started-maia": "Get started with MAIA" } } diff --git a/src/locales/se.json b/src/locales/se.json index 9172648a..c6a7c2f1 100644 --- a/src/locales/se.json +++ b/src/locales/se.json @@ -464,6 +464,8 @@ "joinedAt": "Gick med", "addedAt": "Lades till", "jobRestarted": "Jobb startat om", + "maia": "MAIA", + "maia-text": "BLA BLA BLA", "funding-provided-by": "kthcloud samarbetar med och stöttas av många organisationer. Finansiellt stöd har vi fått från ", "the": " ", "program-and-the": "programmet och ", @@ -554,6 +556,10 @@ "unschedulable": "Oschemaläggningsbar", "schedulable": "Schemaläggningsbar", "schedulable-description": "Senast denna nod rapporterade sin status var den schemaläggningsbar i Kubernetes-klustret", - "unschedulable-description": "Senast denna nod rapporterade sin status var den oschemaläggningsbar i Kubernetes-klustret" + "unschedulable-description": "Senast denna nod rapporterade sin status var den oschemaläggningsbar i Kubernetes-klustret", + "maia-intro-body": "Är du forskare eller student inom medicinsk teknik på KTH och behöver tillgång till datorresurser för deep learning och simuleringar? MAIA är en plattform för att utveckla, testa och implementera medicinsk AI, från tidiga prototyper till kliniska arbetsflöden. Du får åtkomst till beräkningsresurser via JupyterHub, SSH eller en virtual desktop. Vår infrastruktur, som tillhandahålls tillsammans med KTH Cloud, kan möta olika typer av behov för beräkningsresurser. MAIA är utformad för samarbete och integration med sjukhussystem och gör det möjligt för dig att validera idéer och testa dina lösningar även i kliniska miljöer.", + "maia-intro-footer": "Skapa ett MAIA-konto redan idag och registrera ett projekt", + "maia-intro-header": "Testa ", + "button-get-started-maia": "Kom igång med MAIA" } } diff --git a/src/pages/landing/Landing.tsx b/src/pages/landing/Landing.tsx index c7fbe363..7974a65f 100644 --- a/src/pages/landing/Landing.tsx +++ b/src/pages/landing/Landing.tsx @@ -5,6 +5,7 @@ import { Box, Container } from "@mui/material"; import { useKeycloak } from "@react-keycloak/web"; import LoadingPage from "../../components/LoadingPage"; import Funding from "./components/funding/Funding"; +import Maia from "./components/maia/Maia"; import { AlertList } from "../../components/AlertList"; import { useContext, useEffect } from "react"; import { AuthContextWrapper } from "../../contexts/AuthContextWrapper"; @@ -40,6 +41,7 @@ export function Landing() { + ); diff --git a/src/pages/landing/components/maia/Brain.tsx b/src/pages/landing/components/maia/Brain.tsx new file mode 100644 index 00000000..7a823049 --- /dev/null +++ b/src/pages/landing/components/maia/Brain.tsx @@ -0,0 +1,43 @@ +/* eslint-disable react/no-unknown-property */ +import { Canvas } from "@react-three/fiber"; +import { BrainMesh } from "./BrainMesh"; +import * as THREE from "three"; + +export function Brain({ + mobile, + position, +}: { + mobile: boolean; + position: number[]; +}) { + return ( + + + + + + + + + ); +} diff --git a/src/pages/landing/components/maia/BrainMesh.tsx b/src/pages/landing/components/maia/BrainMesh.tsx new file mode 100644 index 00000000..f604ed9e --- /dev/null +++ b/src/pages/landing/components/maia/BrainMesh.tsx @@ -0,0 +1,54 @@ +// @ts-nocheck + +import { useRef, useEffect, useState } from "react"; +import { useFrame, useLoader } from "@react-three/fiber"; +import { GLTFLoader } from "three-stdlib"; +import { Vector3 } from "three"; + +const url = "/static/models/Brain.glb"; + +function BrainModel() { + const gltf = useLoader(GLTFLoader, url); + gltf.scene.traverse((child) => { + if (child.material) child.material.metalness = 0; + }); + + return ; +} + +export function BrainMesh({ mobile, position, props }) { + const [mouseCoordinates, setMouseCoordinates] = useState({ x: 0, y: 0 }); + const meshRef = useRef(); + + const mouseMoveHandler = (event) => { + setMouseCoordinates({ + x: event.clientX, + y: event.clientY, + }); + }; + + useEffect(() => { + window.addEventListener("mousemove", mouseMoveHandler); + return () => { + window.removeEventListener("mousemove", mouseMoveHandler); + }; + }, []); + + useFrame(({ camera }, delta) => { + if (mobile) { + meshRef.current.rotation.y += delta; + } else { + let x = mouseCoordinates.x / window.innerWidth; + let y = 1 - mouseCoordinates.y / window.innerHeight; + const vector = new Vector3(x, y, 0); + vector.unproject(camera); + meshRef.current.rotation.set(1 - vector.y * 20, vector.x * 10, 0); + } + }); + + return ( + + + + ); +} diff --git a/src/pages/landing/components/maia/Maia.tsx b/src/pages/landing/components/maia/Maia.tsx new file mode 100644 index 00000000..dcc2e656 --- /dev/null +++ b/src/pages/landing/components/maia/Maia.tsx @@ -0,0 +1,106 @@ +import { + Grid, + Card, + Container, + Typography, + Button, + Stack, + Box, +} from "@mui/material"; +import "./intro.css"; +import { Brain } from "./Brain"; +import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; + +const Maia = () => { + const { t } = useTranslation(); + + return ( + + + + + {/* Text block - comes first on all screen sizes */} + + + {t("maia-intro-header")} MAIA + + + + {t("maia-intro-body")} + + + + {t("maia-intro-footer")} + + + + + + + {/* Brain block - comes second on desktop, second (below) on mobile */} + + {/* Desktop brain */} + + + + + {/* Mobile brain */} + + + + + + + + + ); +}; + +export default Maia; diff --git a/src/pages/landing/components/maia/intro.css b/src/pages/landing/components/maia/intro.css new file mode 100644 index 00000000..6c1b97d1 --- /dev/null +++ b/src/pages/landing/components/maia/intro.css @@ -0,0 +1,43 @@ +.lil-curve { + width: 64px; + text-align: center; +} +.jeremy-card { + z-index: 10; +} +.blur { + position: relative; + top: 100px; + left: -150px; + z-index: 1; + margin-top: -100px; + width: 500px; +} + +@media screen and (min-width: 900px) { + .lil-curve { + transform: translateY(-100px); + } +} +@media screen and (max-width: 900px) { + .blur { + position: relative; + top: 0px; + left: -10px; + margin: 0 auto; + margin-top: -100px; + margin-bottom: -30px; + + text-align: center; + } +} + +@media screen and (max-width: 600px) { + .blur { + top: 0px; + left: -500px; + margin-bottom: -30px; + + text-align: center; + } +}