Files
komodo/frontend/src/components/resources/action/index.tsx
Maxwell Becker 98d72fc908 1.19.4 (#812)
* start 1.19.4

* deploy 1.19.4-dev-1

* try smaller binaries with cargo strip

* deploy 1.19.4-dev-2

* smaller binaries with cargo strip

* Fix Submit Dialog Button Behavior with 500 Errors on Duplicate Names (#819)

* Implement enhanced error handling and messaging for resource creation

* Implement improved error handling for resource creation across alerter, build, and sync

* Implement error handling improvements for resource copying and validation feedback

* Adjust error handling for resource creation to distinguish validation errors from unexpected system errors

* Refactor resource creation error handling by removing redundant match statements and simplifying the error propagation in multiple API modules.

* fmt

* bump indexmap

* fix account selector showing empty when account no longer found

* clean up theme logic, ensure monaco and others get up to date current theme

* enforce disable_non_admin_create for tags. Clean up status code responses

* update server cache concurrency controller

* deploy 1.19.4-dev-3

* Allow signing in by pressing enter (#830)

* Improve dialog overflow handling to prevent clipping of content (#828)

* Add Email notification entry to community.md (#824)

* Add clickable file path to show/hide file contents in StackInfo (#827)

* add clickable file path to show/hide file contents in StackInfo

Also added CopyButton due to the new functionality making the file path not selectable.

* Move clicking interaction to CardHeader

* Avoid sync edge cases of having toggle show function capturing showContents from outside

Co-authored-by: Maxwell Becker <49575486+mbecker20@users.noreply.github.com>

* Format previous change

* Add `default_show_contents` to `handleToggleShow`

---------

Co-authored-by: Maxwell Becker <49575486+mbecker20@users.noreply.github.com>

* deploy 1.19.4-dev-4

* avoid stake info ShowHideButton double toggle

* Allow multiple simultaneous Action runs for use with Args

* deploy 1.19.4-dev-5

* feat: persist all table sorting states including unsorted (#832)

- Always save sorting state to localStorage, even when empty/unsorted
- Fixes issue where 'unsorted' state was not persisted across page reloads
- Ensures consistent and predictable sorting behavior for all DataTable components

* autofocus on login username field (#837)

* Fix unnecessary auth queries flooding console on login page (#842)

* Refactor authentication error handling to use serror::Result and status codes

* Enable user query only when JWT is present

* Enable query execution in useRead only if JWT is present

* Revert backend auth changes - keep PR focused on frontend only

* Fix unnecessary API queries to unreachable servers flooding console (#843)

* Implement server availability checks in various components

* Refactor server availability check to ensure only healthy servers are identified

* cargo fmt

* fmt

* Auth error handling with status codes (#841)

* Refactor authentication error handling to use serror::Result and status codes

* Refactor error messages

* Refactor authentication error handling to include status codes and improve error messages

* clean up

* clean

* fmt

* invalid user id also UNAUTHORIZED

* deploy 1.19.4-dev-6

* deploy 1.19.4-dev-7

---------

Co-authored-by: Marcel Pfennig <82059270+MP-Tool@users.noreply.github.com>
Co-authored-by: jack <45038833+jackra1n@users.noreply.github.com>
Co-authored-by: Guten <ywzhaifei@gmail.com>
Co-authored-by: Paulo Roberto Albuquerque <paulora2405@gmail.com>
Co-authored-by: Lorenzo Farnararo <2814802+baldarn@users.noreply.github.com>
2025-09-14 12:32:06 -07:00

165 lines
4.8 KiB
TypeScript

import { ActionWithDialog, StatusBadge } from "@components/util";
import { useExecute, useRead } from "@lib/hooks";
import { RequiredResourceComponents } from "@types";
import { Clapperboard, Clock } from "lucide-react";
import { ActionConfig } from "./config";
import { ActionTable } from "./table";
import { DeleteResource, NewResource, ResourcePageHeader } from "../common";
import {
action_state_intention,
stroke_color_class_by_intention,
} from "@lib/color";
import { cn, updateLogToHtml } from "@lib/utils";
import { Types } from "komodo_client";
import { DashboardPieChart } from "@pages/home/dashboard";
import { GroupActions } from "@components/group-actions";
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
import { Card } from "@ui/card";
const useAction = (id?: string) =>
useRead("ListActions", {}).data?.find((d) => d.id === id);
const ActionIcon = ({ id, size }: { id?: string; size: number }) => {
const state = useAction(id)?.info.state;
const color = stroke_color_class_by_intention(action_state_intention(state));
return <Clapperboard className={cn(`w-${size} h-${size}`, state && color)} />;
};
export const ActionComponents: RequiredResourceComponents = {
list_item: (id) => useAction(id),
resource_links: () => undefined,
Description: () => <>Custom scripts using the Komodo client.</>,
Dashboard: () => {
const summary = useRead("GetActionsSummary", {}).data;
return (
<DashboardPieChart
data={[
{ title: "Ok", intention: "Good", value: summary?.ok ?? 0 },
{
title: "Running",
intention: "Warning",
value: summary?.running ?? 0,
},
{
title: "Failed",
intention: "Critical",
value: summary?.failed ?? 0,
},
{
title: "Unknown",
intention: "Unknown",
value: summary?.unknown ?? 0,
},
]}
/>
);
},
New: () => <NewResource type="Action" />,
GroupActions: () => <GroupActions type="Action" actions={["RunAction"]} />,
Table: ({ resources }) => (
<ActionTable actions={resources as Types.ActionListItem[]} />
),
Icon: ({ id }) => <ActionIcon id={id} size={4} />,
BigIcon: ({ id }) => <ActionIcon id={id} size={8} />,
State: ({ id }) => {
let state = useAction(id)?.info.state;
return <StatusBadge text={state} intent={action_state_intention(state)} />;
},
Status: {},
Info: {
Schedule: ({ id }) => {
const next_scheduled_run = useAction(id)?.info.next_scheduled_run;
return (
<div className="flex gap-2 items-center">
<Clock className="w-4 h-4" />
Next Run:
<div className="font-bold">
{next_scheduled_run
? new Date(next_scheduled_run).toLocaleString()
: "Not Scheduled"}
</div>
</div>
);
},
ScheduleErrors: ({ id }) => {
const error = useAction(id)?.info.schedule_error;
if (!error) {
return null;
}
return (
<Tooltip>
<TooltipTrigger asChild>
<Card className="px-3 py-2 bg-destructive/75 hover:bg-destructive transition-colors cursor-pointer">
<div className="text-sm text-nowrap overflow-hidden overflow-ellipsis">
Schedule Error
</div>
</Card>
</TooltipTrigger>
<TooltipContent className="w-[400px]">
<pre
dangerouslySetInnerHTML={{
__html: updateLogToHtml(error),
}}
className="max-h-[500px] overflow-y-auto"
/>
</TooltipContent>
</Tooltip>
);
},
},
Actions: {
RunAction: ({ id }) => {
const running =
(useRead(
"GetActionActionState",
{ action: id },
{ refetchInterval: 5000 }
).data?.running ?? 0) > 0;
const { mutate, isPending } = useExecute("RunAction");
const action = useAction(id);
if (!action) return null;
return (
<ActionWithDialog
name={action.name}
title={running ? "Running" : "Run Action"}
icon={<Clapperboard className="h-4 w-4" />}
onClick={() => mutate({ action: id, args: {} })}
disabled={running || isPending}
loading={running}
/>
);
},
},
Page: {},
Config: ActionConfig,
DangerZone: ({ id }) => <DeleteResource type="Action" id={id} />,
ResourcePageHeader: ({ id }) => {
const action = useAction(id);
return (
<ResourcePageHeader
intent={action_state_intention(action?.info.state)}
icon={<ActionIcon id={id} size={8} />}
type="Action"
id={id}
resource={action}
state={action?.info.state}
status={undefined}
/>
);
},
};