From cb6c28ac26a7b6ccf12b9d67d57d3e1f8863abb5 Mon Sep 17 00:00:00 2001 From: x032205 Date: Mon, 30 Jun 2025 14:08:27 -0400 Subject: [PATCH] UI updates --- .../v2/HighlightText/HighlightText.tsx | 43 +++++ .../src/components/v2/HighlightText/index.tsx | 1 + .../components/EnvironmentPageForm.tsx | 156 ++++++++++++------ 3 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 frontend/src/components/v2/HighlightText/HighlightText.tsx create mode 100644 frontend/src/components/v2/HighlightText/index.tsx diff --git a/frontend/src/components/v2/HighlightText/HighlightText.tsx b/frontend/src/components/v2/HighlightText/HighlightText.tsx new file mode 100644 index 000000000..9b1c525eb --- /dev/null +++ b/frontend/src/components/v2/HighlightText/HighlightText.tsx @@ -0,0 +1,43 @@ +export const HighlightText = ({ + text, + highlight, + highlightClassName +}: { + text: string | undefined | null; + highlight: string; + highlightClassName?: string; +}) => { + if (!text) return null; + const searchTerm = highlight.toLowerCase().trim(); + + if (!searchTerm) return {text}; + + const parts: React.ReactNode[] = []; + let lastIndex = 0; + + const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const regex = new RegExp(escapedSearchTerm, "gi"); + + while (true) { + const match = regex.exec(text); + if (match === null) break; + + if (match.index > lastIndex) { + parts.push({text.substring(lastIndex, match.index)}); + } + + parts.push( + + {text.substring(match.index, match.index + match[0].length)} + + ); + + lastIndex = regex.lastIndex; + } + + if (lastIndex < text.length) { + parts.push({text.substring(lastIndex)}); + } + + return parts; +}; diff --git a/frontend/src/components/v2/HighlightText/index.tsx b/frontend/src/components/v2/HighlightText/index.tsx new file mode 100644 index 000000000..a2ff1b504 --- /dev/null +++ b/frontend/src/components/v2/HighlightText/index.tsx @@ -0,0 +1 @@ +export { HighlightText } from "./HighlightText"; diff --git a/frontend/src/pages/admin/EnvironmentPage/components/EnvironmentPageForm.tsx b/frontend/src/pages/admin/EnvironmentPage/components/EnvironmentPageForm.tsx index 8e07e37be..f2c50a3f5 100644 --- a/frontend/src/pages/admin/EnvironmentPage/components/EnvironmentPageForm.tsx +++ b/frontend/src/pages/admin/EnvironmentPage/components/EnvironmentPageForm.tsx @@ -1,16 +1,116 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; -import { faExclamationTriangle, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; +import { Control, Controller, useForm, useWatch } from "react-hook-form"; +import { + faChevronRight, + faExclamationTriangle, + faMagnifyingGlass +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { Button, FormControl, Input, SecretInput, Tooltip } from "@app/components/v2"; +import { HighlightText } from "@app/components/v2/HighlightText"; import { useGetEnvOverrides, useUpdateServerConfig } from "@app/hooks/api"; type TForm = Record; +export const GroupContainer = ({ + group, + control, + search +}: { + group: { + fields: { + key: string; + value: string; + hasEnvEntry: boolean; + description?: string; + }[]; + name: string; + }; + control: Control; + search: string; +}) => { + const [open, setOpen] = useState(false); + + return ( +
+
setOpen((o) => !o)} + onKeyDown={(e) => { + if (e.key === "Enter") { + setOpen((o) => !o); + } + }} + > + + +
{group.name}
+
+ + {(open || search) && ( +
+ {group.fields.map((field) => ( +
+
+ + + + + + +
+ +
+ {field.hasEnvEntry && ( + + + + )} + + ( + + + + )} + /> +
+
+ ))} +
+ )} +
+ ); +}; + export const EnvironmentPageForm = () => { const { data: envOverrides } = useGetEnvOverrides(); const { mutateAsync: updateServerConfig } = useUpdateServerConfig(); @@ -130,57 +230,9 @@ export const EnvironmentPageForm = () => { placeholder="Search for keys, descriptions, and values..." className="flex-1" /> -
+
{filteredData.map((group) => ( -
- {group!.name} -
-
- {group!.fields.map((field, i) => ( -
-
- {field.key} - {field.description} -
- -
- {field.hasEnvEntry && ( - - - - )} - - ( - - - - )} - /> -
-
- ))} -
-
-
+ ))}