const useState = React.useState; const useEffect = React.useEffect; const api = apiFactory(); const App = () => { const [loading, setLoading] = useState(true); const [loggedIn, setLoggedIn] = useState(false); useEffect(() => { setLoading(true); (async () => { setLoggedIn(await api.checkLogin()); setLoading(false); })(); }, []); const onLogIn = () => { setLoading(false); setLoggedIn(true); } const onLogOut = () => { api.logOut(); setLoading(false); setLoggedIn(false); }; if (loading === true) { return
; } if (loggedIn !== true) { return ; } return (
); }; const Login = (props) => { const [password, setPassword] = useState(undefined); const [remember, setRemember] = useState(false); const [error, setError] = useState(undefined); const handleChange = (event) => { if (event.target.type === "checkbox") { setRemember(event.target.checked); } else { setPassword(event.target.value); } }; const handleSubmit = async (event) => { setError(undefined); event.preventDefault(); const loggedIn = await api.logIn(password, remember); if (loggedIn) { props.onLogin(); } else { setError("Invalid password."); } }; return (
); }; const Header = (props) => ( ); const ContainerView = (props) => { const [loading, setLoading] = useState(true); const [checking, setChecking] = useState(false); const [updating, setUpdating] = useState(false); const [hasChecked, setHasChecked] = useState(false); const [viewModel, setViewModel] = useState({ Containers: [] }); const api = props.api; useEffect(() => { listContainers(); }, []); const containers = viewModel.Containers; const containersWithUpdates = containers.filter(c => c.HasUpdate); const containersWithoutUpdates = containers.filter(c => !c.HasUpdate); const hasSelectedContainers = containers.some(c => c.Selected); const hasUpdates = containersWithUpdates.length > 0; const checkForUpdates = async () => { setChecking(true); setViewModel(m => ({ ...m, Containers: m.Containers.map(c => ({ ...c, IsChecking: true })) })); await Promise.all(containers.map(async (c1) => { const result = await api.check(c1.ContainerId); setViewModel(m => ({ ...m, Containers: m.Containers.map((c2, i) => (c1.ContainerId === c2.ContainerId ? { ...c2, ...result, IsChecking: false } : c2 )) })); })); setChecking(false); setHasChecked(true); }; const listContainers = async () => { setLoading(true); const data = await api.list(); setViewModel(data); setLoading(false); setHasChecked(false); }; const updateImages = async (imagesToUpdate) => { setUpdating(true); await api.update(imagesToUpdate); await listContainers(); await checkForUpdates(); setUpdating(false); }; const updateAll = async () => { await updateImages(); }; const updateSelected = async () => { const selectedImages = containers.filter(c => c.Selected === true).map(c => c.ImageNameShort); await updateImages(selectedImages); }; const onContainerClick = (container) => { setViewModel(m => ({ ...m, Containers: m.Containers.map(c2 => (container.ContainerId === c2.ContainerId ? { ...c2, Selected: !c2.Selected, } : c2 )) })); }; return (
{hasUpdates ? {containersWithUpdates.length} container{containersWithUpdates.length === 1 ? " has" : "s have"} updates. : checking ? Checking for updates... : (hasChecked && containers.length > 0) ? All containers are up to date. : {containers.length} running container{containers.length !== 1 && "s"} found.}
{hasUpdates && } {hasUpdates && }
{hasUpdates && containersWithoutUpdates.length > 0 &&
{containersWithoutUpdates.length} container{containersWithoutUpdates.length === 1 ? " is" : "s are"} up to date.
} {loading && }
); }; const UpdateSelected = (props) => ( ); const UpdateAll = (props) => ( ); const UpdateCheck = (props) => ( ); const ContainerList = (props) => (
    {props.containers.map(c => props.onContainerClick(c)} />)}
); const ContainerListEntry = (props) => (
  • {props.Selected ? : }
    {props.ContainerName}
    {props.ImageName}
    {props.HasUpdate === true && } {props.IsChecking ? : props.HasUpdate === true && }
  • ); const ImageInfo = (props) => ( {props.created.substr(0, 10)} ); const Spinner = () => (
    Loading...
    ); const SpinnerGrow = () => (
    Loading...
    ); const SpinnerModal = (props) => { useEffect(() => { document.body.classList.toggle("modal-open", props.visible === true); }, [props.visible]) return (props.visible === true &&
    ) }; const container = document.getElementById('root'); const root = ReactDOM.createRoot(container); root.render();