Add ISO catalog

This commit is contained in:
2025-05-21 20:28:46 +02:00
parent 35e7f4b59c
commit 8c27010396
12 changed files with 308 additions and 19 deletions

View File

@ -5,6 +5,15 @@ export interface IsoFile {
size: number;
}
/**
* ISO catalog entries
*/
export interface ISOCatalogEntry {
name: string;
url: string;
image: string;
}
export class IsoFilesApi {
/**
* Upload a new ISO file to the server
@ -74,4 +83,23 @@ export class IsoFilesApi {
uri: `/iso/${file.filename}`,
});
}
/**
* Get iso catalog
*/
static async Catalog(): Promise<ISOCatalogEntry[]> {
return (
await APIClient.exec({
method: "GET",
uri: "/assets/iso_catalog.json",
})
).data;
}
/**
* Get catalog image URL
*/
static CatalogImageURL(entry: ISOCatalogEntry): string {
return APIClient.backendURL() + entry.image;
}
}

View File

@ -0,0 +1,75 @@
import {
Avatar,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
List,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemText,
} from "@mui/material";
import React from "react";
import { ISOCatalogEntry, IsoFilesApi } from "../api/IsoFilesApi";
import { AsyncWidget } from "../widgets/AsyncWidget";
export function IsoCatalogDialog(p: {
open: boolean;
onClose: () => void;
}): React.ReactElement {
const [catalog, setCatalog] = React.useState<ISOCatalogEntry[] | undefined>();
const load = async () => {
setCatalog(await IsoFilesApi.Catalog());
};
return (
<Dialog open={p.open} onClose={p.onClose}>
<DialogTitle>Iso catalog</DialogTitle>
<DialogContent>
<AsyncWidget
loadKey={1}
load={load}
errMsg="Failed to load catalog"
build={() => <IsoCatalogDialogInner catalog={catalog!} />}
/>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={p.onClose}>
Close
</Button>
</DialogActions>
</Dialog>
);
}
export function IsoCatalogDialogInner(p: {
catalog: ISOCatalogEntry[];
}): React.ReactElement {
return (
<List dense>
{p.catalog.map((entry) => (
<a
href={entry.url}
target="_blank"
rel="noopener"
style={{ color: "inherit", textDecoration: "none" }}
>
<ListItem key={entry.name}>
<ListItemButton>
<ListItemAvatar>
<img
src={IsoFilesApi.CatalogImageURL(entry)}
style={{ width: "2em" }}
/>
</ListItemAvatar>
<ListItemText primary={entry.name} />
</ListItemButton>
</ListItem>
</a>
))}
</List>
);
}

View File

@ -24,9 +24,12 @@ import { AsyncWidget } from "../widgets/AsyncWidget";
import { FileInput } from "../widgets/forms/FileInput";
import { VirtWebPaper } from "../widgets/VirtWebPaper";
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
import MenuBookIcon from "@mui/icons-material/MenuBook";
import { IsoCatalogDialog } from "../dialogs/IsoCatalogDialog";
export function IsoFilesRoute(): React.ReactElement {
const [list, setList] = React.useState<IsoFile[] | undefined>();
const [isoCatalog, setIsoCatalog] = React.useState(false);
const loadKey = React.useRef(1);
@ -40,19 +43,34 @@ export function IsoFilesRoute(): React.ReactElement {
};
return (
<AsyncWidget
loadKey={loadKey.current}
errMsg="Failed to load ISO files list!"
load={load}
ready={list !== undefined}
build={() => (
<VirtWebRouteContainer label="ISO files management">
<UploadIsoFileCard onFileUploaded={reload} />
<UploadIsoFileFromUrlCard onFileUploaded={reload} />
<IsoFilesList list={list!} onReload={reload} />
</VirtWebRouteContainer>
)}
/>
<>
<AsyncWidget
loadKey={loadKey.current}
errMsg="Failed to load ISO files list!"
load={load}
ready={list !== undefined}
build={() => (
<VirtWebRouteContainer
label="ISO files management"
actions={
<Tooltip title="Open the ISO catalog">
<IconButton onClick={() => setIsoCatalog(true)}>
<MenuBookIcon />
</IconButton>
</Tooltip>
}
>
<UploadIsoFileCard onFileUploaded={reload} />
<UploadIsoFileFromUrlCard onFileUploaded={reload} />
<IsoFilesList list={list!} onReload={reload} />
</VirtWebRouteContainer>
)}
/>
<IsoCatalogDialog
open={isoCatalog}
onClose={() => setIsoCatalog(false)}
/>
</>
);
}