Canvas: Add Reload button on Monitoring page
Change-Id: I593d9068870bcd5f0d43680af4a08d814a18a2a9
diff --git a/apps/canvas/front/src/Monitoring.tsx b/apps/canvas/front/src/Monitoring.tsx
index 659d1a1..6f7446c 100644
--- a/apps/canvas/front/src/Monitoring.tsx
+++ b/apps/canvas/front/src/Monitoring.tsx
@@ -6,7 +6,7 @@
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@/components/ui/resizable";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useToast } from "@/hooks/use-toast";
-import { LogsIcon } from "lucide-react";
+import { Check, Ellipsis, LoaderCircle, LogsIcon, X, RefreshCw } from "lucide-react";
// ANSI escape sequence regex
// eslint-disable-next-line no-control-regex
@@ -16,39 +16,6 @@
return text.replace(ANSI_ESCAPE_REGEX, "");
}
-const WaitingIcon = () => (
- <svg viewBox="0 0 24 24" fill="currentColor" className="w-4 h-4 mr-2 inline-block align-middle text-gray-500">
- <circle cx="6" cy="12" r="2" />
- <circle cx="12" cy="12" r="2" />
- <circle cx="18" cy="12" r="2" />
- </svg>
-);
-const RunningIcon = () => (
- <svg
- className="animate-spin w-4 h-4 mr-2 inline-block align-middle text-blue-500"
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- >
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
- <path
- className="opacity-75"
- fill="currentColor"
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
- ></path>
- </svg>
-);
-const SuccessIcon = () => (
- <svg viewBox="0 0 24 24" fill="currentColor" className="w-4 h-4 mr-2 inline-block align-middle text-green-500">
- <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" />
- </svg>
-);
-const FailureIcon = () => (
- <svg viewBox="0 0 24 24" fill="currentColor" className="w-4 h-4 mr-2 inline-block align-middle text-red-500">
- <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
- </svg>
-);
-
export function Logs() {
const { toast } = useToast();
const projectId = useProjectId();
@@ -126,7 +93,6 @@
useEffect(() => {
if (sortedServices.length > 0 && sortedServices[0].workers && sortedServices[0].workers.length > 0) {
- // Only set if no logs are currently selected, to avoid overriding user interaction
if (!selectedServiceForLogs && !selectedWorkerIdForLogs) {
handleViewLogsClick(sortedServices[0].name, sortedServices[0].workers[0].id);
}
@@ -139,6 +105,37 @@
setSelectedWorkerIdForLogs(workerId);
};
+ const handleReloadWorkerClick = useCallback(
+ (serviceName: string, workerId: string) => {
+ if (!projectId) return;
+ toast({
+ title: "Worker reload initiated",
+ description: `Worker ${serviceName} / ${workerId} is reloading.`,
+ });
+ fetch(`/api/project/${projectId}/reload/${serviceName}/${workerId}`, {
+ method: "POST",
+ })
+ .then((resp) => {
+ if (!resp.ok) {
+ throw new Error(`Failed to reload worker: ${resp.statusText}`);
+ }
+ toast({
+ title: "Worker reloaded",
+ description: `Successfully reloaded worker ${serviceName} / ${workerId}`,
+ });
+ })
+ .catch((e) => {
+ console.error(e);
+ toast({
+ variant: "destructive",
+ title: "Failed to reload worker",
+ description: `Failed to reload worker ${serviceName} / ${workerId} in service`,
+ });
+ });
+ },
+ [projectId, toast],
+ );
+
const defaultExpandedServiceNames = useMemo(() => sortedServices.map((s) => s.name), [sortedServices]);
const defaultExpandedFirstWorkerId = useMemo(() => {
if (sortedServices.length > 0 && sortedServices[0].workers && sortedServices[0].workers.length > 0) {
@@ -173,7 +170,7 @@
</AccordionTrigger>
<AccordionContent className="pl-2">
<TooltipProvider>
- <div className="text-sm">
+ <div className="text-sm flex flex-col gap-1 items-start">
<Button
onClick={() =>
handleViewLogsClick(service.name, worker.id)
@@ -185,6 +182,20 @@
<LogsIcon className="w-4 h-4" />
View Logs
</Button>
+ <Button
+ onClick={() =>
+ handleReloadWorkerClick(
+ service.name,
+ worker.id,
+ )
+ }
+ size="sm"
+ variant="link"
+ className="!px-0"
+ >
+ <RefreshCw className="w-4 h-4" />
+ Reload Worker
+ </Button>
{!worker.commit && (
<p className="flex items-center">
<FailureIcon />
@@ -293,6 +304,21 @@
);
}
+function WaitingIcon(): JSX.Element {
+ return <Ellipsis className="w-4 h-4 mr-2 inline-block align-middle" />;
+}
+function RunningIcon(): JSX.Element {
+ return <LoaderCircle className="animate-spin w-4 h-4 mr-2 inline-block align-middle" />;
+}
+
+function SuccessIcon(): JSX.Element {
+ return <Check className="w-4 h-4 mr-2 inline-block align-middle" />;
+}
+
+function FailureIcon(): JSX.Element {
+ return <X className="w-4 h-4 mr-2 inline-block align-middle" />;
+}
+
function CommandStateIcon({ state }: { state: string | undefined }): JSX.Element | null {
switch (state?.toLowerCase()) {
case "running":