From e7feb3dc34e67135192a4054d066c5302fbc4d60 Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Tue, 15 Oct 2024 23:19:44 +0300 Subject: [PATCH] fix: update favicon path to reference root --- pages/_document.tsx | 3 +- .../Formik/FormikDurationNanosecondsField.tsx | 186 +++++++++++------- .../Rules/NotificationsRulesForm.tsx | 3 +- .../Rules/notificationsRulesTableColumns.tsx | 7 +- 4 files changed, 127 insertions(+), 72 deletions(-) diff --git a/pages/_document.tsx b/pages/_document.tsx index 4eaa92aa8..59c4e6f1f 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -1,5 +1,4 @@ import Document, { Head, Html, Main, NextScript } from "next/document"; -import React from "react"; import { isCanaryUI } from "../src/context/Environment"; export default class MyDocument extends Document { @@ -16,7 +15,7 @@ export default class MyDocument extends Document { /> diff --git a/src/components/Forms/Formik/FormikDurationNanosecondsField.tsx b/src/components/Forms/Formik/FormikDurationNanosecondsField.tsx index 23cbb12a3..1a47280d7 100644 --- a/src/components/Forms/Formik/FormikDurationNanosecondsField.tsx +++ b/src/components/Forms/Formik/FormikDurationNanosecondsField.tsx @@ -1,85 +1,135 @@ -import { TextInput } from "@flanksource-ui/ui/FormControls/TextInput"; +import dayjs from "dayjs"; import { useField } from "formik"; -import { useState } from "react"; - -type DurationUnit = "nanoseconds" | "milliseconds" | "seconds" | "minutes"; - -const units = ["nanoseconds", "milliseconds", "seconds", "minutes"] as const; - -const unitsMap = { - nanoseconds: 1, - milliseconds: 1000000, - seconds: 1000000000, - minutes: 60000000000 -} as const; +import { useCallback, useMemo, useState } from "react"; +import CreatableSelect from "react-select/creatable"; type FormikDurationNanosecondsFieldProps = { - fieldName: string; - className?: string; + name: string; + required?: boolean; label?: string; + hint?: string; + hintPosition?: "top" | "bottom"; + isClearable?: boolean; }; - export default function FormikDurationNanosecondsField({ - fieldName, + name, + required = false, label, - className = "flex flex-col py-2" + hint, + hintPosition = "bottom", + isClearable = true }: FormikDurationNanosecondsFieldProps) { - const [field] = useField({ - name: fieldName + const [isTouched, setIsTouched] = useState(false); + + const [field, meta] = useField({ + name, + type: "text", + required, + validate: useCallback( + (value: string) => { + if (required && !value) { + return "This field is required"; + } + + // if value is less than 1 minute, show error + if (parseInt(value, 10) < 60 * 1e9) { + return "Duration must be greater than 1 minute"; + } + }, + [required] + ) }); - const value = field.value; + const value = useMemo(() => { + // we want to take nanoseconds and convert them to 1h, 1m, 1s + if (!field.value) { + return undefined; + } - console.log("value", value); + const duration = dayjs.duration( + parseInt(field.value, 10) / 1000000, + "milliseconds" + ); + return `${duration.humanize()}`; + }, [field.value]); - const [unit, setUnit] = useState("nanoseconds"); + const handleOnChange = (value?: string) => { + if (!value) { + field.onChange({ + target: { + name: field.name, + value: "" + } + }); + return; + } - return ( -
- {label && } -
- { - const value = parseInt(e.target.value); - const multiplier = unitsMap[unit]; - const nanoseconds = value * multiplier; - field.onChange({ - target: { - name: fieldName, - value: nanoseconds - } - }); - }} - id={fieldName} - /> - -
+ field.onChange({ + target: { + name: field.name, + value: nanoseconds + } + }); + }; + + return ( +
+ {label && } + {hint && hintPosition === "top" && ( +

{hint}

+ )} + + className="h-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" + onChange={(value) => { + handleOnChange(value?.value ?? undefined); + setIsTouched(true); + }} + value={[{ label: value!, value: value! }]} + options={["1h", "2h", "3h", "6h", "1d", "1w", "1m"].map((value) => ({ + label: value, + value + }))} + onBlur={(event) => { + field.onBlur(event); + setIsTouched(true); + }} + onFocus={(event) => { + field.onBlur(event); + setIsTouched(true); + }} + name={field.name} + isClearable={isClearable} + isMulti={false} + menuPortalTarget={document.body} + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }) + }} + menuPosition={"fixed"} + menuShouldBlockScroll={true} + /> + {hint && hintPosition === "bottom" && ( +

{hint}

+ )} + {isTouched && meta.error ? ( +

{meta.error}

+ ) : null}
); } diff --git a/src/components/Notifications/Rules/NotificationsRulesForm.tsx b/src/components/Notifications/Rules/NotificationsRulesForm.tsx index ef2ad38ac..73dff66b4 100644 --- a/src/components/Notifications/Rules/NotificationsRulesForm.tsx +++ b/src/components/Notifications/Rules/NotificationsRulesForm.tsx @@ -68,7 +68,8 @@ export default function NotificationsRulesForm({ label="Repeat Interval" /> diff --git a/src/components/Notifications/Rules/notificationsRulesTableColumns.tsx b/src/components/Notifications/Rules/notificationsRulesTableColumns.tsx index f99253482..0e01c48a2 100644 --- a/src/components/Notifications/Rules/notificationsRulesTableColumns.tsx +++ b/src/components/Notifications/Rules/notificationsRulesTableColumns.tsx @@ -5,6 +5,7 @@ import MRTAvatarCell from "@flanksource-ui/ui/MRTDataTable/Cells/MRTAvataCell"; import { MRTDateCell } from "@flanksource-ui/ui/MRTDataTable/Cells/MRTDateCells"; import { MRTCellProps } from "@flanksource-ui/ui/MRTDataTable/MRTCellProps"; import { formatDuration } from "@flanksource-ui/utils/date"; +import dayjs from "dayjs"; import { atom, useAtom } from "jotai"; import { MRT_ColumnDef } from "mantine-react-table"; import { useState } from "react"; @@ -292,7 +293,11 @@ export const notificationsRulesTableColumns: MRT_ColumnDef[] size: 130, Cell: ({ row }) => { const value = row.original.wait_for; - return value; + if (!value) { + return null; + } + // Convert nanoseconds to date + return dayjs.duration(value / 1000000, "milliseconds").humanize(false); } }, {