Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(2.5): Batch Flash Nodes #8

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
30 changes: 28 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bmc-ui",
"version": "3.3.3",
"version": "3.4.0",
"private": true,
"type": "module",
"scripts": {
Expand All @@ -23,6 +23,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/react-query": "^5.51.24",
"@tanstack/react-router": "^1.48.4",
Expand Down
30 changes: 30 additions & 0 deletions src/components/skeletons/flash-node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import TabView from "../TabView";

export default function FlashNodeSkeleton() {
return (
<TabView>
<div className="flex flex-col">
<div className="mb-10 h-7 w-96 animate-pulse bg-neutral-200 dark:bg-neutral-700"></div>
<div className="space-y-5">
<div className="flex items-center gap-4">
<div className="h-6 w-36 animate-pulse bg-neutral-200 dark:bg-neutral-700"></div>
<div className="flex gap-2">
{Array.from({ length: 4 }).map((_, index) => (
<div
key={index}
className="h-9 w-24 animate-pulse rounded-md bg-neutral-200 dark:bg-neutral-700"
></div>
))}
</div>
</div>
<div className="h-10 w-full animate-pulse bg-neutral-200 dark:bg-neutral-700"></div>
<div className="h-10 w-full animate-pulse bg-neutral-200 dark:bg-neutral-700"></div>
</div>
<div className="mb-4 mt-5 flex items-center gap-4">
<div className="h-9 w-24 animate-pulse rounded-full bg-neutral-200 dark:bg-neutral-700"></div>
<div className="h-6 w-28 animate-pulse bg-neutral-200 dark:bg-neutral-700"></div>
</div>
</div>
</TabView>
);
}
43 changes: 43 additions & 0 deletions src/components/ui/toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Root } from "@radix-ui/react-toggle";
import { cva, type VariantProps } from "class-variance-authority";
import { forwardRef } from "react";

import { cn } from "@/lib/utils";

const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors hover:bg-neutral-100 hover:text-neutral-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-neutral-100 data-[state=on]:text-neutral-900 dark:ring-offset-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-400 dark:focus-visible:ring-neutral-300 dark:data-[state=on]:bg-neutral-800 dark:data-[state=on]:text-neutral-50",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-neutral-200 bg-transparent hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
},
size: {
default: "h-10 px-3",
sm: "h-9 px-2.5",
lg: "h-11 px-5",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);

const Toggle = forwardRef<
React.ElementRef<typeof Root>,
React.ComponentPropsWithoutRef<typeof Root> &
VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => (
<Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
));

Toggle.displayName = Root.displayName;

