From 0f1a3c1624dd4b837d3fa3a30cbf1ba07967057e Mon Sep 17 00:00:00 2001 From: Moshe Immermam Date: Tue, 24 Oct 2023 16:57:30 +0300 Subject: [PATCH 01/15] chore: switch to Age component --- .../Canary/CanaryPopup/CheckDetails.tsx | 33 +++--- .../Changelog/IncidentChangelogItems.tsx | 4 +- .../Cells/ConfigChangeAgeCell.tsx | 10 +- .../ConfigList/Cells/ConfigListDateCell.tsx | 4 +- .../ConfigSidebar/ConfigDetails.tsx | 100 ++++++++++++++++++ src/components/ConfigViewer/columns.tsx | 4 +- .../EvidenceLogList/EvidenceLogList.js | 4 +- .../FeatureFlags/FeatureFlagList.tsx | 6 +- .../HypothesisCommentsViewContainer.tsx | 39 ++++--- .../Hypothesis/ResponseLine/index.tsx | 18 ++-- src/components/IncidentCard/IncidentCard.tsx | 6 +- .../IncidentDetails/IncidentDetailsPanel.tsx | 6 +- .../Insights/cells/ConfigInsightAgeCell.tsx | 7 +- .../Runs/Actions/PlaybookRunsActionItem.tsx | 4 +- .../Runs/Actions/PlaybookRunsActions.tsx | 6 +- .../Playbooks/Runs/PlaybookRunsList.tsx | 11 +- .../Playbooks/Runs/PlaybookRunsSidePanel.tsx | 5 +- .../SchemaResourcePage/SchemaResourceList.tsx | 9 +- src/components/TopologyCard/Property.tsx | 4 +- src/components/UI/Age/Age.tsx | 36 +++++++ src/components/UI/Age/index.tsx | 3 + 21 files changed, 227 insertions(+), 92 deletions(-) create mode 100644 src/components/ConfigSidebar/ConfigDetails.tsx create mode 100644 src/components/UI/Age/Age.tsx create mode 100644 src/components/UI/Age/index.tsx diff --git a/src/components/Canary/CanaryPopup/CheckDetails.tsx b/src/components/Canary/CanaryPopup/CheckDetails.tsx index af3a12658..8e4a43834 100644 --- a/src/components/Canary/CanaryPopup/CheckDetails.tsx +++ b/src/components/Canary/CanaryPopup/CheckDetails.tsx @@ -1,25 +1,24 @@ import React, { Suspense, useMemo, useRef } from "react"; -import { usePrevious } from "../../../utils/hooks"; -import { AccordionBox } from "../../AccordionBox"; +import { useCanaryGraphQuery } from "../../../api/query-hooks/health"; +import { HealthCheck } from "../../../types/healthChecks"; import { capitalizeFirstLetter, toFixedIfNecessary } from "../../../utils/common"; +import { usePrevious } from "../../../utils/hooks"; import mixins from "../../../utils/mixins.module.css"; -import { PopupTabs } from "./tabs"; -import { CheckStat } from "./CheckStat"; -import { getUptimePercentage } from "./utils"; -import { StatusHistory } from "./StatusHistory/StatusHistory"; -import { DetailField } from "./DetailField"; -import { Duration } from "../renderers"; +import { AccordionBox } from "../../AccordionBox"; import { DropdownStandaloneWrapper } from "../../Dropdown/StandaloneWrapper"; import { TimeRange, timeRanges } from "../../Dropdown/TimeRange"; -import { HealthCheck } from "../../../types/healthChecks"; -import { CanaryCheckDetailsSpecTab } from "./CanaryCheckDetailsSpec"; +import { Age } from "../../UI/Age"; +import { Duration } from "../renderers"; import { CanaryCheckDetailsLabel } from "./CanaryCheckDetailsLabel"; -import { relativeDateTime } from "../../../utils/date"; -import { useCanaryGraphQuery } from "../../../api/query-hooks/health"; - +import { CanaryCheckDetailsSpecTab } from "./CanaryCheckDetailsSpec"; +import { CheckStat } from "./CheckStat"; +import { DetailField } from "./DetailField"; +import { StatusHistory } from "./StatusHistory/StatusHistory"; +import { PopupTabs } from "./tabs"; +import { getUptimePercentage } from "./utils"; const CanaryStatusChart = React.lazy(() => import("../CanaryStatusChart").then(({ CanaryStatusChart }) => ({ default: CanaryStatusChart @@ -57,9 +56,11 @@ export function CheckDetails({ check, timeRange, ...rest }: CheckDetailsProps) { Interval: validCheck?.interval || "-", Location: validCheck?.location || "-", Schedule: validCheck?.schedule || "-", - "Last Runtime": validCheck?.lastRuntime - ? relativeDateTime(validCheck.lastRuntime) - : "-" + "Last Runtime": validCheck?.lastRuntime ? ( + + ) : ( + "-" + ) }), [validCheck] ); diff --git a/src/components/Changelog/IncidentChangelogItems.tsx b/src/components/Changelog/IncidentChangelogItems.tsx index 986632d31..8d96e6158 100644 --- a/src/components/Changelog/IncidentChangelogItems.tsx +++ b/src/components/Changelog/IncidentChangelogItems.tsx @@ -1,6 +1,6 @@ import { IncidentHistory } from "../../api/services/IncidentsHistory"; -import { relativeDateTime } from "../../utils/date"; import { Avatar } from "../Avatar"; +import { Age } from "../UI/Age"; import IncidentHistoryItemTypeContent from "./IncidentHistoryItemTypeContent"; type IncidentChangelogItemProps = { @@ -20,7 +20,7 @@ export default function IncidentChangelogItem({ {history.created_by?.name}{" "} {" "} - {relativeDateTime(history.created_at)} + diff --git a/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx b/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx index f26c9d0a1..ea5355a93 100644 --- a/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx +++ b/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx @@ -1,12 +1,16 @@ import { CellContext } from "@tanstack/react-table"; import { ConfigTypeChanges } from ".."; -import { relativeDateTime } from "../../../utils/date"; +import { Age } from "../../UI/Age"; export default function ConfigChangeAgeCell({ row, column, getValue }: CellContext) { - const age = relativeDateTime(row.original.created_at); - return {age}; + return ( + + ); } diff --git a/src/components/ConfigList/Cells/ConfigListDateCell.tsx b/src/components/ConfigList/Cells/ConfigListDateCell.tsx index 8badb3a28..cdd7ff402 100644 --- a/src/components/ConfigList/Cells/ConfigListDateCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListDateCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; import { FaTrash } from "react-icons/fa"; -import { relativeDateTime } from "../../../utils/date"; +import { Age } from "../../UI/Age"; export default function ConfigListDateCell>({ getValue, @@ -15,7 +15,7 @@ export default function ConfigListDateCell>({ const value = isDeleted ? row.original.deleted_at : dateString; return (
- {value ? relativeDateTime(value) : ""} + {column.id === "updated_at" && isDeleted && ( )} diff --git a/src/components/ConfigSidebar/ConfigDetails.tsx b/src/components/ConfigSidebar/ConfigDetails.tsx new file mode 100644 index 000000000..6957512d6 --- /dev/null +++ b/src/components/ConfigSidebar/ConfigDetails.tsx @@ -0,0 +1,100 @@ +import { useMemo } from "react"; +import { FaTags } from "react-icons/fa"; +import { useGetConfigByIdQuery } from "../../api/query-hooks"; +import { relativeDateTime } from "../../utils/date"; +import { CountBadge } from "../Badge/CountBadge"; +import CollapsiblePanel from "../CollapsiblePanel"; +import { DescriptionCard } from "../DescriptionCard"; +import { InfoMessage } from "../InfoMessage"; +import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; +import Title from "../Title/title"; +import { capitalize } from "lodash"; +import { Age } from "../UI/Age"; + +type Props = { + configId: string; + isCollapsed?: boolean; + onCollapsedStateChange?: (isClosed: boolean) => void; +}; + +export function ConfigDetails({ + configId, + isCollapsed = true, + onCollapsedStateChange = () => {} +}: Props) { + const { + data: configDetails, + isLoading, + error + } = useGetConfigByIdQuery(configId); + + const displayDetails = useMemo(() => { + if (configDetails) { + return [ + { + label: "Name", + value: configDetails.name + }, + ...(configDetails.tags + ? Object.entries(configDetails.tags) + .filter(([key]) => key !== "Name") + .map(([key, value]) => ({ + label: capitalize(key), + value + })) + : []), + ...(configDetails.created_at + ? [ + { + label: "Created At", + value: + } + ] + : []), + ...(configDetails.updated_at + ? [ + { + label: "Updated At", + value: + } + ] + : []), + ...(configDetails.deleted_at + ? [ + { + label: "Deleted At", + value: + } + ] + : []) + ]; + } + }, [configDetails]); + + return ( + + } /> + <CountBadge + roundedClass="rounded-full" + value={displayDetails?.length ?? 0} + /> + </div> + } + dataCount={displayDetails?.length} + > + <div className="flex flex-col space-y-2 py-2 max-w-full"> + {isLoading ? ( + <TextSkeletonLoader /> + ) : displayDetails && !error ? ( + <DescriptionCard items={displayDetails} labelStyle="top" /> + ) : ( + <InfoMessage message="Details not found" /> + )} + </div> + </CollapsiblePanel> + ); +} diff --git a/src/components/ConfigViewer/columns.tsx b/src/components/ConfigViewer/columns.tsx index 208035d71..6a96c2413 100644 --- a/src/components/ConfigViewer/columns.tsx +++ b/src/components/ConfigViewer/columns.tsx @@ -1,5 +1,5 @@ import { CellContext, ColumnDef } from "@tanstack/table-core"; -import { relativeDateTime } from "../../utils/date"; +import { Age } from "../UI/Age"; export const defaultTableColumns: ColumnDef<any>[] = [ { @@ -67,7 +67,7 @@ export function DateCell({ row, column }: CellContext<any, any>) { const dateString = row?.getValue<string>(column.id); return ( <div className="text-xs"> - {dateString ? relativeDateTime(dateString) : "None"} + <Age from={dateString} /> </div> ); } diff --git a/src/components/EvidenceLogList/EvidenceLogList.js b/src/components/EvidenceLogList/EvidenceLogList.js index 75fda6502..08dfa408f 100644 --- a/src/components/EvidenceLogList/EvidenceLogList.js +++ b/src/components/EvidenceLogList/EvidenceLogList.js @@ -1,10 +1,10 @@ -import { relativeDateTime } from "../../utils/date"; +import { Age } from "../UI/Age"; export const EvidenceLogList = ({ evidence }) => ( <div className="flex flex-row gap-x-10 py-1.5 border-b" key={evidence.id}> <div className="flex flex-row"> <p className="ml-2.5 text-sm leading-5 font-medium text-gray-900"> - {relativeDateTime(evidence.created_at)} + <Age from={evidence.created_at} /> </p> </div> <p className="text-sm leading-5 font-medium truncate"> diff --git a/src/components/FeatureFlags/FeatureFlagList.tsx b/src/components/FeatureFlags/FeatureFlagList.tsx index fc8116803..13caf34b1 100644 --- a/src/components/FeatureFlags/FeatureFlagList.tsx +++ b/src/components/FeatureFlags/FeatureFlagList.tsx @@ -5,6 +5,7 @@ import { relativeDateTime } from "../../utils/date"; import { DataTable } from "../DataTable"; import { Avatar } from "../Avatar"; import { Property } from "../../services/permissions/permissionsService"; +import { Age } from "../UI/Age"; type FeatureFlagsListProps = { data: any[]; @@ -12,8 +13,9 @@ type FeatureFlagsListProps = { onRowClick?: (data: Property) => void; } & Omit<React.HTMLProps<HTMLDivElement>, "data">; -const DateCell = ({ getValue }: CellContext<User, any>) => - relativeDateTime(getValue<string>()); +const DateCell = ({ getValue }: CellContext<User, any>) => ( + <Age from={getValue()} /> +); const AvatarCell = ({ getValue }: CellContext<User, any>) => { return <Avatar user={getValue()} circular />; diff --git a/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx b/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx index ce65f491a..43c4f6d2f 100644 --- a/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx +++ b/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx @@ -1,31 +1,28 @@ +import clsx from "clsx"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { IoMdSend } from "react-icons/io"; +import { GroupHeadingProps, OptionProps, components } from "react-select"; import { Comment } from "../../../api/services/comments"; -import { dateSortHelper, relativeDateTime } from "../../../utils/date"; -import { CommentInput, CommentText } from "../../Comment"; -import { Icon } from "../../Icon"; -import { Avatar } from "../../Avatar"; -import { CreatedBy } from "../ResponseLine"; -import { Tag } from "../../Tag/Tag"; -import { SortOrders } from "../../../constants"; +import { Evidence } from "../../../api/services/evidence"; import { Hypothesis } from "../../../api/services/hypothesis"; +import { SortOrders } from "../../../constants"; +import { useUser } from "../../../context"; +import useRunTaskOnPropChange from "../../../hooks/useRunTaskOnPropChange"; import { HypothesisAPIs, TreeNode } from "../../../pages/incident/IncidentDetails"; -import { Evidence } from "../../../api/services/evidence"; -import { EvidenceItem } from "../EvidenceSection"; -import { useUser } from "../../../context"; -import { toastError } from "../../Toast/toast"; -import clsx from "clsx"; -import { IoMdSend } from "react-icons/io"; -import { - GroupedOptionItem, - OptionItem, - SearchSelect -} from "../../SearchSelect"; -import useRunTaskOnPropChange from "../../../hooks/useRunTaskOnPropChange"; -import { components, GroupHeadingProps, OptionProps } from "react-select"; import { useIncidentState } from "../../../store/incident.state"; +import { dateSortHelper } from "../../../utils/date"; +import { Avatar } from "../../Avatar"; +import { CommentInput, CommentText } from "../../Comment"; +import { Icon } from "../../Icon"; +import { OptionItem, SearchSelect } from "../../SearchSelect"; +import { Tag } from "../../Tag/Tag"; +import { toastError } from "../../Toast/toast"; +import { Age } from "../../UI/Age"; +import { EvidenceItem } from "../EvidenceSection"; +import { CreatedBy } from "../ResponseLine"; interface IProps { incidentId: string; @@ -319,7 +316,7 @@ export function HypothesisCommentViewEntry({ ? `${created_by.team.name} (${created_by?.name})` : created_by?.name} <span className="inline-block pl-1 text-xs text-gray-500"> - {relativeDateTime(created_at)}{" "} + <Age from={created_at} /> {type === CommentViewEntryTypes.evidence && "added evidence"} </span> </div> diff --git a/src/components/Hypothesis/ResponseLine/index.tsx b/src/components/Hypothesis/ResponseLine/index.tsx index 0111bd1cd..27b85efbc 100644 --- a/src/components/Hypothesis/ResponseLine/index.tsx +++ b/src/components/Hypothesis/ResponseLine/index.tsx @@ -1,18 +1,18 @@ import { BsTrash } from "react-icons/bs"; -import { Evidence } from "../../../api/services/evidence"; +import { useCallback, useMemo } from "react"; +import { BiCheck } from "react-icons/bi"; +import { IoMdRemoveCircle } from "react-icons/io"; import { Comment } from "../../../api/services/comments"; +import { Evidence } from "../../../api/services/evidence"; import { User } from "../../../api/services/users"; -import { relativeDateTime } from "../../../utils/date"; import { Avatar } from "../../Avatar"; import { CommentText } from "../../Comment"; -import { IconButton } from "../../IconButton"; -import { EvidenceItem } from "../EvidenceSection"; import { Icon } from "../../Icon"; +import { IconButton } from "../../IconButton"; import { Menu } from "../../Menu"; -import { BiCheck } from "react-icons/bi"; -import { IoMdRemoveCircle } from "react-icons/io"; -import { useCallback, useMemo } from "react"; +import { Age } from "../../UI/Age"; +import { EvidenceItem } from "../EvidenceSection"; export type CreatedBy = User & { team: { @@ -84,8 +84,8 @@ export function ResponseLine({ </span> )} </div> - <p className="mt-0.5 text-gray-500 text-xs leading-5 font-normal"> - {relativeDateTime(created_at)} + <p className="mt-0.5 text-gray-500 leading-5 font-normal"> + <Age from={created_at} /> </p> </div> diff --git a/src/components/IncidentCard/IncidentCard.tsx b/src/components/IncidentCard/IncidentCard.tsx index b733425cd..b483915d2 100644 --- a/src/components/IncidentCard/IncidentCard.tsx +++ b/src/components/IncidentCard/IncidentCard.tsx @@ -1,10 +1,10 @@ import clsx from "clsx"; import { Link } from "react-router-dom"; import { Incident } from "../../api/services/incident"; -import { relativeDateTime } from "../../utils/date"; import { IncidentStatusTag } from "../IncidentStatusTag"; import { typeItems } from "../Incidents/data"; import { IncidentTypeIcon } from "../incidentTypeTag"; +import { Age } from "../UI/Age"; type IncidentCardProps = { incident: Incident; @@ -28,8 +28,8 @@ export default function IncidentCard({ {incident.title} </Link> <IncidentStatusTag status={incident.status!} className="ml-1" /> - <div className="text-right grow text-xs"> - {relativeDateTime(incident.created_at)} + <div className="text-right grow"> + <Age from={incident.created_at} /> </div> </div> </div> diff --git a/src/components/IncidentDetails/IncidentDetailsPanel.tsx b/src/components/IncidentDetails/IncidentDetailsPanel.tsx index b0b6073b3..7c4c4a256 100644 --- a/src/components/IncidentDetails/IncidentDetailsPanel.tsx +++ b/src/components/IncidentDetails/IncidentDetailsPanel.tsx @@ -5,12 +5,12 @@ import { useForm } from "react-hook-form"; import { BsCardList, BsShareFill } from "react-icons/bs"; import { Incident } from "../../api/services/incident"; import { IncidentPriority } from "../../constants"; -import { relativeDateTime } from "../../utils/date"; import CollapsiblePanel from "../CollapsiblePanel"; import IncidentTypeDropdown from "../Incidents/IncidentTypeDropdown"; import { incidentStatusItems, typeItems } from "../Incidents/data"; import { ReactSelectDropdown } from "../ReactSelectDropdown"; import Title from "../Title/title"; +import { Age } from "../UI/Age"; import { IncidentDetailsRow } from "./IncidentDetailsRow"; import { priorities } from "./IncidentSidebar"; import { IncidentWorkflow } from "./IncidentWorkflow"; @@ -74,11 +74,11 @@ export function IncidentDetailsPanel({ const formValues = getValues(); const formattedCreatedAt = useMemo( - () => relativeDateTime(watchCreatedAt), + () => <Age from={watchCreatedAt} />, [watchCreatedAt] ); const formattedDuration = useMemo( - () => relativeDateTime(watchCreatedAt), + () => <Age from={watchCreatedAt} />, [watchCreatedAt] ); diff --git a/src/components/Insights/cells/ConfigInsightAgeCell.tsx b/src/components/Insights/cells/ConfigInsightAgeCell.tsx index 46ddab094..1588c16cc 100644 --- a/src/components/Insights/cells/ConfigInsightAgeCell.tsx +++ b/src/components/Insights/cells/ConfigInsightAgeCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; -import { relativeDateTime } from "../../../utils/date"; import { ConfigTypeInsights } from "../../ConfigInsights"; +import { Age } from "../../UI/Age"; export default function ConfigInsightAgeCell({ row, @@ -20,6 +20,7 @@ export default function ConfigInsightAgeCell({ >, unknown >) { - const age = relativeDateTime(row.original.first_observed); - return <span className="flex whitespace-nowrap py-1 text-sm">{age}</span>; + return ( + <Age className="flex py-1 text-sm" from={row.original.first_observed} /> + ); } diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx index 936bd11c0..e1d5538b5 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx @@ -1,4 +1,4 @@ -import { relativeDateTime } from "../../../../utils/date"; +import { Age } from "../../../UI/Age"; import { PlaybookRunAction } from "../PlaybookRunTypes"; import PlaybookRunsStatus from "../PlaybookRunsStatus"; @@ -31,7 +31,7 @@ export default function PlaybookRunsActionItem({ </div> <div className="flex flex-col"> <div className="text-sm font-medium text-gray-600"> - {relativeDateTime(action.start_time!, action.end_time)} + <Age from={action.start_time} to={action.end_time} /> </div> </div> </div> diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx index 7629c5a1f..6470a4237 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx @@ -1,8 +1,8 @@ import { ReactNode, useMemo, useState } from "react"; import { Link } from "react-router-dom"; -import { relativeDateTime } from "../../../../utils/date"; import { Avatar } from "../../../Avatar"; import { Icon } from "../../../Icon"; +import { Age } from "../../../UI/Age"; import { PlaybookRun, PlaybookRunAction } from "../PlaybookRunTypes"; import PlaybookRunsStatus from "./../PlaybookRunsStatus"; import PlaybookRunActionFetch from "./PlaybookRunActionFetch"; @@ -47,8 +47,8 @@ export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) { <PlaybookRunsStatus status={data.status} /> </div> ], - ["Start Time", relativeDateTime(data.start_time)], - ["Duration", relativeDateTime(data.start_time, data.end_time)], + ["Start Time", <Age from={data.start_time} />], + ["Duration", <Age from={data.start_time} to={data.end_time} />], [ "Triggered By", data.created_by ? ( diff --git a/src/components/Playbooks/Runs/PlaybookRunsList.tsx b/src/components/Playbooks/Runs/PlaybookRunsList.tsx index ad3f2ea99..fd185b17d 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsList.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsList.tsx @@ -2,11 +2,11 @@ import { ColumnDef } from "@tanstack/react-table"; import { useCallback } from "react"; import { Link, useNavigate } from "react-router-dom"; import { User } from "../../../api/services/users"; -import { relativeDateTime } from "../../../utils/date"; import { Avatar } from "../../Avatar"; import { DateCell } from "../../ConfigViewer/columns"; import { DataTable, PaginationOptions } from "../../DataTable"; import { Icon } from "../../Icon"; +import { Age } from "../../UI/Age"; import { PlaybookRun, PlaybookRunStatus } from "./PlaybookRunTypes"; import PlaybookRunsStatus from "./PlaybookRunsStatus"; @@ -59,14 +59,7 @@ const playbookRunsTableColumns: ColumnDef<PlaybookRun>[] = [ header: "Duration", accessorKey: "duration", cell: ({ row }) => { - const startTime = row.original.start_time; - const endTime = row.original.end_time; - - return ( - <span> - {startTime && endTime && relativeDateTime(startTime, endTime)} - </span> - ); + return <Age from={row.original.start_time} to={row.original.end_time} />; } }, { diff --git a/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx b/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx index 5909cd894..c9d19d153 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx @@ -5,7 +5,6 @@ import { useEffect, useMemo, useState } from "react"; import { AiOutlineTeam } from "react-icons/ai"; import { useNavigate } from "react-router-dom"; import { getPlaybookRuns } from "../../../api/services/playbooks"; -import { relativeDateTime } from "../../../utils/date"; import PillBadge from "../../Badge/PillBadge"; import CollapsiblePanel from "../../CollapsiblePanel"; import EmptyState from "../../EmptyState"; @@ -13,6 +12,7 @@ import { InfiniteTable } from "../../InfiniteTable/InfiniteTable"; import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader"; import { refreshButtonClickedTrigger } from "../../SlidingSideBar"; import Title from "../../Title/title"; +import { Age } from "../../UI/Age"; import { PlaybookRun } from "./PlaybookRunTypes"; type TopologySidePanelProps = { @@ -51,8 +51,7 @@ const runsColumns: ColumnDef<PlaybookRun, any>[] = [ id: "duration", cell: ({ row }) => { const { start_time, end_time } = row.original; - const value = relativeDateTime(end_time ?? start_time!, start_time); - return <span className="whitespace-nowrap">{value}</span>; + return <Age from={start_time} to={end_time} />; }, size: 20 } diff --git a/src/components/SchemaResourcePage/SchemaResourceList.tsx b/src/components/SchemaResourcePage/SchemaResourceList.tsx index 020f739a6..7481ec8dc 100644 --- a/src/components/SchemaResourcePage/SchemaResourceList.tsx +++ b/src/components/SchemaResourcePage/SchemaResourceList.tsx @@ -2,13 +2,12 @@ import clsx from "clsx"; import { useNavigate } from "react-router-dom"; import { SchemaResourceWithJobStatus } from "../../api/schemaResources"; import { tables } from "../../context/UserAccessContext/permissions"; -import { relativeDateTime } from "../../utils/date"; import { Avatar } from "../Avatar"; import { InfoMessage } from "../InfoMessage"; import JobHistoryStatusColumn from "../JobsHistory/JobHistoryStatusColumn"; import TableSkeletonLoader from "../SkeletonLoader/TableSkeletonLoader"; +import { Age } from "../UI/Age"; import ConfigScrapperIcon from "./ConfigScrapperIcon"; - interface Props { items: SchemaResourceWithJobStatus[]; baseUrl: string; @@ -137,16 +136,16 @@ function SchemaResourceListItem({ </Cell> {table === "canaries" && <Cell>{schedule}</Cell>} <Cell className="text-gray-500"> - {created_at && relativeDateTime(created_at)} + <Age from={created_at} /> </Cell> <Cell className="text-gray-500"> - {updated_at && relativeDateTime(updated_at)} + <Age from={updated_at} /> </Cell> <Cell className="text-gray-500 lowercase space-x-2"> <JobHistoryStatusColumn status={job_status} /> </Cell> <Cell className="text-gray-500"> - {job_time_start ? relativeDateTime(job_time_start) : ""} + <Age from={job_time_start} /> </Cell> <Cell className="text-gray-500"> {created_by && <Avatar user={created_by} circular />} diff --git a/src/components/TopologyCard/Property.tsx b/src/components/TopologyCard/Property.tsx index 0f29d6945..a23afd406 100644 --- a/src/components/TopologyCard/Property.tsx +++ b/src/components/TopologyCard/Property.tsx @@ -1,9 +1,9 @@ import clsx from "clsx"; import { NodePodPropToLabelMap } from "../../constants"; import { TopologyProperty } from "../../context/TopologyPageContext"; -import { relativeDateTime } from "../../utils/date"; import { isEmpty } from "../Canary/utils"; import { Icon } from "../Icon"; +import { Age } from "../UI/Age"; import { FormatPropertyCurrency, FormatPropertyDefault, @@ -25,7 +25,7 @@ export function FormatProperty({ let { text } = property; if (property.name === "created" && typeof text === "string") { - return <span className="text-sm">{relativeDateTime(text)}</span>; + return <Age from={text} />; } if (property.type === "url") { diff --git a/src/components/UI/Age/Age.tsx b/src/components/UI/Age/Age.tsx new file mode 100644 index 000000000..d0a5b6a16 --- /dev/null +++ b/src/components/UI/Age/Age.tsx @@ -0,0 +1,36 @@ +import dayjs from "dayjs"; +import clsx from "clsx"; + +export default function Age({ + className = "text-sm", + from, + to +}: { + className?: string; + from?: Date | string; + to?: Date | string; +}) { + if ( + from === "" || + from == null || + dayjs(from).isSame(dayjs("0001-01-01T00:00:00+00:00")) + ) { + return null; + } + let _from = dayjs.utc(from); + + if (to == null) { + return ( + <span title={_from.format()} className={className}> + {_from.local().fromNow(true)} + </span> + ); + } + let _to = dayjs.utc(to); + + return ( + <span className={clsx(className, "whitespace-nowrap")}> + {_from.local().to(_to)} + </span> + ); +} diff --git a/src/components/UI/Age/index.tsx b/src/components/UI/Age/index.tsx new file mode 100644 index 000000000..8fb02ad24 --- /dev/null +++ b/src/components/UI/Age/index.tsx @@ -0,0 +1,3 @@ +import Age from "./Age"; + +export { Age }; From 453e356b5250f4511c440a761b95175c0c6925e0 Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Tue, 24 Oct 2023 17:03:43 +0300 Subject: [PATCH 02/15] chore: switch to ConfigLink / Icon --- src/api/services/configs.ts | 95 +++++++++++-------- .../ConfigsDetailsBreadCrumb.tsx | 8 +- .../ConfigAnalysis/ConfigInsightsColumns.tsx | 10 +- .../ConfigAnalysisLink/ConfigAnalysisLink.tsx | 9 +- .../ConfigInsightsDetailsModal.tsx | 5 +- src/components/ConfigBreadcrumb/index.tsx | 8 +- src/components/ConfigChangeHistory/index.tsx | 31 +++--- .../ConfigDetailsChanges.tsx | 29 ++---- src/components/ConfigLink/ConfigLink.tsx | 40 ++++---- .../ConfigSidebar/ConfigDetails.tsx | 3 +- .../Hypothesis/EvidenceSection/index.tsx | 43 ++++----- src/components/Icon/ChangeIcon.tsx | 20 ++++ src/components/Icon/ConfigIcon.tsx | 23 +++++ src/components/Icon/index.tsx | 2 +- .../DefinitionOfDone/EvidenceView/index.tsx | 8 +- src/components/Sidebars/configs.tsx | 5 +- src/components/Sidebars/incidents.tsx | 14 +-- .../TopologyConfigLinkModal.tsx | 14 ++- src/components/UserList/index.tsx | 13 +-- 19 files changed, 198 insertions(+), 182 deletions(-) create mode 100644 src/components/Icon/ChangeIcon.tsx create mode 100644 src/components/Icon/ConfigIcon.tsx diff --git a/src/api/services/configs.ts b/src/api/services/configs.ts index df9077b64..492d7c718 100644 --- a/src/api/services/configs.ts +++ b/src/api/services/configs.ts @@ -1,13 +1,33 @@ // http://incident-commander.canary.lab.flanksource.com/config/db -import { ConfigTypeChanges } from "../../components/ConfigChanges"; import { ConfigTypeInsights } from "../../components/ConfigInsights"; +import { User } from "../../api/services/users"; import { Config, ConfigDB, IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -export interface ConfigItem { +export type ConfigChange = { + id: string; + config_id: string; + external_change_id: string; + change_type: string; + severity: string; + source: string; + summary: string; + patches?: string; + diff?: string; + details: string; + created_at: string; + created_by: string | User; + external_created_by: string; + config?: ConfigItem; + config_class?: string; + type?: string; + name?: string; +}; + +export type ConfigItem = { name: string; - external_id: string; + external_id?: string; config_class?: string; type?: string; id: string; @@ -15,8 +35,8 @@ export interface ConfigItem { analysis?: Analysis[]; tags?: Record<string, any>; allTags?: Record<string, any>; - created_at: string; - updated_at: string; + created_at?: string; + updated_at?: string; deleted_at?: string; cost_per_minute?: number; cost_total_1d?: number; @@ -135,7 +155,7 @@ export const getAllChanges = ( } }); return resolve( - ConfigDB.get<ConfigTypeChanges[]>( + ConfigDB.get<ConfigChange[]>( `/config_changes?order=created_at.desc${pagingParams}&select=id,change_type,summary,source,created_at,config_id,config:config_names!inner(id,name,type)${queryString}`, { headers: { @@ -169,12 +189,11 @@ export const getConfigChanges = ( ) => { let paginationQueryParams = ""; if (pageIndex !== undefined && pageSize !== undefined) { - paginationQueryParams = `&limit=${pageSize}&offset=${ - pageIndex! * pageSize - }`; + paginationQueryParams = `&limit=${pageSize}&offset=${pageIndex! * pageSize + }`; } return resolve( - ConfigDB.get<ConfigTypeChanges[]>( + ConfigDB.get<ConfigChange[]>( `/config_changes?config_id=eq.${id}&order=created_at.desc${paginationQueryParams}`, { headers: { @@ -186,7 +205,7 @@ export const getConfigChanges = ( }; export const getConfigChangeById = async (id: string, configId: string) => { - const res = await ConfigDB.get<ConfigTypeChanges[] | null>( + const res = await ConfigDB.get<ConfigChange[] | null>( `/config_changes?config_id=eq.${configId}&id=eq.${id}&select=id,config_id,change_type,created_at,external_created_by,source,diff,details,patches,created_by,config:configs(id,name,type,config_class)` ); return res.data?.[0] || undefined; @@ -363,9 +382,8 @@ export const getConfigInsights = ( ) => { let paginationQueryParams = ""; if (pageIndex !== undefined && pageSize !== undefined) { - paginationQueryParams = `&limit=${pageSize}&offset=${ - pageIndex! * pageSize - }`; + paginationQueryParams = `&limit=${pageSize}&offset=${pageIndex! * pageSize + }`; } return resolve( ConfigDB.get< @@ -411,34 +429,33 @@ export const getTopologyRelatedInsights = async ( ) => { let paginationQueryParams = ""; if (pageIndex !== undefined && pageSize !== undefined) { - paginationQueryParams = `&limit=${pageSize}&offset=${ - pageIndex! * pageSize - }`; + paginationQueryParams = `&limit=${pageSize}&offset=${pageIndex! * pageSize + }`; } return resolve( ConfigDB.get< | { - analysis_id: string; - config: { - id: string; - name: string; - config_class: string; - type: string; - analysis: Pick< - ConfigTypeInsights, - | "id" - | "analyzer" - | "config" - | "severity" - | "analysis_type" - | "sanitizedMessageTxt" - | "sanitizedMessageHTML" - | "first_observed" - | "message" - >[]; - }; - }[] + analysis_id: string; + config: { + id: string; + name: string; + config_class: string; + type: string; + analysis: Pick< + ConfigTypeInsights, + | "id" + | "analyzer" + | "config" + | "severity" + | "analysis_type" + | "sanitizedMessageTxt" + | "sanitizedMessageHTML" + | "first_observed" + | "message" + >[]; + }; + }[] | null >( `/analysis_by_component?component_id=eq.${id}${paginationQueryParams}&select=analysis_id,config:configs(id,name,config_class,type,analysis:config_analysis(id,analyzer,analysis_type,message,severity,analysis,first_observed))`, @@ -493,7 +510,7 @@ export const getAllConfigInsights = async ( const sortString = sortBy.sortBy ? `&order=${sortBy.sortBy}.${sortBy.sortOrder}` : // default sort by first_observed - "&order=first_observed.desc"; + "&order=first_observed.desc"; return resolve( ConfigDB.get<ConfigTypeInsights[] | null>( @@ -522,7 +539,7 @@ export const getConfigComponentRelationships = async <T>(configID: string) => { }; export const getComponentConfigChanges = async (topologyID: string) => { - const res = await ConfigDB.get<ConfigTypeChanges[]>( + const res = await ConfigDB.get<ConfigChange[]>( `/changes_by_component?component_id=eq.${topologyID}&select=id,type,config_id,name,change_type,config_class,created_at,config:configs(id, name, type, config_class)` ); return res.data; diff --git a/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx b/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx index fe15e7996..641cd6bee 100644 --- a/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx +++ b/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx @@ -1,7 +1,7 @@ import { BreadcrumbChild, BreadcrumbNav, BreadcrumbRoot } from "."; import { useGetConfigByIdQuery } from "../../api/query-hooks"; import { ConfigItem } from "../../api/services/configs"; -import { Icon } from "../Icon"; +import { ConfigIcon } from "../Icon/ConfigIcon"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; type Props = { @@ -23,11 +23,7 @@ export function ConfigsDetailsBreadcrumbNav({ configId, config }: Props) { ) : ( <BreadcrumbChild> <span> - <Icon - name={configItem?.type} - secondary={configItem?.config_class} - className="h-5 mr-1" - /> + <ConfigIcon config={configItem!} className="h-5 mr-1" /> {configItem?.name} </span> </BreadcrumbChild> diff --git a/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx b/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx index dd2d4a1cb..05610d04d 100644 --- a/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx +++ b/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx @@ -2,9 +2,9 @@ import { ColumnDef } from "@tanstack/react-table"; import { Link } from "react-router-dom"; import { ConfigItem } from "../../api/services/configs"; import { ConfigTypeInsights } from "../ConfigInsights"; -import { DateCell } from "../ConfigViewer/columns"; -import { Icon } from "../Icon"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; +import { DateCell } from "../ConfigViewer/columns"; +import { ConfigIcon } from "../Icon/ConfigIcon"; import ConfigInsightsSeverityIcons from "./ConfigInsightsSeverityIcons"; export const ConfigInsightsColumns: ColumnDef< @@ -25,11 +25,7 @@ export const ConfigInsightsColumns: ColumnDef< className="space-x-2 items-center" to={`/catalog/${config?.id}`} > - <Icon - className="w-4 h-4" - name={config?.type} - secondary={config?.config_class} - /> + <ConfigIcon config={config} className="w-4 h-4" /> <span>{config?.name}</span> </Link> </div> diff --git a/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx b/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx index 49cda9407..79be11101 100644 --- a/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx +++ b/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx @@ -3,7 +3,7 @@ import { ViewType } from "../../types"; import { ConfigTypeInsights } from "../ConfigInsights"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; import { DescriptionCard } from "../DescriptionCard"; -import { Icon } from "../Icon"; +import { ConfigIcon } from "../Icon/ConfigIcon"; import ConfigInsightsDetailsModal from "./ConfigInsightsDetailsModal"; type Props = { @@ -47,10 +47,9 @@ export function ConfigAnalysisLink({ > {showConfigLogo && ( <div className="flex flex-row gap-2 items-center"> - <Icon - name={configAnalysis?.config?.type} - secondary={configAnalysis?.config?.config_class} - className="w-5 h-5" + <ConfigIcon + config={configAnalysis?.config} + className="h-5 mr-1" /> <span>{configAnalysis?.config?.name}</span> <span>/</span> diff --git a/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx b/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx index df7105c2a..5ef4344b2 100644 --- a/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx +++ b/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx @@ -84,10 +84,7 @@ export default function ConfigInsightsDetailsModal({ </div>, <ConfigLink className="text-blue-600 text-xl font-semibold whitespace-nowrap overflow-hidden overflow-ellipsis" - configId={configInsight.config!.id} - configName={configInsight.config!.name} - configType={configInsight.config!.type} - configTypeSecondary={configInsight.config!.config_class} + config={configInsight.config} /> ]} /> diff --git a/src/components/ConfigBreadcrumb/index.tsx b/src/components/ConfigBreadcrumb/index.tsx index 14df22200..a80b973fa 100644 --- a/src/components/ConfigBreadcrumb/index.tsx +++ b/src/components/ConfigBreadcrumb/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from "react"; import { useParams } from "react-router-dom"; import { getConfigName } from "../../api/services/configs"; import { BreadcrumbNav } from "../BreadcrumbNav"; -import { Icon } from "../Icon"; +import { ConfigIcon } from "../Icon/ConfigIcon"; type ConfigBreadcrumbProps = { setTitle: (child: React.ReactNode) => void; @@ -25,11 +25,7 @@ export function ConfigBreadcrumb({ setTitle }: ConfigBreadcrumbProps) { list={[ { to: "/catalog", title: "Catalog" }, <span> - <Icon - name={data?.type} - secondary={data?.config_class} - className="h-5 mr-1" - /> + <ConfigIcon config={data} className="h-5 mr-1" /> {data?.name} </span> ]} diff --git a/src/components/ConfigChangeHistory/index.tsx b/src/components/ConfigChangeHistory/index.tsx index 7ab2d0c3b..f617a8077 100644 --- a/src/components/ConfigChangeHistory/index.tsx +++ b/src/components/ConfigChangeHistory/index.tsx @@ -1,12 +1,14 @@ import { ColumnDef } from "@tanstack/table-core"; import { useState } from "react"; -import { ConfigTypeChanges } from "../ConfigChanges"; -import ConfigLink from "../ConfigLink/ConfigLink"; import { useGetConfigChangesByConfigChangeIdQuery } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; +import { ConfigChange } from "../../api/services/configs"; +import { ConfigTypeChanges } from "../ConfigChanges"; import { ConfigDetailChangeModal } from "../ConfigDetailsChanges/ConfigDetailsChanges"; +import ConfigLink from "../ConfigLink/ConfigLink"; import { DateCell } from "../ConfigViewer/columns"; import { PaginationOptions } from "../DataTable"; -import { DataTable, Icon } from "../index"; +import { ChangeIcon } from "../Icon/ChangeIcon"; +import { DataTable } from "../index"; const columns: ColumnDef<ConfigTypeChanges>[] = [ { @@ -16,12 +18,8 @@ const columns: ColumnDef<ConfigTypeChanges>[] = [ const changeType = row?.getValue(column.id) as string; return ( <div className="text-ellipsis overflow-hidden"> - <Icon - name={changeType} - secondary="diff" - className="w-5 h-auto pr-1" - /> - {changeType} + <ChangeIcon change={row.original} className="w-5 pr-1" /> + <span>{changeType}</span> </div> ); }, @@ -53,7 +51,7 @@ const columns: ColumnDef<ConfigTypeChanges>[] = [ } ]; -const configLinkCol: ColumnDef<ConfigTypeChanges>[] = [ +const configLinkCol: ColumnDef<ConfigChange>[] = [ { header: "Catalog", accessorKey: "config_id", @@ -62,21 +60,14 @@ const configLinkCol: ColumnDef<ConfigTypeChanges>[] = [ if (!config) { return null; } - return ( - <ConfigLink - configId={config.id} - configName={config.name} - configType={config.type} - configTypeSecondary={config.config_class} - /> - ); + return <ConfigLink config={config} />; }, size: 84 } ]; type ConfigChangeHistoryProps = { - data: ConfigTypeChanges[]; + data: ConfigChange[]; isLoading?: boolean; linkConfig?: boolean; className?: string; @@ -93,7 +84,7 @@ export function ConfigChangeHistory({ tableStyle }: ConfigChangeHistoryProps) { const [selectedConfigChange, setSelectedConfigChange] = - useState<ConfigTypeChanges>(); + useState<ConfigChange>(); const [modalIsOpen, setModalIsOpen] = useState(false); const { data: configChange } = useGetConfigChangesByConfigChangeIdQuery( diff --git a/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx b/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx index 7c9caf1ab..5d68a6d76 100644 --- a/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx +++ b/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx @@ -12,12 +12,13 @@ import { ConfigTypeChanges } from "../ConfigChanges"; import ConfigLink from "../ConfigLink/ConfigLink"; import { DiffRenderer } from "../DiffRenderer/DiffRenderer"; import EmptyState from "../EmptyState"; -import { Icon } from "../Icon"; +import { ChangeIcon } from "../Icon/ChangeIcon"; import { JSONViewer } from "../JSONViewer"; import { Modal } from "../Modal"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; import ConfigChangeDetailSection from "./ConfigChangeDetailsSection"; import ModalTitleListItems from "../Modal/ModalTitleListItems"; +import { ConfigIcon } from "../Icon/ConfigIcon"; type ConfigDetailsChangesProps = { id: string; @@ -103,7 +104,7 @@ export function ConfigDetailsChanges({ } return ( - <div className="flex flex-col flex-1 overflow-y-auto bg-white cursor-pointer"> + <div className="flex flex-col flex-1 overflow-y-auto cursor-pointer"> <ConfigDetailChangeModal // do not show modal if the config is not loaded open={open && !isLoading} @@ -163,20 +164,13 @@ export function ConfigDetailsChanges({ > {showConfigLogo && ( <> - <Icon - name={config?.type} - secondary={config?.config_class} - className="w-5 mr-1" - /> + <ConfigIcon config={config} /> <span>{config?.name}</span>  /  </> )} - <Icon - name={changeDetails?.change_type} - secondary="diff" - className="w-5 h-auto pr-1" - /> + + <ChangeIcon change={changeDetails} className="w-5 h-auto pr-1" /> {changeDetails?.change_type} </div> )} @@ -205,20 +199,13 @@ export function ConfigDetailChangeModal({ items={[ <div className="flex flex-row gap-1 flex-shrink items-center"> <div className="block w-6 h-auto"> - <Icon - name={changeDetails?.change_type} - secondary="diff" - className="w-5 h-5" - /> + <ChangeIcon change={changeDetails} /> </div> <span> {changeDetails?.change_type}</span> </div>, <ConfigLink className="text-blue-600 text-xl font-semibold whitespace-nowrap overflow-hidden overflow-ellipsis" - configId={config.id} - configName={config.name} - configType={config.type} - configTypeSecondary={config.config_class} + config={config} /> ]} /> diff --git a/src/components/ConfigLink/ConfigLink.tsx b/src/components/ConfigLink/ConfigLink.tsx index cd82db8d8..adc7629fd 100644 --- a/src/components/ConfigLink/ConfigLink.tsx +++ b/src/components/ConfigLink/ConfigLink.tsx @@ -1,13 +1,12 @@ -import { Link } from "react-router-dom"; -import { Icon } from "../Icon"; -import { HTMLAttributeAnchorTarget } from "react"; import clsx from "clsx"; +import { HTMLAttributeAnchorTarget } from "react"; +import { Link } from "react-router-dom"; +import { ConfigItem } from "../../api/services/configs"; import ConfigsTypeIcon from "../Configs/ConfigsTypeIcon"; +import { ConfigIcon } from "../Icon/ConfigIcon"; type ConfigLinkProps = { - configId: string; - configName: string; - configType?: string; + config?: ConfigItem; className?: string; configTypeSecondary?: string; variant?: "label" | "link"; @@ -15,38 +14,33 @@ type ConfigLinkProps = { }; export default function ConfigLink({ - configId, - configName, - configType, - configTypeSecondary, + config, variant = "link", className = "text-zinc-600 text-sm whitespace-nowrap overflow-hidden overflow-ellipsis" }: ConfigLinkProps) { + if (config == null) { + return null; + } if (variant === "link") { return ( <Link to={{ - pathname: `/catalog/${configId}` + pathname: `/catalog/${config.id}` }} className={clsx("flex flex-row gap-2", className)} > - <ConfigsTypeIcon - config={{ - type: configType - }} - /> - <div className="overflow-hidden text-ellipsis flex-1">{configName}</div> + <ConfigsTypeIcon config={config} /> + + <div className="overflow-hidden text-ellipsis flex-1"> + {config.name} + </div> </Link> ); } return ( <div className={clsx("flex flex-row gap-1", className)}> - <Icon - name={configType} - secondary={configTypeSecondary} - className="w-5 h-5" - /> - <div className="overflow-hidden text-ellipsis text-sm">{configName}</div> + <ConfigIcon config={config} /> + <div className="overflow-hidden text-ellipsis text-sm">{config.name}</div> </div> ); } diff --git a/src/components/ConfigSidebar/ConfigDetails.tsx b/src/components/ConfigSidebar/ConfigDetails.tsx index 6957512d6..d3f722ff7 100644 --- a/src/components/ConfigSidebar/ConfigDetails.tsx +++ b/src/components/ConfigSidebar/ConfigDetails.tsx @@ -1,14 +1,13 @@ +import { capitalize } from "lodash"; import { useMemo } from "react"; import { FaTags } from "react-icons/fa"; import { useGetConfigByIdQuery } from "../../api/query-hooks"; -import { relativeDateTime } from "../../utils/date"; import { CountBadge } from "../Badge/CountBadge"; import CollapsiblePanel from "../CollapsiblePanel"; import { DescriptionCard } from "../DescriptionCard"; import { InfoMessage } from "../InfoMessage"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; import Title from "../Title/title"; -import { capitalize } from "lodash"; import { Age } from "../UI/Age"; type Props = { diff --git a/src/components/Hypothesis/EvidenceSection/index.tsx b/src/components/Hypothesis/EvidenceSection/index.tsx index e43251037..63c9a750e 100644 --- a/src/components/Hypothesis/EvidenceSection/index.tsx +++ b/src/components/Hypothesis/EvidenceSection/index.tsx @@ -1,31 +1,31 @@ -import React, { ReactNode, useEffect, useState } from "react"; import { ChevronRightIcon, DotsVerticalIcon } from "@heroicons/react/outline"; -import { TopologyCard } from "../../TopologyCard"; +import React, { ReactNode, useEffect, useState } from "react"; import { BsTrash } from "react-icons/bs"; +import { + useGetConfigByIdQuery, + useGetConfigInsight +} from "../../../api/query-hooks"; import { Evidence, EvidenceType } from "../../../api/services/evidence"; +import { Hypothesis } from "../../../api/services/hypothesis"; import { getCanaries } from "../../../api/services/topology"; +import { Size, ViewType } from "../../../types"; +import { sanitizeHTMLContent, toFixedIfNecessary } from "../../../utils/common"; import mixins from "../../../utils/mixins.module.css"; +import { Button } from "../../Button"; import { CheckDetails } from "../../Canary/CanaryPopup/CheckDetails"; import { CheckTitle } from "../../Canary/CanaryPopup/CheckTitle"; -import { sanitizeHTMLContent, toFixedIfNecessary } from "../../../utils/common"; import { getUptimePercentage } from "../../Canary/CanaryPopup/utils"; import { Duration, StatusList } from "../../Canary/renderers"; -import { Modal } from "../../Modal"; -import { relativeDateTime } from "../../../utils/date"; -import { Size, ViewType } from "../../../types"; -import ConfigLink from "../../ConfigLink/ConfigLink"; -import { LogsTable } from "../../Logs/Table/LogsTable"; -import { Icon } from "../../Icon"; -import { Button } from "../../Button"; +import { ConfigAnalysisLink } from "../../ConfigAnalysisLink/ConfigAnalysisLink"; import { ConfigDetailsChanges } from "../../ConfigDetailsChanges/ConfigDetailsChanges"; -import { - useGetConfigByIdQuery, - useGetConfigInsight -} from "../../../api/query-hooks"; import { ConfigTypeInsights } from "../../ConfigInsights"; -import { ConfigAnalysisLink } from "../../ConfigAnalysisLink/ConfigAnalysisLink"; +import ConfigLink from "../../ConfigLink/ConfigLink"; +import { Icon } from "../../Icon"; import { CommentEvidence } from "../../IncidentDetails/DefinitionOfDone/EvidenceView"; -import { Hypothesis } from "../../../api/services/hypothesis"; +import { LogsTable } from "../../Logs/Table/LogsTable"; +import { Modal } from "../../Modal"; +import { TopologyCard } from "../../TopologyCard"; +import { Age } from "../../UI/Age"; const ColumnSizes = { Time: { @@ -125,19 +125,14 @@ const EvidenceAccordion: React.FC<{ <div className="flex justify-between w-full items-center"> {title || <span className="text-gray-400">(no title)</span>} {date && ( - <div className="text-gray-400 text-sm pl-2"> - {relativeDateTime(date)} + <div className="text-gray-400 pl-2"> + <Age from={date} /> </div> )} </div> </button> <div> - <ConfigLink - configId={configId} - configName={configName} - configType={config?.type} - configTypeSecondary={config?.config_class} - /> + <ConfigLink config={config} /> </div> </div> {expanded && children} diff --git a/src/components/Icon/ChangeIcon.tsx b/src/components/Icon/ChangeIcon.tsx new file mode 100644 index 000000000..77d70be73 --- /dev/null +++ b/src/components/Icon/ChangeIcon.tsx @@ -0,0 +1,20 @@ +import React, { memo } from "react"; + +import { Icon, IconProps } from "."; +import { ConfigChange } from "../../api/services/configs"; + +interface ChangeIconProps extends IconProps { + change?: ConfigChange; +} + +export const ChangeIcon: React.FC<ChangeIconProps> = memo( + ({ className = "w-5 h-auto", name, change, ...props }) => { + return ( + <Icon + name={change?.change_type || name} + className={`opacity-50 ${className}`} + {...props} + /> + ); + } +); diff --git a/src/components/Icon/ConfigIcon.tsx b/src/components/Icon/ConfigIcon.tsx new file mode 100644 index 000000000..0cbfeaafe --- /dev/null +++ b/src/components/Icon/ConfigIcon.tsx @@ -0,0 +1,23 @@ +import React, { memo } from "react"; +import { Icon, IconProps } from "."; +import { ConfigItem } from "../../api/services/configs"; + +interface ConfigIconProps extends IconProps { + config?: ConfigItem; +} + +export const ConfigIcon: React.FC<ConfigIconProps> = memo( + ({ className = "h-5 mr-1", config, ...props }) => { + if (config == null) { + return null; + } + return ( + <Icon + name={config.type} + secondary={config.config_class} + className={className} + {...props} + /> + ); + } +); diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index e5456f22c..a425499ac 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -481,7 +481,7 @@ function findByName(name?: string) { return icon; } -type IconProps = { +export type IconProps = { name?: string; secondary?: string; className?: string; diff --git a/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx b/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx index 18a3f33c0..a71bafb73 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx @@ -19,6 +19,7 @@ import { Icon } from "../../../Icon"; import TextSkeletonLoader from "../../../SkeletonLoader/TextSkeletonLoader"; import { StatusStyles } from "../../../TopologyCard"; import { CardMetrics } from "../../../TopologyCard/CardMetrics"; +import { ConfigIcon } from "../../../Icon/ConfigIcon"; type EvidenceViewProps = Omit<React.HTMLProps<HTMLDivElement>, "size"> & { evidence: Evidence; @@ -151,8 +152,11 @@ function ConfigEvidence({ return ( <div className={clsx("overflow-hidden py-2", className)} {...rest}> - <Icon name={config.type} secondary={config.config_class} />{" "} - <span className="pl-1 text-gray-500 font-medium"> {config.name} </span>{" "} + <ConfigIcon config={config} /> + <span className="pl-1 text-gray-500 font-medium"> + {" "} + {config.name}{" "} + </span>{" "} </div> ); } diff --git a/src/components/Sidebars/configs.tsx b/src/components/Sidebars/configs.tsx index b44f1c22d..69c79ae76 100644 --- a/src/components/Sidebars/configs.tsx +++ b/src/components/Sidebars/configs.tsx @@ -85,10 +85,7 @@ export function ConfigsList({ })} > <ConfigLink - configId={config.id} - configName={config.name} - configType={config.type} - configTypeSecondary={config.config_class} + config={config} className="overflow-hidden text-ellipsis flex-1 whitespace-nowrap" /> {config.deleted_at && ( diff --git a/src/components/Sidebars/incidents.tsx b/src/components/Sidebars/incidents.tsx index 3fc784cfc..c5f2c8ebf 100644 --- a/src/components/Sidebars/incidents.tsx +++ b/src/components/Sidebars/incidents.tsx @@ -1,21 +1,21 @@ import { useQuery } from "@tanstack/react-query"; +import { useAtom } from "jotai"; import { useEffect, useMemo, useState } from "react"; import { ImLifebuoy } from "react-icons/im"; import { Link } from "react-router-dom"; import { getIncidentsBy } from "../../api/services/incident"; -import { relativeDateTime } from "../../utils/date"; +import { useFeatureFlagsContext } from "../../context/FeatureFlagsContext"; +import { features } from "../../services/permissions/features"; import PillBadge from "../Badge/PillBadge"; import CollapsiblePanel from "../CollapsiblePanel"; import { DetailsTable } from "../DetailsTable/DetailsTable"; import { IncidentStatusTag } from "../IncidentStatusTag"; +import { typeItems } from "../Incidents/data"; import IncidentsFilterBar, { IncidentFilter } from "../IncidentsFilterBar"; +import { refreshButtonClickedTrigger } from "../SlidingSideBar"; import Title from "../Title/title"; +import { Age } from "../UI/Age"; import { IncidentTypeIcon } from "../incidentTypeTag"; -import { useAtom } from "jotai"; -import { refreshButtonClickedTrigger } from "../SlidingSideBar"; -import { typeItems } from "../Incidents/data"; -import { useFeatureFlagsContext } from "../../context/FeatureFlagsContext"; -import { features } from "../../services/permissions/features"; type Props = { topologyId?: string; @@ -103,7 +103,7 @@ export default function Incidents({ <IncidentStatusTag status={item.status!} className="ml-1 text-sm" /> </div> ), - age: relativeDateTime(item.created_at) + age: <Age from={item.created_at} /> }; }); }, [data]); diff --git a/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx b/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx index a1ea49e86..d171cd6ed 100644 --- a/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx +++ b/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx @@ -17,7 +17,7 @@ import { toastError, toastSuccess } from "../Toast/toast"; import { useAtom } from "jotai"; import { refreshButtonClickedTrigger } from "../SlidingSideBar"; import { Events, sendAnalyticEvent } from "../../services/analytics"; - +import { ConfigItem } from "../../api/services/configs"; type TopologyConfigLinkModalProps = { topology: Topology; openModal: boolean; @@ -138,10 +138,14 @@ export function TopologyConfigLinkModal({ <div className="w-auto cursor-pointer"> <div className="flex flex-row truncate"> <ConfigLink - configId={option.id} - configName={option.name} - configType={option.type} - configTypeSecondary={option.config_class} + config={ + { + id: option.id, + name: option.name, + type: option.type, + config_class: option.config_class + } as ConfigItem + } variant="label" /> </div> diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index 70daf4587..8143f05ed 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -3,13 +3,13 @@ import clsx from "clsx"; import { useEffect, useMemo, useRef, useState } from "react"; import { BsTrash } from "react-icons/bs"; import { User } from "../../api/services/users"; -import { relativeDateTime } from "../../utils/date"; +import { useUserAccessStateContext } from "../../context/UserAccessContext/UserAccessContext"; +import { tables } from "../../context/UserAccessContext/permissions"; +import { withAccessCheck } from "../AccessCheck/AccessCheck"; import { DataTable } from "../DataTable"; import { IconButton } from "../IconButton"; import { Menu } from "../Menu"; -import { withAccessCheck } from "../AccessCheck/AccessCheck"; -import { tables } from "../../context/UserAccessContext/permissions"; -import { useUserAccessStateContext } from "../../context/UserAccessContext/UserAccessContext"; +import { Age } from "../UI/Age"; type UserListProps = { data: any[]; @@ -17,8 +17,9 @@ type UserListProps = { deleteUser: (userId: string) => void; } & Omit<React.HTMLProps<HTMLDivElement>, "data">; -const DateCell = ({ getValue }: CellContext<User, any>) => - relativeDateTime(getValue<string>()); +const DateCell = ({ getValue }: CellContext<User, any>) => ( + <Age from={getValue<string>()} /> +); const getColumns = ( deleteUser: (userId: string) => void, From c700ab9ee31f0f01d16e46717e213d503cdba95e Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Tue, 24 Oct 2023 22:40:53 +0300 Subject: [PATCH 03/15] refactor: standard types and component reuse chore: move age to ui package --- src/api/query-hooks/health.tsx | 2 +- src/api/query-hooks/index.ts | 18 +-- src/api/query-hooks/mutations/comment.tsx | 8 +- src/api/query-hooks/mutations/evidence.tsx | 7 +- src/api/query-hooks/mutations/hypothesis.tsx | 7 +- .../mutations/useUpdateComponentMutation.tsx | 2 +- src/api/query-hooks/playbooks.tsx | 2 +- src/api/query-hooks/responders.tsx | 5 +- src/api/query-hooks/useCheckStattiQuery.ts | 2 +- .../useComponentConfigRelationshipQuery.ts | 3 +- src/api/query-hooks/useConfigAnalysisQuery.ts | 10 +- ...seGetConfigChangesByConfigChangeIdQuery.ts | 2 +- src/api/schemaResources.ts | 14 +- src/api/services/IncidentsHistory.ts | 80 +--------- src/api/services/comments.ts | 20 +-- src/api/services/configs.ts | 119 ++------------- src/api/services/evidence.ts | 35 +---- src/api/services/hypothesis.ts | 70 ++------- src/api/services/incident.ts | 70 +-------- src/api/services/playbooks.ts | 18 +-- src/api/services/responder.ts | 24 +-- src/api/services/teams.ts | 14 +- src/api/services/topology.ts | 49 +----- src/api/services/users.ts | 61 ++------ src/api/traits.ts | 29 ++++ src/api/types/common.ts | 47 ++++++ src/api/types/configs.ts | 92 +++++++++++ src/api/types/evidence.ts | 43 ++++++ .../healthChecks.ts => api/types/health.ts} | 8 +- src/api/types/hypothesis.ts | 60 ++++++++ src/api/types/incident.ts | 144 ++++++++++++++++++ .../types/playbooks.ts} | 42 +++-- .../types/topology.ts} | 50 ++++-- src/api/types/users.ts | 50 ++++++ src/components/Agents/AgentPage.tsx | 2 +- .../Agents/List/AgentsTableColumns.tsx | 4 +- src/components/AttachEvidenceDialog/index.tsx | 16 +- src/components/Avatar/index.tsx | 4 +- .../ConfigsDetailsBreadCrumb.tsx | 2 +- .../CanaryPopup/CanaryCheckDetailsLabel.tsx | 2 +- .../CanaryPopup/CanaryCheckDetailsSpec.tsx | 2 +- .../CanaryPopup/CanaryCheckDetailsUptime.tsx | 2 +- .../Canary/CanaryPopup/CheckDetails.tsx | 4 +- .../Canary/CanaryPopup/CheckTitle.tsx | 2 +- .../StatusHistory/StatusHistory.tsx | 2 +- .../Canary/CanaryStatusChart/index.tsx | 2 +- src/components/Canary/CanaryTabs.tsx | 2 +- .../Canary/HealthCheckEdit/index.tsx | 2 +- src/components/Canary/filter.ts | 4 +- src/components/Canary/index.tsx | 2 +- src/components/Canary/minimal.tsx | 4 +- src/components/Canary/renderers.tsx | 2 +- src/components/Canary/table.tsx | 2 +- src/components/CanaryInterface/minimal.tsx | 2 +- .../Changelog/EvidenceChangelogContent.tsx | 6 +- .../Changelog/IncidentChangelogItems.tsx | 4 +- .../IncidentHistoryItemTypeContent.tsx | 6 +- src/components/CollapsiblePanel/index.tsx | 6 +- .../ConfigAnalysis/ConfigInsightsColumns.tsx | 7 +- .../ConfigAnalysis/ConfigInsightsList.tsx | 4 +- .../ConfigAnalysisLink/ConfigAnalysisLink.tsx | 4 +- .../ConfigInsightsDetailsModal.tsx | 2 +- .../ConfigChangeHistory.stories.tsx | 5 +- src/components/ConfigChangeHistory/index.tsx | 11 +- .../Cells/ConfigChangeAgeCell.tsx | 6 +- .../Cells/ConfigChangeNameCell.tsx | 6 +- src/components/ConfigCosts/index.tsx | 3 +- .../ConfigDetailsChanges.tsx | 26 ++-- src/components/ConfigInsights/index.tsx | 21 --- src/components/ConfigInsightsIcon/index.tsx | 9 +- src/components/ConfigLink/ConfigLink.tsx | 2 +- .../Cells/ConfigListAnalysisCell.tsx | 2 +- .../ConfigList/Cells/ConfigListChangeCell.tsx | 2 +- .../ConfigList/Cells/ConfigListCostCell.tsx | 2 +- .../ConfigList/Cells/ConfigListDateCell.tsx | 2 +- .../ConfigList/Cells/ConfigListNameCell.tsx | 2 +- .../ConfigList/Cells/ConfigListTagsCell.tsx | 2 +- .../ConfigList/Cells/ConfigListTypeCell.tsx | 2 +- .../ConfigList/ConfigListColumn.tsx | 6 +- src/components/ConfigList/index.tsx | 2 +- .../ConfigSidebar/ConfigDetails.tsx | 2 +- .../ConfigSummary/ConfigSummaryList.tsx | 2 +- src/components/ConfigViewer/columns.tsx | 35 +---- src/components/Configs/ConfigsTypeIcon.tsx | 2 +- .../Configs/Sidebar/ConfigActionBar.tsx | 14 +- .../Connections/ConnectionsList.tsx | 2 +- src/components/CostDetails/CostDetails.tsx | 8 +- .../Dropdown/DropdownWithActions.stories.tsx | 2 +- src/components/Dropdown/GroupByDropdown.tsx | 2 +- .../EventQueueStatus/EventQueueStatusList.tsx | 2 +- .../components/EvidenceSearch/index.js | 5 - .../components/EvidenceSelect/index.js | 5 - .../components/EvidenceType/index.js | 5 - src/components/EvidenceBuilder/index.tsx | 9 +- .../EvidenceLogList/EvidenceLogList.js | 2 +- .../FeatureFlags/FeatureFlagList.tsx | 9 +- src/components/HealthChecks/CheckLink.tsx | 2 +- src/components/Hypothesis/Comments/index.tsx | 2 +- .../Hypothesis/CreateHypothesis/index.tsx | 7 +- .../EvidenceSection.stories.tsx | 5 +- .../Hypothesis/EvidenceSection/index.tsx | 16 +- .../HypothesisBar/HypothesisBar.stories.tsx | 2 +- .../Hypothesis/HypothesisBar/index.tsx | 4 +- .../HypothesisBarMenu.stories.tsx | 2 +- .../Hypothesis/HypothesisBarMenu/index.tsx | 7 +- .../Hypothesis/HypothesisBuilder/index.tsx | 2 +- .../HypothesisCommentsViewContainer.tsx | 18 ++- .../Hypothesis/HypothesisDetails/index.tsx | 28 ++-- .../Hypothesis/HypothesisNode/index.tsx | 11 +- .../Hypothesis/ResponseLine/index.tsx | 23 +-- .../StatusDropdownContainer/index.tsx | 2 +- src/components/Icon/ChangeIcon.tsx | 3 +- src/components/Icon/ConfigIcon.tsx | 2 +- src/components/IncidentCard/IncidentCard.tsx | 4 +- src/components/IncidentCardSummary/index.tsx | 5 +- .../AddAutoDefinitionOfDone.tsx | 6 +- .../AddDefinitionOfDoneHome.tsx | 6 +- .../AddManualDefinitionOfDone.tsx | 2 +- .../steps/EvidenceSelectorStep.tsx | 8 +- .../AddDefinitionOfDone/steps/ScriptStep.tsx | 2 +- .../useAddCommentAsDoD.tsx | 2 +- .../AddResponders/AddResponder.tsx | 2 +- .../AddResponders/AddResponderModal.tsx | 13 +- .../SelectPeopleResponderDropdown.tsx | 2 +- .../SelectTeamResponderDropdown.tsx | 2 +- .../EditEvidenceDefinitionOfDoneComment.tsx | 2 +- .../EditEvidenceDefinitionOfDoneScript.tsx | 2 +- .../EvidenceSelectionModal.tsx | 2 +- .../DefinitionOfDone/EvidenceView/index.tsx | 4 +- .../IncidentsDefinitionOfDone.tsx | 23 +-- .../IncidentsDefinitionOfDoneItem.tsx | 22 +-- .../IncidentDetails/EditIncidentTitleForm.tsx | 2 +- .../EditableIncidentTitleBreadcrumb.tsx | 5 +- .../IncidentDetails.stories.tsx | 2 +- .../IncidentDetails/IncidentDetailsPanel.tsx | 10 +- .../IncidentDetails/IncidentSidebar.tsx | 7 +- .../IncidentDetails/IncidentWorkflow.tsx | 8 +- src/components/IncidentDetails/Responders.tsx | 2 +- src/components/IncidentSeverityTag/index.tsx | 2 +- src/components/IncidentStatusTag/index.tsx | 2 +- .../Incidents/IncidentCreate/index.tsx | 26 ++-- .../IncidentList/IncidentListColumns.tsx | 14 +- .../IncidentList/incidenttList.stories.tsx | 2 +- .../Incidents/IncidentList/index.tsx | 2 +- src/components/Incidents/data.tsx | 11 +- src/components/Insights/Insights.tsx | 8 +- .../Insights/cells/ConfigInsightAgeCell.tsx | 10 +- .../Insights/cells/ConfigInsightNameCell.tsx | 4 +- .../JobsHistory/JobsHistoryTableColumn.tsx | 2 +- src/components/LogBackends/LogBackends.ts | 2 +- .../LogBackends/LogBackendsList.tsx | 2 +- src/components/Logs/Table/LogsTable.tsx | 2 +- .../ManageUserRoles/ManageUserRoles.tsx | 2 +- .../notificationsTableColumns.tsx | 5 +- .../Runs/Actions/PlaybookRunsActionItem.tsx | 4 +- .../Actions/PlaybookRunsActions.stories.tsx | 8 +- .../Runs/Actions/PlaybookRunsActions.tsx | 11 +- .../Runs/Actions/PlaybooksActionsResults.tsx | 2 +- .../Playbooks/Runs/PlaybookRunsList.tsx | 8 +- .../Playbooks/Runs/PlaybookRunsSidePanel.tsx | 4 +- .../Playbooks/Runs/PlaybookRunsStatus.tsx | 2 +- .../Runs/Submit/AddPlaybookToRunParams.tsx | 2 +- .../Runs/Submit/SelectPlaybookToRun.tsx | 2 +- .../Runs/Submit/SubmitPlaybookRunForm.tsx | 2 +- .../Playbooks/Settings/PlaybookSpecsForm.tsx | 2 +- .../Playbooks/Settings/PlaybookSpecsTable.tsx | 28 +--- .../RadioOptionsGroup.stories.tsx | 2 +- .../SchemaResourcePage/SchemaResourceList.tsx | 8 +- .../Cells/ConfigChangeAgeCell.tsx | 13 ++ src/components/Sidebars/incidents.tsx | 2 +- src/components/Status/HealthCheckStatus.tsx | 2 +- src/components/TeamMembers/TeamMembers.tsx | 3 +- src/components/TopologyCard/Property.tsx | 4 +- .../TopologyConfigAnalysisLine.tsx | 2 +- .../TopologyCard/TopologyDropdownMenu.tsx | 4 +- .../TopologyCard/TopologySnapshotModal.tsx | 2 +- .../TopologyCard/Utils/FormatProperties.tsx | 2 +- .../TopologyConfigLinkModal.tsx | 5 +- src/components/TopologyDetails/index.tsx | 2 +- .../TopologyPopover/topologySort.tsx | 3 +- .../TopologySidebar/ComponentTeamLink.tsx | 2 +- .../TopologySidebar/TopologyActionBar.tsx | 4 +- .../TopologySidebar/TopologyCost.tsx | 2 +- .../TopologySidebar/TopologySidebar.tsx | 2 +- src/components/UserList/index.tsx | 4 +- src/constants/hypothesisStatusOptions.tsx | 2 +- src/constants/incidentPriority.ts | 7 - src/constants/index.ts | 2 - src/context/ConfigPageContext.tsx | 2 +- src/context/HealthPageContext.tsx | 2 +- src/context/index.ts | 2 +- src/data/sampleConfigList.ts | 2 +- src/data/sampleIncident.ts | 2 +- src/hooks/useHypothesisStatusForm.ts | 2 +- src/pages/TopologyPage.tsx | 2 +- src/pages/UsersPage.tsx | 2 +- src/pages/config/ConfigDetailsPage.tsx | 6 +- src/pages/incident/IncidentDetails.tsx | 41 +++-- src/store/incident.state.ts | 2 +- src/{components/UI => ui}/Age/Age.tsx | 8 +- src/{components/UI => ui}/Age/index.tsx | 0 src/ui/table/DateCells.tsx | 51 +++++++ src/ui/table/TagCell.tsx | 22 +++ src/ui/table/User.tsx | 20 +++ src/ui/table/index.tsx | 18 +++ src/utils/date.ts | 18 ++- 206 files changed, 1156 insertions(+), 1104 deletions(-) create mode 100644 src/api/traits.ts create mode 100644 src/api/types/common.ts create mode 100644 src/api/types/configs.ts create mode 100644 src/api/types/evidence.ts rename src/{types/healthChecks.ts => api/types/health.ts} (89%) create mode 100644 src/api/types/hypothesis.ts create mode 100644 src/api/types/incident.ts rename src/{components/Playbooks/Runs/PlaybookRunTypes.tsx => api/types/playbooks.ts} (52%) rename src/{context/TopologyPageContext.tsx => api/types/topology.ts} (51%) create mode 100644 src/api/types/users.ts delete mode 100644 src/components/EvidenceBuilder/components/EvidenceSearch/index.js delete mode 100644 src/components/EvidenceBuilder/components/EvidenceSelect/index.js delete mode 100644 src/components/EvidenceBuilder/components/EvidenceType/index.js create mode 100644 src/components/Sidebars/ConfigChanges/Cells/ConfigChangeAgeCell.tsx delete mode 100644 src/constants/incidentPriority.ts rename src/{components/UI => ui}/Age/Age.tsx (85%) rename src/{components/UI => ui}/Age/index.tsx (100%) create mode 100644 src/ui/table/DateCells.tsx create mode 100644 src/ui/table/TagCell.tsx create mode 100644 src/ui/table/User.tsx create mode 100644 src/ui/table/index.tsx diff --git a/src/api/query-hooks/health.tsx b/src/api/query-hooks/health.tsx index ad5120851..4c2e72116 100644 --- a/src/api/query-hooks/health.tsx +++ b/src/api/query-hooks/health.tsx @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../types/health"; import { getStartValue } from "../../utils/common"; import { getCanaryGraph } from "../services/topology"; diff --git a/src/api/query-hooks/index.ts b/src/api/query-hooks/index.ts index 3a12b13b7..1db6ed8bb 100644 --- a/src/api/query-hooks/index.ts +++ b/src/api/query-hooks/index.ts @@ -1,5 +1,5 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import { CostsData } from "../../components/CostDetails/CostDetails"; +import { CostsData } from "../../api/types/common"; import { toastError } from "../../components/Toast/toast"; import { getAllChanges, @@ -9,19 +9,16 @@ import { getConfigChanges, getConfigInsight, getConfigInsights, - getConfigTagsList, getConfigName, - getTopologyRelatedInsights, - getConfigsSummary + getConfigsSummary, + getConfigTagsList, + getTopologyRelatedInsights } from "../services/configs"; import { getHypothesisResponse } from "../services/hypothesis"; import { getIncident } from "../services/incident"; +import { getIncidentHistory } from "../services/IncidentsHistory"; +import { LogsResponse, searchLogs, SearchLogsPayload } from "../services/logs"; import { - getIncidentHistory, - IncidentHistory -} from "../services/IncidentsHistory"; -import { - ComponentTeamItem, getAllAgents, getComponentTeams, getHealthCheckItem, @@ -30,8 +27,9 @@ import { getTopologyComponents, getTopologyComponentsWithLogs } from "../services/topology"; +import { ComponentTeamItem } from "../types/topology"; import { getPersons, getVersionInfo } from "../services/users"; -import { LogsResponse, searchLogs, SearchLogsPayload } from "../services/logs"; +import { IncidentHistory } from "../types/incident"; const defaultStaleTime = 1000 * 60 * 5; diff --git a/src/api/query-hooks/mutations/comment.tsx b/src/api/query-hooks/mutations/comment.tsx index 0b4ebcca4..2b6d29a65 100644 --- a/src/api/query-hooks/mutations/comment.tsx +++ b/src/api/query-hooks/mutations/comment.tsx @@ -5,7 +5,9 @@ import { } from "@tanstack/react-query"; import { uniqueId } from "lodash"; import { createIncidentQueryKey } from ".."; -import { Comment, createComment, NewComment } from "../../services/comments"; +import { createComment } from "../../services/comments"; +import { NewComment } from "../../types/incident"; +import { Comment } from "../../types/incident"; const updatesHypothesisComments = ( queryClient: QueryClient, @@ -41,9 +43,7 @@ export function useCreateCommentMutation() { created_by: user, incident_id: incidentId, hypothesis_id: hypothesisId, - comment, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString() + comment }; updatesHypothesisComments(queryClient, payload, "add"); return createComment({ user, incidentId, hypothesisId, comment }).catch( diff --git a/src/api/query-hooks/mutations/evidence.tsx b/src/api/query-hooks/mutations/evidence.tsx index 9de893faa..4026121fa 100644 --- a/src/api/query-hooks/mutations/evidence.tsx +++ b/src/api/query-hooks/mutations/evidence.tsx @@ -1,10 +1,7 @@ import { useMutation } from "@tanstack/react-query"; -import { - createEvidence, - Evidence, - updateEvidence -} from "../../services/evidence"; +import { createEvidence, updateEvidence } from "../../services/evidence"; import { useIncidentState } from "../../../store/incident.state"; +import { Evidence } from "../../types/evidence"; /** * diff --git a/src/api/query-hooks/mutations/hypothesis.tsx b/src/api/query-hooks/mutations/hypothesis.tsx index b530aa3f7..fda10d738 100644 --- a/src/api/query-hooks/mutations/hypothesis.tsx +++ b/src/api/query-hooks/mutations/hypothesis.tsx @@ -1,11 +1,10 @@ import { useMutation } from "@tanstack/react-query"; +import { createHypothesis, updateHypothesis } from "../../services/hypothesis"; import { Hypothesis, - NewHypothesis, - createHypothesis, HypothesisInfo, - updateHypothesis -} from "../../services/hypothesis"; + NewHypothesis +} from "../../types/hypothesis"; export function useCreateHypothesisMutation({ onSuccess diff --git a/src/api/query-hooks/mutations/useUpdateComponentMutation.tsx b/src/api/query-hooks/mutations/useUpdateComponentMutation.tsx index 08c553811..922294f0f 100644 --- a/src/api/query-hooks/mutations/useUpdateComponentMutation.tsx +++ b/src/api/query-hooks/mutations/useUpdateComponentMutation.tsx @@ -1,6 +1,6 @@ import { useMutation } from "@tanstack/react-query"; -import { Topology } from "../../../context/TopologyPageContext"; import { updateComponent } from "../../services/topology"; +import { Topology } from "../../types/topology"; export default function useUpdateComponentMutation(onSuccess?: () => void) { return useMutation( diff --git a/src/api/query-hooks/playbooks.tsx b/src/api/query-hooks/playbooks.tsx index c432dc768..d1a82ae53 100644 --- a/src/api/query-hooks/playbooks.tsx +++ b/src/api/query-hooks/playbooks.tsx @@ -5,13 +5,13 @@ import { useQuery } from "@tanstack/react-query"; import { SubmitPlaybookRunFormValues } from "../../components/Playbooks/Runs/Submit/SubmitPlaybookRunForm"; -import { PlaybookSpec } from "../../components/Playbooks/Settings/PlaybookSpecsTable"; import { getAllPlaybooksSpecs, getPlaybookSpec, getPlaybookToRunForResource, submitPlaybookRun } from "../services/playbooks"; +import { PlaybookSpec } from "../types/playbooks"; export function useGetAllPlaybookSpecs( options: UseQueryOptions<PlaybookSpec[], Error> = {} diff --git a/src/api/query-hooks/responders.tsx b/src/api/query-hooks/responders.tsx index c47d22dc3..a1920b7f2 100644 --- a/src/api/query-hooks/responders.tsx +++ b/src/api/query-hooks/responders.tsx @@ -1,6 +1,7 @@ import { useQuery } from "@tanstack/react-query"; -import { Team, getTeams } from "../services/teams"; -import { User, getPersons } from "../services/users"; +import { getTeams } from "../services/teams"; +import { Team, User } from "../types/users"; +import { getPersons } from "../services/users"; export function useGetAllTeams() { return useQuery<Team[], Error>(["teams", "all"], () => getTeams()); diff --git a/src/api/query-hooks/useCheckStattiQuery.ts b/src/api/query-hooks/useCheckStattiQuery.ts index f5fcac705..93336e0d3 100644 --- a/src/api/query-hooks/useCheckStattiQuery.ts +++ b/src/api/query-hooks/useCheckStattiQuery.ts @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { PaginationInfo } from "../services/configs"; +import { PaginationInfo } from "../types/common"; import { getCheckStatuses } from "../services/topology"; export function useCheckStattiQuery( diff --git a/src/api/query-hooks/useComponentConfigRelationshipQuery.ts b/src/api/query-hooks/useComponentConfigRelationshipQuery.ts index 2a89584a0..fc24e8142 100644 --- a/src/api/query-hooks/useComponentConfigRelationshipQuery.ts +++ b/src/api/query-hooks/useComponentConfigRelationshipQuery.ts @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; -import { ConfigItem, getConfigsBy } from "../services/configs"; +import { getConfigsBy } from "../services/configs"; +import { ConfigItem } from "../types/configs"; export const componentConfigRelationshipQueryKey = ({ topologyId, diff --git a/src/api/query-hooks/useConfigAnalysisQuery.ts b/src/api/query-hooks/useConfigAnalysisQuery.ts index 090747c62..d835fe312 100644 --- a/src/api/query-hooks/useConfigAnalysisQuery.ts +++ b/src/api/query-hooks/useConfigAnalysisQuery.ts @@ -1,12 +1,12 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import { ConfigTypeInsights } from "../../components/ConfigInsights"; import { AxiosResponseWithTotalEntries } from "../../types"; import { getAllConfigInsights, getConfigAnalysisByComponent, - getConfigsByIDs, - PaginationInfo + getConfigsByIDs } from "../services/configs"; +import { PaginationInfo } from "../types/common"; +import { ConfigAnalysis } from "../types/configs"; export function useConfigInsightsQuery( queryParams: { @@ -23,11 +23,11 @@ export function useConfigInsightsQuery( }, pageInfo: PaginationInfo, options?: UseQueryOptions< - AxiosResponseWithTotalEntries<ConfigTypeInsights[]>, + AxiosResponseWithTotalEntries<ConfigAnalysis[]>, Error > ) { - return useQuery<AxiosResponseWithTotalEntries<ConfigTypeInsights[]>, Error>( + return useQuery<AxiosResponseWithTotalEntries<ConfigAnalysis[]>, Error>( ["configs", "insights", pageInfo, queryParams, sortBy], async () => { if (queryParams.component) { diff --git a/src/api/query-hooks/useGetConfigChangesByConfigChangeIdQuery.ts b/src/api/query-hooks/useGetConfigChangesByConfigChangeIdQuery.ts index cd76829fd..9a908aa71 100644 --- a/src/api/query-hooks/useGetConfigChangesByConfigChangeIdQuery.ts +++ b/src/api/query-hooks/useGetConfigChangesByConfigChangeIdQuery.ts @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import { getConfigChangeById } from "../services/configs"; -export function useGetConfigChangesByConfigChangeIdQuery( +export function useGetConfigChangesById( id: string, configId: string, { enabled = true } diff --git a/src/api/schemaResources.ts b/src/api/schemaResources.ts index b454c859f..dc067bfbe 100644 --- a/src/api/schemaResources.ts +++ b/src/api/schemaResources.ts @@ -1,15 +1,15 @@ +import { AxiosResponse } from "axios"; +import { EventQueueStatus } from "../components/EventQueueStatus/eventQueue"; +import { TopologyComponentItem } from "../components/FilterIncidents/FilterIncidentsByComponents"; +import { JobHistoryStatus } from "../components/JobsHistory/JobsHistoryTable"; +import { LogBackends } from "../components/LogBackends/LogBackends"; import { SchemaApi, SchemaBackends } from "../components/SchemaResourcePage/resourceTypes"; -import { CanaryCheckerDB, ConfigDB, IncidentCommander } from "./axios"; import { AVATAR_INFO } from "../constants"; -import { AxiosResponse } from "axios"; -import { JobHistoryStatus } from "../components/JobsHistory/JobsHistoryTable"; -import { ConfigItem } from "./services/configs"; -import { TopologyComponentItem } from "../components/FilterIncidents/FilterIncidentsByComponents"; -import { LogBackends } from "../components/LogBackends/LogBackends"; -import { EventQueueStatus } from "../components/EventQueueStatus/eventQueue"; +import { CanaryCheckerDB, ConfigDB, IncidentCommander } from "./axios"; +import { ConfigItem } from "./types/configs"; export interface SchemaResourceI { id: string; diff --git a/src/api/services/IncidentsHistory.ts b/src/api/services/IncidentsHistory.ts index be9a86a3d..759037453 100644 --- a/src/api/services/IncidentsHistory.ts +++ b/src/api/services/IncidentsHistory.ts @@ -1,83 +1,5 @@ import { IncidentCommander } from "../axios"; -import { ConfigItem } from "./configs"; -import { Evidence } from "./evidence"; -import { Hypothesis } from "./hypothesis"; -import { Incident } from "./incident"; -import { User } from "./users"; -import { Topology } from "../../context/TopologyPageContext"; -import { ConfigTypeChanges } from "../../components/ConfigChanges"; -import { ConfigTypeInsights } from "../../components/ConfigInsights"; -import { HealthCheck } from "../../types/healthChecks"; - -// expand this list to add more items -export enum IncidentHistoryType { - "incident.created" = "incident.created", - "responder.created" = "responder.created", - "evidence.created" = "evidence.created", - "incident.status_updated" = "incident.status_updated", - "evidence.done_definition_added" = "evidence.done_definition_added", - "incident_status.updated" = "incident_status.updated", - "responder.commented" = "responder.commented", - "hypothesis.created" = "hypothesis.created", - "hypothesis.status_updated" = "hypothesis.status_updated" -} - -export interface Comment { - id: string; - created_by: Pick<User, "id" | "name" | "avatar">; - comment: string; - external_id?: string; - incident_id?: string; - responder_id?: string; - hypothesis_id?: string; - read?: string; - updated_at?: string; - external_created_by?: string; -} - -export interface EvidenceWithEvidenceItems extends Evidence { - components?: Topology; - configs?: ConfigItem; - config_changes?: ConfigTypeChanges; - config_analysis?: ConfigTypeInsights; - checks?: HealthCheck; -} - -export interface Responder { - id: string; - incident_id: string; - type: string; - index: number; - person_id: string; - person?: Pick<User, "id" | "name" | "avatar">; - team_id?: string; - team?: { id: string; name: string; icon: string }; - external_id?: string; - properties?: Record<string, any>; - acknowledged?: string; - resolved?: string; - closed?: string; - created_by: Pick<User, "id" | "name" | "avatar">; - created_at: string; - updated_at: string; -} - -export interface IncidentHistory { - id: string; - incident_id: string; - created_by: Pick<User, "id" | "name" | "avatar">; - type: IncidentHistoryType; - description: string; - hypothesis_id?: string; - created_at: string; - updated_at: string; - hypotheses?: Hypothesis[]; - incident?: Incident; - evidence?: EvidenceWithEvidenceItems; - hypothesis?: Hypothesis; - responder?: Responder; - comment?: Comment; -} +import { IncidentHistory } from "../types/incident"; export const getIncidentHistory = async (incidentID: string) => { const res = await IncidentCommander.get<IncidentHistory[]>( diff --git a/src/api/services/comments.ts b/src/api/services/comments.ts index 93b4ebf4b..e99e81aa2 100644 --- a/src/api/services/comments.ts +++ b/src/api/services/comments.ts @@ -1,23 +1,7 @@ import { IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -import { User } from "./users"; - -export interface NewComment { - user: User; - incidentId: string; - hypothesisId: string; - comment: string; -} - -export interface Comment { - id: string; - created_by: User; - incident_id: string; - hypothesis_id: string; - comment: string; - created_at: string; - updated_at: string; -} +import { NewComment } from "../types/incident"; +import { Comment } from "../types/incident"; export const getCommentsByHypothesis = (hypothesisId: string) => resolve<Comment[]>( diff --git a/src/api/services/configs.ts b/src/api/services/configs.ts index 492d7c718..140e7ad73 100644 --- a/src/api/services/configs.ts +++ b/src/api/services/configs.ts @@ -1,91 +1,13 @@ -// http://incident-commander.canary.lab.flanksource.com/config/db - -import { ConfigTypeInsights } from "../../components/ConfigInsights"; -import { User } from "../../api/services/users"; import { Config, ConfigDB, IncidentCommander } from "../axios"; import { resolve } from "../resolve"; - -export type ConfigChange = { - id: string; - config_id: string; - external_change_id: string; - change_type: string; - severity: string; - source: string; - summary: string; - patches?: string; - diff?: string; - details: string; - created_at: string; - created_by: string | User; - external_created_by: string; - config?: ConfigItem; - config_class?: string; - type?: string; - name?: string; -}; - -export type ConfigItem = { - name: string; - external_id?: string; - config_class?: string; - type?: string; - id: string; - changes?: Change[]; - analysis?: Analysis[]; - tags?: Record<string, any>; - allTags?: Record<string, any>; - created_at?: string; - updated_at?: string; - deleted_at?: string; - cost_per_minute?: number; - cost_total_1d?: number; - cost_total_7d?: number; - cost_total_30d?: number; - config: Record<string, any>; - agent_id?: string; - agent?: { - id: string; - name: string; - }; - config_scrapers?: { - id: string; - name: string; - }; -} - -export type ConfigTypeRelationships = { - config_id: string; - related_id: string; - property: string; - created_at: string; - updated_at: string; - deleted_at: string; - selector_id: string; - configs: ConfigItem; - related: ConfigItem; -}; - -interface Change { - change_type: string; - total: number; - severity?: string; -} - -interface Analysis { - category: string; - severity: string; - description: string; - analysis_type: string; - analyzer: string; -} - -export type PaginationInfo = { - pageSize: number; - pageIndex: number; -}; - -// Config Items +import { PaginationInfo } from "../types/common"; +import { + ConfigAnalysis, + ConfigChange, + ConfigItem, + ConfigSummary, + ConfigTypeRelationships +} from "../types/configs"; export const getAllConfigs = () => resolve<ConfigItem[]>(ConfigDB.get(`/configs`)); @@ -107,21 +29,6 @@ export const getAllConfigsForSearchPurpose = async () => { return res.data ?? []; }; -export type ConfigSummary = { - type: string; - analysis?: Record<string, any>; - changes?: string; - total_configs: number; - cost_per_minute?: number; - cost_total_1d?: number; - cost_total_7d?: number; - cost_total_30d?: number; - agent?: { - id: string; - name: string; - }; -}; - export const getConfigsSummary = async () => { const res = await resolve<ConfigSummary[] | null>( ConfigDB.get(`/config_summary`) @@ -388,7 +295,7 @@ export const getConfigInsights = ( return resolve( ConfigDB.get< Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" @@ -411,7 +318,7 @@ export const getConfigInsights = ( }; export const getConfigInsightsByID = async (id: string) => { - const res = await ConfigDB.get<ConfigTypeInsights[] | null>( + const res = await ConfigDB.get<ConfigAnalysis[] | null>( `/config_analysis?select=id,source,analyzer,analysis_type,message,severity,status,analysis,first_observed,config:configs(id,name,config_class,type)&id=eq.${id}`, { headers: { @@ -443,7 +350,7 @@ export const getTopologyRelatedInsights = async ( config_class: string; type: string; analysis: Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" @@ -513,7 +420,7 @@ export const getAllConfigInsights = async ( "&order=first_observed.desc"; return resolve( - ConfigDB.get<ConfigTypeInsights[] | null>( + ConfigDB.get<ConfigAnalysis[] | null>( `/config_analysis?select=id,analysis_type,analyzer,severity,status,first_observed,last_observed,config:configs(id,name,config_class,type)${pagingParams}${queryParamsString}${sortString}`, { headers: { @@ -546,7 +453,7 @@ export const getComponentConfigChanges = async (topologyID: string) => { }; export const getConfigAnalysisByComponent = async (componentId: string) => { - const res = await ConfigDB.get<ConfigTypeInsights[]>( + const res = await ConfigDB.get<ConfigAnalysis[]>( `/rpc/lookup_analysis_by_component?id=${componentId}` ); return res.data; diff --git a/src/api/services/evidence.ts b/src/api/services/evidence.ts index ea9bd308e..55e305ee6 100644 --- a/src/api/services/evidence.ts +++ b/src/api/services/evidence.ts @@ -1,39 +1,6 @@ import { IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -import { User } from "./users"; - -export enum EvidenceType { - Log = "log", - Config = "config", - Topology = "topology", - Check = "check", - ConfigAnalysis = "config_analysis", - ConfigChange = "config_change", - // inline comment - Comment = "comment" -} - -export type Evidence = { - user?: User; - id: string; - hypothesisId: string; - component_id?: string; - config_id?: string; - config_analysis_id?: string; - config_change_id?: string; - check_id?: string; - evidence?: Record<string, any>; - description?: string; - definition_of_done?: boolean; - script?: string; - properties?: string; - created_at: string; - updated_at?: string; - created_by?: User; - type: EvidenceType; - done?: boolean; - hypothesis_id: string; -}; +import { Evidence } from "../types/evidence"; export const getEvidence = async (id: string) => resolve(IncidentCommander.get(`/evidences?id=eq.${id}`)); diff --git a/src/api/services/hypothesis.ts b/src/api/services/hypothesis.ts index 656a739e3..3c959cb50 100644 --- a/src/api/services/hypothesis.ts +++ b/src/api/services/hypothesis.ts @@ -1,42 +1,14 @@ import { IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -import { Comment } from "./comments"; -import { Evidence } from "./evidence"; -import { User } from "./users"; - -export type HypothesisNodeType = "root" | "factor" | "solution"; - -export enum HypothesisStatus { - Proven = "proven", - Likely = "likely", - Possible = "possible", - Unlikely = "unlikely", - Improbable = "improbable", - Disproven = "disproven" -} - -export interface Hypothesis { - title: string; - status: HypothesisStatus; - created_by?: User; - parent_id?: string; - evidences?: Evidence[]; - comment?: any[]; - id: string; - incident_id: string; - type: HypothesisNodeType; - children?: any[]; -} - -const hypothesisChildType = { - default: "root", - root: "factor", - factor: "solution" -} as const; - -export const getHypothesisChildType = ( - nodeType?: keyof typeof hypothesisChildType -) => hypothesisChildType[nodeType || "default"]; +import { Evidence } from "../types/evidence"; +import { + Hypothesis, + HypothesisInfo, + HypothesisStatus, + NewHypothesis +} from "../types/hypothesis"; +import { User } from "../types/users"; +import { Comment } from "../types/incident"; export const getAllHypothesisByIncident = (incidentId: string) => resolve<Hypothesis[]>( @@ -86,30 +58,6 @@ export const searchHypothesis = (incidentId: string, query: string) => ) ); -export interface HypothesisInfo { - type: HypothesisNodeType; - title: string; - status: HypothesisStatus; -} - -interface NewBaseHypothesis { - user: User; - incident_id: string; - title?: string; - status: HypothesisStatus; -} - -export type NewRootNode = { - type: "root"; -} & NewBaseHypothesis; - -export type NewChildNode = { - type: "factor" | "solution"; - parent_id?: string; -} & NewBaseHypothesis; - -export type NewHypothesis = NewRootNode | NewChildNode; - export const createHypothesis = async ({ user, ...params }: NewHypothesis) => { const { data, error } = await resolve<[Hypothesis]>( IncidentCommander.post(`/hypotheses`, { diff --git a/src/api/services/incident.ts b/src/api/services/incident.ts index d6c5e504a..e8215e8ff 100644 --- a/src/api/services/incident.ts +++ b/src/api/services/incident.ts @@ -1,54 +1,14 @@ import { stringify } from "qs"; +import { AVATAR_INFO } from "../../constants"; import { IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -import { Hypothesis } from "./hypothesis"; -import { User } from "./users"; -import { AVATAR_INFO } from "../../constants"; -import { typeItems } from "../../components/Incidents/data"; - -export const enum IncidentSeverity { - Low = "low", - Medium = "medium", - High = "high", - Blocker = "blocker", - Critical = "critical" -} - -export enum IncidentStatus { - "New" = "new", - Open = "open", - Investigating = "investigating", - Mitigated = "mitigated", - Resolved = "resolved", - Closed = "closed" -} - -export interface NewIncident { - title: string; - description: string; - - severity: IncidentSeverity | string; - type?: string; - status?: IncidentStatus; - - created_by: string; - commander_id: { - id?: string; - name?: string; - avatar?: string; - }; - communicator_id: string; -} - -export interface Incident extends NewIncident { - id: string; - incident_id: string; - parent_id: string; - hypotheses: Hypothesis[]; - created_at: string; - involved: User[]; - commander: User; -} +import { + Incident, + IncidentStatus, + IncidentSummary, + NewIncident +} from "../types/incident"; +import { User } from "../types/users"; export const searchIncident = (query: string) => { const hypotheses = `hypotheses(id, type)`; @@ -120,20 +80,6 @@ export const getIncidentsBy = async ({ } }; -export type IncidentSummary = { - id: string; - incident_id: string; - title: string; - severity: IncidentSeverity; - type: keyof typeof typeItems; - status: IncidentStatus; - created_at: string; - updated_at: string; - commander?: User; - responders?: User[]; - commenters?: User[]; -}; - export const getIncidentsSummary = async ( params?: Record<string, string | undefined> ) => { diff --git a/src/api/services/playbooks.ts b/src/api/services/playbooks.ts index ccde80d5f..4866bd071 100644 --- a/src/api/services/playbooks.ts +++ b/src/api/services/playbooks.ts @@ -1,18 +1,16 @@ -import { PlaybookRunWithActions } from "../../components/Playbooks/Runs/Actions/PlaybookRunsActions"; -import { - PlaybookRun, - PlaybookRunAction -} from "../../components/Playbooks/Runs/PlaybookRunTypes"; import { SubmitPlaybookRunFormValues } from "../../components/Playbooks/Runs/Submit/SubmitPlaybookRunForm"; -import { - NewPlaybookSpec, - PlaybookSpec, - UpdatePlaybookSpec -} from "../../components/Playbooks/Settings/PlaybookSpecsTable"; import { AVATAR_INFO } from "../../constants"; import { ConfigDB, IncidentCommander, PlaybookAPI } from "../axios"; import { GetPlaybooksToRunParams } from "../query-hooks/playbooks"; import { resolve } from "../resolve"; +import { + NewPlaybookSpec, + PlaybookRun, + PlaybookRunAction, + PlaybookRunWithActions, + PlaybookSpec, + UpdatePlaybookSpec +} from "../types/playbooks"; export async function getAllPlaybooksSpecs() { const res = await IncidentCommander.get<PlaybookSpec[] | null>( diff --git a/src/api/services/responder.ts b/src/api/services/responder.ts index 46dedfc6a..434758762 100644 --- a/src/api/services/responder.ts +++ b/src/api/services/responder.ts @@ -1,32 +1,12 @@ import { IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -import { Team } from "./teams"; -import { User } from "./users"; - -export type Responder = { - id: string; - incident_id?: string; - type: string; - index?: number; - person_id?: string; - team_id?: string; - external_id?: string; - properties?: Record<string, any>; - acknowledged?: string; - resolved?: string; - closed?: string; - created_by?: string; - created_at: string; - updated_at: string; - person?: User; - team?: Pick<Team, "id" | "name" | "icon" | "spec">; -}; +import { Responder } from "../types/incident"; export const getResponder = async (ids: string[]) => resolve(IncidentCommander.get<Responder | null>(`/person?id=in.(${ids})`)); export const saveResponder = ( - params: Omit<Responder, "id" | "updated_at" | "created_at"> + params: Omit<Responder, "id" | "created_at" | "updated_at"> ) => resolve(IncidentCommander.post<Responder>(`/responders?select=*`, params)); export const getRespondersForTheIncident = async (id: string) => { diff --git a/src/api/services/teams.ts b/src/api/services/teams.ts index 00a3c3a05..298066c4a 100644 --- a/src/api/services/teams.ts +++ b/src/api/services/teams.ts @@ -1,18 +1,6 @@ import { IncidentCommander } from "../axios"; import { resolve } from "../resolve"; -import { User } from "./users"; - -export type Team = { - id: string; - name: string; - icon: string; - spec: Record<string, any>; - source?: string; - created_by?: User; - created_at: string; - updated_at: string; - deleted_at?: string; -}; +import { Team, User } from "../types/users"; export const getTeam = async (id: string): Promise<Team | undefined> => { const response = await IncidentCommander.get<Team | null>( diff --git a/src/api/services/topology.ts b/src/api/services/topology.ts index f0035dea0..cb9c4d0c3 100644 --- a/src/api/services/topology.ts +++ b/src/api/services/topology.ts @@ -1,17 +1,19 @@ import { stringify } from "qs"; +import { TopologyComponentItem } from "../../components/FilterIncidents/FilterIncidentsByComponents"; +import { AVATAR_INFO, TimeRangeToMinutes } from "../../constants"; import { CanaryChecker, CanaryCheckerDB, IncidentCommander, Snapshot } from "../axios"; -import { TopologyComponentItem } from "../../components/FilterIncidents/FilterIncidentsByComponents"; -import { HealthCheck, HealthCheckStatus } from "../../types/healthChecks"; -import { AVATAR_INFO, TimeRangeToMinutes } from "../../constants"; -import { User } from "./users"; -import { Topology } from "../../context/TopologyPageContext"; -import { PaginationInfo } from "./configs"; import { resolve } from "../resolve"; +import { PaginationInfo } from "../types/common"; +import { HealthCheck, HealthCheckStatus } from "../types/health"; +import { Topology } from "../types/topology"; +import { ComponentTeamItem } from "../types/topology"; +import { ComponentTemplateItem } from "../types/topology"; +import { AgentItem } from "../types/common"; interface IParam { id?: string; @@ -170,12 +172,6 @@ export const getTopologyComponentsWithLogs = () => { ); }; -type AgentItem = { - id: string; - name: string; - description: string; -}; - export const getAgentByID = async (id: string) => { const res = await IncidentCommander.get<AgentItem[] | null>( `/agents?select=id,name,description&id=eq.${id}` @@ -213,18 +209,6 @@ export const getTopologyComponent = (id: string) => { ); }; -export type ComponentTemplateItem = { - id: string; - name: string; - namespace: string; - labels: Record<string, string>; - spec: any; - created_at: string; - updated_at: string; - schedule: string; - deleted_at: string; -}; - export const getComponentTemplate = async (id: string) => { const res = await IncidentCommander.get<ComponentTemplateItem[] | null>( `/topologies?id=eq.${id}` @@ -239,23 +223,6 @@ export const getHealthCheckItem = async (id: string) => { return res.data?.[0]; }; -export type ComponentTeamItem = { - component_id: string; - team_id: string; - role: string; - selector_id: string; - team: { - id: string; - name: string; - icon: string; - spec: any; - source: string; - created_by: Pick<User, "avatar" | "id" | "name">; - created_at: string; - updated_at: string; - }; -}; - export const getComponentTeams = async (id: string) => { const res = await IncidentCommander.get<ComponentTeamItem[] | null>( `/team_components?component_id=eq.${id}&select=*,team:teams(*, created_by(${AVATAR_INFO}))` diff --git a/src/api/services/users.ts b/src/api/services/users.ts index 1ab136624..43a3fd1a8 100644 --- a/src/api/services/users.ts +++ b/src/api/services/users.ts @@ -1,55 +1,8 @@ import { Auth, CanaryChecker, IncidentCommander, Rback } from "../axios"; import { resolve } from "../resolve"; import packageJson from "../../../package.json"; - -interface NewUser { - name: string; - email: string; - avatar?: string; -} - -export type VersionInfo = { - frontend: string; - backend: string; - Version: string; - Timestamp: string; -}; - -export type RegisteredUser = { - id: string; - nid: string; - state: string; - state_changed_at: string | Date; - name: string; - email: string; - traits: { - name: { - last: string; - first: string; - }; - email: string; - }; - created_at: string | Date; - updated_at: string | Date; - roles?: string[]; -}; - -export type PeopleRoles = { - id: string; - name: string; - email: string; - roles: string[]; -}; - -export interface User extends NewUser { - id: string; -} - -export type InviteUserPayload = { - firstName: string; - lastName: string; - email: string; -}; +import { PeopleRoles, RegisteredUser, User, NewUser } from "../types/users"; +import { VersionInfo } from "../types/common"; export const getPerson = (id: string) => resolve<User[]>(IncidentCommander.get<User[]>(`/people?id=eq.${id}`)); @@ -79,9 +32,9 @@ export const getRegisteredUsers = () => const data = res.data.map((item: RegisteredUser) => { return { ...item, - created_at: new Date(item.created_at), + created_at: item.created_at, state_changed_at: new Date(item.state_changed_at), - updated_at: new Date(item.updated_at), + updated_at: item.updated_at, name: `${item.traits?.name?.first ?? ""} ${ item.traits?.name?.last ?? "" }`, @@ -98,6 +51,12 @@ export const getRegisteredUsers = () => }) ); +export type InviteUserPayload = { + firstName: string; + lastName: string; + email: string; +}; + export const inviteUser = ({ firstName, lastName, email }: InviteUserPayload) => resolve<{ id: string; diff --git a/src/api/traits.ts b/src/api/traits.ts new file mode 100644 index 000000000..695d7e01e --- /dev/null +++ b/src/api/traits.ts @@ -0,0 +1,29 @@ +import { DateType } from "./types/common"; +import { User } from "./types/users"; + +export interface Timestamped extends CreatedAt, UpdatedAt, Deletable { } + +export interface CreatedAt { + created_at?: DateType; +} + +export interface Avatar { + created_by?: Pick<User, "id" | "name" | "avatar">; +} + +export interface UpdatedAt { + updated_at?: DateType; +} + +export interface Deletable { + deleted_at?: DateType; +} + +export interface Agent { + agent_id?: string; +} + +export interface Namespaced { + name: string; + namespace?: string; +} diff --git a/src/api/types/common.ts b/src/api/types/common.ts new file mode 100644 index 000000000..bf506441e --- /dev/null +++ b/src/api/types/common.ts @@ -0,0 +1,47 @@ +export type PaginationInfo = { + pageSize: number; + pageIndex: number; +}; + +export type VersionInfo = { + frontend: string; + backend: string; + Version: string; + Timestamp: string; +}; + +export type CostsData = { + cost_per_minute?: number; + cost_total_1d?: number; + cost_total_7d?: number; + cost_total_30d?: number; +}; + +export type Severity = + | "info" + | "warning" + | "low" + | "medium" + | "high" + | "blocker" + | "critical"; + +export type ValueType = number | string | Date; + +export type DateType = Date | string | undefined; + +export function sortByCreatedAt<T extends { created_at?: DateType }>( + a: T, + b: T +) { + if (!a.created_at || !b.created_at) { + return 0; + } + return new Date(a.created_at).getTime() - new Date(b.created_at).getTime(); +} + +export type AgentItem = { + id: string; + name: string; + description: string; +}; diff --git a/src/api/types/configs.ts b/src/api/types/configs.ts new file mode 100644 index 000000000..2df65db68 --- /dev/null +++ b/src/api/types/configs.ts @@ -0,0 +1,92 @@ +import { Agent, CreatedAt, Avatar, Timestamped } from "../traits"; + +export interface ConfigChange extends CreatedAt, Avatar { + id: string; + config_id: string; + external_change_id: string; + change_type: string; + severity: string; + source: string; + summary: string; + patches?: string; + diff?: string; + details: string; + external_created_by: string; + config?: ConfigItem; + config_class?: string; + type?: string; + name?: string; +} + +export interface Change { + change_type: string; + total: number; + severity?: string; +} + +export interface ConfigItem extends Timestamped, Avatar, Agent { + id: string; + name: string; + external_id?: string; + config_class?: string; + type?: string; + changes?: Change[]; + analysis?: Analysis[]; + tags?: Record<string, any>; + allTags?: Record<string, any>; + cost_per_minute?: number; + cost_total_1d?: number; + cost_total_7d?: number; + cost_total_30d?: number; + config?: Record<string, any>; + agent?: { + id: string; + name: string; + }; +} + +export interface ConfigTypeRelationships extends Timestamped { + config_id: string; + related_id: string; + property: string; + selector_id: string; + configs: ConfigItem; + related: ConfigItem; +} + +export interface Analysis { + category: string; + severity: string; + description: string; + analysis_type: string; + analyzer: string; +} + +export interface ConfigAnalysis extends Analysis, CreatedAt, Avatar { + id: string; + config_id: string; + summary: string; + status: string; + message: string; + sanitizedMessageHTML?: string; + sanitizedMessageTxt?: string; + first_observed: string; + last_observed: string; + source: any; + config?: ConfigItem; +} + +export type ConfigSummary = { + type: string; + analysis?: Record<string, any>; + changes?: string; + total_configs: number; + cost_per_minute?: number; + cost_total_1d?: number; + cost_total_7d?: number; + cost_total_30d?: number; + agent?: { + id: string; + name: string; + }; +}; diff --git a/src/api/types/evidence.ts b/src/api/types/evidence.ts new file mode 100644 index 000000000..5897e46f3 --- /dev/null +++ b/src/api/types/evidence.ts @@ -0,0 +1,43 @@ +import { CreatedAt, Avatar, UpdatedAt } from "../traits"; +import { ConfigItem, ConfigChange, ConfigAnalysis } from "./configs"; +import { HealthCheck } from "./health"; +import { Topology } from "./topology"; +import { User } from "./users"; + +export enum EvidenceType { + Log = "log", + Config = "config", + Topology = "topology", + Check = "check", + ConfigAnalysis = "config_analysis", + ConfigChange = "config_change", + // inline comment + Comment = "comment" +} + +export interface Evidence extends CreatedAt, UpdatedAt, Avatar { + user?: User; + id: string; + hypothesisId: string; + component_id?: string; + config_id?: string; + config_analysis_id?: string; + config_change_id?: string; + check_id?: string; + evidence?: Record<string, any>; + description?: string; + definition_of_done?: boolean; + script?: string; + properties?: string; + type: EvidenceType; + done?: boolean; + hypothesis_id: string; +} + +export interface EvidenceWithEvidenceItems extends Evidence { + components?: Topology; + configs?: ConfigItem; + config_changes?: ConfigChange; + config_analysis?: ConfigAnalysis; + checks?: HealthCheck; +} diff --git a/src/types/healthChecks.ts b/src/api/types/health.ts similarity index 89% rename from src/types/healthChecks.ts rename to src/api/types/health.ts index 4559079fb..e8f067f41 100644 --- a/src/types/healthChecks.ts +++ b/src/api/types/health.ts @@ -1,3 +1,5 @@ +import { Agent, Avatar, Timestamped } from "../traits"; + export interface HealthChecksResponse { duration: number; runnerName: string; @@ -5,7 +7,7 @@ export interface HealthChecksResponse { checks_summary: HealthCheck[]; } -export interface HealthCheck { +export interface HealthCheck extends Timestamped, Avatar, Agent { id: string; canary_id: string; type: string; @@ -18,10 +20,7 @@ export interface HealthCheck { latency: HealthCheckLatency; checkStatuses: HealthCheckStatus[]; lastRuntime: string; - updatedAt: string; - createdAt: string; description?: string; - deletedAt?: string; source?: string; spec?: any; icon?: string; @@ -33,7 +32,6 @@ export interface HealthCheck { owner?: any; loading?: boolean; severity?: string; - agent_id?: string; } export interface HealthCheckStatus { diff --git a/src/api/types/hypothesis.ts b/src/api/types/hypothesis.ts new file mode 100644 index 000000000..be0c1d276 --- /dev/null +++ b/src/api/types/hypothesis.ts @@ -0,0 +1,60 @@ +import { Evidence } from "./evidence"; +import { User } from "./users"; + +export type HypothesisNodeType = "root" | "factor" | "solution"; + +export enum HypothesisStatus { + Proven = "proven", + Likely = "likely", + Possible = "possible", + Unlikely = "unlikely", + Improbable = "improbable", + Disproven = "disproven" +} + +export interface Hypothesis { + title: string; + status: HypothesisStatus; + created_by?: User; + parent_id?: string; + evidences?: Evidence[]; + comment?: any[]; + id: string; + incident_id: string; + type: HypothesisNodeType; + children?: any[]; +} + +export interface HypothesisInfo { + type: HypothesisNodeType; + title: string; + status: HypothesisStatus; +} + +interface NewBaseHypothesis { + user: User; + incident_id: string; + title?: string; + status: HypothesisStatus; +} + +export type NewRootNode = { + type: "root"; +} & NewBaseHypothesis; + +export type NewChildNode = { + type: "factor" | "solution"; + parent_id?: string; +} & NewBaseHypothesis; + +export type NewHypothesis = NewRootNode | NewChildNode; + +const hypothesisChildType = { + default: "root", + root: "factor", + factor: "solution" +} as const; + +export const getHypothesisChildType = ( + nodeType?: keyof typeof hypothesisChildType +) => hypothesisChildType[nodeType || "default"]; diff --git a/src/api/types/incident.ts b/src/api/types/incident.ts new file mode 100644 index 000000000..9880b82ae --- /dev/null +++ b/src/api/types/incident.ts @@ -0,0 +1,144 @@ +import { CreatedAt, Avatar, UpdatedAt } from "../traits"; +import { EvidenceWithEvidenceItems } from "./evidence"; +import { Hypothesis } from "./hypothesis"; +import { Team, User } from "./users"; + +export const IncidentPriority = { + Low: "Low", + Medium: "Medium", + High: "High", + Critical: "Critical", + Blocker: "Blocker" +}; + +export const enum IncidentSeverity { + Low = "low", + Medium = "medium", + High = "high", + Blocker = "blocker", + Critical = "critical" +} + +export type IncidentType = + | "cost" + | "availability" + | "performance" + | "security" + | "integration" + | "technicalDebt" + | "reliability" + | "compliance"; + +// expand this list to add more items +export enum IncidentHistoryType { + "incident.created" = "incident.created", + "responder.created" = "responder.created", + "evidence.created" = "evidence.created", + "incident.status_updated" = "incident.status_updated", + "evidence.done_definition_added" = "evidence.done_definition_added", + "incident_status.updated" = "incident_status.updated", + "responder.commented" = "responder.commented", + "hypothesis.created" = "hypothesis.created", + "hypothesis.status_updated" = "hypothesis.status_updated" +} + +export enum IncidentStatus { + "New" = "new", + Open = "open", + Investigating = "investigating", + Mitigated = "mitigated", + Resolved = "resolved", + Closed = "closed" +} + +export interface NewIncident { + title: string; + description: string; + severity: IncidentSeverity | string; + type?: string; + status?: IncidentStatus; + commander_id: { + id?: string; + name?: string; + avatar?: string; + }; + communicator_id: string; +} + +export interface Incident extends NewIncident, CreatedAt { + id: string; + incident_id: string; + created_by: string; + parent_id: string; + hypotheses: Hypothesis[]; + involved: User[]; + commander: User; +} + +export interface Comment extends UpdatedAt, CreatedAt, Avatar { + id: string; + comment: string; + external_id?: string; + incident_id: string; + responder_id?: string; + hypothesis_id?: string; + read?: string; + external_created_by?: string; +} + +export interface NewComment { + user: User; + incidentId: string; + hypothesisId: string; + comment: string; +} + +export interface IncidentSummary extends CreatedAt, UpdatedAt { + id: string; + incident_id: string; + title: string; + severity: IncidentSeverity; + type: IncidentType; + status: IncidentStatus; + commander?: User; + responders?: User[]; + commenters?: User[]; +} + +export interface IncidentHistory extends Avatar { + id: string; + incident_id: string; + type: IncidentHistoryType; + description: string; + hypothesis_id?: string; + created_at: string; + updated_at: string; + hypotheses?: Hypothesis[]; + incident?: Incident; + evidence?: EvidenceWithEvidenceItems; + hypothesis?: Hypothesis; + responder?: Responder; + comment?: Comment; +} + +export interface Responder extends UpdatedAt, CreatedAt { + id: string; + incident_id: string; + type: string; + index?: number; + person_id?: string; + person?: Pick<User, "id" | "name" | "avatar">; + team_id?: string; + team?: Pick<Team, "id" | "name" | "icon" | "spec">; + external_id?: string; + created_by?: string; + properties?: Record<string, any>; + acknowledged?: string; + resolved?: string; + closed?: string; +} + +export type NewResponder = Omit< + Responder, + "id" | "updated_at" | "created_at" | "created_by" +> & { created_by: string }; diff --git a/src/components/Playbooks/Runs/PlaybookRunTypes.tsx b/src/api/types/playbooks.ts similarity index 52% rename from src/components/Playbooks/Runs/PlaybookRunTypes.tsx rename to src/api/types/playbooks.ts index 866d9e67f..4f292b84c 100644 --- a/src/components/Playbooks/Runs/PlaybookRunTypes.tsx +++ b/src/api/types/playbooks.ts @@ -1,8 +1,8 @@ -import { ConfigItem } from "../../../api/services/configs"; -import { User } from "../../../api/services/users"; -import { Topology } from "../../../context/TopologyPageContext"; -import { HealthCheck } from "../../../types/healthChecks"; -import { PlaybookSpec } from "../Settings/PlaybookSpecsTable"; +import { Agent, CreatedAt, Avatar } from "../traits"; +import { ConfigItem } from "./configs"; +import { HealthCheck } from "./health"; +import { Topology } from "./topology"; +import { User } from "./users"; export type PlaybookRunStatus = | "scheduled" @@ -28,23 +28,47 @@ export type PlaybookRunAction = { error?: string; }; -export type PlaybookRun = { +export type PlaybookRunWithActions = PlaybookRun & { + actions: PlaybookRunAction[]; +}; + +export interface PlaybookRun extends CreatedAt, Avatar, Agent { id: string; playbook_id?: string; status: PlaybookRunStatus; start_time: string; scheduled_time?: string; end_time?: string; - created_at?: string; - created_by?: User; check_id?: string; config_id?: string; component_id?: string; parameters?: Record<string, unknown>; - agent_id?: string; /* relationships */ playbooks?: PlaybookSpec; component?: Pick<Topology, "id" | "name" | "icon">; check?: Pick<HealthCheck, "id" | "name" | "icon">; config?: Pick<ConfigItem, "id" | "name" | "type" | "config_class">; +} + +export type PlaybookSpec = { + id: string; + name: string; + created_by?: User; + spec: any; + source: "KubernetesCRD" | "ConfigFile" | "UI"; + created_at: string; + updated_at: string; + deleted_at?: string; }; + +export type NewPlaybookSpec = Omit< + PlaybookSpec, + "id" | "created_at" | "updated_at" | "deleted_at" | "created_by" +> & { + created_by?: string; +}; + +export type UpdatePlaybookSpec = Omit< + PlaybookSpec, + "created_at" | "updated_at" | "deleted_at" | "created_by" +>; diff --git a/src/context/TopologyPageContext.tsx b/src/api/types/topology.ts similarity index 51% rename from src/context/TopologyPageContext.tsx rename to src/api/types/topology.ts index e85d6f064..b3f3a3770 100644 --- a/src/context/TopologyPageContext.tsx +++ b/src/api/types/topology.ts @@ -1,7 +1,8 @@ -import { CostsData } from "../components/CostDetails/CostDetails"; -import { Severity, typeItems } from "../components/Incidents/data"; - -export type ValueType = number | string | Date; +import { Agent, Namespaced, Timestamped } from "../traits"; +import { ValueType } from "./common"; +import { CostsData, Severity } from "./common"; +import { IncidentType } from "./incident"; +import { User } from "./users"; export type TopologyProperty = { name: string; @@ -17,13 +18,10 @@ export type TopologyProperty = { color?: string; }; -export type Topology = { +export interface Topology extends Timestamped, CostsData, Agent, Namespaced { id: string; parent_id?: string; - name: string; type?: string; - created_at?: string; - updated_at?: string; title?: string; properties?: TopologyProperty[]; components?: Topology[]; @@ -33,18 +31,14 @@ export type Topology = { text?: string; status?: string; status_reason?: string; - namespace?: string; hidden?: boolean; external_id?: string; - agent_id?: string; topology_id?: string; summary?: { - incidents?: Record< - keyof typeof typeItems, + incidents?: Record<IncidentType, Record<"High" | "Medium" | "Low", number> >; - insights?: Record< - keyof typeof typeItems, + insights?: Record<IncidentType, Record<Severity, number | undefined> >; [key: string]: any; @@ -61,5 +55,29 @@ export type Topology = { warning: number; unhealthy: number; }; - deleted_at?: string; -} & CostsData; +}; + + +export type ComponentTeamItem = { + component_id: string; + team_id: string; + role: string; + selector_id: string; + team: { + id: string; + name: string; + icon: string; + spec: any; + source: string; + created_by: Pick<User, "avatar" | "id" | "name">; + created_at: string; + updated_at: string; + }; +}; + +export interface ComponentTemplateItem extends Timestamped, Namespaced { + id: string; + labels: Record<string, string>; + spec: any; + schedule: string; +}; diff --git a/src/api/types/users.ts b/src/api/types/users.ts new file mode 100644 index 000000000..db37bbbe4 --- /dev/null +++ b/src/api/types/users.ts @@ -0,0 +1,50 @@ +import { CreatedAt, Avatar, Timestamped, UpdatedAt } from "../traits"; + +export interface NewUser { + name: string; + email: string; + avatar?: string; +} + +export interface RegisteredUser extends CreatedAt, UpdatedAt { + id: string; + nid: string; + state: string; + state_changed_at: string | Date; + name: string; + email: string; + traits: { + name: { + last: string; + first: string; + }; + email: string; + }; + roles?: string[]; +} + +export type PeopleRoles = { + id: string; + name: string; + email: string; + roles: string[]; +}; + +export interface User extends NewUser { + id: string; +} + +export type UserWithTeam = User & { + team: { + icon: string; + name: string; + }; +}; + +export interface Team extends Timestamped, Avatar { + id: string; + name: string; + icon: string; + spec: Record<string, any>; + source?: string; +} diff --git a/src/components/Agents/AgentPage.tsx b/src/components/Agents/AgentPage.tsx index 629f8a687..33d1a9ebb 100644 --- a/src/components/Agents/AgentPage.tsx +++ b/src/components/Agents/AgentPage.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { useSearchParams } from "react-router-dom"; import { useAgentsListQuery } from "../../api/query-hooks/useAgentsQuery"; -import { User } from "../../api/services/users"; +import { User } from "../../api/types/users"; import { BreadcrumbNav, BreadcrumbRoot } from "../BreadcrumbNav"; import { Head } from "../Head/Head"; import { SearchLayout } from "../Layout"; diff --git a/src/components/Agents/List/AgentsTableColumns.tsx b/src/components/Agents/List/AgentsTableColumns.tsx index b6ecba6c2..6b50fe4d4 100644 --- a/src/components/Agents/List/AgentsTableColumns.tsx +++ b/src/components/Agents/List/AgentsTableColumns.tsx @@ -1,7 +1,7 @@ import { ColumnDef } from "@tanstack/react-table"; -import { User } from "../../../api/services/users"; +import { User } from "../../../api/types/users"; +import { DateCell } from "../../../ui/table"; import { Avatar } from "../../Avatar"; -import { DateCell } from "../../ConfigViewer/columns"; import { AgentSummary } from "../AgentPage"; export const agentsTableColumns: ColumnDef<AgentSummary>[] = [ diff --git a/src/components/AttachEvidenceDialog/index.tsx b/src/components/AttachEvidenceDialog/index.tsx index 56e085549..d25729100 100644 --- a/src/components/AttachEvidenceDialog/index.tsx +++ b/src/components/AttachEvidenceDialog/index.tsx @@ -9,25 +9,23 @@ import { createIncidentQueryKey } from "../../api/query-hooks"; import { createEvidence } from "../../api/services/evidence"; import { createHypothesis, - Hypothesis, - HypothesisStatus, - NewHypothesis, searchHypothesis } from "../../api/services/hypothesis"; +import { createIncident, searchIncident } from "../../api/services/incident"; import { - createIncident, - IncidentSeverity, - IncidentStatus, - searchIncident -} from "../../api/services/incident"; + Hypothesis, + HypothesisStatus, + NewHypothesis +} from "../../api/types/hypothesis"; +import { IncidentSeverity, IncidentStatus } from "../../api/types/incident"; import { useUser } from "../../context"; import { Events, sendAnalyticEvent } from "../../services/analytics"; import { IItem } from "../../types/IItem"; import { DropdownWithActions } from "../Dropdown/DropdownWithActions"; import SelectDropdown from "../Dropdown/SelectDropdown"; -import { severityItems, typeItems } from "../Incidents/data"; import { IncidentSeverityTag } from "../IncidentSeverityTag"; import { IncidentStatusTag } from "../IncidentStatusTag"; +import { severityItems, typeItems } from "../Incidents/data"; import { Modal } from "../Modal"; import { TextInput } from "../TextInput"; import { toastSuccess } from "../Toast/toast"; diff --git a/src/components/Avatar/index.tsx b/src/components/Avatar/index.tsx index 88620fcef..f28fb89b1 100644 --- a/src/components/Avatar/index.tsx +++ b/src/components/Avatar/index.tsx @@ -3,7 +3,7 @@ import { useImage } from "react-image"; import { BsFillPersonFill } from "react-icons/bs"; import clsx from "clsx"; import ReactTooltip from "react-tooltip"; -import { User } from "../../api/services/users"; +import { User } from "../../api/types/users"; interface IProps { size?: "sm" | "lg" | "md"; @@ -91,7 +91,7 @@ export function Avatar({ <div {...containerProps} className={clsx( - `overflow-hidden justify-center items-center leading-none + `overflow-hidden justify-center items-center leading-none ${inline ? "inline-flex" : "flex"} `, sizeClass, containerProps?.className, diff --git a/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx b/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx index 641cd6bee..eaa523b0b 100644 --- a/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx +++ b/src/components/BreadcrumbNav/ConfigsDetailsBreadCrumb.tsx @@ -1,6 +1,6 @@ import { BreadcrumbChild, BreadcrumbNav, BreadcrumbRoot } from "."; import { useGetConfigByIdQuery } from "../../api/query-hooks"; -import { ConfigItem } from "../../api/services/configs"; +import { ConfigItem } from "../../api/types/configs"; import { ConfigIcon } from "../Icon/ConfigIcon"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; diff --git a/src/components/Canary/CanaryPopup/CanaryCheckDetailsLabel.tsx b/src/components/Canary/CanaryPopup/CanaryCheckDetailsLabel.tsx index 0e579ed76..e785ef3f9 100644 --- a/src/components/Canary/CanaryPopup/CanaryCheckDetailsLabel.tsx +++ b/src/components/Canary/CanaryPopup/CanaryCheckDetailsLabel.tsx @@ -1,6 +1,6 @@ import { useEffect } from "react"; import ReactTooltip from "react-tooltip"; -import { HealthCheck } from "../../../types/healthChecks"; +import { HealthCheck } from "../../../api/types/health"; type CanaryCheckDetailsLabelProps = { check?: Partial<HealthCheck>; diff --git a/src/components/Canary/CanaryPopup/CanaryCheckDetailsSpec.tsx b/src/components/Canary/CanaryPopup/CanaryCheckDetailsSpec.tsx index c9265db03..5dbe72668 100644 --- a/src/components/Canary/CanaryPopup/CanaryCheckDetailsSpec.tsx +++ b/src/components/Canary/CanaryPopup/CanaryCheckDetailsSpec.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { useCanaryCheckItemQuery } from "../../../api/query-hooks"; -import { HealthCheck } from "../../../types/healthChecks"; +import { HealthCheck } from "../../../api/types/health"; import { JSONViewer } from "../../JSONViewer"; import { Loading } from "../../Loading"; diff --git a/src/components/Canary/CanaryPopup/CanaryCheckDetailsUptime.tsx b/src/components/Canary/CanaryPopup/CanaryCheckDetailsUptime.tsx index 40a24eb32..330177f75 100644 --- a/src/components/Canary/CanaryPopup/CanaryCheckDetailsUptime.tsx +++ b/src/components/Canary/CanaryPopup/CanaryCheckDetailsUptime.tsx @@ -1,4 +1,4 @@ -import { HealthCheckUptime } from "../../../types/healthChecks"; +import { HealthCheckUptime } from "../../../api/types/health"; type CanaryCheckDetailsUptimeProps = { uptime?: HealthCheckUptime; diff --git a/src/components/Canary/CanaryPopup/CheckDetails.tsx b/src/components/Canary/CanaryPopup/CheckDetails.tsx index 8e4a43834..7cfe2a8b6 100644 --- a/src/components/Canary/CanaryPopup/CheckDetails.tsx +++ b/src/components/Canary/CanaryPopup/CheckDetails.tsx @@ -1,6 +1,6 @@ import React, { Suspense, useMemo, useRef } from "react"; import { useCanaryGraphQuery } from "../../../api/query-hooks/health"; -import { HealthCheck } from "../../../types/healthChecks"; +import { HealthCheck } from "../../../api/types/health"; import { capitalizeFirstLetter, toFixedIfNecessary @@ -10,7 +10,7 @@ import mixins from "../../../utils/mixins.module.css"; import { AccordionBox } from "../../AccordionBox"; import { DropdownStandaloneWrapper } from "../../Dropdown/StandaloneWrapper"; import { TimeRange, timeRanges } from "../../Dropdown/TimeRange"; -import { Age } from "../../UI/Age"; +import { Age } from "../../../ui/Age"; import { Duration } from "../renderers"; import { CanaryCheckDetailsLabel } from "./CanaryCheckDetailsLabel"; import { CanaryCheckDetailsSpecTab } from "./CanaryCheckDetailsSpec"; diff --git a/src/components/Canary/CanaryPopup/CheckTitle.tsx b/src/components/Canary/CanaryPopup/CheckTitle.tsx index 1a37cccb6..e87baf28f 100644 --- a/src/components/Canary/CanaryPopup/CheckTitle.tsx +++ b/src/components/Canary/CanaryPopup/CheckTitle.tsx @@ -1,6 +1,6 @@ import clsx from "clsx"; import React, { useMemo } from "react"; -import { HealthCheck } from "../../../types/healthChecks"; +import { HealthCheck } from "../../../api/types/health"; import { usePrevious } from "../../../utils/hooks"; import { Badge } from "../../Badge"; import { Icon } from "../../Icon"; diff --git a/src/components/Canary/CanaryPopup/StatusHistory/StatusHistory.tsx b/src/components/Canary/CanaryPopup/StatusHistory/StatusHistory.tsx index f0732c11a..618cad449 100644 --- a/src/components/Canary/CanaryPopup/StatusHistory/StatusHistory.tsx +++ b/src/components/Canary/CanaryPopup/StatusHistory/StatusHistory.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useState } from "react"; import { format } from "timeago.js"; import { CanaryStatus, Duration } from "../../renderers"; -import { HealthCheck, HealthCheckStatus } from "../../../../types/healthChecks"; +import { HealthCheck, HealthCheckStatus } from "../../../../api/types/health"; import { toastError } from "../../../Toast/toast"; import { CellContext, ColumnDef } from "@tanstack/react-table"; import { DataTable } from "../../../DataTable"; diff --git a/src/components/Canary/CanaryStatusChart/index.tsx b/src/components/Canary/CanaryStatusChart/index.tsx index d9e958ba8..0d7516c3c 100644 --- a/src/components/Canary/CanaryStatusChart/index.tsx +++ b/src/components/Canary/CanaryStatusChart/index.tsx @@ -18,7 +18,7 @@ import { formatISODate, subtractDateFromNow } from "../../../utils/date"; -import { HealthCheck } from "../../../types/healthChecks"; +import { HealthCheck } from "../../../api/types/health"; import { useCanaryGraphQuery } from "../../../api/query-hooks/health"; // @TODO: duration should be formatted properly, not just by ms diff --git a/src/components/Canary/CanaryTabs.tsx b/src/components/Canary/CanaryTabs.tsx index 70ea666b5..5741efa2a 100644 --- a/src/components/Canary/CanaryTabs.tsx +++ b/src/components/Canary/CanaryTabs.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../../api/types/health"; import { Tab, Tabs } from "../Tabs/Tabs"; import { useQuery } from "@tanstack/react-query"; import { getAgentByIDs } from "../../api/services/topology"; diff --git a/src/components/Canary/HealthCheckEdit/index.tsx b/src/components/Canary/HealthCheckEdit/index.tsx index 3c1105778..45a58b8eb 100644 --- a/src/components/Canary/HealthCheckEdit/index.tsx +++ b/src/components/Canary/HealthCheckEdit/index.tsx @@ -6,7 +6,7 @@ import { getComponentTemplate, getTopology } from "../../../api/services/topology"; -import { HealthCheck } from "../../../types/healthChecks"; +import { HealthCheck } from "../../../api/types/health"; import { Icon } from "../../Icon"; import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader"; diff --git a/src/components/Canary/filter.ts b/src/components/Canary/filter.ts index 319abb9cd..f8969ab7e 100644 --- a/src/components/Canary/filter.ts +++ b/src/components/Canary/filter.ts @@ -1,5 +1,5 @@ -import { orderBy, findIndex, forEach, isEmpty } from "lodash"; -import { HealthCheck, HealthCheckLabels } from "../../types/healthChecks"; +import { findIndex, isEmpty, orderBy } from "lodash"; +import { HealthCheck, HealthCheckLabels } from "../../api/types/health"; import { hasStringMatch } from "../../utils/common"; export function matchesLabel( diff --git a/src/components/Canary/index.tsx b/src/components/Canary/index.tsx index b0fac70f1..d2a0e5168 100644 --- a/src/components/Canary/index.tsx +++ b/src/components/Canary/index.tsx @@ -32,7 +32,7 @@ import { useHealthPageContext } from "../../context/HealthPageContext"; import { isCanaryUI } from "../../context/Environment"; import clsx from "clsx"; import HealthPageSkeletonLoader from "../SkeletonLoader/HealthPageSkeletonLoader"; -import { HealthChecksResponse } from "../../types/healthChecks"; +import { HealthChecksResponse } from "../../api/types/health"; import { useLocation, useSearchParams } from "react-router-dom"; import useRefreshRateFromLocalStorage from "../Hooks/useRefreshRateFromLocalStorage"; import { HEALTH_SETTINGS } from "../../constants"; diff --git a/src/components/Canary/minimal.tsx b/src/components/Canary/minimal.tsx index a1b82f22d..46b445212 100644 --- a/src/components/Canary/minimal.tsx +++ b/src/components/Canary/minimal.tsx @@ -1,9 +1,9 @@ import React, { useCallback, useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; -import { EvidenceType } from "../../api/services/evidence"; import { getCanaries } from "../../api/services/topology"; +import { EvidenceType } from "../../api/types/evidence"; +import { HealthCheck } from "../../api/types/health"; import { isCanaryUI } from "../../context/Environment"; -import { HealthCheck } from "../../types/healthChecks"; import AttachAsEvidenceButton from "../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; import { timeRanges } from "../Dropdown/TimeRange"; import { Modal } from "../Modal"; diff --git a/src/components/Canary/renderers.tsx b/src/components/Canary/renderers.tsx index 699ce78f4..8b7b84697 100644 --- a/src/components/Canary/renderers.tsx +++ b/src/components/Canary/renderers.tsx @@ -1,4 +1,4 @@ -import { HealthCheck, HealthCheckStatus } from "../../types/healthChecks"; +import { HealthCheck, HealthCheckStatus } from "../../api/types/health"; import { Icon } from "../Icon"; import { Status } from "../Status"; import { isEmpty } from "./utils"; diff --git a/src/components/Canary/table.tsx b/src/components/Canary/table.tsx index b86814bfa..11decb088 100644 --- a/src/components/Canary/table.tsx +++ b/src/components/Canary/table.tsx @@ -13,7 +13,7 @@ import { columnObject, firstColumns } from "./Columns/columns"; import { prepareRows } from "./Rows/lib"; import { useCheckSetEqualityForPreviousVsCurrent } from "../Hooks/useCheckSetEqualityForPreviousVsCurrent"; import { useSearchParams } from "react-router-dom"; -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../../api/types/health"; import clsx from "clsx"; const styles = { diff --git a/src/components/CanaryInterface/minimal.tsx b/src/components/CanaryInterface/minimal.tsx index f81332aa7..cadbeaf1d 100644 --- a/src/components/CanaryInterface/minimal.tsx +++ b/src/components/CanaryInterface/minimal.tsx @@ -10,7 +10,7 @@ import { } from "../Canary/labels"; import { CanarySorter } from "../Canary/data"; import { useSearchParams } from "react-router-dom"; -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../../api/types/health"; type Props = { checks?: HealthCheck[]; diff --git a/src/components/Changelog/EvidenceChangelogContent.tsx b/src/components/Changelog/EvidenceChangelogContent.tsx index 0c5a44094..19a5fa4d3 100644 --- a/src/components/Changelog/EvidenceChangelogContent.tsx +++ b/src/components/Changelog/EvidenceChangelogContent.tsx @@ -1,5 +1,7 @@ -import { EvidenceType } from "../../api/services/evidence"; -import { EvidenceWithEvidenceItems } from "../../api/services/IncidentsHistory"; +import { + EvidenceWithEvidenceItems, + EvidenceType +} from "../../api/types/evidence"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; import { Icon } from "../Icon"; diff --git a/src/components/Changelog/IncidentChangelogItems.tsx b/src/components/Changelog/IncidentChangelogItems.tsx index 8d96e6158..b91fcfc66 100644 --- a/src/components/Changelog/IncidentChangelogItems.tsx +++ b/src/components/Changelog/IncidentChangelogItems.tsx @@ -1,6 +1,6 @@ -import { IncidentHistory } from "../../api/services/IncidentsHistory"; +import { IncidentHistory } from "../../api/types/incident"; import { Avatar } from "../Avatar"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; import IncidentHistoryItemTypeContent from "./IncidentHistoryItemTypeContent"; type IncidentChangelogItemProps = { diff --git a/src/components/Changelog/IncidentHistoryItemTypeContent.tsx b/src/components/Changelog/IncidentHistoryItemTypeContent.tsx index 0719b25e3..a4033fe26 100644 --- a/src/components/Changelog/IncidentHistoryItemTypeContent.tsx +++ b/src/components/Changelog/IncidentHistoryItemTypeContent.tsx @@ -1,9 +1,9 @@ import { FaComment } from "react-icons/fa"; -import { IncidentStatus } from "../../api/services/incident"; import { IncidentHistory, - IncidentHistoryType -} from "../../api/services/IncidentsHistory"; + IncidentHistoryType, + IncidentStatus +} from "../../api/types/incident"; import { hypothesisStatusIconMap } from "../../constants/hypothesisStatusOptions"; import { Avatar } from "../Avatar"; import { Badge } from "../Badge"; diff --git a/src/components/CollapsiblePanel/index.tsx b/src/components/CollapsiblePanel/index.tsx index 3128be510..6b6a5e59c 100644 --- a/src/components/CollapsiblePanel/index.tsx +++ b/src/components/CollapsiblePanel/index.tsx @@ -74,11 +74,7 @@ export default function CollapsiblePanel({ leaveFrom="opacity-100" leaveTo="opacity-0" > - <div - className={`flex-1 p-2 flex flex-col max-h-full border-b border-dashed border-gray-200`} - > - {children} - </div> + <div className={`flex-1 flex flex-col max-h-full `}>{children}</div> </Transition> </div> ); diff --git a/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx b/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx index 05610d04d..e8d1f147f 100644 --- a/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx +++ b/src/components/ConfigAnalysis/ConfigInsightsColumns.tsx @@ -1,14 +1,13 @@ import { ColumnDef } from "@tanstack/react-table"; import { Link } from "react-router-dom"; -import { ConfigItem } from "../../api/services/configs"; -import { ConfigTypeInsights } from "../ConfigInsights"; +import { ConfigAnalysis, ConfigItem } from "../../api/types/configs"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; -import { DateCell } from "../ConfigViewer/columns"; import { ConfigIcon } from "../Icon/ConfigIcon"; import ConfigInsightsSeverityIcons from "./ConfigInsightsSeverityIcons"; +import { DateCell } from "../../ui/table"; export const ConfigInsightsColumns: ColumnDef< - ConfigTypeInsights & { config?: ConfigItem }, + ConfigAnalysis & { config?: ConfigItem }, any >[] = [ { diff --git a/src/components/ConfigAnalysis/ConfigInsightsList.tsx b/src/components/ConfigAnalysis/ConfigInsightsList.tsx index 37a1a162a..84dfd1b15 100644 --- a/src/components/ConfigAnalysis/ConfigInsightsList.tsx +++ b/src/components/ConfigAnalysis/ConfigInsightsList.tsx @@ -2,8 +2,8 @@ import { SortingState } from "@tanstack/react-table"; import { useMemo, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { useConfigInsightsQuery } from "../../api/query-hooks/useConfigAnalysisQuery"; +import { ConfigAnalysis } from "../../api/types/configs"; import ConfigInsightsDetailsModal from "../ConfigAnalysisLink/ConfigInsightsDetailsModal"; -import { ConfigTypeInsights } from "../ConfigInsights"; import { DataTable } from "../DataTable"; import { InfoMessage } from "../InfoMessage"; import { ConfigInsightsColumns } from "./ConfigInsightsColumns"; @@ -21,7 +21,7 @@ export default function ConfigInsightsList({ }: Props) { const [params, setParams] = useSearchParams(); const [clickedInsightItem, setClickedInsightItem] = - useState<ConfigTypeInsights>(); + useState<ConfigAnalysis>(); const [isInsightDetailsModalOpen, setIsInsightDetailsModalOpen] = useState(false); diff --git a/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx b/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx index 79be11101..7799a2f92 100644 --- a/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx +++ b/src/components/ConfigAnalysisLink/ConfigAnalysisLink.tsx @@ -1,14 +1,14 @@ import { useState } from "react"; import { ViewType } from "../../types"; -import { ConfigTypeInsights } from "../ConfigInsights"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; import { DescriptionCard } from "../DescriptionCard"; import { ConfigIcon } from "../Icon/ConfigIcon"; import ConfigInsightsDetailsModal from "./ConfigInsightsDetailsModal"; +import { ConfigAnalysis } from "../../api/types/configs"; type Props = { configAnalysis: Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" diff --git a/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx b/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx index 5ef4344b2..4b189f498 100644 --- a/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx +++ b/src/components/ConfigAnalysisLink/ConfigInsightsDetailsModal.tsx @@ -2,7 +2,6 @@ import { useQuery } from "@tanstack/react-query"; import { sanitize } from "dompurify"; import { useMemo } from "react"; import { getConfigInsightsByID } from "../../api/services/configs"; -import { EvidenceType } from "../../api/services/evidence"; import { formatISODate, isValidDate } from "../../utils/date"; import AttachAsEvidenceButton from "../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; @@ -11,6 +10,7 @@ import { DescriptionCard } from "../DescriptionCard"; import { Modal } from "../Modal"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; import ModalTitleListItems from "../Modal/ModalTitleListItems"; +import { EvidenceType } from "../../api/types/evidence"; type Props = { id?: string; diff --git a/src/components/ConfigChangeHistory/ConfigChangeHistory.stories.tsx b/src/components/ConfigChangeHistory/ConfigChangeHistory.stories.tsx index 1321a83f7..90498e925 100644 --- a/src/components/ConfigChangeHistory/ConfigChangeHistory.stories.tsx +++ b/src/components/ConfigChangeHistory/ConfigChangeHistory.stories.tsx @@ -1,9 +1,8 @@ import { MemoryRouter } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; - import { ConfigChangeHistory } from "./index"; import { ComponentMeta, ComponentStory } from "@storybook/react"; -import { ConfigTypeChanges } from "../ConfigChanges"; +import { ConfigChange } from "../../api/types/configs"; export default { title: "ConfigChangeHistory", @@ -59,7 +58,7 @@ const data = Array(10) created_at: new Date(), config_id: `id-${i}`, patches: codeSample(i) - })) as unknown as ConfigTypeChanges[]; + })) as unknown as ConfigChange[]; Default.args = { data: Array.from(data), isLoading: false }; diff --git a/src/components/ConfigChangeHistory/index.tsx b/src/components/ConfigChangeHistory/index.tsx index f617a8077..8567685c5 100644 --- a/src/components/ConfigChangeHistory/index.tsx +++ b/src/components/ConfigChangeHistory/index.tsx @@ -1,16 +1,15 @@ import { ColumnDef } from "@tanstack/table-core"; import { useState } from "react"; -import { useGetConfigChangesByConfigChangeIdQuery } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; -import { ConfigChange } from "../../api/services/configs"; -import { ConfigTypeChanges } from "../ConfigChanges"; +import { useGetConfigChangesById } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; import { ConfigDetailChangeModal } from "../ConfigDetailsChanges/ConfigDetailsChanges"; import ConfigLink from "../ConfigLink/ConfigLink"; -import { DateCell } from "../ConfigViewer/columns"; import { PaginationOptions } from "../DataTable"; import { ChangeIcon } from "../Icon/ChangeIcon"; import { DataTable } from "../index"; +import { ConfigChange } from "../../api/types/configs"; +import { DateCell } from "../../ui/table"; -const columns: ColumnDef<ConfigTypeChanges>[] = [ +const columns: ColumnDef<ConfigChange>[] = [ { header: "Type", accessorKey: "change_type", @@ -87,7 +86,7 @@ export function ConfigChangeHistory({ useState<ConfigChange>(); const [modalIsOpen, setModalIsOpen] = useState(false); - const { data: configChange } = useGetConfigChangesByConfigChangeIdQuery( + const { data: configChange } = useGetConfigChangesById( selectedConfigChange?.id!, selectedConfigChange?.config_id!, { diff --git a/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx b/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx index ea5355a93..f4474c7d3 100644 --- a/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx +++ b/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx @@ -1,12 +1,12 @@ import { CellContext } from "@tanstack/react-table"; -import { ConfigTypeChanges } from ".."; -import { Age } from "../../UI/Age"; +import { Age } from "../../../ui/Age"; +import { ConfigChange } from "../../../api/types/configs"; export default function ConfigChangeAgeCell({ row, column, getValue -}: CellContext<ConfigTypeChanges, unknown>) { +}: CellContext<ConfigChange, unknown>) { return ( <Age className="whitespace-nowrap text-xs text-slate-500 pr-2" diff --git a/src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx b/src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx index b4420c1d7..deb205016 100644 --- a/src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx +++ b/src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx @@ -1,16 +1,16 @@ import { CellContext } from "@tanstack/react-table"; -import { ConfigTypeChanges } from ".."; import { ViewType } from "../../../types"; import { ConfigDetailsChanges } from "../../ConfigDetailsChanges/ConfigDetailsChanges"; +import { ConfigChange } from "../../../api/types/configs"; export default function ConfigChangeNameCell({ row, column, getValue -}: CellContext<ConfigTypeChanges, unknown>) { +}: CellContext<ConfigChange, unknown>) { const item = row.original; return ( - <div className="whitespace-nowrap py-1"> + <div className="whitespace-nowrap text-xs "> <ConfigDetailsChanges key={item.id} id={item.id} diff --git a/src/components/ConfigCosts/index.tsx b/src/components/ConfigCosts/index.tsx index 914de3187..63c816e4d 100644 --- a/src/components/ConfigCosts/index.tsx +++ b/src/components/ConfigCosts/index.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { useConfigAnalysisQuery } from "../../api/query-hooks"; -import { CostInfoPanel, CostsData } from "../CostDetails/CostDetails"; +import { CostInfoPanel } from "../CostDetails/CostDetails"; +import { CostsData } from "../../api/types/common"; type Props = { configID: string; diff --git a/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx b/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx index 5d68a6d76..a4e3dd670 100644 --- a/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx +++ b/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx @@ -1,30 +1,30 @@ import clsx from "clsx"; import { useEffect, useMemo, useState } from "react"; import ReactTooltip from "react-tooltip"; -import { useGetConfigChangesByConfigChangeIdQuery } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; -import { EvidenceType } from "../../api/services/evidence"; -import { User } from "../../api/services/users"; +import { useGetConfigChangesById } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; +import { ConfigChange } from "../../api/types/configs"; +import { User } from "../../api/types/users"; import { ViewType } from "../../types"; import { formatISODate } from "../../utils/date"; import AttachAsEvidenceButton from "../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; import { Avatar } from "../Avatar"; -import { ConfigTypeChanges } from "../ConfigChanges"; import ConfigLink from "../ConfigLink/ConfigLink"; import { DiffRenderer } from "../DiffRenderer/DiffRenderer"; import EmptyState from "../EmptyState"; import { ChangeIcon } from "../Icon/ChangeIcon"; +import { ConfigIcon } from "../Icon/ConfigIcon"; import { JSONViewer } from "../JSONViewer"; import { Modal } from "../Modal"; +import ModalTitleListItems from "../Modal/ModalTitleListItems"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; import ConfigChangeDetailSection from "./ConfigChangeDetailsSection"; -import ModalTitleListItems from "../Modal/ModalTitleListItems"; -import { ConfigIcon } from "../Icon/ConfigIcon"; +import { EvidenceType } from "../../api/types/evidence"; type ConfigDetailsChangesProps = { id: string; configId: string; viewType?: ViewType; - data?: ConfigTypeChanges; + data?: ConfigChange; showConfigLogo?: boolean; }; @@ -37,13 +37,9 @@ export function ConfigDetailsChanges({ }: ConfigDetailsChangesProps) { const [open, setOpen] = useState(false); - const { data: change, isLoading } = useGetConfigChangesByConfigChangeIdQuery( - id, - configId!, - { - enabled: open - } - ); + const { data: change, isLoading } = useGetConfigChangesById(id, configId!, { + enabled: open + }); const config = useMemo(() => change?.config, [change]); @@ -181,7 +177,7 @@ export function ConfigDetailsChanges({ type ConfigDetailChangeModalProps = { open: boolean; setOpen: (val: boolean) => void; - changeDetails?: ConfigTypeChanges; + changeDetails?: ConfigChange; }; export function ConfigDetailChangeModal({ diff --git a/src/components/ConfigInsights/index.tsx b/src/components/ConfigInsights/index.tsx index 23a72f455..182413496 100644 --- a/src/components/ConfigInsights/index.tsx +++ b/src/components/ConfigInsights/index.tsx @@ -1,31 +1,10 @@ import { MdOutlineInsights } from "react-icons/md"; import { useGetConfigInsights } from "../../api/query-hooks"; -import { ConfigItem } from "../../api/services/configs"; import CollapsiblePanel from "../CollapsiblePanel"; import InsightsDetails from "../Insights/Insights"; import Title from "../Title/title"; import PillBadge from "../Badge/PillBadge"; -export type ConfigTypeInsights = { - id: string; - config_id: string; - analyzer: string; - analysis_type: string; - severity: string; - summary: string; - status: string; - message: string; - sanitizedMessageHTML?: string; - sanitizedMessageTxt?: string; - analysis: string; - first_observed: string; - last_observed: string; - created_at: string | number | Date | null | undefined; - source: any; - created_by: any; - config?: ConfigItem; -}; - type Props = { configID: string; isCollapsed?: boolean; diff --git a/src/components/ConfigInsightsIcon/index.tsx b/src/components/ConfigInsightsIcon/index.tsx index 36f460aa6..c565d8e02 100644 --- a/src/components/ConfigInsightsIcon/index.tsx +++ b/src/components/ConfigInsightsIcon/index.tsx @@ -1,17 +1,16 @@ +import clsx from "clsx"; import { useMemo } from "react"; import { AiFillWarning } from "react-icons/ai"; import { BiDollarCircle } from "react-icons/bi"; -import { FaTasks } from "react-icons/fa"; +import { FaRegClock, FaTasks } from "react-icons/fa"; import { GrIntegration, GrWorkshop } from "react-icons/gr"; import { ImHeartBroken } from "react-icons/im"; import { IoMdSpeedometer } from "react-icons/io"; import { MdOutlineRecommend, MdSecurity } from "react-icons/md"; -import { FaRegClock } from "react-icons/fa"; -import { ConfigTypeInsights } from "../ConfigInsights"; -import clsx from "clsx"; +import { ConfigAnalysis } from "../../api/types/configs"; type Props = { - analysis: Pick<ConfigTypeInsights, "severity" | "analysis_type">; + analysis: Pick<ConfigAnalysis, "severity" | "analysis_type">; size?: number; }; diff --git a/src/components/ConfigLink/ConfigLink.tsx b/src/components/ConfigLink/ConfigLink.tsx index adc7629fd..a55425f6d 100644 --- a/src/components/ConfigLink/ConfigLink.tsx +++ b/src/components/ConfigLink/ConfigLink.tsx @@ -1,7 +1,7 @@ import clsx from "clsx"; import { HTMLAttributeAnchorTarget } from "react"; import { Link } from "react-router-dom"; -import { ConfigItem } from "../../api/services/configs"; +import { ConfigItem } from "../../api/types/configs"; import ConfigsTypeIcon from "../Configs/ConfigsTypeIcon"; import { ConfigIcon } from "../Icon/ConfigIcon"; diff --git a/src/components/ConfigList/Cells/ConfigListAnalysisCell.tsx b/src/components/ConfigList/Cells/ConfigListAnalysisCell.tsx index f2e5f587b..c9a761bc8 100644 --- a/src/components/ConfigList/Cells/ConfigListAnalysisCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListAnalysisCell.tsx @@ -1,7 +1,7 @@ import { CellContext } from "@tanstack/react-table"; -import { ConfigItem } from "../../../api/services/configs"; import Popover from "../../Popover/Popover"; import ConfigInsightsIcon from "../../ConfigInsightsIcon"; +import { ConfigItem } from "../../../api/types/configs"; export default function ConfigListAnalysisCell({ row, diff --git a/src/components/ConfigList/Cells/ConfigListChangeCell.tsx b/src/components/ConfigList/Cells/ConfigListChangeCell.tsx index de96dbc36..30038b908 100644 --- a/src/components/ConfigList/Cells/ConfigListChangeCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListChangeCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; import { useMemo } from "react"; -import { ConfigItem } from "../../../api/services/configs"; +import { ConfigItem } from "../../../api/types/configs"; export default function ConfigListChangeCell({ row, diff --git a/src/components/ConfigList/Cells/ConfigListCostCell.tsx b/src/components/ConfigList/Cells/ConfigListCostCell.tsx index 7332c4914..91a729baa 100644 --- a/src/components/ConfigList/Cells/ConfigListCostCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListCostCell.tsx @@ -1,5 +1,5 @@ import { CellContext } from "@tanstack/react-table"; -import { ConfigItem, ConfigSummary } from "../../../api/services/configs"; +import { ConfigItem, ConfigSummary } from "../../../api/types/configs"; import { FormatCurrency } from "../../CostDetails/CostDetails"; export default function ConfigListCostCell({ diff --git a/src/components/ConfigList/Cells/ConfigListDateCell.tsx b/src/components/ConfigList/Cells/ConfigListDateCell.tsx index cdd7ff402..d436d1b5e 100644 --- a/src/components/ConfigList/Cells/ConfigListDateCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListDateCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; import { FaTrash } from "react-icons/fa"; -import { Age } from "../../UI/Age"; +import { Age } from "../../../ui/Age"; export default function ConfigListDateCell<T extends Record<string, any>>({ getValue, diff --git a/src/components/ConfigList/Cells/ConfigListNameCell.tsx b/src/components/ConfigList/Cells/ConfigListNameCell.tsx index 83f9c3c54..19425edf2 100644 --- a/src/components/ConfigList/Cells/ConfigListNameCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListNameCell.tsx @@ -1,8 +1,8 @@ import { CellContext } from "@tanstack/react-table"; import React from "react"; -import { ConfigItem } from "../../../api/services/configs"; import { Badge } from "../../Badge"; import ConfigsTypeIcon from "../../Configs/ConfigsTypeIcon"; +import { ConfigItem } from "../../../api/types/configs"; function ConfigListNameCell({ row, getValue }: CellContext<ConfigItem, any>) { const isDeleted = !!row.original.deleted_at; diff --git a/src/components/ConfigList/Cells/ConfigListTagsCell.tsx b/src/components/ConfigList/Cells/ConfigListTagsCell.tsx index c511d278b..4e9de3b6e 100644 --- a/src/components/ConfigList/Cells/ConfigListTagsCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListTagsCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; import { useSearchParams } from "react-router-dom"; -import { ConfigItem } from "../../../api/services/configs"; +import { ConfigItem } from "../../../api/types/configs"; import Popover from "../../Popover/Popover"; import { TagItem, TagList } from "../../TagList/TagList"; diff --git a/src/components/ConfigList/Cells/ConfigListTypeCell.tsx b/src/components/ConfigList/Cells/ConfigListTypeCell.tsx index 24672e61a..39b79e9ab 100644 --- a/src/components/ConfigList/Cells/ConfigListTypeCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListTypeCell.tsx @@ -1,5 +1,5 @@ import { CellContext } from "@tanstack/react-table"; -import { ConfigItem, ConfigSummary } from "../../../api/services/configs"; +import { ConfigItem, ConfigSummary } from "../../../api/types/configs"; import { Icon } from "../../Icon"; import { useMemo } from "react"; diff --git a/src/components/ConfigList/ConfigListColumn.tsx b/src/components/ConfigList/ConfigListColumn.tsx index 2de8ab3e8..2f5be39dc 100644 --- a/src/components/ConfigList/ConfigListColumn.tsx +++ b/src/components/ConfigList/ConfigListColumn.tsx @@ -1,6 +1,6 @@ import { CellContext, ColumnDef, Row } from "@tanstack/react-table"; import React from "react"; -import { ConfigAnalysisTypeItem, ConfigItem } from "../../api/services/configs"; +import { ConfigAnalysisTypeItem } from "../../api/services/configs"; import { getTimeBucket, TIME_BUCKETS } from "../../utils/date"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; import { FormatCurrency } from "../CostDetails/CostDetails"; @@ -10,7 +10,7 @@ import ConfigListCostCell from "./Cells/ConfigListCostCell"; import ConfigListDateCell from "./Cells/ConfigListDateCell"; import ConfigListNameCell from "./Cells/ConfigListNameCell"; import ConfigListTagsCell from "./Cells/ConfigListTagsCell"; -import { ConfigTypeInsights } from "../ConfigInsights"; +import { ConfigAnalysis, ConfigItem } from "../../api/types/configs"; function CountBadge({ value }: { value: number | undefined | null }) { if (!value) { @@ -58,7 +58,7 @@ export const configListColumns: ColumnDef<ConfigItem, any>[] = [ const data = getValue(); return ( <div className="inline-flex space-x-2 overflow-hidden truncate"> - {data.map((item: { count: number; analysis: ConfigTypeInsights }) => { + {data.map((item: { count: number; analysis: ConfigAnalysis }) => { return ( <span className="inline-flex space-x-0.5" diff --git a/src/components/ConfigList/index.tsx b/src/components/ConfigList/index.tsx index ef0012738..b99dd7730 100644 --- a/src/components/ConfigList/index.tsx +++ b/src/components/ConfigList/index.tsx @@ -2,7 +2,7 @@ import { Row, SortingState, Updater } from "@tanstack/react-table"; import { useCallback, useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { DataTable } from "../"; -import { ConfigItem } from "../../api/services/configs"; +import { ConfigItem } from "../../api/types/configs"; import { configListColumns } from "./ConfigListColumn"; export interface Props { diff --git a/src/components/ConfigSidebar/ConfigDetails.tsx b/src/components/ConfigSidebar/ConfigDetails.tsx index d3f722ff7..2fc83dfd7 100644 --- a/src/components/ConfigSidebar/ConfigDetails.tsx +++ b/src/components/ConfigSidebar/ConfigDetails.tsx @@ -8,7 +8,7 @@ import { DescriptionCard } from "../DescriptionCard"; import { InfoMessage } from "../InfoMessage"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; import Title from "../Title/title"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; type Props = { configId: string; diff --git a/src/components/ConfigSummary/ConfigSummaryList.tsx b/src/components/ConfigSummary/ConfigSummaryList.tsx index 309e0a658..0e17226ad 100644 --- a/src/components/ConfigSummary/ConfigSummaryList.tsx +++ b/src/components/ConfigSummary/ConfigSummaryList.tsx @@ -1,5 +1,5 @@ import { CellContext, ColumnDef, Row } from "@tanstack/react-table"; -import { ConfigSummary } from "../../api/services/configs"; +import { ConfigSummary } from "../../api/types/configs"; import { DataTable } from "../DataTable"; import ConfigInsightsIcon from "../ConfigInsightsIcon"; import { CountBadge } from "../Badge/CountBadge"; diff --git a/src/components/ConfigViewer/columns.tsx b/src/components/ConfigViewer/columns.tsx index 6a96c2413..1f9275391 100644 --- a/src/components/ConfigViewer/columns.tsx +++ b/src/components/ConfigViewer/columns.tsx @@ -1,5 +1,6 @@ -import { CellContext, ColumnDef } from "@tanstack/table-core"; -import { Age } from "../UI/Age"; +import { ColumnDef } from "@tanstack/react-table"; +import { DateCell } from "../../ui/table/DateCells"; +import { TagsCell } from "../../ui/table/TagCell"; export const defaultTableColumns: ColumnDef<any>[] = [ { @@ -41,33 +42,3 @@ export const defaultTableColumns: ColumnDef<any>[] = [ } } ]; - -export function TagsCell({ row, column }: CellContext<any, any>): JSX.Element { - const tags = row?.getValue<any[]>(column.id); - - return ( - <div className="flex"> - {tags?.length > 0 ? ( - tags?.map((tag) => ( - <div - className="bg-gray-200 px-1 py-0.5 mr-1 rounded-md text-gray-600 font-semibold text-xs" - key={tag} - > - {tag} - </div> - )) - ) : ( - <span className="text-gray-400"></span> - )} - </div> - ); -} - -export function DateCell({ row, column }: CellContext<any, any>) { - const dateString = row?.getValue<string>(column.id); - return ( - <div className="text-xs"> - <Age from={dateString} /> - </div> - ); -} diff --git a/src/components/Configs/ConfigsTypeIcon.tsx b/src/components/Configs/ConfigsTypeIcon.tsx index 87f84b245..6e976c6fc 100644 --- a/src/components/Configs/ConfigsTypeIcon.tsx +++ b/src/components/Configs/ConfigsTypeIcon.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { ConfigItem } from "../../api/services/configs"; +import { ConfigItem } from "../../api/types/configs"; import { Icon } from "../Icon"; type ConfigIconProps = { diff --git a/src/components/Configs/Sidebar/ConfigActionBar.tsx b/src/components/Configs/Sidebar/ConfigActionBar.tsx index b666b62fd..84320bf20 100644 --- a/src/components/Configs/Sidebar/ConfigActionBar.tsx +++ b/src/components/Configs/Sidebar/ConfigActionBar.tsx @@ -1,12 +1,12 @@ import clsx from "clsx"; import React, { useEffect, useMemo, useState } from "react"; import { MdAlarmAdd } from "react-icons/md"; -import { useGetConfigByIdQuery } from "../../../api/query-hooks"; -import { EvidenceType } from "../../../api/services/evidence"; -import { usePartialUpdateSearchParams } from "../../../hooks/usePartialUpdateSearchParams"; -import { ActionLink } from "../../ActionLink/ActionLink"; -import AttachAsEvidenceButton from "../../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; -import SelectPlaybookToRun from "../../Playbooks/Runs/Submit/SelectPlaybookToRun"; +import { useGetConfigByIdQuery } from "../../api/query-hooks"; +import { usePartialUpdateSearchParams } from "../../hooks/usePartialUpdateSearchParams"; +import { ActionLink } from "../ActionLink/ActionLink"; +import AttachAsEvidenceButton from "../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; +import SelectPlaybookToRun from "../Playbooks/Runs/Submit/SelectPlaybookToRun"; +import { EvidenceType } from "../../api/types/evidence"; type ConfigActionBarProps = { configId: string; @@ -40,7 +40,7 @@ export default function ConfigActionBar({ const ordered = Object.keys(configDetails.config) .sort() .reduce((obj: Record<string, any>, key) => { - obj[key] = configDetails.config[key]; + obj[key] = configDetails.config ? [key] : null; return obj; }, {}); diff --git a/src/components/Connections/ConnectionsList.tsx b/src/components/Connections/ConnectionsList.tsx index cebe486a0..92d2fd5a6 100644 --- a/src/components/Connections/ConnectionsList.tsx +++ b/src/components/Connections/ConnectionsList.tsx @@ -3,8 +3,8 @@ import clsx from "clsx"; import { DataTable } from "../DataTable"; import { Avatar } from "../Avatar"; import { Connection } from "./ConnectionForm"; -import { DateCell } from "../ConfigViewer/columns"; import { Icon } from "../Icon"; +import { DateCell } from "../../ui/table"; type ConnectionListProps = { data: Connection[]; diff --git a/src/components/CostDetails/CostDetails.tsx b/src/components/CostDetails/CostDetails.tsx index e66f07afc..93d63b8f4 100644 --- a/src/components/CostDetails/CostDetails.tsx +++ b/src/components/CostDetails/CostDetails.tsx @@ -3,13 +3,7 @@ import { FaDollarSign } from "react-icons/fa"; import Title from "../Title/title"; import CollapsiblePanel from "../CollapsiblePanel"; import { Loading } from "../Loading"; - -export type CostsData = { - cost_per_minute?: number; - cost_total_1d?: number; - cost_total_7d?: number; - cost_total_30d?: number; -}; +import { CostsData } from "../../api/types/common"; type CostDetailsTableProps = CostsData; diff --git a/src/components/Dropdown/DropdownWithActions.stories.tsx b/src/components/Dropdown/DropdownWithActions.stories.tsx index 67af82db8..a12ca36f5 100644 --- a/src/components/Dropdown/DropdownWithActions.stories.tsx +++ b/src/components/Dropdown/DropdownWithActions.stories.tsx @@ -1,9 +1,9 @@ import { ComponentMeta, ComponentStory } from "@storybook/react"; import { Controller, useForm } from "react-hook-form"; -import { IncidentSeverity } from "../../api/services/incident"; import { severityItems } from "../Incidents/data"; import { TextInput } from "../TextInput"; import { DropdownWithActions } from "./DropdownWithActions"; +import { IncidentSeverity } from "../../api/types/incident"; export default { title: "Dropdown/DropdownWithActions", diff --git a/src/components/Dropdown/GroupByDropdown.tsx b/src/components/Dropdown/GroupByDropdown.tsx index 7ef4cfa1d..1ae69a2fe 100644 --- a/src/components/Dropdown/GroupByDropdown.tsx +++ b/src/components/Dropdown/GroupByDropdown.tsx @@ -4,7 +4,7 @@ import { AiOutlineAlignLeft } from "react-icons/ai"; import { getLabelSelections } from "./lib/lists"; import { ReactSelectDropdown } from "../ReactSelectDropdown"; import { ComponentProps } from "react"; -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../../api/types/health"; const defaultGroupSelections = { "no-group": { diff --git a/src/components/EventQueueStatus/EventQueueStatusList.tsx b/src/components/EventQueueStatus/EventQueueStatusList.tsx index 60cd63750..412560325 100644 --- a/src/components/EventQueueStatus/EventQueueStatusList.tsx +++ b/src/components/EventQueueStatus/EventQueueStatusList.tsx @@ -1,9 +1,9 @@ import { ColumnDef } from "@tanstack/react-table"; import { useState } from "react"; -import { DateCell } from "../ConfigViewer/columns"; import { DataTable } from "../DataTable"; import { Modal } from "../Modal"; import { EventQueueStatus } from "./eventQueue"; +import { DateCell } from "../../ui/table"; const columns: ColumnDef<EventQueueStatus>[] = [ { diff --git a/src/components/EvidenceBuilder/components/EvidenceSearch/index.js b/src/components/EvidenceBuilder/components/EvidenceSearch/index.js deleted file mode 100644 index 41e0a1cc4..000000000 --- a/src/components/EvidenceBuilder/components/EvidenceSearch/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export function EvidenceSearch({ ...rest }) { - return <div {...rest}>EvidenceSearch</div>; -} diff --git a/src/components/EvidenceBuilder/components/EvidenceSelect/index.js b/src/components/EvidenceBuilder/components/EvidenceSelect/index.js deleted file mode 100644 index f5a767217..000000000 --- a/src/components/EvidenceBuilder/components/EvidenceSelect/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export function EvidenceSelect({ ...rest }) { - return <div {...rest}>EvidenceSelect</div>; -} diff --git a/src/components/EvidenceBuilder/components/EvidenceType/index.js b/src/components/EvidenceBuilder/components/EvidenceType/index.js deleted file mode 100644 index f6772b988..000000000 --- a/src/components/EvidenceBuilder/components/EvidenceType/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export function EvidenceType({ ...rest }) { - return <div {...rest}>EvidenceType</div>; -} diff --git a/src/components/EvidenceBuilder/index.tsx b/src/components/EvidenceBuilder/index.tsx index 62fed33a3..824d326f0 100644 --- a/src/components/EvidenceBuilder/index.tsx +++ b/src/components/EvidenceBuilder/index.tsx @@ -1,24 +1,21 @@ import React, { useState } from "react"; -import { EvidenceSearch } from "./components/EvidenceSearch"; -import { EvidenceSelect } from "./components/EvidenceSelect"; import { EvidenceStepper } from "./components/EvidenceStepper"; -import { EvidenceType } from "./components/EvidenceType"; export const evidenceSteps = [ { title: "Evidence type", description: "Lorem ipsum dolor sit amet", - component: <EvidenceType /> + component: <div>EvidenceType</div> }, { title: "Search", description: "Lorem ipsum dolor sit amet", - component: <EvidenceSearch /> + component: <div>EvidenceSearch</div> }, { title: "Select", description: "Lorem ipsum dolor sit amet", - component: <EvidenceSelect /> + component: <div>EvidenceSelect</div> } ]; diff --git a/src/components/EvidenceLogList/EvidenceLogList.js b/src/components/EvidenceLogList/EvidenceLogList.js index 08dfa408f..3ccb7eef5 100644 --- a/src/components/EvidenceLogList/EvidenceLogList.js +++ b/src/components/EvidenceLogList/EvidenceLogList.js @@ -1,4 +1,4 @@ -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; export const EvidenceLogList = ({ evidence }) => ( <div className="flex flex-row gap-x-10 py-1.5 border-b" key={evidence.id}> diff --git a/src/components/FeatureFlags/FeatureFlagList.tsx b/src/components/FeatureFlags/FeatureFlagList.tsx index 13caf34b1..6f05e0a7b 100644 --- a/src/components/FeatureFlags/FeatureFlagList.tsx +++ b/src/components/FeatureFlags/FeatureFlagList.tsx @@ -1,11 +1,10 @@ import { CellContext, ColumnDef } from "@tanstack/table-core"; import clsx from "clsx"; -import { User } from "../../api/services/users"; -import { relativeDateTime } from "../../utils/date"; -import { DataTable } from "../DataTable"; -import { Avatar } from "../Avatar"; +import { User } from "../../api/types/users"; import { Property } from "../../services/permissions/permissionsService"; -import { Age } from "../UI/Age"; +import { Avatar } from "../Avatar"; +import { DataTable } from "../DataTable"; +import { Age } from "../../ui/Age"; type FeatureFlagsListProps = { data: any[]; diff --git a/src/components/HealthChecks/CheckLink.tsx b/src/components/HealthChecks/CheckLink.tsx index 90b86de5e..b2b4f86f7 100644 --- a/src/components/HealthChecks/CheckLink.tsx +++ b/src/components/HealthChecks/CheckLink.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; import { ComponentHealthCheckView } from "../../api/services/topology"; -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../../api/types/health"; import { Icon } from "../Icon"; import { HealthCheckStatus } from "../Status/HealthCheckStatus"; diff --git a/src/components/Hypothesis/Comments/index.tsx b/src/components/Hypothesis/Comments/index.tsx index 4c47b26d8..c53a06274 100644 --- a/src/components/Hypothesis/Comments/index.tsx +++ b/src/components/Hypothesis/Comments/index.tsx @@ -3,7 +3,7 @@ import React, { useState } from "react"; import { IoMdSend } from "react-icons/io"; import { CommentInput } from "../../Comment"; -import { Comment } from "../../../api/services/comments"; +import { Comment } from "../../../api/types/incident"; import { ResponseLine } from "../ResponseLine"; interface Props { diff --git a/src/components/Hypothesis/CreateHypothesis/index.tsx b/src/components/Hypothesis/CreateHypothesis/index.tsx index a945f1422..a9e087e5b 100644 --- a/src/components/Hypothesis/CreateHypothesis/index.tsx +++ b/src/components/Hypothesis/CreateHypothesis/index.tsx @@ -1,10 +1,9 @@ import { Controller, useForm } from "react-hook-form"; import { v4 as uuidv4 } from "uuid"; - import { - getHypothesisChildType, - HypothesisStatus -} from "../../../api/services/hypothesis"; + HypothesisStatus, + getHypothesisChildType +} from "../../../api/types/hypothesis"; import { hypothesisStatusDropdownOptions } from "../../../constants/hypothesisStatusOptions"; import { useUser } from "../../../context"; import { capitalizeFirstLetter } from "../../../utils/common"; diff --git a/src/components/Hypothesis/EvidenceSection/EvidenceSection.stories.tsx b/src/components/Hypothesis/EvidenceSection/EvidenceSection.stories.tsx index a8b796542..5e31dd252 100644 --- a/src/components/Hypothesis/EvidenceSection/EvidenceSection.stories.tsx +++ b/src/components/Hypothesis/EvidenceSection/EvidenceSection.stories.tsx @@ -3,7 +3,7 @@ import { MemoryRouter } from "react-router-dom"; import { sampleIncidentNode } from "../../../data/sampleIncident"; import { EvidenceSection } from "./index"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { EvidenceType } from "../../../api/services/evidence"; +import { EvidenceType } from "../../../api/types/evidence"; const client = new QueryClient(); @@ -37,8 +37,7 @@ Base.args = { hypothesis_id: "f87c055a-7529-47ee-b363-7059c258e135", created_by: { id: "01814f6d-edba-8467-55ca-78974a2004f6", - name: "Galileo Galilei", - email: "" + name: "Galileo Galilei" }, type: EvidenceType.Log, evidence: { diff --git a/src/components/Hypothesis/EvidenceSection/index.tsx b/src/components/Hypothesis/EvidenceSection/index.tsx index 63c9a750e..620a62d83 100644 --- a/src/components/Hypothesis/EvidenceSection/index.tsx +++ b/src/components/Hypothesis/EvidenceSection/index.tsx @@ -5,8 +5,6 @@ import { useGetConfigByIdQuery, useGetConfigInsight } from "../../../api/query-hooks"; -import { Evidence, EvidenceType } from "../../../api/services/evidence"; -import { Hypothesis } from "../../../api/services/hypothesis"; import { getCanaries } from "../../../api/services/topology"; import { Size, ViewType } from "../../../types"; import { sanitizeHTMLContent, toFixedIfNecessary } from "../../../utils/common"; @@ -18,14 +16,16 @@ import { getUptimePercentage } from "../../Canary/CanaryPopup/utils"; import { Duration, StatusList } from "../../Canary/renderers"; import { ConfigAnalysisLink } from "../../ConfigAnalysisLink/ConfigAnalysisLink"; import { ConfigDetailsChanges } from "../../ConfigDetailsChanges/ConfigDetailsChanges"; -import { ConfigTypeInsights } from "../../ConfigInsights"; import ConfigLink from "../../ConfigLink/ConfigLink"; import { Icon } from "../../Icon"; import { CommentEvidence } from "../../IncidentDetails/DefinitionOfDone/EvidenceView"; import { LogsTable } from "../../Logs/Table/LogsTable"; import { Modal } from "../../Modal"; import { TopologyCard } from "../../TopologyCard"; -import { Age } from "../../UI/Age"; +import { Age } from "../../../ui/Age"; +import { ConfigAnalysis } from "../../../api/types/configs"; +import { Evidence, EvidenceType } from "../../../api/types/evidence"; +import { Hypothesis } from "../../../api/types/hypothesis"; const ColumnSizes = { Time: { @@ -62,7 +62,7 @@ export function EvidenceItem({ case EvidenceType.Config: return ( <EvidenceAccordion - date={evidence.created_at} + date={evidence.created_at || ""} title={evidence.description!} configId={evidence.config_id!} configName={evidence.evidence?.configName} @@ -100,7 +100,7 @@ export function EvidenceItem({ } const EvidenceAccordion: React.FC<{ - date: string; + date: string | Date; title: string; configId: string; configName: string; @@ -476,11 +476,11 @@ export function ConfigAnalysisEvidence({ className?: string; viewType?: ViewType; }) { - const { data: response } = useGetConfigInsight<ConfigTypeInsights[]>( + const { data: response } = useGetConfigInsight<ConfigAnalysis[]>( evidence.config_id!, evidence.config_analysis_id! ); - const [configAnalysis, setConfigAnalysis] = useState<ConfigTypeInsights>(); + const [configAnalysis, setConfigAnalysis] = useState<ConfigAnalysis>(); useEffect(() => { const analysis = response?.[0]; diff --git a/src/components/Hypothesis/HypothesisBar/HypothesisBar.stories.tsx b/src/components/Hypothesis/HypothesisBar/HypothesisBar.stories.tsx index e395e2486..0212bca31 100644 --- a/src/components/Hypothesis/HypothesisBar/HypothesisBar.stories.tsx +++ b/src/components/Hypothesis/HypothesisBar/HypothesisBar.stories.tsx @@ -1,6 +1,6 @@ import { ComponentMeta, ComponentStory } from "@storybook/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { HypothesisStatus } from "../../../api/services/hypothesis"; +import { HypothesisStatus } from "../../../api/types/hypothesis"; import { HypothesisBar } from "./index"; const defaultQueryClient = new QueryClient(); diff --git a/src/components/Hypothesis/HypothesisBar/index.tsx b/src/components/Hypothesis/HypothesisBar/index.tsx index 8f6bbd09e..bcfe3457f 100644 --- a/src/components/Hypothesis/HypothesisBar/index.tsx +++ b/src/components/Hypothesis/HypothesisBar/index.tsx @@ -11,14 +11,14 @@ import { } from "react-icons/bs"; import { IconBaseProps, IconType } from "react-icons/lib"; import { VscTypeHierarchy } from "react-icons/vsc"; -import { EvidenceType } from "../../../api/services/evidence"; -import { Hypothesis } from "../../../api/services/hypothesis"; import { HypothesisAPIs } from "../../../pages/incident/IncidentDetails"; import { AvatarGroup } from "../../AvatarGroup"; import { EditableText } from "../../EditableText"; import { HypothesisBarMenu } from "../HypothesisBarMenu"; import { StatusDropdownContainer } from "../StatusDropdownContainer"; import { recentlyAddedHypothesisIdAtom } from "../../../store/hypothesis.state"; +import { EvidenceType } from "../../../api/types/evidence"; +import { Hypothesis } from "../../../api/types/hypothesis"; enum CommentInfo { Comment = "comment" diff --git a/src/components/Hypothesis/HypothesisBarMenu/HypothesisBarMenu.stories.tsx b/src/components/Hypothesis/HypothesisBarMenu/HypothesisBarMenu.stories.tsx index ce88ef6e0..98ea693d4 100644 --- a/src/components/Hypothesis/HypothesisBarMenu/HypothesisBarMenu.stories.tsx +++ b/src/components/Hypothesis/HypothesisBarMenu/HypothesisBarMenu.stories.tsx @@ -1,6 +1,6 @@ import { ComponentMeta, ComponentStory } from "@storybook/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { HypothesisStatus } from "../../../api/services/hypothesis"; +import { HypothesisStatus } from "../../../api/types/hypothesis"; import { HypothesisBarMenu } from "./index"; const defaultQueryClient = new QueryClient(); diff --git a/src/components/Hypothesis/HypothesisBarMenu/index.tsx b/src/components/Hypothesis/HypothesisBarMenu/index.tsx index 43a56d244..62ad9f987 100644 --- a/src/components/Hypothesis/HypothesisBarMenu/index.tsx +++ b/src/components/Hypothesis/HypothesisBarMenu/index.tsx @@ -1,11 +1,12 @@ +import { useQueryClient } from "@tanstack/react-query"; import { useCallback, useState } from "react"; import { BiHide } from "react-icons/bi"; import { BsTrash } from "react-icons/bs"; -import { useQueryClient } from "@tanstack/react-query"; -import { deleteHypothesis, Hypothesis } from "../../../api/services/hypothesis"; +import { createIncidentQueryKey } from "../../../api/query-hooks"; +import { deleteHypothesis } from "../../../api/services/hypothesis"; +import { Hypothesis } from "../../../api/types/hypothesis"; import { IconButton } from "../../IconButton"; import { Menu } from "../../Menu"; -import { createIncidentQueryKey } from "../../../api/query-hooks"; import { HypothesisDeleteDialog } from "../HypothesisDeleteDialog"; interface IProps { diff --git a/src/components/Hypothesis/HypothesisBuilder/index.tsx b/src/components/Hypothesis/HypothesisBuilder/index.tsx index 3e10c9066..de97cd10c 100644 --- a/src/components/Hypothesis/HypothesisBuilder/index.tsx +++ b/src/components/Hypothesis/HypothesisBuilder/index.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from "react"; import { HypothesisNode } from "../HypothesisNode"; import { CreateHypothesis } from "../CreateHypothesis"; import { HypothesisAPIs } from "../../../pages/incident/IncidentDetails"; -import { Hypothesis } from "../../../api/services/hypothesis"; +import { Hypothesis } from "../../../api/types/hypothesis"; import { useSearchParams } from "react-router-dom"; interface IProps { diff --git a/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx b/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx index 43c4f6d2f..07a87ce3e 100644 --- a/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx +++ b/src/components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer.tsx @@ -2,9 +2,7 @@ import clsx from "clsx"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { IoMdSend } from "react-icons/io"; import { GroupHeadingProps, OptionProps, components } from "react-select"; -import { Comment } from "../../../api/services/comments"; -import { Evidence } from "../../../api/services/evidence"; -import { Hypothesis } from "../../../api/services/hypothesis"; +import { Comment } from "../../../api/types/incident"; import { SortOrders } from "../../../constants"; import { useUser } from "../../../context"; import useRunTaskOnPropChange from "../../../hooks/useRunTaskOnPropChange"; @@ -13,6 +11,7 @@ import { TreeNode } from "../../../pages/incident/IncidentDetails"; import { useIncidentState } from "../../../store/incident.state"; +import { Age } from "../../../ui/Age"; import { dateSortHelper } from "../../../utils/date"; import { Avatar } from "../../Avatar"; import { CommentInput, CommentText } from "../../Comment"; @@ -20,9 +19,12 @@ import { Icon } from "../../Icon"; import { OptionItem, SearchSelect } from "../../SearchSelect"; import { Tag } from "../../Tag/Tag"; import { toastError } from "../../Toast/toast"; -import { Age } from "../../UI/Age"; import { EvidenceItem } from "../EvidenceSection"; -import { CreatedBy } from "../ResponseLine"; + +import { Evidence } from "../../../api/types/evidence"; +import { Hypothesis } from "../../../api/types/hypothesis"; +import { UserWithTeam } from "../../../api/types/users"; +import { DateType } from "../../../api/types/common"; interface IProps { incidentId: string; @@ -201,7 +203,7 @@ export function HypothesisCommentsViewContainer({ } key={index} hypothesis={hypothesis} - created_by={data.created_by as CreatedBy} + created_by={data.created_by as UserWithTeam} created_at={data.created_at} data={data} lastComment={index === comments.length - 1} @@ -285,9 +287,9 @@ export function HypothesisCommentViewEntry({ className }: { type: CommentViewEntryTypes; - created_by: CreatedBy; + created_by: UserWithTeam; hypothesis: string; - created_at: string; + created_at: DateType; data: Comment & Evidence; lastComment: boolean; className?: string; diff --git a/src/components/Hypothesis/HypothesisDetails/index.tsx b/src/components/Hypothesis/HypothesisDetails/index.tsx index 626d9ea3c..0a20363a7 100644 --- a/src/components/Hypothesis/HypothesisDetails/index.tsx +++ b/src/components/Hypothesis/HypothesisDetails/index.tsx @@ -1,21 +1,18 @@ -import React, { useEffect, useState } from "react"; import clsx from "clsx"; - -import { Modal } from "../../Modal"; -import { Comment } from "../../../api/services/comments"; -import { - deleteEvidence, - Evidence, - updateEvidence -} from "../../../api/services/evidence"; +import React, { useEffect, useState } from "react"; +import { deleteEvidence, updateEvidence } from "../../../api/services/evidence"; +import { sortByCreatedAt } from "../../../api/types/common"; +import { Evidence } from "../../../api/types/evidence"; +import { Hypothesis } from "../../../api/types/hypothesis"; +import { Comment } from "../../../api/types/incident"; import { useUser } from "../../../context"; -import { toastError } from "../../Toast/toast"; +import { TreeNode } from "../../../pages/incident/IncidentDetails"; +import { useIncidentState } from "../../../store/incident.state"; import { EvidenceBuilder } from "../../EvidenceBuilder"; +import { Modal } from "../../Modal"; +import { toastError } from "../../Toast/toast"; import { CommentsSection } from "../Comments"; import { ResponseLine } from "../ResponseLine"; -import { Hypothesis } from "../../../api/services/hypothesis"; -import { TreeNode } from "../../../pages/incident/IncidentDetails"; -import { useIncidentState } from "../../../store/incident.state"; type IProps = { node: TreeNode<Hypothesis>; @@ -37,10 +34,7 @@ export function HypothesisDetails({ node, api, ...rest }: IProps) { const arrangeData = (data: any) => { let responses = (data?.comments || []) .concat(data?.evidences || []) - .sort((a: Response, b: Response) => { - if (a.created_at > b.created_at) return 1; - return -1; - }); + .sort(sortByCreatedAt); responses = responses.map((response: any) => { response.created_by = response.external_created_by diff --git a/src/components/Hypothesis/HypothesisNode/index.tsx b/src/components/Hypothesis/HypothesisNode/index.tsx index c845b95ca..15a5827a8 100644 --- a/src/components/Hypothesis/HypothesisNode/index.tsx +++ b/src/components/Hypothesis/HypothesisNode/index.tsx @@ -1,17 +1,16 @@ import clsx from "clsx"; -import { useEffect, useState } from "react"; import { useAtomValue, useSetAtom } from "jotai"; - +import { useEffect, useState } from "react"; import { AiOutlinePlusCircle } from "react-icons/ai"; import { - getHypothesisChildType, Hypothesis, - HypothesisStatus -} from "../../../api/services/hypothesis"; + HypothesisStatus, + getHypothesisChildType +} from "../../../api/types/hypothesis"; import { HypothesisAPIs } from "../../../pages/incident/IncidentDetails"; +import { recentlyAddedHypothesisIdAtom } from "../../../store/hypothesis.state"; import { HypothesisBar } from "../HypothesisBar"; import { HypothesisDetails } from "../HypothesisDetails"; -import { recentlyAddedHypothesisIdAtom } from "../../../store/hypothesis.state"; interface IHypothesisNodeProps { hasParent?: boolean; diff --git a/src/components/Hypothesis/ResponseLine/index.tsx b/src/components/Hypothesis/ResponseLine/index.tsx index 27b85efbc..d50f6bdfa 100644 --- a/src/components/Hypothesis/ResponseLine/index.tsx +++ b/src/components/Hypothesis/ResponseLine/index.tsx @@ -1,29 +1,22 @@ -import { BsTrash } from "react-icons/bs"; - import { useCallback, useMemo } from "react"; import { BiCheck } from "react-icons/bi"; +import { BsTrash } from "react-icons/bs"; import { IoMdRemoveCircle } from "react-icons/io"; -import { Comment } from "../../../api/services/comments"; -import { Evidence } from "../../../api/services/evidence"; -import { User } from "../../../api/services/users"; +import { Evidence } from "../../../api/types/evidence"; +import { Comment } from "../../../api/types/incident"; +import { UserWithTeam } from "../../../api/types/users"; +import { Age } from "../../../ui/Age"; import { Avatar } from "../../Avatar"; import { CommentText } from "../../Comment"; import { Icon } from "../../Icon"; import { IconButton } from "../../IconButton"; import { Menu } from "../../Menu"; -import { Age } from "../../UI/Age"; import { EvidenceItem } from "../EvidenceSection"; - -export type CreatedBy = User & { - team: { - icon: string; - name: string; - }; -}; +import { DateType } from "../../../api/types/common"; interface IProps { - created_by: CreatedBy; - created_at: string; + created_by: UserWithTeam; + created_at?: DateType; response: Comment & Evidence; onDelete?: () => void; markAsDefinitionOfDone?: () => void; diff --git a/src/components/Hypothesis/StatusDropdownContainer/index.tsx b/src/components/Hypothesis/StatusDropdownContainer/index.tsx index 6db505f76..34cc524eb 100644 --- a/src/components/Hypothesis/StatusDropdownContainer/index.tsx +++ b/src/components/Hypothesis/StatusDropdownContainer/index.tsx @@ -1,6 +1,6 @@ import { UseMutationResult } from "@tanstack/react-query"; import { useEffect } from "react"; -import { Hypothesis, HypothesisStatus } from "../../../api/services/hypothesis"; +import { Hypothesis, HypothesisStatus } from "../../../api/types/hypothesis"; import { hypothesisStatusDropdownOptions } from "../../../constants/hypothesisStatusOptions"; import useHypothesisStatusForm from "../../../hooks/useHypothesisStatusForm"; import { SubtleDropdown } from "../../Dropdown/SubtleDropdown"; diff --git a/src/components/Icon/ChangeIcon.tsx b/src/components/Icon/ChangeIcon.tsx index 77d70be73..23f86fbf3 100644 --- a/src/components/Icon/ChangeIcon.tsx +++ b/src/components/Icon/ChangeIcon.tsx @@ -1,7 +1,6 @@ import React, { memo } from "react"; - import { Icon, IconProps } from "."; -import { ConfigChange } from "../../api/services/configs"; +import { ConfigChange } from "../../api/types/configs"; interface ChangeIconProps extends IconProps { change?: ConfigChange; diff --git a/src/components/Icon/ConfigIcon.tsx b/src/components/Icon/ConfigIcon.tsx index 0cbfeaafe..9cff66162 100644 --- a/src/components/Icon/ConfigIcon.tsx +++ b/src/components/Icon/ConfigIcon.tsx @@ -1,6 +1,6 @@ import React, { memo } from "react"; import { Icon, IconProps } from "."; -import { ConfigItem } from "../../api/services/configs"; +import { ConfigItem } from "../../api/types/configs"; interface ConfigIconProps extends IconProps { config?: ConfigItem; diff --git a/src/components/IncidentCard/IncidentCard.tsx b/src/components/IncidentCard/IncidentCard.tsx index b483915d2..c838e0c67 100644 --- a/src/components/IncidentCard/IncidentCard.tsx +++ b/src/components/IncidentCard/IncidentCard.tsx @@ -1,10 +1,10 @@ import clsx from "clsx"; import { Link } from "react-router-dom"; -import { Incident } from "../../api/services/incident"; import { IncidentStatusTag } from "../IncidentStatusTag"; import { typeItems } from "../Incidents/data"; import { IncidentTypeIcon } from "../incidentTypeTag"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; +import { Incident } from "../../api/types/incident"; type IncidentCardProps = { incident: Incident; diff --git a/src/components/IncidentCardSummary/index.tsx b/src/components/IncidentCardSummary/index.tsx index c6d173bb6..18f45955a 100644 --- a/src/components/IncidentCardSummary/index.tsx +++ b/src/components/IncidentCardSummary/index.tsx @@ -1,8 +1,9 @@ import { useMemo } from "react"; -import { IncidentSeverity } from "../../api/services/incident"; -import { Topology } from "../../context/TopologyPageContext"; +import { IncidentSeverity } from "../../api/types/incident"; + import { typeItems, severityItems } from "../Incidents/data"; import { StatusLine, StatusLineProps } from "../StatusLine/StatusLine"; +import { Topology } from "../../api/types/topology"; const chipColorFromSeverity = ( severity: IncidentSeverity diff --git a/src/components/IncidentDetails/AddDefinitionOfDone/AddAutoDefinitionOfDone.tsx b/src/components/IncidentDetails/AddDefinitionOfDone/AddAutoDefinitionOfDone.tsx index 37b5051e6..3823d1090 100644 --- a/src/components/IncidentDetails/AddDefinitionOfDone/AddAutoDefinitionOfDone.tsx +++ b/src/components/IncidentDetails/AddDefinitionOfDone/AddAutoDefinitionOfDone.tsx @@ -1,9 +1,9 @@ import { useMemo, useReducer } from "react"; import { useUpdateEvidenceMutation } from "../../../api/query-hooks/mutations/evidence"; -import { Evidence, EvidenceType } from "../../../api/services/evidence"; -import { ScriptStep } from "./steps/ScriptStep"; -import EvidenceSelectorStep from "./steps/EvidenceSelectorStep"; +import { Evidence, EvidenceType } from "../../../api/types/evidence"; import { Events, sendAnalyticEvent } from "../../../services/analytics"; +import EvidenceSelectorStep from "./steps/EvidenceSelectorStep"; +import { ScriptStep } from "./steps/ScriptStep"; export type DefinitionOfDoneType = `${EvidenceType}`; diff --git a/src/components/IncidentDetails/AddDefinitionOfDone/AddDefinitionOfDoneHome.tsx b/src/components/IncidentDetails/AddDefinitionOfDone/AddDefinitionOfDoneHome.tsx index 89699873d..dd1684518 100644 --- a/src/components/IncidentDetails/AddDefinitionOfDone/AddDefinitionOfDoneHome.tsx +++ b/src/components/IncidentDetails/AddDefinitionOfDone/AddDefinitionOfDoneHome.tsx @@ -1,11 +1,11 @@ import { useState } from "react"; import { BsPersonFill } from "react-icons/bs"; import { SiAutomattic } from "react-icons/si"; -import { Evidence } from "../../../api/services/evidence"; +import { Evidence } from "../../../api/types/evidence"; +import { Hypothesis } from "../../../api/types/hypothesis"; import { Modal } from "../../Modal"; -import AddManualDefinitionOfDone from "./AddManualDefinitionOfDone"; import AddAutoDefinitionOfDoneStepper from "./AddAutoDefinitionOfDone"; -import { Hypothesis } from "../../../api/services/hypothesis"; +import AddManualDefinitionOfDone from "./AddManualDefinitionOfDone"; type DefinitionOfDoneOptions = "Auto" | "Manual"; diff --git a/src/components/IncidentDetails/AddDefinitionOfDone/AddManualDefinitionOfDone.tsx b/src/components/IncidentDetails/AddDefinitionOfDone/AddManualDefinitionOfDone.tsx index 5e57e7ffd..f15fd8ca0 100644 --- a/src/components/IncidentDetails/AddDefinitionOfDone/AddManualDefinitionOfDone.tsx +++ b/src/components/IncidentDetails/AddDefinitionOfDone/AddManualDefinitionOfDone.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Evidence } from "../../../api/services/evidence"; +import { Evidence } from "../../../api/types/evidence"; import { TextInput } from "../../TextInput"; import useAddCommentAsDoD from "./useAddCommentAsDoD"; diff --git a/src/components/IncidentDetails/AddDefinitionOfDone/steps/EvidenceSelectorStep.tsx b/src/components/IncidentDetails/AddDefinitionOfDone/steps/EvidenceSelectorStep.tsx index 4a9bbd649..320a1f083 100644 --- a/src/components/IncidentDetails/AddDefinitionOfDone/steps/EvidenceSelectorStep.tsx +++ b/src/components/IncidentDetails/AddDefinitionOfDone/steps/EvidenceSelectorStep.tsx @@ -1,11 +1,11 @@ +import { ChangeEvent, useCallback, useMemo } from "react"; +import { Evidence, EvidenceType } from "../../../../api/types/evidence"; +import { EvidenceItem } from "../../../Hypothesis/EvidenceSection"; +import MultiSelectList from "../../../MultiSelectList/MultiSelectList"; import { Action, SelectDefinitionOfDoneState } from "../AddAutoDefinitionOfDone"; -import { ChangeEvent, useCallback, useMemo } from "react"; -import { Evidence, EvidenceType } from "../../../../api/services/evidence"; -import { EvidenceItem } from "../../../Hypothesis/EvidenceSection"; -import MultiSelectList from "../../../MultiSelectList/MultiSelectList"; type EvidenceGroupSelectorStepProps = { noneDODEvidences: Evidence[]; diff --git a/src/components/IncidentDetails/AddDefinitionOfDone/steps/ScriptStep.tsx b/src/components/IncidentDetails/AddDefinitionOfDone/steps/ScriptStep.tsx index d738384a7..217a33bd0 100644 --- a/src/components/IncidentDetails/AddDefinitionOfDone/steps/ScriptStep.tsx +++ b/src/components/IncidentDetails/AddDefinitionOfDone/steps/ScriptStep.tsx @@ -1,5 +1,5 @@ import dynamic from "next/dynamic"; -import { EvidenceType } from "../../../../api/services/evidence"; +import { EvidenceType } from "../../../../api/types/evidence"; const CodeEditor = dynamic( () => import("../../../CodeEditor").then((m) => m.CodeEditor), diff --git a/src/components/IncidentDetails/AddDefinitionOfDone/useAddCommentAsDoD.tsx b/src/components/IncidentDetails/AddDefinitionOfDone/useAddCommentAsDoD.tsx index 11de0866c..72acc5dea 100644 --- a/src/components/IncidentDetails/AddDefinitionOfDone/useAddCommentAsDoD.tsx +++ b/src/components/IncidentDetails/AddDefinitionOfDone/useAddCommentAsDoD.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react"; import { v4 as uuidv4 } from "uuid"; import { useCreateEvidenceMutation } from "../../../api/query-hooks/mutations/evidence"; -import { Evidence, EvidenceType } from "../../../api/services/evidence"; +import { Evidence, EvidenceType } from "../../../api/types/evidence"; import { useUser } from "../../../context"; export default function useAddCommentAsDoD( diff --git a/src/components/IncidentDetails/AddResponders/AddResponder.tsx b/src/components/IncidentDetails/AddResponders/AddResponder.tsx index 9b8f473e0..ef4b4bfa6 100644 --- a/src/components/IncidentDetails/AddResponders/AddResponder.tsx +++ b/src/components/IncidentDetails/AddResponders/AddResponder.tsx @@ -5,7 +5,7 @@ import { GrVmware } from "react-icons/gr"; import { MdEmail } from "react-icons/md"; import { SiJira } from "react-icons/si"; import { Icon } from "../../Icon"; -import { Incident } from "../../../api/services/incident"; +import { Incident } from "../../../api/types/incident"; import AddResponderModal from "./AddResponderModal"; export type AddResponderAction = { diff --git a/src/components/IncidentDetails/AddResponders/AddResponderModal.tsx b/src/components/IncidentDetails/AddResponders/AddResponderModal.tsx index 00621d151..c73d54d8f 100644 --- a/src/components/IncidentDetails/AddResponders/AddResponderModal.tsx +++ b/src/components/IncidentDetails/AddResponders/AddResponderModal.tsx @@ -4,11 +4,11 @@ import { isEqual, template } from "lodash"; import { useCallback, useEffect, useMemo, useReducer, useState } from "react"; import { useForm } from "react-hook-form"; import { IconType } from "react-icons"; -import { Incident } from "../../../api/services/incident"; -import { Responder, saveResponder } from "../../../api/services/responder"; -import { Team } from "../../../api/services/teams"; -import { User } from "../../../api/services/users"; +import { saveResponder } from "../../../api/services/responder"; +import { Incident, Responder } from "../../../api/types/incident"; +import { Team, User } from "../../../api/types/users"; import { useUser } from "../../../context"; +import { Events, sendAnalyticEvent } from "../../../services/analytics"; import { incidentStatusItems, typeItems } from "../../Incidents/data"; import { Modal } from "../../Modal"; import { OptionsList } from "../../OptionsList"; @@ -22,7 +22,6 @@ import { import SelectPeopleResponderDropdown from "./SelectPeopleResponderDropdown"; import SelectTeamResponderDropdown from "./SelectTeamResponderDropdown"; import TeamResponderTypeForm from "./TeamResponderTypeForm"; -import { Events, sendAnalyticEvent } from "../../../services/analytics"; export type SelectedResponderType = { value: string; @@ -190,9 +189,7 @@ export default function AddResponderModal({ }; const { mutate: addResponder, isLoading: loading } = useMutation({ - mutationFn: ( - responder: Omit<Responder, "id" | "updated_at" | "created_at"> - ) => saveResponder(responder), + mutationFn: (responder: Omit<Responder, "id">) => saveResponder(responder), onSuccess: (_) => { sendAnalyticEvent(Events.AddedResponderToIncident); toastSuccess("Responder added successfully"); diff --git a/src/components/IncidentDetails/AddResponders/SelectPeopleResponderDropdown.tsx b/src/components/IncidentDetails/AddResponders/SelectPeopleResponderDropdown.tsx index f5e4fcf08..5e16ac33b 100644 --- a/src/components/IncidentDetails/AddResponders/SelectPeopleResponderDropdown.tsx +++ b/src/components/IncidentDetails/AddResponders/SelectPeopleResponderDropdown.tsx @@ -2,7 +2,7 @@ import { useMemo } from "react"; import { ReactSelectDropdown } from "../../ReactSelectDropdown"; import { useGetAllPeople } from "../../../api/query-hooks/responders"; import { Avatar } from "../../Avatar"; -import { User } from "../../../api/services/users"; +import { User } from "../../../api/types/users"; type SelectTeamResponderDropdownProps = { onChange?: (value: User | undefined) => void; diff --git a/src/components/IncidentDetails/AddResponders/SelectTeamResponderDropdown.tsx b/src/components/IncidentDetails/AddResponders/SelectTeamResponderDropdown.tsx index ad7cccc32..46fcf3fb2 100644 --- a/src/components/IncidentDetails/AddResponders/SelectTeamResponderDropdown.tsx +++ b/src/components/IncidentDetails/AddResponders/SelectTeamResponderDropdown.tsx @@ -2,7 +2,7 @@ import { useMemo } from "react"; import { Icon } from "../../Icon"; import { ReactSelectDropdown } from "../../ReactSelectDropdown"; import { useGetAllTeams } from "../../../api/query-hooks/responders"; -import { Team } from "../../../api/services/teams"; +import { Team } from "../../../api/types/users"; type SelectTeamResponderDropdownProps = { onChange?: (value: Team | undefined) => void; diff --git a/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneComment.tsx b/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneComment.tsx index 9d6f65497..c55c40a7e 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneComment.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneComment.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { useUpdateEvidenceMutation } from "../../../api/query-hooks/mutations/evidence"; -import { Evidence } from "../../../api/services/evidence"; +import { Evidence } from "../../../api/types/evidence"; import { Modal } from "../../Modal"; import { ManualDoDInput } from "../AddDefinitionOfDone/AddManualDefinitionOfDone"; diff --git a/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneScript.tsx b/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneScript.tsx index 83d7e8a75..17ef3c0d9 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneScript.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/EditEvidenceDefinitionOfDoneScript.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { useUpdateEvidenceMutation } from "../../../api/query-hooks/mutations/evidence"; -import { Evidence } from "../../../api/services/evidence"; +import { Evidence } from "../../../api/types/evidence"; import { Modal } from "../../Modal"; import { ScriptStep } from "../AddDefinitionOfDone/steps/ScriptStep"; diff --git a/src/components/IncidentDetails/DefinitionOfDone/EvidenceSelectionModal.tsx b/src/components/IncidentDetails/DefinitionOfDone/EvidenceSelectionModal.tsx index d99c73f89..d93fa6b0a 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/EvidenceSelectionModal.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/EvidenceSelectionModal.tsx @@ -1,6 +1,6 @@ import clsx from "clsx"; import { useState } from "react"; -import { Evidence } from "../../../api/services/evidence"; +import { Evidence } from "../../../api/types/evidence"; import { ViewType } from "../../../types"; import { EvidenceItem } from "../../Hypothesis/EvidenceSection"; import { InfoMessage } from "../../InfoMessage"; diff --git a/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx b/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx index a71bafb73..61fca618c 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/EvidenceView/index.tsx @@ -7,8 +7,8 @@ import { useComponentNameQuery, useConfigNameQuery } from "../../../../api/query-hooks"; -import { Evidence, EvidenceType } from "../../../../api/services/evidence"; import { getCanaries } from "../../../../api/services/topology"; +import { Evidence, EvidenceType } from "../../../../api/types/evidence"; import { Size, ViewType } from "../../../../types"; import { Badge } from "../../../Badge"; import { @@ -16,10 +16,10 @@ import { ConfigChangeEvidence } from "../../../Hypothesis/EvidenceSection"; import { Icon } from "../../../Icon"; +import { ConfigIcon } from "../../../Icon/ConfigIcon"; import TextSkeletonLoader from "../../../SkeletonLoader/TextSkeletonLoader"; import { StatusStyles } from "../../../TopologyCard"; import { CardMetrics } from "../../../TopologyCard/CardMetrics"; -import { ConfigIcon } from "../../../Icon/ConfigIcon"; type EvidenceViewProps = Omit<React.HTMLProps<HTMLDivElement>, "size"> & { evidence: Evidence; diff --git a/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDone.tsx b/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDone.tsx index a3fe189be..109379820 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDone.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDone.tsx @@ -1,20 +1,21 @@ import clsx from "clsx"; import { useEffect, useMemo, useState } from "react"; -import { Evidence, updateEvidence } from "../../../api/services/evidence"; -import { ConfirmationPromptDialog } from "../../Dialogs/ConfirmationPromptDialog"; -import EvidenceSelectionModal from "./EvidenceSelectionModal"; -import IncidentsDefinitionOfDoneItem from "./IncidentsDefinitionOfDoneItem"; -import AddDefinitionOfDoneModal from "../AddDefinitionOfDone/AddDefinitionOfDoneHome"; -import CollapsiblePanel from "../../CollapsiblePanel"; -import Title from "../../Title/title"; +import { BsCardChecklist } from "react-icons/bs"; import { MdRefresh } from "react-icons/md"; import { RiFullscreenLine } from "react-icons/ri"; -import { BsCardChecklist } from "react-icons/bs"; -import { ClickableSvg } from "../../ClickableSvg/ClickableSvg"; +import { updateEvidence } from "../../../api/services/evidence"; +import { Evidence } from "../../../api/types/evidence"; import { useIncidentState } from "../../../store/incident.state"; -import EmptyState from "../../EmptyState"; -import { CountBadge } from "../../Badge/CountBadge"; import { dateSortHelper } from "../../../utils/date"; +import { CountBadge } from "../../Badge/CountBadge"; +import { ClickableSvg } from "../../ClickableSvg/ClickableSvg"; +import CollapsiblePanel from "../../CollapsiblePanel"; +import { ConfirmationPromptDialog } from "../../Dialogs/ConfirmationPromptDialog"; +import EmptyState from "../../EmptyState"; +import Title from "../../Title/title"; +import AddDefinitionOfDoneModal from "../AddDefinitionOfDone/AddDefinitionOfDoneHome"; +import EvidenceSelectionModal from "./EvidenceSelectionModal"; +import IncidentsDefinitionOfDoneItem from "./IncidentsDefinitionOfDoneItem"; type DefinitionOfDoneProps = React.HTMLProps<HTMLDivElement> & { incidentId: string; diff --git a/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDoneItem.tsx b/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDoneItem.tsx index 363054a54..0b14ceda4 100644 --- a/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDoneItem.tsx +++ b/src/components/IncidentDetails/DefinitionOfDone/IncidentsDefinitionOfDoneItem.tsx @@ -1,9 +1,3 @@ -import { Menu } from "../../Menu"; -import { AiFillCheckCircle } from "react-icons/ai"; -import { BsHourglassSplit, BsTrash } from "react-icons/bs"; -import { Evidence, EvidenceType } from "../../../api/services/evidence"; -import { IconButton } from "../../IconButton"; -import { EvidenceView } from "./EvidenceView"; import { CSSProperties, Dispatch, @@ -12,13 +6,19 @@ import { useLayoutEffect, useState } from "react"; -import { Size } from "../../../types"; +import { AiFillCheckCircle } from "react-icons/ai"; +import { BsHourglassSplit, BsTrash } from "react-icons/bs"; import { FaEdit } from "react-icons/fa"; -import EditEvidenceDefinitionOfDoneScript from "./EditEvidenceDefinitionOfDoneScript"; -import EditEvidenceDefinitionOfDoneComment from "./EditEvidenceDefinitionOfDoneComment"; -import { ConfirmationPromptDialog } from "../../Dialogs/ConfirmationPromptDialog"; -import { useUpdateEvidenceMutation } from "../../../api/query-hooks/mutations/evidence"; import { MdRefresh } from "react-icons/md"; +import { useUpdateEvidenceMutation } from "../../../api/query-hooks/mutations/evidence"; +import { Evidence, EvidenceType } from "../../../api/types/evidence"; +import { Size } from "../../../types"; +import { ConfirmationPromptDialog } from "../../Dialogs/ConfirmationPromptDialog"; +import { IconButton } from "../../IconButton"; +import { Menu } from "../../Menu"; +import EditEvidenceDefinitionOfDoneComment from "./EditEvidenceDefinitionOfDoneComment"; +import EditEvidenceDefinitionOfDoneScript from "./EditEvidenceDefinitionOfDoneScript"; +import { EvidenceView } from "./EvidenceView"; type Props = { evidence: Evidence; diff --git a/src/components/IncidentDetails/EditIncidentTitleForm.tsx b/src/components/IncidentDetails/EditIncidentTitleForm.tsx index b1ad625ed..186b281be 100644 --- a/src/components/IncidentDetails/EditIncidentTitleForm.tsx +++ b/src/components/IncidentDetails/EditIncidentTitleForm.tsx @@ -1,5 +1,5 @@ import { Form, Formik } from "formik"; -import { Incident } from "../../api/services/incident"; +import { Incident } from "../../api/types/incident"; import { EditableText } from "../EditableText"; type EditIncidentTitleFormProps = { diff --git a/src/components/IncidentDetails/EditableIncidentTitleBreadcrumb.tsx b/src/components/IncidentDetails/EditableIncidentTitleBreadcrumb.tsx index d525525b2..d881fa169 100644 --- a/src/components/IncidentDetails/EditableIncidentTitleBreadcrumb.tsx +++ b/src/components/IncidentDetails/EditableIncidentTitleBreadcrumb.tsx @@ -1,8 +1,7 @@ import { useState } from "react"; -import { Incident } from "../../api/services/incident"; -import { IconButton } from "../IconButton"; import { MdModeEditOutline } from "react-icons/md"; -import { TextInput } from "../TextInput"; +import { Incident } from "../../api/types/incident"; +import { IconButton } from "../IconButton"; import { EditIncidentTitleForm } from "./EditIncidentTitleForm"; type EditableIncidentTitleBreadcrumbProps = { diff --git a/src/components/IncidentDetails/IncidentDetails.stories.tsx b/src/components/IncidentDetails/IncidentDetails.stories.tsx index ba318e61e..b2ce9c20f 100644 --- a/src/components/IncidentDetails/IncidentDetails.stories.tsx +++ b/src/components/IncidentDetails/IncidentDetails.stories.tsx @@ -1,7 +1,7 @@ import { ComponentMeta, ComponentStory } from "@storybook/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { MemoryRouter } from "react-router-dom"; -import { IncidentSeverity, IncidentStatus } from "../../api/services/incident"; +import { IncidentSeverity, IncidentStatus } from "../../api/types/incident"; import { IncidentSidebar } from "./IncidentSidebar"; export default { diff --git a/src/components/IncidentDetails/IncidentDetailsPanel.tsx b/src/components/IncidentDetails/IncidentDetailsPanel.tsx index 7c4c4a256..0572b101c 100644 --- a/src/components/IncidentDetails/IncidentDetailsPanel.tsx +++ b/src/components/IncidentDetails/IncidentDetailsPanel.tsx @@ -3,18 +3,17 @@ import { debounce } from "lodash"; import { useEffect, useMemo } from "react"; import { useForm } from "react-hook-form"; import { BsCardList, BsShareFill } from "react-icons/bs"; -import { Incident } from "../../api/services/incident"; -import { IncidentPriority } from "../../constants"; import CollapsiblePanel from "../CollapsiblePanel"; import IncidentTypeDropdown from "../Incidents/IncidentTypeDropdown"; import { incidentStatusItems, typeItems } from "../Incidents/data"; import { ReactSelectDropdown } from "../ReactSelectDropdown"; import Title from "../Title/title"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; import { IncidentDetailsRow } from "./IncidentDetailsRow"; import { priorities } from "./IncidentSidebar"; import { IncidentWorkflow } from "./IncidentWorkflow"; import { Responders } from "./Responders"; +import { Incident, IncidentPriority } from "../../api/types/incident"; type IncidentDetailsPanelProps = React.HTMLProps<HTMLDivElement> & { incident: Incident; @@ -46,12 +45,7 @@ export function IncidentDetailsPanel({ mode: "onBlur", defaultValues: { title: incident.title, - tracking: "123456", created_at: incident.created_at, - chartRoomTitle: "#Slack", - chartRoom: "https://google.com.ua", - statusPageTitle: "StatusPage.io", - statusPage: "https://www.atlassian.com/software/statuspage", priority: incident.severity ?? IncidentPriority.High, type: typeItems[incident.type as keyof typeof typeItems] ? incident.type diff --git a/src/components/IncidentDetails/IncidentSidebar.tsx b/src/components/IncidentDetails/IncidentSidebar.tsx index 922dc5f83..1971c19db 100644 --- a/src/components/IncidentDetails/IncidentSidebar.tsx +++ b/src/components/IncidentDetails/IncidentSidebar.tsx @@ -1,11 +1,14 @@ import { severityItems } from "../Incidents/data"; -import { IncidentPriority } from "../../constants/incidentPriority"; -import { Incident, IncidentStatus } from "../../api/services/incident"; import { IncidentsDefinitionOfDone } from "./DefinitionOfDone/IncidentsDefinitionOfDone"; import { IncidentChangelog } from "../Changelog/IncidentChangelog"; import SlidingSideBar from "../SlidingSideBar"; import { IncidentDetailsPanel } from "./IncidentDetailsPanel"; import { useCallback, useState } from "react"; +import { + Incident, + IncidentPriority, + IncidentStatus +} from "../../api/types/incident"; export const priorities = Object.entries(severityItems).map(([key, value]) => ({ label: value.name, diff --git a/src/components/IncidentDetails/IncidentWorkflow.tsx b/src/components/IncidentDetails/IncidentWorkflow.tsx index ca302115d..37fa4b292 100644 --- a/src/components/IncidentDetails/IncidentWorkflow.tsx +++ b/src/components/IncidentDetails/IncidentWorkflow.tsx @@ -1,8 +1,10 @@ import { useEffect, useMemo, useState } from "react"; import { Controller } from "react-hook-form"; -import { Evidence, updateEvidence } from "../../api/services/evidence"; -import { Hypothesis } from "../../api/services/hypothesis"; -import { IncidentStatus, updateIncident } from "../../api/services/incident"; +import { updateEvidence } from "../../api/services/evidence"; +import { updateIncident } from "../../api/services/incident"; +import { Evidence } from "../../api/types/evidence"; +import { Hypothesis } from "../../api/types/hypothesis"; +import { IncidentStatus } from "../../api/types/incident"; import { useIncidentState } from "../../store/incident.state"; import { incidentStatusItems } from "../Incidents/data"; import { ReactSelectDropdown } from "../ReactSelectDropdown"; diff --git a/src/components/IncidentDetails/Responders.tsx b/src/components/IncidentDetails/Responders.tsx index 6a3b3a6f9..de0e4157c 100644 --- a/src/components/IncidentDetails/Responders.tsx +++ b/src/components/IncidentDetails/Responders.tsx @@ -2,7 +2,7 @@ import clsx from "clsx"; import { useState } from "react"; import { BsTrash } from "react-icons/bs"; import { useIncidentRespondersQuery } from "../../api/query-hooks/useIncidentRespondersQuery"; -import { Incident } from "../../api/services/incident"; +import { Incident } from "../../api/types/incident"; import { deleteResponder } from "../../api/services/responder"; import { ClickableSvg } from "../ClickableSvg/ClickableSvg"; import { ConfirmationPromptDialog } from "../Dialogs/ConfirmationPromptDialog"; diff --git a/src/components/IncidentSeverityTag/index.tsx b/src/components/IncidentSeverityTag/index.tsx index 1d4fcc6b7..fbe19aa42 100644 --- a/src/components/IncidentSeverityTag/index.tsx +++ b/src/components/IncidentSeverityTag/index.tsx @@ -1,4 +1,4 @@ -import { IncidentSeverity } from "../../api/services/incident"; +import { IncidentSeverity } from "../../api/types/incident"; import { severityItems } from "../Incidents/data"; const getSeverity = (severity: IncidentSeverity | string) => { diff --git a/src/components/IncidentStatusTag/index.tsx b/src/components/IncidentStatusTag/index.tsx index 50ed3223f..6bdcede93 100644 --- a/src/components/IncidentStatusTag/index.tsx +++ b/src/components/IncidentStatusTag/index.tsx @@ -1,7 +1,7 @@ import clsx from "clsx"; import { capitalize } from "lodash"; -import { IncidentStatus } from "../../api/services/incident"; import { Tag } from "../Tag/Tag"; +import { IncidentStatus } from "../../api/types/incident"; interface IProps { status: IncidentStatus; diff --git a/src/components/Incidents/IncidentCreate/index.tsx b/src/components/Incidents/IncidentCreate/index.tsx index 822cfa775..1cd8d1c5d 100644 --- a/src/components/Incidents/IncidentCreate/index.tsx +++ b/src/components/Incidents/IncidentCreate/index.tsx @@ -4,21 +4,9 @@ import { Controller, useForm } from "react-hook-form"; import { useNavigate, useSearchParams } from "react-router-dom"; import { v4 as uuidv4 } from "uuid"; import * as yup from "yup"; -import { - Evidence, - EvidenceType, - createEvidence -} from "../../../api/services/evidence"; -import { - HypothesisStatus, - createHypothesisOld -} from "../../../api/services/hypothesis"; -import { - Incident, - IncidentStatus, - NewIncident, - createIncident -} from "../../../api/services/incident"; +import { createEvidence } from "../../../api/services/evidence"; +import { createHypothesisOld } from "../../../api/services/hypothesis"; +import { createIncident } from "../../../api/services/incident"; import { useUser } from "../../../context"; import { Events, sendAnalyticEvent } from "../../../services/analytics"; import { severityOptions, typeOptions } from "../../AttachEvidenceDialog"; @@ -27,6 +15,14 @@ import SelectDropdown from "../../Dropdown/SelectDropdown"; import { TextInput } from "../../TextInput"; import { toastError } from "../../Toast/toast"; import { incidentStatusItems, severityItems } from "../data"; +import { Evidence } from "../../../api/types/evidence"; +import { HypothesisStatus } from "../../../api/types/hypothesis"; +import { + Incident, + NewIncident, + IncidentStatus +} from "../../../api/types/incident"; +import { EvidenceType } from "../../../api/types/evidence"; const incidentStatusOptions = Object.entries(incidentStatusItems).map( ([_, { value, description, icon }]) => ({ diff --git a/src/components/Incidents/IncidentList/IncidentListColumns.tsx b/src/components/Incidents/IncidentList/IncidentListColumns.tsx index d73b55c66..87d1ae176 100644 --- a/src/components/Incidents/IncidentList/IncidentListColumns.tsx +++ b/src/components/Incidents/IncidentList/IncidentListColumns.tsx @@ -2,15 +2,15 @@ import { CellContext, ColumnDef } from "@tanstack/react-table"; import { IncidentSeverity, IncidentStatus, - IncidentSummary -} from "../../../api/services/incident"; -import { DateCell } from "../../ConfigViewer/columns"; -import { typeItems } from "../data"; -import { IncidentTypeTag } from "../../incidentTypeTag"; + IncidentSummary, + Responder +} from "../../../api/types/incident"; +import { DateCell } from "../../../ui/table"; +import { Avatar } from "../../Avatar"; import { IncidentSeverityTag } from "../../IncidentSeverityTag"; import { IncidentStatusTag } from "../../IncidentStatusTag"; -import { Responder } from "../../../api/services/responder"; -import { Avatar } from "../../Avatar"; +import { IncidentTypeTag } from "../../incidentTypeTag"; +import { typeItems } from "../data"; export const incidentListColumns: ColumnDef<IncidentSummary, any>[] = [ { diff --git a/src/components/Incidents/IncidentList/incidenttList.stories.tsx b/src/components/Incidents/IncidentList/incidenttList.stories.tsx index 6d3852add..24e9a9d71 100644 --- a/src/components/Incidents/IncidentList/incidenttList.stories.tsx +++ b/src/components/Incidents/IncidentList/incidenttList.stories.tsx @@ -4,7 +4,7 @@ import { IncidentSeverity, IncidentStatus, IncidentSummary -} from "../../../api/services/incident"; +} from "../../../api/types/incident"; import { IncidentList } from "./index"; import { ComponentStory } from "@storybook/react"; diff --git a/src/components/Incidents/IncidentList/index.tsx b/src/components/Incidents/IncidentList/index.tsx index 91aec0f40..e51c3eaef 100644 --- a/src/components/Incidents/IncidentList/index.tsx +++ b/src/components/Incidents/IncidentList/index.tsx @@ -1,5 +1,5 @@ import { useNavigate } from "react-router-dom"; -import { IncidentSummary } from "../../../api/services/incident"; +import { IncidentSummary } from "../../../api/types/incident"; import { useCallback, useMemo } from "react"; import { incidentListColumns } from "./IncidentListColumns"; import { DataTable } from "../../DataTable"; diff --git a/src/components/Incidents/data.tsx b/src/components/Incidents/data.tsx index 0486fe968..bd718f744 100644 --- a/src/components/Incidents/data.tsx +++ b/src/components/Incidents/data.tsx @@ -20,9 +20,9 @@ import { HiOutlineChevronUp, HiOutlineMinus } from "react-icons/hi"; -import { IncidentStatus } from "../../api/services/incident"; import { capitalizeFirstLetter } from "../../utils/common"; import { GoIssueClosed, GoIssueOpened } from "react-icons/go"; +import { IncidentStatus } from "../../api/types/incident"; export const defaultSelections = { all: { @@ -32,15 +32,6 @@ export const defaultSelections = { } }; -export type Severity = - | "info" - | "warning" - | "low" - | "medium" - | "high" - | "blocker" - | "critical"; - export const severityItems = { Info: { id: "dropdown-severity-info", diff --git a/src/components/Insights/Insights.tsx b/src/components/Insights/Insights.tsx index 9e8766f90..9f07deca5 100644 --- a/src/components/Insights/Insights.tsx +++ b/src/components/Insights/Insights.tsx @@ -4,12 +4,12 @@ import { useGetConfigInsights, useGetTopologyRelatedInsightsQuery } from "../../api/query-hooks"; +import { ConfigAnalysis } from "../../api/types/configs"; import { sanitizeHTMLContent, sanitizeHTMLContentToText, truncateText } from "../../utils/common"; -import { ConfigTypeInsights } from "../ConfigInsights"; import EmptyState from "../EmptyState"; import { InfiniteTable } from "../InfiniteTable/InfiniteTable"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; @@ -54,7 +54,7 @@ type TopologyInsightsProps = { export const columns: ColumnDef< Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" @@ -138,7 +138,7 @@ export default function InsightsDetails( <div className="flex flex-col overflow-y-hidden"> <InfiniteTable< Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" @@ -152,7 +152,7 @@ export default function InsightsDetails( columns={columns} isLoading={isLoading && !isFetching} isFetching={isFetching} - allRows={insightsWithSanitizedMessages as ConfigTypeInsights[]} + allRows={insightsWithSanitizedMessages as ConfigAnalysis[]} loaderView={<TextSkeletonLoader className="w-full my-2" />} totalEntries={totalEntries} fetchNextPage={() => { diff --git a/src/components/Insights/cells/ConfigInsightAgeCell.tsx b/src/components/Insights/cells/ConfigInsightAgeCell.tsx index 1588c16cc..796e6471d 100644 --- a/src/components/Insights/cells/ConfigInsightAgeCell.tsx +++ b/src/components/Insights/cells/ConfigInsightAgeCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; -import { ConfigTypeInsights } from "../../ConfigInsights"; -import { Age } from "../../UI/Age"; +import { Age } from "../../../ui/Age"; +import { ConfigAnalysis } from "../../../api/types/configs"; export default function ConfigInsightAgeCell({ row, @@ -8,7 +8,7 @@ export default function ConfigInsightAgeCell({ getValue }: CellContext< Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" @@ -20,7 +20,5 @@ export default function ConfigInsightAgeCell({ >, unknown >) { - return ( - <Age className="flex py-1 text-sm" from={row.original.first_observed} /> - ); + return <Age className="flex py-1" from={row.original.first_observed} />; } diff --git a/src/components/Insights/cells/ConfigInsightNameCell.tsx b/src/components/Insights/cells/ConfigInsightNameCell.tsx index 28887535c..7fb712067 100644 --- a/src/components/Insights/cells/ConfigInsightNameCell.tsx +++ b/src/components/Insights/cells/ConfigInsightNameCell.tsx @@ -1,6 +1,6 @@ import { CellContext } from "@tanstack/react-table"; import { ConfigAnalysisLink } from "../../ConfigAnalysisLink/ConfigAnalysisLink"; -import { ConfigTypeInsights } from "../../ConfigInsights"; +import { ConfigAnalysis } from "../../../api/types/configs"; export default function ConfigInsightNameCell({ row, @@ -8,7 +8,7 @@ export default function ConfigInsightNameCell({ getValue }: CellContext< Pick< - ConfigTypeInsights, + ConfigAnalysis, | "id" | "analyzer" | "config" diff --git a/src/components/JobsHistory/JobsHistoryTableColumn.tsx b/src/components/JobsHistory/JobsHistoryTableColumn.tsx index a228ca220..6230d1a44 100644 --- a/src/components/JobsHistory/JobsHistoryTableColumn.tsx +++ b/src/components/JobsHistory/JobsHistoryTableColumn.tsx @@ -3,9 +3,9 @@ import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; import { formatJobName } from "../../utils/common"; -import { DateCell } from "../ConfigViewer/columns"; import JobHistoryStatusColumn from "./JobHistoryStatusColumn"; import { JobHistory } from "./JobsHistoryTable"; +import { DateCell } from "../../ui/table"; dayjs.extend(duration); dayjs.extend(relativeTime); diff --git a/src/components/LogBackends/LogBackends.ts b/src/components/LogBackends/LogBackends.ts index b5b11e73d..c25c51bcc 100644 --- a/src/components/LogBackends/LogBackends.ts +++ b/src/components/LogBackends/LogBackends.ts @@ -1,4 +1,4 @@ -import { User } from "../../api/services/users"; +import { User } from "../../api/types/users"; type LogBackendsSource = "KubernetesCRD" | "ConfigFile"; diff --git a/src/components/LogBackends/LogBackendsList.tsx b/src/components/LogBackends/LogBackendsList.tsx index 6edadf28b..dbd556177 100644 --- a/src/components/LogBackends/LogBackendsList.tsx +++ b/src/components/LogBackends/LogBackendsList.tsx @@ -1,11 +1,11 @@ import { CellContext, ColumnDef } from "@tanstack/react-table"; import { LogBackends } from "./LogBackends"; -import { DateCell } from "../ConfigViewer/columns"; import { Avatar } from "../Avatar"; import { DataTable } from "../DataTable"; import { useState } from "react"; import { Modal } from "../Modal"; import LogBackendsForm from "./LogBackendsForm"; +import { DateCell } from "../../ui/table"; const columns: ColumnDef<LogBackends>[] = [ { diff --git a/src/components/Logs/Table/LogsTable.tsx b/src/components/Logs/Table/LogsTable.tsx index 7e82b4ef0..13e85717f 100644 --- a/src/components/Logs/Table/LogsTable.tsx +++ b/src/components/Logs/Table/LogsTable.tsx @@ -10,7 +10,7 @@ import Convert from "ansi-to-html"; import clsx from "clsx"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useSearchParams } from "react-router-dom"; -import { EvidenceType } from "../../../api/services/evidence"; +import { EvidenceType } from "../../../api/types/evidence"; import useDebouncedValue from "../../../hooks/useDebounce"; import LogItem from "../../../types/Logs"; import { sanitizeHTMLContent } from "../../../utils/common"; diff --git a/src/components/ManageUserRoles/ManageUserRoles.tsx b/src/components/ManageUserRoles/ManageUserRoles.tsx index 7d424e9a9..ba2c2b170 100644 --- a/src/components/ManageUserRoles/ManageUserRoles.tsx +++ b/src/components/ManageUserRoles/ManageUserRoles.tsx @@ -1,7 +1,7 @@ import clsx from "clsx"; import React from "react"; import { useForm } from "react-hook-form"; -import { RegisteredUser } from "../../api/services/users"; +import { RegisteredUser } from "../../api/types/users"; import { Roles } from "../../context/UserAccessContext/UserAccessContext"; export type ManageUserRoleValue = { diff --git a/src/components/Notifications/notificationsTableColumns.tsx b/src/components/Notifications/notificationsTableColumns.tsx index e4521e352..003ebd170 100644 --- a/src/components/Notifications/notificationsTableColumns.tsx +++ b/src/components/Notifications/notificationsTableColumns.tsx @@ -1,9 +1,8 @@ import { CellContext, ColumnDef } from "@tanstack/react-table"; -import { Team } from "../../api/services/teams"; -import { User } from "../../api/services/users"; +import { Team, User } from "../../api/types/users"; +import { DateCell } from "../../ui/table"; import { Avatar } from "../Avatar"; import { Badge } from "../Badge"; -import { DateCell } from "../ConfigViewer/columns"; import { Icon } from "../Icon"; import JobHistoryStatusColumn from "../JobsHistory/JobHistoryStatusColumn"; import { JobHistoryStatus } from "../JobsHistory/JobsHistoryTable"; diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx index e1d5538b5..cc17ec58c 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx @@ -1,5 +1,5 @@ -import { Age } from "../../../UI/Age"; -import { PlaybookRunAction } from "../PlaybookRunTypes"; +import { PlaybookRunAction } from "../../../../api/types/playbooks"; +import { Age } from "../../../../ui/Age"; import PlaybookRunsStatus from "../PlaybookRunsStatus"; type PlaybookRunsActionItemProps = { diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.stories.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.stories.tsx index 583aeee28..704ee96fd 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.stories.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.stories.tsx @@ -1,7 +1,6 @@ import { StoryObj } from "@storybook/react"; -import PlaybookRunsActions, { - PlaybookRunWithActions -} from "./PlaybookRunsActions"; +import PlaybookRunsActions from "./PlaybookRunsActions"; +import { PlaybookRunWithActions } from "../../../../api/types/playbooks"; export default { title: "PlaybookRunsActions", @@ -21,8 +20,7 @@ const mockPlaybookRun: PlaybookRunWithActions = { }, created_by: { id: "f4e2326c-9c37-4195-8677-40dd30ae824f", - name: "Admin", - email: "" + name: "Admin" }, playbooks: { id: "018abbad-1869-23c9-61c2-1ad79513d598", diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx index 6470a4237..dfc9047d8 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx @@ -2,15 +2,14 @@ import { ReactNode, useMemo, useState } from "react"; import { Link } from "react-router-dom"; import { Avatar } from "../../../Avatar"; import { Icon } from "../../../Icon"; -import { Age } from "../../../UI/Age"; -import { PlaybookRun, PlaybookRunAction } from "../PlaybookRunTypes"; +import { Age } from "../../../../ui/Age"; import PlaybookRunsStatus from "./../PlaybookRunsStatus"; import PlaybookRunActionFetch from "./PlaybookRunActionFetch"; import PlaybookRunsActionItem from "./PlaybookRunsActionItem"; - -export type PlaybookRunWithActions = PlaybookRun & { - actions: PlaybookRunAction[]; -}; +import { + PlaybookRunWithActions, + PlaybookRunAction +} from "../../../../api/types/playbooks"; type PlaybookRunActionsProps = { data: PlaybookRunWithActions; diff --git a/src/components/Playbooks/Runs/Actions/PlaybooksActionsResults.tsx b/src/components/Playbooks/Runs/Actions/PlaybooksActionsResults.tsx index e5bb8d3f3..060b8217e 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybooksActionsResults.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybooksActionsResults.tsx @@ -1,5 +1,5 @@ import Convert from "ansi-to-html"; -import { PlaybookRunAction } from "../PlaybookRunTypes"; +import { PlaybookRunAction } from "../../../../api/types/playbooks"; const convert = new Convert(); diff --git a/src/components/Playbooks/Runs/PlaybookRunsList.tsx b/src/components/Playbooks/Runs/PlaybookRunsList.tsx index fd185b17d..73dccddc4 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsList.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsList.tsx @@ -1,13 +1,13 @@ import { ColumnDef } from "@tanstack/react-table"; import { useCallback } from "react"; import { Link, useNavigate } from "react-router-dom"; -import { User } from "../../../api/services/users"; +import { PlaybookRun, PlaybookRunStatus } from "../../../api/types/playbooks"; +import { User } from "../../../api/types/users"; +import { Age } from "../../../ui/Age"; +import { DateCell } from "../../../ui/table"; import { Avatar } from "../../Avatar"; -import { DateCell } from "../../ConfigViewer/columns"; import { DataTable, PaginationOptions } from "../../DataTable"; import { Icon } from "../../Icon"; -import { Age } from "../../UI/Age"; -import { PlaybookRun, PlaybookRunStatus } from "./PlaybookRunTypes"; import PlaybookRunsStatus from "./PlaybookRunsStatus"; const playbookRunsTableColumns: ColumnDef<PlaybookRun>[] = [ diff --git a/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx b/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx index c9d19d153..671407003 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx @@ -12,8 +12,8 @@ import { InfiniteTable } from "../../InfiniteTable/InfiniteTable"; import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader"; import { refreshButtonClickedTrigger } from "../../SlidingSideBar"; import Title from "../../Title/title"; -import { Age } from "../../UI/Age"; -import { PlaybookRun } from "./PlaybookRunTypes"; +import { Age } from "../../../ui/Age"; +import { PlaybookRun } from "../../../api/types/playbooks"; type TopologySidePanelProps = { panelType: "topology"; diff --git a/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx b/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx index 899b72c7f..08aa3c980 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx @@ -1,6 +1,6 @@ import { BsFillExclamationCircleFill } from "react-icons/bs"; import { FaCheckCircle, FaClock, FaSpinner } from "react-icons/fa"; -import { PlaybookRunStatus } from "./PlaybookRunTypes"; +import { PlaybookRunStatus } from "../../../api/types/playbooks"; const statusIconMap: Record<PlaybookRunStatus, { icon: React.ReactNode }> = { completed: { diff --git a/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx b/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx index ee5f15fc6..f1ab1d7b0 100644 --- a/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx +++ b/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import FormikTextInput from "../../../Forms/Formik/FormikTextInput"; -import { PlaybookSpec } from "../../Settings/PlaybookSpecsTable"; +import { PlaybookSpec } from "../../../../api/types/playbooks"; type AddPlaybookToRunParamsProps = { playbookSpec: PlaybookSpec; diff --git a/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx b/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx index 96b10b202..42e255d0e 100644 --- a/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx +++ b/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx @@ -4,8 +4,8 @@ import { FaChevronDown, FaChevronUp } from "react-icons/fa"; import { useGetPlaybooksToRun } from "../../../../api/query-hooks/playbooks"; import { Button } from "../../../Button"; import { Menu } from "@headlessui/react"; -import { PlaybookSpec } from "../../Settings/PlaybookSpecsTable"; import SubmitPlaybookRunForm from "./SubmitPlaybookRunForm"; +import { PlaybookSpec } from "../../../../api/types/playbooks"; type SelectPlaybookToRunProps = { component_id?: string; diff --git a/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx b/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx index d8987fedb..e263918b3 100644 --- a/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx +++ b/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx @@ -4,8 +4,8 @@ import { useSubmitPlaybookRunMutation } from "../../../../api/query-hooks/playbo import { Button } from "../../../Button"; import { Modal } from "../../../Modal"; import { toastError, toastSuccess } from "../../../Toast/toast"; -import { PlaybookSpec } from "../../Settings/PlaybookSpecsTable"; import AddPlaybookToRunParams from "./AddPlaybookToRunParams"; +import { PlaybookSpec } from "../../../../api/types/playbooks"; export type SubmitPlaybookRunFormValues = { // if this is present in the form, we show step to add params diff --git a/src/components/Playbooks/Settings/PlaybookSpecsForm.tsx b/src/components/Playbooks/Settings/PlaybookSpecsForm.tsx index f50380e97..046de6fb4 100644 --- a/src/components/Playbooks/Settings/PlaybookSpecsForm.tsx +++ b/src/components/Playbooks/Settings/PlaybookSpecsForm.tsx @@ -17,7 +17,7 @@ import { NewPlaybookSpec, PlaybookSpec, UpdatePlaybookSpec -} from "./PlaybookSpecsTable"; +} from "../../../api/types/playbooks"; type PlaybookSpecsFormProps = { playbook?: PlaybookSpec; diff --git a/src/components/Playbooks/Settings/PlaybookSpecsTable.tsx b/src/components/Playbooks/Settings/PlaybookSpecsTable.tsx index 22e48058b..17f673357 100644 --- a/src/components/Playbooks/Settings/PlaybookSpecsTable.tsx +++ b/src/components/Playbooks/Settings/PlaybookSpecsTable.tsx @@ -1,31 +1,9 @@ import { CellContext, ColumnDef } from "@tanstack/react-table"; -import { User } from "../../../api/services/users"; import { Avatar } from "../../Avatar"; -import { DateCell } from "../../ConfigViewer/columns"; import { DataTable } from "../../DataTable"; - -export type PlaybookSpec = { - id: string; - name: string; - created_by?: User; - spec: any; - source: "KubernetesCRD" | "ConfigFile" | "UI"; - created_at: string; - updated_at: string; - deleted_at?: string; -}; - -export type NewPlaybookSpec = Omit< - PlaybookSpec, - "id" | "created_at" | "updated_at" | "deleted_at" | "created_by" -> & { - created_by?: string; -}; - -export type UpdatePlaybookSpec = Omit< - PlaybookSpec, - "created_at" | "updated_at" | "deleted_at" | "created_by" ->; +import { PlaybookSpec } from "../../../api/types/playbooks"; +import { User } from "../../../api/types/users"; +import { DateCell } from "../../../ui/table"; const playbookSpecsTableColumns: ColumnDef<PlaybookSpec>[] = [ { diff --git a/src/components/RadioOptionsGroup/RadioOptionsGroup.stories.tsx b/src/components/RadioOptionsGroup/RadioOptionsGroup.stories.tsx index cc2b31363..124836dfd 100644 --- a/src/components/RadioOptionsGroup/RadioOptionsGroup.stories.tsx +++ b/src/components/RadioOptionsGroup/RadioOptionsGroup.stories.tsx @@ -1,5 +1,5 @@ import { ComponentMeta, ComponentStory } from "@storybook/react"; -import { IncidentSeverity } from "../../api/services/incident"; +import { IncidentSeverity } from "../../api/types/incident"; import { severityItems } from "../Incidents/data"; import { RadioOptionsGroup } from "./index"; diff --git a/src/components/SchemaResourcePage/SchemaResourceList.tsx b/src/components/SchemaResourcePage/SchemaResourceList.tsx index 7481ec8dc..ff4551828 100644 --- a/src/components/SchemaResourcePage/SchemaResourceList.tsx +++ b/src/components/SchemaResourcePage/SchemaResourceList.tsx @@ -6,7 +6,7 @@ import { Avatar } from "../Avatar"; import { InfoMessage } from "../InfoMessage"; import JobHistoryStatusColumn from "../JobsHistory/JobHistoryStatusColumn"; import TableSkeletonLoader from "../SkeletonLoader/TableSkeletonLoader"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; import ConfigScrapperIcon from "./ConfigScrapperIcon"; interface Props { items: SchemaResourceWithJobStatus[]; @@ -136,16 +136,16 @@ function SchemaResourceListItem({ </Cell> {table === "canaries" && <Cell>{schedule}</Cell>} <Cell className="text-gray-500"> - <Age from={created_at} /> + <Age from={created_at} suffix={true} /> </Cell> <Cell className="text-gray-500"> - <Age from={updated_at} /> + <Age from={updated_at} suffix={true} /> </Cell> <Cell className="text-gray-500 lowercase space-x-2"> <JobHistoryStatusColumn status={job_status} /> </Cell> <Cell className="text-gray-500"> - <Age from={job_time_start} /> + <Age from={job_time_start} suffix={true} /> </Cell> <Cell className="text-gray-500"> {created_by && <Avatar user={created_by} circular />} diff --git a/src/components/Sidebars/ConfigChanges/Cells/ConfigChangeAgeCell.tsx b/src/components/Sidebars/ConfigChanges/Cells/ConfigChangeAgeCell.tsx new file mode 100644 index 000000000..ddaf84c28 --- /dev/null +++ b/src/components/Sidebars/ConfigChanges/Cells/ConfigChangeAgeCell.tsx @@ -0,0 +1,13 @@ +import { CellContext } from "@tanstack/react-table"; +import { Age } from "../../../../ui/Age"; +import { ConfigChange } from "../../../../api/types/configs"; + +export default function ConfigChangeAgeCell({ + row, + column, + getValue +}: CellContext<ConfigChange, unknown>) { + return ( + <Age className="whitespace-nowrap pr-2" from={row.original.created_at} /> + ); +} diff --git a/src/components/Sidebars/incidents.tsx b/src/components/Sidebars/incidents.tsx index c5f2c8ebf..5c80959c4 100644 --- a/src/components/Sidebars/incidents.tsx +++ b/src/components/Sidebars/incidents.tsx @@ -14,7 +14,7 @@ import { typeItems } from "../Incidents/data"; import IncidentsFilterBar, { IncidentFilter } from "../IncidentsFilterBar"; import { refreshButtonClickedTrigger } from "../SlidingSideBar"; import Title from "../Title/title"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; import { IncidentTypeIcon } from "../incidentTypeTag"; type Props = { diff --git a/src/components/Status/HealthCheckStatus.tsx b/src/components/Status/HealthCheckStatus.tsx index 241a58fdf..af775fda1 100644 --- a/src/components/Status/HealthCheckStatus.tsx +++ b/src/components/Status/HealthCheckStatus.tsx @@ -1,4 +1,4 @@ -import { HealthCheck } from "../../types/healthChecks"; +import { HealthCheck } from "../../api/types/health"; type StatusProps = { check?: Pick<HealthCheck, "status">; diff --git a/src/components/TeamMembers/TeamMembers.tsx b/src/components/TeamMembers/TeamMembers.tsx index 2e19a89a7..16445866f 100644 --- a/src/components/TeamMembers/TeamMembers.tsx +++ b/src/components/TeamMembers/TeamMembers.tsx @@ -7,7 +7,7 @@ import { getTeamMembers, removeUserFromTeam } from "../../api/services/teams"; -import { User, getPersons } from "../../api/services/users"; +import { getPersons } from "../../api/services/users"; import { useLoader } from "../../hooks"; import { Avatar } from "../Avatar"; import EmptyState from "../EmptyState"; @@ -17,6 +17,7 @@ import { Modal } from "../Modal"; import MultiSelectList from "../MultiSelectList/MultiSelectList"; import TableSkeletonLoader from "../SkeletonLoader/TableSkeletonLoader"; import { toastError, toastSuccess } from "../Toast/toast"; +import { User } from "../../api/types/users"; type TeamMembersProps = { teamId: string; diff --git a/src/components/TopologyCard/Property.tsx b/src/components/TopologyCard/Property.tsx index a23afd406..3f38d5b24 100644 --- a/src/components/TopologyCard/Property.tsx +++ b/src/components/TopologyCard/Property.tsx @@ -1,9 +1,9 @@ import clsx from "clsx"; +import { TopologyProperty } from "../../api/types/topology"; import { NodePodPropToLabelMap } from "../../constants"; -import { TopologyProperty } from "../../context/TopologyPageContext"; +import { Age } from "../../ui/Age"; import { isEmpty } from "../Canary/utils"; import { Icon } from "../Icon"; -import { Age } from "../UI/Age"; import { FormatPropertyCurrency, FormatPropertyDefault, diff --git a/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx b/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx index 59afed7c7..d3b72cd20 100644 --- a/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx +++ b/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx @@ -1,12 +1,12 @@ import { useMemo } from "react"; import { MdOutlineInsights } from "react-icons/md"; -import { Topology } from "../../context/TopologyPageContext"; import { InsightTypeToIcon } from "../ConfigInsightsIcon"; import { StatusInfo, StatusLine, StatusLineData } from "../StatusLine/StatusLine"; +import { Topology } from "../../api/types/topology"; const severityToColorMap = (severity: string) => { if (severity === "critical") { diff --git a/src/components/TopologyCard/TopologyDropdownMenu.tsx b/src/components/TopologyCard/TopologyDropdownMenu.tsx index 6c1a87653..e061db5cd 100644 --- a/src/components/TopologyCard/TopologyDropdownMenu.tsx +++ b/src/components/TopologyCard/TopologyDropdownMenu.tsx @@ -1,13 +1,13 @@ import { CSSProperties, useCallback, useMemo, useState } from "react"; -import { EvidenceType } from "../../api/services/evidence"; import { useFeatureFlagsContext } from "../../context/FeatureFlagsContext"; -import { Topology } from "../../context/TopologyPageContext"; import { features } from "../../services/permissions/features"; import { AttachEvidenceDialog } from "../AttachEvidenceDialog"; import { Menu } from "../Menu"; import { TopologyConfigLinkModal } from "../TopologyConfigLinkModal/TopologyConfigLinkModal"; import { topologyActionItems } from "../TopologySidebar/TopologyActionBar"; import TopologySnapshotModal from "./TopologySnapshotModal"; +import { Topology } from "../../api/types/topology"; +import { EvidenceType } from "../../api/types/evidence"; type TopologyMenuItemProps = { onClick?: () => void; diff --git a/src/components/TopologyCard/TopologySnapshotModal.tsx b/src/components/TopologyCard/TopologySnapshotModal.tsx index bfb22069e..a27edf8e2 100644 --- a/src/components/TopologyCard/TopologySnapshotModal.tsx +++ b/src/components/TopologyCard/TopologySnapshotModal.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; -import { Topology } from "../../context/TopologyPageContext"; import { useDownloadTopologySnapshot } from "./useDownloadTopologySnapshot"; import { TimeRange, timeRanges } from "../Dropdown/TimeRange"; import { Modal } from "../Modal"; @@ -9,6 +8,7 @@ import { Toggle } from "../Toggle"; import { FaDownload } from "react-icons/fa"; import { Oval } from "react-loading-icons"; import { Events, sendAnalyticEvent } from "../../services/analytics"; +import { Topology } from "../../api/types/topology"; type Props = { onCloseModal: () => void; diff --git a/src/components/TopologyCard/Utils/FormatProperties.tsx b/src/components/TopologyCard/Utils/FormatProperties.tsx index 120aea13a..b97d40528 100644 --- a/src/components/TopologyCard/Utils/FormatProperties.tsx +++ b/src/components/TopologyCard/Utils/FormatProperties.tsx @@ -1,8 +1,8 @@ import { FiExternalLink } from "react-icons/fi"; -import { TopologyProperty } from "../../../context/TopologyPageContext"; import React, { useMemo, useState } from "react"; import { formatBytes } from "../../../utils/common"; import { isEmpty } from "lodash"; +import { TopologyProperty } from "../../../api/types/topology"; type FormatPropertyProps = { property?: TopologyProperty; diff --git a/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx b/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx index d171cd6ed..015ab4bb7 100644 --- a/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx +++ b/src/components/TopologyConfigLinkModal/TopologyConfigLinkModal.tsx @@ -6,7 +6,6 @@ import { addManualComponentConfigRelationship, getAllConfigsForSearchPurpose } from "../../api/services/configs"; -import { Topology } from "../../context/TopologyPageContext"; import { queryClient } from "../../query-client"; import { delayedPromise, stringSortHelper } from "../../utils/common"; import ConfigLink from "../ConfigLink/ConfigLink"; @@ -17,7 +16,9 @@ import { toastError, toastSuccess } from "../Toast/toast"; import { useAtom } from "jotai"; import { refreshButtonClickedTrigger } from "../SlidingSideBar"; import { Events, sendAnalyticEvent } from "../../services/analytics"; -import { ConfigItem } from "../../api/services/configs"; +import { ConfigItem } from "../../api/types/configs"; +import { Topology } from "../../api/types/topology"; + type TopologyConfigLinkModalProps = { topology: Topology; openModal: boolean; diff --git a/src/components/TopologyDetails/index.tsx b/src/components/TopologyDetails/index.tsx index 09588a8b2..dedd4f840 100644 --- a/src/components/TopologyDetails/index.tsx +++ b/src/components/TopologyDetails/index.tsx @@ -1,6 +1,5 @@ import { isEmpty, map } from "lodash"; import { BsCardList } from "react-icons/bs"; -import { Topology } from "../../context/TopologyPageContext"; import CollapsiblePanel from "../CollapsiblePanel"; import { DescriptionCard } from "../DescriptionCard"; import EmptyState from "../EmptyState"; @@ -9,6 +8,7 @@ import Title from "../Title/title"; import { FormatProperty } from "../TopologyCard/Property"; import { TopologyLink } from "../TopologyLink"; import { useMemo } from "react"; +import { Topology } from "../../api/types/topology"; type Props = { topology?: Topology; diff --git a/src/components/TopologyPopover/topologySort.tsx b/src/components/TopologyPopover/topologySort.tsx index 7acd07974..dc4efc2ca 100644 --- a/src/components/TopologyPopover/topologySort.tsx +++ b/src/components/TopologyPopover/topologySort.tsx @@ -3,7 +3,8 @@ import { uniq } from "lodash"; import { LegacyRef } from "react"; import { BsSortDown, BsSortUp } from "react-icons/bs"; import { useSearchParams } from "react-router-dom"; -import type { Topology, ValueType } from "../../context/TopologyPageContext"; +import { ValueType } from "../../api/types/common"; +import { Topology } from "../../api/types/topology"; import { useOnMouseActivity } from "../../hooks/useMouseActivity"; import { saveSortBy, saveSortOrder } from "../../pages/TopologyPage"; import { isDate } from "../../utils/date"; diff --git a/src/components/TopologySidebar/ComponentTeamLink.tsx b/src/components/TopologySidebar/ComponentTeamLink.tsx index 5462394c5..280d36412 100644 --- a/src/components/TopologySidebar/ComponentTeamLink.tsx +++ b/src/components/TopologySidebar/ComponentTeamLink.tsx @@ -1,5 +1,5 @@ import { Link } from "react-router-dom"; -import { ComponentTeamItem } from "../../api/services/topology"; +import { ComponentTeamItem } from "../../api/types/topology"; import { Icon } from "../Icon"; type Props = { diff --git a/src/components/TopologySidebar/TopologyActionBar.tsx b/src/components/TopologySidebar/TopologyActionBar.tsx index a48fb5f5b..174f41469 100644 --- a/src/components/TopologySidebar/TopologyActionBar.tsx +++ b/src/components/TopologySidebar/TopologyActionBar.tsx @@ -5,9 +5,9 @@ import { ImTree } from "react-icons/im"; import { MdAlarmAdd, MdTableRows } from "react-icons/md"; import { useNavigate } from "react-router-dom"; import useUpdateComponentMutation from "../../api/query-hooks/mutations/useUpdateComponentMutation"; -import { EvidenceType } from "../../api/services/evidence"; +import { EvidenceType } from "../../api/types/evidence"; +import { Topology } from "../../api/types/topology"; import { useFeatureFlagsContext } from "../../context/FeatureFlagsContext"; -import { Topology } from "../../context/TopologyPageContext"; import { features } from "../../services/permissions/features"; import { ActionLink } from "../ActionLink/ActionLink"; import { AttachEvidenceDialog } from "../AttachEvidenceDialog"; diff --git a/src/components/TopologySidebar/TopologyCost.tsx b/src/components/TopologySidebar/TopologyCost.tsx index 6cb4d141f..49c55de18 100644 --- a/src/components/TopologySidebar/TopologyCost.tsx +++ b/src/components/TopologySidebar/TopologyCost.tsx @@ -1,4 +1,4 @@ -import { Topology } from "../../context/TopologyPageContext"; +import { Topology } from "../../api/types/topology"; import { CostInfoPanel } from "../CostDetails/CostDetails"; type Props = { diff --git a/src/components/TopologySidebar/TopologySidebar.tsx b/src/components/TopologySidebar/TopologySidebar.tsx index a374e131a..ff7f5439e 100644 --- a/src/components/TopologySidebar/TopologySidebar.tsx +++ b/src/components/TopologySidebar/TopologySidebar.tsx @@ -1,6 +1,5 @@ import { useCallback, useState } from "react"; import { useParams } from "react-router-dom"; -import { Topology } from "../../context/TopologyPageContext"; import { PlaybookRunsSidePanel } from "../Playbooks/Runs/PlaybookRunsSidePanel"; import Configs from "../Sidebars/configs"; import Incidents from "../Sidebars/incidents"; @@ -12,6 +11,7 @@ import TopologyActionBar from "./TopologyActionBar"; import TopologyCost from "./TopologyCost"; import TopologyInsights from "./TopologyInsights"; import { ComponentChecks } from "./ComponentChecks"; +import { Topology } from "../../api/types/topology"; type Props = { topology?: Topology; diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx index 8143f05ed..765809136 100644 --- a/src/components/UserList/index.tsx +++ b/src/components/UserList/index.tsx @@ -2,14 +2,14 @@ import { CellContext } from "@tanstack/table-core"; import clsx from "clsx"; import { useEffect, useMemo, useRef, useState } from "react"; import { BsTrash } from "react-icons/bs"; -import { User } from "../../api/services/users"; +import { User } from "../../api/types/users"; import { useUserAccessStateContext } from "../../context/UserAccessContext/UserAccessContext"; import { tables } from "../../context/UserAccessContext/permissions"; import { withAccessCheck } from "../AccessCheck/AccessCheck"; import { DataTable } from "../DataTable"; import { IconButton } from "../IconButton"; import { Menu } from "../Menu"; -import { Age } from "../UI/Age"; +import { Age } from "../../ui/Age"; type UserListProps = { data: any[]; diff --git a/src/constants/hypothesisStatusOptions.tsx b/src/constants/hypothesisStatusOptions.tsx index 9a90ebe95..f54f8ca3b 100644 --- a/src/constants/hypothesisStatusOptions.tsx +++ b/src/constants/hypothesisStatusOptions.tsx @@ -1,7 +1,7 @@ import { ThumbDownIcon, ThumbUpIcon } from "@heroicons/react/solid"; import clsx from "clsx"; import { ComponentProps } from "react"; -import { HypothesisStatus } from "../api/services/hypothesis"; +import { HypothesisStatus } from "../api/types/hypothesis"; const { Proven, Likely, Possible, Unlikely, Improbable, Disproven } = HypothesisStatus; diff --git a/src/constants/incidentPriority.ts b/src/constants/incidentPriority.ts deleted file mode 100644 index 9609a3bf5..000000000 --- a/src/constants/incidentPriority.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const IncidentPriority = { - Low: "Low", - Medium: "Medium", - High: "High", - Critical: "Critical", - Blocker: "Blocker" -}; diff --git a/src/constants/index.ts b/src/constants/index.ts index fda3c2c12..da67db401 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,5 +1,3 @@ -export { IncidentPriority } from "./incidentPriority"; - export const NodePodPropToLabelMap = { node: "Node", created: "Created At", diff --git a/src/context/ConfigPageContext.tsx b/src/context/ConfigPageContext.tsx index f8e361694..7acce5666 100644 --- a/src/context/ConfigPageContext.tsx +++ b/src/context/ConfigPageContext.tsx @@ -1,5 +1,5 @@ import React, { useState, createContext, useContext } from "react"; -import { ConfigItem } from "../api/services/configs"; +import { ConfigItem } from "../api/types/configs"; export type ConfigState = { data?: ConfigItem[]; diff --git a/src/context/HealthPageContext.tsx b/src/context/HealthPageContext.tsx index 46f3f64ad..f5e1d933b 100644 --- a/src/context/HealthPageContext.tsx +++ b/src/context/HealthPageContext.tsx @@ -6,7 +6,7 @@ import React, { SetStateAction } from "react"; import { URLSearchParamsInit } from "react-router-dom"; -import { HealthCheck } from "../types/healthChecks"; +import { HealthCheck } from "../api/types/health"; export type HealthState = { checks: HealthCheck[] | null; diff --git a/src/context/index.ts b/src/context/index.ts index 16a0e1dc5..7eb4f685d 100644 --- a/src/context/index.ts +++ b/src/context/index.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from "react"; -import { User } from "../api/services/users"; +import { User } from "../api/types/users"; interface IAuthContext { user: User; diff --git a/src/data/sampleConfigList.ts b/src/data/sampleConfigList.ts index e4e66d09f..f2315f454 100644 --- a/src/data/sampleConfigList.ts +++ b/src/data/sampleConfigList.ts @@ -1,4 +1,4 @@ -import { ConfigItem } from "../api/services/configs"; +import { ConfigItem } from "../api/types/configs"; function canonTags( tags: undefined | { Key: string; Value: string }[] | { [index: string]: any } diff --git a/src/data/sampleIncident.ts b/src/data/sampleIncident.ts index 714be186c..370937045 100644 --- a/src/data/sampleIncident.ts +++ b/src/data/sampleIncident.ts @@ -1,4 +1,4 @@ -import { IncidentSeverity } from "../api/services/incident"; +import { IncidentSeverity } from "../api/types/incident"; const sampleIncident = [ { diff --git a/src/hooks/useHypothesisStatusForm.ts b/src/hooks/useHypothesisStatusForm.ts index 27c0d4f41..0d1d60371 100644 --- a/src/hooks/useHypothesisStatusForm.ts +++ b/src/hooks/useHypothesisStatusForm.ts @@ -2,7 +2,7 @@ import { UseMutationResult } from "@tanstack/react-query"; import { debounce } from "lodash"; import { useEffect, useMemo } from "react"; import { useForm } from "react-hook-form"; -import { Hypothesis, HypothesisStatus } from "../api/services/hypothesis"; +import { Hypothesis, HypothesisStatus } from "../api/types/hypothesis"; interface Props { id: string; diff --git a/src/pages/TopologyPage.tsx b/src/pages/TopologyPage.tsx index 114e7f14b..2ed7ba105 100644 --- a/src/pages/TopologyPage.tsx +++ b/src/pages/TopologyPage.tsx @@ -19,7 +19,7 @@ import { getSortedTopology } from "../components/TopologyPopover/topologySort"; import TopologySidebar from "../components/TopologySidebar/TopologySidebar"; -import { Topology } from "../context/TopologyPageContext"; +import { Topology } from "../api/types/topology"; export const allOption = { All: { diff --git a/src/pages/UsersPage.tsx b/src/pages/UsersPage.tsx index b662afbe4..d8422cf24 100644 --- a/src/pages/UsersPage.tsx +++ b/src/pages/UsersPage.tsx @@ -4,7 +4,6 @@ import { getRegisteredUsers, inviteUser, deleteUser, - RegisteredUser, updateUserRole } from "../api/services/users"; import { Modal } from "../components"; @@ -26,6 +25,7 @@ import { } from "../components/ManageUserRoles/ManageUserRoles"; import { AccessCheck } from "../components/AccessCheck/AccessCheck"; import { tables } from "../context/UserAccessContext/permissions"; +import { RegisteredUser } from "../api/types/users"; export function UsersPage() { const [users, setUsers] = useState<RegisteredUser[]>([]); diff --git a/src/pages/config/ConfigDetailsPage.tsx b/src/pages/config/ConfigDetailsPage.tsx index 0b26adb7f..3305e33c7 100644 --- a/src/pages/config/ConfigDetailsPage.tsx +++ b/src/pages/config/ConfigDetailsPage.tsx @@ -80,7 +80,7 @@ export function ConfigDetailsPage() { }, []); const code = useMemo(() => { - if (!configDetails?.config) { + if (configDetails === null || !configDetails?.config) { return ""; } if (configDetails?.config?.content != null) { @@ -90,7 +90,7 @@ export function ConfigDetailsPage() { const ordered = Object.keys(configDetails.config) .sort() .reduce((obj: Record<string, any>, key) => { - obj[key] = configDetails.config[key]; + obj[key] = configDetails.config ? [key] : null; return obj; }, {}); @@ -99,7 +99,7 @@ export function ConfigDetailsPage() { const format = useMemo( () => - configDetails?.config.format != null + configDetails?.config?.format != null ? configDetails?.config.format : "json", [configDetails] diff --git a/src/pages/incident/IncidentDetails.tsx b/src/pages/incident/IncidentDetails.tsx index a9d703097..297f0c2c6 100644 --- a/src/pages/incident/IncidentDetails.tsx +++ b/src/pages/incident/IncidentDetails.tsx @@ -1,40 +1,37 @@ -import { useCallback, useMemo, useState } from "react"; import { UseMutationResult } from "@tanstack/react-query"; +import { useCallback, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import { EvidenceType } from "../../api/services/evidence"; +import { useCreateHypothesisMutation } from "../../api/mutations/useCreateHypothesisMutation"; +import { useUpdateHypothesisMutation } from "../../api/mutations/useUpdateHypothesisMutation"; +import { useCreateCommentMutation } from "../../api/query-hooks/mutations/comment"; import { createHypothesis, deleteHypothesis, deleteHypothesisBulk, - Hypothesis, updateHypothesis } from "../../api/services/hypothesis"; +import { updateIncident } from "../../api/services/incident"; +import { EvidenceType } from "../../api/types/evidence"; +import { Hypothesis } from "../../api/types/hypothesis"; +import { Incident, IncidentStatus } from "../../api/types/incident"; import { - Incident, - IncidentStatus, - updateIncident -} from "../../api/services/incident"; + BreadcrumbChild, + BreadcrumbNav, + BreadcrumbRoot +} from "../../components/BreadcrumbNav"; +import EmptyState from "../../components/EmptyState"; +import { Head } from "../../components/Head/Head"; +import { HypothesisActionPlanViewContainer } from "../../components/Hypothesis/HypothesisActionPlanViewContainer/HypothesisActionPlanViewContainer"; import { HypothesisBuilder } from "../../components/Hypothesis/HypothesisBuilder"; +import { HypothesisCommentsViewContainer } from "../../components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer"; +import EditableIncidentTitleBreadcrumb from "../../components/IncidentDetails/EditableIncidentTitleBreadcrumb"; import { IncidentSidebar } from "../../components/IncidentDetails/IncidentSidebar"; import { SearchLayout } from "../../components/Layout"; -import { useCreateHypothesisMutation } from "../../api/mutations/useCreateHypothesisMutation"; -import { useUpdateHypothesisMutation } from "../../api/mutations/useUpdateHypothesisMutation"; -import { TopologyCard } from "../../components/TopologyCard"; -import { Size } from "../../types"; import IncidentDetailsPageSkeletonLoader from "../../components/SkeletonLoader/IncidentDetailsPageSkeletonLoader"; -import { Head } from "../../components/Head/Head"; -import { HypothesisCommentsViewContainer } from "../../components/Hypothesis/HypothesisCommentsViewContainer/HypothesisCommentsViewContainer"; -import { HypothesisActionPlanViewContainer } from "../../components/Hypothesis/HypothesisActionPlanViewContainer/HypothesisActionPlanViewContainer"; import { Tab, Tabs } from "../../components/Tabs/Tabs"; -import EmptyState from "../../components/EmptyState"; -import { useCreateCommentMutation } from "../../api/query-hooks/mutations/comment"; +import { TopologyCard } from "../../components/TopologyCard"; import { useIncidentState } from "../../store/incident.state"; -import { - BreadcrumbChild, - BreadcrumbNav, - BreadcrumbRoot -} from "../../components/BreadcrumbNav"; -import EditableIncidentTitleBreadcrumb from "../../components/IncidentDetails/EditableIncidentTitleBreadcrumb"; +import { Size } from "../../types"; export enum IncidentDetailsViewTypes { comments = "Comments", diff --git a/src/store/incident.state.ts b/src/store/incident.state.ts index 8270aa3f0..143d8070f 100644 --- a/src/store/incident.state.ts +++ b/src/store/incident.state.ts @@ -1,7 +1,7 @@ import { atom, useAtomValue, useSetAtom } from "jotai"; import { useEffect } from "react"; import { useIncidentQuery } from "../api/query-hooks"; -import { Incident } from "../api/services/incident"; +import { Incident } from "../api/types/incident"; export const viewedIncidentAtom = atom<Incident | null>(null); diff --git a/src/components/UI/Age/Age.tsx b/src/ui/Age/Age.tsx similarity index 85% rename from src/components/UI/Age/Age.tsx rename to src/ui/Age/Age.tsx index d0a5b6a16..ccf0ab0b9 100644 --- a/src/components/UI/Age/Age.tsx +++ b/src/ui/Age/Age.tsx @@ -2,13 +2,15 @@ import dayjs from "dayjs"; import clsx from "clsx"; export default function Age({ - className = "text-sm", + className = "", from, - to + to, + suffix = false }: { className?: string; from?: Date | string; to?: Date | string; + suffix?: boolean; }) { if ( from === "" || @@ -22,7 +24,7 @@ export default function Age({ if (to == null) { return ( <span title={_from.format()} className={className}> - {_from.local().fromNow(true)} + {_from.local().fromNow(!suffix)} </span> ); } diff --git a/src/components/UI/Age/index.tsx b/src/ui/Age/index.tsx similarity index 100% rename from src/components/UI/Age/index.tsx rename to src/ui/Age/index.tsx diff --git a/src/ui/table/DateCells.tsx b/src/ui/table/DateCells.tsx new file mode 100644 index 000000000..8b64b9643 --- /dev/null +++ b/src/ui/table/DateCells.tsx @@ -0,0 +1,51 @@ +import { CellContext } from "@tanstack/react-table"; +import { Age } from "../Age"; +import { CreatedAt, UpdatedAt, Deletable } from "../../api/traits"; + +export function CreatedAtCell<T extends CreatedAt>({ + row, + column, + getValue +}: CellContext<T, unknown>) { + return ( + <Age + className="whitespace-nowrap text-xs text-slate-500 pr-2" + from={row.original.created_at} + /> + ); +} + +export function UpdatedAtCell<T extends UpdatedAt>({ + row, + column, + getValue +}: CellContext<T, unknown>) { + return ( + <Age + className="whitespace-nowrap text-xs text-slate-500 pr-2" + from={row.original.updated_at} + /> + ); +} + +export function DeletedAtCell<T extends Deletable>({ + row, + column, + getValue +}: CellContext<T, unknown>) { + return ( + <Age + className="whitespace-nowrap text-xs text-slate-500 pr-2" + from={row.original.deleted_at} + /> + ); +} + +export function DateCell({ row, column }: CellContext<any, any>) { + const dateString = row?.getValue<string>(column.id); + return ( + <div className="text-xs"> + <Age from={dateString} /> + </div> + ); +} diff --git a/src/ui/table/TagCell.tsx b/src/ui/table/TagCell.tsx new file mode 100644 index 000000000..0f29ae283 --- /dev/null +++ b/src/ui/table/TagCell.tsx @@ -0,0 +1,22 @@ +import { CellContext } from "@tanstack/react-table"; + +export function TagsCell({ row, column }: CellContext<any, any>): JSX.Element { + const tags = row?.getValue<any[]>(column.id); + + return ( + <div className="flex"> + {tags?.length > 0 ? ( + tags?.map((tag) => ( + <div + className="bg-gray-200 px-1 py-0.5 mr-1 rounded-md text-gray-600 font-semibold text-xs" + key={tag} + > + {tag} + </div> + )) + ) : ( + <span className="text-gray-400"></span> + )} + </div> + ); +} diff --git a/src/ui/table/User.tsx b/src/ui/table/User.tsx new file mode 100644 index 000000000..251089cc5 --- /dev/null +++ b/src/ui/table/User.tsx @@ -0,0 +1,20 @@ +import { CellContext } from "@tanstack/react-table"; +import { User } from "../../api/types/users"; +import { Avatar } from "../../components/Avatar"; + +export function UserCell<T extends User>({ + row, + column, + getValue +}: CellContext<T, unknown>) { + return <Avatar user={row.original} circular />; +} + +export function AvatarCell({ + row, + column, + getValue +}: CellContext<unknown, unknown>) { + const user = getValue<User>(); + return <Avatar user={user} circular />; +} diff --git a/src/ui/table/index.tsx b/src/ui/table/index.tsx new file mode 100644 index 000000000..fb7c0b533 --- /dev/null +++ b/src/ui/table/index.tsx @@ -0,0 +1,18 @@ +import { + DateCell, + CreatedAtCell, + UpdatedAtCell, + DeletedAtCell +} from "./DateCells"; +import { TagsCell } from "./TagCell"; +import { UserCell, AvatarCell } from "./User"; + +export { + DateCell, + TagsCell, + CreatedAtCell, + DeletedAtCell, + UpdatedAtCell, + UserCell, + AvatarCell +}; diff --git a/src/utils/date.ts b/src/utils/date.ts index 9ca957b47..ec49bd306 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -7,8 +7,8 @@ import relativeTime from "dayjs/plugin/relativeTime"; import updateLocale from "dayjs/plugin/updateLocale"; import utc from "dayjs/plugin/utc"; -import { ValueType } from "../context/TopologyPageContext"; import { SortOrders } from "../constants"; +import { ValueType } from "../api/types/common"; dayjs.extend(isBetween); dayjs.extend(isToday); @@ -118,6 +118,17 @@ export const formatTimeRange = (date: string | Date) => { }); }; +export const age = (from: string | Date) => { + if ( + from === "" || + from == null || + dayjs(from).isSame(dayjs("0001-01-01T00:00:00+00:00")) + ) { + return ""; + } + return dayjs.utc(from).local().fromNow(true); +}; + /** * Parse the given date relative to the user locale * @@ -206,9 +217,10 @@ export const formatDateToTime = (date: string | Date) => { export const dateSortHelper = ( order: string, - dateOne: string, - dateTwo: string + dateOne?: string | Date, + dateTwo?: string | Date ) => { + if (dateOne === undefined || dateTwo === undefined) return 0; const value1 = +new Date(dateOne); const value2 = +new Date(dateTwo); if (value1 > value2) { From 1efc244d474ec32fe3486aa8f03ff1499a40c9c7 Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Tue, 24 Oct 2023 23:34:24 +0300 Subject: [PATCH 04/15] feat: condense insights and changes sidebars --- pages/global.css | 18 +++++- .../Cells/ConfigChangeAgeCell.tsx | 16 ----- .../ConfigDetailsChanges.tsx | 2 +- .../Configs/Sidebar/ConfigSidebar.tsx | 12 ++-- src/components/CostDetails/CostDetails.tsx | 2 +- src/components/DataTable/index.tsx | 33 +++++------ .../InfiniteTable/InfiniteTable.tsx | 14 ++--- src/components/Insights/Insights.tsx | 1 - .../Insights/cells/ConfigInsightNameCell.tsx | 2 +- .../Cells/ConfigChangeNameCell.tsx | 6 +- .../{ => Sidebars}/ConfigChanges/index.tsx | 59 +++++++------------ .../{ => Sidebars}/ConfigInsights/index.tsx | 10 ++-- .../TopologyConfigChanges/index.tsx | 15 ++--- 13 files changed, 81 insertions(+), 109 deletions(-) delete mode 100644 src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx rename src/components/{ => Sidebars}/ConfigChanges/Cells/ConfigChangeNameCell.tsx (69%) rename src/components/{ => Sidebars}/ConfigChanges/index.tsx (65%) rename src/components/{ => Sidebars}/ConfigInsights/index.tsx (77%) diff --git a/pages/global.css b/pages/global.css index 66681fe55..196d4e90f 100644 --- a/pages/global.css +++ b/pages/global.css @@ -36,12 +36,16 @@ @layer components { .table-auto { - @apply shadow-lg bg-white rounded-md py-2 px-4; + @apply shadow-lg rounded-md py-2 px-4; } + .table-auto tbody tr { + @apply hover:bg-slate-50; + } + .table-auto thead tr { border-bottom-width: 1px; - @apply border-gray-200 uppercase text-gray-600 font-bold text-sm items-center text-left px-4 py-4; + @apply border-gray-200 bg-white text-gray-600 font-bold text-sm items-center text-left px-4 py-1; } .table-auto thead tr th { @@ -53,6 +57,16 @@ @apply px-4; } + .table-condensed thead tr th { + @apply px-2 py-1 text-xs; + } + + .table-condensed td { + border-bottom-width: 1px; + @apply px-1 text-xs; + + } + .btn-primary { @apply inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blue-500; } diff --git a/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx b/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx deleted file mode 100644 index f4474c7d3..000000000 --- a/src/components/ConfigChanges/Cells/ConfigChangeAgeCell.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { CellContext } from "@tanstack/react-table"; -import { Age } from "../../../ui/Age"; -import { ConfigChange } from "../../../api/types/configs"; - -export default function ConfigChangeAgeCell({ - row, - column, - getValue -}: CellContext<ConfigChange, unknown>) { - return ( - <Age - className="whitespace-nowrap text-xs text-slate-500 pr-2" - from={row.original.created_at} - /> - ); -} diff --git a/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx b/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx index a4e3dd670..ff0118ecf 100644 --- a/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx +++ b/src/components/ConfigDetailsChanges/ConfigDetailsChanges.tsx @@ -214,7 +214,7 @@ export function ConfigDetailChangeModal({ setOpen(false); }} size="full" - bodyClass="flex h-full flex-col flex-1 overflow-y-auto" + bodyClass="flex h-full w-full flex-col flex-1 overflow-y-auto" containerClassName="min-h-[15rem] h-auto max-h-full overflow-y-auto" > <div className="flex flex-col flex-1 overflow-y-auto"> diff --git a/src/components/Configs/Sidebar/ConfigSidebar.tsx b/src/components/Configs/Sidebar/ConfigSidebar.tsx index eb7ef7e99..dc884a8bb 100644 --- a/src/components/Configs/Sidebar/ConfigSidebar.tsx +++ b/src/components/Configs/Sidebar/ConfigSidebar.tsx @@ -1,10 +1,10 @@ import { useParams } from "react-router-dom"; -import ConfigChanges from "../../ConfigChanges"; -import ConfigCosts from "../../ConfigCosts"; -import ConfigInsights from "../../ConfigInsights"; -import Configs from "../../Sidebars/configs"; -import Incidents from "../../Sidebars/incidents"; -import SlidingSideBar from "../../SlidingSideBar"; +import ConfigChanges from "../Sidebars/ConfigChanges"; +import ConfigCosts from "../ConfigCosts"; +import ConfigInsights from "../Sidebars/ConfigInsights"; +import Configs from "../Sidebars/configs"; +import Incidents from "../Sidebars/incidents"; +import SlidingSideBar from "../SlidingSideBar"; import { ConfigDetails } from "./ConfigDetails"; import ConfigActionBar from "./ConfigActionBar"; import { useCallback, useState } from "react"; diff --git a/src/components/CostDetails/CostDetails.tsx b/src/components/CostDetails/CostDetails.tsx index 93d63b8f4..12fea069b 100644 --- a/src/components/CostDetails/CostDetails.tsx +++ b/src/components/CostDetails/CostDetails.tsx @@ -70,7 +70,7 @@ export function CostInfo({ label, value, defaultValue }: CostInfoProps) { return ( <div className="overflow-hidden flex flex-col flex-1 space-y-2 px-2"> <div className="text-gray-500 text-sm whitespace-nowrap">{label}</div> - <div className="text-black text-sm font-semibold"> + <div className="text-black font-semibold"> <FormatCurrency value={value} defaultValue={defaultValue} /> </div> </div> diff --git a/src/components/DataTable/index.tsx b/src/components/DataTable/index.tsx index 90f672cb5..a406ff7dc 100644 --- a/src/components/DataTable/index.tsx +++ b/src/components/DataTable/index.tsx @@ -23,13 +23,6 @@ import { Pagination, PaginationType } from "./Pagination/Pagination"; import TableSkeletonLoader from "../SkeletonLoader/TableSkeletonLoader"; import usePreferences from "../../hooks/userPreferences"; -const tableStyles = { - theadHeaderClass: - "px-3 py-3 text-left text-gray-500 font-medium text-xs tracking-wider", - tbodyRowClass: "cursor-pointer text-sm", - tbodyDataClass: "whitespace-nowrap p-2" -}; - export type PaginationOptions = { setPagination: any; pageIndex: number; @@ -49,6 +42,9 @@ type DataTableProps<TableColumns, Data extends TableColumns> = { isLoading?: boolean; groupBy?: string[]; hiddenColumns?: string[]; + theadHeaderClass?: string; + tbodyRowClass?: string; + tbodyDataClass?: string; className?: string; isVirtualized?: boolean; virtualizedRowEstimatedHeight?: number; @@ -118,7 +114,10 @@ export function DataTable<TableColumns, Data extends TableColumns>({ isLoading, groupBy, hiddenColumns, - className, + className = "table-auto table-fixed", + theadHeaderClass = "px-3 py-3 text-left text-gray-500 font-medium text-xs tracking-wider", + tbodyRowClass = "cursor-pointer text-sm", + tbodyDataClass = "whitespace-nowrap p-2", isVirtualized = false, virtualizedRowEstimatedHeight = 35, tableSortByState, @@ -255,7 +254,7 @@ export function DataTable<TableColumns, Data extends TableColumns>({ }, [rows.length, virtualRows, pagination, paginationType, table]); return ( - <div className="flex flex-col flex-1 overflow-y-auto space-y-2 h-full"> + <div className="flex flex-col flex-1 overflow-y-auto space-y-2 w-full h-full"> <div ref={tableContainerRef} className={clsx("flex flex-col flex-1 overflow-y-auto", className)} @@ -268,7 +267,7 @@ export function DataTable<TableColumns, Data extends TableColumns>({ className={clsx( // for some reason, it seems to need both auto and fixed, there may be // some other css class tied to auto - `table-auto table-fixed w-full border border-gray-200 rounded-md`, + `w-full border border-gray-200 rounded-md`, stickyHead && "relative" )} style={tableStyle} @@ -291,7 +290,7 @@ export function DataTable<TableColumns, Data extends TableColumns>({ colIndex === 1 ? null : ( <th key={header.id} - className={`${tableStyles.theadHeaderClass}${ + className={`${theadHeaderClass}${ header.column.getCanSort() ? " cursor-pointer" : "" }`} onClick={header.column.getToggleSortingHandler()} @@ -343,10 +342,10 @@ export function DataTable<TableColumns, Data extends TableColumns>({ return ( <DataTableRow row={row} - cellClassNames={tableStyles.tbodyDataClass} + cellClassNames={tbodyDataClass} onRowClick={handleRowClick} isGrouped={isGrouped} - rowClassNames={tableStyles.tbodyRowClass} + rowClassNames={tbodyRowClass} key={row.id} /> ); @@ -354,12 +353,12 @@ export function DataTable<TableColumns, Data extends TableColumns>({ : rows.map((row) => ( <DataTableRow row={row} - cellClassNames={tableStyles.tbodyDataClass} + cellClassNames={tbodyDataClass} onRowClick={handleRowClick} isGrouped={isGrouped} - rowClassNames={`${ - tableStyles.tbodyRowClass - } ${determineRowClassNamesCallback(row)}`} + rowClassNames={`${tbodyRowClass} ${determineRowClassNamesCallback( + row + )}`} key={row.id} /> ))} diff --git a/src/components/InfiniteTable/InfiniteTable.tsx b/src/components/InfiniteTable/InfiniteTable.tsx index 0725dfdb4..73974a4e9 100644 --- a/src/components/InfiniteTable/InfiniteTable.tsx +++ b/src/components/InfiniteTable/InfiniteTable.tsx @@ -13,6 +13,7 @@ import React, { useCallback, useMemo, useRef } from "react"; type InfiniteTableProps<T> = React.HTMLProps<HTMLDivElement> & { columns: ColumnDef<T, any>[]; isFetching: boolean; + className?: string; isLoading: boolean; totalEntries: number; allRows: T[]; @@ -31,6 +32,7 @@ export function InfiniteTable<T>({ isLoading, totalEntries, allRows, + className = "table-auto table-condensed", fetchNextPage, maxHeight, loaderView, @@ -102,18 +104,16 @@ export function InfiniteTable<T>({ ref={tableContainerRef} style={containerStyle} > - <table - className={clsx(`table-auto table-fixed p-0`, stickyHead && "relative")} - > - <thead className={`bg-white ${stickyHead ? "sticky top-0 z-1" : ""}`}> + <table className={clsx(`p-0 mr-2`, stickyHead && "relative", className)}> + <thead className={`${stickyHead ? "sticky top-0 " : ""}`}> {table.getHeaderGroups().map((headerGroup) => ( - <tr className="" key={headerGroup.id}> + <tr key={headerGroup.id}> {headerGroup.headers.map((header) => { return ( <th key={header.id} colSpan={header.colSpan} - className={clsx(columnsClassName?.[header.id], "py-0")} + className={clsx(columnsClassName?.[header.id], "py-0 ")} > {header.isPlaceholder ? null : ( <div @@ -152,7 +152,7 @@ export function InfiniteTable<T>({ key={cell.id} className={clsx( columnsClassName?.[cell.column.id], - "py-1" + "py-0.5 px-0.5" )} > {flexRender( diff --git a/src/components/Insights/Insights.tsx b/src/components/Insights/Insights.tsx index 9f07deca5..7a83ac8a2 100644 --- a/src/components/Insights/Insights.tsx +++ b/src/components/Insights/Insights.tsx @@ -163,7 +163,6 @@ export default function InsightsDetails( }); } }} - stickyHead virtualizedRowEstimatedHeight={40} columnsClassName={{ change_type: "", diff --git a/src/components/Insights/cells/ConfigInsightNameCell.tsx b/src/components/Insights/cells/ConfigInsightNameCell.tsx index 7fb712067..97245d7f4 100644 --- a/src/components/Insights/cells/ConfigInsightNameCell.tsx +++ b/src/components/Insights/cells/ConfigInsightNameCell.tsx @@ -25,7 +25,7 @@ export default function ConfigInsightNameCell({ <div data-html={true} data-class="max-w-[20rem]" - className="py-2 text-black overflow-hidden cursor-pointer flex-1 text-sm" + className="overflow-hidden cursor-pointer flex-1" > <ConfigAnalysisLink key={insight.id} configAnalysis={insight} /> </div> diff --git a/src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx b/src/components/Sidebars/ConfigChanges/Cells/ConfigChangeNameCell.tsx similarity index 69% rename from src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx rename to src/components/Sidebars/ConfigChanges/Cells/ConfigChangeNameCell.tsx index deb205016..36f59f2d6 100644 --- a/src/components/ConfigChanges/Cells/ConfigChangeNameCell.tsx +++ b/src/components/Sidebars/ConfigChanges/Cells/ConfigChangeNameCell.tsx @@ -1,7 +1,7 @@ import { CellContext } from "@tanstack/react-table"; -import { ViewType } from "../../../types"; -import { ConfigDetailsChanges } from "../../ConfigDetailsChanges/ConfigDetailsChanges"; -import { ConfigChange } from "../../../api/types/configs"; +import { ViewType } from "../../../../types"; +import { ConfigDetailsChanges } from "../../../ConfigDetailsChanges/ConfigDetailsChanges"; +import { ConfigChange } from "../../../../api/types/configs"; export default function ConfigChangeNameCell({ row, diff --git a/src/components/ConfigChanges/index.tsx b/src/components/Sidebars/ConfigChanges/index.tsx similarity index 65% rename from src/components/ConfigChanges/index.tsx rename to src/components/Sidebars/ConfigChanges/index.tsx index 238c98814..fa9244a71 100644 --- a/src/components/ConfigChanges/index.tsx +++ b/src/components/Sidebars/ConfigChanges/index.tsx @@ -1,38 +1,17 @@ import { ColumnDef } from "@tanstack/table-core"; import { useEffect, useMemo, useState } from "react"; -import { GoDiff } from "react-icons/go"; -import { useGetConfigChangesByConfigIdQuery } from "../../api/query-hooks"; -import { ConfigItem } from "../../api/services/configs"; -import { User } from "../../api/services/users"; -import PillBadge from "../Badge/PillBadge"; -import CollapsiblePanel from "../CollapsiblePanel"; -import EmptyState from "../EmptyState"; -import Title from "../Title/title"; -import { toastError } from "../Toast/toast"; -import ConfigChangeAgeCell from "./Cells/ConfigChangeAgeCell"; +import { useGetConfigChangesByConfigIdQuery } from "../../../api/query-hooks"; +import { ConfigChange } from "../../../api/types/configs"; +import { CreatedAtCell } from "../../../ui/table/DateCells"; +import PillBadge from "../../Badge/PillBadge"; +import CollapsiblePanel from "../../CollapsiblePanel"; +import EmptyState from "../../EmptyState"; +import { Icon } from "../../Icon"; +import { InfiniteTable } from "../../InfiniteTable/InfiniteTable"; +import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader"; +import Title from "../../Title/title"; +import { toastError } from "../../Toast/toast"; import ConfigChangeNameCell from "./Cells/ConfigChangeNameCell"; -import { InfiniteTable } from "../InfiniteTable/InfiniteTable"; -import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; - -export type ConfigTypeChanges = { - id: string; - config_id: string; - external_change_id: string; - change_type: string; - severity: string; - source: string; - summary: string; - patches?: string; - diff?: string; - details: string; - created_at: string; - created_by: string | User; - external_created_by: string; - config?: ConfigItem; - config_class?: string; - type?: string; - name?: string; -}; type Props = { configID: string; @@ -40,7 +19,7 @@ type Props = { onCollapsedStateChange?: (isClosed: boolean) => void; }; -export const columns: ColumnDef<ConfigTypeChanges, any>[] = [ +export const columns: ColumnDef<ConfigChange, any>[] = [ { header: "Name", id: "change_type", @@ -53,7 +32,7 @@ export const columns: ColumnDef<ConfigTypeChanges, any>[] = [ header: "Age", id: "created_at", accessorKey: "created_at", - cell: ConfigChangeAgeCell, + cell: CreatedAtCell, enableGrouping: true, enableSorting: true } @@ -64,7 +43,7 @@ export function ConfigChangesDetails({ configID }: Props) { pageIndex: 0, pageSize: 50 }); - const [changes, setChanges] = useState<ConfigTypeChanges[]>([]); + const [changes, setChanges] = useState<ConfigChange[]>([]); const { data: response, @@ -99,8 +78,9 @@ export function ConfigChangesDetails({ configID }: Props) { return ( <div className="flex flex-col overflow-y-hidden"> - <InfiniteTable<ConfigTypeChanges> + <InfiniteTable<ConfigChange> columns={columns} + // className="border-none" isLoading={isLoading && !isFetching} isFetching={isFetching} allRows={changes} @@ -114,10 +94,8 @@ export function ConfigChangesDetails({ configID }: Props) { }); } }} - stickyHead virtualizedRowEstimatedHeight={40} columnsClassName={{ - change_type: "flex-1 w-[100px]", created_at: "text-right" }} /> @@ -143,7 +121,10 @@ export default function ConfigChanges({ onCollapsedStateChange={onCollapsedStateChange} Header={ <div className="flex flex-row w-full items-center space-x-2"> - <Title title="Changes" icon={<GoDiff className="w-6 h-auto" />} /> + <Title + title="Changes" + icon={<Icon name="diff" className="w-6 h-auto opacity-40" />} + /> <PillBadge>{count}</PillBadge> </div> } diff --git a/src/components/ConfigInsights/index.tsx b/src/components/Sidebars/ConfigInsights/index.tsx similarity index 77% rename from src/components/ConfigInsights/index.tsx rename to src/components/Sidebars/ConfigInsights/index.tsx index 182413496..f070d8c7b 100644 --- a/src/components/ConfigInsights/index.tsx +++ b/src/components/Sidebars/ConfigInsights/index.tsx @@ -1,9 +1,9 @@ import { MdOutlineInsights } from "react-icons/md"; -import { useGetConfigInsights } from "../../api/query-hooks"; -import CollapsiblePanel from "../CollapsiblePanel"; -import InsightsDetails from "../Insights/Insights"; -import Title from "../Title/title"; -import PillBadge from "../Badge/PillBadge"; +import { useGetConfigInsights } from "../../../api/query-hooks"; +import CollapsiblePanel from "../../CollapsiblePanel"; +import InsightsDetails from "../../Insights/Insights"; +import Title from "../../Title/title"; +import PillBadge from "../../Badge/PillBadge"; type Props = { configID: string; diff --git a/src/components/TopologyConfigChanges/index.tsx b/src/components/TopologyConfigChanges/index.tsx index 71ea7512a..c446961ab 100644 --- a/src/components/TopologyConfigChanges/index.tsx +++ b/src/components/TopologyConfigChanges/index.tsx @@ -3,9 +3,10 @@ import { useAtom } from "jotai"; import { useEffect, useState } from "react"; import { GoDiff } from "react-icons/go"; import { useComponentConfigChanges } from "../../api/query-hooks/useComponentConfigChanges"; +import { useGetConfigChangesById } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; +import { ConfigChange } from "../../api/types/configs"; import PillBadge from "../Badge/PillBadge"; import CollapsiblePanel from "../CollapsiblePanel"; -import { ConfigTypeChanges } from "../ConfigChanges"; import { ConfigDetailChangeModal } from "../ConfigDetailsChanges/ConfigDetailsChanges"; import ConfigLink from "../ConfigLink/ConfigLink"; import EmptyState from "../EmptyState"; @@ -13,7 +14,6 @@ import { Icon } from "../Icon"; import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; import { refreshButtonClickedTrigger } from "../SlidingSideBar"; import Title from "../Title/title"; -import { useGetConfigChangesByConfigChangeIdQuery } from "../../api/query-hooks/useGetConfigChangesByConfigChangeIdQuery"; type Props = { topologyID: string; @@ -29,10 +29,10 @@ export function TopologyConfigChanges({ topologyID }: Props) { const [selectedConfigChange, setSelectedConfigChanges] = useState< - Pick<ConfigTypeChanges, "change_type" | "id" | "config_id" | "config"> + Pick<ConfigChange, "change_type" | "id" | "config_id" | "config"> >(); - const { data: changeDetails } = useGetConfigChangesByConfigChangeIdQuery( + const { data: changeDetails } = useGetConfigChangesById( selectedConfigChange?.id!, selectedConfigChange?.config_id!, {} @@ -48,12 +48,7 @@ export function TopologyConfigChanges({ topologyID }: Props) { ) : componentConfigChanges.length > 0 ? ( componentConfigChanges.map((item) => ( <div className="flex flex-row text-sm mb-2"> - <ConfigLink - configId={item.config_id} - configName={item.config?.name!} - configType={item.type} - configTypeSecondary={item.config_class} - /> + <ConfigLink config={item.config} />  /  <span role="button" From f25a7ed2fe72b819bb35ef878b1fb012b5aeab22 Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Tue, 24 Oct 2023 23:40:05 +0300 Subject: [PATCH 05/15] chore: update icons merge --- .../Configs/Sidebar/ConfigActionBar.tsx | 12 ++++---- src/components/HealthSummary/summary.tsx | 2 +- src/components/Icon/index.tsx | 6 ++-- src/icons/index.ts | 12 ++++++++ src/icons/input/acr.svg | 1 + src/icons/k6.svg | 4 +++ src/icons/opensearch.svg | 6 ++++ src/icons/opensearch_logo.svg | 7 +++++ src/icons/playwright.svg | 10 +++++++ src/icons/postman.svg | 28 +++++++++++++++++++ src/icons/powershell.svg | 1 + 11 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 src/icons/input/acr.svg create mode 100644 src/icons/k6.svg create mode 100644 src/icons/opensearch.svg create mode 100644 src/icons/opensearch_logo.svg create mode 100644 src/icons/playwright.svg create mode 100644 src/icons/postman.svg create mode 100644 src/icons/powershell.svg diff --git a/src/components/Configs/Sidebar/ConfigActionBar.tsx b/src/components/Configs/Sidebar/ConfigActionBar.tsx index 84320bf20..abfef99d0 100644 --- a/src/components/Configs/Sidebar/ConfigActionBar.tsx +++ b/src/components/Configs/Sidebar/ConfigActionBar.tsx @@ -1,12 +1,12 @@ import clsx from "clsx"; import React, { useEffect, useMemo, useState } from "react"; import { MdAlarmAdd } from "react-icons/md"; -import { useGetConfigByIdQuery } from "../../api/query-hooks"; -import { usePartialUpdateSearchParams } from "../../hooks/usePartialUpdateSearchParams"; -import { ActionLink } from "../ActionLink/ActionLink"; -import AttachAsEvidenceButton from "../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; -import SelectPlaybookToRun from "../Playbooks/Runs/Submit/SelectPlaybookToRun"; -import { EvidenceType } from "../../api/types/evidence"; +import { useGetConfigByIdQuery } from "../../../api/query-hooks"; +import { usePartialUpdateSearchParams } from "../../../hooks/usePartialUpdateSearchParams"; +import { ActionLink } from "../../ActionLink/ActionLink"; +import AttachAsEvidenceButton from "../../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; +import SelectPlaybookToRun from "../../Playbooks/Runs/Submit/SelectPlaybookToRun"; +import { EvidenceType } from "../../../api/types/evidence"; type ConfigActionBarProps = { configId: string; diff --git a/src/components/HealthSummary/summary.tsx b/src/components/HealthSummary/summary.tsx index 49a92b3f9..b968ba424 100644 --- a/src/components/HealthSummary/summary.tsx +++ b/src/components/HealthSummary/summary.tsx @@ -4,7 +4,7 @@ import { StatusLine, StatusLineProps } from "../StatusLine/StatusLine"; -import { Topology } from "../../context/TopologyPageContext"; +import { Topology } from "../../api/types/topology"; type HealthSummaryProps = { component: Topology; diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index a425499ac..790f06a45 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -4,6 +4,8 @@ import React, { memo } from "react"; type IconMap = Record<string, string>; const aliases: IconMap = { + "aws-cloudwatch-alarm": "aws-cloudwatch", + "aws-config-alarm": "aws-config", "aws--account": "aws", "aws-ec2-dhcpoptions": "settings", "aws-ec2-securitygroup": "firewall", @@ -476,8 +478,6 @@ function findByName(name?: string) { return icon; } } - console.log(name, icon == null); - return icon; } @@ -513,7 +513,7 @@ export const Icon: React.FC<IconProps> = memo( } if (icon == null) { - console.warn("Icon not found: " + name); + console.log("Icon not found: " + name); return null; } diff --git a/src/icons/index.ts b/src/icons/index.ts index 70bc2a749..d4dcb2bb2 100644 --- a/src/icons/index.ts +++ b/src/icons/index.ts @@ -262,6 +262,7 @@ import js from "./js.svg"; import json from "./json.svg"; import junit from "./junit.svg"; import jwt from "./jwt.svg"; +import k6 from "./k6.svg"; import k8s_clusterrole from "./k8s-clusterrole.svg"; import k8s_clusterrolebinding from "./k8s-clusterrolebinding.svg"; import k8s_configmap from "./k8s-configmap.svg"; @@ -347,6 +348,8 @@ import ok from "./ok.svg"; import on from "./on.svg"; import opa from "./opa.svg"; import openid from "./openid.svg"; +import opensearch from "./opensearch.svg"; +import opensearch_logo from "./opensearch_logo.svg"; import openshift from "./openshift.svg"; import opentelemetry from "./opentelemetry.svg"; import operatorframework from "./operatorframework.svg"; @@ -365,11 +368,14 @@ import pagerduty from "./pagerduty.svg"; import pause from "./pause.svg"; import php from "./php.svg"; import ping from "./ping.svg"; +import playwright from "./playwright.svg"; import plus from "./plus.svg"; import pod from "./pod.svg"; import pods from "./pods.svg"; import pom from "./pom.svg"; import postgres from "./postgres.svg"; +import postman from "./postman.svg"; +import powershell from "./powershell.svg"; import prometheus from "./prometheus.svg"; import pushbullet from "./pushbullet.svg"; import pushover from "./pushover.svg"; @@ -762,6 +768,7 @@ export const Icons = { json: json, junit: junit, jwt: jwt, + k6: k6, "k8s-clusterrole": k8s_clusterrole, "k8s-clusterrolebinding": k8s_clusterrolebinding, "k8s-configmap": k8s_configmap, @@ -847,6 +854,8 @@ export const Icons = { on: on, opa: opa, openid: openid, + opensearch: opensearch, + opensearch_logo: opensearch_logo, openshift: openshift, opentelemetry: opentelemetry, operatorframework: operatorframework, @@ -865,11 +874,14 @@ export const Icons = { pause: pause, php: php, ping: ping, + playwright: playwright, plus: plus, pod: pod, pods: pods, pom: pom, postgres: postgres, + postman: postman, + powershell: powershell, prometheus: prometheus, pushbullet: pushbullet, pushover: pushover, diff --git a/src/icons/input/acr.svg b/src/icons/input/acr.svg new file mode 100644 index 000000000..4870f54eb --- /dev/null +++ b/src/icons/input/acr.svg @@ -0,0 +1 @@ +<svg id="e9f7ba73-56b8-4864-96ff-dce80bef729a" xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><defs><linearGradient id="bdf27dc6-025c-4805-8bfd-3a05defd3fbd" x1="8.637" y1="-1.991" x2="8.637" y2="16.739" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5ea0ef" /><stop offset="1" stop-color="#0078d4" /></linearGradient><linearGradient id="fcd6c6c7-e2eb-4a32-b914-3a8485585a1b" x1="12.96" y1="8.561" x2="12.96" y2="6.141" gradientTransform="matrix(1, 0, 0, -1, 0, 20)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#333132" /><stop offset="1" stop-color="#5b5a5c" /></linearGradient></defs><path d="M7.43,8.178l2.589-1.573,7.255,2.731A3.664,3.664,0,0,0,16.23,7.49l-.01-.05A4.194,4.194,0,0,0,14,6.32a4.91,4.91,0,0,0-5.1-4.7A5.071,5.071,0,0,0,4.06,4.91,4.621,4.621,0,0,0,0,9.39a4.73,4.73,0,0,0,4.89,4.54H7.43Z" fill="url(#bdf27dc6-025c-4805-8bfd-3a05defd3fbd)" /><polygon points="10.07 7.159 10.08 11.439 17.99 13.119 17.99 10.139 10.07 7.159" fill="#767676" /><polyline points="10.07 7.159 7.93 8.459 7.93 12.439 10.08 11.439" fill="#999" /><polygon points="13.68 11.499 14.4 11.699 14.4 9.369 13.68 9.119 13.68 11.499" fill="#a3a3a3" /><polygon points="12.96 8.889 12.24 8.629 12.24 11.119 12.96 11.319 12.96 8.889" fill="#a3a3a3" /><polygon points="15.12 11.889 15.82 12.079 15.84 9.859 15.12 9.619 15.12 11.889" fill="#a3a3a3" /><polygon points="10.81 10.749 11.53 10.929 11.53 8.399 10.81 8.159 10.81 10.749" fill="#a3a3a3" /><polygon points="17.27 10.349 16.55 10.099 16.55 12.269 17.27 12.469 17.27 10.349" fill="#a3a3a3" /><path id="ef0d1b54-a1e7-4cb9-a4e5-8a8518e7c127" d="M8.66,11.369l-.36.21V8.749l.36-.19Zm.71-3.22L9,8.389v2.75l.37-.2Z" fill="#b3b3b3" /><polygon points="17.99 13.119 15.83 13.859 7.93 12.439 10.08 11.439 17.99 13.119" fill="url(#fcd6c6c7-e2eb-4a32-b914-3a8485585a1b)" /><polygon points="17.99 16.169 10.04 17.679 10.08 12.089 17.99 13.559 17.99 16.169" fill="#767676" /><polygon points="10.81 16.759 10.81 13.209 11.53 13.299 11.53 16.639 10.81 16.759" fill="#a3a3a3" /><polygon points="12.96 16.399 12.24 16.529 12.24 13.389 12.96 13.499 12.96 16.399 12.96 16.399" fill="#a3a3a3" /><polygon points="13.68 16.289 13.68 13.569 14.4 13.659 14.4 16.159 13.68 16.289" fill="#a3a3a3" /><polygon points="15.83 15.909 15.12 16.039 15.12 13.749 15.83 13.859 15.83 15.909" fill="#a3a3a3" /><polygon points="17.29 15.679 16.55 15.809 16.55 13.929 17.24 14.019 17.29 15.679" fill="#a3a3a3" /><path id="b9f25eb4-4c88-45c2-bc4d-f992757d7e0e" d="M7.93,16.4v-3.26l2.16-1v5.6Z" fill="#999" /><path id="eb9200a7-4693-4427-bdae-b33ce90ff7f2" d="M8.61,16.389l-.32-.16v-2.76l.32-.15Zm.77-3.45-.38.19v3.48l.37.19v-3.86Z" fill="#b3b3b3" /></svg> diff --git a/src/icons/k6.svg b/src/icons/k6.svg new file mode 100644 index 000000000..deb080d11 --- /dev/null +++ b/src/icons/k6.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="0 0 48 48" width="48" height="48" xmlns="http://www.w3.org/2000/svg"> + <path d="M 47.368 46 L 1 46 L 16.436 13.074 L 25.735 19.909 L 37.863 1 L 47.368 46 Z M 30.367 38.888 L 30.467 38.888 C 31.614 38.89 32.718 38.447 33.544 37.651 C 34.401 36.873 34.883 35.765 34.868 34.608 C 34.906 33.491 34.437 32.418 33.592 31.687 C 32.889 30.982 31.942 30.574 30.946 30.547 L 30.87 30.547 C 30.74 30.547 30.61 30.565 30.485 30.6 L 32.933 26.962 L 30.983 25.601 L 30.06 26.962 L 27.702 30.564 C 27.296 31.161 26.957 31.687 26.747 32.052 C 26.529 32.439 26.342 32.842 26.187 33.258 C 26.01 33.7 25.919 34.172 25.919 34.648 C 25.907 35.791 26.382 36.886 27.225 37.658 C 28.041 38.453 29.136 38.897 30.275 38.896 L 30.367 38.888 Z M 20.001 34.972 L 22.703 38.794 L 25.592 38.794 L 22.413 34.36 L 25.236 30.441 L 23.362 29.144 L 22.535 30.235 L 19.997 33.815 L 19.997 26.618 L 17.466 24.555 L 17.466 38.793 L 19.997 38.793 L 19.997 34.969 L 20.001 34.972 Z M 30.371 36.502 C 28.939 36.502 28.044 34.951 28.76 33.711 C 29.092 33.135 29.707 32.78 30.371 32.78 L 30.388 32.78 C 30.883 32.781 31.357 32.983 31.699 33.341 C 32.06 33.672 32.264 34.141 32.26 34.631 C 32.247 35.665 31.406 36.497 30.371 36.498 L 30.371 36.502 Z" fill="#7d64ff"/> +</svg> \ No newline at end of file diff --git a/src/icons/opensearch.svg b/src/icons/opensearch.svg new file mode 100644 index 000000000..26da9ddb9 --- /dev/null +++ b/src/icons/opensearch.svg @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="0 0 48 48" width="48" height="48" xmlns="http://www.w3.org/2000/svg"> + <path d="M 45.373 17.423 C 44.475 17.423 43.747 18.151 43.747 19.049 C 43.747 32.432 32.899 43.28 19.517 43.28 C 18.618 43.28 17.89 44.008 17.89 44.906 C 17.89 45.804 18.618 46.533 19.517 46.533 C 34.696 46.533 47 34.228 47 19.049 C 47 18.151 46.271 17.423 45.373 17.423 Z" fill="#005EB8"/> + <path d="M 35.558 27.844 C 37.122 25.293 38.635 21.891 38.338 17.129 C 37.721 7.265 28.785 -0.218 20.348 0.593 C 17.046 0.91 13.654 3.602 13.956 8.424 C 14.087 10.52 15.112 11.757 16.779 12.708 C 18.366 13.613 20.403 14.187 22.715 14.837 C 25.506 15.621 28.743 16.503 31.232 18.336 C 34.215 20.534 36.253 23.082 35.558 27.844 Z" fill="#003B5C"/> + <path d="M 3.816 10.595 C 2.252 13.146 0.74 16.548 1.037 21.31 C 1.654 31.173 10.589 38.658 19.025 37.848 C 22.328 37.53 25.719 34.837 25.417 30.013 C 25.288 27.918 24.262 26.683 22.595 25.731 C 21.008 24.826 18.97 24.252 16.66 23.603 C 13.868 22.818 10.63 21.935 8.141 20.103 C 5.16 17.905 3.12 15.356 3.816 10.595 Z" fill="#005EB8"/> +</svg> \ No newline at end of file diff --git a/src/icons/opensearch_logo.svg b/src/icons/opensearch_logo.svg new file mode 100644 index 000000000..a59dc4537 --- /dev/null +++ b/src/icons/opensearch_logo.svg @@ -0,0 +1,7 @@ +<svg viewBox="0 0 372 72" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M61.7374 26.5C60.4878 26.5 59.4748 27.513 59.4748 28.7626C59.4748 47.3814 44.3814 62.4748 25.7626 62.4748C24.513 62.4748 23.5 63.4878 23.5 64.7374C23.5 65.987 24.513 67 25.7626 67C46.8805 67 64 49.8805 64 28.7626C64 27.513 62.987 26.5 61.7374 26.5Z" fill="#005EB8"/> +<path d="M48.0814 41C50.2572 37.4505 52.3615 32.7178 51.9475 26.0921C51.0899 12.3673 38.6589 1.95537 26.9206 3.08373C22.3253 3.52547 17.6068 7.2712 18.026 13.9805C18.2082 16.8961 19.6352 18.6169 21.9544 19.9399C24.1618 21.1992 26.9978 21.9969 30.2128 22.9011C34.0962 23.9934 38.6009 25.2203 42.0631 27.7717C46.2125 30.8296 49.0491 34.3743 48.0814 41Z" fill="#003B5C"/> +<path d="M3.91861 17C1.74276 20.5495 -0.361506 25.2822 0.0524931 31.9079C0.910072 45.6327 13.3411 56.0446 25.0794 54.9163C29.6747 54.4745 34.3932 50.7288 33.974 44.0195C33.7918 41.1039 32.3647 39.3831 30.0456 38.0601C27.8382 36.8008 25.0022 36.0031 21.7872 35.0989C17.9038 34.0066 13.3991 32.7797 9.93695 30.2283C5.78747 27.1704 2.95092 23.6257 3.91861 17Z" fill="#005EB8"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M362.5 31V54H371.5V29C371.5 24.3927 370.6 20.9121 368.799 18.5511C366.998 16.1672 364.282 15 360.75 15C356.918 15 353.847 17.2408 352 21H351.5C351.636 19.0591 351.76 17.9472 351.85 17.1353C351.943 16.298 352 15.7797 352 15V0.5H343V54H352.5V35.5C352.5 31.3511 352.639 28.2815 353.493 26.081C354.347 23.8575 355.836 22.7458 357.96 22.7458C360.8 22.7458 362.5 25.3841 362.5 31ZM231.852 51.2289C234.284 48.7148 235.5 45.0936 235.5 40.3653C235.5 37.4129 234.834 34.7835 233.501 32.477C232.191 30.1705 229.865 27.9102 226.521 25.6959C224.042 24.0814 222.3 22.6398 221.294 21.3713C220.312 20.1027 219.821 18.615 219.821 16.9082C219.821 15.1783 220.23 13.8175 221.049 12.8257C221.891 11.8108 223.083 11.3034 224.627 11.3034C226.03 11.3034 227.339 11.5571 228.555 12.0645C229.794 12.572 230.975 13.1486 232.098 13.7944L235.254 6.25216C231.63 4.08405 227.854 3 223.925 3C219.809 3 216.524 4.26857 214.069 6.80572C211.637 9.34287 210.421 12.7796 210.421 17.1158C210.421 19.3761 210.725 21.3597 211.333 23.0665C211.964 24.7733 212.841 26.3187 213.964 27.7026C215.109 29.0634 216.781 30.4935 218.979 31.9927C221.505 33.6995 223.317 35.2564 224.416 36.6633C225.515 38.0472 226.065 39.5811 226.065 41.2648C226.065 42.9716 225.597 44.3209 224.662 45.3127C223.75 46.3045 222.382 46.8004 220.558 46.8004C217.354 46.8004 213.835 45.5664 210 43.0985V52.4052C213.133 54.1351 216.933 55 221.4 55C225.959 55 229.444 53.743 231.852 51.2289ZM241.674 49.8745C244.48 53.2915 248.306 55 253.152 55C257.303 55 260.862 54.1111 263.83 52.3333V44.7489C260.677 46.619 257.593 47.5541 254.578 47.5541C252.213 47.5541 250.358 46.7229 249.013 45.0606C247.668 43.3752 247.07 40.9401 247 37.5H265.5V32.4545C265.5 26.9365 264.283 22.6537 261.848 19.6061C259.413 16.5354 256.086 15 251.865 15C247.343 15 243.819 16.7893 241.291 20.368C238.764 23.9466 237.5 28.9221 237.5 35.2944C237.5 41.5743 238.891 46.4343 241.674 49.8745ZM248.526 24.2121C249.384 22.8038 250.474 22.0996 251.796 22.0996C253.21 22.0996 254.323 22.8268 255.135 24.2814C255.946 25.7359 256.454 28.1833 256.5 31H247C247.139 28.0678 247.668 25.5974 248.526 24.2121ZM288 54L286.5 49H286C284.622 51.2587 283.295 52.868 281.824 53.7208C280.352 54.5736 278.494 55 276.252 55C273.378 55 271.112 53.9398 269.453 51.8194C267.818 49.6989 267 46.7488 267 42.9689C267 38.9124 268.121 35.9046 270.364 33.9455C272.63 31.9634 276.006 30.8686 280.492 30.6612L285.678 30.4538V27.688C285.678 24.0925 284.101 22.2947 280.947 22.2947C278.611 22.2947 275.924 23.1936 272.887 24.9914L269.663 18.6301C273.542 16.21 277.694 15 282.25 15C286.385 15 289.592 16.1755 291.741 18.5264C293.914 20.8542 295 24.1616 295 28.4486V54H288ZM280.071 47.809C281.777 47.809 283.132 47.0599 284.136 45.5618C285.164 44.0406 285.678 42.0239 285.678 39.5117V36.2619L282.805 36.4002C280.679 36.5154 279.113 37.1147 278.109 38.1979C277.128 39.2812 276.637 40.8946 276.637 43.038C276.637 46.2187 277.782 47.809 280.071 47.809ZM318 15.75C316.93 15.405 315.337 15 314.222 15C312.651 15 311.273 15.5174 310.089 16.5523C308.905 17.5872 308.002 18.5853 307 21H306.5L305 16H298V54H307.463V34C307.463 30.6424 307.676 28.4763 308.86 26.7285C310.044 24.9577 311.74 24.0723 313.948 24.0723C314.973 24.0723 315.863 24.27 316.5 24.5L318 15.75ZM332 55C327.443 55 323.954 53.478 321.573 50.1302C319.191 46.7824 318 41.8647 318 35.377C318 28.5891 319.122 23.5213 321.366 20.1735C323.634 16.8257 327.017 15 331.735 15C333.154 15 334.752 15.3596 336.309 15.7752C337.866 16.1908 339.763 16.715 341 17.5L337.889 24.7449C335.989 23.6136 334.305 23.048 332.84 23.048C330.893 23.048 329.485 24.0754 328.614 26.1302C327.767 28.162 327.344 31.2211 327.344 35.3077C327.344 39.3019 327.767 42.2918 328.614 44.2774C329.462 46.2399 330.847 47.2211 332.771 47.2211C335.061 47.2211 337.454 46.413 339.95 44.7969V52.9008C337.546 54.4015 334.908 55 332 55Z" fill="#003B5C"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M107.777 48.2625C110.926 43.7708 112.5 37.3442 112.5 28.9827C112.5 20.6213 110.937 14.2062 107.812 9.73754C104.686 5.24585 100.194 3 94.3368 3C88.4098 3 83.8719 5.23433 80.7231 9.70299C77.5744 14.1486 76 20.5522 76 28.9136C76 37.3442 77.5744 43.8053 80.7231 48.297C83.8719 52.7657 88.3866 55 94.2674 55C100.125 55 104.628 52.7542 107.777 48.2625ZM87.8425 42.1468C86.3839 39.1293 85.6546 34.7413 85.6546 28.9827C85.6546 23.2011 86.3839 18.8131 87.8425 15.8186C89.3011 12.8011 91.4659 11.2924 94.3368 11.2924C99.986 11.2924 102.811 17.1891 102.811 28.9827C102.811 40.7763 99.9629 46.6731 94.2674 46.6731C91.4428 46.6731 89.3011 45.1643 87.8425 42.1468ZM128.186 53.9979C129.469 54.7387 130.85 55 132.5 55C136.03 55 138.9 53.3265 140.94 49.7612C142.98 46.196 144 41.2764 144 35.0025C144 28.6359 143.014 23.7164 141.043 20.2437C139.072 16.7479 136.345 15 132.861 15C129.24 15 126.402 17.1569 124.5 21H124L122.5 16H115.5V71.5H124.5V55C124.5 54.3518 124.367 52.1485 124 49H124.5C125.25 51.25 126.925 53.2339 128.186 53.9979ZM125.882 25.3832C126.685 23.6932 127.979 22.8482 129.767 22.8482C131.44 22.8482 132.666 23.8437 133.446 25.8347C134.248 27.8257 134.649 30.8353 134.649 34.8636C134.649 43.059 133.045 47.1567 129.836 47.1567C127.979 47.1567 126.65 46.1844 125.848 44.2397C125.046 42.295 124.645 39.1928 124.645 34.933V33.7176C124.691 29.8282 125.103 27.0501 125.882 25.3832ZM161.652 55C156.806 55 152.98 53.2915 150.174 49.8745C147.391 46.4343 146 41.5743 146 35.2944C146 28.9221 147.264 23.9466 149.791 20.368C152.319 16.7893 155.843 15 160.365 15C164.586 15 167.913 16.5354 170.348 19.6061C172.783 22.6537 174 26.9365 174 32.4545V37.5H155.5C155.57 40.9401 156.168 43.3752 157.513 45.0606C158.858 46.7229 160.713 47.5541 163.078 47.5541C166.093 47.5541 169.177 46.619 172.33 44.7489V52.3333C169.362 54.1111 165.803 55 161.652 55ZM160.296 22.0996C158.974 22.0996 157.884 22.8038 157.026 24.2121C156.168 25.5974 155.639 28.0678 155.5 31H165C164.954 28.1833 164.446 25.7359 163.635 24.2814C162.823 22.8268 161.71 22.0996 160.296 22.0996ZM196.5 31V54H205.5V29.1991C205.5 24.5589 204.623 21.0323 202.868 18.6194C201.137 16.2065 198.516 15 195.007 15C192.93 15 191.117 15.5104 189.57 16.5313C188.024 17.5289 186.831 19.2135 186 21H185.5L184.25 16H177V54H186.5V35.75C186.5 31.0402 186.673 27.8302 187.597 25.8582C188.52 23.8628 189.974 22.8652 191.96 22.8652C193.461 22.8652 194.546 23.5844 195.215 25.0229C195.885 26.4614 196.5 28.1927 196.5 31Z" fill="#005EB8"/> +</svg> diff --git a/src/icons/playwright.svg b/src/icons/playwright.svg new file mode 100644 index 000000000..da5b84bb9 --- /dev/null +++ b/src/icons/playwright.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="0 0 48 48" width="48" height="48" xmlns="http://www.w3.org/2000/svg"> + <path d="M 16.332 26.185 C 14.652 26.662 13.55 27.498 12.824 28.333 C 13.519 27.725 14.451 27.166 15.707 26.81 C 16.992 26.446 18.089 26.448 18.995 26.623 L 18.995 25.915 C 18.222 25.844 17.336 25.9 16.332 26.185 Z M 12.747 20.231 L 6.508 21.874 C 6.508 21.874 6.622 22.034 6.833 22.249 L 12.123 20.855 C 12.123 20.855 12.048 21.821 11.396 22.685 C 12.628 21.753 12.747 20.231 12.747 20.231 Z M 17.97 34.892 C 9.19 37.257 4.545 27.083 3.139 21.801 C 2.489 19.364 2.206 17.517 2.13 16.327 C 2.122 16.202 2.126 16.098 2.135 16.003 C 1.679 16.03 1.461 16.267 1.505 16.951 C 1.581 18.142 1.864 19.988 2.514 22.426 C 3.919 27.706 8.565 37.882 17.345 35.517 C 19.256 35.002 20.692 34.064 21.77 32.867 C 20.776 33.764 19.533 34.471 17.97 34.892 Z M 19.62 14.004 L 19.62 14.629 L 23.063 14.629 C 22.992 14.407 22.921 14.208 22.851 14.004 L 19.62 14.004 Z" fill="#2D4552"/> + <path d="M 23.832 19.15 C 25.381 19.589 26.2 20.675 26.633 21.636 L 28.36 22.127 C 28.36 22.127 28.125 18.764 25.083 17.9 C 22.237 17.091 20.486 19.481 20.273 19.79 C 21.101 19.2 22.31 18.717 23.832 19.15 Z M 37.578 21.652 C 34.73 20.839 32.98 23.234 32.77 23.539 C 33.598 22.95 34.807 22.467 36.328 22.901 C 37.875 23.341 38.693 24.426 39.128 25.388 L 40.857 25.88 C 40.857 25.88 40.618 22.516 37.578 21.652 Z M 35.863 30.519 L 21.498 26.503 C 21.498 26.503 21.653 27.291 22.251 28.312 L 34.345 31.693 C 35.34 31.117 35.863 30.519 35.863 30.519 Z M 25.904 39.162 C 14.529 36.113 15.904 21.621 17.746 14.754 C 18.503 11.924 19.282 9.82 19.928 8.41 C 19.542 8.331 19.223 8.534 18.908 9.176 C 18.222 10.567 17.345 12.832 16.496 16.003 C 14.656 22.871 13.281 37.362 24.654 40.411 C 30.015 41.848 34.191 39.665 37.305 36.237 C 34.349 38.913 30.577 40.414 25.904 39.162 Z" fill="#2D4552"/> + <path d="M 19.62 31.496 L 19.62 28.572 L 11.494 30.876 C 11.494 30.876 12.095 27.388 16.332 26.185 C 17.617 25.821 18.714 25.824 19.62 25.998 L 19.62 14.004 L 23.688 14.004 C 23.245 12.635 22.816 11.581 22.457 10.848 C 21.861 9.637 21.251 10.44 19.865 11.599 C 18.889 12.414 16.423 14.154 12.712 15.154 C 8.999 16.154 5.999 15.889 4.747 15.673 C 2.972 15.366 2.044 14.976 2.131 16.327 C 2.207 17.517 2.49 19.363 3.139 21.801 C 4.545 27.082 9.191 37.257 17.97 34.892 C 20.263 34.274 21.883 33.053 23.004 31.496 L 19.62 31.496 Z M 6.508 21.874 L 12.747 20.231 C 12.747 20.231 12.565 22.631 10.227 23.248 C 7.887 23.864 6.508 21.874 6.508 21.874 Z" fill="#E2574C"/> + <path d="M 43.101 14.142 C 41.479 14.426 37.588 14.781 32.78 13.492 C 27.97 12.204 24.779 9.951 23.514 8.892 C 21.722 7.391 20.933 6.348 20.158 7.926 C 19.471 9.317 18.595 11.583 17.746 14.754 C 15.905 21.621 14.53 36.112 25.904 39.162 C 37.275 42.209 43.328 28.97 45.168 22.103 C 46.017 18.932 46.39 16.531 46.492 14.983 C 46.609 13.23 45.404 13.739 43.101 14.142 Z M 20.25 19.824 C 20.25 19.824 22.042 17.036 25.082 17.9 C 28.124 18.764 28.36 22.127 28.36 22.127 L 20.25 19.824 Z M 27.671 32.333 C 22.323 30.767 21.499 26.503 21.499 26.503 L 35.863 30.519 C 35.863 30.518 32.963 33.879 27.671 32.333 Z M 32.749 23.571 C 32.749 23.571 34.539 20.785 37.578 21.651 C 40.618 22.516 40.857 25.879 40.857 25.879 L 32.749 23.571 Z" fill="#2EAD33"/> + <path d="M 16.78 29.377 L 11.494 30.875 C 11.494 30.875 12.069 27.604 15.962 26.308 L 12.969 15.075 L 12.711 15.154 C 8.999 16.154 5.998 15.889 4.746 15.673 C 2.972 15.366 2.043 14.976 2.13 16.327 C 2.206 17.517 2.489 19.363 3.139 21.801 C 4.544 27.082 9.19 37.257 17.97 34.892 L 18.228 34.811 L 16.78 29.377 Z M 6.508 21.874 L 12.747 20.231 C 12.747 20.231 12.565 22.631 10.227 23.248 C 7.887 23.864 6.508 21.874 6.508 21.874 Z" fill="#D65348"/> + <path d="M 27.912 32.392 L 27.67 32.333 C 22.323 30.767 21.498 26.503 21.498 26.503 L 28.905 28.573 L 32.827 13.505 L 32.779 13.492 C 27.969 12.204 24.778 9.951 23.513 8.892 C 21.721 7.391 20.933 6.348 20.157 7.926 C 19.471 9.317 18.595 11.583 17.746 14.754 C 15.905 21.621 14.53 36.112 25.904 39.162 L 26.137 39.215 L 27.912 32.392 Z M 20.25 19.824 C 20.25 19.824 22.042 17.036 25.082 17.9 C 28.124 18.764 28.36 22.127 28.36 22.127 L 20.25 19.824 Z" fill="#1D8D22"/> + <path d="M 17.049 29.3 L 15.632 29.702 C 15.967 31.591 16.557 33.403 17.483 35.003 C 17.644 34.967 17.805 34.937 17.969 34.892 C 18.4 34.775 18.798 34.632 19.183 34.475 C 18.148 32.939 17.463 31.17 17.049 29.3 Z M 16.496 16.004 C 15.767 18.722 15.115 22.635 15.295 26.561 C 15.616 26.421 15.955 26.291 16.332 26.185 L 16.595 26.126 C 16.275 21.933 16.966 17.661 17.746 14.754 C 17.943 14.019 18.14 13.335 18.338 12.697 C 18.021 12.899 17.678 13.107 17.288 13.32 C 17.025 14.132 16.759 15.019 16.496 16.004 Z" fill="#C04B41"/> +</svg> \ No newline at end of file diff --git a/src/icons/postman.svg b/src/icons/postman.svg new file mode 100644 index 000000000..2ca9182ea --- /dev/null +++ b/src/icons/postman.svg @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="0 0 48 48" width="48" height="48" xmlns="http://www.w3.org/2000/svg"> + <g transform="matrix(0.175781, 0, 0, 0.175781, 1.500031, 1.500036)"> + <path d="M254.953118,144.253071 C263.911504,74.1217108 214.38443,10.0052669 144.381048,1.04688158 C74.3776647,-7.9115038 10.0052669,41.6155696 1.04688158,111.618952 C-7.9115038,181.622335 41.6155696,245.866756 111.618952,254.953118 C181.750312,263.911504 245.866756,214.38443 254.953118,144.253071 Z" fill="#FF6C37"/> + <g transform="translate(50.181225, 45.198924)"> + <path d="M124.018448,36.9853339 L70.012182,90.9916 L54.7829269,75.7623449 C107.893354,22.6519173 113.140409,27.2590869 124.018448,36.9853339 L124.018448,36.9853339 Z" fill="#FFFFFF"/> + <path d="M70.012182,92.2713693 C69.6282512,92.2713693 69.3722974,92.1433924 69.1163435,91.8874385 L53.7591114,76.6581834 C53.2472037,76.1462757 53.2472037,75.3784141 53.7591114,74.8665063 C107.765378,20.8602402 113.396363,25.9793176 124.78631,36.2174723 C125.042264,36.4734262 125.170241,36.72938 125.170241,37.1133108 C125.170241,37.4972416 125.042264,37.7531955 124.78631,38.0091494 L70.7800436,91.8874385 C70.6520667,92.1433924 70.2681359,92.2713693 70.012182,92.2713693 Z M56.574604,75.7623449 L70.012182,89.1999229 L122.098794,37.1133108 C112.628501,28.6668332 106.229654,26.1072945 56.574604,75.7623449 L56.574604,75.7623449 Z" fill="#FF6C37"/> + <path d="M85.497391,106.476809 L70.7800436,91.7594616 L124.78631,37.7531955 C139.247703,52.342566 117.619601,76.0182987 85.497391,106.476809 Z" fill="#FFFFFF"/> + <path d="M85.497391,107.756578 C85.1134602,107.756578 84.8575064,107.628601 84.6015525,107.372648 L69.8842051,92.6553001 C69.6282512,92.3993463 69.6282512,92.1433924 69.6282512,91.7594616 C69.6282512,91.3755308 69.7562282,91.1195769 70.012182,90.8636231 L124.018448,36.857357 C124.530356,36.3454492 125.298217,36.3454492 125.810125,36.857357 C129.137525,39.9288034 130.929203,44.2800191 130.801226,48.7592118 C130.545272,62.9646515 114.420178,81.0093992 86.5212065,107.372648 C86.1372757,107.628601 85.7533449,107.756578 85.497391,107.756578 L85.497391,107.756578 Z M72.5717207,91.7594616 C80.7622445,100.077962 84.2176217,103.405363 85.497391,104.685132 C106.997516,84.2088225 127.857756,63.2206053 127.985733,48.7592118 C128.11371,45.4318115 126.833941,42.1044113 124.658333,39.5448726 L72.5717207,91.7594616 Z" fill="#FF6C37"/> + <path d="M55.0388808,76.1462757 L65.9169201,87.024315 C66.172874,87.2802689 66.172874,87.5362228 65.9169201,87.7921767 C65.7889432,87.9201536 65.7889432,87.9201536 65.6609663,87.9201536 L43.1370259,92.7832771 C41.9852335,92.911254 40.961418,92.1433924 40.7054642,90.9916 C40.5774872,90.3517153 40.8334411,89.7118307 41.2173719,89.3278999 L54.2710192,76.2742526 C54.526973,76.0182987 54.9109038,75.8903218 55.0388808,76.1462757 Z" fill="#FFFFFF"/> + <path d="M42.7530951,94.0630464 C40.8334411,94.0630464 39.4256948,92.5273232 39.4256948,90.6076692 C39.4256948,89.7118307 39.8096256,88.8159921 40.4495103,88.1761075 L53.5031576,75.1224602 C54.2710192,74.4825755 55.2948346,74.4825755 56.0626962,75.1224602 L66.9407356,86.0004996 C67.7085972,86.6403842 67.7085972,87.7921767 66.9407356,88.5600383 C66.6847817,88.8159921 66.4288279,88.9439691 66.0448971,89.071946 L43.5209567,93.9350695 C43.2650028,93.9350695 43.009049,94.0630464 42.7530951,94.0630464 L42.7530951,94.0630464 Z M54.65495,77.5540219 L42.1132104,90.0957615 C41.8572566,90.3517153 41.7292796,90.7356461 41.9852335,91.1195769 C42.1132104,91.5035077 42.4971412,91.6314847 42.881072,91.5035077 L63.9972661,86.8963381 L54.65495,77.5540219 Z" fill="#FF6C37"/> + <path d="M152.557304,7.03873136 C144.366781,-0.895838537 131.185156,-0.639884669 123.250587,7.67861603 C115.316017,15.9971167 115.57197,29.050764 123.890471,36.9853339 C130.673249,43.5121575 140.911403,44.6639499 148.97395,39.8008264 L134.38458,25.211456 L152.557304,7.03873136 Z" fill="#FFFFFF"/> + <path d="M138.223888,44.0240653 C126.066079,44.0240653 116.211855,34.1698413 116.211855,22.0120326 C116.211855,9.85422391 126.066079,-1.81866161e-14 138.223888,-1.81866161e-14 C143.854873,-1.81866161e-14 149.357881,2.17560788 153.453143,6.14289283 C153.709097,6.39884669 153.837074,6.65480056 153.837074,7.03873136 C153.837074,7.42266217 153.709097,7.67861603 153.453143,7.9345699 L136.176257,25.211456 L149.741812,38.777011 C150.25372,39.2889187 150.25372,40.0567803 149.741812,40.568688 C149.613835,40.696665 149.613835,40.696665 149.485858,40.8246419 C146.158458,42.8722729 142.191173,44.0240653 138.223888,44.0240653 Z M138.223888,2.68751561 C127.473825,2.68751561 118.771394,11.3899471 118.899371,22.1400096 C118.899371,32.890072 127.601802,41.5925035 138.351865,41.4645266 C141.295334,41.4645266 144.238804,40.8246419 146.926319,39.4168956 L133.488741,26.1072945 C133.232787,25.8513406 133.10481,25.5953868 133.10481,25.211456 C133.10481,24.8275252 133.232787,24.5715713 133.488741,24.3156174 L150.63765,7.1667083 C147.182273,4.22323882 142.831057,2.68751561 138.223888,2.68751561 L138.223888,2.68751561 Z" fill="#FF6C37"/> + <path d="M152.941235,7.42266217 L152.685281,7.1667083 L134.38458,25.211456 L148.845973,39.6728495 C150.25372,38.777011 151.661466,37.7531955 152.813258,36.6014031 C161.003782,28.5388563 161.003782,15.485209 152.941235,7.42266217 L152.941235,7.42266217 Z" fill="#FFFFFF"/> + <path d="M148.97395,41.0805958 C148.590019,41.0805958 148.334066,40.9526188 148.078112,40.696665 L133.488741,26.1072945 C133.232787,25.8513406 133.10481,25.5953868 133.10481,25.211456 C133.10481,24.8275252 133.232787,24.5715713 133.488741,24.3156174 L151.661466,6.14289283 C152.173374,5.63098509 152.941235,5.63098509 153.453143,6.14289283 L153.837074,6.39884669 C162.411528,14.9733013 162.411528,28.7948101 153.965051,37.4972416 C152.685281,38.777011 151.277535,39.9288034 149.741812,40.8246419 C149.357881,40.9526188 149.101927,41.0805958 148.97395,41.0805958 L148.97395,41.0805958 Z M136.176257,25.211456 L149.101927,38.1371263 C150.125743,37.4972416 151.149558,36.6014031 151.91742,35.8335415 C159.212105,28.5388563 159.596036,16.6370014 152.557304,8.95838537 L136.176257,25.211456 Z" fill="#FF6C37"/> + <path d="M126.194056,39.2889187 C123.12261,36.2174723 118.131509,36.2174723 115.060063,39.2889187 L66.8127587,87.5362228 L74.8753055,95.5987696 L125.938102,50.8068428 C129.265502,47.9913502 129.521456,43.0002498 126.705964,39.6728495 C126.45001,39.5448726 126.322033,39.4168956 126.194056,39.2889187 L126.194056,39.2889187 Z" fill="#FFFFFF"/> + <path d="M74.7473286,96.878539 C74.3633978,96.878539 74.1074439,96.750562 73.85149,96.4946082 L65.7889432,88.4320613 C65.2770355,87.9201536 65.2770355,87.152292 65.7889432,86.6403842 L114.036247,38.3930802 C117.619601,34.809726 123.378563,34.809726 126.961918,38.3930802 C130.545272,41.9764343 130.545272,47.7353963 126.961918,51.3187505 C126.833941,51.4467274 126.705964,51.5747044 126.577987,51.7026813 L75.5151902,96.4946082 C75.3872133,96.750562 75.1312594,96.878539 74.7473286,96.878539 L74.7473286,96.878539 Z M68.6044358,87.5362228 L74.8753055,93.8070925 L125.042264,49.7830273 C127.857756,47.4794425 128.11371,43.2562037 125.810125,40.4407111 C123.50654,37.6252186 119.283302,37.3692647 116.467809,39.6728495 C116.339832,39.8008264 116.211855,39.9288034 115.955901,40.0567803 L68.6044358,87.5362228 Z" fill="#FF6C37"/> + <path d="M29.8274248,142.438327 C29.3155171,142.694281 29.0595632,143.206189 29.1875401,143.718097 L31.363148,152.932436 C31.8750557,154.212205 31.1071941,155.747929 29.6994479,156.131859 C28.6756324,156.51579 27.52384,156.131859 26.8839553,155.363998 L12.8064926,141.414512 L58.7502118,95.4707927 L74.6193516,95.7267466 L85.3694141,106.476809 C82.8098754,108.652417 67.3246664,123.625718 29.8274248,142.438327 L29.8274248,142.438327 Z" fill="#FFFFFF"/> + <path d="M28.8036093,157.411629 C27.7797938,157.411629 26.7559784,157.027698 26.1160937,156.259836 L12.1666079,142.31035 C11.910654,142.054397 11.7826771,141.798443 11.7826771,141.414512 C11.7826771,141.030581 11.910654,140.774627 12.1666079,140.518673 L58.1103272,94.5749541 C58.366281,94.3190003 58.7502118,94.1910233 59.0061657,94.1910233 L74.8753055,94.4469772 C75.2592363,94.4469772 75.5151902,94.5749541 75.7711441,94.830908 L86.5212065,105.58097 C86.7771604,105.836924 86.9051373,106.220855 86.9051373,106.604786 C86.9051373,106.988717 86.7771604,107.244671 86.3932296,107.500624 L85.497391,108.268486 C71.931836,120.170341 53.5031576,132.072196 30.5952864,143.462143 L32.7708943,152.548505 C33.1548251,154.212205 32.3869635,156.003882 30.8512403,156.899721 C30.0833787,157.283652 29.443494,157.411629 28.8036093,157.411629 Z M14.7261466,141.414512 L27.9077708,154.468159 C28.2917016,155.108044 29.0595632,155.363998 29.6994479,154.980067 C30.3393325,154.596136 30.5952864,153.828275 30.2113556,153.18839 L28.0357477,143.974051 C27.7797938,142.822258 28.2917016,141.798443 29.3155171,141.286535 C51.9674343,129.896588 70.2681359,118.12271 83.705714,106.476809 L74.2354208,97.0065159 L59.5180734,96.750562 L14.7261466,141.414512 Z" fill="#FF6C37"/> + <path d="M1.9284532,152.420528 L12.9344695,141.414512 L29.3155171,157.795559 L3.20822254,156.003882 C2.05643013,155.875905 1.28856853,154.85209 1.41654546,153.700298 C1.41654546,153.18839 1.5445224,152.676482 1.9284532,152.420528 L1.9284532,152.420528 Z" fill="#FFFFFF"/> + <path d="M29.3155171,158.947352 L3.0802456,157.155675 C1.16059159,157.027698 -0.119177745,155.363998 0.00879918845,153.444344 C0.136776122,152.676482 0.39272999,151.908621 1.03261466,151.396713 L12.038631,140.390696 C12.5505387,139.878789 13.3184003,139.878789 13.830308,140.390696 L30.2113556,156.771744 C30.5952864,157.155675 30.7232633,157.667583 30.4673095,158.17949 C30.2113556,158.691398 29.8274248,158.947352 29.3155171,158.947352 L29.3155171,158.947352 Z M12.9344695,143.206189 L2.82429173,153.316367 C2.44036093,153.572321 2.44036093,154.212205 2.82429173,154.468159 C2.95226867,154.596136 3.0802456,154.724113 3.33619947,154.724113 L25.9881168,156.259836 L12.9344695,143.206189 Z" fill="#FF6C37"/> + <path d="M54.2710192,101.357732 C53.5031576,101.357732 52.9912498,100.717847 52.9912498,100.077962 C52.9912498,99.6940315 53.1192268,99.4380776 53.3751806,99.1821238 L65.7889432,86.7683612 C66.3008509,86.2564534 67.0687125,86.2564534 67.5806203,86.7683612 L75.6431671,94.830908 C76.0270979,95.2148388 76.1550749,95.5987696 76.0270979,96.1106774 C75.899121,96.4946082 75.5151902,96.878539 75.0032825,97.0065159 L54.526973,101.357732 C54.3989961,101.357732 54.2710192,101.357732 54.2710192,101.357732 L54.2710192,101.357732 Z M66.6847817,89.4558768 L58.2383041,97.9023544 L72.059813,94.9588849 L66.6847817,89.4558768 Z" fill="#FF6C37"/> + <path d="M74.6193516,95.7267466 L60.5418889,98.798193 C59.5180734,99.0541468 58.494258,98.4142622 58.2383041,97.3904467 C58.1103272,96.750562 58.2383041,96.1106774 58.7502118,95.5987696 L66.5568048,87.7921767 L74.6193516,95.7267466 Z" fill="#FFFFFF"/> + <path d="M60.2859351,100.077962 C58.494258,100.077962 57.0865117,98.670216 57.0865117,96.878539 C57.0865117,95.9827004 57.4704425,95.2148388 57.9823502,94.5749541 L65.7889432,86.7683612 C66.3008509,86.2564534 67.0687125,86.2564534 67.5806203,86.7683612 L75.6431671,94.830908 C76.0270979,95.2148388 76.1550749,95.5987696 76.0270979,96.1106774 C75.899121,96.4946082 75.5151902,96.878539 75.0032825,97.0065159 L60.9258197,100.077962 C60.6698659,100.077962 60.413912,100.077962 60.2859351,100.077962 L60.2859351,100.077962 Z M66.6847817,89.4558768 L59.7740273,96.3666312 C59.5180734,96.6225851 59.5180734,96.878539 59.6460504,97.1344928 C59.7740273,97.3904467 60.0299812,97.5184236 60.413912,97.5184236 L72.1877899,94.9588849 L66.6847817,89.4558768 Z" fill="#FF6C37"/> + <path d="M153.069212,19.7084478 C152.813258,18.9405862 151.91742,18.5566554 151.149558,18.8126093 C150.381697,19.0685632 149.997766,19.9644017 150.25372,20.7322633 C150.25372,20.8602402 150.381697,20.9882172 150.381697,21.1161941 C151.149558,22.6519173 150.893604,24.5715713 149.869789,25.9793176 C149.357881,26.6192023 149.485858,27.5150408 149.997766,28.0269485 C150.63765,28.5388563 151.533489,28.4108793 152.045397,27.7709947 C153.965051,25.3394329 154.348981,22.2679865 153.069212,19.7084478 L153.069212,19.7084478 Z" fill="#FF6C37"/> + </g> + </g> +</svg> \ No newline at end of file diff --git a/src/icons/powershell.svg b/src/icons/powershell.svg new file mode 100644 index 000000000..eb6b06d3c --- /dev/null +++ b/src/icons/powershell.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="32px" height="32px"><path fill="#0277bd" d="M19.847,41.956c-5.629-0.002-11.259,0.024-16.888-0.013c-2.855-0.019-3.374-0.7-2.731-3.525 c2.178-9.58,4.427-19.143,6.557-28.734C7.356,7.112,8.588,5.975,11.312,6C22.57,6.106,33.829,6.034,45.088,6.046 c2.824,0.003,3.298,0.614,2.664,3.511c-2.058,9.406-4.129,18.809-6.236,28.203c-0.789,3.516-1.697,4.187-5.353,4.195 C30.724,41.966,25.285,41.958,19.847,41.956z"/><path fill="#fafafa" d="M25.057 23.922c-.608-.687-1.114-1.267-1.531-1.732-2.43-2.728-4.656-5.27-7.063-7.869-1.102-1.189-1.453-2.344-.13-3.518 1.307-1.16 2.592-1.058 3.791.277 3.34 3.717 6.676 7.438 10.071 11.104 1.268 1.369.972 2.3-.424 3.315-5.359 3.895-10.687 7.833-16.01 11.778-1.196.887-2.337 1.109-3.304-.201-1.066-1.445-.08-2.305 1.026-3.114 3.955-2.893 7.903-5.798 11.834-8.725C23.865 24.83 24.595 24.267 25.057 23.922zM21.75 37C20.625 37 20 36 20 35s.625-2 1.75-2c4.224 0 6.112 0 9.5 0 1.125 0 1.75 1 1.75 2s-.625 2-1.75 2C29.125 37 25 37 21.75 37z"/></svg> \ No newline at end of file From 39e7343258fb8e5b70097ac1672e4bd3e44bf1bc Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 17:05:03 +0200 Subject: [PATCH 06/15] reactor: standardise types / services --- src/api/query-hooks/index.ts | 2 +- src/api/query-hooks/playbooks.tsx | 6 +-- src/api/services/agents.ts | 18 ++++++++ src/api/services/configs.ts | 67 +++++++++++++++++----------- src/api/services/playbooks.ts | 10 ++++- src/api/services/topology.ts | 48 +++++++------------- src/api/types/configs.ts | 4 ++ src/api/types/health.ts | 5 +++ src/api/types/playbooks.ts | 21 +++++++-- src/api/types/topology.ts | 16 +++---- src/components/Agents/AgentName.tsx | 2 +- src/components/Canary/CanaryTabs.tsx | 2 +- 12 files changed, 122 insertions(+), 79 deletions(-) diff --git a/src/api/query-hooks/index.ts b/src/api/query-hooks/index.ts index 1db6ed8bb..9e3bed06b 100644 --- a/src/api/query-hooks/index.ts +++ b/src/api/query-hooks/index.ts @@ -19,7 +19,6 @@ import { getIncident } from "../services/incident"; import { getIncidentHistory } from "../services/IncidentsHistory"; import { LogsResponse, searchLogs, SearchLogsPayload } from "../services/logs"; import { - getAllAgents, getComponentTeams, getHealthCheckItem, getTopology, @@ -27,6 +26,7 @@ import { getTopologyComponents, getTopologyComponentsWithLogs } from "../services/topology"; +import { getAllAgents } from "../services/agents"; import { ComponentTeamItem } from "../types/topology"; import { getPersons, getVersionInfo } from "../services/users"; import { IncidentHistory } from "../types/incident"; diff --git a/src/api/query-hooks/playbooks.tsx b/src/api/query-hooks/playbooks.tsx index d1a82ae53..6a28ca4e8 100644 --- a/src/api/query-hooks/playbooks.tsx +++ b/src/api/query-hooks/playbooks.tsx @@ -11,7 +11,7 @@ import { getPlaybookToRunForResource, submitPlaybookRun } from "../services/playbooks"; -import { PlaybookSpec } from "../types/playbooks"; +import { PlaybookSpec, RunnablePlaybook } from "../types/playbooks"; export function useGetAllPlaybookSpecs( options: UseQueryOptions<PlaybookSpec[], Error> = {} @@ -35,9 +35,9 @@ export type GetPlaybooksToRunParams = { export function useGetPlaybooksToRun( params: GetPlaybooksToRunParams, - options: UseQueryOptions<PlaybookSpec[], Error> = {} + options: UseQueryOptions<RunnablePlaybook[], Error> = {} ) { - return useQuery<PlaybookSpec[], Error>( + return useQuery<RunnablePlaybook[], Error>( ["playbooks", "run", params], () => getPlaybookToRunForResource(params), { diff --git a/src/api/services/agents.ts b/src/api/services/agents.ts index da6770862..b4f37a684 100644 --- a/src/api/services/agents.ts +++ b/src/api/services/agents.ts @@ -2,6 +2,7 @@ import { Agent, AgentSummary } from "../../components/Agents/AgentPage"; import { AVATAR_INFO } from "../../constants"; import { AgentAPI, IncidentCommander } from "../axios"; import { resolve } from "../resolve"; +import { AgentItem } from "../types/common"; export const getAgentsList = async ( params: { @@ -69,3 +70,20 @@ export async function deleteAgent(id: string, cleanup: boolean = false) { }); return res.data; } +export const getAgentByID = async (id: string) => { + const res = await IncidentCommander.get<AgentItem[] | null>( + `/agents?select=id,name,description&id=eq.${id}` + ); + return res.data?.[0] ?? null; +}; +export const getAgentByIDs = async (ids: string[]) => { + const res = await IncidentCommander.get<AgentItem[] | null>( + `/agents?select=id,name,description&id=in.(${ids.join(",")})` + ); + return res.data ?? []; +}; +export const getAllAgents = () => { + return IncidentCommander.get<AgentItem[] | null>( + `/agents?select=id,name,description` + ); +}; diff --git a/src/api/services/configs.ts b/src/api/services/configs.ts index 140e7ad73..61a209b8f 100644 --- a/src/api/services/configs.ts +++ b/src/api/services/configs.ts @@ -45,6 +45,16 @@ export const getConfigsByIDs = async (ids: string[]) => { return res.data ?? []; }; +export const getConfigsByID = async (id: string) => { + const res = await resolve<ConfigItem[] | null>( + ConfigDB.get(`/configs?id=eq.${id}&select=id,name,config_class,type`) + ); + if (res.data && res.data.length > 0) { + return res.data[0]; + } + return null; +}; + export const getAllChanges = ( queryParams: Record<string, string | undefined>, pageIndex?: number, @@ -96,8 +106,9 @@ export const getConfigChanges = ( ) => { let paginationQueryParams = ""; if (pageIndex !== undefined && pageSize !== undefined) { - paginationQueryParams = `&limit=${pageSize}&offset=${pageIndex! * pageSize - }`; + paginationQueryParams = `&limit=${pageSize}&offset=${ + pageIndex! * pageSize + }`; } return resolve( ConfigDB.get<ConfigChange[]>( @@ -289,8 +300,9 @@ export const getConfigInsights = ( ) => { let paginationQueryParams = ""; if (pageIndex !== undefined && pageSize !== undefined) { - paginationQueryParams = `&limit=${pageSize}&offset=${pageIndex! * pageSize - }`; + paginationQueryParams = `&limit=${pageSize}&offset=${ + pageIndex! * pageSize + }`; } return resolve( ConfigDB.get< @@ -336,33 +348,34 @@ export const getTopologyRelatedInsights = async ( ) => { let paginationQueryParams = ""; if (pageIndex !== undefined && pageSize !== undefined) { - paginationQueryParams = `&limit=${pageSize}&offset=${pageIndex! * pageSize - }`; + paginationQueryParams = `&limit=${pageSize}&offset=${ + pageIndex! * pageSize + }`; } return resolve( ConfigDB.get< | { - analysis_id: string; - config: { - id: string; - name: string; - config_class: string; - type: string; - analysis: Pick< - ConfigAnalysis, - | "id" - | "analyzer" - | "config" - | "severity" - | "analysis_type" - | "sanitizedMessageTxt" - | "sanitizedMessageHTML" - | "first_observed" - | "message" - >[]; - }; - }[] + analysis_id: string; + config: { + id: string; + name: string; + config_class: string; + type: string; + analysis: Pick< + ConfigAnalysis, + | "id" + | "analyzer" + | "config" + | "severity" + | "analysis_type" + | "sanitizedMessageTxt" + | "sanitizedMessageHTML" + | "first_observed" + | "message" + >[]; + }; + }[] | null >( `/analysis_by_component?component_id=eq.${id}${paginationQueryParams}&select=analysis_id,config:configs(id,name,config_class,type,analysis:config_analysis(id,analyzer,analysis_type,message,severity,analysis,first_observed))`, @@ -417,7 +430,7 @@ export const getAllConfigInsights = async ( const sortString = sortBy.sortBy ? `&order=${sortBy.sortBy}.${sortBy.sortOrder}` : // default sort by first_observed - "&order=first_observed.desc"; + "&order=first_observed.desc"; return resolve( ConfigDB.get<ConfigAnalysis[] | null>( diff --git a/src/api/services/playbooks.ts b/src/api/services/playbooks.ts index 4866bd071..586c66ff2 100644 --- a/src/api/services/playbooks.ts +++ b/src/api/services/playbooks.ts @@ -9,6 +9,7 @@ import { PlaybookRunAction, PlaybookRunWithActions, PlaybookSpec, + RunnablePlaybook, UpdatePlaybookSpec } from "../types/playbooks"; @@ -49,10 +50,15 @@ export async function deletePlaybookSpec(id: string) { return res.data; } +export type PlaybookRunResponse = { + run_id: string; + starts_at: string; +}; + export async function submitPlaybookRun( input: Omit<SubmitPlaybookRunFormValues, "playbook_spec"> ) { - const res = await PlaybookAPI.post("/run", input); + const res = await PlaybookAPI.post<PlaybookRunResponse>("/run", input); return res.data; } @@ -63,7 +69,7 @@ export async function getPlaybookToRunForResource( .filter(([, value]) => value) .map(([key, value]) => `${key}=${value}`) .join("&"); - const res = await PlaybookAPI.get<PlaybookSpec[] | null>( + const res = await PlaybookAPI.get<RunnablePlaybook[] | null>( `/list?${paramsString}` ); return res.data ?? []; diff --git a/src/api/services/topology.ts b/src/api/services/topology.ts index cb9c4d0c3..297f50fc1 100644 --- a/src/api/services/topology.ts +++ b/src/api/services/topology.ts @@ -9,11 +9,14 @@ import { } from "../axios"; import { resolve } from "../resolve"; import { PaginationInfo } from "../types/common"; -import { HealthCheck, HealthCheckStatus } from "../types/health"; -import { Topology } from "../types/topology"; +import { + HealthCheck, + HealthCheckStatus, + HealthCheckSummary +} from "../types/health"; +import { ComponentHealthCheckView, Topology } from "../types/topology"; import { ComponentTeamItem } from "../types/topology"; import { ComponentTemplateItem } from "../types/topology"; -import { AgentItem } from "../types/common"; interface IParam { id?: string; @@ -172,26 +175,6 @@ export const getTopologyComponentsWithLogs = () => { ); }; -export const getAgentByID = async (id: string) => { - const res = await IncidentCommander.get<AgentItem[] | null>( - `/agents?select=id,name,description&id=eq.${id}` - ); - return res.data?.[0] ?? null; -}; - -export const getAgentByIDs = async (ids: string[]) => { - const res = await IncidentCommander.get<AgentItem[] | null>( - `/agents?select=id,name,description&id=in.(${ids.join(",")})` - ); - return res.data ?? []; -}; - -export const getAllAgents = () => { - return IncidentCommander.get<AgentItem[] | null>( - `/agents?select=id,name,description` - ); -}; - export const getTopologyComponentByID = async (topologyID: string) => { const res = await IncidentCommander.get<TopologyComponentItem[]>( `/component_names?id=eq.${topologyID}` @@ -216,6 +199,16 @@ export const getComponentTemplate = async (id: string) => { return res.data?.[0]; }; +export const getHealthCheckSummary = async (id: string) => { + const res = await resolve<HealthCheckSummary[] | null>( + IncidentCommander.get(`/checks?id=eq.${id}&select=id,name,icon,status,type`) + ); + if (res.data && res.data.length > 0) { + return res.data[0]; + } + return null; +}; + export const getHealthCheckItem = async (id: string) => { const res = await IncidentCommander.get<HealthCheck[] | null>( `/canaries?id=eq.${id}` @@ -247,15 +240,6 @@ export const getTopologySnapshot = async ( return res.data; }; -export type ComponentHealthCheckView = { - component_id: string; - id: string; - type: string; - name: string; - severity: string; - status: string; -}; - export const getComponentChecks = async (id: string) => { const res = await IncidentCommander.get<ComponentHealthCheckView[]>( `/checks_by_component?component_id=eq.${id}` diff --git a/src/api/types/configs.ts b/src/api/types/configs.ts index 2df65db68..d8d0fca06 100644 --- a/src/api/types/configs.ts +++ b/src/api/types/configs.ts @@ -43,6 +43,10 @@ export interface ConfigItem extends Timestamped, Avatar, Agent { id: string; name: string; }; + config_scrapers?: { + id: string; + name: string; + }; } export interface ConfigTypeRelationships extends Timestamped { diff --git a/src/api/types/health.ts b/src/api/types/health.ts index e8f067f41..d8929c2c8 100644 --- a/src/api/types/health.ts +++ b/src/api/types/health.ts @@ -34,6 +34,11 @@ export interface HealthCheck extends Timestamped, Avatar, Agent { severity?: string; } +export type HealthCheckSummary = Pick< + HealthCheck, + "id" | "name" | "icon" | "type" | "status" | "severity" +>; + export interface HealthCheckStatus { status: boolean; time: string; diff --git a/src/api/types/playbooks.ts b/src/api/types/playbooks.ts index 4f292b84c..c4bba00dd 100644 --- a/src/api/types/playbooks.ts +++ b/src/api/types/playbooks.ts @@ -1,8 +1,9 @@ import { Agent, CreatedAt, Avatar } from "../traits"; import { ConfigItem } from "./configs"; -import { HealthCheck } from "./health"; +import { HealthCheck, HealthCheckSummary } from "./health"; import { Topology } from "./topology"; import { User } from "./users"; +import { ElementType } from "react"; export type PlaybookRunStatus = | "scheduled" @@ -28,9 +29,9 @@ export type PlaybookRunAction = { error?: string; }; -export type PlaybookRunWithActions = PlaybookRun & { +export interface PlaybookRunWithActions extends PlaybookRun { actions: PlaybookRunAction[]; -}; +} export interface PlaybookRun extends CreatedAt, Avatar, Agent { id: string; @@ -46,7 +47,7 @@ export interface PlaybookRun extends CreatedAt, Avatar, Agent { /* relationships */ playbooks?: PlaybookSpec; component?: Pick<Topology, "id" | "name" | "icon">; - check?: Pick<HealthCheck, "id" | "name" | "icon">; + check?: HealthCheckSummary; config?: Pick<ConfigItem, "id" | "name" | "type" | "config_class">; } @@ -61,6 +62,18 @@ export type PlaybookSpec = { deleted_at?: string; }; +export type PlaybookParam = { + name: string; + label: string; +}; + +export type RunnablePlaybook = Omit<PlaybookSpec, "spec"> & { + check_id?: string; + config_id?: string; + component_id?: string; + parameters: PlaybookParam[]; +}; + export type NewPlaybookSpec = Omit< PlaybookSpec, "id" | "created_at" | "updated_at" | "deleted_at" | "created_by" diff --git a/src/api/types/topology.ts b/src/api/types/topology.ts index b3f3a3770..bd1e02e40 100644 --- a/src/api/types/topology.ts +++ b/src/api/types/topology.ts @@ -1,6 +1,7 @@ import { Agent, Namespaced, Timestamped } from "../traits"; import { ValueType } from "./common"; import { CostsData, Severity } from "./common"; +import { HealthCheckSummary } from "./health"; import { IncidentType } from "./incident"; import { User } from "./users"; @@ -35,12 +36,8 @@ export interface Topology extends Timestamped, CostsData, Agent, Namespaced { external_id?: string; topology_id?: string; summary?: { - incidents?: Record<IncidentType, - Record<"High" | "Medium" | "Low", number> - >; - insights?: Record<IncidentType, - Record<Severity, number | undefined> - >; + incidents?: Record<IncidentType, Record<"High" | "Medium" | "Low", number>>; + insights?: Record<IncidentType, Record<Severity, number | undefined>>; [key: string]: any; }; logs?: { @@ -55,8 +52,7 @@ export interface Topology extends Timestamped, CostsData, Agent, Namespaced { warning: number; unhealthy: number; }; -}; - +} export type ComponentTeamItem = { component_id: string; @@ -80,4 +76,8 @@ export interface ComponentTemplateItem extends Timestamped, Namespaced { labels: Record<string, string>; spec: any; schedule: string; +} + +export type ComponentHealthCheckView = HealthCheckSummary & { + component_id: string; }; diff --git a/src/components/Agents/AgentName.tsx b/src/components/Agents/AgentName.tsx index 7c4ad069a..958da8c86 100644 --- a/src/components/Agents/AgentName.tsx +++ b/src/components/Agents/AgentName.tsx @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { getAgentByID } from "../../api/services/topology"; +import { getAgentByID } from "../../api/services/agents"; import { Badge } from "../Badge"; type TopologyCardAgentProps = { diff --git a/src/components/Canary/CanaryTabs.tsx b/src/components/Canary/CanaryTabs.tsx index 5741efa2a..819397437 100644 --- a/src/components/Canary/CanaryTabs.tsx +++ b/src/components/Canary/CanaryTabs.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { HealthCheck } from "../../api/types/health"; import { Tab, Tabs } from "../Tabs/Tabs"; import { useQuery } from "@tanstack/react-query"; -import { getAgentByIDs } from "../../api/services/topology"; +import { getAgentByIDs } from "../../api/services/agents"; const defaultTabs = { all: { From 7bc202f8a2a1813a74be5aa815ecc0d75c62e730 Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 17:08:17 +0200 Subject: [PATCH 07/15] chore: switch to form-label global style --- pages/global.css | 5 +++++ src/components/AttachEvidenceDialog/index.tsx | 8 ++----- .../AutoCompleteDropdown.tsx | 6 +----- src/components/ConfigItem/index.tsx | 4 +--- .../Forms/Formik/FormikAuthFields.tsx | 1 - .../Formik/FormikAutocompleteDropdown.tsx | 6 +----- .../Forms/Formik/FormikCodeEditor.tsx | 6 +----- .../FormikNotificationsTemplateField.tsx | 4 +--- .../Forms/Formik/FormikSelectDropdown.tsx | 6 +----- src/components/Forms/SpecEditorForm.tsx | 8 ++----- .../Hypothesis/EvidenceSection/index.tsx | 2 +- .../ResponderTypes/Jira/index.tsx | 21 ++++--------------- .../ResponderTypes/MicrosoftPlanner/index.tsx | 19 ++++------------- .../InviteUserForm/InviteUserForm.tsx | 20 ++++-------------- .../ManageUserRoles/ManageUserRoles.tsx | 10 ++------- .../SchemaResourcePage/SchemaResourceEdit.tsx | 11 ++++------ src/components/TextArea/TextArea.tsx | 5 +---- src/components/TextInput/index.tsx | 5 +---- src/components/ory/ui/NodeInputDefault.tsx | 5 +---- 19 files changed, 37 insertions(+), 115 deletions(-) diff --git a/pages/global.css b/pages/global.css index 196d4e90f..936d067f7 100644 --- a/pages/global.css +++ b/pages/global.css @@ -35,6 +35,11 @@ } @layer components { + + .form-label { + @apply block text-sm font-semibold text-gray-700 mb-2; + } + .table-auto { @apply shadow-lg rounded-md py-2 px-4; } diff --git a/src/components/AttachEvidenceDialog/index.tsx b/src/components/AttachEvidenceDialog/index.tsx index d25729100..c7683829d 100644 --- a/src/components/AttachEvidenceDialog/index.tsx +++ b/src/components/AttachEvidenceDialog/index.tsx @@ -371,9 +371,7 @@ export function AttachEvidenceDialog({ <form onSubmit={handleSubmit(onSubmit)}> <div className="px-8"> <div className="mb-4"> - <div className="block text-sm font-bold text-gray-700 mb-2"> - Incident - </div> + <div className="form-label">Incident</div> <DropdownWithActions onQuery={fetchIncidentOptions} label="Incident" @@ -448,9 +446,7 @@ export function AttachEvidenceDialog({ )} </div> <div> - <div className="block text-sm font-bold text-gray-700 mb-2"> - Hypothesis - </div> + <div className="form-label">Hypothesis</div> <DropdownWithActions onQuery={(e) => { return fetchHypothesisOptions(e, watch("incident")); diff --git a/src/components/AutoCompleteDropdown/AutoCompleteDropdown.tsx b/src/components/AutoCompleteDropdown/AutoCompleteDropdown.tsx index d00e7670f..6a9adf9c2 100644 --- a/src/components/AutoCompleteDropdown/AutoCompleteDropdown.tsx +++ b/src/components/AutoCompleteDropdown/AutoCompleteDropdown.tsx @@ -20,11 +20,7 @@ export default function AutoCompleteDropdown({ }: Props) { return ( <div className="flex flex-col relative w-full"> - {label && ( - <label className={`block text-sm font-bold text-gray-700 mb-0`}> - {label} - </label> - )} + {label && <label className={`form-label mb-0`}>{label}</label>} <CreatableSelect className="h-full shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm border-gray-300 rounded-md" onChange={(value) => { diff --git a/src/components/ConfigItem/index.tsx b/src/components/ConfigItem/index.tsx index 7aaf34751..bfc0466ee 100644 --- a/src/components/ConfigItem/index.tsx +++ b/src/components/ConfigItem/index.tsx @@ -218,9 +218,7 @@ export const ConfigItem = ({ return ( <> {label && typeof label === "string" && ( - <label className="block text-sm font-medium text-gray-700 mb-2"> - {label} - </label> + <label className="form-label mb-2">{label}</label> )} {label && typeof label !== "string" && label} </> diff --git a/src/components/Forms/Formik/FormikAuthFields.tsx b/src/components/Forms/Formik/FormikAuthFields.tsx index 44d3ee7d4..3742747b9 100644 --- a/src/components/Forms/Formik/FormikAuthFields.tsx +++ b/src/components/Forms/Formik/FormikAuthFields.tsx @@ -24,7 +24,6 @@ export default function FormikAuthFields({ const [selectedMethod, setSelectedMethod] = useState<"None" | string>(() => { types.forEach((field) => { - console.log("field", field); if (get(values, `${name}.${Object.keys(field.value)[0]}`)) { return field.label; } diff --git a/src/components/Forms/Formik/FormikAutocompleteDropdown.tsx b/src/components/Forms/Formik/FormikAutocompleteDropdown.tsx index bdd71d271..f6f0db45b 100644 --- a/src/components/Forms/Formik/FormikAutocompleteDropdown.tsx +++ b/src/components/Forms/Formik/FormikAutocompleteDropdown.tsx @@ -44,11 +44,7 @@ export default function FormikAutocompleteDropdown({ return ( <div className="flex flex-col space-y-2 py-2"> - {label && ( - <label className={`block text-sm font-semibold text-gray-700`}> - {label} - </label> - )} + {label && <label className="form-label">{label}</label>} <CreatableSelect className="h-full shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm border-gray-300 rounded-md" onChange={(value) => { diff --git a/src/components/Forms/Formik/FormikCodeEditor.tsx b/src/components/Forms/Formik/FormikCodeEditor.tsx index 08fe75955..4cec61542 100644 --- a/src/components/Forms/Formik/FormikCodeEditor.tsx +++ b/src/components/Forms/Formik/FormikCodeEditor.tsx @@ -97,11 +97,7 @@ export function FormikCodeEditor({ return ( <div className={className}> {label && ( - <label - className={`block text-sm font-bold text-gray-700 mb-2 ${labelClassName}`} - > - {label} - </label> + <label className={`form-label ${labelClassName}`}>{label}</label> )} <CodeEditor onChange={(v) => { diff --git a/src/components/Forms/Formik/FormikNotificationsTemplateField.tsx b/src/components/Forms/Formik/FormikNotificationsTemplateField.tsx index 919566435..ebb23733d 100644 --- a/src/components/Forms/Formik/FormikNotificationsTemplateField.tsx +++ b/src/components/Forms/Formik/FormikNotificationsTemplateField.tsx @@ -7,9 +7,7 @@ type Props = { export default function FormikNotificationsTemplateField({ name }: Props) { return ( <div className="flex flex-col gap-2"> - <label className={`block text-sm font-bold text-gray-700`}> - Markdown Template - </label> + <label className={`form-label`}>Markdown Template</label> <FormikCodeEditor className="flex flex-col h-[200px]" fieldName={name} diff --git a/src/components/Forms/Formik/FormikSelectDropdown.tsx b/src/components/Forms/Formik/FormikSelectDropdown.tsx index a0916d90a..d270c935d 100644 --- a/src/components/Forms/Formik/FormikSelectDropdown.tsx +++ b/src/components/Forms/Formik/FormikSelectDropdown.tsx @@ -59,11 +59,7 @@ export default function FormikSelectDropdown({ return ( <div className="flex flex-col space-y-2 py-2"> - {label && ( - <label className={`block text-sm font-semibold text-gray-700`}> - {label} - </label> - )} + {label && <label className="form-label">{label}</label>} <Select name={name} className="h-full shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm border-gray-300 rounded-md" diff --git a/src/components/Forms/SpecEditorForm.tsx b/src/components/Forms/SpecEditorForm.tsx index cebfd3a3d..0b45e5bfa 100644 --- a/src/components/Forms/SpecEditorForm.tsx +++ b/src/components/Forms/SpecEditorForm.tsx @@ -121,9 +121,7 @@ export default function SpecEditorForm({ )} {isFieldSupportedByResourceType("labels") && ( <div className="flex flex-col space-y-2"> - <label className="block text-sm font-bold text-gray-700"> - Labels - </label> + <label className="form-label">Labels</label> <FormikCodeEditor fieldName="labels" format="json" @@ -175,9 +173,7 @@ export default function SpecEditorForm({ <div className="flex flex-col py-2"> {showCodeEditorOnly ? ( <> - <label className="block text-sm font-bold text-gray-700"> - Specs - </label> + <label className="form-label">Specs</label> <FormikCodeEditor format={specFormat} // map to the spec field to the spec field in the resource diff --git a/src/components/Hypothesis/EvidenceSection/index.tsx b/src/components/Hypothesis/EvidenceSection/index.tsx index 620a62d83..c8030e8b2 100644 --- a/src/components/Hypothesis/EvidenceSection/index.tsx +++ b/src/components/Hypothesis/EvidenceSection/index.tsx @@ -403,7 +403,7 @@ export function HealthEvidenceViewer({ </div> <div className="flex flex-col w-auto px-2 py-2"> <div className="flex flex-row"> - <label className="block text-sm font-medium text-gray-700"> + <label className="form-label"> Latency: <span className="pl-2 inline-block"> <Duration ms={check.latency.p99} /> diff --git a/src/components/IncidentDetails/ResponderTypes/Jira/index.tsx b/src/components/IncidentDetails/ResponderTypes/Jira/index.tsx index d0db84f81..b6bdd82c0 100644 --- a/src/components/IncidentDetails/ResponderTypes/Jira/index.tsx +++ b/src/components/IncidentDetails/ResponderTypes/Jira/index.tsx @@ -89,11 +89,7 @@ export const Jira = ({ setValue("project", ""); setValue("issueType", ""); }} - label={ - <label className="block text-sm font-bold text-gray-700 mb-2"> - Jira Instance - </label> - } + label={<label className="form-label">Jira Instance</label>} value={jiraProjectType} id="config-type" isDisabled={jiraProjectType} @@ -121,10 +117,7 @@ export const Jira = ({ namePath="$.name" valuePath="$.key" label={ - <label - htmlFor="project" - className="block text-sm font-bold text-gray-700 mb-2 mt-4" - > + <label htmlFor="project" className="form-label mt-4"> Project </label> } @@ -153,10 +146,7 @@ export const Jira = ({ namePath="$" valuePath="$" label={ - <label - htmlFor="issueType" - className="block text-sm font-bold text-gray-700 mb-2 mt-4" - > + <label htmlFor="issueType" className="form-label mt-4"> Issue Type </label> } @@ -185,10 +175,7 @@ export const Jira = ({ namePath="$" valuePath="$" label={ - <label - htmlFor="priority" - className="block text-sm font-bold text-gray-700 mb-2 mt-4" - > + <label htmlFor="priority" className="form-label mt-4"> Priority </label> } diff --git a/src/components/IncidentDetails/ResponderTypes/MicrosoftPlanner/index.tsx b/src/components/IncidentDetails/ResponderTypes/MicrosoftPlanner/index.tsx index 6dfadc760..aa89b8841 100644 --- a/src/components/IncidentDetails/ResponderTypes/MicrosoftPlanner/index.tsx +++ b/src/components/IncidentDetails/ResponderTypes/MicrosoftPlanner/index.tsx @@ -90,9 +90,7 @@ export const MicrosoftPlanner = ({ setValue("issueType", ""); }} label={ - <label className="block text-sm font-bold text-gray-700 mb-2"> - Microsoft Planner Instance - </label> + <label className="form-label">Microsoft Planner Instance</label> } value={msProjectType} isDisabled={msProjectType} @@ -121,10 +119,7 @@ export const MicrosoftPlanner = ({ namePath="$.name" valuePath="$.id" label={ - <label - htmlFor="plan_id" - className="block text-sm font-bold text-gray-700 mb-2 mt-4" - > + <label htmlFor="plan_id" className="form-label mt-4"> Plan ID </label> } @@ -153,10 +148,7 @@ export const MicrosoftPlanner = ({ namePath="$.name" valuePath="$.id" label={ - <label - htmlFor="bucket_id" - className="block text-sm font-bold text-gray-700 mb-2 mt-4" - > + <label htmlFor="bucket_id" className="form-label mt-4"> Bucket ID </label> } @@ -185,10 +177,7 @@ export const MicrosoftPlanner = ({ namePath="$" valuePath="$" label={ - <label - htmlFor="priority" - className="block text-sm font-bold text-gray-700 mb-2 mt-4" - > + <label htmlFor="priority" className="form-label mt-4"> Priority </label> } diff --git a/src/components/InviteUserForm/InviteUserForm.tsx b/src/components/InviteUserForm/InviteUserForm.tsx index 54acaa504..f730c2283 100644 --- a/src/components/InviteUserForm/InviteUserForm.tsx +++ b/src/components/InviteUserForm/InviteUserForm.tsx @@ -57,10 +57,7 @@ export function InviteUserForm({ noValidate > <div> - <label - className="block text-sm font-medium text-gray-700" - htmlFor="firstName" - > + <label className="form-label" htmlFor="firstName"> First name </label> <div className="mt-1"> @@ -80,10 +77,7 @@ export function InviteUserForm({ )} </div> <div> - <label - className="block text-sm font-medium text-gray-700" - htmlFor="lastName" - > + <label className="form-label" htmlFor="lastName"> Last name </label> <div className="mt-1"> @@ -103,10 +97,7 @@ export function InviteUserForm({ )} </div> <div> - <label - htmlFor="email" - className="block text-sm font-medium text-gray-700" - > + <label htmlFor="email" className="form-label"> Email </label> <div className="mt-1"> @@ -128,10 +119,7 @@ export function InviteUserForm({ )} </div> <div> - <label - htmlFor="role" - className="block text-sm font-medium text-gray-700" - > + <label htmlFor="role" className="form-label"> Role </label> <div className="mt-1"> diff --git a/src/components/ManageUserRoles/ManageUserRoles.tsx b/src/components/ManageUserRoles/ManageUserRoles.tsx index ba2c2b170..68c113ea4 100644 --- a/src/components/ManageUserRoles/ManageUserRoles.tsx +++ b/src/components/ManageUserRoles/ManageUserRoles.tsx @@ -56,10 +56,7 @@ export function ManageUserRoles({ noValidate > <div> - <label - className="block text-sm font-medium text-gray-700" - htmlFor="userId" - > + <label className="form-label" htmlFor="userId"> User </label> <div className="mt-1"> @@ -88,10 +85,7 @@ export function ManageUserRoles({ )} </div> <div> - <label - className="block text-sm font-medium text-gray-700" - htmlFor="role" - > + <label className="form-label" htmlFor="role"> Role </label> <div className="mt-1"> diff --git a/src/components/SchemaResourcePage/SchemaResourceEdit.tsx b/src/components/SchemaResourcePage/SchemaResourceEdit.tsx index 4d8d3db04..0be822398 100644 --- a/src/components/SchemaResourcePage/SchemaResourceEdit.tsx +++ b/src/components/SchemaResourcePage/SchemaResourceEdit.tsx @@ -261,7 +261,7 @@ export function SchemaResourceEdit({ <div className="space-y-2"> <label htmlFor="icon-picker" - className="block text-sm font-bold text-gray-700 pt-4" + className="form-label pt-4" > Icon </label> @@ -324,7 +324,7 @@ export function SchemaResourceEdit({ render={({ field: { onChange, value } }) => { return ( <div className="h-[100px]"> - <label className="block text-sm font-bold text-gray-700"> + <label className="form-label"> Labels </label> <CodeEditor @@ -362,7 +362,7 @@ export function SchemaResourceEdit({ render={({ field: { onChange, value } }) => { return ( <div className="space-y-2"> - <label className="block text-sm font-bold text-gray-700"> + <label className="form-label"> Schedule </label> <AutoCompleteDropdown @@ -412,10 +412,7 @@ export function SchemaResourceEdit({ )} </div> <div className="px-8 space-y-2"> - <label - htmlFor="icon-picker" - className="block text-sm font-bold text-gray-700" - > + <label htmlFor="icon-picker" className="form-label"> Spec </label> <div className="flex flex-col h-[min(850px,calc(100vh-500px))]"> diff --git a/src/components/TextArea/TextArea.tsx b/src/components/TextArea/TextArea.tsx index 8f29fc832..4f0c9fe4a 100644 --- a/src/components/TextArea/TextArea.tsx +++ b/src/components/TextArea/TextArea.tsx @@ -19,10 +19,7 @@ export function TextArea({ return ( <> {label && ( - <label - htmlFor={id} - className={`block text-sm font-bold text-gray-700 mb-2 ${labelClassName}`} - > + <label htmlFor={id} className={`form-label ${labelClassName}`}> {label} </label> )} diff --git a/src/components/TextInput/index.tsx b/src/components/TextInput/index.tsx index c0b26674c..842a59562 100644 --- a/src/components/TextInput/index.tsx +++ b/src/components/TextInput/index.tsx @@ -26,10 +26,7 @@ export function TextInput({ return ( <> {label && ( - <label - htmlFor={id} - className={`block text-sm font-semibold text-gray-700 mb-2 ${labelClassName}`} - > + <label htmlFor={id} className={`form-label ${labelClassName}`}> {label} </label> )} diff --git a/src/components/ory/ui/NodeInputDefault.tsx b/src/components/ory/ui/NodeInputDefault.tsx index a40588540..3f104b3c3 100644 --- a/src/components/ory/ui/NodeInputDefault.tsx +++ b/src/components/ory/ui/NodeInputDefault.tsx @@ -22,10 +22,7 @@ export function NodeInputDefault(props: NodeInputProps) { // Render a generic text input field. return ( <div className="space-y-1"> - <label - htmlFor={attributes.name} - className="block text-sm font-bold text-gray-700" - > + <label htmlFor={attributes.name} className="form-label"> {node.meta.label?.text} </label> <div className="mt-1"> From 1a08bb53e00b3879ef79354d3f4ae05f65631c56 Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 18:05:36 +0200 Subject: [PATCH 08/15] feat: playbook improvements --- .../Runs/Actions/PlaybookRunActionFetch.tsx | 4 +- .../Runs/Actions/PlaybookRunsActionItem.tsx | 8 +- .../Runs/Actions/PlaybookRunsActions.tsx | 110 ++++++++---------- .../Playbooks/Runs/PlaybookRunsList.tsx | 4 +- .../Playbooks/Runs/PlaybookRunsSidePanel.tsx | 36 +++--- .../Playbooks/Runs/PlaybookRunsStatus.tsx | 48 ++------ .../Runs/Submit/AddPlaybookToRunParams.tsx | 36 ------ .../Runs/Submit/PlaybookRunParams.tsx | 38 ++++++ .../Runs/Submit/PlaybooksDropdownMenu.tsx | 91 +++++++++++++++ .../Runs/Submit/SelectPlaybookToRun.tsx | 91 --------------- .../Runs/Submit/SubmitPlaybookRunForm.tsx | 52 ++++++--- .../SelectPlaybookToRun.unit.test.tsx | 6 +- src/components/Playbooks/Runs/services.tsx | 41 +++++++ 13 files changed, 290 insertions(+), 275 deletions(-) delete mode 100644 src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx create mode 100644 src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx create mode 100644 src/components/Playbooks/Runs/Submit/PlaybooksDropdownMenu.tsx delete mode 100644 src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx create mode 100644 src/components/Playbooks/Runs/services.tsx diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunActionFetch.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunActionFetch.tsx index a35da032b..2ef54fcc7 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunActionFetch.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunActionFetch.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import { getPlaybookRunActionById } from "../../../../api/services/playbooks"; -import TableSkeletonLoader from "../../../SkeletonLoader/TableSkeletonLoader"; import PlaybooksRunActionsResults from "./PlaybooksActionsResults"; +import { Loading } from "../../../Loading"; type Props = { playbookRunActionId: string; @@ -15,7 +15,7 @@ export default function PlaybookRunActionFetch({ playbookRunActionId }: Props) { }); if (isLoading || !action) { - return <TableSkeletonLoader />; + return <Loading text="..." />; } return <PlaybooksRunActionsResults action={action} />; diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx index cc17ec58c..e64721c5c 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActionItem.tsx @@ -1,6 +1,6 @@ import { PlaybookRunAction } from "../../../../api/types/playbooks"; import { Age } from "../../../../ui/Age"; -import PlaybookRunsStatus from "../PlaybookRunsStatus"; +import { PlaybookStatusIcon } from "../../../Icon/PlaybookStatusIcon"; type PlaybookRunsActionItemProps = { action: PlaybookRunAction; @@ -23,14 +23,14 @@ export default function PlaybookRunsActionItem({ }`} > <div className="flex flex-col"> - <div className="flex flex-row gap-2 text-sm font-medium text-gray-600"> - <PlaybookRunsStatus status={action.status} hideStatusLabel /> + <div className="flex flex-row gap-2 text-sm text-gray-600"> + <PlaybookStatusIcon status={action.status} /> {action.name} </div> <div className={`text-xs flex flex-row gap-1 items-center`}></div> </div> <div className="flex flex-col"> - <div className="text-sm font-medium text-gray-600"> + <div className="text-sm text-gray-600"> <Age from={action.start_time} to={action.end_time} /> </div> </div> diff --git a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx index dfc9047d8..b9daf8dfb 100644 --- a/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx +++ b/src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx @@ -1,15 +1,16 @@ -import { ReactNode, useMemo, useState } from "react"; +import { useState } from "react"; import { Link } from "react-router-dom"; import { Avatar } from "../../../Avatar"; -import { Icon } from "../../../Icon"; import { Age } from "../../../../ui/Age"; -import PlaybookRunsStatus from "./../PlaybookRunsStatus"; +import { PlaybookStatusDescription } from "./../PlaybookRunsStatus"; import PlaybookRunActionFetch from "./PlaybookRunActionFetch"; import PlaybookRunsActionItem from "./PlaybookRunsActionItem"; import { PlaybookRunWithActions, PlaybookRunAction } from "../../../../api/types/playbooks"; +import { getResourceForRun } from "../services"; +import VerticalDescription from "../../../../ui/description/VerticalDescription"; type PlaybookRunActionsProps = { data: PlaybookRunWithActions; @@ -17,72 +18,59 @@ type PlaybookRunActionsProps = { export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) { const [selectedAction, setSelectedAction] = useState<PlaybookRunAction>(); - - const headerContent = useMemo( - () => - new Map<string, ReactNode>([ - [ - "Playbook", - <Link - className="text-blue-500 hover:underline" - to={`/playbooks/${data.playbook_id}`} - > - {data.playbooks?.name} - </Link> - ], - [ - "Component", - <Link - className="text-blue-500 hover:underline" - to={`/topology/${data.component_id}`} - > - <Icon name={data.component?.icon} className="mr-1 h-5 w-5" /> - {data.component?.name} - </Link> - ], - [ - "Status", - <div className="flex flex-row gap-2 items-center"> - <PlaybookRunsStatus status={data.status} /> - </div> - ], - ["Start Time", <Age from={data.start_time} />], - ["Duration", <Age from={data.start_time} to={data.end_time} />], - [ - "Triggered By", - data.created_by ? ( - <div className="flex flex-row gap-2"> - <Avatar user={data.created_by} />{" "} - <span>{data.created_by.name}</span> - </div> - ) : null - ] - ]), - [data] - ); + let resource = getResourceForRun(data); return ( <div className="flex flex-col flex-1 gap-6"> <div className="flex flex-col px-4 py-2"> <div className="flex flex-row w-full lg:w-auto gap-4"> - {Array.from(headerContent).map(([label, value]) => ( - <div - key={label} - className="flex flex-col gap-2 px-2 flex-1 xl:flex-none" - > - <div className="text-sm overflow-hidden truncate text-gray-500"> - {label} - </div> - <div className="flex justify-start break-all text-sm font-semibold"> - {value} - </div> - </div> - ))} + <VerticalDescription + label="Playbook" + value={ + <Link + className="text-blue-500 hover:underline" + to={`/playbooks/runs?playbook=${data.playbook_id}`} + > + {data.playbooks?.name} + </Link> + } + /> + + <VerticalDescription + label="Status" + value={<PlaybookStatusDescription status={data.status} />} + /> + <VerticalDescription + label="Started" + value={<Age from={data.start_time} suffix={true} />} + /> + <VerticalDescription + label="Duration" + value={<Age from={data.start_time} to={data.end_time} />} + /> + <VerticalDescription + label="Queued" + value={<Age from={data.created_at} to={data.start_time} />} + /> + {resource && ( + <VerticalDescription label={resource.label} value={resource.link} /> + )} + {data.created_by && ( + <VerticalDescription + label="Triggered By" + value={ + <div className="flex flex-row gap-2"> + <Avatar user={data.created_by} />{" "} + <span>{data.created_by.name}</span> + </div> + } + /> + )} </div> </div> <div className="flex flex-row h-full"> - <div className="flex flex-col w-[15rem] lg:w-[20rem] h-full px-2 border-t border-gray-200"> - <div className="flex flex-row items-center justify-between px-4 py-2 mb-2 border-b border-gray-100"> + <div className="flex flex-col w-[15rem] lg:w-[20rem] h-full pr-2 border-gray-200"> + <div className="flex flex-row items-center justify-between ml-[-25px] mr-[-15px] pl-[25px] py-2 mb-2 border-t border-gray-200"> <div className="font-semibold text-gray-600">Actions</div> </div> <div className="flex flex-col flex-1 overflow-y-auto gap-2"> diff --git a/src/components/Playbooks/Runs/PlaybookRunsList.tsx b/src/components/Playbooks/Runs/PlaybookRunsList.tsx index 73dccddc4..729479db7 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsList.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsList.tsx @@ -8,7 +8,7 @@ import { DateCell } from "../../../ui/table"; import { Avatar } from "../../Avatar"; import { DataTable, PaginationOptions } from "../../DataTable"; import { Icon } from "../../Icon"; -import PlaybookRunsStatus from "./PlaybookRunsStatus"; +import { PlaybookStatusDescription } from "./PlaybookRunsStatus"; const playbookRunsTableColumns: ColumnDef<PlaybookRun>[] = [ { @@ -46,7 +46,7 @@ const playbookRunsTableColumns: ColumnDef<PlaybookRun>[] = [ accessorKey: "status", cell: ({ getValue }) => { const status = getValue<PlaybookRunStatus>(); - return <PlaybookRunsStatus status={status} className="capitalize" />; + return <PlaybookStatusDescription status={status} />; } }, { diff --git a/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx b/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx index 671407003..223a754c0 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsSidePanel.tsx @@ -14,6 +14,7 @@ import { refreshButtonClickedTrigger } from "../../SlidingSideBar"; import Title from "../../Title/title"; import { Age } from "../../../ui/Age"; import { PlaybookRun } from "../../../api/types/playbooks"; +import { PlaybookStatusIcon } from "../../Icon/PlaybookStatusIcon"; type TopologySidePanelProps = { panelType: "topology"; @@ -34,24 +35,26 @@ const runsColumns: ColumnDef<PlaybookRun, any>[] = [ { header: "Name", id: "name", - size: 60, + size: 80, cell: ({ row }) => { const name = row.original.playbooks?.name; - return <span>{name}</span>; + return ( + <span> + <PlaybookStatusIcon status={row.original.status} /> {name} + </span> + ); } }, { - header: "Status", - id: "status", - accessorKey: "status", - size: 60 - }, - { - header: "Duration", - id: "duration", + header: "Age", + id: "age", cell: ({ row }) => { - const { start_time, end_time } = row.original; - return <Age from={start_time} to={end_time} />; + return ( + <Age + from={row.original.start_time} + className="text-xs text-slate-500 pr-2" + /> + ); }, size: 20 } @@ -136,17 +139,12 @@ export function PlaybookRunsSidePanel({ }); } }} - stickyHead virtualizedRowEstimatedHeight={40} columnsClassName={{ - name: "", - status: "fit-content", - duration: "fit-content" + age: "text-right" }} onRowClick={(row) => { - navigate( - `/playbooks/runs/018af987-d406-a5df-5a63-a07d8842c7cf/${row.original.id}` - ); + navigate(`/playbooks/runs/${row.original.id}`); }} /> </div> diff --git a/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx b/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx index 08aa3c980..a2ccfa3e0 100644 --- a/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx +++ b/src/components/Playbooks/Runs/PlaybookRunsStatus.tsx @@ -1,45 +1,13 @@ -import { BsFillExclamationCircleFill } from "react-icons/bs"; -import { FaCheckCircle, FaClock, FaSpinner } from "react-icons/fa"; -import { PlaybookRunStatus } from "../../../api/types/playbooks"; - -const statusIconMap: Record<PlaybookRunStatus, { icon: React.ReactNode }> = { - completed: { - icon: <FaCheckCircle className="text-green-500" /> - }, - cancelled: { - icon: <BsFillExclamationCircleFill className="text-yellow-500" /> - }, - failed: { - icon: <BsFillExclamationCircleFill className="text-red-500" /> - }, - pending: { - icon: <FaCheckCircle className="text-gray-500" /> - }, - running: { - icon: <FaSpinner className="text-yellow-500" /> - }, - scheduled: { - icon: <FaClock className="text-gray-500" /> - } -}; - -type PlaybookRunsStatusProps = { - status: PlaybookRunStatus; - className?: string; - hideStatusLabel?: boolean; -}; - -export default function PlaybookRunsStatus({ - status, - className = "capitalize", - hideStatusLabel = false -}: PlaybookRunsStatusProps) { - const { icon } = statusIconMap[status]; +import { + PlaybookRunsStatusProps, + PlaybookStatusIcon +} from "../../Icon/PlaybookStatusIcon"; +export function PlaybookStatusDescription({ status }: PlaybookRunsStatusProps) { return ( - <div className={`flex items-center ${className}`}> - {icon} - {!hideStatusLabel && <span className="ml-2">{status}</span>} + <div> + <PlaybookStatusIcon status={status} /> + <span className="capitalize">{status}</span> </div> ); } diff --git a/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx b/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx deleted file mode 100644 index f1ab1d7b0..000000000 --- a/src/components/Playbooks/Runs/Submit/AddPlaybookToRunParams.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useMemo } from "react"; -import FormikTextInput from "../../../Forms/Formik/FormikTextInput"; -import { PlaybookSpec } from "../../../../api/types/playbooks"; - -type AddPlaybookToRunParamsProps = { - playbookSpec: PlaybookSpec; -}; - -export default function AddPlaybookToRunParams({ - playbookSpec -}: AddPlaybookToRunParamsProps) { - const playbookParams = useMemo( - () => - (playbookSpec.spec.parameters as { name: string; label: string }[]) ?? [], - [playbookSpec] - ); - - return ( - <div className="flex flex-col gap-4"> - <div className="font-bold">Params</div> - <div className="flex flex-col gap-2"> - {playbookParams.length > 0 ? ( - playbookParams.map(({ name, label }) => ( - <FormikTextInput - name={`params.${name}`} - label={label} - key={`${label}-${name}`} - /> - )) - ) : ( - <div className="text-gray-400">No parameters for this playbook.</div> - )} - </div> - </div> - ); -} diff --git a/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx b/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx new file mode 100644 index 000000000..6a5b129bb --- /dev/null +++ b/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx @@ -0,0 +1,38 @@ +import FormikTextInput from "../../../Forms/Formik/FormikTextInput"; +import { RunnablePlaybook } from "../../../../api/types/playbooks"; +import { ActionResource } from "../services"; + +export default function PlaybookRunParams({ + playbook, + resource +}: { + playbook: RunnablePlaybook; + resource?: ActionResource | null; +}) { + return ( + <div className="flex flex-col my-4"> + {resource && ( + <> + <div className="flex flex-col gap-2 mb-2"> + <label className="form-label mb-0">Resource</label> + {resource.link} + </div> + <div className="border-b border-gray-200 mb-4 mt-2 " /> + </> + )} + <div className="flex flex-col gap-2"> + {playbook.parameters.length > 0 ? ( + playbook.parameters.map((i) => ( + <FormikTextInput + name={`params.${i.name}`} + label={i.label} + key={i.name} + /> + )) + ) : ( + <div className="text-gray-400">No parameters for this playbook.</div> + )} + </div> + </div> + ); +} diff --git a/src/components/Playbooks/Runs/Submit/PlaybooksDropdownMenu.tsx b/src/components/Playbooks/Runs/Submit/PlaybooksDropdownMenu.tsx new file mode 100644 index 000000000..30fde8762 --- /dev/null +++ b/src/components/Playbooks/Runs/Submit/PlaybooksDropdownMenu.tsx @@ -0,0 +1,91 @@ +import { Fragment, useState } from "react"; +import { FaCog } from "react-icons/fa"; +import { useGetPlaybooksToRun } from "../../../../api/query-hooks/playbooks"; +import { RunnablePlaybook } from "../../../../api/types/playbooks"; +import SubmitPlaybookRunForm from "./SubmitPlaybookRunForm"; + +import { Menu, Transition } from "@headlessui/react"; +import { ChevronDownIcon } from "@heroicons/react/solid"; + +type PlaybooksDropdownMenuProps = { + component_id?: string; + config_id?: string; + check_id?: string; + className?: string; +}; + +export default function PlaybooksDropdownMenu({ + check_id, + component_id, + config_id, + className = "text-sm btn-white" +}: PlaybooksDropdownMenuProps) { + const [selectedPlaybookSpec, setSelectedPlaybookSpec] = + useState<RunnablePlaybook>(); + + const { + data: playbooks, + isLoading, + error + } = useGetPlaybooksToRun({ + check_id, + component_id, + config_id + }); + + if (error || playbooks?.length === 0 || isLoading) { + return null; + } + + //nolint + return ( + <div className="text-right"> + <Menu as="div" className="relative inline-block text-left"> + <div> + <Menu.Button className="btn-white"> + <FaCog className="mr-2 h-4 w-4 mt-0.5" /> + Playbooks + <ChevronDownIcon + className="ml-2 -mr-1 h-5 w-5" + aria-hidden="true" + /> + </Menu.Button> + </div> + <Transition + as={Fragment} + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <Menu.Items className="menu-items"> + {playbooks?.map((playbook) => ( + <Menu.Item + as="button" + className="menu-item" + onClick={() => setSelectedPlaybookSpec(playbook)} + key={playbook.id} + > + {playbook.name} + </Menu.Item> + ))} + </Menu.Items> + </Transition> + </Menu> + {selectedPlaybookSpec && ( + <SubmitPlaybookRunForm + componentId={component_id} + checkId={check_id} + configId={config_id} + isOpen={!!selectedPlaybookSpec} + onClose={() => { + setSelectedPlaybookSpec(undefined); + }} + playbook={selectedPlaybookSpec} + /> + )} + </div> + ); +} diff --git a/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx b/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx deleted file mode 100644 index 42e255d0e..000000000 --- a/src/components/Playbooks/Runs/Submit/SelectPlaybookToRun.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Float } from "@headlessui-float/react"; -import { Fragment, useState } from "react"; -import { FaChevronDown, FaChevronUp } from "react-icons/fa"; -import { useGetPlaybooksToRun } from "../../../../api/query-hooks/playbooks"; -import { Button } from "../../../Button"; -import { Menu } from "@headlessui/react"; -import SubmitPlaybookRunForm from "./SubmitPlaybookRunForm"; -import { PlaybookSpec } from "../../../../api/types/playbooks"; - -type SelectPlaybookToRunProps = { - component_id?: string; - config_id?: string; - check_id?: string; - className?: string; -}; - -export default function SelectPlaybookToRun({ - check_id, - component_id, - config_id, - className = "text-sm btn-white" -}: SelectPlaybookToRunProps) { - const [selectedPlaybookSpec, setSelectedPlaybookSpec] = - useState<PlaybookSpec>(); - - const { - data: playbooks, - isLoading, - error - } = useGetPlaybooksToRun({ - check_id, - component_id, - config_id - }); - - if (error || playbooks?.length === 0 || isLoading) { - return null; - } - - return ( - <div className="flex flex-col items-center p-1 relative"> - <Menu> - <Float portal> - <Menu.Button as={Fragment}> - {({ open }) => ( - <div className="flex items-center"> - <Button - text={ - <div className="flex flex-row gap-2"> - <span>Playbooks</span> - {!open ? <FaChevronDown /> : <FaChevronUp />} - </div> - } - className={className} - disabled={isLoading} - /> - </div> - )} - </Menu.Button> - <Menu.Items - as="div" - className={`mt-2 origin-top-right w-56 bg-white divide-y divide-gray-100 rounded-md shadow-card focus:outline-none z-10`} - > - {playbooks?.map((playbook) => ( - <Menu.Item - className="flex text-left w-full text-gray-700 hover:bg-gray-200 hover:rounded-md p-1.5" - as="button" - onClick={() => setSelectedPlaybookSpec(playbook)} - key={playbook.id} - > - {playbook.name} - </Menu.Item> - ))} - </Menu.Items> - </Float> - </Menu> - {selectedPlaybookSpec && ( - <SubmitPlaybookRunForm - componentId={component_id} - checkId={check_id} - configId={config_id} - isOpen={!!selectedPlaybookSpec} - onClose={() => { - setSelectedPlaybookSpec(undefined); - }} - playbookSpec={selectedPlaybookSpec} - /> - )} - </div> - ); -} diff --git a/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx b/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx index e263918b3..02fff827f 100644 --- a/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx +++ b/src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx @@ -4,21 +4,24 @@ import { useSubmitPlaybookRunMutation } from "../../../../api/query-hooks/playbo import { Button } from "../../../Button"; import { Modal } from "../../../Modal"; import { toastError, toastSuccess } from "../../../Toast/toast"; -import AddPlaybookToRunParams from "./AddPlaybookToRunParams"; -import { PlaybookSpec } from "../../../../api/types/playbooks"; +import PlaybookRunParams from "./PlaybookRunParams"; +import { RunnablePlaybook } from "../../../../api/types/playbooks"; +import { Link } from "react-router-dom"; +import { getResourceForRun } from "../services"; export type SubmitPlaybookRunFormValues = { // if this is present in the form, we show step to add params id: string; component_id?: string; config_id?: string; + check_id?: string; params?: Record<string, any>; }; type Props = { isOpen: boolean; onClose: () => void; - playbookSpec: PlaybookSpec; + playbook: RunnablePlaybook; checkId?: string; componentId?: string; configId?: string; @@ -27,25 +30,37 @@ type Props = { export default function SubmitPlaybookRunForm({ isOpen, onClose, - playbookSpec, - componentId, + playbook, checkId, + componentId, configId }: Props) { const initialValues: Partial<SubmitPlaybookRunFormValues> = useMemo( () => ({ - id: playbookSpec.id, + id: playbook.id, params: undefined, component_id: componentId, check_id: checkId, config_id: configId }), - [checkId, componentId, configId, playbookSpec.id] + [checkId, componentId, configId, playbook.id] ); + console.log("initialValues", initialValues); + const { mutate: submitPlaybookRun } = useSubmitPlaybookRunMutation({ - onSuccess: () => { - toastSuccess("Playbook run submitted successfully"); + onSuccess: (run) => { + toastSuccess( + <> + <Link + className="underline text-blue-600 hover:text-blue-800 visited:text-blue-600" + to={`/playbooks/runs/${run.run_id}`} + > + Playbook Run{" "} + </Link>{" "} + {" submitted successfully"} + </> + ); onClose(); }, onError: (error) => { @@ -53,14 +68,16 @@ export default function SubmitPlaybookRunForm({ } }); + const resource = getResourceForRun(initialValues); + return ( <Modal - title={playbookSpec.name} + title={playbook.name} open={isOpen} onClose={onClose} size="slightly-small" - bodyClass="" - containerClassName="" + // bodyClass="" + // containerClassName="" > <Formik initialValues={initialValues} @@ -70,14 +87,15 @@ export default function SubmitPlaybookRunForm({ > {({ values, handleSubmit }) => { return ( - <Form onSubmit={handleSubmit} className="flex flex-col gap-2"> - <div className="flex p-4 flex-col gap-2"> - <AddPlaybookToRunParams playbookSpec={playbookSpec} /> + <Form onSubmit={handleSubmit} className="flex flex-col "> + <div className="flex flex-col gap-2"> + <PlaybookRunParams playbook={playbook} resource={resource} /> </div> - <div className="flex flex-row p-4 justify-end bg-gray-200 rounded-b rounded-md"> + + <div className="flex justify-end px-4 mx-[-30px] pr-[30px] rounded-b py-4 space-x-2 bg-slate-50 ring-1 ring-black/5 "> <Button disabled={values.id === undefined} - text="Submit" + text="Run" type="submit" /> </div> diff --git a/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx b/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx index 15b0c07f7..8e3399cf3 100644 --- a/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx +++ b/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx @@ -4,7 +4,7 @@ import userEvent from "@testing-library/user-event"; import { rest } from "msw"; import { setupServer } from "msw/node"; import { PlaybookSpec } from "../../../Settings/PlaybookSpecsTable"; -import SelectPlaybookToRun from "./../SelectPlaybookToRun"; +import PlaybooksDropdownMenu from "../PlaybooksDropdownMenu"; const playbooks: PlaybookSpec[] = [ { @@ -48,7 +48,7 @@ describe("SelectPlaybookToRun", () => { it("should render dropdown list with playbooks", async () => { render( <QueryClientProvider client={queryClient}> - <SelectPlaybookToRun component_id="component_id" /> + <PlaybooksDropdownMenu component_id="component_id" /> </QueryClientProvider> ); @@ -65,7 +65,7 @@ describe("SelectPlaybookToRun", () => { it("should open runs page, when you click a playbook item", async () => { render( <QueryClientProvider client={queryClient}> - <SelectPlaybookToRun check_id="check_id" /> + <PlaybooksDropdownMenu check_id="check_id" /> </QueryClientProvider> ); diff --git a/src/components/Playbooks/Runs/services.tsx b/src/components/Playbooks/Runs/services.tsx new file mode 100644 index 000000000..207f32ac0 --- /dev/null +++ b/src/components/Playbooks/Runs/services.tsx @@ -0,0 +1,41 @@ +import { isEmpty } from "lodash"; +import ConfigLink from "../../ConfigLink/ConfigLink"; +import { CheckLink } from "../../HealthChecks/CheckLink"; +import { TopologyLink } from "../../TopologyLink"; +import { SubmitPlaybookRunFormValues } from "./Submit/SubmitPlaybookRunForm"; + +export type ActionResource = { + label: string; + link: React.ReactElement; + id: string; +}; + +export function getResourceForRun( + data: Partial<SubmitPlaybookRunFormValues> +): ActionResource | null { + if (!isEmpty(data.component_id)) { + return { + id: data.component_id, + label: "Component", + link: <TopologyLink topologyId={data.component_id} /> + } as ActionResource; + } + + if (!isEmpty(data.config_id)) { + return { + id: data.config_id, + label: "Config", + link: <ConfigLink configId={data.config_id} /> + } as ActionResource; + } + + if (!isEmpty(data.check_id)) { + return { + id: data.check_id, + label: "Check", + link: <CheckLink checkId={data.check_id} /> + } as ActionResource; + } + + return null; +} From 234bb16acf83e4c94208ea66c6aa23c56c47c39d Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 18:41:54 +0200 Subject: [PATCH 09/15] chore: improve json viewer styling --- src/components/JSONViewer/index.tsx | 11 ++-- src/components/JSONViewer/theme.tsx | 78 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/components/JSONViewer/theme.tsx diff --git a/src/components/JSONViewer/index.tsx b/src/components/JSONViewer/index.tsx index 32c416417..a63943e22 100644 --- a/src/components/JSONViewer/index.tsx +++ b/src/components/JSONViewer/index.tsx @@ -1,5 +1,5 @@ import Highlight, { Language, defaultProps } from "prism-react-renderer"; -import vsLight from "prism-react-renderer/themes/vsLight"; +import theme from "./theme"; import { ComponentProps, useMemo } from "react"; import { GoCopy } from "react-icons/go"; import { parse, stringify } from "yaml"; @@ -37,7 +37,7 @@ function JSONViewerLine({ > {showLineNo && ( <span - className="text-gray-400 text-xs pr-2 select-none" + className="text-gray-300 text-xs pr-3 select-none" style={{ display: "table-cell" }} > {idx + 1} @@ -47,6 +47,7 @@ function JSONViewerLine({ {line.map((token, key) => ( // key is in the getTokenProps responses. Disabling eslint to skip // check for explicit keys. + // eslint-disable-next-line react/jsx-key <span {...getTokenProps({ token, key })} /> ))} </span> @@ -99,6 +100,8 @@ export function JSONViewer({ const copyFn = useCopyToClipboard(); + theme.plain.backgroundColor = "#fffff"; + const formatDerived = useMemo(() => { if (format !== "json") { return format; @@ -110,11 +113,11 @@ export function JSONViewer({ }, [convertToYaml, format]); return ( - <div className="flex flex-col relative"> + <div className="flex flex-col w-full relative"> <Highlight {...defaultProps} code={codeForHighlight} - theme={vsLight} + theme={theme} language={formatDerived} > {({ className, style, tokens, getLineProps, getTokenProps }) => ( diff --git a/src/components/JSONViewer/theme.tsx b/src/components/JSONViewer/theme.tsx new file mode 100644 index 000000000..e1293757c --- /dev/null +++ b/src/components/JSONViewer/theme.tsx @@ -0,0 +1,78 @@ +import { PrismTheme } from "prism-react-renderer"; + +let theme = { + plain: { + color: "##002b5c", + backgroundColor: "#fffff" + }, + styles: [ + { + types: ["comment", "prolog", "doctype", "cdata"], + style: { + color: "#6a737d", + fontStyle: "italic" + } + }, + { + types: ["namespace"], + style: { + opacity: 0.7 + } + }, + { + types: ["string", "attr-value", "plain"], + style: { + color: "#032f62" + } + }, + { + types: ["punctuation", "operator"], + style: { + color: "#393A34" + } + }, + { + types: [ + "entity", + "url", + "symbol", + "number", + "boolean", + "variable", + "constant", + "property", + "regex", + "inserted" + ], + style: { + color: "#032f62" + } + }, + { + types: ["atrule", "keyword", "attr-name", "selector"], + style: { + color: "#0f5929" + } + }, + { + types: ["function", "deleted", "tag"], + style: { + color: "#d73a49" + } + }, + { + types: ["function-variable"], + style: { + color: "#6f42c1" + } + }, + { + types: ["tag", "selector", "keyword"], + style: { + color: "#00009f" + } + } + ] +} as PrismTheme; + +export default theme; From c8513501e86f4f191db1839774c350a514044fed Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 18:46:58 +0200 Subject: [PATCH 10/15] chore: improve table styling --- pages/global.css | 28 ++++-- src/components/Canary/minimal.tsx | 4 +- src/components/ConfigChangeHistory/index.tsx | 9 +- .../ConfigSidebar/ConfigDetails.tsx | 99 ------------------- .../Configs/Sidebar/ConfigActionBar.tsx | 4 +- .../Configs/Sidebar/ConfigDetails.tsx | 8 +- .../Configs/Sidebar/ConfigSidebar.tsx | 12 +-- src/components/DataTable/index.tsx | 5 +- src/components/Icon/PlaybookStatusIcon.tsx | 34 +++++++ .../InfiniteTable/InfiniteTable.tsx | 2 +- src/components/Logs/Table/LogsTable.tsx | 8 +- src/components/Modal/index.tsx | 2 +- .../TopologySidebar/TopologyActionBar.tsx | 4 +- src/pages/config/ConfigDetailsPage.tsx | 2 +- src/ui/Age/Age.tsx | 31 ++++-- src/ui/description/VerticalDescription.tsx | 20 ++++ 16 files changed, 130 insertions(+), 142 deletions(-) delete mode 100644 src/components/ConfigSidebar/ConfigDetails.tsx create mode 100644 src/components/Icon/PlaybookStatusIcon.tsx create mode 100644 src/ui/description/VerticalDescription.tsx diff --git a/pages/global.css b/pages/global.css index 936d067f7..570e6a15d 100644 --- a/pages/global.css +++ b/pages/global.css @@ -41,7 +41,7 @@ } .table-auto { - @apply shadow-lg rounded-md py-2 px-4; + @apply shadow-lg bg-white rounded-md py-2 px-4; } .table-auto tbody tr { @@ -50,7 +50,7 @@ .table-auto thead tr { border-bottom-width: 1px; - @apply border-gray-200 bg-white text-gray-600 font-bold text-sm items-center text-left px-4 py-1; + @apply border-gray-200 uppercase text-gray-600 font-bold text-sm items-center text-left px-4 py-1; } .table-auto thead tr th { @@ -63,25 +63,25 @@ } .table-condensed thead tr th { - @apply px-2 py-1 text-xs; + @apply px-2 py-1 text-sm; } .table-condensed td { border-bottom-width: 1px; - @apply px-1 text-xs; - + @apply px-1 text-sm; } + .btn-primary { - @apply inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blue-500; + @apply inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none ; } .btn-white { - @apply inline-flex items-center justify-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md shadow-sm text-gray-700 bg-white hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blue-500; + @apply inline-flex items-center justify-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md shadow-sm text-gray-700 bg-white hover:bg-gray-100 focus:outline-none ; } .btn-danger { - @apply inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-red-500; + @apply inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none ; } .btn-disabled { @@ -89,7 +89,7 @@ } .btn-secondary { - @apply inline-flex items-center justify-center border-none shadow-sm font-medium rounded-md text-blue-500 bg-blue-100 hover:bg-blue-200 focus:ring-offset-white focus:ring-blue-500 focus:outline-none focus:ring-2 focus:ring-offset-2; + @apply inline-flex items-center justify-center border-none shadow-sm font-medium rounded-md text-blue-500 bg-blue-100 hover:bg-blue-200 focus:ring-offset-white focus:ring-blue-500 focus:outline-none ; } .btn-secondary-disabled { @@ -241,4 +241,14 @@ width: 3.125rem; height: 3.125rem; } + + .menu-items { + @apply absolute bg-white focus:outline-none mt-2 opacity-100 origin-top-right right-0 ring-1 ring-black ring-opacity-5 rounded-md scale-100 shadow-md transform w-64 z-50; + } + + .menu-item { + @apply block w-full py-2 px-4 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 divide-gray-300 text-left; + } + + } diff --git a/src/components/Canary/minimal.tsx b/src/components/Canary/minimal.tsx index 46b445212..05851b2d3 100644 --- a/src/components/Canary/minimal.tsx +++ b/src/components/Canary/minimal.tsx @@ -7,7 +7,7 @@ import { isCanaryUI } from "../../context/Environment"; import AttachAsEvidenceButton from "../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; import { timeRanges } from "../Dropdown/TimeRange"; import { Modal } from "../Modal"; -import SelectPlaybookToRun from "../Playbooks/Runs/Submit/SelectPlaybookToRun"; +import PlaybooksDropdownMenu from "../Playbooks/Runs/Submit/PlaybooksDropdownMenu"; import { toastError } from "../Toast/toast"; import { CheckDetails } from "./CanaryPopup/CheckDetails"; import { CheckTitle } from "./CanaryPopup/CheckTitle"; @@ -128,7 +128,7 @@ const MinimalCanaryFC = ({ {!isCanaryUI && ( <> <div className="flex flex-col items-center "> - <SelectPlaybookToRun + <PlaybooksDropdownMenu className="btn-primary" check_id={selectedCheck?.id} /> diff --git a/src/components/ConfigChangeHistory/index.tsx b/src/components/ConfigChangeHistory/index.tsx index 8567685c5..d7ca5cebe 100644 --- a/src/components/ConfigChangeHistory/index.tsx +++ b/src/components/ConfigChangeHistory/index.tsx @@ -22,14 +22,15 @@ const columns: ColumnDef<ConfigChange>[] = [ </div> ); }, - size: 48 + maxSize: 40 }, { header: "Summary", accessorKey: "summary", meta: { cellClassName: "text-ellipsis overflow-hidden" - } + }, + maxSize: 150 }, { header: "Source", @@ -45,7 +46,7 @@ const columns: ColumnDef<ConfigChange>[] = [ meta: { cellClassName: "text-ellipsis overflow-hidden" }, - size: 36, + maxSize: 20, cell: DateCell } ]; @@ -78,7 +79,7 @@ export function ConfigChangeHistory({ data, isLoading, linkConfig, - className = "w-full", + className = "table-auto table-fixed", pagination, tableStyle }: ConfigChangeHistoryProps) { diff --git a/src/components/ConfigSidebar/ConfigDetails.tsx b/src/components/ConfigSidebar/ConfigDetails.tsx deleted file mode 100644 index 2fc83dfd7..000000000 --- a/src/components/ConfigSidebar/ConfigDetails.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { capitalize } from "lodash"; -import { useMemo } from "react"; -import { FaTags } from "react-icons/fa"; -import { useGetConfigByIdQuery } from "../../api/query-hooks"; -import { CountBadge } from "../Badge/CountBadge"; -import CollapsiblePanel from "../CollapsiblePanel"; -import { DescriptionCard } from "../DescriptionCard"; -import { InfoMessage } from "../InfoMessage"; -import TextSkeletonLoader from "../SkeletonLoader/TextSkeletonLoader"; -import Title from "../Title/title"; -import { Age } from "../../ui/Age"; - -type Props = { - configId: string; - isCollapsed?: boolean; - onCollapsedStateChange?: (isClosed: boolean) => void; -}; - -export function ConfigDetails({ - configId, - isCollapsed = true, - onCollapsedStateChange = () => {} -}: Props) { - const { - data: configDetails, - isLoading, - error - } = useGetConfigByIdQuery(configId); - - const displayDetails = useMemo(() => { - if (configDetails) { - return [ - { - label: "Name", - value: configDetails.name - }, - ...(configDetails.tags - ? Object.entries(configDetails.tags) - .filter(([key]) => key !== "Name") - .map(([key, value]) => ({ - label: capitalize(key), - value - })) - : []), - ...(configDetails.created_at - ? [ - { - label: "Created At", - value: <Age from={configDetails.created_at} /> - } - ] - : []), - ...(configDetails.updated_at - ? [ - { - label: "Updated At", - value: <Age from={configDetails.updated_at} /> - } - ] - : []), - ...(configDetails.deleted_at - ? [ - { - label: "Deleted At", - value: <Age from={configDetails.deleted_at} /> - } - ] - : []) - ]; - } - }, [configDetails]); - - return ( - <CollapsiblePanel - isCollapsed={isCollapsed} - onCollapsedStateChange={onCollapsedStateChange} - Header={ - <div className="flex py-2 flex-row flex-1 items-center space-x-2"> - <Title title="Tags" icon={<FaTags className="w-6 h-auto" />} /> - <CountBadge - roundedClass="rounded-full" - value={displayDetails?.length ?? 0} - /> - </div> - } - dataCount={displayDetails?.length} - > - <div className="flex flex-col space-y-2 py-2 max-w-full"> - {isLoading ? ( - <TextSkeletonLoader /> - ) : displayDetails && !error ? ( - <DescriptionCard items={displayDetails} labelStyle="top" /> - ) : ( - <InfoMessage message="Details not found" /> - )} - </div> - </CollapsiblePanel> - ); -} diff --git a/src/components/Configs/Sidebar/ConfigActionBar.tsx b/src/components/Configs/Sidebar/ConfigActionBar.tsx index abfef99d0..f41bd0bde 100644 --- a/src/components/Configs/Sidebar/ConfigActionBar.tsx +++ b/src/components/Configs/Sidebar/ConfigActionBar.tsx @@ -5,7 +5,7 @@ import { useGetConfigByIdQuery } from "../../../api/query-hooks"; import { usePartialUpdateSearchParams } from "../../../hooks/usePartialUpdateSearchParams"; import { ActionLink } from "../../ActionLink/ActionLink"; import AttachAsEvidenceButton from "../../AttachEvidenceDialog/AttachAsEvidenceDialogButton"; -import SelectPlaybookToRun from "../../Playbooks/Runs/Submit/SelectPlaybookToRun"; +import PlaybooksDropdownMenu from "../../Playbooks/Runs/Submit/PlaybooksDropdownMenu"; import { EvidenceType } from "../../../api/types/evidence"; type ConfigActionBarProps = { @@ -79,7 +79,7 @@ export default function ConfigActionBar({ setChecked({}); }} /> - <SelectPlaybookToRun config_id={configId} /> + <PlaybooksDropdownMenu config_id={configId} /> </div> ); } diff --git a/src/components/Configs/Sidebar/ConfigDetails.tsx b/src/components/Configs/Sidebar/ConfigDetails.tsx index a861fbae9..c9d508548 100644 --- a/src/components/Configs/Sidebar/ConfigDetails.tsx +++ b/src/components/Configs/Sidebar/ConfigDetails.tsx @@ -2,12 +2,12 @@ import { useMemo } from "react"; import { FaTags } from "react-icons/fa"; import { Link } from "react-router-dom"; import { useGetConfigByIdQuery } from "../../../api/query-hooks"; -import { relativeDateTime } from "../../../utils/date"; import CollapsiblePanel from "../../CollapsiblePanel"; import { InfoMessage } from "../../InfoMessage"; import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader"; import Title from "../../Title/title"; import DisplayDetailsRow from "../../Utils/DisplayDetailsRow"; +import { Age } from "../../../ui/Age"; type Props = { configId: string; @@ -97,11 +97,11 @@ export function ConfigDetails({ items={[ { label: "Created At", - value: relativeDateTime(configDetails.created_at) + value: <Age from={configDetails.created_at} /> }, { label: "Updated At", - value: relativeDateTime(configDetails.updated_at) + value: <Age from={configDetails.updated_at} /> } ]} /> @@ -169,7 +169,7 @@ export function ConfigDetails({ items={[ { label: "Deleted At", - value: relativeDateTime(configDetails.deleted_at) + value: <Age from={configDetails.deleted_at} /> } ]} /> diff --git a/src/components/Configs/Sidebar/ConfigSidebar.tsx b/src/components/Configs/Sidebar/ConfigSidebar.tsx index dc884a8bb..8c85044e7 100644 --- a/src/components/Configs/Sidebar/ConfigSidebar.tsx +++ b/src/components/Configs/Sidebar/ConfigSidebar.tsx @@ -1,10 +1,10 @@ import { useParams } from "react-router-dom"; -import ConfigChanges from "../Sidebars/ConfigChanges"; -import ConfigCosts from "../ConfigCosts"; -import ConfigInsights from "../Sidebars/ConfigInsights"; -import Configs from "../Sidebars/configs"; -import Incidents from "../Sidebars/incidents"; -import SlidingSideBar from "../SlidingSideBar"; +import ConfigChanges from "../../Sidebars/ConfigChanges"; +import ConfigCosts from "../../ConfigCosts"; +import ConfigInsights from "../../Sidebars/ConfigInsights"; +import Configs from "../../Sidebars/configs"; +import Incidents from "../../Sidebars/incidents"; +import SlidingSideBar from "../../SlidingSideBar"; import { ConfigDetails } from "./ConfigDetails"; import ConfigActionBar from "./ConfigActionBar"; import { useCallback, useState } from "react"; diff --git a/src/components/DataTable/index.tsx b/src/components/DataTable/index.tsx index a406ff7dc..6b7a4fa76 100644 --- a/src/components/DataTable/index.tsx +++ b/src/components/DataTable/index.tsx @@ -114,6 +114,8 @@ export function DataTable<TableColumns, Data extends TableColumns>({ isLoading, groupBy, hiddenColumns, + // for some reason, it seems to need both auto and fixed, there may be + // some other css class tied to auto className = "table-auto table-fixed", theadHeaderClass = "px-3 py-3 text-left text-gray-500 font-medium text-xs tracking-wider", tbodyRowClass = "cursor-pointer text-sm", @@ -265,8 +267,7 @@ export function DataTable<TableColumns, Data extends TableColumns>({ > <table className={clsx( - // for some reason, it seems to need both auto and fixed, there may be - // some other css class tied to auto + className, `w-full border border-gray-200 rounded-md`, stickyHead && "relative" )} diff --git a/src/components/Icon/PlaybookStatusIcon.tsx b/src/components/Icon/PlaybookStatusIcon.tsx new file mode 100644 index 000000000..b842d4549 --- /dev/null +++ b/src/components/Icon/PlaybookStatusIcon.tsx @@ -0,0 +1,34 @@ +import { FaHourglassStart, FaSpinner } from "react-icons/fa"; +import { IoCalendarNumberOutline } from "react-icons/io5"; +import { MdOutlineCancel } from "react-icons/md"; +import { PiCheckCircleLight, PiWarningLight } from "react-icons/pi"; +import { PlaybookRunStatus } from "../../api/types/playbooks"; + +export const statusIconMap: Record<PlaybookRunStatus, React.ReactElement> = { + completed: ( + <PiCheckCircleLight className="text-green-500 inline-block object-center h-5 w-auto pr-1" /> + ), + cancelled: ( + <MdOutlineCancel className="text-red-500 inline-block object-center h-5 w-auto pr-1" /> + ), + failed: ( + <PiWarningLight className="text-red-500 inline-block object-center h-5 w-auto pr-1" /> + ), + pending: ( + <FaHourglassStart className="text-gray-400 inline-block object-center h-5 w-auto pr-1" /> + ), + running: ( + <FaSpinner className="text-blue-500 inline-block object-center h-5 w-auto pr-1" /> + ), + scheduled: ( + <IoCalendarNumberOutline className="text-gray-500 inline-block object-center h-5 w-auto pr-1" /> + ) +}; + +export type PlaybookRunsStatusProps = { + status: PlaybookRunStatus; +}; + +export function PlaybookStatusIcon({ status }: PlaybookRunsStatusProps) { + return statusIconMap[status]; +} diff --git a/src/components/InfiniteTable/InfiniteTable.tsx b/src/components/InfiniteTable/InfiniteTable.tsx index 73974a4e9..5c37043a6 100644 --- a/src/components/InfiniteTable/InfiniteTable.tsx +++ b/src/components/InfiniteTable/InfiniteTable.tsx @@ -32,7 +32,7 @@ export function InfiniteTable<T>({ isLoading, totalEntries, allRows, - className = "table-auto table-condensed", + className = "table-auto table-condensed text-sm", fetchNextPage, maxHeight, loaderView, diff --git a/src/components/Logs/Table/LogsTable.tsx b/src/components/Logs/Table/LogsTable.tsx index 13e85717f..12336df5c 100644 --- a/src/components/Logs/Table/LogsTable.tsx +++ b/src/components/Logs/Table/LogsTable.tsx @@ -193,7 +193,13 @@ export function LogsTable({ // sizes are rebased to relation to 100% of the width. const determineColumnWidth = useCallback( (column: string) => { - const columnSize = table.getColumn(column).getSize(); + if (column === undefined) { + return; + } + const columnSize = table.getColumn(column)?.getSize(); + if (columnSize === undefined) { + return (1 / (table.getTotalSize() - 6)) * 400; + } if (column === "selection") { return columnSize; } diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 9475a3843..779b52247 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -107,7 +107,7 @@ export function Modal({ > <span className="sr-only">Close</span> <XIcon - className="fill-gray-700 w-6 h-6" + className="fill-gray-400 w-6 h-6" aria-hidden="true" /> </button> diff --git a/src/components/TopologySidebar/TopologyActionBar.tsx b/src/components/TopologySidebar/TopologyActionBar.tsx index 174f41469..082c7881a 100644 --- a/src/components/TopologySidebar/TopologyActionBar.tsx +++ b/src/components/TopologySidebar/TopologyActionBar.tsx @@ -11,7 +11,7 @@ import { useFeatureFlagsContext } from "../../context/FeatureFlagsContext"; import { features } from "../../services/permissions/features"; import { ActionLink } from "../ActionLink/ActionLink"; import { AttachEvidenceDialog } from "../AttachEvidenceDialog"; -import SelectPlaybookToRun from "../Playbooks/Runs/Submit/SelectPlaybookToRun"; +import PlaybooksDropdownMenu from "../Playbooks/Runs/Submit/PlaybooksDropdownMenu"; import TopologySnapshotModal from "../TopologyCard/TopologySnapshotModal"; import { TopologyConfigLinkModal } from "../TopologyConfigLinkModal/TopologyConfigLinkModal"; @@ -280,7 +280,7 @@ export default function TopologyActionBar({ } return null; })} - <SelectPlaybookToRun component_id={topology.id} /> + <PlaybooksDropdownMenu component_id={topology.id} /> </div> <AttachEvidenceDialog diff --git a/src/pages/config/ConfigDetailsPage.tsx b/src/pages/config/ConfigDetailsPage.tsx index 3305e33c7..065b2ac2f 100644 --- a/src/pages/config/ConfigDetailsPage.tsx +++ b/src/pages/config/ConfigDetailsPage.tsx @@ -138,7 +138,7 @@ export function ConfigDetailsPage() { <div className="flex flex-row space-x-2 h-full"> <div className="flex flex-col w-full object-contain h-full"> <div className="flex flex-col mb-6 w-full h-full"> - <div className="flex relative pt-2 px-4 border-gray-300 bg-white rounded shadow-md flex-1 overflow-x-auto overflow-y-atuo"> + <div className="flex relative pt-2 px-4 border-gray-300 bg-white flex-1 overflow-x-auto overflow-y-auto"> <JSONViewer code={code} format={format} diff --git a/src/ui/Age/Age.tsx b/src/ui/Age/Age.tsx index ccf0ab0b9..775a12355 100644 --- a/src/ui/Age/Age.tsx +++ b/src/ui/Age/Age.tsx @@ -1,6 +1,14 @@ import dayjs from "dayjs"; import clsx from "clsx"; +export function isEmpty(value: any): boolean { + return ( + value === "" || + value == null || + dayjs(value).isSame(dayjs("0001-01-01T00:00:00+00:00")) + ); +} + export default function Age({ className = "", from, @@ -12,23 +20,30 @@ export default function Age({ to?: Date | string; suffix?: boolean; }) { - if ( - from === "" || - from == null || - dayjs(from).isSame(dayjs("0001-01-01T00:00:00+00:00")) - ) { + if (isEmpty(from)) { return null; } - let _from = dayjs.utc(from); + let _from = dayjs(from); - if (to == null) { + if (isEmpty(to)) { return ( <span title={_from.format()} className={className}> {_from.local().fromNow(!suffix)} </span> ); } - let _to = dayjs.utc(to); + let _to = dayjs(to); + + let duration = dayjs.duration(_to.diff(_from)); + + console.log(from, to, duration); + if (duration.asMilliseconds() < 1000) { + return ( + <span title={_from.format()} className={className}> + {duration.asMilliseconds()}ms + </span> + ); + } return ( <span className={clsx(className, "whitespace-nowrap")}> diff --git a/src/ui/description/VerticalDescription.tsx b/src/ui/description/VerticalDescription.tsx new file mode 100644 index 000000000..00c1c0ef6 --- /dev/null +++ b/src/ui/description/VerticalDescription.tsx @@ -0,0 +1,20 @@ +import { ReactElement } from "react"; + +export default function VerticalDescription({ + label, + value +}: { + label: string; + value: ReactElement; +}) { + return ( + <div className="flex flex-col gap-2 px-2 flex-1 xl:flex-none"> + <div className="text-sm overflow-hidden truncate text-gray-500"> + {label} + </div> + <div className="flex justify-start break-all text-sm font-semibold"> + {value} + </div> + </div> + ); +} From 1a3a4f5bcc955d59ec51863967c0641a2d1f31ff Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 20:36:42 +0200 Subject: [PATCH 11/15] feat: improve cost dispay --- src/api/types/configs.ts | 22 ++++-- .../ConfigCosts/ConfigCostValue.tsx | 66 ++++++++++++++++ src/components/ConfigCosts/index.tsx | 39 ---------- .../ConfigList/Cells/ConfigListCostCell.tsx | 12 +-- .../ConfigList/ConfigListColumn.tsx | 76 ++++++------------- .../ConfigSummary/ConfigSummaryList.tsx | 68 ++++------------- .../Configs/Sidebar/ConfigDetails.tsx | 15 +++- .../Configs/Sidebar/ConfigSidebar.tsx | 8 -- src/components/CostDetails/CostDetails.tsx | 63 ++------------- src/components/TopologyDetails/index.tsx | 9 +++ .../TopologySidebar/TopologyCost.tsx | 26 ------- .../TopologySidebar/TopologySidebar.tsx | 8 -- src/ui/stats/Percentage.tsx | 74 ++++++++++++++++++ 13 files changed, 225 insertions(+), 261 deletions(-) create mode 100644 src/components/ConfigCosts/ConfigCostValue.tsx delete mode 100644 src/components/ConfigCosts/index.tsx delete mode 100644 src/components/TopologySidebar/TopologyCost.tsx create mode 100644 src/ui/stats/Percentage.tsx diff --git a/src/api/types/configs.ts b/src/api/types/configs.ts index d8d0fca06..7b2e0d485 100644 --- a/src/api/types/configs.ts +++ b/src/api/types/configs.ts @@ -24,7 +24,23 @@ export interface Change { severity?: string; } -export interface ConfigItem extends Timestamped, Avatar, Agent { +export interface Costs { + cost_per_minute?: number; + cost_total_1d?: number; + cost_total_7d?: number; + cost_total_30d?: number; +} + +export function isCostsEmpty(costs: Costs) { + return ( + costs.cost_per_minute === undefined && + costs.cost_total_1d === undefined && + costs.cost_total_7d === undefined && + costs.cost_total_30d === undefined + ); +} + +export interface ConfigItem extends Timestamped, Avatar, Agent, Costs { id: string; name: string; external_id?: string; @@ -34,10 +50,6 @@ export interface ConfigItem extends Timestamped, Avatar, Agent { analysis?: Analysis[]; tags?: Record<string, any>; allTags?: Record<string, any>; - cost_per_minute?: number; - cost_total_1d?: number; - cost_total_7d?: number; - cost_total_30d?: number; config?: Record<string, any>; agent?: { id: string; diff --git a/src/components/ConfigCosts/ConfigCostValue.tsx b/src/components/ConfigCosts/ConfigCostValue.tsx new file mode 100644 index 000000000..c7aa92e25 --- /dev/null +++ b/src/components/ConfigCosts/ConfigCostValue.tsx @@ -0,0 +1,66 @@ +import { Popover } from "@headlessui/react"; +import { Costs } from "../../api/types/configs"; +import Percentage from "../../ui/stats/Percentage"; +import { CostDetailsTable, FormatCurrency } from "../CostDetails/CostDetails"; + +export default function ConfigCostValue({ + config, + popover = true +}: { + config: Costs; + popover?: boolean; +}) { + if ( + !config.cost_total_1d || + !config.cost_total_7d || + !config.cost_total_30d + ) { + return null; + } + + let trend = config.cost_total_1d - config.cost_total_30d / 30; + + let val = ( + <FormatCurrency + value={config.cost_total_30d} + defaultValue="" + hideMinimumValue + /> + ); + let trendIcon = null; + + if (Math.abs(trend) / (config.cost_total_30d / 30) > 0.1) { + let percent = (trend / (config.cost_total_30d / 30)) * 100; + trendIcon = ( + <Percentage value={percent} increaseColor="red" decreaseColor="green" /> + ); + } + + if (!popover) { + return ( + <div className="flex flex-row"> + {val} + {trendIcon} + </div> + ); + } + return ( + <Popover className="relative "> + <Popover.Button> + <div className="flex w-full flex-row"> + {val} + {trendIcon} + </div> + </Popover.Button> + + <Popover.Panel className="absolute z-10 px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm dark:bg-gray-700"> + <CostDetailsTable + cost_per_minute={config.cost_per_minute} + cost_total_1d={config.cost_total_1d} + cost_total_30d={config.cost_total_30d} + cost_total_7d={config.cost_total_7d} + /> + </Popover.Panel> + </Popover> + ); +} diff --git a/src/components/ConfigCosts/index.tsx b/src/components/ConfigCosts/index.tsx deleted file mode 100644 index 63c816e4d..000000000 --- a/src/components/ConfigCosts/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect, useState } from "react"; -import { useConfigAnalysisQuery } from "../../api/query-hooks"; -import { CostInfoPanel } from "../CostDetails/CostDetails"; -import { CostsData } from "../../api/types/common"; - -type Props = { - configID: string; - isCollapsed?: boolean; - onCollapsedStateChange?: (isClosed: boolean) => void; -}; - -export default function ConfigCosts({ - configID, - isCollapsed = true, - onCollapsedStateChange = () => {} -}: Props) { - const [configCosts, setConfigCosts] = useState<CostsData>(); - const { data, isLoading } = useConfigAnalysisQuery(configID); - - useEffect(() => { - if (!data) { - return; - } - setConfigCosts(data[0]); - }, [data]); - - if (isLoading) { - return null; - } - - return ( - <CostInfoPanel - onCollapsedStateChange={onCollapsedStateChange} - isCollapsed={isCollapsed} - title="Costs" - {...(configCosts || {})} - /> - ); -} diff --git a/src/components/ConfigList/Cells/ConfigListCostCell.tsx b/src/components/ConfigList/Cells/ConfigListCostCell.tsx index 91a729baa..8263f3703 100644 --- a/src/components/ConfigList/Cells/ConfigListCostCell.tsx +++ b/src/components/ConfigList/Cells/ConfigListCostCell.tsx @@ -1,14 +1,10 @@ import { CellContext } from "@tanstack/react-table"; import { ConfigItem, ConfigSummary } from "../../../api/types/configs"; -import { FormatCurrency } from "../../CostDetails/CostDetails"; +import ConfigCostValue from "../../ConfigCosts/ConfigCostValue"; export default function ConfigListCostCell({ - getValue + getValue, + row }: CellContext<ConfigItem, any> | CellContext<ConfigSummary, any>) { - const cost = getValue<string | number>(); - return ( - <div className="w-full text-center"> - <FormatCurrency value={cost} defaultValue="" hideMinimumValue /> - </div> - ); + return <ConfigCostValue config={row.original} popover={false} />; } diff --git a/src/components/ConfigList/ConfigListColumn.tsx b/src/components/ConfigList/ConfigListColumn.tsx index 2f5be39dc..3ccda0c11 100644 --- a/src/components/ConfigList/ConfigListColumn.tsx +++ b/src/components/ConfigList/ConfigListColumn.tsx @@ -74,60 +74,28 @@ export const configListColumns: ColumnDef<ConfigItem, any>[] = [ }, size: 150 }, + { - id: "cost", - header: () => <div className="block w-full text-center">Cost</div>, - accessorKey: "", + header: () => <div>Cost</div>, + accessorKey: "cost_total_1d", + aggregationFn: "sum", + aggregatedCell: CostAggregate, cell: ConfigListCostCell, - size: 180, - enableSorting: false, - columns: [ - { - header: () => <div className="block w-full text-center">min</div>, - accessorKey: "cost_per_minute", - aggregationFn: "sum", - aggregatedCell: CostAggregate, - cell: ConfigListCostCell, - size: 30 - }, - { - header: () => <div className="block w-full text-center">1d</div>, - accessorKey: "cost_total_1d", - aggregationFn: "sum", - aggregatedCell: CostAggregate, - cell: ConfigListCostCell, - size: 30 - }, - { - header: () => <div className="block w-full text-center">7d</div>, - accessorKey: "cost_total_7d", - aggregationFn: "sum", - aggregatedCell: CostAggregate, - cell: ConfigListCostCell, - size: 30 - }, - { - header: () => <div className="block w-full text-center">30d</div>, - accessorKey: "cost_total_30d", - aggregationFn: "sum", - aggregatedCell: CostAggregate, - cell: ConfigListCostCell, - size: 30 - } - ] - }, - { - header: "Agent", - accessorKey: "agent", - enableSorting: false, - cell: ({ getValue }: CellContext<ConfigItem, any>) => { - const agent = getValue<ConfigItem["agent"]>(); - if (agent?.name === "local") { - return null; - } - return <span>{agent?.name}</span>; - } + maxSize: 60 }, + + // { + // header: "Agent", + // accessorKey: "agent", + // enableSorting: false, + // cell: ({ getValue }: CellContext<ConfigItem, any>) => { + // const agent = getValue<ConfigItem["agent"]>(); + // if (agent?.name === "local") { + // return null; + // } + // return <span>{agent?.name}</span>; + // } + // }, { header: "Tags", accessorKey: "tags", @@ -149,14 +117,14 @@ export const configListColumns: ColumnDef<ConfigItem, any>[] = [ accessorKey: "created_at", cell: ConfigListDateCell, aggregatedCell: "", - size: 100 + maxSize: 70 }, { - header: "Last Updated", + header: "Updated", accessorKey: "updated_at", cell: ConfigListDateCell, aggregatedCell: "", - size: 130 + maxSize: 70 }, { header: "Deleted At", diff --git a/src/components/ConfigSummary/ConfigSummaryList.tsx b/src/components/ConfigSummary/ConfigSummaryList.tsx index 0e17226ad..79e702737 100644 --- a/src/components/ConfigSummary/ConfigSummaryList.tsx +++ b/src/components/ConfigSummary/ConfigSummaryList.tsx @@ -75,12 +75,6 @@ const configSummaryColumns: ColumnDef<ConfigSummary, any>[] = [ cell: ConfigSummaryTypeCell, maxSize: 200 }, - { - header: "analysis", - accessorKey: "analysis", - cell: ConfigSummaryAnalysisCell, - maxSize: 100 - }, { header: "changes", accessorKey: "changes", @@ -91,69 +85,33 @@ const configSummaryColumns: ColumnDef<ConfigSummary, any>[] = [ } return <CountBadge value={value} />; }, - maxSize: 40 + maxSize: 20 }, { - id: "cost", - header: () => <div className="block w-full text-center">cost</div>, - size: 200, - columns: [ - { - header: () => <div className="block w-full text-center">Min</div>, - accessorKey: "cost_per_minute", - cell: ConfigListCostCell - }, - { - header: () => <div className="block w-full text-center">1d</div>, - accessorKey: "cost_total_1d", - cell: ConfigListCostCell - }, - { - header: () => <div className="block w-full text-center">7d</div>, - accessorKey: "cost_total_7d", - cell: ConfigListCostCell - }, - { - header: () => <div className="block w-full text-center">30d</div>, - accessorKey: "cost_total_30d", - cell: ConfigListCostCell - } - ] - }, - { - header: "Agent", - accessorKey: "agent", - enableSorting: false, - cell: ({ getValue }: CellContext<ConfigSummary, any>) => { - const agent = getValue<ConfigSummary["agent"]>(); - if (agent?.name === "local" || !agent) { - return null; - } - return <span>{agent.name}</span>; - }, - size: 70 + header: "analysis", + accessorKey: "analysis", + cell: ConfigSummaryAnalysisCell, + maxSize: 60 }, { - header: "Tags", - accessorKey: "tags", - cell: ({ getValue }: CellContext<ConfigSummary, any>) => { - return null; - }, - size: 140 + header: () => <div title="Cost">Cost (30d)</div>, + accessorKey: "cost_total_30d", + cell: ConfigListCostCell, + maxSize: 20 }, { header: "Created", accessorKey: "created_at", cell: ConfigListDateCell<ConfigSummary>, aggregatedCell: "", - size: 50 + maxSize: 30 }, { - header: "Last Updated", + header: "Updated", accessorKey: "updated_at", cell: ConfigListDateCell<ConfigSummary>, aggregatedCell: "", - size: 50 + maxSize: 30 } ]; @@ -186,7 +144,7 @@ export default function ConfigSummaryList({ handleRowClick={handleRowClick} tableStyle={{ borderSpacing: "0" }} isLoading={isLoading} - className="max-w-full overflow-x-auto" + className="max-w-full overflow-x-auto table-fixed table-auto" preferencesKey={""} savePreferences={false} /> diff --git a/src/components/Configs/Sidebar/ConfigDetails.tsx b/src/components/Configs/Sidebar/ConfigDetails.tsx index c9d508548..84b3e6990 100644 --- a/src/components/Configs/Sidebar/ConfigDetails.tsx +++ b/src/components/Configs/Sidebar/ConfigDetails.tsx @@ -8,6 +8,8 @@ import TextSkeletonLoader from "../../SkeletonLoader/TextSkeletonLoader"; import Title from "../../Title/title"; import DisplayDetailsRow from "../../Utils/DisplayDetailsRow"; import { Age } from "../../../ui/Age"; +import ConfigCostValue from "../../ConfigCosts/ConfigCostValue"; +import { isCostsEmpty } from "../../../api/types/configs"; type Props = { configId: string; @@ -110,7 +112,7 @@ export function ConfigDetails({ <DisplayDetailsRow items={[ { - label: "Scrapper", + label: "Scraper", value: ( <Link to={`/settings/config_scrapers/${configDetails.config_scrapers.id}`} @@ -123,6 +125,17 @@ export function ConfigDetails({ /> )} + {!isCostsEmpty(configDetails) && ( + <DisplayDetailsRow + items={[ + { + label: "Cost", + value: <ConfigCostValue config={configDetails} /> + } + ]} + /> + )} + {displayDetails && Object.entries(Object.fromEntries(displayDetails.entries())).map( ([key, values]) => { diff --git a/src/components/Configs/Sidebar/ConfigSidebar.tsx b/src/components/Configs/Sidebar/ConfigSidebar.tsx index 8c85044e7..481536f41 100644 --- a/src/components/Configs/Sidebar/ConfigSidebar.tsx +++ b/src/components/Configs/Sidebar/ConfigSidebar.tsx @@ -1,6 +1,5 @@ import { useParams } from "react-router-dom"; import ConfigChanges from "../../Sidebars/ConfigChanges"; -import ConfigCosts from "../../ConfigCosts"; import ConfigInsights from "../../Sidebars/ConfigInsights"; import Configs from "../../Sidebars/configs"; import Incidents from "../../Sidebars/incidents"; @@ -65,13 +64,6 @@ export default function ConfigSidebar() { panelCollapsedStatusChange(status, "Insights") } /> - <ConfigCosts - configID={id} - isCollapsed={openedPanel !== "Costs"} - onCollapsedStateChange={(status) => - panelCollapsedStatusChange(status, "Costs") - } - /> <PlaybookRunsSidePanel panelType="config" configId={id} diff --git a/src/components/CostDetails/CostDetails.tsx b/src/components/CostDetails/CostDetails.tsx index 12fea069b..e8c7f14f9 100644 --- a/src/components/CostDetails/CostDetails.tsx +++ b/src/components/CostDetails/CostDetails.tsx @@ -1,19 +1,8 @@ import { useMemo } from "react"; -import { FaDollarSign } from "react-icons/fa"; -import Title from "../Title/title"; -import CollapsiblePanel from "../CollapsiblePanel"; -import { Loading } from "../Loading"; import { CostsData } from "../../api/types/common"; type CostDetailsTableProps = CostsData; -type CostInfoPanelProps = CostsData & { - loading?: boolean; - title: string; - isCollapsed?: boolean; - onCollapsedStateChange?: (isClosed: boolean) => void; -}; - type FormatCurrencyProps = { value: number | string | undefined; defaultValue?: string | null | React.ReactNode | number; @@ -26,20 +15,6 @@ type CostInfoProps = { defaultValue?: string | null | React.ReactNode | number; }; -function isCostsDataEmpty({ - cost_per_minute, - cost_total_1d, - cost_total_7d, - cost_total_30d -}: CostsData) { - return ( - cost_per_minute === undefined && - cost_total_1d === undefined && - cost_total_7d === undefined && - cost_total_30d === undefined - ); -} - export function FormatCurrency({ value, defaultValue, @@ -68,9 +43,11 @@ export function FormatCurrency({ export function CostInfo({ label, value, defaultValue }: CostInfoProps) { return ( - <div className="overflow-hidden flex flex-col flex-1 space-y-2 px-2"> - <div className="text-gray-500 text-sm whitespace-nowrap">{label}</div> - <div className="text-black font-semibold"> + <div className="overflow-hidden flex flex-row "> + <div className="text-gray-500 text-sm whitespace-nowrap pr-2 uppercase"> + {label}: + </div> + <div className="pr-2 mr-2 text-gray-100 "> <FormatCurrency value={value} defaultValue={defaultValue} /> </div> </div> @@ -84,38 +61,10 @@ export function CostDetailsTable({ cost_total_30d }: CostDetailsTableProps) { return ( - <div className="flex flex-row justify-between space-x-3 w-full px-2"> - <CostInfo value={cost_per_minute} label="Per min" defaultValue="" /> + <div className="flex flex-row "> <CostInfo value={cost_total_1d} label="1d" defaultValue="" /> <CostInfo value={cost_total_7d} label="7d" defaultValue="" /> <CostInfo value={cost_total_30d} label="30d" defaultValue="" /> </div> ); } - -export function CostInfoPanel({ - title, - loading, - isCollapsed, - onCollapsedStateChange, - ...costDetails -}: CostInfoPanelProps) { - if (isCostsDataEmpty({ ...costDetails })) { - return null; - } - - return ( - <CollapsiblePanel - isCollapsed={isCollapsed} - onCollapsedStateChange={onCollapsedStateChange} - Header={ - <Title title={title} icon={<FaDollarSign className="w-6 h-auto" />} /> - } - data-panel-height="150px" - > - <div className="flex flex-col space-y-2"> - {loading ? <Loading /> : <CostDetailsTable {...costDetails} />} - </div> - </CollapsiblePanel> - ); -} diff --git a/src/components/TopologyDetails/index.tsx b/src/components/TopologyDetails/index.tsx index dedd4f840..7270da130 100644 --- a/src/components/TopologyDetails/index.tsx +++ b/src/components/TopologyDetails/index.tsx @@ -9,6 +9,8 @@ import { FormatProperty } from "../TopologyCard/Property"; import { TopologyLink } from "../TopologyLink"; import { useMemo } from "react"; import { Topology } from "../../api/types/topology"; +import ConfigCostValue from "../ConfigCosts/ConfigCostValue"; +import { isCostsEmpty } from "../../api/types/configs"; type Props = { topology?: Topology; @@ -41,6 +43,13 @@ export default function TopologyDetails({ }); } + if (!isCostsEmpty(topology)) { + items.push({ + label: "Costs", + value: <ConfigCostValue config={topology} /> + }); + } + const topologyProperties = topology?.properties ?? []; for (var property of topologyProperties) { diff --git a/src/components/TopologySidebar/TopologyCost.tsx b/src/components/TopologySidebar/TopologyCost.tsx deleted file mode 100644 index 49c55de18..000000000 --- a/src/components/TopologySidebar/TopologyCost.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Topology } from "../../api/types/topology"; -import { CostInfoPanel } from "../CostDetails/CostDetails"; - -type Props = { - topology?: Topology; - isCollapsed?: boolean; - onCollapsedStateChange?: (isClosed: boolean) => void; -}; - -export default function TopologyCost({ - topology, - isCollapsed = false, - onCollapsedStateChange = () => {} -}: Props) { - if (!topology) { - return null; - } - return ( - <CostInfoPanel - isCollapsed={isCollapsed} - onCollapsedStateChange={onCollapsedStateChange} - title="Costs" - {...topology} - /> - ); -} diff --git a/src/components/TopologySidebar/TopologySidebar.tsx b/src/components/TopologySidebar/TopologySidebar.tsx index ff7f5439e..7bb114708 100644 --- a/src/components/TopologySidebar/TopologySidebar.tsx +++ b/src/components/TopologySidebar/TopologySidebar.tsx @@ -8,7 +8,6 @@ import TopologyConfigChanges from "../TopologyConfigChanges"; import TopologyDetails from "../TopologyDetails"; import { ComponentTeams } from "./ComponentTeams"; import TopologyActionBar from "./TopologyActionBar"; -import TopologyCost from "./TopologyCost"; import TopologyInsights from "./TopologyInsights"; import { ComponentChecks } from "./ComponentChecks"; import { Topology } from "../../api/types/topology"; @@ -82,13 +81,6 @@ export default function TopologySidebar({ } /> - <TopologyCost - topology={topology} - isCollapsed={openedPanel !== "Costs"} - onCollapsedStateChange={(status) => - panelCollapsedStatusChange(status, "Costs") - } - /> <TopologyConfigChanges topologyID={id} isCollapsed={openedPanel !== "ConfigChanges"} diff --git a/src/ui/stats/Percentage.tsx b/src/ui/stats/Percentage.tsx new file mode 100644 index 000000000..71fa27e96 --- /dev/null +++ b/src/ui/stats/Percentage.tsx @@ -0,0 +1,74 @@ +import clsx from "clsx"; + +export function percentage(value: number, precision = 0) { + if (value > 100) { + return (value / 100).toFixed(Math.max(precision, 1)) + "x"; + } + return value.toFixed(precision) + "%"; +} +export default function Percentage({ + value, + increaseColor = "green", + decreaseColor = "red", + className = " h-4 w-4 ", + precision = 0 +}: { + value: number; + precision?: number; + increaseColor?: string; + decreaseColor?: string; + className?: string; +}) { + if (Math.abs(value) > 100) { + } + + if (value > 0) { + return ( + <div + className={`bg-${increaseColor}-100 text-${increaseColor}-800 inline-flex items-baseline rounded-full ml-1 px-1.5 py-0.5 text-xs font-medium md:mt-2 lg:mt-0`} + > + <svg + className={clsx( + className, + `-ml-1 mr-0.5 flex-shrink-0 self-center text-${increaseColor}-500` + )} + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + <path + fill-rule="evenodd" + d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z" + clip-rule="evenodd" + /> + </svg> + <span className="sr-only"> Increased by </span> + {percentage(value, precision)} + </div> + ); + } else { + return ( + <div + className={`inline-flex items-baseline rounded-full ml-1 px-1.5 py-0.5 text-xs font-medium bg-${decreaseColor}-100 text-${decreaseColor}-800 md:mt-2 lg:mt-0`} + > + <svg + className={clsx( + className, + `-ml-1 mr-0.5 flex-shrink-0 self-center text-${decreaseColor}-500` + )} + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + <path + fill-rule="evenodd" + d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z" + clip-rule="evenodd" + /> + </svg> + <span className="sr-only"> Decreased by </span> + {percentage(Math.abs(value), precision)} + </div> + ); + } +} From ba17444d61a711228026afb71fdf617b491c9272 Mon Sep 17 00:00:00 2001 From: Moshe Immermam <moshe@flanksource.com> Date: Sun, 29 Oct 2023 20:40:22 +0200 Subject: [PATCH 12/15] feat: add embedded topology page --- src/App.tsx | 16 ++++++++- src/components/Head/Head.tsx | 10 ++++-- src/components/HealthChecksSummary/index.tsx | 4 ++- src/components/IncidentCardSummary/index.tsx | 13 +++++-- src/components/StatusLine/StatusLine.tsx | 11 +++++- .../TopologyCard/TopologyCard.stories.tsx | 2 +- .../TopologyConfigAnalysisLine.tsx | 6 ++-- .../TopologyCard/TopologyDropdownMenu.tsx | 10 +++--- src/components/TopologyCard/index.tsx | 24 ++++++++----- src/pages/TopologyCard.tsx | 35 +++++++++++++++++++ src/pages/TopologyPage.tsx | 3 +- 11 files changed, 108 insertions(+), 26 deletions(-) create mode 100644 src/pages/TopologyCard.tsx diff --git a/src/App.tsx b/src/App.tsx index 21d75d132..366194bee 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -68,6 +68,7 @@ import { features } from "./services/permissions/features"; import { stringSortHelper } from "./utils/common"; import { MdOutlineSupportAgent } from "react-icons/md"; import AgentsPage from "./components/Agents/AgentPage"; +import { TopologyCardPage } from "./pages/TopologyCard"; export type NavigationItems = { name: string; @@ -223,7 +224,11 @@ export function HealthRoutes({ sidebar }: { sidebar: ReactNode }) { export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) { const { featureFlagsLoaded } = useFeatureFlagsContext(); - if (!featureFlagsLoaded) { + console.log(window.location.pathname); + if ( + !featureFlagsLoaded && + !window.location.pathname.startsWith("/view/topology") + ) { return <FullPageSkeletonLoader />; } @@ -231,6 +236,15 @@ export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) { <Routes> <Route path="" element={<Navigate to="/topology" />} /> + <Route + path="/view/topology/:id" + element={withAccessCheck( + <TopologyCardPage />, + tables.topologies, + "read" + )} + /> + <Route path="topology" element={sidebar}> <Route path=":id" diff --git a/src/components/Head/Head.tsx b/src/components/Head/Head.tsx index 9abd51244..6caa958fe 100644 --- a/src/components/Head/Head.tsx +++ b/src/components/Head/Head.tsx @@ -2,13 +2,17 @@ import NextHead from "next/head"; interface Props { prefix: string; + suffix?: string; } -export function Head({ prefix }: Props) { +export function Head({ prefix, suffix = " | Mission Control" }: Props) { return ( <NextHead> - <title>{prefix} | Mission Control - + + {prefix} + {suffix} + + ); } diff --git a/src/components/HealthChecksSummary/index.tsx b/src/components/HealthChecksSummary/index.tsx index 7e6a86503..f480a68d3 100644 --- a/src/components/HealthChecksSummary/index.tsx +++ b/src/components/HealthChecksSummary/index.tsx @@ -3,6 +3,7 @@ import { AiFillHeart } from "react-icons/ai"; import { StatusLine, StatusLineProps } from "../StatusLine/StatusLine"; type HealthChecksSummaryProps = React.HTMLProps & { + target?: string; checks?: { health: number; warning: number; @@ -12,6 +13,7 @@ type HealthChecksSummaryProps = React.HTMLProps & { export function HealthChecksSummary({ checks, + target = "", ...rest }: HealthChecksSummaryProps) { const statusLineInfo = useMemo(() => { @@ -53,5 +55,5 @@ export function HealthChecksSummary({ return null; } - return ; + return ; } diff --git a/src/components/IncidentCardSummary/index.tsx b/src/components/IncidentCardSummary/index.tsx index 18f45955a..c0d061fe0 100644 --- a/src/components/IncidentCardSummary/index.tsx +++ b/src/components/IncidentCardSummary/index.tsx @@ -25,11 +25,13 @@ const chipColorFromSeverity = ( type IncidentSummaryTypes = keyof typeof typeItems; type IncidentCardSummaryProps = { + target?: string; topology: Pick; }; export default function IncidentCardSummary({ - topology + topology, + target = "" }: IncidentCardSummaryProps) { const statusLines: StatusLineProps[] = useMemo(() => { const incidentSummary = Object.entries(topology?.summary?.incidents || {}); @@ -75,7 +77,14 @@ export default function IncidentCardSummary({ return ( <> {statusLines.map((statusLine, index) => { - return ; + return ( + + ); })} ); diff --git a/src/components/StatusLine/StatusLine.tsx b/src/components/StatusLine/StatusLine.tsx index b296438c8..d3da5658a 100644 --- a/src/components/StatusLine/StatusLine.tsx +++ b/src/components/StatusLine/StatusLine.tsx @@ -31,13 +31,20 @@ const renderIcon = (icon: string | React.ReactNode) => { } }; -const StatusInfoEntry = ({ statusInfo }: { statusInfo: StatusInfo }) => { +const StatusInfoEntry = ({ + statusInfo, + target +}: { + statusInfo: StatusInfo; + target?: string; +}) => { if (statusInfo.url) { return ( {statusInfo.icon && renderIcon(statusInfo.icon)} @@ -55,6 +62,7 @@ const StatusInfoEntry = ({ statusInfo }: { statusInfo: StatusInfo }) => { export function StatusLine({ icon, + target = "", label, url, statuses, @@ -70,6 +78,7 @@ export function StatusLine({ {url && ( diff --git a/src/components/TopologyCard/TopologyCard.stories.tsx b/src/components/TopologyCard/TopologyCard.stories.tsx index 0d52ca280..3b918e723 100644 --- a/src/components/TopologyCard/TopologyCard.stories.tsx +++ b/src/components/TopologyCard/TopologyCard.stories.tsx @@ -3,7 +3,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { MemoryRouter } from "react-router-dom"; import { Size } from "../../types"; import { TopologyCard } from "./index"; -import { Topology } from "../../context/TopologyPageContext"; +import { Topology } from "../../api/types/topology"; const defaultQueryClient = new QueryClient(); diff --git a/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx b/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx index d3b72cd20..771891809 100644 --- a/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx +++ b/src/components/TopologyCard/TopologyConfigAnalysisLine.tsx @@ -31,11 +31,13 @@ const severityToColorMap = (severity: string) => { }; type TopologyConfigAnalysisLineProps = { + target?: string; topology: Pick; }; export function TopologyConfigAnalysisLine({ - topology + topology, + target = "" }: TopologyConfigAnalysisLineProps) { const insights = topology?.summary?.insights; @@ -86,5 +88,5 @@ export function TopologyConfigAnalysisLine({ return null; } - return ; + return ; } diff --git a/src/components/TopologyCard/TopologyDropdownMenu.tsx b/src/components/TopologyCard/TopologyDropdownMenu.tsx index e061db5cd..fd8fd7ee6 100644 --- a/src/components/TopologyCard/TopologyDropdownMenu.tsx +++ b/src/components/TopologyCard/TopologyDropdownMenu.tsx @@ -31,13 +31,13 @@ function TopologyMenuItem({ interface IProps { topology: Topology; onRefresh?: () => void; - isTopologyPage?: boolean; + position?: "fixed" | "absolute"; } export const TopologyDropdownMenu = ({ topology, onRefresh, - isTopologyPage = false + position = "fixed" }: IProps) => { const [dropDownMenuStyles, setDropDownMenuStyles] = useState(); @@ -62,13 +62,13 @@ export const TopologyDropdownMenu = ({ const top = node.getBoundingClientRect().bottom; if (left && top) { - if (isTopologyPage) { + if (position === "absolute") { setDropDownMenuStyles({ right: 0, top: "1.5rem", position: "absolute" }); - } else { + } else if (position === "fixed") { setDropDownMenuStyles({ left: left - 200, top: top, @@ -77,7 +77,7 @@ export const TopologyDropdownMenu = ({ } } }, - [isTopologyPage] + [position] ); const [ diff --git a/src/components/TopologyCard/index.tsx b/src/components/TopologyCard/index.tsx index f22f6d101..d8c9499ae 100644 --- a/src/components/TopologyCard/index.tsx +++ b/src/components/TopologyCard/index.tsx @@ -3,7 +3,6 @@ import clsx from "clsx"; import { MouseEventHandler, useMemo } from "react"; import { Link, useParams, useSearchParams } from "react-router-dom"; import { getTopology } from "../../api/services/topology"; -import { Topology } from "../../context/TopologyPageContext"; import { Size } from "../../types"; import AgentName from "../Agents/AgentName"; import { CustomScroll } from "../CustomScroll"; @@ -16,6 +15,7 @@ import { CardMetrics } from "./CardMetrics"; import { Property } from "./Property"; import { TopologyConfigAnalysisLine } from "./TopologyConfigAnalysisLine"; import { TopologyDropdownMenu } from "./TopologyDropdownMenu"; +import { Topology } from "../../api/types/topology"; export enum ComponentStatus { unhealthy = "unhealthy", @@ -39,9 +39,12 @@ interface IProps { topologyId?: string; topology?: Topology; selectionMode?: boolean; + hideMenu?: boolean; + // where to open new links + target?: string; selected?: boolean; onSelectionChange?: MouseEventHandler; - isTopologyPage?: boolean; + menuPosition?: "fixed" | "absolute"; } export function TopologyCard({ @@ -49,9 +52,11 @@ export function TopologyCard({ topology: topologyData, topologyId, selectionMode, + target = "", + hideMenu, selected, onSelectionChange, - isTopologyPage = false + menuPosition = "fixed" }: IProps) { const [searchParams] = useSearchParams(); const { id: parentId } = useParams(); @@ -177,7 +182,7 @@ export function TopologyCard({ )}
- {selectionMode ? ( + {selectionMode && (
- ) : ( - + )} + + {!selectionMode && !hideMenu && ( + )}
@@ -231,6 +235,7 @@ export function TopologyCard({ {canShowChildHealth() && ( @@ -240,6 +245,7 @@ export function TopologyCard({ {topology?.components?.map((component: any) => ( diff --git a/src/pages/TopologyCard.tsx b/src/pages/TopologyCard.tsx new file mode 100644 index 000000000..aa3105070 --- /dev/null +++ b/src/pages/TopologyCard.tsx @@ -0,0 +1,35 @@ +import { useQuery } from "@tanstack/react-query"; +import { useParams } from "react-router-dom"; +import { getTopology } from "../api/services/topology"; +import { TopologyCard } from "../components/TopologyCard"; +import { Head } from "../components/Head/Head"; +import { InfoMessage } from "../components/InfoMessage"; + +export function TopologyCardPage() { + const { id, size } = useParams(); + + const { data } = useQuery({ + queryKey: ["topology", id], + queryFn: () => getTopology({ id: id }) + }); + + if (!data || !data.components || data.components?.length === 0) { + return ; + } + + const topology = data.components[0]; + return ( + <> + + +
+ +
+ + ); +} diff --git a/src/pages/TopologyPage.tsx b/src/pages/TopologyPage.tsx index 2ed7ba105..3449811fe 100644 --- a/src/pages/TopologyPage.tsx +++ b/src/pages/TopologyPage.tsx @@ -80,6 +80,7 @@ export function TopologyPage() { const refererId = searchParams.get("refererId") ?? undefined; const sortBy = searchParams.get("sortBy") ?? undefined; const sortOrder = searchParams.get("sortOrder") ?? undefined; + const hideFilters = searchParams.get("hideFilters") ?? undefined; const showHiddenComponents = searchParams.get("showHiddenComponents") ?? undefined; @@ -230,7 +231,7 @@ export function TopologyPage() { key={item.id} topology={item} size={topologyCardSize} - isTopologyPage + menuPosition="absolute" /> ))} {!topology?.length && !isLoading && ( From f523c8a24a0d16f58f0ddc125a9e7778d12294c6 Mon Sep 17 00:00:00 2001 From: Moshe Immermam Date: Sun, 29 Oct 2023 20:41:56 +0200 Subject: [PATCH 13/15] chore: misc fixes --- src/components/ConfigLink/ConfigLink.tsx | 29 ++++++++++++----- src/components/ConfigList/index.tsx | 2 +- src/components/HealthChecks/CheckLink.tsx | 32 ++++++++++++++----- .../__tests__/CheckLink.unit.test.tsx | 4 +-- src/components/JSONViewer/index.tsx | 2 -- .../TopologySidebar/ComponentChecks.tsx | 5 +-- src/pages/TopologyCard.tsx | 1 - src/pages/config/ConfigDetailsPage.tsx | 3 +- 8 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/components/ConfigLink/ConfigLink.tsx b/src/components/ConfigLink/ConfigLink.tsx index a55425f6d..6651675c1 100644 --- a/src/components/ConfigLink/ConfigLink.tsx +++ b/src/components/ConfigLink/ConfigLink.tsx @@ -1,12 +1,15 @@ +import { useQuery } from "@tanstack/react-query"; import clsx from "clsx"; import { HTMLAttributeAnchorTarget } from "react"; import { Link } from "react-router-dom"; +import { getConfigsByID } from "../../api/services/configs"; import { ConfigItem } from "../../api/types/configs"; import ConfigsTypeIcon from "../Configs/ConfigsTypeIcon"; import { ConfigIcon } from "../Icon/ConfigIcon"; type ConfigLinkProps = { config?: ConfigItem; + configId?: string; className?: string; configTypeSecondary?: string; variant?: "label" | "link"; @@ -15,32 +18,42 @@ type ConfigLinkProps = { export default function ConfigLink({ config, + configId, variant = "link", className = "text-zinc-600 text-sm whitespace-nowrap overflow-hidden overflow-ellipsis" }: ConfigLinkProps) { - if (config == null) { + const { data } = useQuery(["config", configId, config], () => { + if (config) { + return config; + } + if (configId != null) { + return getConfigsByID(configId); + } + return null; + }); + + if (data == null) { return null; } + if (variant === "link") { return ( - + -
- {config.name} -
+
{data.name}
); } return (
- -
{config.name}
+ +
{data.name}
); } diff --git a/src/components/ConfigList/index.tsx b/src/components/ConfigList/index.tsx index b99dd7730..5da360916 100644 --- a/src/components/ConfigList/index.tsx +++ b/src/components/ConfigList/index.tsx @@ -134,7 +134,7 @@ export default function ConfigList({ data, handleRowClick, isLoading }: Props) { : [groupByField] } hiddenColumns={setHiddenColumns()} - className="max-w-full overflow-x-auto" + className="max-w-full table-auto table-fixed" tableSortByState={sortBy} onTableSortByChanged={updateSortBy} determineRowClassNamesCallback={determineRowClassNames} diff --git a/src/components/HealthChecks/CheckLink.tsx b/src/components/HealthChecks/CheckLink.tsx index b2b4f86f7..ccc99a681 100644 --- a/src/components/HealthChecks/CheckLink.tsx +++ b/src/components/HealthChecks/CheckLink.tsx @@ -1,28 +1,44 @@ +import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; -import { ComponentHealthCheckView } from "../../api/services/topology"; -import { HealthCheck } from "../../api/types/health"; +import { getHealthCheckSummary } from "../../api/services/topology"; +import { HealthCheckSummary } from "../../api/types/health"; import { Icon } from "../Icon"; import { HealthCheckStatus } from "../Status/HealthCheckStatus"; type ComponentCheckLinkProps = { - componentCheck: ComponentHealthCheckView; + check?: HealthCheckSummary; + checkId?: string; }; -export function CheckLink({ componentCheck: check }: ComponentCheckLinkProps) { +export function CheckLink({ check, checkId }: ComponentCheckLinkProps) { + const { data } = useQuery(["check", checkId, check], () => { + if (check) { + return check; + } + if (checkId != null) { + return getHealthCheckSummary(checkId); + } + return null; + }); + + if (data == null) { + return null; + } + return (
- } /> - + +
- {check.name} + {data.name}
diff --git a/src/components/HealthChecks/__tests__/CheckLink.unit.test.tsx b/src/components/HealthChecks/__tests__/CheckLink.unit.test.tsx index 16c43000a..b3d50dc27 100644 --- a/src/components/HealthChecks/__tests__/CheckLink.unit.test.tsx +++ b/src/components/HealthChecks/__tests__/CheckLink.unit.test.tsx @@ -16,7 +16,7 @@ describe("CheckLink", () => { it("renders with healthy status", () => { render( - + ); @@ -38,7 +38,7 @@ describe("CheckLink", () => { render( - + ); diff --git a/src/components/JSONViewer/index.tsx b/src/components/JSONViewer/index.tsx index a63943e22..b549411b5 100644 --- a/src/components/JSONViewer/index.tsx +++ b/src/components/JSONViewer/index.tsx @@ -100,8 +100,6 @@ export function JSONViewer({ const copyFn = useCopyToClipboard(); - theme.plain.backgroundColor = "#fffff"; - const formatDerived = useMemo(() => { if (format !== "json") { return format; diff --git a/src/components/TopologySidebar/ComponentChecks.tsx b/src/components/TopologySidebar/ComponentChecks.tsx index 4293de0f3..3cb00e047 100644 --- a/src/components/TopologySidebar/ComponentChecks.tsx +++ b/src/components/TopologySidebar/ComponentChecks.tsx @@ -61,10 +61,7 @@ export function ComponentChecks({ ) : componentChecks && componentChecks.length > 0 ? ( componentChecks.map((componentCheck) => ( - + )) ) : ( diff --git a/src/pages/TopologyCard.tsx b/src/pages/TopologyCard.tsx index aa3105070..0238d20a3 100644 --- a/src/pages/TopologyCard.tsx +++ b/src/pages/TopologyCard.tsx @@ -12,7 +12,6 @@ export function TopologyCardPage() { queryKey: ["topology", id], queryFn: () => getTopology({ id: id }) }); - if (!data || !data.components || data.components?.length === 0) { return ; } diff --git a/src/pages/config/ConfigDetailsPage.tsx b/src/pages/config/ConfigDetailsPage.tsx index 065b2ac2f..8b647ee96 100644 --- a/src/pages/config/ConfigDetailsPage.tsx +++ b/src/pages/config/ConfigDetailsPage.tsx @@ -90,7 +90,8 @@ export function ConfigDetailsPage() { const ordered = Object.keys(configDetails.config) .sort() .reduce((obj: Record, key) => { - obj[key] = configDetails.config ? [key] : null; + //eslint-disable-next-line + obj[key] = configDetails.config[key]; return obj; }, {}); From df0e73ef782ae17abc6fc619e99003938192fc1d Mon Sep 17 00:00:00 2001 From: Moshe Immermam Date: Mon, 30 Oct 2023 10:44:14 +0200 Subject: [PATCH 14/15] fix: tests --- src/components/HealthChecks/CheckLink.tsx | 5 +-- .../__tests__/CheckLink.unit.test.tsx | 32 +++++++++++++------ .../Runs/Submit/PlaybookRunParams.tsx | 2 +- .../Runs/Submit/SubmitPlaybookRunForm.tsx | 4 +-- .../SelectPlaybookToRun.unit.test.tsx | 8 ++--- .../SubmitPlaybookRunForm.unit.test.tsx | 28 ++++++++-------- src/ui/Age/Age.tsx | 9 +----- src/utils/date.ts | 8 +++++ 8 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/components/HealthChecks/CheckLink.tsx b/src/components/HealthChecks/CheckLink.tsx index ccc99a681..a3cc0b7f0 100644 --- a/src/components/HealthChecks/CheckLink.tsx +++ b/src/components/HealthChecks/CheckLink.tsx @@ -5,12 +5,12 @@ import { HealthCheckSummary } from "../../api/types/health"; import { Icon } from "../Icon"; import { HealthCheckStatus } from "../Status/HealthCheckStatus"; -type ComponentCheckLinkProps = { +type CheckLinkProps = { check?: HealthCheckSummary; checkId?: string; }; -export function CheckLink({ check, checkId }: ComponentCheckLinkProps) { +export function CheckLink({ check, checkId }: CheckLinkProps) { const { data } = useQuery(["check", checkId, check], () => { if (check) { return check; @@ -27,6 +27,7 @@ export function CheckLink({ check, checkId }: ComponentCheckLinkProps) { return ( { - const check: ComponentHealthCheckView = { + const check: HealthCheckSummary = { id: "1", name: "Example Check", type: "example", status: "healthy", - component_id: "1", severity: "critical" }; - it("renders with healthy status", () => { + it("renders with healthy status", async () => { render( - + + + ); - const linkElement = screen.getByRole("link"); + const linkElement = await screen.findByRole("link"); expect(linkElement).toHaveAttribute( "href", "/health?checkId=1&timeRange=1h" ); const statusElement = screen.getByTestId("health-check-status"); + expect(statusElement).toHaveClass("bg-green-400"); const nameElement = screen.getByText("Example Check"); expect(nameElement).toBeInTheDocument(); }); - it("renders with unhealthy status", () => { - const unhealthyCheck = { ...check, status: "unhealthy" }; + it("renders with unhealthy status", async () => { + const unhealthyCheck: HealthCheckSummary = { + id: "1", + name: "Example Check", + type: "example", + status: "unhealthy", + severity: "critical" + }; render( - + + + ); - const statusElement = screen.getByTestId("health-check-status"); + const statusElement = await screen.findByTestId("health-check-status"); expect(statusElement).toHaveClass("bg-red-400"); }); }); diff --git a/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx b/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx index 6a5b129bb..365453471 100644 --- a/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx +++ b/src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx @@ -21,7 +21,7 @@ export default function PlaybookRunParams({ )}
- {playbook.parameters.length > 0 ? ( + {playbook.parameters && playbook.parameters.length > 0 ? ( playbook.parameters.map((i) => ( = useMemo( () => ({ id: playbook.id, - params: undefined, component_id: componentId, check_id: checkId, config_id: configId @@ -46,8 +45,6 @@ export default function SubmitPlaybookRunForm({ [checkId, componentId, configId, playbook.id] ); - console.log("initialValues", initialValues); - const { mutate: submitPlaybookRun } = useSubmitPlaybookRunMutation({ onSuccess: (run) => { toastSuccess( @@ -96,6 +93,7 @@ export default function SubmitPlaybookRunForm({
diff --git a/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx b/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx index 8e3399cf3..94e08a17f 100644 --- a/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx +++ b/src/components/Playbooks/Runs/Submit/__tests__/SelectPlaybookToRun.unit.test.tsx @@ -3,16 +3,16 @@ import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { rest } from "msw"; import { setupServer } from "msw/node"; -import { PlaybookSpec } from "../../../Settings/PlaybookSpecsTable"; import PlaybooksDropdownMenu from "../PlaybooksDropdownMenu"; +import { RunnablePlaybook } from "../../../../../api/types/playbooks"; -const playbooks: PlaybookSpec[] = [ +const playbooks: RunnablePlaybook[] = [ { id: "1", name: "Playbook 1", created_at: "2021-09-01T00:00:00Z", source: "UI", - spec: {}, + parameters: [], updated_at: "2021-09-01T00:00:00Z" }, { @@ -20,7 +20,7 @@ const playbooks: PlaybookSpec[] = [ name: "Playbook 2", created_at: "2021-09-01T00:00:00Z", source: "UI", - spec: {}, + parameters: [], updated_at: "2021-09-01T00:00:00Z" } ]; diff --git a/src/components/Playbooks/Runs/Submit/__tests__/SubmitPlaybookRunForm.unit.test.tsx b/src/components/Playbooks/Runs/Submit/__tests__/SubmitPlaybookRunForm.unit.test.tsx index 698f1b6a0..b76d38f7a 100644 --- a/src/components/Playbooks/Runs/Submit/__tests__/SubmitPlaybookRunForm.unit.test.tsx +++ b/src/components/Playbooks/Runs/Submit/__tests__/SubmitPlaybookRunForm.unit.test.tsx @@ -3,21 +3,19 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { rest } from "msw"; import { setupServer } from "msw/node"; -import { PlaybookSpec } from "../../../Settings/PlaybookSpecsTable"; import SubmitPlaybookRunForm from "./../SubmitPlaybookRunForm"; +import { RunnablePlaybook } from "../../../../../api/types/playbooks"; -const playbookSpec: PlaybookSpec = { +const playbook: RunnablePlaybook = { id: "1", name: "Playbook 1", source: "UI", - spec: { - parameters: [ - { - label: "Label", - name: "name" - } - ] - }, + parameters: [ + { + label: "Label", + name: "name" + } + ], created_at: "2021-09-01T00:00:00Z", updated_at: "2021-09-01T00:00:00Z" }; @@ -31,7 +29,7 @@ global.ResizeObserver = jest.fn().mockImplementation(() => ({ // Define a mock server to handle PATCH requests const server = setupServer( rest.post("/api/playbook/run", (req, res, ctx) => { - return res(ctx.json(playbookSpec)); + return res(ctx.json(playbook)); }) ); @@ -54,7 +52,7 @@ describe("SubmitPlaybookRunForm", () => { { expect(screen.getByLabelText("Label")).toBeInTheDocument(); - expect(screen.getByRole("button", { name: /Submit/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /Run/i })).toBeInTheDocument(); userEvent.click(screen.getByRole("button", { name: /close/i })); @@ -84,7 +82,7 @@ describe("SubmitPlaybookRunForm", () => { { fireEvent.change(input, { target: { value: "test" } }); - const btn = screen.getByRole("button", { name: /Submit/i }); + const btn = screen.getByRole("button", { name: /Run/i }); userEvent.click(btn); diff --git a/src/ui/Age/Age.tsx b/src/ui/Age/Age.tsx index 775a12355..099caf6f4 100644 --- a/src/ui/Age/Age.tsx +++ b/src/ui/Age/Age.tsx @@ -1,13 +1,6 @@ import dayjs from "dayjs"; import clsx from "clsx"; - -export function isEmpty(value: any): boolean { - return ( - value === "" || - value == null || - dayjs(value).isSame(dayjs("0001-01-01T00:00:00+00:00")) - ); -} +import { isEmpty } from "../../utils/date"; export default function Age({ className = "", diff --git a/src/utils/date.ts b/src/utils/date.ts index ec49bd306..f364c2b52 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -48,6 +48,14 @@ export function isDate(dateString?: ValueType) { return !isNaN(new Date(dateString).getDate()); } +export function isEmpty(value: any): boolean { + return ( + value === "" || + value == null || + dayjs(value).isSame(dayjs("0001-01-01T00:00:00+00:00")) + ); +} + export const TIME_BUCKETS = { TODAY: { name: "today", sortOrder: 0 }, THIS_WEEK: { name: "this week", sortOrder: 1 }, From e80e705f967e85a1d4769541d3328d1aedc971c5 Mon Sep 17 00:00:00 2001 From: Moshe Immermam Date: Mon, 30 Oct 2023 11:45:26 +0200 Subject: [PATCH 15/15] chore: fix linting error --- src/pages/config/ConfigDetailsPage.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/config/ConfigDetailsPage.tsx b/src/pages/config/ConfigDetailsPage.tsx index 8b647ee96..159a8c53c 100644 --- a/src/pages/config/ConfigDetailsPage.tsx +++ b/src/pages/config/ConfigDetailsPage.tsx @@ -13,6 +13,7 @@ import { usePartialUpdateSearchParams } from "../../hooks/usePartialUpdateSearch import useRunTaskOnPropChange from "../../hooks/useRunTaskOnPropChange"; import { useAtom } from "jotai"; import { refreshButtonClickedTrigger } from "../../components/SlidingSideBar"; +import React from "react"; export function ConfigDetailsPage() { const [, setRefreshButtonClickedTrigger] = useAtom( @@ -90,8 +91,9 @@ export function ConfigDetailsPage() { const ordered = Object.keys(configDetails.config) .sort() .reduce((obj: Record, key) => { - //eslint-disable-next-line - obj[key] = configDetails.config[key]; + if (configDetails.config) { + obj[key] = configDetails.config[key]; + } return obj; }, {});