-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add silence notification form page
Fixes #2284
- Loading branch information
1 parent
43c30a7
commit e03d982
Showing
10 changed files
with
525 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export type SilenceNotificationResponse = { | ||
id: string; | ||
component_id: string; | ||
config_id: string; | ||
check_id: string; | ||
canary_id: string; | ||
from: string; | ||
until: string; | ||
description: string; | ||
recursive: boolean; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { getCanaryNames } from "@flanksource-ui/api/services/topology"; | ||
import { useQuery } from "@tanstack/react-query"; | ||
import { useMemo } from "react"; | ||
import FormikSelectDropdown from "./FormikSelectDropdown"; | ||
|
||
type FormikCanaryDropdownProps = { | ||
name: string; | ||
label?: string; | ||
required?: boolean; | ||
hint?: string; | ||
|
||
className?: string; | ||
}; | ||
|
||
export default function FormikCanaryDropdown({ | ||
name, | ||
label, | ||
required = false, | ||
hint, | ||
className = "flex flex-col space-y-2 py-2" | ||
}: FormikCanaryDropdownProps) { | ||
const { isLoading, data: canary } = useQuery({ | ||
queryKey: ["canaries", "canary_names"], | ||
queryFn: () => getCanaryNames() | ||
}); | ||
|
||
const options = useMemo( | ||
() => | ||
canary?.map((canary) => ({ | ||
label: canary.name, | ||
value: canary.id | ||
})), | ||
[canary] | ||
); | ||
|
||
return ( | ||
<FormikSelectDropdown | ||
name={name} | ||
className={className} | ||
options={options} | ||
label={label} | ||
isLoading={isLoading} | ||
required={required} | ||
hint={hint} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; | ||
import clsx from "clsx"; | ||
import dayjs from "dayjs"; | ||
import { useFormikContext } from "formik"; | ||
import { useCallback } from "react"; | ||
import { MdOutlineKeyboardArrowDown } from "react-icons/md"; | ||
import FormikTextInput from "./FormikTextInput"; | ||
|
||
const commonDurations = [ | ||
{ | ||
label: "1 Day", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(1, "day").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "2 Days", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(2, "day").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "3 Days", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(3, "day").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "1 Week", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(1, "week").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "2 Weeks", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(2, "week").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "1 Month", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(1, "month").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "3 Months", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(3, "month").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "6 Months", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(6, "month").toISOString() | ||
} | ||
}, | ||
{ | ||
label: "1 Year", | ||
values: { | ||
from: dayjs().toISOString(), | ||
to: dayjs().add(1, "year").toISOString() | ||
} | ||
} | ||
]; | ||
|
||
type FormikDurationPickerProps = { | ||
fieldNames: { | ||
from: string; | ||
to: string; | ||
}; | ||
label: string; | ||
className?: string; | ||
placeholder?: string; | ||
}; | ||
|
||
export default function FormikDurationPicker({ | ||
fieldNames: { from, to }, | ||
label, | ||
placeholder = "Select duration", | ||
className | ||
}: FormikDurationPickerProps) { | ||
const { values, setFieldValue } = | ||
useFormikContext<Record<string, string | undefined>>(); | ||
|
||
const updateFrom = useCallback( | ||
(value: string) => { | ||
setFieldValue(from, dayjs(value).format("YYYY-MM-DDTHH:mm")); | ||
}, | ||
[from, setFieldValue] | ||
); | ||
|
||
const updateTo = useCallback( | ||
(value: string) => { | ||
setFieldValue(to, dayjs(value).format("YYYY-MM-DDTHH:mm")); | ||
}, | ||
[to, setFieldValue] | ||
); | ||
|
||
const clearFields = useCallback(() => { | ||
setFieldValue(from, undefined); | ||
setFieldValue(to, undefined); | ||
}, [from, to, setFieldValue]); | ||
|
||
return ( | ||
<Popover className={clsx("relative py-2 text-sm", className)}> | ||
<PopoverButton as="div" className="w-full"> | ||
{({ open }) => { | ||
return ( | ||
<div className="flex w-full flex-col gap-2"> | ||
{label && ( | ||
<label className="text-sm font-medium text-gray-700"> | ||
{label} | ||
</label> | ||
)} | ||
<div className="flex w-full flex-row items-center rounded-md border border-gray-300 bg-white px-2 py-2 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"> | ||
{values?.[to] || values?.[from] ? ( | ||
<div className="flex flex-1 flex-row text-sm"> | ||
<span> | ||
{dayjs(values?.[from]).format("DD/MM/YYYY HH:mm")} | ||
</span> | ||
<span className="mx-2">-</span> | ||
<span> | ||
{dayjs(values?.[to]).format("DD/MM/YYYY HH:mm")} | ||
</span> | ||
</div> | ||
) : ( | ||
<span>{placeholder || "Select duration"}</span> | ||
)} | ||
<div | ||
className={clsx("ml-2 mt-1", { | ||
"rotate-180": open | ||
})} | ||
> | ||
<MdOutlineKeyboardArrowDown size={18} /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}} | ||
</PopoverButton> | ||
<PopoverPanel | ||
className={`absolute z-50 flex w-full origin-top-right flex-col divide-y divide-gray-100 rounded-md bg-slate-50 ring-1 ring-black ring-opacity-5 drop-shadow-xl focus:outline-none`} | ||
> | ||
{({ close }) => { | ||
return ( | ||
<div | ||
className={clsx( | ||
"z-50 flex max-h-96 w-full cursor-auto rounded-md bg-white py-2 shadow-lg shadow-gray-200 ring-1 ring-black ring-opacity-5 focus:outline-none" | ||
)} | ||
> | ||
<div className="w-1/2 overflow-hidden border-r border-gray-300 px-1"> | ||
<div className="p-3"> | ||
<div> | ||
<div className="mb-2"> | ||
<div className="my-3"> | ||
<FormikTextInput | ||
label="From" | ||
type="datetime-local" | ||
name={from} | ||
/> | ||
</div> | ||
<div className="my-3"> | ||
<FormikTextInput | ||
label="To" | ||
type="datetime-local" | ||
name={to} | ||
/> | ||
</div> | ||
</div> | ||
<div className="flex justify-end gap-2"> | ||
<button | ||
title="Clear Time Range" | ||
className="inline-flex justify-center rounded-md border border-gray-300 bg-white px-2 py-1 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-gray-100" | ||
onClick={clearFields} | ||
> | ||
Clear | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="flex w-1/2 flex-col overflow-y-hidden p-2"> | ||
<div className="mb-2 whitespace-nowrap text-sm font-medium sm:space-x-2"> | ||
Common Durations | ||
</div> | ||
{commonDurations.map((option) => { | ||
return ( | ||
<button | ||
type="button" | ||
onClick={() => { | ||
updateFrom(option.values.from); | ||
updateTo(option.values.to); | ||
close(); | ||
}} | ||
key={option.label} | ||
className={clsx( | ||
"option-item flex w-full items-center justify-between px-2 py-1 text-left hover:bg-gray-100" | ||
)} | ||
> | ||
{option.label} | ||
</button> | ||
); | ||
})} | ||
</div> | ||
</div> | ||
); | ||
}} | ||
</PopoverPanel> | ||
</Popover> | ||
); | ||
} |
Oops, something went wrong.