export { Toggle };
12 changes: 7 additions & 5 deletions src/contexts/FlashContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ interface FlashContextValue {
}) => Promise<void>;
handleNodeUpdate: (variables: {
nodeId: number;
batch?: number[];
file?: File;
url?: string;
sha256?: string;
Expand Down Expand Up @@ -140,28 +141,29 @@ export const FlashProvider: React.FC<FlashProviderProps> = ({ children }) => {

const handleNodeUpdate = async (variables: {
nodeId: number;
batch?: number[];
file?: File;
url?: string;
sha256?: string;
skipCRC: boolean;
}) => {
const nodeId = variables.nodeId + 1;
const flashCount = variables.batch?.length ? variables.batch.length + 1 : 1;
setFlashType("node");
setIsUploading(true);
setStatusMessage(t("flashNode.uploading", { nodeId }));
setStatusMessage(t("flashNode.uploading", { count: flashCount }));
await nodeUpdateMutation.mutateAsync(variables, {
onSuccess: () => {
setIsUploading(false);
setIsFlashing(true);
const msg = variables.skipCRC
? t("flashNode.flashing", { nodeId })
: t("flashNode.flashingCrc", { nodeId });
? t("flashNode.flashing", { count: flashCount })
: t("flashNode.flashingCrc", { count: flashCount });
setStatusMessage(msg);
void flashStatus.refetch();
},
onError: (error) => {
setIsUploading(false);
const title = t("flashNode.transferFailed", { nodeId });
const title = t("flashNode.transferFailed", { count: flashCount });
const errorMessage =
((error as AxiosError).response?.data as string) ?? error.message;
setStatusMessage(`${title}: ${errorMessage}`);
Expand Down
7 changes: 7 additions & 0 deletions src/lib/api/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const handleParams = (variables: {
sha256?: string;
skipCRC?: boolean;
node?: number;
batch?: number[];
}) => {
const params: Record<string, unknown> = {
opt: "set",
Expand All @@ -35,6 +36,10 @@ const handleParams = (variables: {
params.node = variables.node;
}

if (variables.batch !== undefined) {
params.batch = variables.batch.join(",");
}

return params;
};

Expand Down Expand Up @@ -114,6 +119,7 @@ export function useNodeUpdateMutation(
mutationKey: ["nodeUpdateMutation"],
mutationFn: async (variables: {
nodeId: number;
batch?: number[];
file?: File;
url?: string;
sha256?: string;
Expand All @@ -129,6 +135,7 @@ export function useNodeUpdateMutation(
params: handleParams({
type: "flash",
node: variables.nodeId,
batch: variables.batch,
file: variables.file,
url: variables.url,
skipCRC: variables.skipCRC,
Expand Down
22 changes: 14 additions & 8 deletions src/locale/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,12 @@ const translations = {
"<0>Um die Aktualisierung abzuschließen, ist ein Systemneustart erforderlich.</0><1>Möchten Sie jetzt mit dem Neustart fortfahren?</1><2>Die Knoten verlieren vorübergehend die Stromversorgung, bis der Neustart abgeschlossen ist.</2>",
},
flashNode: {
header:
"Installieren Sie ein Betriebssystemabbild auf einem ausgewählten Knoten",
nodeSelect: "Ausgewählter Knoten:",
header_one:
"Installiere ein Betriebssystemabbild auf einem ausgewählten Knoten",
header_other:
"Installiere ein Betriebssystemabbild auf ausgewählten Knoten",
nodeSelect_one: "Ausgewählter Knoten:",
nodeSelect_other: "Ausgewählte Knoten:",
fileInput: "Datei (Remote oder lokal):",
shaInput: "SHA-256 (optional):",
skipCrc: "CRC überspringen",
Expand All @@ -129,11 +132,14 @@ const translations = {
flashModalTitle: "Betriebssystemabbild installieren",
flashModalDescription:
"Sie sind dabei, ein neues Image auf den ausgewählten Knoten zu überschreiben.",
uploading: "Übertrage Image auf Knoten {{nodeId}}...",
flashing: "Flashe Image auf Knoten {{nodeId}}...",
flashingCrc: "Prüfe CRC und flashe Image auf Knoten {{nodeId}}...",
transferFailed:
"Übertragung des Images auf Knoten {{nodeId}} fehlgeschlagen",
uploading_one: "Übertrage Image auf Knoten...",
uploading_other: "Übertrage Image auf Knoten...",
flashing_one: "Flashe Image auf Knoten...",
flashing_other: "Flashe Image auf Knoten...",
flashingCrc_one: "Prüfe CRC und flashe Image auf Knoten...",
flashingCrc_other: "Prüfe CRC und flashe Image auf Knoten...",
transferFailed_one: "Übertragung des Images auf Knoten fehlgeschlagen",
transferFailed_other: "Übertragung des Images auf Knoten fehlgeschlagen",
success: "Flashen erfolgreich",
successMessage: "Image erfolgreich auf den Knoten geflasht",
},
Expand Down
20 changes: 13 additions & 7 deletions src/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ const translations = {
"<0>To finalize the upgrade, a system reboot is necessary.</0><1>Would you like to proceed with the reboot now?</1><2>The nodes will temporarily lose power until the reboot process is completed.</2>",
},
flashNode: {
header: "Install an OS image on a selected node",
nodeSelect: "Selected node:",
header_one: "Install an OS image on a selected node",
header_other: "Install an OS image on selected nodes",
nodeSelect_one: "Selected node:",
nodeSelect_other: "Selected nodes:",
fileInput: "File (remote or local):",
shaInput: "SHA-256 (optional):",
skipCrc: "Skip CRC",
Expand All @@ -123,12 +125,16 @@ const translations = {
flashModalTitle: "Install OS Image",
flashModalDescription:
"You are about to overwrite a new image to the selected node.",
uploading: "Transferring image to node {{nodeId}}...",
flashing: "Flashing image to node {{nodeId}}...",
flashingCrc: "Checking CRC and flashing image to node {{nodeId}}...",
transferFailed: "Failed to transfer the image to node {{nodeId}}",
uploading_one: "Transferring image to node...",
uploading_other: "Transferring image to nodes...",
flashing_one: "Flashing image to node...",
flashing_other: "Flashing image to nodes...",
flashingCrc_one: "Checking CRC and flashing image to node...",
flashingCrc_other: "Checking CRC and flashing image to nodes...",
transferFailed_one: "Failed to transfer the image to node",
transferFailed_other: "Failed to transfer the image to nodes",
success: "Flashing successful",
successMessage: "Image flashed successfully to the node",
successMessage: "Image flashed successfully to the node(s)",
},
about: {
boardModel: "Board model",
Expand Down
18 changes: 12 additions & 6 deletions src/locale/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ const translations = {
"<0>Para finalizar la actualización, es necesario reiniciar el sistema.</0><1>¿Desea proceder con el reinicio ahora?</1><2>Los nodos perderán energía temporalmente hasta que se complete el proceso de reinicio.</2>",
},
flashNode: {
header: "Instalar una imagen de SO en un nodo seleccionado",
nodeSelect: "Nodo seleccionado:",
header_one: "Instalar una imagen de SO en un nodo seleccionado",
header_other: "Instalar una imagen de SO en nodos seleccionados",
nodeSelect_one: "Nodo seleccionado:",
nodeSelect_other: "Nodos seleccionados:",
fileInput: "Archivo (remoto o local):",
shaInput: "SHA-256 (opcional):",
skipCrc: "Omitir CRC",
Expand All @@ -126,10 +128,14 @@ const translations = {
flashModalTitle: "Instalar imagen de SO",
flashModalDescription:
"Estás a punto de sobrescribir una nueva imagen en el nodo seleccionado.",
uploading: "Subiendo imagen al nodo {{nodeId}}...",
flashing: "Flasheando imagen al nodo {{nodeId}}...",
flashingCrc: "Verificando CRC y flasheando imagen al nodo {{nodeId}}...",
transferFailed: "Error al transferir la imagen al nodo {{nodeId}}",
uploading_one: "Subiendo imagen al nodo...",
uploading_other: "Subiendo imagen a los nodos...",
flashing_one: "Flasheando imagen al nodo...",
flashing_other: "Flasheando imagen a los nodos...",
flashingCrc_one: "Verificando CRC y flasheando imagen al nodo...",
flashingCrc_other: "Verificando CRC y flasheando imagen a los nodos...",
transferFailed_one: "Error al transferir la imagen al nodo",
transferFailed_other: "Error al transferir la imagen a los nodos",
success: "Flasheo exitoso",
successMessage: "Imagen flasheada con éxito al nodo",
},
Expand Down
20 changes: 14 additions & 6 deletions src/locale/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,12 @@ const translations = {
"<0>Om de upgrade af te ronden, is een herstart van het systeem nodig.</0><1>Wilt u nu doorgaan met de herstart?</1><2>De nodes verliezen tijdelijk de stroom totdat het herstartsproces is voltooid.</2>",
},
flashNode: {
header: "Installeer een besturingssysteemimage op een geselecteerde node",
nodeSelect: "Geselecteerde node:",
header_one:
"Installeer een besturingssysteemimage op een geselecteerde node",
header_other:
"Installeer een besturingssysteemimage op geselecteerde nodes",
nodeSelect_one: "Geselecteerde node:",
nodeSelect_other: "Geselecteerde nodes:",
fileInput: "Bestand (extern of lokaal):",
shaInput: "SHA-256 (optioneel):",
skipCrc: "CRC overslaan",
Expand All @@ -125,10 +129,14 @@ const translations = {
flashModalTitle: "Besturingssysteemimage installeren",
flashModalDescription:
"U staat op het punt een nieuw image naar de geselecteerde node te schrijven.",
uploading: "Image overbrengen naar node {{nodeId}}...",
flashing: "Image flashen naar node {{nodeId}}...",
flashingCrc: "CRC controleren en image flashen naar node {{nodeId}}...",
transferFailed: "Overbrengen van image naar node {{nodeId}} mislukt",
uploading_one: "Image overbrengen naar node...",
uploading_other: "Image overbrengen naar nodes...",
flashing_one: "Image flashen naar node...",
flashing_other: "Image flashen naar nodes...",
flashingCrc_one: "CRC controleren en image flashen naar node...",
flashingCrc_other: "CRC controleren en image flashen naar nodes...",
transferFailed_one: "Overbrengen van image naar node mislukt",
transferFailed_other: "Overbrengen van image naar nodes mislukt",
success: "Flashen geslaagd",
successMessage: "Image succesvol geflasht naar de node",
},
Expand Down
18 changes: 12 additions & 6 deletions src/locale/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,25 @@ const translations = {
"<0>Aby sfinalizować aktualizację, wymagany jest restart systemu.</0><1>Czy chcesz teraz przejść do restartu?</1><2>Węzły tymczasowo stracą zasilanie do czasu zakończenia procesu restartu.</2>",
},
flashNode: {
header: "Zainstaluj obraz systemu operacyjnego na wybranym węźle",
nodeSelect: "Wybrany węzeł:",
header_one: "Zainstaluj obraz systemu operacyjnego na wybranym węźle",
header_other: "Zainstaluj obraz systemu operacyjnego na wybranych węzłach",
nodeSelect_one: "Wybrany węzeł:",
nodeSelect_other: "Wybrane węzły:",
fileInput: "Plik (zdalnie lub lokalnie):",
shaInput: "SHA-256 (opcjonalnie):",
skipCrc: "Pomiń CRC",
submitButton: "Zainstaluj system",
ariaProgress: "Postęp flashowania",
flashModalTitle: "Zainstalować obraz systemu?",
flashModalDescription: "Masz zamiar nadpisać nowy obraz na wybranym węźle.",
uploading: "Przesyłanie obrazu do węzła {{nodeId}}...",
flashing: "Flashowanie obrazu do węzła {{nodeId}}...",
flashingCrc: "Sprawdzanie CRC i flashowanie obrazu do węzła {{nodeId}}...",
transferFailed: "Nie udało się przesłać obrazu do węzła {{nodeId}}",
uploading_one: "Przesyłanie obrazu do węzła...",
uploading_other: "Przesyłanie obrazu do węzłów...",
flashing_one: "Flashowanie obrazu do węzła...",
flashing_other: "Flashowanie obrazu do węzłów...",
flashingCrc_one: "Sprawdzanie CRC i flashowanie obrazu do węzła...",
flashingCrc_other: "Sprawdzanie CRC i flashowanie obrazu do węzłów...",
transferFailed_one: "Nie udało się przesłać obrazu do węzła",
transferFailed_other: "Nie udało się przesłać obrazu do węzłów",
success: "Flashowanie zakończone pomyślnie",
successMessage: "Obraz został pomyślnie sflashowany na węźle",
},
Expand Down
Loading
Loading