airavata-local-agent/renderer/pages/docker-home.jsx (183 lines of code) (raw):

/***************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * *****************************************************************/ import { Grid, GridItem, Tabs, useToast, Box, Progress, Text, keyframes, TabPanels, Tab, TabPanel, Stack, Heading, IconButton, Icon, Alert, AlertIcon } from "@chakra-ui/react"; import { HeaderBox } from "../components/HeaderBox"; import { DockerContainersList } from "../components/DockerComponents/DockerContainersList"; import { useEffect, useState } from "react"; import { AvailablePrograms } from "../components/DockerComponents/AvaliablePrograms"; import { LuContainer } from "react-icons/lu"; import { AiOutlineCode } from "react-icons/ai"; import { useInterval } from "usehooks-ts"; import { DEBUG_DOCKER_MODE, API_BASE_URL, AUTH_BASE_URL, TOKEN_FILE } from "../lib/constants"; import { motion } from 'framer-motion'; import { useBackendUrls } from "../lib/Contexts"; const PING_DOCKER_INTERVAL = 10000; const animationKeyframes = keyframes` 0% { opacity: 1 } 25% { opacity: 0.5 } 50% { opacity: 0 } 75% { opacity: 0.5 } 100% { opacity: 1 } `; const animation = `${animationKeyframes} 2s linear infinite`; const CustomTab = ({ icon, children }) => { return ( <Tab w='100%' rounded='md' _hover={{ bg: 'gray.200', }} _selected={{ bg: 'blue.500', color: 'white', fontWeight: 'semibold', }} gap={2} justifyContent='flex-start' alignItems={'center'} > <Icon as={icon} /> <Text> {children} </Text> </Tab> ); }; const DockerHome = () => { const [pullLoading, setPullLoading] = useState(null); const [dockerUp, setDockerUp] = useState(false); const [error, setError] = useState(null); const [tabIndex, setTabIndex] = useState(1); const toast = useToast(); const { gatewayName } = useBackendUrls(); useEffect(() => { pingDocker(); window.ipc.send('ensure-token'); window.ipc.on('docker-pull-progress', (progress) => { setPullLoading(progress); }); window.ipc.on('docker-pull-finished', (image) => { console.log("Image pulled: ", image); setPullLoading(null); }); window.ipc.on('docker-pinged', (data) => { if (data) { setDockerUp(true); } else { setDockerUp(false); } }); window.ipc.on("notebook-started", (containerId, err) => { console.log("notebook started: ", containerId); if (err) { toast({ title: "Error", description: err, status: "error", duration: 9000, isClosable: true, }); } else { toast({ title: "Success", description: "Notebook started successfully. It may take a few seconds for the program to show up.", status: "success", duration: 9000, isClosable: true, }); } }); window.ipc.on('ensure-token-result', (isValid) => { if (!isValid) { setError("You may have been logged out or inactive for too long. Please login again."); } }); return () => { window.ipc.removeAllListeners("notebook-started"); window.ipc.removeAllListeners("docker-pinged"); window.ipc.removeAllListeners('docker-pull-progress'); window.ipc.removeAllListeners('docker-pull-finished'); window.ipc.removeAllListeners('ensure-token-result'); }; }, []); const pingDocker = () => { window.ipc.send("docker-ping"); }; useInterval(() => { pingDocker(); }, PING_DOCKER_INTERVAL); return ( <Box h='100vh' overflow='hidden' bg='gray.100'> <HeaderBox gatewayName={gatewayName} /> <Tabs h='100%' index={tabIndex} onChange={(index) => setTabIndex(index)} isLazy> <Grid templateColumns='repeat(20, 1fr)' h='inherit'> <GridItem colSpan={4} bg='gray.100' h='inherit'> <Stack direction='column' spacing={2} p={4}> <CustomTab icon={AiOutlineCode}>Launch Local Apps</CustomTab> <CustomTab icon={LuContainer}>Local App Containers</CustomTab> </Stack> <Stack direction='row' align='center' p={4} position='fixed' bottom='0' > <Box w='10px' h='10px' bg={dockerUp ? 'green.500' : 'red.500'} rounded='full' as={motion.div} animation={animation} ></Box> <Text>{dockerUp ? "Docker is running" : "Docker is down"}</Text> </Stack> { error && ( <Alert status='error'> <AlertIcon /> {error} </Alert> ) } </GridItem> <GridItem colSpan={16} bg='white' roundedTopLeft='md'> <TabPanels> <TabPanel> <AvailablePrograms isDisabled={pullLoading !== null} loadingText={pullLoading?.progressDetail?.current ? `${pullLoading?.status}` : "Pulling..."} progress={pullLoading?.progressDetail?.current ? pullLoading?.progress : 'Unknown progress...'} /> </TabPanel> <TabPanel> <DockerContainersList setTabIndex={setTabIndex} /> </TabPanel> </TabPanels> </GridItem> </Grid> </Tabs > </Box > ); }; export default DockerHome;