diff --git a/keep-ui/app/alerts/alerts.tsx b/keep-ui/app/alerts/alerts.tsx index c08f82689..64e1f57b9 100644 --- a/keep-ui/app/alerts/alerts.tsx +++ b/keep-ui/app/alerts/alerts.tsx @@ -63,8 +63,8 @@ export default function Alerts({ const [groupedByAlerts, setGroupedByAlerts] = useState<{ [key: string]: Alert[]; }>({}); - const [alertNameSearchString, setAlertNameSearchString] = useState( - searchParams?.get("searchQuery") || "" + const [alertSearchString, setAlertSearchString] = useState( + searchParams?.get("searchQuery") || searchParams?.get("fingerprint") || "" ); const [selectedStatus, setSelectedStatus] = useState([]); const [selectedAssignees, setSelectedAssignees] = useState([]); @@ -225,13 +225,14 @@ export default function Alerts({ function searchAlert(alert: Alert): boolean { return ( - alertNameSearchString === "" || - alertNameSearchString === undefined || - alertNameSearchString === null || - alert.name.toLowerCase().includes(alertNameSearchString.toLowerCase()) || + alertSearchString === "" || + alertSearchString === undefined || + alertSearchString === null || + alert.name.toLowerCase().includes(alertSearchString.toLowerCase()) || alert.description ?.toLowerCase() - .includes(alertNameSearchString.toLowerCase()) || + .includes(alertSearchString.toLowerCase()) || + alert.fingerprint === alertSearchString || false ); } @@ -302,9 +303,9 @@ export default function Alerts({ className="max-w-[280px] ml-2.5" icon={MagnifyingGlassIcon} placeholder="Search Alert..." - value={alertNameSearchString} + value={alertSearchString} onChange={(e) => { - setAlertNameSearchString(e.target.value); + setAlertSearchString(e.target.value); router.push( pathname + "?" + diff --git a/keep-ui/app/command-menu.tsx b/keep-ui/app/command-menu.tsx index 3a262abbf..8869d7a44 100644 --- a/keep-ui/app/command-menu.tsx +++ b/keep-ui/app/command-menu.tsx @@ -1,146 +1,222 @@ "use client"; -import { Command } from 'cmdk' -import { useState, useEffect } from 'react'; -import { useRouter } from 'next/navigation'; -import { GitHubLogoIcon, FileTextIcon, TwitterLogoIcon } from '@radix-ui/react-icons' - -import '../styles/linear.scss'; +import { Command } from "cmdk"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import { + GitHubLogoIcon, + FileTextIcon, + TwitterLogoIcon, +} from "@radix-ui/react-icons"; +import { + GlobeAltIcon, + UserGroupIcon, + EnvelopeIcon, + KeyIcon, + BriefcaseIcon, +} from "@heroicons/react/24/outline"; +import "../styles/linear.scss"; export function CMDK() { - const [open, setOpen] = useState(false) - const router = useRouter(); + const [open, setOpen] = useState(false); + const router = useRouter(); // Toggle the menu when ⌘K is pressed useEffect(() => { const down = (e: any) => { - if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { - e.preventDefault() - setOpen((open) => !open) + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setOpen((open) => !open); } - } + }; - document.addEventListener('keydown', down) - return () => document.removeEventListener('keydown', down) - }, []) + document.addEventListener("keydown", down); + return () => document.removeEventListener("keydown", down); + }, []); return (
Keep Command Palette
- + No results found. {navigationItems.map(({ icon, label, shortcut, navigate }) => { - return ( - { - setOpen((open) => !open); - router.push(navigate!); - }}> - {icon} - {label} -
+ return ( + { + setOpen((open) => !open); + router.push(navigate!); + }} + > + {icon} + {label} +
{shortcut.map((key) => { - return {key} + return {key}; })} -
+
- ) + ); })} -
+
- + {externalItems.map(({ icon, label, shortcut, navigate }) => { - return ( - { - setOpen((open) => !open); - window.open(navigate, "_blank"); - }}> - {icon} - {label} -
+ return ( + { + setOpen((open) => !open); + window.open(navigate, "_blank"); + }} + > + {icon} + {label} +
{shortcut.map((key) => { - return {key} + return {key}; })} -
+
- ) + ); })} -
+
- ) + ); } const navigationItems = [ + { + icon: , + label: "Go to the providers page", + shortcut: ["p"], + navigate: "/providers", + }, { icon: , - label: 'Go to alert console', - shortcut: ['G'], - navigate: '/alerts' + label: "Go to alert console", + shortcut: ["g"], + navigate: "/alerts", }, { - icon: , - label: 'Go to the providers page', - shortcut: ['P'], - navigate: '/providers' + icon: , + label: "Go to the workflows page", + shortcut: ["wf"], + navigate: "/workflows", + }, + { + icon: , + label: "Go to users management", + shortcut: ["u"], + navigate: "/settings", + }, + { + icon: , + label: "Go to generic webhook", + shortcut: ["w"], + navigate: "/settings?selectedTab=webhook", + }, + { + icon: , + label: "Go to SMTP settings", + shortcut: ["s"], + navigate: "/settings?selectedTab=smtp", }, { - icon: , - label: 'Go to alert workflow builder', - shortcut: ['D'], - navigate: '/workflows/builder' - } -] + icon: , + label: "Go to API key", + shortcut: ["a"], + navigate: "/settings?selectedTab=api-key", + }, +]; const externalItems = [ - { - icon: , - label: 'Keep Docs', - shortcut: ['⇧', 'D'], - navigate: 'https://docs.keephq.dev' - }, - { - icon: , - label: 'Keep Source code', - shortcut: ['⇧', 'C'], - navigate: 'https://github.com/keephq/keep' - }, - { - icon: , - label: 'Keep Twitter', - shortcut: ['⇧', 'T'], - navigate: 'https://twitter.com/keepalerting' - } -] + { + icon: , + label: "Keep Docs", + shortcut: ["⇧", "D"], + navigate: "https://docs.keephq.dev", + }, + { + icon: , + label: "Keep Source code", + shortcut: ["⇧", "C"], + navigate: "https://github.com/keephq/keep", + }, + { + icon: , + label: "Keep Twitter", + shortcut: ["⇧", "T"], + navigate: "https://twitter.com/keepalerting", + }, +]; function ConnectIntegrationIcon() { return ( - - - - ) + + + + ); } function GoToConsoleIcon() { return ( - - ) + + + + + ); } function CreateAlertIcon() { return ( - - ) + + + + ); } function GoToDashboardIcon() { return ( - - + + - ) + ); } diff --git a/keep-ui/app/navbar-inner.tsx b/keep-ui/app/navbar-inner.tsx index 80127238b..9731942cf 100644 --- a/keep-ui/app/navbar-inner.tsx +++ b/keep-ui/app/navbar-inner.tsx @@ -5,7 +5,11 @@ import { signOut } from "next-auth/react"; import { Fragment } from "react"; import { Bars3Icon, + BellAlertIcon, + BriefcaseIcon, DocumentTextIcon, + EnvelopeOpenIcon, + PuzzlePieceIcon, XMarkIcon, } from "@heroicons/react/24/outline"; import Link from "next/link"; @@ -17,9 +21,14 @@ import { User } from "next-auth"; import { InternalConfig } from "types/internal-config"; const navigation = [ - { name: "Providers", href: "/providers" }, - { name: "Alerts", href: "/alerts" }, - { name: "Workflows", href: "/workflows" }, + { name: "Providers", href: "/providers", icon: PuzzlePieceIcon }, + { name: "Alerts", href: "/alerts", icon: BellAlertIcon }, + { name: "Workflows", href: "/workflows", icon: BriefcaseIcon }, + // { + // name: "Notifications Hub", + // href: "/notifications-hub", + // icon: EnvelopeOpenIcon, + // }, ]; function classNames(...classes: string[]) { @@ -108,6 +117,7 @@ export default function NavbarInner({ user }: { user?: User }) { )} aria-current={pathname === item.href ? "page" : undefined} > + {item.name} ))} diff --git a/keep-ui/app/notifications-hub/layout.tsx b/keep-ui/app/notifications-hub/layout.tsx new file mode 100644 index 000000000..9393d37c7 --- /dev/null +++ b/keep-ui/app/notifications-hub/layout.tsx @@ -0,0 +1,15 @@ +import { Title, Subtitle } from "@tremor/react"; + +export default function Layout({ children }: { children: any }) { + return ( + <> +
+ Notifications Hub + + A single pane for everything related with notifications + + {children} +
+ + ); +} diff --git a/keep-ui/app/notifications-hub/page.tsx b/keep-ui/app/notifications-hub/page.tsx new file mode 100644 index 000000000..853d619fe --- /dev/null +++ b/keep-ui/app/notifications-hub/page.tsx @@ -0,0 +1,14 @@ +import { Card } from "@tremor/react"; + +export default function Page() { + return ( + +
Hello World
+ + ); +} + +export const metadata = { + title: "Keep - Notifications Hub", + description: "Manage everything related with notifications.", +}; diff --git a/keep-ui/app/settings/settings.client.tsx b/keep-ui/app/settings/settings.client.tsx index e20dc9735..ce0c7c3a8 100644 --- a/keep-ui/app/settings/settings.client.tsx +++ b/keep-ui/app/settings/settings.client.tsx @@ -13,7 +13,7 @@ import { useSession } from "next-auth/react"; import Loading from "app/loading"; import SmtpSettings from "./smtp-settings"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { useState } from "react"; +import { useCallback, useEffect, useState } from "react"; export default function SettingsPage() { const { data: session, status } = useSession(); @@ -23,31 +23,37 @@ export default function SettingsPage() { const [selectedTab, setSelectedTab] = useState( searchParams?.get("selectedTab") || "users" ); + const [tabIndex, setTabIndex] = useState(0); - const handleTabChange = (tab: string) => { - setSelectedTab(tab); - router.push(`${pathname}?selectedTab=${tab}`); - }; + const handleTabChange = useCallback( + (tab: string) => { + setSelectedTab(tab); + router.push(`${pathname}?selectedTab=${tab}`); + }, + [pathname, router] + ); + + useEffect(() => { + const selectedTab = searchParams?.get("selectedTab") || "users"; + handleTabChange(selectedTab); + }, [searchParams, handleTabChange]); // TODO: more robust way to handle this - const tabIndex = - selectedTab === "users" - ? 0 - : selectedTab === "webhook" - ? 1 - : selectedTab === "smtp" - ? 2 - : 3; + useEffect(() => { + const tabIndex = + selectedTab === "users" + ? 0 + : selectedTab === "webhook" + ? 1 + : selectedTab === "smtp" + ? 2 + : 3; + setTabIndex(tabIndex); + }, [selectedTab]); if (status === "loading") return ; if (status === "unauthenticated") router.push("/signin"); - /** - * TODO: Refactor this page to use pages - * Right now we load all components at once when we load the main settings page. - * It should be /settings/users and /settings/webhook, etc. - * Think about a proper way to implement it. - */ return (