diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.tsx index 8a0b793d7..f8539943f 100644 --- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.tsx +++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.tsx @@ -1,4 +1,4 @@ -import { Ref, CSSProperties, useRef, useState, useCallback } from "react"; +import { FC, Ref, CSSProperties, useRef, useState, useCallback } from "react"; import { observer } from "mobx-react-lite"; @@ -180,59 +180,63 @@ interface AlertMenuProps { setIsMenuOpen: (isOpen: boolean) => void; } -const AlertMenu = observer( - ({ group, alert, alertStore, silenceFormStore, setIsMenuOpen }) => { - const [isHidden, setIsHidden] = useState(true); +const AlertMenu: FC = ({ + group, + alert, + alertStore, + silenceFormStore, + setIsMenuOpen, +}) => { + const [isHidden, setIsHidden] = useState(true); - const toggle = useCallback(() => { - setIsMenuOpen(isHidden); - setIsHidden(!isHidden); - }, [isHidden, setIsMenuOpen]); + const toggle = useCallback(() => { + setIsMenuOpen(isHidden); + setIsHidden(!isHidden); + }, [isHidden, setIsMenuOpen]); - const hide = useCallback(() => { - setIsHidden(true); - setIsMenuOpen(false); - }, [setIsMenuOpen]); + const hide = useCallback(() => { + setIsHidden(true); + setIsMenuOpen(false); + }, [setIsMenuOpen]); - const rootRef = useRef(null); - useOnClickOutside(rootRef, hide, !isHidden); + const rootRef = useRef(null); + useOnClickOutside(rootRef, hide, !isHidden); - const { x, y, refs, strategy } = useFloating({ - placement: "bottom-start", - middleware: [shift(), offset(5)], - }); + const { x, y, refs, strategy } = useFloating({ + placement: "bottom-start", + middleware: [shift(), offset(5)], + }); - return ( - - - - - - - - + return ( + + + + - ); - }, -); + + + + + ); +}; export { AlertMenu, MenuContent }; diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx index adcf0f8c2..efbbbd48f 100644 --- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx +++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/index.tsx @@ -1,7 +1,5 @@ import type { FC, MouseEvent } from "react"; -import { observer } from "mobx-react-lite"; - import type { APIAlertGroupT } from "Models/APITypes"; import type { AlertStore } from "Stores/AlertStore"; import type { SilenceFormStore } from "Stores/SilenceFormStore"; @@ -116,4 +114,4 @@ const GroupHeader: FC<{ ); }; -export default observer(GroupHeader); +export default GroupHeader; diff --git a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx index 43de54fb1..f23bc6c77 100644 --- a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx +++ b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx @@ -8,8 +8,6 @@ import { useCallback, } from "react"; -import { observer } from "mobx-react-lite"; - import { useFloating, shift, offset } from "@floating-ui/react-dom"; import type { OnChangeValue } from "react-select"; @@ -134,7 +132,7 @@ const GridLabelSelect: FC<{ alertStore: AlertStore; settingsStore: Settings; grid: APIGridT; -}> = observer(({ alertStore, settingsStore, grid }) => { +}> = ({ alertStore, settingsStore, grid }) => { const [isVisible, setIsVisible] = useState(false); const hide = useCallback(() => setIsVisible(false), []); const toggle = useCallback(() => { @@ -172,6 +170,6 @@ const GridLabelSelect: FC<{ ); -}); +}; export { GridLabelSelect }; diff --git a/ui/src/Components/Labels/FilteringCounterBadge/index.tsx b/ui/src/Components/Labels/FilteringCounterBadge/index.tsx index 894fcb122..02b0f3c85 100644 --- a/ui/src/Components/Labels/FilteringCounterBadge/index.tsx +++ b/ui/src/Components/Labels/FilteringCounterBadge/index.tsx @@ -57,8 +57,6 @@ const FilteringCounterBadge: FC<{ [alertStore.filters, name, value, isAppend], ); - if (!alwaysVisible && counter === 0) return null; - const cs = GetClassAndStyle( alertStore, name, @@ -66,6 +64,8 @@ const FilteringCounterBadge: FC<{ "rounded-pill components-label-with-hover", ); + if (!alwaysVisible && counter === 0) return null; + return ( = observer(({ silence }) => { +}> = ({ silence }) => { const diff = differenceInSeconds(parseISO(silence.endsAt), new Date()); if (diff <= 0) { return ( @@ -35,7 +35,7 @@ const SilenceProgress: FC<{ Expires ); -}); +}; const SilenceComment: FC<{ cluster: string; @@ -68,6 +68,9 @@ const SilenceComment: FC<{ ), ); + const hasMultipleClusters = + Object.keys(alertStore.data.upstreams.clusters).length > 1; + return ( <>
@@ -93,8 +96,7 @@ const SilenceComment: FC<{ {collapsed ? (
- {Object.keys(alertStore.data.upstreams.clusters).length > - 1 ? ( + {hasMultipleClusters ? ( {cluster} diff --git a/ui/src/Components/ManagedSilence/index.tsx b/ui/src/Components/ManagedSilence/index.tsx index 45f539bae..ffafc2ad3 100644 --- a/ui/src/Components/ManagedSilence/index.tsx +++ b/ui/src/Components/ManagedSilence/index.tsx @@ -1,7 +1,6 @@ import { FC, useEffect, useState } from "react"; import { action } from "mobx"; -import { observer } from "mobx-react-lite"; import { parseISO } from "date-fns/parseISO"; import { getUnixTime } from "date-fns/getUnixTime"; @@ -38,89 +37,86 @@ const ManagedSilence: FC<{ isOpen?: boolean; onDidUpdate?: () => void; isNested?: boolean; -}> = observer( - ({ - cluster, - alertCount, - alertCountAlwaysVisible, - silence, - alertStore, - silenceFormStore, - isOpen = false, - onDidUpdate, - isNested = false, - }) => { - useEffect(() => { - if (onDidUpdate) onDidUpdate(); - }); +}> = ({ + cluster, + alertCount, + alertCountAlwaysVisible, + silence, + alertStore, + silenceFormStore, + isOpen = false, + onDidUpdate, + isNested = false, +}) => { + useEffect(() => { + if (onDidUpdate) onDidUpdate(); + }); - const [showDetails, setShowDetails] = useState(isOpen); + const [showDetails, setShowDetails] = useState(isOpen); - const onEditSilence = action(() => { - const alertmanager = GetAlertmanager(alertStore, cluster); + const onEditSilence = action(() => { + const alertmanager = GetAlertmanager(alertStore, cluster); - silenceFormStore.data.fillFormFromSilence(alertmanager, silence); - silenceFormStore.data.resetProgress(); - silenceFormStore.tab.setTab("editor"); - silenceFormStore.toggle.show(); - }); + silenceFormStore.data.fillFormFromSilence(alertmanager, silence); + silenceFormStore.data.resetProgress(); + silenceFormStore.tab.setTab("editor"); + silenceFormStore.toggle.show(); + }); - const [progress, setProgress] = useState(() => - calculatePercent(silence.startsAt, silence.endsAt), - ); + const [progress, setProgress] = useState(() => + calculatePercent(silence.startsAt, silence.endsAt), + ); - useEffect(() => { - const timer = setInterval(() => { - setProgress(calculatePercent(silence.startsAt, silence.endsAt)); - }, 30 * 1000); - return () => clearInterval(timer); - }, [silence.startsAt, silence.endsAt]); + useEffect(() => { + const timer = setInterval(() => { + setProgress(calculatePercent(silence.startsAt, silence.endsAt)); + }, 30 * 1000); + return () => clearInterval(timer); + }, [silence.startsAt, silence.endsAt]); - return ( -
-
- +
+ setShowDetails(!showDetails)} + /> +
+ {showDetails ? ( +
+ setShowDetails(!showDetails)} - /> -
- {showDetails ? ( -
- -
- ) : null} -
-
90 - ? "progress-bar bg-danger" - : progress > 75 - ? "progress-bar bg-warning" - : "progress-bar bg-success" - } - role="progressbar" - style={{ width: progress + "%" }} - aria-valuenow={progress} - aria-valuemin={0} - aria-valuemax={100} + alertStore={alertStore} + silenceFormStore={silenceFormStore} + onEditSilence={onEditSilence} + isUpper={isNested} />
+ ) : null} +
+
90 + ? "progress-bar bg-danger" + : progress > 75 + ? "progress-bar bg-warning" + : "progress-bar bg-success" + } + role="progressbar" + style={{ width: progress + "%" }} + aria-valuenow={progress} + aria-valuemin={0} + aria-valuemax={100} + />
- ); - }, -); -ManagedSilence.displayName = "ManagedSilence"; +
+ ); +}; export { ManagedSilence, GetAlertmanager }; diff --git a/ui/src/Components/OverviewModal/OverviewModalContent.tsx b/ui/src/Components/OverviewModal/OverviewModalContent.tsx index 89efbe78a..fbf16b465 100644 --- a/ui/src/Components/OverviewModal/OverviewModalContent.tsx +++ b/ui/src/Components/OverviewModal/OverviewModalContent.tsx @@ -64,7 +64,7 @@ const LabelsTable: FC<{ counters: CountersResponseT; showAllLabels: boolean; toggleAllLabels: () => void; -}> = observer(({ alertStore, counters, showAllLabels, toggleAllLabels }) => ( +}> = ({ alertStore, counters, showAllLabels, toggleAllLabels }) => ( <>
-)); +); const NothingToShow: FC = () => (
diff --git a/ui/src/Components/SilenceModal/DateTimeSelect/Duration.tsx b/ui/src/Components/SilenceModal/DateTimeSelect/Duration.tsx index c0d90bc2f..5895121de 100644 --- a/ui/src/Components/SilenceModal/DateTimeSelect/Duration.tsx +++ b/ui/src/Components/SilenceModal/DateTimeSelect/Duration.tsx @@ -1,7 +1,5 @@ import { FC, useEffect, useRef } from "react"; -import { observer } from "mobx-react-lite"; - import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAngleUp } from "@fortawesome/free-solid-svg-icons/faAngleUp"; import { faAngleDown } from "@fortawesome/free-solid-svg-icons/faAngleDown"; @@ -11,7 +9,7 @@ const Duration: FC<{ label: string; onInc: () => void; onDec: () => void; -}> = observer(({ value, label, onInc, onDec }) => { +}> = ({ value, label, onInc, onDec }) => { const rootRef = useRef(null); useEffect(() => { @@ -78,6 +76,6 @@ const Duration: FC<{
); -}); +}; export { Duration }; diff --git a/ui/src/Components/SilenceModal/DateTimeSelect/HourMinute.tsx b/ui/src/Components/SilenceModal/DateTimeSelect/HourMinute.tsx index 8aec06931..6ad80924d 100644 --- a/ui/src/Components/SilenceModal/DateTimeSelect/HourMinute.tsx +++ b/ui/src/Components/SilenceModal/DateTimeSelect/HourMinute.tsx @@ -1,7 +1,5 @@ import { FC, useEffect, useRef, MouseEvent, WheelEvent } from "react"; -import { observer } from "mobx-react-lite"; - import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAngleUp } from "@fortawesome/free-solid-svg-icons/faAngleUp"; @@ -30,94 +28,92 @@ const HourMinute: FC<{ onHourDec: () => void; onMinuteInc: () => void; onMinuteDec: () => void; -}> = observer( - ({ dateValue, onHourInc, onHourDec, onMinuteInc, onMinuteDec }) => { - const rootRef = useRef(null); +}> = ({ dateValue, onHourInc, onHourDec, onMinuteInc, onMinuteDec }) => { + const rootRef = useRef(null); - useEffect(() => { - const cancelWheel = (event: Event) => event.preventDefault(); + useEffect(() => { + const cancelWheel = (event: Event) => event.preventDefault(); - const elem = rootRef.current as HTMLDivElement; + const elem = rootRef.current as HTMLDivElement; - elem.addEventListener("wheel", cancelWheel, { passive: false }); + elem.addEventListener("wheel", cancelWheel, { passive: false }); - return () => { - elem.removeEventListener("wheel", cancelWheel); - }; - }, []); - - const onHourWheel = (event: WheelEvent) => { - if (event.deltaY < 0) { - onHourInc(); - } else { - onHourDec(); - } + return () => { + elem.removeEventListener("wheel", cancelWheel); }; + }, []); - const onMinuteWheel = (event: WheelEvent) => { - if (event.deltaY < 0) { - onMinuteInc(); - } else { - onMinuteDec(); - } - }; + const onHourWheel = (event: WheelEvent) => { + if (event.deltaY < 0) { + onHourInc(); + } else { + onHourDec(); + } + }; - const hour = dateValue.getHours(); - const minute = dateValue.getMinutes(); + const onMinuteWheel = (event: WheelEvent) => { + if (event.deltaY < 0) { + onMinuteInc(); + } else { + onMinuteDec(); + } + }; - return ( -
- - - - - - - - - - - - - - -
- -
-

{hour > 9 ? hour : `0${hour}`}

-
-

:

-
-

{minute > 9 ? minute : `0${minute}`}

-
- -
-
- ); - }, -); + const hour = dateValue.getHours(); + const minute = dateValue.getMinutes(); + + return ( +
+ + + + + + + + + + + + + + +
+ +
+

{hour > 9 ? hour : `0${hour}`}

+
+

:

+
+

{minute > 9 ? minute : `0${minute}`}

+
+ +
+
+ ); +}; export { HourMinute }; diff --git a/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.tsx b/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.tsx index c37272fbf..8316073d9 100644 --- a/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.tsx +++ b/ui/src/Components/SilenceModal/SilenceMatch/LabelNameInput.tsx @@ -1,5 +1,7 @@ import { use, FC } from "react"; +import { action } from "mobx"; + import Creatable from "react-select/creatable"; import { FormatBackendURI } from "Stores/AlertStore"; @@ -11,6 +13,12 @@ import { AnimatedMenu } from "Components/Select"; import { NewLabelName, OptionT, StringToOption } from "Common/Select"; import { OnChangeValue } from "react-select"; +const setMatcherName = action( + (matcher: MatcherWithIDT, option: OnChangeValue) => { + matcher.name = (option as OptionT).value; + }, +); + const LabelNameInput: FC<{ matcher: MatcherWithIDT; isValid: boolean; @@ -32,10 +40,9 @@ const LabelNameInput: FC<{ response ? response.map((value: string) => StringToOption(value)) : [] } placeholder={isValid ? "Label name" : } - onChange={(option: OnChangeValue) => { - // eslint-disable-next-line react-compiler/react-compiler -- intentional MobX observable mutation - matcher.name = (option as OptionT).value; - }} + onChange={(option: OnChangeValue) => + setMatcherName(matcher, option) + } hideSelectedOptions components={{ Menu: AnimatedMenu }} /> diff --git a/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.tsx b/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.tsx index 99c95a175..7ec8b556d 100644 --- a/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.tsx +++ b/ui/src/Components/SilenceModal/SilenceMatch/LabelValueInput.tsx @@ -1,6 +1,6 @@ import { use, FC, useEffect } from "react"; -import { observer } from "mobx-react-lite"; +import { action } from "mobx"; import { ActionMeta, @@ -21,6 +21,23 @@ import { ThemeContext } from "Components/Theme"; import { AnimatedMenuMultiple } from "Components/Select"; import { MatchCounter } from "./MatchCounter"; +const updateMatcherValues = action( + ( + matcher: MatcherWithIDT, + newValue: OnChangeValue, + meta: ActionMeta, + ) => { + matcher.values = newValue as OptionT[]; + // force regex if we have multiple values + if (matcher.values.length > 1 && matcher.isRegex === false) { + matcher.isRegex = true; + } + if (meta.action === "create-option") { + matcher.values[matcher.values.length - 1].wasCreated = true; + } + }, +); + const GenerateHashFromMatchers = ( silenceFormStore: SilenceFormStore, matcher: MatcherWithIDT, @@ -66,7 +83,7 @@ const LabelValueInput: FC<{ silenceFormStore: SilenceFormStore; matcher: MatcherWithIDT; isValid: boolean; -}> = observer(({ silenceFormStore, matcher, isValid }) => { +}> = ({ silenceFormStore, matcher, isValid }) => { const { response, get, cancelGet } = useFetchGet( FormatBackendURI(`labelValues.json?name=${matcher.name}`), { autorun: false }, @@ -97,16 +114,7 @@ const LabelValueInput: FC<{ onChange={( newValue: OnChangeValue, meta: ActionMeta, - ) => { - matcher.values = newValue as OptionT[]; - // force regex if we have multiple values - if (matcher.values.length > 1 && matcher.isRegex === false) { - matcher.isRegex = true; - } - if (meta.action === "create-option") { - matcher.values[matcher.values.length - 1].wasCreated = true; - } - }} + ) => updateMatcherValues(matcher, newValue, meta)} hideSelectedOptions isMulti components={{ @@ -116,6 +124,6 @@ const LabelValueInput: FC<{ }} /> ); -}); +}; export { LabelValueInput }; diff --git a/ui/src/Components/SilenceModal/SilenceMatch/index.tsx b/ui/src/Components/SilenceModal/SilenceMatch/index.tsx index 3e66fcfc5..8b001626a 100644 --- a/ui/src/Components/SilenceModal/SilenceMatch/index.tsx +++ b/ui/src/Components/SilenceModal/SilenceMatch/index.tsx @@ -1,6 +1,6 @@ import type { FC } from "react"; -import { observer } from "mobx-react-lite"; +import { action } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTrash } from "@fortawesome/free-solid-svg-icons/faTrash"; @@ -10,6 +10,20 @@ import { TooltipWrapper } from "Components/TooltipWrapper"; import { LabelNameInput } from "./LabelNameInput"; import { LabelValueInput } from "./LabelValueInput"; +const setMatcherIsEqual = action( + (matcher: MatcherWithIDT, checked: boolean) => { + matcher.isEqual = checked; + }, +); + +const setMatcherIsRegex = action( + (matcher: MatcherWithIDT, checked: boolean) => { + if (matcher.values.length <= 1) { + matcher.isRegex = checked; + } + }, +); + const SilenceMatch: FC<{ silenceFormStore: SilenceFormStore; matcher: MatcherWithIDT; @@ -48,10 +62,9 @@ const SilenceMatch: FC<{ type="checkbox" value="" checked={matcher.isEqual} - onChange={(event) => { - // eslint-disable-next-line react-compiler/react-compiler -- intentional MobX observable mutation - matcher.isEqual = event.target.checked; - }} + onChange={(event) => + setMatcherIsEqual(matcher, event.target.checked) + } />