diff --git a/docker/Dockerfile.api b/docker/Dockerfile.api index 291210d25..9e000384e 100644 --- a/docker/Dockerfile.api +++ b/docker/Dockerfile.api @@ -35,4 +35,7 @@ RUN chgrp -R 0 /app && chmod -R g=u /app RUN chown -R keep:keep /app RUN chown -R keep:keep /venv USER keep -ENTRYPOINT ["gunicorn", "keep.api.api:get_app", "--bind" , "0.0.0.0:8080" , "--workers", "4" , "-k" , "uvicorn.workers.UvicornWorker", "-c", "/venv/lib/python3.11/site-packages/keep/api/config.py"] + +ENTRYPOINT ["/venv/lib/python3.11/site-packages/keep/entrypoint.sh"] + +CMD ["gunicorn", "keep.api.api:get_app", "--bind" , "0.0.0.0:8080" , "--workers", "4" , "-k" , "uvicorn.workers.UvicornWorker", "-c", "/venv/lib/python3.11/site-packages/keep/api/config.py"] diff --git a/docker/Dockerfile.dev.api b/docker/Dockerfile.dev.api index 5b3f12b70..d4f4e3a7b 100644 --- a/docker/Dockerfile.dev.api +++ b/docker/Dockerfile.dev.api @@ -21,6 +21,8 @@ RUN . /venv/bin/activate && poetry install --no-root ENV PYTHONPATH="/app:${PYTHONPATH}" ENV PATH="/venv/bin:${PATH}" ENV VIRTUAL_ENV="/venv" +ENV POSTHOG_DISABLED="true" +ENTRYPOINT ["/app/keep/entrypoint.sh"] CMD ["gunicorn", "keep.api.api:get_app", "--bind" , "0.0.0.0:8080" , "--workers", "1" , "-k" , "uvicorn.workers.UvicornWorker", "-c", "./keep/api/config.py", "--reload"] diff --git a/docs/deployment/configuration.mdx b/docs/deployment/configuration.mdx index eaa04d439..ddf404912 100644 --- a/docs/deployment/configuration.mdx +++ b/docs/deployment/configuration.mdx @@ -25,6 +25,7 @@ General configuration variables control the core behavior of the Keep server. Th | **KEEP_API_URL** | Specifies the Keep API URL | No | Constructed from HOST and PORT | Valid URL | | **KEEP_STORE_RAW_ALERTS** | Enables storing of raw alerts | No | "false" | "true" or "false" | | **TENANT_CONFIGURATION_RELOAD_TIME** | Time in minutes to reload tenant configurations | No | 5 | Positive integer | +| **KEEP_LIVE_DEMO_MODE** | Keep will simulate incoming alerts and other activity | No | "false" | "true" or "false" | ### Logging and Environment diff --git a/docs/images/appdynamics_1.png b/docs/images/appdynamics_1.png new file mode 100644 index 000000000..ab13ca82c Binary files /dev/null and b/docs/images/appdynamics_1.png differ diff --git a/docs/images/appdynamics_10.png b/docs/images/appdynamics_10.png new file mode 100644 index 000000000..40c51be10 Binary files /dev/null and b/docs/images/appdynamics_10.png differ diff --git a/docs/images/appdynamics_2.png b/docs/images/appdynamics_2.png new file mode 100644 index 000000000..fbb1c9f9c Binary files /dev/null and b/docs/images/appdynamics_2.png differ diff --git a/docs/images/appdynamics_3.png b/docs/images/appdynamics_3.png new file mode 100644 index 000000000..5ee96bdd4 Binary files /dev/null and b/docs/images/appdynamics_3.png differ diff --git a/docs/images/appdynamics_4.png b/docs/images/appdynamics_4.png new file mode 100644 index 000000000..5acd5be83 Binary files /dev/null and b/docs/images/appdynamics_4.png differ diff --git a/docs/images/appdynamics_5.png b/docs/images/appdynamics_5.png new file mode 100644 index 000000000..04aeb2449 Binary files /dev/null and b/docs/images/appdynamics_5.png differ diff --git a/docs/images/appdynamics_6.png b/docs/images/appdynamics_6.png new file mode 100644 index 000000000..3b5a67ed6 Binary files /dev/null and b/docs/images/appdynamics_6.png differ diff --git a/docs/images/appdynamics_7.png b/docs/images/appdynamics_7.png new file mode 100644 index 000000000..3bc5cfb6b Binary files /dev/null and b/docs/images/appdynamics_7.png differ diff --git a/docs/images/appdynamics_8.png b/docs/images/appdynamics_8.png new file mode 100644 index 000000000..be94a6b88 Binary files /dev/null and b/docs/images/appdynamics_8.png differ diff --git a/docs/images/appdynamics_9.png b/docs/images/appdynamics_9.png new file mode 100644 index 000000000..ed91c73fe Binary files /dev/null and b/docs/images/appdynamics_9.png differ diff --git a/docs/providers/documentation/appdynamics-provider.mdx b/docs/providers/documentation/appdynamics-provider.mdx index eef0e7a76..ebc8f9cf1 100644 --- a/docs/providers/documentation/appdynamics-provider.mdx +++ b/docs/providers/documentation/appdynamics-provider.mdx @@ -7,19 +7,108 @@ description: "AppDynamics provider allows you to get AppDynamics `alerts/actions ## Authentication Parameters The AppDynamics provider requires the following authentication parameter: -- `AppDynamics Username`: Required. This is your AppDynamics account username. -- `AppDynamics Password`: This is the password associated with your AppDynamics Username. +- `AppDynamics Access Token`: Required if username/password is not provided for Bearer token authentication. +- `AppDynamics Username`: Required for Basic Auth authentication. This is your AppDynamics account username. +- `AppDynamics Password`: Required for Basic Auth authentication. This is the password associated with your AppDynamics Username. - `AppDynamics Account Name`: This is your account's name. - `App Id`: The Id of the Application in which you would like to install the webhook. - `Host`: This is the hostname of the AppDynamics instance you wish to connect to. It identifies the AppDynamics server that the API will interact with. ## Connecting with the Provider +1. Ensure you have a AppDynamics account with the necessary [permissions](https://docs.appdynamics.com/accounts/en/cisco-appdynamics-on-premises-user-management/roles-and-permissions). The basic permissions required are `Account Owner` or `Administrator`. Alternatively you can create an account [instructions](https://docs.appdynamics.com/accounts/en/global-account-administration/access-management/manage-user-accounts) -Obtain AppDynamics Username and Password: -1. Ensure you have a AppDynamics account with the necessary [permissions](https://docs.appdynamics.com/accounts/en/cisco-appdynamics-on-premises-user-management/roles-and-permissions). The basic permissions required are `Account Owner` or `Administrator`. Alternatively you can create an account (instructions)[https://docs.appdynamics.com/accounts/en/global-account-administration/access-management/manage-user-accounts] -2. Find your account name [here](https://accounts.appdynamics.com/overview). +## Provider configuration + +1. Find your account name [here](https://accounts.appdynamics.com/overview). +2. Get the appId of the Appdynamics instance in which you wish to install the webhook into. 3. Determine the Host [here](https://accounts.appdynamics.com/overview). -4. Get the appId of the Appdynamics instance in which you wish to install the webhook into. + +### Basic Auth authentication + +1. Obtain AppDynamics **Username** and **Password** +2. Go to **Basic Auth** tab under **Authentication** section +3. Enter **Username** and **Password** + + + Keep add AppDynamics Username and Password + + +### Access Token authentication + +1. Log in to the **Controller UI** as an **Account Owner** or other roles with the **Administer users**, **groups**, **roles** permission. +2. Go to **Administration** + + + AppDynamics Administration + + +3. Go to **API Client** tab + + + AppDynamics API Client tab + + +4. Click **+ Create** + + + Create new AppDynamics API Client + + +5. Fill Client **Name** and **Description** +6. Click **Generate Secret** + + + AppDynamics generate API Client Secret + + + + This API Client secret is not an authentication token yet + + +7. Add **Account Owner** and/or **Administrator** roles + + + AppDynamics add API Client roles + + +8. Click **Save** + + + AppDynamics save API Client + + +9. Click **Generate Temporary Token** + + + AppDynamics Generate API Client Temporary Access Token + + + + This token is not persistent, but since Keep uses it just once to install Webhook, we will use it without oAuth + + +10. Click **Save** one again + + This is important. Otherwise generated token will not be saved and authentication will fail + +11. Copy generated token + + + AppDynamics copy API Client Temporary Access Token + + +12. Go to **Access Token** tab under **Authentication** section + + + Keep add AppDynamics Access Token + + +13. Enter Access Token + +## Connecting provider + +1. Ensure **Install webhook** is checked +2. Click **Connect** ## Webhook Integration Modifications diff --git a/keep-ui/app/(keep)/[...not-found]/page.tsx b/keep-ui/app/(keep)/[...not-found]/page.tsx new file mode 100644 index 000000000..13a9bef77 --- /dev/null +++ b/keep-ui/app/(keep)/[...not-found]/page.tsx @@ -0,0 +1,8 @@ +"use client"; + +import { notFound } from "next/navigation"; + +// https://github.com/vercel/next.js/discussions/50034 +export default function NotFoundDummy() { + notFound(); +} diff --git a/keep-ui/app/ai/ai.tsx b/keep-ui/app/(keep)/ai/ai.tsx similarity index 98% rename from keep-ui/app/ai/ai.tsx rename to keep-ui/app/(keep)/ai/ai.tsx index 7e9d23aa7..2446337a7 100644 --- a/keep-ui/app/ai/ai.tsx +++ b/keep-ui/app/(keep)/ai/ai.tsx @@ -1,7 +1,7 @@ "use client"; import { Card, List, ListItem, Title, Subtitle } from "@tremor/react"; import { useAIStats, usePollAILogs } from "utils/hooks/useAI"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useApiUrl } from "utils/hooks/useConfig"; import { toast } from "react-toastify"; import { useEffect, useState, useRef, FormEvent } from "react"; diff --git a/keep-ui/app/ai/model.ts b/keep-ui/app/(keep)/ai/model.ts similarity index 83% rename from keep-ui/app/ai/model.ts rename to keep-ui/app/(keep)/ai/model.ts index 3d78cbb9f..c393d3960 100644 --- a/keep-ui/app/ai/model.ts +++ b/keep-ui/app/(keep)/ai/model.ts @@ -3,9 +3,9 @@ export interface AIStats { incidents_count: number; first_alert_datetime?: Date; is_mining_enabled: boolean; - algorithm_verbose_name: string + algorithm_verbose_name: string; } export interface AILogs { log: string; -} \ No newline at end of file +} diff --git a/keep-ui/app/ai/page.tsx b/keep-ui/app/(keep)/ai/page.tsx similarity index 100% rename from keep-ui/app/ai/page.tsx rename to keep-ui/app/(keep)/ai/page.tsx diff --git a/keep-ui/app/alerts/ColumnSelection.tsx b/keep-ui/app/(keep)/alerts/ColumnSelection.tsx similarity index 100% rename from keep-ui/app/alerts/ColumnSelection.tsx rename to keep-ui/app/(keep)/alerts/ColumnSelection.tsx diff --git a/keep-ui/app/alerts/ThemeSelection.tsx b/keep-ui/app/(keep)/alerts/ThemeSelection.tsx similarity index 54% rename from keep-ui/app/alerts/ThemeSelection.tsx rename to keep-ui/app/(keep)/alerts/ThemeSelection.tsx index f91c2c7a0..f9058dddd 100644 --- a/keep-ui/app/alerts/ThemeSelection.tsx +++ b/keep-ui/app/(keep)/alerts/ThemeSelection.tsx @@ -1,43 +1,54 @@ -import React, { useState, Fragment, useRef, FormEvent } from 'react'; -import { Popover } from '@headlessui/react'; -import { Button, Tab, TabGroup, TabList, TabPanels, TabPanel } from "@tremor/react"; -import { IoColorPaletteOutline } from 'react-icons/io5'; +import React, { useState, Fragment, useRef, FormEvent } from "react"; +import { Popover } from "@headlessui/react"; +import { + Button, + Tab, + TabGroup, + TabList, + TabPanels, + TabPanel, +} from "@tremor/react"; +import { IoColorPaletteOutline } from "react-icons/io5"; import { FloatingArrow, arrow, offset, useFloating } from "@floating-ui/react"; const predefinedThemes = { Transparent: { - critical: 'bg-white', - high: 'bg-white', - warning: 'bg-white', - low: 'bg-white', - info: 'bg-white' + critical: "bg-white", + high: "bg-white", + warning: "bg-white", + low: "bg-white", + info: "bg-white", }, Keep: { - critical: 'bg-orange-400', // Highest opacity for critical - high: 'bg-orange-300', - warning: 'bg-orange-200', - low: 'bg-orange-100', - info: 'bg-orange-50' // Lowest opacity for info + critical: "bg-orange-400", // Highest opacity for critical + high: "bg-orange-300", + warning: "bg-orange-200", + low: "bg-orange-100", + info: "bg-orange-50", // Lowest opacity for info }, Basic: { - critical: 'bg-red-200', - high: 'bg-orange-200', - warning: 'bg-yellow-200', - low: 'bg-green-200', - info: 'bg-blue-200' - } + critical: "bg-red-200", + high: "bg-orange-200", + warning: "bg-yellow-200", + low: "bg-green-200", + info: "bg-blue-200", + }, }; const themeKeyMapping = { - 0: 'Transparent', - 1: 'Keep', - 2: 'Basic' + 0: "Transparent", + 1: "Keep", + 2: "Basic", }; type ThemeName = keyof typeof predefinedThemes; -export const ThemeSelection = ({ onThemeChange }: { onThemeChange: (theme: any) => void }) => { +export const ThemeSelection = ({ + onThemeChange, +}: { + onThemeChange: (theme: any) => void; +}) => { const arrowRef = useRef(null); - const [selectedTab, setSelectedTab] = useState('Transparent'); + const [selectedTab, setSelectedTab] = useState("Transparent"); const { refs, floatingStyles, context } = useFloating({ strategy: "fixed", @@ -50,22 +61,17 @@ export const ThemeSelection = ({ onThemeChange }: { onThemeChange: (theme: any) handleApplyTheme(themeIndex as 0 | 1 | 2); }; - - - const handleApplyTheme = (themeKey: keyof typeof themeKeyMapping) => { const themeName = themeKeyMapping[themeKey]; setSelectedTab(themeName as ThemeName); -}; - - + }; const onApplyTheme = (close: () => void) => { // themeName is now assured to be a key of predefinedThemes const themeName: ThemeName = selectedTab; const newTheme = predefinedThemes[themeName]; // This should now be error-free onThemeChange(newTheme); - setSelectedTab('Transparent'); // Assuming 'Transparent' is a valid key + setSelectedTab("Transparent"); // Assuming 'Transparent' is a valid key close(); // Close the popover }; @@ -85,7 +91,7 @@ export const ThemeSelection = ({ onThemeChange }: { onThemeChange: (theme: any) Basic - {Object.keys(predefinedThemes).map(themeName => ( + {Object.keys(predefinedThemes).map((themeName) => ( - {Object.entries(predefinedThemes[themeName as keyof typeof predefinedThemes]).map(([severity, color]) => ( -
- {severity.charAt(0).toUpperCase() + severity.slice(1).toLowerCase()} -
+ {Object.entries( + predefinedThemes[ + themeName as keyof typeof predefinedThemes + ] + ).map(([severity, color]) => ( +
+ + {severity.charAt(0).toUpperCase() + + severity.slice(1).toLowerCase()} + +
))} ))} - diff --git a/keep-ui/app/alerts/TitleAndFilters.tsx b/keep-ui/app/(keep)/alerts/TitleAndFilters.tsx similarity index 100% rename from keep-ui/app/alerts/TitleAndFilters.tsx rename to keep-ui/app/(keep)/alerts/TitleAndFilters.tsx diff --git a/keep-ui/app/alerts/ViewAlertModal.css b/keep-ui/app/(keep)/alerts/ViewAlertModal.css similarity index 100% rename from keep-ui/app/alerts/ViewAlertModal.css rename to keep-ui/app/(keep)/alerts/ViewAlertModal.css diff --git a/keep-ui/app/alerts/ViewAlertModal.tsx b/keep-ui/app/(keep)/alerts/ViewAlertModal.tsx similarity index 98% rename from keep-ui/app/alerts/ViewAlertModal.tsx rename to keep-ui/app/(keep)/alerts/ViewAlertModal.tsx index 6bbeca28f..7bc404172 100644 --- a/keep-ui/app/alerts/ViewAlertModal.tsx +++ b/keep-ui/app/(keep)/alerts/ViewAlertModal.tsx @@ -3,7 +3,7 @@ import Modal from "@/components/ui/Modal"; // Ensure this path matches your proj import { Button, Icon, Switch, Text } from "@tremor/react"; import { toast } from "react-toastify"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { XMarkIcon } from "@heroicons/react/24/outline"; import "./ViewAlertModal.css"; import React, { useState } from "react"; diff --git a/keep-ui/app/alerts/[id]/page.tsx b/keep-ui/app/(keep)/alerts/[id]/page.tsx similarity index 88% rename from keep-ui/app/alerts/[id]/page.tsx rename to keep-ui/app/(keep)/alerts/[id]/page.tsx index 89a007659..2137123d8 100644 --- a/keep-ui/app/alerts/[id]/page.tsx +++ b/keep-ui/app/(keep)/alerts/[id]/page.tsx @@ -1,4 +1,4 @@ -import AlertsPage from "../alerts.client"; +import AlertsPage from "../alerts"; type PageProps = { params: { id: string }; diff --git a/keep-ui/app/alerts/alert-actions.tsx b/keep-ui/app/(keep)/alerts/alert-actions.tsx similarity index 100% rename from keep-ui/app/alerts/alert-actions.tsx rename to keep-ui/app/(keep)/alerts/alert-actions.tsx diff --git a/keep-ui/app/alerts/alert-assign-ticket-modal.tsx b/keep-ui/app/(keep)/alerts/alert-assign-ticket-modal.tsx similarity index 98% rename from keep-ui/app/alerts/alert-assign-ticket-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-assign-ticket-modal.tsx index f3920c764..5bcd694ae 100644 --- a/keep-ui/app/alerts/alert-assign-ticket-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-assign-ticket-modal.tsx @@ -3,8 +3,8 @@ import Select, { components } from "react-select"; import { Button, TextInput, Text } from "@tremor/react"; import { PlusIcon } from "@heroicons/react/20/solid"; import { useForm, Controller, SubmitHandler } from "react-hook-form"; -import { Providers } from "./../providers/providers"; -import { useSession } from "next-auth/react"; +import { Providers } from "../providers/providers"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useApiUrl } from "utils/hooks/useConfig"; import { AlertDto } from "./models"; import Modal from "@/components/ui/Modal"; diff --git a/keep-ui/app/alerts/alert-assignee.tsx b/keep-ui/app/(keep)/alerts/alert-assignee.tsx similarity index 100% rename from keep-ui/app/alerts/alert-assignee.tsx rename to keep-ui/app/(keep)/alerts/alert-assignee.tsx diff --git a/keep-ui/app/alerts/alert-associate-incident-modal.tsx b/keep-ui/app/(keep)/alerts/alert-associate-incident-modal.tsx similarity index 95% rename from keep-ui/app/alerts/alert-associate-incident-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-associate-incident-modal.tsx index 9d59afdbf..ff8de5ab5 100644 --- a/keep-ui/app/alerts/alert-associate-incident-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-associate-incident-modal.tsx @@ -2,12 +2,15 @@ import Modal from "@/components/ui/Modal"; import { Button, Divider, Title } from "@tremor/react"; import Select from "@/components/ui/Select"; import { CreateOrUpdateIncidentForm } from "@/features/create-or-update-incident"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { FormEvent, useCallback, useEffect, useState } from "react"; import { toast } from "react-toastify"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useIncidents, usePollIncidents } from "../../utils/hooks/useIncidents"; -import Loading from "../loading"; +import { + useIncidents, + usePollIncidents, +} from "../../../utils/hooks/useIncidents"; +import Loading from "@/app/(keep)/loading"; import { AlertDto } from "./models"; import { getIncidentName } from "@/entities/incidents/lib/utils"; diff --git a/keep-ui/app/alerts/alert-change-status-modal.tsx b/keep-ui/app/(keep)/alerts/alert-change-status-modal.tsx similarity index 98% rename from keep-ui/app/alerts/alert-change-status-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-change-status-modal.tsx index d090599dc..f68f46e61 100644 --- a/keep-ui/app/alerts/alert-change-status-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-change-status-modal.tsx @@ -9,7 +9,7 @@ import Select, { import { useState } from "react"; import { AlertDto, Status } from "./models"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { toast } from "react-toastify"; import { CheckCircleIcon, diff --git a/keep-ui/app/alerts/alert-create-incident-ai-card.tsx b/keep-ui/app/(keep)/alerts/alert-create-incident-ai-card.tsx similarity index 100% rename from keep-ui/app/alerts/alert-create-incident-ai-card.tsx rename to keep-ui/app/(keep)/alerts/alert-create-incident-ai-card.tsx diff --git a/keep-ui/app/alerts/alert-create-incident-ai-modal.tsx b/keep-ui/app/(keep)/alerts/alert-create-incident-ai-modal.tsx similarity index 98% rename from keep-ui/app/alerts/alert-create-incident-ai-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-create-incident-ai-modal.tsx index ada499e65..242eead3f 100644 --- a/keep-ui/app/alerts/alert-create-incident-ai-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-create-incident-ai-modal.tsx @@ -1,9 +1,9 @@ import React, { useState } from "react"; import Modal from "@/components/ui/Modal"; import { Callout, Button, Title, Card } from "@tremor/react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { toast } from "react-toastify"; -import Loading from "../loading"; +import Loading from "@/app/(keep)/loading"; import { AlertDto } from "./models"; import { IncidentDto, IncidentCandidateDto } from "@/entities/incidents/model"; import { useApiUrl } from "utils/hooks/useConfig"; diff --git a/keep-ui/app/(keep)/alerts/alert-dismiss-modal.css b/keep-ui/app/(keep)/alerts/alert-dismiss-modal.css new file mode 100644 index 000000000..20a0bc089 --- /dev/null +++ b/keep-ui/app/(keep)/alerts/alert-dismiss-modal.css @@ -0,0 +1,37 @@ +.react-datepicker { + font-size: 14px !important; + color: #070707 !important; +} + +.react-datepicker__header { + background-color: white !important; + padding-top: 0px !important; + border: none !important; +} + +.react-datepicker__day-name { + color: #c7c7c7 !important; + font-size: 14px !important; +} + +.react-datepicker__day { + color: black !important; + font-size: 13px !important; +} + +.react-datepicker__day--selected, +.react-datepicker__day--keyboard-selected { + border-radius: 25px !important; + background: orange !important; + color: white !important; +} + +.react-datepicker__time-list-item--selected { + background: orange !important; + color: white !important; +} + +.react-datepicker__day--disabled, +.react-datepicker__time-list-item--disabled { + color: #c7c7c7 !important; +} diff --git a/keep-ui/app/alerts/alert-dismiss-modal.tsx b/keep-ui/app/(keep)/alerts/alert-dismiss-modal.tsx similarity index 98% rename from keep-ui/app/alerts/alert-dismiss-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-dismiss-modal.tsx index 11bf3d63f..93976f22d 100644 --- a/keep-ui/app/alerts/alert-dismiss-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-dismiss-modal.tsx @@ -17,7 +17,7 @@ import "react-quill/dist/quill.snow.css"; import { AlertDto } from "./models"; import { format, set, isSameDay, isAfter, addMinutes } from "date-fns"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { usePresets } from "utils/hooks/usePresets"; import { useAlerts } from "utils/hooks/useAlerts"; import { toast } from "react-toastify"; diff --git a/keep-ui/app/alerts/alert-extra-payload.tsx b/keep-ui/app/(keep)/alerts/alert-extra-payload.tsx similarity index 100% rename from keep-ui/app/alerts/alert-extra-payload.tsx rename to keep-ui/app/(keep)/alerts/alert-extra-payload.tsx diff --git a/keep-ui/app/alerts/alert-fatigue-meter.tsx b/keep-ui/app/(keep)/alerts/alert-fatigue-meter.tsx similarity index 100% rename from keep-ui/app/alerts/alert-fatigue-meter.tsx rename to keep-ui/app/(keep)/alerts/alert-fatigue-meter.tsx diff --git a/keep-ui/app/alerts/alert-history-charts.tsx b/keep-ui/app/(keep)/alerts/alert-history-charts.tsx similarity index 74% rename from keep-ui/app/alerts/alert-history-charts.tsx rename to keep-ui/app/(keep)/alerts/alert-history-charts.tsx index 9abb47d8b..b79e37e65 100644 --- a/keep-ui/app/alerts/alert-history-charts.tsx +++ b/keep-ui/app/(keep)/alerts/alert-history-charts.tsx @@ -1,5 +1,5 @@ import { AreaChart } from "@tremor/react"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { AlertDto } from "./models"; import { calculateFatigue } from "utils/fatigue"; @@ -36,23 +36,26 @@ export default function AlertHistoryCharts({ timeUnit = "Hours"; } - const rawChartData = [...alerts].reverse().reduce((prev, curr) => { - const date = curr.lastReceived; - const dateKey = getDateKey(date, timeUnit); - if (!prev[dateKey]) { - prev[dateKey] = { - [curr.status]: 1, - }; - } else { - prev[dateKey][curr.status] - ? (prev[dateKey][curr.status] += 1) - : (prev[dateKey][curr.status] = 1); - } - if (categoriesByStatus.includes(curr.status) === false) { - categoriesByStatus.push(curr.status); - } - return prev; - }, {} as { [date: string]: any }); + const rawChartData = [...alerts].reverse().reduce( + (prev, curr) => { + const date = curr.lastReceived; + const dateKey = getDateKey(date, timeUnit); + if (!prev[dateKey]) { + prev[dateKey] = { + [curr.status]: 1, + }; + } else { + prev[dateKey][curr.status] + ? (prev[dateKey][curr.status] += 1) + : (prev[dateKey][curr.status] = 1); + } + if (categoriesByStatus.includes(curr.status) === false) { + categoriesByStatus.push(curr.status); + } + return prev; + }, + {} as { [date: string]: any } + ); if (categoriesByStatus.includes("Fatigueness") === false) { categoriesByStatus.push("Fatigueness"); diff --git a/keep-ui/app/alerts/alert-history.tsx b/keep-ui/app/(keep)/alerts/alert-history.tsx similarity index 100% rename from keep-ui/app/alerts/alert-history.tsx rename to keep-ui/app/(keep)/alerts/alert-history.tsx diff --git a/keep-ui/app/alerts/alert-menu.tsx b/keep-ui/app/(keep)/alerts/alert-menu.tsx similarity index 98% rename from keep-ui/app/alerts/alert-menu.tsx rename to keep-ui/app/(keep)/alerts/alert-menu.tsx index 997be6959..58846e5ea 100644 --- a/keep-ui/app/alerts/alert-menu.tsx +++ b/keep-ui/app/(keep)/alerts/alert-menu.tsx @@ -12,10 +12,10 @@ import { } from "@heroicons/react/24/outline"; import { IoNotificationsOffOutline } from "react-icons/io5"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useApiUrl } from "utils/hooks/useConfig"; import Link from "next/link"; -import { ProviderMethod } from "app/providers/providers"; +import { ProviderMethod } from "@/app/(keep)/providers/providers"; import { AlertDto } from "./models"; import { useFloating } from "@floating-ui/react"; import { useProviders } from "utils/hooks/useProviders"; diff --git a/keep-ui/app/alerts/alert-method-modal.tsx b/keep-ui/app/(keep)/alerts/alert-method-modal.tsx similarity index 98% rename from keep-ui/app/alerts/alert-method-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-method-modal.tsx index 0d70ab54e..5bd4bfdb2 100644 --- a/keep-ui/app/alerts/alert-method-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-method-modal.tsx @@ -4,11 +4,11 @@ import { Provider, ProviderMethod, ProviderMethodParam, -} from "app/providers/providers"; +} from "@/app/(keep)/providers/providers"; import { getSession } from "next-auth/react"; import { useApiUrl } from "utils/hooks/useConfig"; import { toast } from "react-toastify"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { Button, TextInput, diff --git a/keep-ui/app/alerts/alert-method-results-table.tsx b/keep-ui/app/(keep)/alerts/alert-method-results-table.tsx similarity index 100% rename from keep-ui/app/alerts/alert-method-results-table.tsx rename to keep-ui/app/(keep)/alerts/alert-method-results-table.tsx diff --git a/keep-ui/app/alerts/alert-name.tsx b/keep-ui/app/(keep)/alerts/alert-name.tsx similarity index 96% rename from keep-ui/app/alerts/alert-name.tsx rename to keep-ui/app/(keep)/alerts/alert-name.tsx index 049338de4..8bcca4638 100644 --- a/keep-ui/app/alerts/alert-name.tsx +++ b/keep-ui/app/(keep)/alerts/alert-name.tsx @@ -139,15 +139,15 @@ export default function AlertName({ relevantWorkflowExecution.workflow_status === "success" ? "green" : relevantWorkflowExecution.workflow_status === "error" - ? "red" - : "gray" + ? "red" + : "gray" }`} tooltip={`${ relevantWorkflowExecution.workflow_status === "success" ? "Last workflow executed successfully" : relevantWorkflowExecution.workflow_status === "error" - ? "Last workflow execution failed" - : undefined + ? "Last workflow execution failed" + : undefined }`} onClick={() => handleWorkflowClick(relevantWorkflowExecution)} className="ml-1 cursor-pointer" diff --git a/keep-ui/app/alerts/alert-note-modal.tsx b/keep-ui/app/(keep)/alerts/alert-note-modal.tsx similarity index 97% rename from keep-ui/app/alerts/alert-note-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-note-modal.tsx index a737059a9..fb9f6910b 100644 --- a/keep-ui/app/alerts/alert-note-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-note-modal.tsx @@ -7,7 +7,7 @@ const ReactQuill = import "react-quill/dist/quill.snow.css"; import { Button } from "@tremor/react"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { AlertDto } from "./models"; import Modal from "@/components/ui/Modal"; diff --git a/keep-ui/app/alerts/alert-pagination.tsx b/keep-ui/app/(keep)/alerts/alert-pagination.tsx similarity index 100% rename from keep-ui/app/alerts/alert-pagination.tsx rename to keep-ui/app/(keep)/alerts/alert-pagination.tsx diff --git a/keep-ui/app/alerts/alert-presets.tsx b/keep-ui/app/(keep)/alerts/alert-presets.tsx similarity index 98% rename from keep-ui/app/alerts/alert-presets.tsx rename to keep-ui/app/(keep)/alerts/alert-presets.tsx index 5263b7b1f..217ff5fc2 100644 --- a/keep-ui/app/alerts/alert-presets.tsx +++ b/keep-ui/app/(keep)/alerts/alert-presets.tsx @@ -4,7 +4,7 @@ import Modal from "@/components/ui/Modal"; import { Button, Subtitle, TextInput, Switch, Text } from "@tremor/react"; import { useApiUrl } from "utils/hooks/useConfig"; import { toast } from "react-toastify"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { usePresets } from "utils/hooks/usePresets"; import { useTags } from "utils/hooks/useTags"; import { useRouter } from "next/navigation"; diff --git a/keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx b/keep-ui/app/(keep)/alerts/alert-push-alert-to-server-modal.tsx similarity index 98% rename from keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx rename to keep-ui/app/(keep)/alerts/alert-push-alert-to-server-modal.tsx index 76931c8ed..44576e546 100644 --- a/keep-ui/app/alerts/alert-push-alert-to-server-modal.tsx +++ b/keep-ui/app/(keep)/alerts/alert-push-alert-to-server-modal.tsx @@ -7,7 +7,7 @@ import { FieldValues, } from "react-hook-form"; import Modal from "@/components/ui/Modal"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useApiUrl } from "utils/hooks/useConfig"; import { useProviders } from "utils/hooks/useProviders"; import ImageWithFallback from "@/components/ImageWithFallback"; diff --git a/keep-ui/app/alerts/alert-quality-table.tsx b/keep-ui/app/(keep)/alerts/alert-quality-table.tsx similarity index 98% rename from keep-ui/app/alerts/alert-quality-table.tsx rename to keep-ui/app/(keep)/alerts/alert-quality-table.tsx index 88dcf3e2a..29493b5de 100644 --- a/keep-ui/app/alerts/alert-quality-table.tsx +++ b/keep-ui/app/(keep)/alerts/alert-quality-table.tsx @@ -10,7 +10,7 @@ import React, { import { GenericTable } from "@/components/table/GenericTable"; import { useAlertQualityMetrics } from "utils/hooks/useAlertQuality"; import { useProviders } from "utils/hooks/useProviders"; -import { Provider, ProvidersResponse } from "app/providers/providers"; +import { Provider, ProvidersResponse } from "@/app/(keep)/providers/providers"; import { TabGroup, TabList, Tab, Callout } from "@tremor/react"; import { GenericFilters } from "@/components/filters/GenericFilters"; import { useSearchParams } from "next/navigation"; @@ -171,7 +171,9 @@ const QualityTable = ({ id: "alertsCorrelatedToIncidentsPercentage", header: "% of Alerts Correlated to Incidents", cell: ({ row }) => { - return `${row.original.alertsCorrelatedToIncidentsPercentage.toFixed(2)}%`; + return `${row.original.alertsCorrelatedToIncidentsPercentage.toFixed( + 2 + )}%`; }, }), ] as DisplayColumnDef[]; @@ -341,7 +343,6 @@ const AlertQuality = ({ isDashBoard ? (fieldsValue as string | string[]) : "" ); - if (error) { return ( = ({ alerts, @@ -26,7 +27,9 @@ export const AlertFacets: React.FC = ({ onDelete, className, table, + showSkeleton, }) => { + const pathname = usePathname(); const timeRangeFilter = table .getState() .columnFilters.find((filter) => filter.id === "lastReceived"); @@ -35,7 +38,7 @@ export const AlertFacets: React.FC = ({ | { start: Date; end: Date; isFromCalendar: boolean } | undefined; - const presetName = window.location.pathname.split("/").pop() || "default"; + const presetName = pathname?.split("/").pop() || "default"; const [isModalOpen, setIsModalOpen] = useLocalStorage( `addFacetModalOpen-${presetName}`, @@ -207,6 +210,7 @@ export const AlertFacets: React.FC = ({ } facetKey="severity" facetFilters={facetFilters} + showSkeleton={showSkeleton} /> = ({ } facetKey="status" facetFilters={facetFilters} + showSkeleton={showSkeleton} /> = ({ } facetKey="source" facetFilters={facetFilters} + showSkeleton={showSkeleton} /> = ({ } facetKey="assignee" facetFilters={facetFilters} + showSkeleton={showSkeleton} /> = ({ } facetKey="dismissed" facetFilters={facetFilters} + showSkeleton={showSkeleton} /> = ({ handleSelect("incident", value, exclusive, isAllOnly) } facetFilters={facetFilters} + showSkeleton={showSkeleton} /> {/* Dynamic facets */} {dynamicFacets.map((facet) => ( diff --git a/keep-ui/app/alerts/alert-table-checkbox.tsx b/keep-ui/app/(keep)/alerts/alert-table-checkbox.tsx similarity index 100% rename from keep-ui/app/alerts/alert-table-checkbox.tsx rename to keep-ui/app/(keep)/alerts/alert-table-checkbox.tsx diff --git a/keep-ui/app/alerts/alert-table-facet-dynamic.tsx b/keep-ui/app/(keep)/alerts/alert-table-facet-dynamic.tsx similarity index 100% rename from keep-ui/app/alerts/alert-table-facet-dynamic.tsx rename to keep-ui/app/(keep)/alerts/alert-table-facet-dynamic.tsx diff --git a/keep-ui/app/alerts/alert-table-facet-types.tsx b/keep-ui/app/(keep)/alerts/alert-table-facet-types.tsx similarity index 95% rename from keep-ui/app/alerts/alert-table-facet-types.tsx rename to keep-ui/app/(keep)/alerts/alert-table-facet-types.tsx index d07439d4b..bcee24fd2 100644 --- a/keep-ui/app/alerts/alert-table-facet-types.tsx +++ b/keep-ui/app/(keep)/alerts/alert-table-facet-types.tsx @@ -29,6 +29,7 @@ export interface FacetProps { facetKey: string; facetFilters: FacetFilters; showIcon?: boolean; + showSkeleton?: boolean; } export interface AlertFacetsProps { @@ -44,4 +45,5 @@ export interface AlertFacetsProps { onDelete: (facetKey: string) => void; className?: string; table: Table; + showSkeleton?: boolean; } diff --git a/keep-ui/app/alerts/alert-table-facet-utils.tsx b/keep-ui/app/(keep)/alerts/alert-table-facet-utils.tsx similarity index 100% rename from keep-ui/app/alerts/alert-table-facet-utils.tsx rename to keep-ui/app/(keep)/alerts/alert-table-facet-utils.tsx diff --git a/keep-ui/app/alerts/alert-table-facet-value.tsx b/keep-ui/app/(keep)/alerts/alert-table-facet-value.tsx similarity index 100% rename from keep-ui/app/alerts/alert-table-facet-value.tsx rename to keep-ui/app/(keep)/alerts/alert-table-facet-value.tsx diff --git a/keep-ui/app/alerts/alert-table-facet.tsx b/keep-ui/app/(keep)/alerts/alert-table-facet.tsx similarity index 80% rename from keep-ui/app/alerts/alert-table-facet.tsx rename to keep-ui/app/(keep)/alerts/alert-table-facet.tsx index 03fa7556c..83a53a909 100644 --- a/keep-ui/app/alerts/alert-table-facet.tsx +++ b/keep-ui/app/(keep)/alerts/alert-table-facet.tsx @@ -4,6 +4,8 @@ import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/20/solid"; import { FacetProps } from "./alert-table-facet-types"; import { FacetValue } from "./alert-table-facet-value"; import { useLocalStorage } from "utils/hooks/useLocalStorage"; +import { usePathname } from "next/navigation"; +import Skeleton from "react-loading-skeleton"; export const Facet: React.FC = ({ name, @@ -12,9 +14,11 @@ export const Facet: React.FC = ({ facetKey, facetFilters, showIcon = true, + showSkeleton, }) => { + const pathname = usePathname(); // Get preset name from URL - const presetName = window.location.pathname.split("/").pop() || "default"; + const presetName = pathname?.split("/").pop() || "default"; // Store open/close state in localStorage with a unique key per preset and facet const [isOpen, setIsOpen] = useLocalStorage( @@ -60,7 +64,17 @@ export const Facet: React.FC = ({
)}
- {values.length > 0 ? ( + {showSkeleton ? ( + Array.from({ length: 3 }).map((_, index) => ( +
+ + +
+ )) + ) : values.length > 0 ? ( filteredValues.map((value) => ( {/* Icon logic */} diff --git a/keep-ui/app/alerts/alert-table-tab-panel.tsx b/keep-ui/app/(keep)/alerts/alert-table-tab-panel.tsx similarity index 100% rename from keep-ui/app/alerts/alert-table-tab-panel.tsx rename to keep-ui/app/(keep)/alerts/alert-table-tab-panel.tsx diff --git a/keep-ui/app/alerts/alert-table-utils.tsx b/keep-ui/app/(keep)/alerts/alert-table-utils.tsx similarity index 99% rename from keep-ui/app/alerts/alert-table-utils.tsx rename to keep-ui/app/(keep)/alerts/alert-table-utils.tsx index 0ef8bdc60..bd41cbc22 100644 --- a/keep-ui/app/alerts/alert-table-utils.tsx +++ b/keep-ui/app/(keep)/alerts/alert-table-utils.tsx @@ -192,7 +192,7 @@ export const useAlertTableCols = ( ? [ columnHelper.display({ id: "checkbox", - size: 10, + size: 46, header: (context) => ( { // if presetName is alert-history, do not open sidebar @@ -303,21 +303,12 @@ export function AlertTable({ setDynamicFacets={setDynamicFacets} onDelete={handleFacetDelete} table={table} + showSkeleton={showSkeleton} />
- {isAsyncLoading && ( - - Alerts will show up in this table as they are added to Keep... - - )} {/* For dynamic preset, add alert tabs*/} {!presetStatic && ( -
+
+ {Array(20) + .fill("") + .map((index, rowIndex) => ( + + {table.getAllColumns().map((c, cellIndex) => ( + + + + ))} + + ))} + + ); + } + return ( {table.getRowModel().rows.map((row) => { @@ -100,11 +123,7 @@ export function AlertsTableBody({ "relative z-[1]" // Ensure cell content is above the border )} > - {showSkeleton ? ( - - ) : ( - flexRender(cell.column.columnDef.cell, cell.getContext()) - )} + {flexRender(cell.column.columnDef.cell, cell.getContext())} ); })} diff --git a/keep-ui/app/alerts/alerts.tsx b/keep-ui/app/(keep)/alerts/alerts.tsx similarity index 92% rename from keep-ui/app/alerts/alerts.tsx rename to keep-ui/app/(keep)/alerts/alerts.tsx index d70c6318d..b5125c4f9 100644 --- a/keep-ui/app/alerts/alerts.tsx +++ b/keep-ui/app/(keep)/alerts/alerts.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useEffect, useMemo, useState } from "react"; import { Preset } from "./models"; import { useAlerts } from "utils/hooks/useAlerts"; @@ -9,14 +11,14 @@ import AlertNoteModal from "./alert-note-modal"; import { useProviders } from "utils/hooks/useProviders"; import { AlertDto } from "./models"; import { AlertMethodModal } from "./alert-method-modal"; -import ManualRunWorkflowModal from "@/app/workflows/manual-run-workflow-modal"; +import ManualRunWorkflowModal from "@/app/(keep)/workflows/manual-run-workflow-modal"; import AlertDismissModal from "./alert-dismiss-modal"; import { ViewAlertModal } from "./ViewAlertModal"; import { useRouter, useSearchParams } from "next/navigation"; import AlertChangeStatusModal from "./alert-change-status-modal"; import { useAlertPolling } from "utils/hooks/usePusher"; -import NotFound from "@/app/not-found"; -import NotAuthorized from "@/app/not-authorized"; +import NotFound from "@/app/(keep)/not-found"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; const defaultPresets: Preset[] = [ { @@ -107,6 +109,10 @@ export default function Alerts({ presetName }: AlertsProps) { mutate: mutateAlerts, error: alertsError, } = usePresetAlerts(selectedPreset ? selectedPreset.name : ""); + + const { status: sessionStatus } = useSession(); + const isLoading = isAsyncLoading || sessionStatus === "loading"; + useEffect(() => { const fingerprint = searchParams?.get("alertPayloadFingerprint"); if (fingerprint) { @@ -126,14 +132,6 @@ export default function Alerts({ presetName }: AlertsProps) { if (!selectedPreset) { return ; } - if (alertsError) { - if (alertsError.statusCode === 401) { - console.log("unauthenticated 401"); - window.location.href = "/signin"; - return null; - } - return ; - } return ( <> @@ -141,7 +139,7 @@ export default function Alerts({ presetName }: AlertsProps) { key={selectedPreset.name} preset={selectedPreset} alerts={alerts} - isAsyncLoading={isAsyncLoading} + isAsyncLoading={isLoading} setTicketModalAlert={setTicketModalAlert} setNoteModalAlert={setNoteModalAlert} setRunWorkflowModalAlert={setRunWorkflowModalAlert} diff --git a/keep-ui/app/(keep)/alerts/models.tsx b/keep-ui/app/(keep)/alerts/models.tsx new file mode 100644 index 000000000..0b9561781 --- /dev/null +++ b/keep-ui/app/(keep)/alerts/models.tsx @@ -0,0 +1,135 @@ +export enum Severity { + Critical = "critical", + High = "high", + Warning = "warning", + Low = "low", + Info = "info", + Error = "error", +} + +export const severityMapping: { [id: number]: string } = { + 1: Severity.Low, + 2: Severity.Info, + 3: Severity.Warning, + 4: Severity.High, + 5: Severity.Critical, +}; + +export const reverseSeverityMapping: { [id: string]: number } = { + [Severity.Low]: 1, + [Severity.Info]: 2, + [Severity.Warning]: 3, + [Severity.High]: 4, + [Severity.Critical]: 5, +}; + +export enum Status { + Firing = "firing", + Resolved = "resolved", + Acknowledged = "acknowledged", + Suppressed = "suppressed", + Pending = "pending", +} + +export interface AlertDto { + id: string; + event_id: string; + name: string; + status: Status; + lastReceived: Date; + environment: string; + isDuplicate?: boolean; + duplicateReason?: string; + service?: string; + source: string[]; + message?: string; + description?: string; + severity?: Severity; + url?: string; + pushed: boolean; + generatorURL?: string; + fingerprint: string; + deleted: boolean; + dismissed: boolean; + assignee?: string; + ticket_url: string; + ticket_status?: string; + playbook_url?: string; + providerId?: string; + group?: boolean; + note?: string; + isNoisy?: boolean; + enriched_fields: string[]; + incident?: string; + + // From AlertWithIncidentLinkMetadataDto + is_created_by_ai?: boolean; +} + +interface Option { + readonly label: string; + readonly value: string; +} + +export interface Tag { + id: number; + name: string; +} + +export interface Preset { + id: string; + name: string; + options: Option[]; + is_private: boolean; + is_noisy: boolean; + should_do_noise_now: boolean; + alerts_count: number; + created_by?: string; + tags: Tag[]; +} + +export function getTabsFromPreset(preset: Preset): any[] { + const tabsOption = preset.options.find( + (option) => option.label.toLowerCase() === "tabs" + ); + return tabsOption && Array.isArray(tabsOption.value) ? tabsOption.value : []; +} + +export interface AlertToWorkflowExecution { + workflow_id: string; + workflow_execution_id: string; + alert_fingerprint: string; + workflow_status: + | "timeout" + | "in_progress" + | "success" + | "error" + | "providers_not_configured"; + workflow_started: Date; +} + +export const AlertKnownKeys = [ + "id", + "name", + "status", + "lastReceived", + "isDuplicate", + "duplicateReason", + "source", + "description", + "severity", + "pushed", + "url", + "event_id", + "ticket_url", + "playbook_url", + "ack_status", + "deleted", + "assignee", + "providerId", + "checkbox", + "alertMenu", + "group", + "extraPayload", + "note", +]; diff --git a/keep-ui/app/dashboard/EditGridItemModal.tsx b/keep-ui/app/(keep)/dashboard/EditGridItemModal.tsx similarity index 61% rename from keep-ui/app/dashboard/EditGridItemModal.tsx rename to keep-ui/app/(keep)/dashboard/EditGridItemModal.tsx index 35dfe709e..b131e712c 100644 --- a/keep-ui/app/dashboard/EditGridItemModal.tsx +++ b/keep-ui/app/(keep)/dashboard/EditGridItemModal.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import Modal from "@/components/ui/Modal"; -import { Button } from '@tremor/react'; +import { Button } from "@tremor/react"; import { WidgetData, Threshold } from "./types"; interface EditGridItemModalProps { @@ -10,7 +10,12 @@ interface EditGridItemModalProps { onSave: (updatedItem: WidgetData) => void; } -const EditGridItemModal: React.FC = ({ isOpen, onClose, item, onSave }) => { +const EditGridItemModal: React.FC = ({ + isOpen, + onClose, + item, + onSave, +}) => { const [thresholds, setThresholds] = useState([]); useEffect(() => { @@ -29,7 +34,12 @@ const EditGridItemModal: React.FC = ({ isOpen, onClose, return ( {item && ( -
{ e.preventDefault(); handleSave(); }}> + { + e.preventDefault(); + handleSave(); + }} + >
{thresholds.map((threshold, index) => ( @@ -37,13 +47,27 @@ const EditGridItemModal: React.FC = ({ isOpen, onClose, setThresholds(thresholds.map((t, i) => i === index ? { ...t, value: parseInt(e.target.value, 10) } : t))} + onChange={(e) => + setThresholds( + thresholds.map((t, i) => + i === index + ? { ...t, value: parseInt(e.target.value, 10) } + : t + ) + ) + } className="border p-1" /> setThresholds(thresholds.map((t, i) => i === index ? { ...t, color: e.target.value } : t))} + onChange={(e) => + setThresholds( + thresholds.map((t, i) => + i === index ? { ...t, color: e.target.value } : t + ) + ) + } className="border p-1" />
diff --git a/keep-ui/app/dashboard/GridItem.tsx b/keep-ui/app/(keep)/dashboard/GridItem.tsx similarity index 63% rename from keep-ui/app/dashboard/GridItem.tsx rename to keep-ui/app/(keep)/dashboard/GridItem.tsx index 6c85e2c43..57077fd1d 100644 --- a/keep-ui/app/dashboard/GridItem.tsx +++ b/keep-ui/app/(keep)/dashboard/GridItem.tsx @@ -1,8 +1,8 @@ import React, { useState } from "react"; import { AreaChart, Card } from "@tremor/react"; import MenuButton from "./MenuButton"; -import {WidgetData, WidgetType} from "./types"; -import AlertQuality from "@/app/alerts/alert-quality-table"; +import { WidgetData, WidgetType } from "./types"; +import AlertQuality from "@/app/(keep)/alerts/alert-quality-table"; import { useSearchParams } from "next/navigation"; interface GridItemProps { @@ -55,14 +55,21 @@ const GridItem: React.FC = ({ } const getColor = () => { let color = "#000000"; - if (item.widgetType === WidgetType.PRESET && item.thresholds && item.preset) { - for (let i = item.thresholds.length - 1; i >= 0; i--) { - if (item.preset && item.preset.alerts_count >= item.thresholds[i].value) { - color = item.thresholds[i].color; - break; + if ( + item.widgetType === WidgetType.PRESET && + item.thresholds && + item.preset + ) { + for (let i = item.thresholds.length - 1; i >= 0; i--) { + if ( + item.preset && + item.preset.alerts_count >= item.thresholds[i].value + ) { + color = item.thresholds[i].color; + break; + } } } - } return color; }; @@ -82,9 +89,9 @@ const GridItem: React.FC = ({ }; return ( - +
= ({
)} - { item.metric && ( -
-
- - `${Intl.NumberFormat().format(number).toString()}` - } - startEndOnly - connectNulls - showLegend={false} - showTooltip={true} - xAxisLabel="Timestamp" - /> -
+ {item.metric && ( +
+
+ + `${Intl.NumberFormat().format(number).toString()}` + } + startEndOnly + connectNulls + showLegend={false} + showTooltip={true} + xAxisLabel="Timestamp" + />
+
+ )} - )} - - -
- -
-
-
- ); - }; +
+ +
+
+ + ); +}; - export default GridItem; +export default GridItem; diff --git a/keep-ui/app/dashboard/GridItemContainer.tsx b/keep-ui/app/(keep)/dashboard/GridItemContainer.tsx similarity index 56% rename from keep-ui/app/dashboard/GridItemContainer.tsx rename to keep-ui/app/(keep)/dashboard/GridItemContainer.tsx index 356d8a621..0f0d2d504 100644 --- a/keep-ui/app/dashboard/GridItemContainer.tsx +++ b/keep-ui/app/(keep)/dashboard/GridItemContainer.tsx @@ -9,9 +9,19 @@ interface GridItemContainerProps { onSave: (updateItem: WidgetData) => void; } -const GridItemContainer: React.FC = ({ item, onEdit, onDelete, onSave }) => { +const GridItemContainer: React.FC = ({ + item, + onEdit, + onDelete, + onSave, +}) => { return ( - onEdit(item.i)} onDelete={() => onDelete(item.i)} onSave={onSave}/> + onEdit(item.i)} + onDelete={() => onDelete(item.i)} + onSave={onSave} + /> ); }; diff --git a/keep-ui/app/dashboard/GridLayout.tsx b/keep-ui/app/(keep)/dashboard/GridLayout.tsx similarity index 89% rename from keep-ui/app/dashboard/GridLayout.tsx rename to keep-ui/app/(keep)/dashboard/GridLayout.tsx index d78df13a3..20f4e7d56 100644 --- a/keep-ui/app/dashboard/GridLayout.tsx +++ b/keep-ui/app/(keep)/dashboard/GridLayout.tsx @@ -3,8 +3,8 @@ import { Responsive, WidthProvider, Layout } from "react-grid-layout"; import GridItemContainer from "./GridItemContainer"; import { LayoutItem, WidgetData } from "./types"; import "react-grid-layout/css/styles.css"; -import { Preset } from "app/alerts/models"; -import {MetricsWidget} from "@/utils/hooks/useDashboardMetricWidgets"; +import { Preset } from "@/app/(keep)/alerts/models"; +import { MetricsWidget } from "@/utils/hooks/useDashboardMetricWidgets"; const ResponsiveGridLayout = WidthProvider(Responsive); @@ -27,7 +27,7 @@ const GridLayout: React.FC = ({ onDelete, onSave, presets, - metrics + metrics, }) => { const layouts = { lg: layout }; @@ -63,9 +63,9 @@ const GridLayout: React.FC = ({ alerts_count: preset?.alerts_count ?? 0, }; } else if (item.metric) { - const metric = metrics?.find(m => m?.id === item?.metric?.id); + const metric = metrics?.find((m) => m?.id === item?.metric?.id); if (metric) { - item.metric = {...metric} + item.metric = { ...metric }; } } return ( @@ -78,7 +78,8 @@ const GridLayout: React.FC = ({ onSave={onSave} />
- ); })} + ); + })} ); diff --git a/keep-ui/app/dashboard/MenuButton.tsx b/keep-ui/app/(keep)/dashboard/MenuButton.tsx similarity index 93% rename from keep-ui/app/dashboard/MenuButton.tsx rename to keep-ui/app/(keep)/dashboard/MenuButton.tsx index b27fb2df3..6ee7302ca 100644 --- a/keep-ui/app/dashboard/MenuButton.tsx +++ b/keep-ui/app/(keep)/dashboard/MenuButton.tsx @@ -11,7 +11,11 @@ interface MenuButtonProps { onSave?: () => void; } -const MenuButton: React.FC = ({ onEdit, onDelete, onSave }) => { +const MenuButton: React.FC = ({ + onEdit, + onDelete, + onSave, +}) => { const stopPropagation = (e: React.MouseEvent) => { e.stopPropagation(); }; @@ -20,7 +24,10 @@ const MenuButton: React.FC = ({ onEdit, onDelete, onSave }) =>
- + = ({ title, onEdit }) => { active ? "bg-slate-200" : "text-gray-900" } group flex w-full items-center rounded-md px-2 py-2 text-sm`} > - - Edit - + Edit )} diff --git a/keep-ui/app/(keep)/dashboard/WidgetModal.tsx b/keep-ui/app/(keep)/dashboard/WidgetModal.tsx new file mode 100644 index 000000000..81ed554ec --- /dev/null +++ b/keep-ui/app/(keep)/dashboard/WidgetModal.tsx @@ -0,0 +1,430 @@ +import React, { ChangeEvent, useEffect, useState } from "react"; +import Modal from "@/components/ui/Modal"; +import { + Button, + Icon, + Select, + SelectItem, + Subtitle, + TextInput, +} from "@tremor/react"; +import { Trashcan } from "components/icons"; +import { GenericsMetrics, Threshold, WidgetData, WidgetType } from "./types"; +import { Preset } from "@/app/(keep)/alerts/models"; +import { Controller, get, useForm, useWatch } from "react-hook-form"; +import { MetricsWidget } from "@/utils/hooks/useDashboardMetricWidgets"; + +interface WidgetForm { + widgetName: string; + selectedPreset: string; + thresholds: Threshold[]; + widgetType: WidgetType; + selectedMetricWidget: string; + selectedGenericMetrics: string; +} + +interface WidgetModalProps { + isOpen: boolean; + onClose: () => void; + onAddWidget: ( + name: string, + widgetType: WidgetType, + preset?: Preset, + thresholds?: Threshold[], + metric?: MetricsWidget, + genericMetrics?: GenericsMetrics + ) => void; + onEditWidget: (updatedWidget: WidgetData) => void; + presets: Preset[]; + editingItem?: WidgetData | null; + metricWidgets: MetricsWidget[]; +} + +const GENERIC_METRICS = [ + { + key: "alert_quality", + label: "Alert Quality", + widgetType: "table", + meta: { + defaultFilters: { fields: "severity" }, + }, + }, +] as GenericsMetrics[]; + +const WidgetModal: React.FC = ({ + isOpen, + onClose, + onAddWidget, + onEditWidget, + presets, + editingItem, + metricWidgets, +}) => { + const [thresholds, setThresholds] = useState([ + { value: 0, color: "#22c55e" }, // Green + { value: 20, color: "#ef4444" }, // Red + ]); + + const { + control, + handleSubmit, + setValue, + formState: { errors }, + reset, + } = useForm({ + defaultValues: { + widgetName: "", + selectedPreset: "", + thresholds: thresholds, + widgetType: WidgetType.PRESET, + selectedGenericMetrics: "", + }, + }); + + const widgetType = useWatch({ + control, + name: "widgetType", + }); + + useEffect(() => { + if (editingItem) { + setValue("widgetName", editingItem.name); + setValue("widgetType", editingItem.widgetType); + + if (editingItem.thresholds) { + setThresholds(editingItem.thresholds); + } + setValue("selectedPreset", editingItem?.preset?.id ?? ""); + setValue("selectedMetricWidget", editingItem?.metric?.id ?? ""); + setValue( + "selectedGenericMetrics", + editingItem?.genericMetrics?.key ?? "" + ); + } else { + reset({ + widgetName: "", + selectedPreset: "", + selectedMetricWidget: "", + selectedGenericMetrics: "", + thresholds: thresholds, + widgetType: WidgetType.PRESET, + }); + } + }, [editingItem, setValue, reset]); + + const handleThresholdChange = ( + index: number, + field: "value" | "color", + e: ChangeEvent + ) => { + const value = field === "value" ? e.target.value : e.target.value; + const updatedThresholds = thresholds.map((t, i) => + i === index ? { ...t, [field]: value } : t + ); + setThresholds(updatedThresholds); + }; + + const handleThresholdBlur = () => { + setThresholds((prevThresholds) => { + return prevThresholds + .map((t) => ({ + ...t, + value: parseInt(t.value.toString(), 10) || 0, + })) + .sort((a, b) => a.value - b.value); + }); + }; + + const handleAddThreshold = () => { + const maxThreshold = Math.max(...thresholds.map((t) => t.value), 0); + setThresholds([ + ...thresholds, + { value: maxThreshold + 10, color: "#000000" }, + ]); + }; + + const handleRemoveThreshold = (index: number) => { + setThresholds(thresholds.filter((_, i) => i !== index)); + }; + + const deepClone = (obj: GenericsMetrics | undefined) => { + if (!obj) { + return obj; + } + return JSON.parse(JSON.stringify(obj)) as GenericsMetrics; + }; + + const onSubmit = (data: WidgetForm) => { + const preset = presets.find((p) => p.id === data.selectedPreset); + const metric = metricWidgets.find( + (p) => p.id === data.selectedMetricWidget + ); + if (preset || data.selectedGenericMetrics) { + const formattedThresholds = thresholds.map((t) => ({ + ...t, + value: parseInt(t.value.toString(), 10) || 0, + })); + + if (editingItem) { + let updatedWidget: WidgetData = { + ...editingItem, + name: data.widgetName, + widgetType: data.widgetType || WidgetType.PRESET, // backwards compatibility + preset, + thresholds: formattedThresholds, + genericMetrics: editingItem.genericMetrics, + }; + onEditWidget(updatedWidget); + } else { + onAddWidget( + data.widgetName, + data.widgetType, + preset, + formattedThresholds, + undefined, + deepClone( + GENERIC_METRICS.find((g) => g.key === data.selectedGenericMetrics) + ) + ); + // cleanup form + setThresholds([ + { value: 0, color: "#22c55e" }, // Green + { value: 20, color: "#ef4444" }, // Red + ]); + reset({ + widgetName: "", + selectedPreset: "", + thresholds: thresholds, + selectedGenericMetrics: "", + widgetType: WidgetType.PRESET, + }); + } + onClose(); + } + if (metric) { + if (editingItem) { + const updatedWidget: WidgetData = { + ...editingItem, + name: data.widgetName, + widgetType: data.widgetType, + }; + onEditWidget(updatedWidget); + } else { + onAddWidget( + data.widgetName, + data.widgetType, + undefined, + undefined, + metric, + undefined + ); + reset({ + widgetName: "", + selectedPreset: "", + widgetType: WidgetType.PRESET, + thresholds: thresholds, + }); + } + onClose(); + } + }; + + return ( + + +
+ Widget Name + ( + + )} + /> +
+
+ Widget Type + { + return ( + + ); + }} + /> +
+ {widgetType === WidgetType.PRESET ? ( + <> +
+ Preset + ( + + )} + /> +
+
+
+ Thresholds + +
+
+ {thresholds.map((threshold, index) => ( +
+ handleThresholdChange(index, "value", e)} + onBlur={handleThresholdBlur} + placeholder="Threshold value" + required + /> + handleThresholdChange(index, "color", e)} + className="w-10 h-10 p-1 border" + required + /> + {thresholds.length > 1 && ( + + )} +
+ ))} +
+
+ + ) : widgetType === WidgetType.GENERICS_METRICS ? ( + <> +
+ Generic Metrics + ( + + )} + /> +
+ + ) : ( +
+ Widget + ( + + )} + /> +
+ )} + + +
+ ); +}; + +export default WidgetModal; diff --git a/keep-ui/app/dashboard/[id]/dashboard.tsx b/keep-ui/app/(keep)/dashboard/[id]/dashboard.tsx similarity index 82% rename from keep-ui/app/dashboard/[id]/dashboard.tsx rename to keep-ui/app/(keep)/dashboard/[id]/dashboard.tsx index 77b060624..6a7302461 100644 --- a/keep-ui/app/dashboard/[id]/dashboard.tsx +++ b/keep-ui/app/(keep)/dashboard/[id]/dashboard.tsx @@ -1,20 +1,29 @@ "use client"; -import {useParams} from "next/navigation"; -import {ChangeEvent, useEffect, useState} from "react"; +import { useParams } from "next/navigation"; +import { ChangeEvent, useEffect, useState } from "react"; import GridLayout from "../GridLayout"; import WidgetModal from "../WidgetModal"; -import {Button, Card, Icon, Subtitle, TextInput} from "@tremor/react"; -import {GenericsMetrics, LayoutItem, Threshold, WidgetData, WidgetType} from "../types"; -import {Preset} from "app/alerts/models"; -import {FiEdit2, FiSave} from "react-icons/fi"; -import {useSession} from "next-auth/react"; -import {useDashboards} from "utils/hooks/useDashboards"; -import {useApiUrl} from "utils/hooks/useConfig"; -import "./../styles.css"; -import {toast} from "react-toastify"; -import {GenericFilters} from "@/components/filters/GenericFilters"; -import {useDashboardPreset} from "utils/hooks/useDashboardPresets"; -import {MetricsWidget, useDashboardMetricWidgets} from '@/utils/hooks/useDashboardMetricWidgets'; +import { Button, Card, Icon, Subtitle, TextInput } from "@tremor/react"; +import { + GenericsMetrics, + LayoutItem, + Threshold, + WidgetData, + WidgetType, +} from "../types"; +import { Preset } from "@/app/(keep)/alerts/models"; +import { FiEdit2, FiSave } from "react-icons/fi"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; +import { useDashboards } from "utils/hooks/useDashboards"; +import { useApiUrl } from "utils/hooks/useConfig"; +import "../styles.css"; +import { toast } from "react-toastify"; +import { GenericFilters } from "@/components/filters/GenericFilters"; +import { useDashboardPreset } from "utils/hooks/useDashboardPresets"; +import { + MetricsWidget, + useDashboardMetricWidgets, +} from "@/utils/hooks/useDashboardMetricWidgets"; const DASHBOARD_FILTERS = [ { @@ -33,7 +42,7 @@ const DashboardPage = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [layout, setLayout] = useState([]); const [widgetData, setWidgetData] = useState([]); - const {widgets: allMetricWidgets} = useDashboardMetricWidgets(true); + const { widgets: allMetricWidgets } = useDashboardMetricWidgets(true); const [editingItem, setEditingItem] = useState(null); const [dashboardName, setDashboardName] = useState(decodeURIComponent(id)); const [isEditingName, setIsEditingName] = useState(false); @@ -59,7 +68,9 @@ const DashboardPage = () => { const closeModal = () => setIsModalOpen(false); const handleAddWidget = ( - name: string, widgetType: WidgetType, preset?: Preset , + name: string, + widgetType: WidgetType, + preset?: Preset, thresholds?: Threshold[], metric?: MetricsWidget, genericMetrics?: GenericsMetrics @@ -69,10 +80,25 @@ const DashboardPage = () => { i: uniqueId, x: (layout.length % 12) * 2, y: Math.floor(layout.length / 12) * 2, - w: widgetType === WidgetType.GENERICS_METRICS ? 12 : widgetType === WidgetType.METRIC ? 6 : 3, - h: widgetType === WidgetType.GENERICS_METRICS ? 20 : widgetType === WidgetType.METRIC ? 8 : 3, + w: + widgetType === WidgetType.GENERICS_METRICS + ? 12 + : widgetType === WidgetType.METRIC + ? 6 + : 3, + h: + widgetType === WidgetType.GENERICS_METRICS + ? 20 + : widgetType === WidgetType.METRIC + ? 8 + : 3, minW: widgetType === WidgetType.GENERICS_METRICS ? 10 : 2, - minH: widgetType === WidgetType.GENERICS_METRICS ? 15 : widgetType === WidgetType.METRIC ? 7 : 3, + minH: + widgetType === WidgetType.GENERICS_METRICS + ? 15 + : widgetType === WidgetType.METRIC + ? 7 + : 3, static: false, }; const newWidget: WidgetData = { @@ -82,7 +108,7 @@ const DashboardPage = () => { name, widgetType, genericMetrics, - metric + metric, }; setLayout((prevLayout) => [...prevLayout, newItem]); setWidgetData((prevData) => [...prevData, newWidget]); @@ -90,7 +116,6 @@ const DashboardPage = () => { const handleEditWidget = (id: string, update?: WidgetData) => { let itemToEdit = widgetData.find((d) => d.i === id) || null; - console.log(itemToEdit, update) if (itemToEdit && update) { setEditingItem({ ...itemToEdit, ...update }); } else { diff --git a/keep-ui/app/dashboard/[id]/page.tsx b/keep-ui/app/(keep)/dashboard/[id]/page.tsx similarity index 92% rename from keep-ui/app/dashboard/[id]/page.tsx rename to keep-ui/app/(keep)/dashboard/[id]/page.tsx index 963ca2fad..896c9192c 100644 --- a/keep-ui/app/dashboard/[id]/page.tsx +++ b/keep-ui/app/(keep)/dashboard/[id]/page.tsx @@ -6,7 +6,7 @@ type PageProps = { }; export default function Page({ params }: PageProps) { - return ; + return ; } export const metadata = { diff --git a/keep-ui/app/dashboard/styles.css b/keep-ui/app/(keep)/dashboard/styles.css similarity index 99% rename from keep-ui/app/dashboard/styles.css rename to keep-ui/app/(keep)/dashboard/styles.css index f3e0c0e55..dc1dbc126 100644 --- a/keep-ui/app/dashboard/styles.css +++ b/keep-ui/app/(keep)/dashboard/styles.css @@ -1,4 +1,3 @@ - .grid-item__widget:hover { cursor: move; } diff --git a/keep-ui/app/dashboard/types.tsx b/keep-ui/app/(keep)/dashboard/types.tsx similarity index 73% rename from keep-ui/app/dashboard/types.tsx rename to keep-ui/app/(keep)/dashboard/types.tsx index be80ac842..2de15c968 100644 --- a/keep-ui/app/dashboard/types.tsx +++ b/keep-ui/app/(keep)/dashboard/types.tsx @@ -1,5 +1,5 @@ -import {Preset} from "app/alerts/models"; -import {MetricsWidget} from "@/utils/hooks/useDashboardMetricWidgets"; +import { Preset } from "@/app/(keep)/alerts/models"; +import { MetricsWidget } from "@/utils/hooks/useDashboardMetricWidgets"; export interface LayoutItem { i: string; @@ -19,14 +19,14 @@ export interface GenericsMetrics { meta: { defaultFilters: { [key: string]: string | string[]; - }, - } + }; + }; } export enum WidgetType { - PRESET = 'PRESET', - METRIC = 'METRIC', - GENERICS_METRICS = 'GENERICS_METRICS' + PRESET = "PRESET", + METRIC = "METRIC", + GENERICS_METRICS = "GENERICS_METRICS", } export interface WidgetData extends LayoutItem { diff --git a/keep-ui/app/deduplication/DeduplicationPlaceholder.tsx b/keep-ui/app/(keep)/deduplication/DeduplicationPlaceholder.tsx similarity index 100% rename from keep-ui/app/deduplication/DeduplicationPlaceholder.tsx rename to keep-ui/app/(keep)/deduplication/DeduplicationPlaceholder.tsx diff --git a/keep-ui/app/(keep)/deduplication/DeduplicationSankey.tsx b/keep-ui/app/(keep)/deduplication/DeduplicationSankey.tsx new file mode 100644 index 000000000..e9cb967d2 --- /dev/null +++ b/keep-ui/app/(keep)/deduplication/DeduplicationSankey.tsx @@ -0,0 +1,357 @@ +import { SVGProps } from "react"; + +export const DeduplicationSankey = (props: SVGProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/keep-ui/app/deduplication/DeduplicationSidebar.tsx b/keep-ui/app/(keep)/deduplication/DeduplicationSidebar.tsx similarity index 99% rename from keep-ui/app/deduplication/DeduplicationSidebar.tsx rename to keep-ui/app/(keep)/deduplication/DeduplicationSidebar.tsx index 0a3bf8943..99669c327 100644 --- a/keep-ui/app/deduplication/DeduplicationSidebar.tsx +++ b/keep-ui/app/(keep)/deduplication/DeduplicationSidebar.tsx @@ -13,7 +13,7 @@ import { Card, } from "@tremor/react"; import { IoMdClose } from "react-icons/io"; -import { DeduplicationRule } from "app/deduplication/models"; +import { DeduplicationRule } from "@/app/(keep)/deduplication/models"; import { useProviders } from "utils/hooks/useProviders"; import { useDeduplicationFields } from "utils/hooks/useDeduplicationRules"; import { GroupBase } from "react-select"; @@ -24,7 +24,7 @@ import { InformationCircleIcon, } from "@heroicons/react/24/outline"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { KeyedMutator } from "swr"; interface ProviderOption { diff --git a/keep-ui/app/deduplication/DeduplicationTable.tsx b/keep-ui/app/(keep)/deduplication/DeduplicationTable.tsx similarity index 97% rename from keep-ui/app/deduplication/DeduplicationTable.tsx rename to keep-ui/app/(keep)/deduplication/DeduplicationTable.tsx index 05d634451..d122e28cc 100644 --- a/keep-ui/app/deduplication/DeduplicationTable.tsx +++ b/keep-ui/app/(keep)/deduplication/DeduplicationTable.tsx @@ -20,12 +20,12 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table"; -import { DeduplicationRule } from "app/deduplication/models"; -import DeduplicationSidebar from "app/deduplication/DeduplicationSidebar"; +import { DeduplicationRule } from "@/app/(keep)/deduplication/models"; +import DeduplicationSidebar from "@/app/(keep)/deduplication/DeduplicationSidebar"; import { TrashIcon, PauseIcon, PlusIcon } from "@heroicons/react/24/outline"; import Image from "next/image"; import { useApiUrl } from "utils/hooks/useConfig"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; const columnHelper = createColumnHelper(); diff --git a/keep-ui/app/(keep)/deduplication/client.tsx b/keep-ui/app/(keep)/deduplication/client.tsx new file mode 100644 index 000000000..2e909e405 --- /dev/null +++ b/keep-ui/app/(keep)/deduplication/client.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { useDeduplicationRules } from "utils/hooks/useDeduplicationRules"; +import { DeduplicationPlaceholder } from "./DeduplicationPlaceholder"; +import { DeduplicationTable } from "./DeduplicationTable"; +import Loading from "@/app/(keep)/loading"; + +export const Client = () => { + const { + data: deduplicationRules = [], + isLoading, + mutate: mutateDeduplicationRules, + } = useDeduplicationRules(); + + if (isLoading) { + return ; + } + + if (deduplicationRules.length === 0) { + return ; + } + + return ( + + ); +}; diff --git a/keep-ui/app/deduplication/models.tsx b/keep-ui/app/(keep)/deduplication/models.tsx similarity index 100% rename from keep-ui/app/deduplication/models.tsx rename to keep-ui/app/(keep)/deduplication/models.tsx diff --git a/keep-ui/app/deduplication/page.tsx b/keep-ui/app/(keep)/deduplication/page.tsx similarity index 100% rename from keep-ui/app/deduplication/page.tsx rename to keep-ui/app/(keep)/deduplication/page.tsx diff --git a/keep-ui/app/error.css b/keep-ui/app/(keep)/error.css similarity index 100% rename from keep-ui/app/error.css rename to keep-ui/app/(keep)/error.css diff --git a/keep-ui/app/error.tsx b/keep-ui/app/(keep)/error.tsx similarity index 100% rename from keep-ui/app/error.tsx rename to keep-ui/app/(keep)/error.tsx diff --git a/keep-ui/app/extraction/create-or-update-extraction-rule.tsx b/keep-ui/app/(keep)/extraction/create-or-update-extraction-rule.tsx similarity index 98% rename from keep-ui/app/extraction/create-or-update-extraction-rule.tsx rename to keep-ui/app/(keep)/extraction/create-or-update-extraction-rule.tsx index a770f68c9..ca50f4b4d 100644 --- a/keep-ui/app/extraction/create-or-update-extraction-rule.tsx +++ b/keep-ui/app/(keep)/extraction/create-or-update-extraction-rule.tsx @@ -13,14 +13,14 @@ import { Switch, Badge, } from "@tremor/react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { FormEvent, useEffect, useState } from "react"; import { toast } from "react-toastify"; import { useApiUrl } from "utils/hooks/useConfig"; import { ExtractionRule } from "./model"; import { extractNamedGroups } from "./extractions-table"; import { useExtractions } from "utils/hooks/useExtractionRules"; -import { AlertsRulesBuilder } from "app/alerts/alerts-rules-builder"; +import { AlertsRulesBuilder } from "@/app/(keep)/alerts/alerts-rules-builder"; interface Props { extractionToEdit: ExtractionRule | null; diff --git a/keep-ui/app/extraction/extraction.tsx b/keep-ui/app/(keep)/extraction/extraction.tsx similarity index 97% rename from keep-ui/app/extraction/extraction.tsx rename to keep-ui/app/(keep)/extraction/extraction.tsx index 1e4544e6d..07ad49115 100644 --- a/keep-ui/app/extraction/extraction.tsx +++ b/keep-ui/app/(keep)/extraction/extraction.tsx @@ -3,7 +3,7 @@ import { Badge, Callout, Card } from "@tremor/react"; import CreateOrUpdateExtractionRule from "./create-or-update-extraction-rule"; import ExtractionsTable from "./extractions-table"; import { useExtractions } from "utils/hooks/useExtractionRules"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { MdWarning } from "react-icons/md"; import { useState } from "react"; import { ExtractionRule } from "./model"; diff --git a/keep-ui/app/extraction/extractions-table.tsx b/keep-ui/app/(keep)/extraction/extractions-table.tsx similarity index 98% rename from keep-ui/app/extraction/extractions-table.tsx rename to keep-ui/app/(keep)/extraction/extractions-table.tsx index 48fcb7f51..7b621b05b 100644 --- a/keep-ui/app/extraction/extractions-table.tsx +++ b/keep-ui/app/(keep)/extraction/extractions-table.tsx @@ -18,7 +18,7 @@ import { useReactTable, } from "@tanstack/react-table"; import { MdRemoveCircle, MdModeEdit } from "react-icons/md"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useApiUrl } from "utils/hooks/useConfig"; import { useMappings } from "utils/hooks/useMappingRules"; import { toast } from "react-toastify"; diff --git a/keep-ui/app/extraction/layout.tsx b/keep-ui/app/(keep)/extraction/layout.tsx similarity index 70% rename from keep-ui/app/extraction/layout.tsx rename to keep-ui/app/(keep)/extraction/layout.tsx index f063b205f..5e930dc8d 100644 --- a/keep-ui/app/extraction/layout.tsx +++ b/keep-ui/app/(keep)/extraction/layout.tsx @@ -4,7 +4,9 @@ export default function Layout({ children }: { children: any }) { return (
Extractions - Easily extract more attributes from your alerts using Regex + + Easily extract more attributes from your alerts using Regex + {children}
diff --git a/keep-ui/app/extraction/model.ts b/keep-ui/app/(keep)/extraction/model.ts similarity index 100% rename from keep-ui/app/extraction/model.ts rename to keep-ui/app/(keep)/extraction/model.ts diff --git a/keep-ui/app/extraction/page.tsx b/keep-ui/app/(keep)/extraction/page.tsx similarity index 100% rename from keep-ui/app/extraction/page.tsx rename to keep-ui/app/(keep)/extraction/page.tsx diff --git a/keep-ui/app/global-error.tsx b/keep-ui/app/(keep)/global-error.tsx similarity index 85% rename from keep-ui/app/global-error.tsx rename to keep-ui/app/(keep)/global-error.tsx index 9bda5feef..9388e06e0 100644 --- a/keep-ui/app/global-error.tsx +++ b/keep-ui/app/(keep)/global-error.tsx @@ -4,7 +4,11 @@ import * as Sentry from "@sentry/nextjs"; import NextError from "next/error"; import { useEffect } from "react"; -export default function GlobalError({ error }: { error: Error & { digest?: string } }) { +export default function GlobalError({ + error, +}: { + error: Error & { digest?: string }; +}) { useEffect(() => { Sentry.captureException(error); }, [error]); @@ -20,4 +24,4 @@ export default function GlobalError({ error }: { error: Error & { digest?: strin ); -} \ No newline at end of file +} diff --git a/keep-ui/app/incidents/[id]/activity/incident-activity.css b/keep-ui/app/(keep)/incidents/[id]/activity/incident-activity.css similarity index 100% rename from keep-ui/app/incidents/[id]/activity/incident-activity.css rename to keep-ui/app/(keep)/incidents/[id]/activity/incident-activity.css diff --git a/keep-ui/app/incidents/[id]/activity/incident-activity.tsx b/keep-ui/app/(keep)/incidents/[id]/activity/incident-activity.tsx similarity index 96% rename from keep-ui/app/incidents/[id]/activity/incident-activity.tsx rename to keep-ui/app/(keep)/incidents/[id]/activity/incident-activity.tsx index 3b6ea0787..0c803d477 100644 --- a/keep-ui/app/incidents/[id]/activity/incident-activity.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/activity/incident-activity.tsx @@ -1,6 +1,6 @@ "use client"; -import { AlertDto } from "@/app/alerts/models"; +import { AlertDto } from "@/app/(keep)/alerts/models"; import { IncidentDto } from "@/entities/incidents/model"; import { useUsers } from "@/utils/hooks/useUsers"; import Image from "next/image"; @@ -11,7 +11,7 @@ import { usePollIncidentComments, } from "@/utils/hooks/useIncidents"; import { useAlerts } from "@/utils/hooks/useAlerts"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { IncidentActivityItem } from "./ui/IncidentActivityItem"; import { IncidentActivityComment } from "./ui/IncidentActivityComment"; import { useMemo } from "react"; @@ -125,7 +125,7 @@ export function IncidentActivity({ incident }: { incident: IncidentDto }) { id: "newcomment", type: "newcomment", timestamp: new Date().toISOString(), - initiator: session?.user.email, + initiator: session?.user.email ?? "", }; const renderIcon = (activity: IncidentActivity) => { diff --git a/keep-ui/app/incidents/[id]/activity/page.tsx b/keep-ui/app/(keep)/incidents/[id]/activity/page.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/activity/page.tsx rename to keep-ui/app/(keep)/incidents/[id]/activity/page.tsx diff --git a/keep-ui/app/incidents/[id]/activity/ui/IncidentActivityComment.tsx b/keep-ui/app/(keep)/incidents/[id]/activity/ui/IncidentActivityComment.tsx similarity index 96% rename from keep-ui/app/incidents/[id]/activity/ui/IncidentActivityComment.tsx rename to keep-ui/app/(keep)/incidents/[id]/activity/ui/IncidentActivityComment.tsx index 2cd8f0b77..d015b02e9 100644 --- a/keep-ui/app/incidents/[id]/activity/ui/IncidentActivityComment.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/activity/ui/IncidentActivityComment.tsx @@ -2,7 +2,7 @@ import { IncidentDto } from "@/entities/incidents/model"; import { AuditEvent } from "@/utils/hooks/useAlerts"; import { useApiUrl } from "@/utils/hooks/useConfig"; import { TextInput, Button } from "@tremor/react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useState, useCallback, useEffect } from "react"; import { toast } from "react-toastify"; import { KeyedMutator } from "swr"; diff --git a/keep-ui/app/incidents/[id]/activity/ui/IncidentActivityItem.tsx b/keep-ui/app/(keep)/incidents/[id]/activity/ui/IncidentActivityItem.tsx similarity index 90% rename from keep-ui/app/incidents/[id]/activity/ui/IncidentActivityItem.tsx rename to keep-ui/app/(keep)/incidents/[id]/activity/ui/IncidentActivityItem.tsx index e17c570eb..84ccdde64 100644 --- a/keep-ui/app/incidents/[id]/activity/ui/IncidentActivityItem.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/activity/ui/IncidentActivityItem.tsx @@ -1,5 +1,5 @@ -import AlertSeverity from "@/app/alerts/alert-severity"; -import { AlertDto } from "@/app/alerts/models"; +import AlertSeverity from "@/app/(keep)/alerts/alert-severity"; +import { AlertDto } from "@/app/(keep)/alerts/models"; import TimeAgo from "react-timeago"; export function IncidentActivityItem({ activity }: { activity: any }) { diff --git a/keep-ui/app/incidents/[id]/alerts/incident-alert-menu.tsx b/keep-ui/app/(keep)/incidents/[id]/alerts/incident-alert-menu.tsx similarity index 91% rename from keep-ui/app/incidents/[id]/alerts/incident-alert-menu.tsx rename to keep-ui/app/(keep)/incidents/[id]/alerts/incident-alert-menu.tsx index 03d7c615e..799afbc28 100644 --- a/keep-ui/app/incidents/[id]/alerts/incident-alert-menu.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/alerts/incident-alert-menu.tsx @@ -1,6 +1,6 @@ import { Icon } from "@tremor/react"; -import { AlertDto } from "app/alerts/models"; -import { useSession } from "next-auth/react"; +import { AlertDto } from "@/app/(keep)/alerts/models"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { toast } from "react-toastify"; import { useApiUrl } from "utils/hooks/useConfig"; import { useIncidentAlerts } from "utils/hooks/useIncidents"; diff --git a/keep-ui/app/incidents/[id]/alerts/incident-alerts.tsx b/keep-ui/app/(keep)/incidents/[id]/alerts/incident-alerts.tsx similarity index 97% rename from keep-ui/app/incidents/[id]/alerts/incident-alerts.tsx rename to keep-ui/app/(keep)/incidents/[id]/alerts/incident-alerts.tsx index 4d0b45942..7d3d828e8 100644 --- a/keep-ui/app/incidents/[id]/alerts/incident-alerts.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/alerts/incident-alerts.tsx @@ -16,8 +16,8 @@ import { TableRow, } from "@tremor/react"; import Image from "next/image"; -import AlertSeverity from "app/alerts/alert-severity"; -import { AlertDto } from "app/alerts/models"; +import AlertSeverity from "@/app/(keep)/alerts/alert-severity"; +import { AlertDto } from "@/app/(keep)/alerts/models"; import Skeleton from "react-loading-skeleton"; import "react-loading-skeleton/dist/skeleton.css"; import { getAlertLastReceieved } from "utils/helpers"; @@ -25,12 +25,11 @@ import { useIncidentAlerts, usePollIncidentAlerts, } from "utils/hooks/useIncidents"; -import AlertName from "app/alerts/alert-name"; +import AlertName from "@/app/(keep)/alerts/alert-name"; import IncidentAlertMenu from "./incident-alert-menu"; import React, { useEffect, useMemo, useState } from "react"; import type { IncidentDto } from "@/entities/incidents/model"; import { getCommonPinningStylesAndClassNames } from "@/components/ui/table/utils"; -// import AlertTableCheckbox from "@/app/alerts/alert-table-checkbox"; import { EmptyStateCard } from "@/components/ui"; import { useRouter } from "next/navigation"; import { TablePagination } from "@/shared/ui"; diff --git a/keep-ui/app/incidents/[id]/alerts/page.tsx b/keep-ui/app/(keep)/incidents/[id]/alerts/page.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/alerts/page.tsx rename to keep-ui/app/(keep)/incidents/[id]/alerts/page.tsx diff --git a/keep-ui/app/(keep)/incidents/[id]/chat/incident-chat.css b/keep-ui/app/(keep)/incidents/[id]/chat/incident-chat.css new file mode 100644 index 000000000..dcd13cd66 --- /dev/null +++ b/keep-ui/app/(keep)/incidents/[id]/chat/incident-chat.css @@ -0,0 +1,21 @@ +.copilotKitInput { + @apply sticky bottom-0 bg-white !important; + @apply flex items-center outline-none rounded-tremor-default px-3 py-2 mx-2 text-tremor-default focus:ring-2 transition duration-100 border shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted dark:shadow-dark-tremor-input focus:dark:border-dark-tremor-brand-subtle focus:dark:ring-dark-tremor-brand-muted bg-tremor-background dark:bg-dark-tremor-background hover:bg-tremor-background-muted dark:hover:bg-dark-tremor-background-muted text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis border-tremor-border dark:border-dark-tremor-border placeholder:text-tremor-content dark:placeholder:text-dark-tremor-content !important; +} + +.copilotKitInput:hover textarea { + @apply bg-tremor-background-muted dark:bg-dark-tremor-background-muted transition duration-100 !important; +} + +.copilotKitInput textarea { + height: unset !important; + max-height: unset !important; +} + +.copilotKitMessages { + @apply px-4 !important; +} + +.copilotKitMessages .suggestion { + @apply bg-white text-black scale-100 border-tremor-border border-2 hover:border-tremor-brand hover:text-tremor-brand hover:bg-tremor-brand-muted dark:border-dark-tremor-brand dark:hover:bg-dark-tremor-brand-muted transition !important; +} diff --git a/keep-ui/app/incidents/[id]/chat/incident-chat.tsx b/keep-ui/app/(keep)/incidents/[id]/chat/incident-chat.tsx similarity index 98% rename from keep-ui/app/incidents/[id]/chat/incident-chat.tsx rename to keep-ui/app/(keep)/incidents/[id]/chat/incident-chat.tsx index b3909e250..cace4b0c1 100644 --- a/keep-ui/app/incidents/[id]/chat/incident-chat.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/chat/incident-chat.tsx @@ -7,7 +7,7 @@ import type { IncidentDto } from "@/entities/incidents/model"; import { useIncidentAlerts } from "utils/hooks/useIncidents"; import { EmptyStateCard } from "@/components/ui/EmptyStateCard"; import { useRouter } from "next/navigation"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { useCopilotAction, useCopilotReadable } from "@copilotkit/react-core"; import { Card } from "@tremor/react"; import { useIncidentActions } from "@/entities/incidents/model"; diff --git a/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx b/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx new file mode 100644 index 000000000..e718b90a8 --- /dev/null +++ b/keep-ui/app/(keep)/incidents/[id]/chat/page.client.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { IncidentChat } from "./incident-chat"; +import { IncidentDto } from "@/entities/incidents/model"; +import { EmptyStateCard } from "@/shared/ui"; +import { useConfig } from "@/utils/hooks/useConfig"; +import { CopilotKit } from "@copilotkit/react-core"; +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; + +export function IncidentChatClientPage({ + incident, +}: { + incident: IncidentDto; +}) { + const { data: config } = useConfig(); + if (config && !config.OPEN_AI_API_KEY_SET) { + return ( + + ); + } + + return ( + + + + ); +} diff --git a/keep-ui/app/incidents/[id]/chat/page.tsx b/keep-ui/app/(keep)/incidents/[id]/chat/page.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/chat/page.tsx rename to keep-ui/app/(keep)/incidents/[id]/chat/page.tsx diff --git a/keep-ui/app/incidents/[id]/getIncidentWithErrorHandling.tsx b/keep-ui/app/(keep)/incidents/[id]/getIncidentWithErrorHandling.tsx similarity index 81% rename from keep-ui/app/incidents/[id]/getIncidentWithErrorHandling.tsx rename to keep-ui/app/(keep)/incidents/[id]/getIncidentWithErrorHandling.tsx index a7a262cca..b67b19590 100644 --- a/keep-ui/app/incidents/[id]/getIncidentWithErrorHandling.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/getIncidentWithErrorHandling.tsx @@ -1,8 +1,6 @@ import { getIncident } from "@/entities/incidents/api/incidents"; -import { getServerSession } from "next-auth"; import { getApiURL } from "@/utils/apiUrl"; -import { authOptions } from "@/pages/api/auth/[...nextauth]"; - +import { auth } from "@/auth"; import { notFound } from "next/navigation"; import { KeepApiError } from "@/shared/lib/KeepApiError"; import { IncidentDto } from "@/entities/incidents/model"; @@ -13,7 +11,7 @@ export async function getIncidentWithErrorHandling( // @ts-ignore ignoring since not found will be handled by nextjs ): Promise { try { - const session = await getServerSession(authOptions); + const session = await auth(); const apiUrl = getApiURL(); const incident = await getIncident(apiUrl, session, id); return incident; diff --git a/keep-ui/app/incidents/[id]/incident-header-skeleton.tsx b/keep-ui/app/(keep)/incidents/[id]/incident-header-skeleton.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/incident-header-skeleton.tsx rename to keep-ui/app/(keep)/incidents/[id]/incident-header-skeleton.tsx diff --git a/keep-ui/app/incidents/[id]/incident-header.tsx b/keep-ui/app/(keep)/incidents/[id]/incident-header.tsx similarity index 98% rename from keep-ui/app/incidents/[id]/incident-header.tsx rename to keep-ui/app/(keep)/incidents/[id]/incident-header.tsx index 1562dc2ac..d6ff1a10b 100644 --- a/keep-ui/app/incidents/[id]/incident-header.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/incident-header.tsx @@ -10,7 +10,7 @@ import { ArrowRightIcon } from "@heroicons/react/16/solid"; import { MdBlock, MdDone, MdModeEdit, MdPlayArrow } from "react-icons/md"; import React, { useState } from "react"; import { useRouter } from "next/navigation"; -import ManualRunWorkflowModal from "@/app/workflows/manual-run-workflow-modal"; +import ManualRunWorkflowModal from "@/app/(keep)/workflows/manual-run-workflow-modal"; import { CreateOrUpdateIncidentForm } from "@/features/create-or-update-incident"; import Modal from "@/components/ui/Modal"; import { IncidentSeverityBadge } from "@/entities/incidents/ui"; diff --git a/keep-ui/app/incidents/[id]/incident-overview.tsx b/keep-ui/app/(keep)/incidents/[id]/incident-overview.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/incident-overview.tsx rename to keep-ui/app/(keep)/incidents/[id]/incident-overview.tsx diff --git a/keep-ui/app/incidents/[id]/incident-tabs-navigation.tsx b/keep-ui/app/(keep)/incidents/[id]/incident-tabs-navigation.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/incident-tabs-navigation.tsx rename to keep-ui/app/(keep)/incidents/[id]/incident-tabs-navigation.tsx diff --git a/keep-ui/app/incidents/[id]/layout.tsx b/keep-ui/app/(keep)/incidents/[id]/layout.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/layout.tsx rename to keep-ui/app/(keep)/incidents/[id]/layout.tsx diff --git a/keep-ui/app/incidents/[id]/not-found.tsx b/keep-ui/app/(keep)/incidents/[id]/not-found.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/not-found.tsx rename to keep-ui/app/(keep)/incidents/[id]/not-found.tsx diff --git a/keep-ui/app/incidents/[id]/route.tsx b/keep-ui/app/(keep)/incidents/[id]/route.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/route.tsx rename to keep-ui/app/(keep)/incidents/[id]/route.tsx diff --git a/keep-ui/app/incidents/[id]/timeline/incident-timeline.tsx b/keep-ui/app/(keep)/incidents/[id]/timeline/incident-timeline.tsx similarity index 98% rename from keep-ui/app/incidents/[id]/timeline/incident-timeline.tsx rename to keep-ui/app/(keep)/incidents/[id]/timeline/incident-timeline.tsx index da442b104..525387b19 100644 --- a/keep-ui/app/incidents/[id]/timeline/incident-timeline.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/timeline/incident-timeline.tsx @@ -1,13 +1,13 @@ "use client"; -import Loading from "@/app/loading"; +import Loading from "@/app/(keep)/loading"; import { EmptyStateCard } from "@/components/ui/EmptyStateCard"; import type { IncidentDto } from "@/entities/incidents/model"; import { AuditEvent, useAlerts } from "@/utils/hooks/useAlerts"; import { useIncidentAlerts } from "@/utils/hooks/useIncidents"; import { Card } from "@tremor/react"; -import AlertSeverity from "app/alerts/alert-severity"; -import { AlertDto } from "app/alerts/models"; +import AlertSeverity from "@/app/(keep)/alerts/alert-severity"; +import { AlertDto } from "@/app/(keep)/alerts/models"; import { format, parseISO } from "date-fns"; import Image from "next/image"; import { useRouter } from "next/navigation"; diff --git a/keep-ui/app/incidents/[id]/timeline/page.tsx b/keep-ui/app/(keep)/incidents/[id]/timeline/page.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/timeline/page.tsx rename to keep-ui/app/(keep)/incidents/[id]/timeline/page.tsx diff --git a/keep-ui/app/incidents/[id]/topology/page.tsx b/keep-ui/app/(keep)/incidents/[id]/topology/page.tsx similarity index 86% rename from keep-ui/app/incidents/[id]/topology/page.tsx rename to keep-ui/app/(keep)/incidents/[id]/topology/page.tsx index 264ffe497..2d617199d 100644 --- a/keep-ui/app/incidents/[id]/topology/page.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/topology/page.tsx @@ -1,5 +1,5 @@ -import { TopologySearchProvider } from "@/app/topology/TopologySearchContext"; -import { TopologyMap } from "@/app/topology/ui/map"; +import { TopologySearchProvider } from "@/app/(keep)/topology/TopologySearchContext"; +import { TopologyMap } from "@/app/(keep)/topology/ui/map"; import { getIncidentWithErrorHandling } from "../getIncidentWithErrorHandling"; import { getIncidentName } from "@/entities/incidents/lib/utils"; diff --git a/keep-ui/app/incidents/[id]/workflows/incident-workflow-empty.tsx b/keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-empty.tsx similarity index 91% rename from keep-ui/app/incidents/[id]/workflows/incident-workflow-empty.tsx rename to keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-empty.tsx index 7ff9e31a2..8c5b48615 100644 --- a/keep-ui/app/incidents/[id]/workflows/incident-workflow-empty.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-empty.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { EmptyStateCard } from "@/components/ui"; -import ManualRunWorkflowModal from "@/app/workflows/manual-run-workflow-modal"; +import ManualRunWorkflowModal from "@/app/(keep)/workflows/manual-run-workflow-modal"; import type { IncidentDto } from "@/entities/incidents/model"; export function IncidentWorkflowsEmptyState({ diff --git a/keep-ui/app/incidents/[id]/workflows/incident-workflow-sidebar.tsx b/keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-sidebar.tsx similarity index 96% rename from keep-ui/app/incidents/[id]/workflows/incident-workflow-sidebar.tsx rename to keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-sidebar.tsx index 2f028fa8d..de1907f43 100644 --- a/keep-ui/app/incidents/[id]/workflows/incident-workflow-sidebar.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-sidebar.tsx @@ -2,12 +2,12 @@ import { Fragment } from "react"; import { Dialog, Transition } from "@headlessui/react"; import { Text, Button, TextInput, Badge, Title, Card } from "@tremor/react"; import { IoMdClose } from "react-icons/io"; -import { WorkflowExecution } from "app/workflows/builder/types"; +import { WorkflowExecution } from "@/app/(keep)/workflows/builder/types"; import { getIcon, getTriggerIcon, extractTriggerValue, -} from "app/workflows/[workflow_id]/workflow-execution-table"; +} from "@/app/(keep)/workflows/[workflow_id]/workflow-execution-table"; import { useWorkflowExecution } from "utils/hooks/useWorkflowExecutions"; interface IncidentWorkflowSidebarProps { @@ -60,8 +60,8 @@ const IncidentWorkflowSidebar: React.FC = ({ selectedExecution.status === "error" ? "red" : selectedExecution.status === "success" - ? "green" - : "orange" + ? "green" + : "orange" } > {selectedExecution.status} diff --git a/keep-ui/app/incidents/[id]/workflows/incident-workflow-table.tsx b/keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-table.tsx similarity index 98% rename from keep-ui/app/incidents/[id]/workflows/incident-workflow-table.tsx rename to keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-table.tsx index 83d4ccf0b..3143adf65 100644 --- a/keep-ui/app/incidents/[id]/workflows/incident-workflow-table.tsx +++ b/keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-table.tsx @@ -25,8 +25,8 @@ import { extractTriggerValue, getIcon, getTriggerIcon, -} from "app/workflows/[workflow_id]/workflow-execution-table"; -import { WorkflowExecution } from "app/workflows/builder/types"; +} from "@/app/(keep)/workflows/[workflow_id]/workflow-execution-table"; +import { WorkflowExecution } from "@/app/(keep)/workflows/builder/types"; import { useEffect, useState } from "react"; import Skeleton from "react-loading-skeleton"; import "react-loading-skeleton/dist/skeleton.css"; diff --git a/keep-ui/app/incidents/[id]/workflows/page.tsx b/keep-ui/app/(keep)/incidents/[id]/workflows/page.tsx similarity index 100% rename from keep-ui/app/incidents/[id]/workflows/page.tsx rename to keep-ui/app/(keep)/incidents/[id]/workflows/page.tsx diff --git a/keep-ui/app/incidents/layout.tsx b/keep-ui/app/(keep)/incidents/layout.tsx similarity index 60% rename from keep-ui/app/incidents/layout.tsx rename to keep-ui/app/(keep)/incidents/layout.tsx index be1b63b46..68c3ab1c5 100644 --- a/keep-ui/app/incidents/layout.tsx +++ b/keep-ui/app/(keep)/incidents/layout.tsx @@ -1,5 +1,5 @@ "use client"; -import { IncidentFilterContextProvider } from "../../features/incident-list/ui/incident-table-filters-context"; +import { IncidentFilterContextProvider } from "../../../features/incident-list/ui/incident-table-filters-context"; export default function Layout({ children }: { children: any }) { return ( diff --git a/keep-ui/app/incidents/page.tsx b/keep-ui/app/(keep)/incidents/page.tsx similarity index 83% rename from keep-ui/app/incidents/page.tsx rename to keep-ui/app/(keep)/incidents/page.tsx index 79420d9c2..d44f334d8 100644 --- a/keep-ui/app/incidents/page.tsx +++ b/keep-ui/app/(keep)/incidents/page.tsx @@ -1,6 +1,5 @@ -import { getServerSession } from "next-auth/next"; import { getApiURL } from "@/utils/apiUrl"; -import { authOptions } from "@/pages/api/auth/[...nextauth]"; +import { auth } from "@/auth"; import { IncidentList } from "@/features/incident-list"; import { getIncidents, @@ -19,7 +18,7 @@ const defaultIncidentsParams: GetIncidentsParams = { export default async function Page() { let incidents: PaginatedIncidentsDto | null = null; try { - const session = await getServerSession(authOptions); + const session = await auth(); const apiUrl = getApiURL(); incidents = await getIncidents(apiUrl, session, defaultIncidentsParams); diff --git a/keep-ui/app/incidents/predicted-incidents-table.tsx b/keep-ui/app/(keep)/incidents/predicted-incidents-table.tsx similarity index 100% rename from keep-ui/app/incidents/predicted-incidents-table.tsx rename to keep-ui/app/(keep)/incidents/predicted-incidents-table.tsx diff --git a/keep-ui/app/layout.tsx b/keep-ui/app/(keep)/layout.tsx similarity index 81% rename from keep-ui/app/layout.tsx rename to keep-ui/app/(keep)/layout.tsx index d7b0f90ab..a27212031 100644 --- a/keep-ui/app/layout.tsx +++ b/keep-ui/app/(keep)/layout.tsx @@ -1,17 +1,18 @@ import { ReactNode } from "react"; -import { NextAuthProvider } from "./auth-provider"; +import { NextAuthProvider } from "../auth-provider"; import { Mulish } from "next/font/google"; import { ToastContainer } from "react-toastify"; import Navbar from "components/navbar/Navbar"; -import { TopologyPollingContextProvider } from "@/app/topology/model/TopologyPollingContext"; -import { FrigadeProvider } from "./frigade-provider"; +import { TopologyPollingContextProvider } from "@/app/(keep)/topology/model/TopologyPollingContext"; +import { FrigadeProvider } from "../frigade-provider"; import { getConfig } from "@/shared/lib/server/getConfig"; -import { ConfigProvider } from "./config-provider"; -import "./globals.css"; -import "react-toastify/dist/ReactToastify.css"; -import { PHProvider } from "./posthog-provider"; +import { ConfigProvider } from "../config-provider"; +import { PHProvider } from "../posthog-provider"; import dynamic from "next/dynamic"; -import ReadOnlyBanner from "./read-only-banner"; +import ReadOnlyBanner from "../read-only-banner"; +import { auth } from "@/auth"; +import "@/app/globals.css"; +import "react-toastify/dist/ReactToastify.css"; const PostHogPageView = dynamic(() => import("@/shared/ui/PostHogPageView"), { ssr: false, @@ -29,12 +30,14 @@ type RootLayoutProps = { export default async function RootLayout({ children }: RootLayoutProps) { const config = getConfig(); + const session = await auth(); + return ( - + {/* @ts-ignore-error Server Component */} diff --git a/keep-ui/app/loading.tsx b/keep-ui/app/(keep)/loading.tsx similarity index 100% rename from keep-ui/app/loading.tsx rename to keep-ui/app/(keep)/loading.tsx diff --git a/keep-ui/app/maintenance/create-or-update-maintenance-rule.tsx b/keep-ui/app/(keep)/maintenance/create-or-update-maintenance-rule.tsx similarity index 97% rename from keep-ui/app/maintenance/create-or-update-maintenance-rule.tsx rename to keep-ui/app/(keep)/maintenance/create-or-update-maintenance-rule.tsx index fc50ca66d..6c46152cf 100644 --- a/keep-ui/app/maintenance/create-or-update-maintenance-rule.tsx +++ b/keep-ui/app/(keep)/maintenance/create-or-update-maintenance-rule.tsx @@ -10,13 +10,13 @@ import { Select, SelectItem, } from "@tremor/react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { FormEvent, useEffect, useState } from "react"; import { toast } from "react-toastify"; import { useApiUrl } from "utils/hooks/useConfig"; import { MaintenanceRule } from "./model"; import { useMaintenanceRules } from "utils/hooks/useMaintenanceRules"; -import { AlertsRulesBuilder } from "app/alerts/alerts-rules-builder"; +import { AlertsRulesBuilder } from "@/app/(keep)/alerts/alerts-rules-builder"; import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { useRouter } from "next/navigation"; diff --git a/keep-ui/app/maintenance/layout.tsx b/keep-ui/app/(keep)/maintenance/layout.tsx similarity index 100% rename from keep-ui/app/maintenance/layout.tsx rename to keep-ui/app/(keep)/maintenance/layout.tsx diff --git a/keep-ui/app/maintenance/maintenance-rules-table.tsx b/keep-ui/app/(keep)/maintenance/maintenance-rules-table.tsx similarity index 98% rename from keep-ui/app/maintenance/maintenance-rules-table.tsx rename to keep-ui/app/(keep)/maintenance/maintenance-rules-table.tsx index 819caa4c6..790082e83 100644 --- a/keep-ui/app/maintenance/maintenance-rules-table.tsx +++ b/keep-ui/app/(keep)/maintenance/maintenance-rules-table.tsx @@ -17,7 +17,7 @@ import { useReactTable, } from "@tanstack/react-table"; import { MdRemoveCircle, MdModeEdit } from "react-icons/md"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { toast } from "react-toastify"; import { useApiUrl } from "utils/hooks/useConfig"; import { MaintenanceRule } from "./model"; diff --git a/keep-ui/app/maintenance/maintenance.tsx b/keep-ui/app/(keep)/maintenance/maintenance.tsx similarity index 97% rename from keep-ui/app/maintenance/maintenance.tsx rename to keep-ui/app/(keep)/maintenance/maintenance.tsx index b5c92b542..e012a19d0 100644 --- a/keep-ui/app/maintenance/maintenance.tsx +++ b/keep-ui/app/(keep)/maintenance/maintenance.tsx @@ -1,7 +1,7 @@ "use client"; import { Badge, Callout, Card } from "@tremor/react"; import { useMaintenanceRules } from "utils/hooks/useMaintenanceRules"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { MdWarning } from "react-icons/md"; import { useState } from "react"; import { MaintenanceRule } from "./model"; diff --git a/keep-ui/app/maintenance/model.ts b/keep-ui/app/(keep)/maintenance/model.ts similarity index 100% rename from keep-ui/app/maintenance/model.ts rename to keep-ui/app/(keep)/maintenance/model.ts diff --git a/keep-ui/app/maintenance/page.tsx b/keep-ui/app/(keep)/maintenance/page.tsx similarity index 100% rename from keep-ui/app/maintenance/page.tsx rename to keep-ui/app/(keep)/maintenance/page.tsx diff --git a/keep-ui/app/mapping/create-or-edit-mapping.tsx b/keep-ui/app/(keep)/mapping/create-or-edit-mapping.tsx similarity index 98% rename from keep-ui/app/mapping/create-or-edit-mapping.tsx rename to keep-ui/app/(keep)/mapping/create-or-edit-mapping.tsx index 0145999fa..c526d49bd 100644 --- a/keep-ui/app/mapping/create-or-edit-mapping.tsx +++ b/keep-ui/app/(keep)/mapping/create-or-edit-mapping.tsx @@ -17,7 +17,7 @@ import { TabPanels, TabPanel, } from "@tremor/react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { ChangeEvent, FormEvent, @@ -32,7 +32,7 @@ import { useApiUrl } from "utils/hooks/useConfig"; import { useMappings } from "utils/hooks/useMappingRules"; import { MappingRule } from "./models"; import { CreateableSearchSelect } from "@/components/ui/CreateableSearchSelect"; -import { useTopology } from "@/app/topology/model"; +import { useTopology } from "@/app/(keep)/topology/model"; interface Props { editRule: MappingRule | null; diff --git a/keep-ui/app/mapping/layout.tsx b/keep-ui/app/(keep)/mapping/layout.tsx similarity index 100% rename from keep-ui/app/mapping/layout.tsx rename to keep-ui/app/(keep)/mapping/layout.tsx diff --git a/keep-ui/app/mapping/mapping.tsx b/keep-ui/app/(keep)/mapping/mapping.tsx similarity index 97% rename from keep-ui/app/mapping/mapping.tsx rename to keep-ui/app/(keep)/mapping/mapping.tsx index dadb1509b..d38eec395 100644 --- a/keep-ui/app/mapping/mapping.tsx +++ b/keep-ui/app/(keep)/mapping/mapping.tsx @@ -4,7 +4,7 @@ import CreateOrEditMapping from "./create-or-edit-mapping"; import { useMappings } from "utils/hooks/useMappingRules"; import RulesTable from "./rules-table"; import { MdWarning } from "react-icons/md"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { MappingRule } from "./models"; import { useState } from "react"; diff --git a/keep-ui/app/mapping/models.tsx b/keep-ui/app/(keep)/mapping/models.tsx similarity index 100% rename from keep-ui/app/mapping/models.tsx rename to keep-ui/app/(keep)/mapping/models.tsx diff --git a/keep-ui/app/mapping/page.tsx b/keep-ui/app/(keep)/mapping/page.tsx similarity index 100% rename from keep-ui/app/mapping/page.tsx rename to keep-ui/app/(keep)/mapping/page.tsx diff --git a/keep-ui/app/mapping/rules-table.tsx b/keep-ui/app/(keep)/mapping/rules-table.tsx similarity index 98% rename from keep-ui/app/mapping/rules-table.tsx rename to keep-ui/app/(keep)/mapping/rules-table.tsx index 476ec90f3..dc80a5326 100644 --- a/keep-ui/app/mapping/rules-table.tsx +++ b/keep-ui/app/(keep)/mapping/rules-table.tsx @@ -18,7 +18,7 @@ import { ExpandedState, } from "@tanstack/react-table"; import { MdRemoveCircle, MdModeEdit } from "react-icons/md"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useApiUrl } from "utils/hooks/useConfig"; import { useMappings } from "utils/hooks/useMappingRules"; import { toast } from "react-toastify"; diff --git a/keep-ui/app/not-found.tsx b/keep-ui/app/(keep)/not-found.tsx similarity index 100% rename from keep-ui/app/not-found.tsx rename to keep-ui/app/(keep)/not-found.tsx diff --git a/keep-ui/app/notifications-hub/layout.tsx b/keep-ui/app/(keep)/notifications-hub/layout.tsx similarity index 100% rename from keep-ui/app/notifications-hub/layout.tsx rename to keep-ui/app/(keep)/notifications-hub/layout.tsx diff --git a/keep-ui/app/notifications-hub/page.tsx b/keep-ui/app/(keep)/notifications-hub/page.tsx similarity index 100% rename from keep-ui/app/notifications-hub/page.tsx rename to keep-ui/app/(keep)/notifications-hub/page.tsx diff --git a/keep-ui/app/page.tsx b/keep-ui/app/(keep)/page.tsx similarity index 100% rename from keep-ui/app/page.tsx rename to keep-ui/app/(keep)/page.tsx diff --git a/keep-ui/app/(keep)/providers/components/providers-filter-by-label/index.ts b/keep-ui/app/(keep)/providers/components/providers-filter-by-label/index.ts new file mode 100644 index 000000000..03e00556e --- /dev/null +++ b/keep-ui/app/(keep)/providers/components/providers-filter-by-label/index.ts @@ -0,0 +1 @@ +export { ProvidersFilterByLabel } from "./providers-filter-by-label"; diff --git a/keep-ui/app/providers/components/providers-filter-by-label/providers-filter-by-label.tsx b/keep-ui/app/(keep)/providers/components/providers-filter-by-label/providers-filter-by-label.tsx similarity index 73% rename from keep-ui/app/providers/components/providers-filter-by-label/providers-filter-by-label.tsx rename to keep-ui/app/(keep)/providers/components/providers-filter-by-label/providers-filter-by-label.tsx index 4a2d2edd6..09f696c8e 100644 --- a/keep-ui/app/providers/components/providers-filter-by-label/providers-filter-by-label.tsx +++ b/keep-ui/app/(keep)/providers/components/providers-filter-by-label/providers-filter-by-label.tsx @@ -5,11 +5,12 @@ import { useFilterContext, PROVIDER_LABELS } from "../../filter-context"; import type { TProviderLabels } from "../../providers"; export const ProvidersFilterByLabel: FC = (props) => { - const { setProvidersSelectedTags, providersSelectedTags } = useFilterContext(); + const { setProvidersSelectedTags, providersSelectedTags } = + useFilterContext(); const headerSelect = (value: string[]) => { - setProvidersSelectedTags(value as TProviderLabels[]) - } + setProvidersSelectedTags(value as TProviderLabels[]); + }; const options = Object.entries(PROVIDER_LABELS); @@ -22,8 +23,10 @@ export const ProvidersFilterByLabel: FC = (props) => { icon={TagIcon} > {options.map(([value, label]) => ( - {label} + + {label} + ))} - ) -} \ No newline at end of file + ); +}; diff --git a/keep-ui/app/(keep)/providers/components/providers-search/index.ts b/keep-ui/app/(keep)/providers/components/providers-search/index.ts new file mode 100644 index 000000000..8abce6070 --- /dev/null +++ b/keep-ui/app/(keep)/providers/components/providers-search/index.ts @@ -0,0 +1 @@ +export { ProvidersSearch } from "./providers-search"; diff --git a/keep-ui/app/providers/components/providers-search/providers-search.tsx b/keep-ui/app/(keep)/providers/components/providers-search/providers-search.tsx similarity index 76% rename from keep-ui/app/providers/components/providers-search/providers-search.tsx rename to keep-ui/app/(keep)/providers/components/providers-search/providers-search.tsx index 680a65896..5d4965038 100644 --- a/keep-ui/app/providers/components/providers-search/providers-search.tsx +++ b/keep-ui/app/(keep)/providers/components/providers-search/providers-search.tsx @@ -1,14 +1,15 @@ import { FC, ChangeEvent } from "react"; import { TextInput } from "@tremor/react"; import { MagnifyingGlassIcon } from "@heroicons/react/20/solid"; -import {useFilterContext} from "../../filter-context"; +import { useFilterContext } from "../../filter-context"; export const ProvidersSearch: FC = () => { - const { providersSearchString, setProvidersSearchString } = useFilterContext(); + const { providersSearchString, setProvidersSearchString } = + useFilterContext(); const handleChange = (e: ChangeEvent) => { setProvidersSearchString(e.target.value); - } + }; return ( { value={providersSearchString} onChange={handleChange} /> - ) -} \ No newline at end of file + ); +}; diff --git a/keep-ui/app/providers/context.ts b/keep-ui/app/(keep)/providers/context.ts similarity index 100% rename from keep-ui/app/providers/context.ts rename to keep-ui/app/(keep)/providers/context.ts diff --git a/keep-ui/app/providers/datadog-alert.tsx b/keep-ui/app/(keep)/providers/datadog-alert.tsx similarity index 97% rename from keep-ui/app/providers/datadog-alert.tsx rename to keep-ui/app/(keep)/providers/datadog-alert.tsx index 862f6f1ee..925e25842 100644 --- a/keep-ui/app/providers/datadog-alert.tsx +++ b/keep-ui/app/(keep)/providers/datadog-alert.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from "react"; type DatadogAlertProps = { alert: { diff --git a/keep-ui/app/providers/filter-context/constants.ts b/keep-ui/app/(keep)/providers/filter-context/constants.ts similarity index 53% rename from keep-ui/app/providers/filter-context/constants.ts rename to keep-ui/app/(keep)/providers/filter-context/constants.ts index f6d967eb9..c8f4b5203 100644 --- a/keep-ui/app/providers/filter-context/constants.ts +++ b/keep-ui/app/(keep)/providers/filter-context/constants.ts @@ -1,13 +1,13 @@ import { TProviderLabels } from "../providers"; export const PROVIDER_LABELS: Record = { - alert: 'Alert', - topology: 'Topology', - messaging: 'Messaging', - ticketing: 'Ticketing', - data: 'Data', - queue: 'Queue', - incident: 'Incident' -} + alert: "Alert", + topology: "Topology", + messaging: "Messaging", + ticketing: "Ticketing", + data: "Data", + queue: "Queue", + incident: "Incident", +}; -export const PROVIDER_LABELS_KEYS = Object.keys(PROVIDER_LABELS); \ No newline at end of file +export const PROVIDER_LABELS_KEYS = Object.keys(PROVIDER_LABELS); diff --git a/keep-ui/app/providers/filter-context/filter-context.tsx b/keep-ui/app/(keep)/providers/filter-context/filter-context.tsx similarity index 68% rename from keep-ui/app/providers/filter-context/filter-context.tsx rename to keep-ui/app/(keep)/providers/filter-context/filter-context.tsx index 9886587d9..1e2866721 100644 --- a/keep-ui/app/providers/filter-context/filter-context.tsx +++ b/keep-ui/app/(keep)/providers/filter-context/filter-context.tsx @@ -9,11 +9,16 @@ export const FilterContext = createContext(null); export const FilerContextProvider: FC = ({ children }) => { const searchParams = useSearchParams(); - const [providersSearchString, setProvidersSearchString] = useState(""); + const [providersSearchString, setProvidersSearchString] = + useState(""); - const [providersSelectedTags, setProvidersSelectedTags] = useState(() => { + const [providersSelectedTags, setProvidersSelectedTags] = useState< + TProviderLabels[] + >(() => { const labels = searchParams?.get("labels"); - const labelArray = labels?.split(',').filter(label => PROVIDER_LABELS_KEYS.includes(label)); + const labelArray = labels + ?.split(",") + .filter((label) => PROVIDER_LABELS_KEYS.includes(label)); return (labelArray || []) as TProviderLabels[]; }); @@ -23,7 +28,11 @@ export const FilerContextProvider: FC = ({ children }) => { providersSelectedTags, setProvidersSelectedTags, setProvidersSearchString, - } + }; - return {children} -} \ No newline at end of file + return ( + + {children} + + ); +}; diff --git a/keep-ui/app/(keep)/providers/filter-context/index.ts b/keep-ui/app/(keep)/providers/filter-context/index.ts new file mode 100644 index 000000000..49e19f78e --- /dev/null +++ b/keep-ui/app/(keep)/providers/filter-context/index.ts @@ -0,0 +1,3 @@ +export { FilerContextProvider } from "./filter-context"; +export { useFilterContext } from "./use-filter-context"; +export * from "./constants"; diff --git a/keep-ui/app/providers/filter-context/types.ts b/keep-ui/app/(keep)/providers/filter-context/types.ts similarity index 86% rename from keep-ui/app/providers/filter-context/types.ts rename to keep-ui/app/(keep)/providers/filter-context/types.ts index d77c6dc11..c56f163b6 100644 --- a/keep-ui/app/providers/filter-context/types.ts +++ b/keep-ui/app/(keep)/providers/filter-context/types.ts @@ -3,7 +3,7 @@ import { TProviderLabels } from "../providers"; export interface IFilterContext { providersSearchString: string; - providersSelectedTags: TProviderLabels[], + providersSelectedTags: TProviderLabels[]; setProvidersSearchString: Dispatch>; setProvidersSelectedTags: Dispatch>; -} \ No newline at end of file +} diff --git a/keep-ui/app/(keep)/providers/filter-context/use-filter-context.ts b/keep-ui/app/(keep)/providers/filter-context/use-filter-context.ts new file mode 100644 index 000000000..df2c0925a --- /dev/null +++ b/keep-ui/app/(keep)/providers/filter-context/use-filter-context.ts @@ -0,0 +1,15 @@ +import { useContext } from "react"; +import { FilterContext } from "./filter-context"; +import { IFilterContext } from "./types"; + +export const useFilterContext = (): IFilterContext => { + const filterContext = useContext(FilterContext); + + if (!filterContext) { + throw new ReferenceError( + "Usage of useFilterContext outside of FilterContext provider is forbidden" + ); + } + + return filterContext; +}; diff --git a/keep-ui/app/providers/grafana-alert.tsx b/keep-ui/app/(keep)/providers/grafana-alert.tsx similarity index 97% rename from keep-ui/app/providers/grafana-alert.tsx rename to keep-ui/app/(keep)/providers/grafana-alert.tsx index b4be3f9bb..a8cf16d29 100644 --- a/keep-ui/app/providers/grafana-alert.tsx +++ b/keep-ui/app/(keep)/providers/grafana-alert.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from "react"; type GrafanaAlertProps = { alert: { diff --git a/keep-ui/app/providers/layout.tsx b/keep-ui/app/(keep)/providers/layout.tsx similarity index 100% rename from keep-ui/app/providers/layout.tsx rename to keep-ui/app/(keep)/providers/layout.tsx diff --git a/keep-ui/app/providers/oauth2/[providerType]/page.tsx b/keep-ui/app/(keep)/providers/oauth2/[providerType]/page.tsx similarity index 89% rename from keep-ui/app/providers/oauth2/[providerType]/page.tsx rename to keep-ui/app/(keep)/providers/oauth2/[providerType]/page.tsx index d980e37d5..52f504f63 100644 --- a/keep-ui/app/providers/oauth2/[providerType]/page.tsx +++ b/keep-ui/app/(keep)/providers/oauth2/[providerType]/page.tsx @@ -1,5 +1,4 @@ -import { getServerSession } from "next-auth/next"; -import { authOptions } from "pages/api/auth/[...nextauth]"; +import { auth } from "@/auth"; import { getApiURL } from "@/utils/apiUrl"; import { redirect } from "next/navigation"; import { cookies } from "next/headers"; @@ -11,7 +10,7 @@ export default async function InstallFromOAuth({ params: { providerType: string }; searchParams: { [key: string]: string }; }) { - const accessToken = await getServerSession(authOptions); + const accessToken = await auth(); // this is server so we can use the old getApiURL const apiUrl = getApiURL(); const cookieStore = cookies(); diff --git a/keep-ui/app/providers/page.client.tsx b/keep-ui/app/(keep)/providers/page.client.tsx similarity index 97% rename from keep-ui/app/providers/page.client.tsx rename to keep-ui/app/(keep)/providers/page.client.tsx index b44cdf109..fd49f8ff9 100644 --- a/keep-ui/app/providers/page.client.tsx +++ b/keep-ui/app/(keep)/providers/page.client.tsx @@ -1,11 +1,11 @@ "use client"; import { defaultProvider, Provider } from "./providers"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { KeepApiError } from "@/shared/lib/KeepApiError"; import { useApiUrl } from "utils/hooks/useConfig"; import ProvidersTiles from "./providers-tiles"; import React, { useState, useEffect } from "react"; -import Loading from "../loading"; +import Loading from "@/app/(keep)/loading"; import { useFilterContext } from "./filter-context"; import { toast } from "react-toastify"; import { useRouter } from "next/navigation"; diff --git a/keep-ui/app/providers/page.tsx b/keep-ui/app/(keep)/providers/page.tsx similarity index 100% rename from keep-ui/app/providers/page.tsx rename to keep-ui/app/(keep)/providers/page.tsx diff --git a/keep-ui/app/providers/provider-form-scopes.css b/keep-ui/app/(keep)/providers/provider-form-scopes.css similarity index 100% rename from keep-ui/app/providers/provider-form-scopes.css rename to keep-ui/app/(keep)/providers/provider-form-scopes.css diff --git a/keep-ui/app/providers/provider-form-scopes.tsx b/keep-ui/app/(keep)/providers/provider-form-scopes.tsx similarity index 92% rename from keep-ui/app/providers/provider-form-scopes.tsx rename to keep-ui/app/(keep)/providers/provider-form-scopes.tsx index 6820872b4..8457eec0d 100644 --- a/keep-ui/app/providers/provider-form-scopes.tsx +++ b/keep-ui/app/(keep)/providers/provider-form-scopes.tsx @@ -63,11 +63,13 @@ const ProviderFormScopes = ({ { // provider.scopes! is because we validates scopes exists in the parent component provider.scopes!.map((scope) => { - let isScopeString = typeof validatedScopes[scope.name] === 'string'; + let isScopeString = + typeof validatedScopes[scope.name] === "string"; let isScopeLong = false; if (isScopeString) { - isScopeLong = validatedScopes[scope.name].toString().length > 100; + isScopeLong = + validatedScopes[scope.name].toString().length > 100; } return ( @@ -89,7 +91,9 @@ const ProviderFormScopes = ({ ? "gray" : "red" // scope was tested and is a string, meaning it has an error } - className={`truncate ${isScopeLong? 'max-w-lg' : 'max-w-xs' }`} + className={`truncate ${ + isScopeLong ? "max-w-lg" : "max-w-xs" + }`} > {validatedScopes[scope.name] === true ? "Valid" diff --git a/keep-ui/app/providers/provider-form.css b/keep-ui/app/(keep)/providers/provider-form.css similarity index 100% rename from keep-ui/app/providers/provider-form.css rename to keep-ui/app/(keep)/providers/provider-form.css diff --git a/keep-ui/app/providers/provider-form.tsx b/keep-ui/app/(keep)/providers/provider-form.tsx similarity index 99% rename from keep-ui/app/providers/provider-form.tsx rename to keep-ui/app/(keep)/providers/provider-form.tsx index 80e519a7d..7e58e8a25 100644 --- a/keep-ui/app/providers/provider-form.tsx +++ b/keep-ui/app/(keep)/providers/provider-form.tsx @@ -2,7 +2,7 @@ // There's also a lot of s**t in here, but it works for now 🤷‍♂️ // @ts-nocheck import React, { useEffect, useState, useRef, useCallback } from "react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { Provider } from "./providers"; import { useApiUrl } from "utils/hooks/useConfig"; import Image from "next/image"; @@ -43,7 +43,7 @@ import { PlusIcon, TrashIcon, } from "@heroicons/react/24/outline"; -import { installWebhook } from "../../utils/helpers"; +import { installWebhook } from "../../../utils/helpers"; import { ProviderSemiAutomated } from "./provider-semi-automated"; import ProviderFormScopes from "./provider-form-scopes"; import Link from "next/link"; diff --git a/keep-ui/app/providers/provider-row.css b/keep-ui/app/(keep)/providers/provider-row.css similarity index 100% rename from keep-ui/app/providers/provider-row.css rename to keep-ui/app/(keep)/providers/provider-row.css diff --git a/keep-ui/app/providers/provider-row.tsx b/keep-ui/app/(keep)/providers/provider-row.tsx similarity index 85% rename from keep-ui/app/providers/provider-row.tsx rename to keep-ui/app/(keep)/providers/provider-row.tsx index 396439730..d5298dcdf 100644 --- a/keep-ui/app/providers/provider-row.tsx +++ b/keep-ui/app/(keep)/providers/provider-row.tsx @@ -1,4 +1,4 @@ -'use client'; +"use client"; import React, { useState, useEffect } from "react"; import { TableRow, TableCell } from "@tremor/react"; import Image from "next/image"; @@ -6,7 +6,6 @@ import "./provider-row.css"; import { Provider } from "./providers"; const ProviderRow = ({ provider }: { provider: Provider }) => { - return ( <> @@ -27,11 +26,13 @@ const ProviderRow = ({ provider }: { provider: Provider }) => {
{provider.details.name!}
- {Object.entries(provider.details.authentication).map(([key, value]) => ( -
- {key}: {value} -
- ))} + {Object.entries(provider.details.authentication).map( + ([key, value]) => ( +
+ {key}: {value} +
+ ) + )}
diff --git a/keep-ui/app/providers/provider-semi-automated.tsx b/keep-ui/app/(keep)/providers/provider-semi-automated.tsx similarity index 100% rename from keep-ui/app/providers/provider-semi-automated.tsx rename to keep-ui/app/(keep)/providers/provider-semi-automated.tsx diff --git a/keep-ui/app/providers/provider-tile.css b/keep-ui/app/(keep)/providers/provider-tile.css similarity index 100% rename from keep-ui/app/providers/provider-tile.css rename to keep-ui/app/(keep)/providers/provider-tile.css diff --git a/keep-ui/app/providers/provider-tile.tsx b/keep-ui/app/(keep)/providers/provider-tile.tsx similarity index 100% rename from keep-ui/app/providers/provider-tile.tsx rename to keep-ui/app/(keep)/providers/provider-tile.tsx diff --git a/keep-ui/app/providers/providers-tiles.tsx b/keep-ui/app/(keep)/providers/providers-tiles.tsx similarity index 100% rename from keep-ui/app/providers/providers-tiles.tsx rename to keep-ui/app/(keep)/providers/providers-tiles.tsx diff --git a/keep-ui/app/providers/providers.css b/keep-ui/app/(keep)/providers/providers.css similarity index 100% rename from keep-ui/app/providers/providers.css rename to keep-ui/app/(keep)/providers/providers.css diff --git a/keep-ui/app/providers/providers.tsx b/keep-ui/app/(keep)/providers/providers.tsx similarity index 100% rename from keep-ui/app/providers/providers.tsx rename to keep-ui/app/(keep)/providers/providers.tsx diff --git a/keep-ui/app/providers/table.tsx b/keep-ui/app/(keep)/providers/table.tsx similarity index 58% rename from keep-ui/app/providers/table.tsx rename to keep-ui/app/(keep)/providers/table.tsx index 6f13f50d9..44d137344 100644 --- a/keep-ui/app/providers/table.tsx +++ b/keep-ui/app/(keep)/providers/table.tsx @@ -3,22 +3,19 @@ import { Table } from "@tremor/react"; import ProviderRow from "./provider-row"; import { Providers } from "./providers"; - export default function ProvidersTable({ providers, }: { providers: Providers; }) { - return ( - { - providers.filter((provider) => Object.keys(provider.details).length > 0) - .map((provider) => ( - - )) - } + {providers + .filter((provider) => Object.keys(provider.details).length > 0) + .map((provider) => ( + + ))}
); diff --git a/keep-ui/app/rules/CorrelationPlaceholder.tsx b/keep-ui/app/(keep)/rules/CorrelationPlaceholder.tsx similarity index 100% rename from keep-ui/app/rules/CorrelationPlaceholder.tsx rename to keep-ui/app/(keep)/rules/CorrelationPlaceholder.tsx diff --git a/keep-ui/app/rules/CorrelationSidebar/AlertsFoundBadge.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/AlertsFoundBadge.tsx similarity index 82% rename from keep-ui/app/rules/CorrelationSidebar/AlertsFoundBadge.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/AlertsFoundBadge.tsx index 407c56c54..49b5ad3a1 100644 --- a/keep-ui/app/rules/CorrelationSidebar/AlertsFoundBadge.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/AlertsFoundBadge.tsx @@ -1,6 +1,6 @@ import { Badge } from "@tremor/react"; import Image from "next/image"; -import { AlertDto } from "app/alerts/models"; +import { AlertDto } from "@/app/(keep)/alerts/models"; type AlertsFoundBadgeProps = { alertsFound: AlertDto[]; @@ -30,7 +30,11 @@ export const AlertsFoundBadge = ({ return ( - + {images.map((source, index) => ( } - {alertsFound.length} alert{alertsFound.length > 1 ? "s" : ""} were found{vertical &&
}matching this condition + {alertsFound.length} alert{alertsFound.length > 1 ? "s" : ""} were + found{vertical &&
}matching this condition
diff --git a/keep-ui/app/rules/CorrelationSidebar/CorrelationForm.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationForm.tsx similarity index 99% rename from keep-ui/app/rules/CorrelationSidebar/CorrelationForm.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationForm.tsx index ddb91d15a..9e46cff4d 100644 --- a/keep-ui/app/rules/CorrelationSidebar/CorrelationForm.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationForm.tsx @@ -11,7 +11,7 @@ import { } from "@tremor/react"; import { Controller, get, useFormContext } from "react-hook-form"; import { CorrelationForm as CorrelationFormType } from "."; -import { AlertDto } from "app/alerts/models"; +import { AlertDto } from "@/app/(keep)/alerts/models"; import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline"; import React from "react"; diff --git a/keep-ui/app/rules/CorrelationSidebar/CorrelationGroups.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationGroups.tsx similarity index 96% rename from keep-ui/app/rules/CorrelationSidebar/CorrelationGroups.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationGroups.tsx index 69c295e5d..cfc82cdd7 100644 --- a/keep-ui/app/rules/CorrelationSidebar/CorrelationGroups.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationGroups.tsx @@ -19,7 +19,7 @@ export const CorrelationGroups = () => { className="cursor-default" type="button" tooltip="Any Rule consists of one or more Correlations. Each alert group is evaluated separately and the results are combined using AND combinator. - For example, if you want to group alerts that has a severity of 'critical' and another alert with a source of 'Kibana', you would create a rule with two alert groups. + For example, if you want to group alerts that has a severity of 'critical' and another alert with a source of 'Kibana', you would create a rule with two alert groups. The first alert group would have a rule with severity = 'critical' and the second alert group would have a rule with source = 'kibana'." icon={QuestionMarkCircleIcon} size="xs" diff --git a/keep-ui/app/rules/CorrelationSidebar/CorrelationSidebarBody.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSidebarBody.tsx similarity index 98% rename from keep-ui/app/rules/CorrelationSidebar/CorrelationSidebarBody.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSidebarBody.tsx index ffb225c04..29d5d8fa2 100644 --- a/keep-ui/app/rules/CorrelationSidebar/CorrelationSidebarBody.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSidebarBody.tsx @@ -7,7 +7,7 @@ import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; import { CorrelationForm } from "./CorrelationForm"; import { CorrelationGroups } from "./CorrelationGroups"; import { CorrelationSubmission } from "./CorrelationSubmission"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { useRules } from "utils/hooks/useRules"; import { CorrelationForm as CorrelationFormType } from "."; import { useRouter, useSearchParams } from "next/navigation"; diff --git a/keep-ui/app/rules/CorrelationSidebar/CorrelationSidebarHeader.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSidebarHeader.tsx similarity index 100% rename from keep-ui/app/rules/CorrelationSidebar/CorrelationSidebarHeader.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSidebarHeader.tsx diff --git a/keep-ui/app/rules/CorrelationSidebar/CorrelationSubmission.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSubmission.tsx similarity index 95% rename from keep-ui/app/rules/CorrelationSidebar/CorrelationSubmission.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSubmission.tsx index 1c1605692..a707080a8 100644 --- a/keep-ui/app/rules/CorrelationSidebar/CorrelationSubmission.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSubmission.tsx @@ -3,7 +3,7 @@ import { useSearchParams } from "next/navigation"; import { useFormContext } from "react-hook-form"; import { CorrelationForm } from "."; import { AlertsFoundBadge } from "./AlertsFoundBadge"; -import { AlertDto } from "app/alerts/models"; +import { AlertDto } from "@/app/(keep)/alerts/models"; type CorrelationSubmissionProps = { toggle: VoidFunction; diff --git a/keep-ui/app/rules/CorrelationSidebar/DeleteRule.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/DeleteRule.tsx similarity index 93% rename from keep-ui/app/rules/CorrelationSidebar/DeleteRule.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/DeleteRule.tsx index feabbeb1d..992a2151c 100644 --- a/keep-ui/app/rules/CorrelationSidebar/DeleteRule.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/DeleteRule.tsx @@ -1,6 +1,6 @@ import { TrashIcon } from "@radix-ui/react-icons"; import { Button } from "@tremor/react"; -import { useSession } from "next-auth/react"; +import { useHydratedSession as useSession } from "@/shared/lib/hooks/useHydratedSession"; import { MouseEvent } from "react"; import { useApiUrl } from "utils/hooks/useConfig"; import { useRules } from "utils/hooks/useRules"; diff --git a/keep-ui/app/rules/CorrelationSidebar/RuleFields.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/RuleFields.tsx similarity index 97% rename from keep-ui/app/rules/CorrelationSidebar/RuleFields.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/RuleFields.tsx index 6c51072aa..af2d8cce3 100644 --- a/keep-ui/app/rules/CorrelationSidebar/RuleFields.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationSidebar/RuleFields.tsx @@ -236,9 +236,9 @@ export const RuleFields = ({ }; const { watch } = useFormContext(); - const timeframeInSeconds = watch("timeUnit") ? TIMEFRAME_UNITS_TO_SECONDS[watch("timeUnit")]( - +watch("timeAmount") - ) : 0; + const timeframeInSeconds = watch("timeUnit") + ? TIMEFRAME_UNITS_TO_SECONDS[watch("timeUnit")](+watch("timeAmount")) + : 0; const { data: alertsFound = [], isLoading } = useSearchAlerts({ query: { combinator: "and", rules: ruleFields }, @@ -260,7 +260,9 @@ export const RuleFields = ({ onRemoveRuleFieldClick(ruleFieldIndex)} + onRemoveFieldClick={() => + onRemoveRuleFieldClick(ruleFieldIndex) + } // add the rule field as an available selection avaliableFields={availableFields.concat({ label: ruleField.field, diff --git a/keep-ui/app/rules/CorrelationSidebar/RuleGroup.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/RuleGroup.tsx similarity index 100% rename from keep-ui/app/rules/CorrelationSidebar/RuleGroup.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/RuleGroup.tsx diff --git a/keep-ui/app/rules/CorrelationSidebar/index.tsx b/keep-ui/app/(keep)/rules/CorrelationSidebar/index.tsx similarity index 100% rename from keep-ui/app/rules/CorrelationSidebar/index.tsx rename to keep-ui/app/(keep)/rules/CorrelationSidebar/index.tsx diff --git a/keep-ui/app/rules/CorrelationTable.tsx b/keep-ui/app/(keep)/rules/CorrelationTable.tsx similarity index 90% rename from keep-ui/app/rules/CorrelationTable.tsx rename to keep-ui/app/(keep)/rules/CorrelationTable.tsx index b3dfb81d0..90f33d0e9 100644 --- a/keep-ui/app/rules/CorrelationTable.tsx +++ b/keep-ui/app/(keep)/rules/CorrelationTable.tsx @@ -1,7 +1,8 @@ import { Badge, Button, - Card, Icon, + Card, + Icon, Subtitle, Table, TableBody, @@ -28,14 +29,13 @@ import { DefaultRuleGroupType, parseCEL } from "react-querybuilder"; import { useRouter, useSearchParams } from "next/navigation"; import { FormattedQueryCell } from "./FormattedQueryCell"; import { DeleteRuleCell } from "./CorrelationSidebar/DeleteRule"; -import {PlusIcon} from "@radix-ui/react-icons"; +import { PlusIcon } from "@radix-ui/react-icons"; - -const TIMEFRAME_UNITS_FROM_SECONDS= { +const TIMEFRAME_UNITS_FROM_SECONDS = { seconds: (amount: number) => amount, minutes: (amount: number) => amount / 60, hours: (amount: number) => amount / 3600, - days: (amount: number) => amount / 86400, + days: (amount: number) => amount / 86400, } as const; const columnHelper = createColumnHelper(); @@ -72,7 +72,9 @@ export const CorrelationTable = ({ rules }: CorrelationTableProps) => { return { name: selectedRule.name, description: selectedRule.group_description ?? "", - timeAmount: TIMEFRAME_UNITS_FROM_SECONDS[timeunit](selectedRule.timeframe), + timeAmount: TIMEFRAME_UNITS_FROM_SECONDS[timeunit]( + selectedRule.timeframe + ), timeUnit: timeunit, groupedAttributes: selectedRule.grouping_criteria, requireApprove: selectedRule.require_approve, @@ -117,22 +119,21 @@ export const CorrelationTable = ({ rules }: CorrelationTableProps) => { }), columnHelper.accessor("grouping_criteria", { header: "Grouped by", - cell: (context) => ( - context.getValue().map((group, index) => + cell: (context) => + context.getValue().map((group, index) => ( <> - {group} + + {group} + {context.getValue().length !== index + 1 && ( - - )} + + )} - ) - ), + )), }), columnHelper.accessor("incidents", { header: "Incidents", - cell: (context) => ( - context.getValue() - ), + cell: (context) => context.getValue(), }), columnHelper.display({ id: "menu", diff --git a/keep-ui/app/rules/FormattedQueryCell.tsx b/keep-ui/app/(keep)/rules/FormattedQueryCell.tsx similarity index 100% rename from keep-ui/app/rules/FormattedQueryCell.tsx rename to keep-ui/app/(keep)/rules/FormattedQueryCell.tsx diff --git a/keep-ui/app/rules/client.tsx b/keep-ui/app/(keep)/rules/client.tsx similarity index 90% rename from keep-ui/app/rules/client.tsx rename to keep-ui/app/(keep)/rules/client.tsx index ab52b1cc3..a63fcda66 100644 --- a/keep-ui/app/rules/client.tsx +++ b/keep-ui/app/(keep)/rules/client.tsx @@ -3,7 +3,7 @@ import { useRules } from "utils/hooks/useRules"; import { CorrelationPlaceholder } from "./CorrelationPlaceholder"; import { CorrelationTable } from "./CorrelationTable"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; export const Client = () => { const { data: rules = [], isLoading } = useRules(); diff --git a/keep-ui/app/rules/page.tsx b/keep-ui/app/(keep)/rules/page.tsx similarity index 100% rename from keep-ui/app/rules/page.tsx rename to keep-ui/app/(keep)/rules/page.tsx diff --git a/keep-ui/app/rules/ui/PlaceholderSankey.tsx b/keep-ui/app/(keep)/rules/ui/PlaceholderSankey.tsx similarity index 100% rename from keep-ui/app/rules/ui/PlaceholderSankey.tsx rename to keep-ui/app/(keep)/rules/ui/PlaceholderSankey.tsx diff --git a/keep-ui/app/settings/auth/api-key-settings.tsx b/keep-ui/app/(keep)/settings/auth/api-key-settings.tsx similarity index 96% rename from keep-ui/app/settings/auth/api-key-settings.tsx rename to keep-ui/app/(keep)/settings/auth/api-key-settings.tsx index aa8e5621f..51d98fd18 100644 --- a/keep-ui/app/settings/auth/api-key-settings.tsx +++ b/keep-ui/app/(keep)/settings/auth/api-key-settings.tsx @@ -12,14 +12,14 @@ import { Text, Badge, } from "@tremor/react"; -import Loading from "app/loading"; +import Loading from "@/app/(keep)/loading"; import { CopyBlock, a11yLight } from "react-code-blocks"; import useSWR from "swr"; import { useApiUrl } from "utils/hooks/useConfig"; import { KeyIcon, TrashIcon } from "@heroicons/react/24/outline"; import { fetcher } from "utils/fetcher"; import { useState } from "react"; -import { AuthenticationType } from "utils/authenticationType"; +import { AuthType } from "utils/authenticationType"; import CreateApiKeyModal from "../create-api-key-modal"; import { useRoles } from "utils/hooks/useRoles"; import { getSession } from "next-auth/react"; @@ -80,8 +80,8 @@ export default function ApiKeySettings({ accessToken, selectedTab }: Props) { showLineNumbers: false, }); - const authType = configData?.AUTH_TYPE as AuthenticationType; - const createApiKeyEnabled = authType !== AuthenticationType.NOAUTH; + const authType = configData?.AUTH_TYPE as AuthType; + const createApiKeyEnabled = authType !== AuthType.NOAUTH; const handleRegenerate = async ( apiKeyId: string, diff --git a/keep-ui/app/settings/auth/api-key-tab.tsx b/keep-ui/app/(keep)/settings/auth/api-key-tab.tsx similarity index 100% rename from keep-ui/app/settings/auth/api-key-tab.tsx rename to keep-ui/app/(keep)/settings/auth/api-key-tab.tsx diff --git a/keep-ui/app/settings/auth/api-key-table.tsx b/keep-ui/app/(keep)/settings/auth/api-key-table.tsx similarity index 94% rename from keep-ui/app/settings/auth/api-key-table.tsx rename to keep-ui/app/(keep)/settings/auth/api-key-table.tsx index 8fc6f63ad..fe4bd01df 100644 --- a/keep-ui/app/settings/auth/api-key-table.tsx +++ b/keep-ui/app/(keep)/settings/auth/api-key-table.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from "react"; import { Table, TableBody, @@ -34,7 +34,7 @@ export function APIKeysTable({ apiKeys, onRegenerate, onDelete, - isDisabled = false + isDisabled = false, }: APIKeysTableProps) { const getCopyBlockProps = (secret: string) => ({ theme: { ...a11yLight }, @@ -86,7 +86,9 @@ export function APIKeysTable({ icon={UpdateIcon} variant="light" color="orange" - onClick={(e) => !isDisabled && onRegenerate(key.reference_id, e)} + onClick={(e) => + !isDisabled && onRegenerate(key.reference_id, e) + } disabled={isDisabled} />