Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 34 additions & 25 deletions keep-ui/app/(health)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { ThemeScript, WatchUpdateTheme } from "@/shared/ui";
import "@/app/globals.css";
import "react-toastify/dist/ReactToastify.css";
import { PostHogPageView } from "@/shared/ui/PostHogPageView";
import { cookies } from "next/headers";
import { defaultLocale, isValidLocale } from "@/i18n/config";
import { IntlProvider } from "@/i18n/client";

// If loading a variable font, you don't need to specify the font weight
const mulish = Mulish({
Expand All @@ -25,37 +28,43 @@ type RootLayoutProps = {
export default async function RootLayout({ children }: RootLayoutProps) {
const config = getConfig();
const session = await auth();
const cookieStore = await cookies();
const cookieLocale = cookieStore.get("NEXT_LOCALE")?.value;
const locale = cookieLocale && isValidLocale(cookieLocale) ? cookieLocale : defaultLocale;
const messages = (await import(`@/i18n/messages/${locale}/index.json`)).default;

return (
<html lang="en" className={`bg-gray-50 ${mulish.className}`}>
<html lang={locale} className={`bg-gray-50 ${mulish.className}`}>
<body className="h-screen flex flex-col lg:grid lg:grid-cols-[fit-content(250px)_30px_auto] lg:grid-rows-1 lg:has-[aside[data-minimized='true']]:grid-cols-[0px_30px_auto]">
{/* ThemeScript must be the first thing to avoid flickering */}
<ThemeScript />
<ConfigProvider config={config}>
<PHProvider>
<NextAuthProvider session={session}>
{/* @ts-ignore-error Server Component */}
<PostHogPageView />
{/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */}
<main className="page-container flex flex-col col-start-3 overflow-auto">
{/* Add the banner here, before the navbar */}
{config.READ_ONLY && <ReadOnlyBanner />}
<div className="flex-1">{children}</div>
{/** footer */}
{process.env.GIT_COMMIT_HASH &&
process.env.SHOW_BUILD_INFO !== "false" && (
<div className="pointer-events-none opacity-80 w-full p-2 text-slate-400 text-xs">
<div className="w-full text-right">
Version: {process.env.KEEP_VERSION} | Build:{" "}
{process.env.GIT_COMMIT_HASH.slice(0, 6)}
<IntlProvider messages={messages} locale={locale}>
<ConfigProvider config={config}>
<PHProvider>
<NextAuthProvider session={session}>
{/* @ts-ignore-error Server Component */}
<PostHogPageView />
{/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */}
<main className="page-container flex flex-col col-start-3 overflow-auto">
{/* Add the banner here, before the navbar */}
{config.READ_ONLY && <ReadOnlyBanner />}
<div className="flex-1">{children}</div>
{/** footer */}
{process.env.GIT_COMMIT_HASH &&
process.env.SHOW_BUILD_INFO !== "false" && (
<div className="pointer-events-none opacity-80 w-full p-2 text-slate-400 text-xs">
<div className="w-full text-right">
Version: {process.env.KEEP_VERSION} | Build:{" "}
{process.env.GIT_COMMIT_HASH.slice(0, 6)}
</div>
</div>
</div>
)}
<ToastContainer />
</main>
</NextAuthProvider>
</PHProvider>
</ConfigProvider>
)}
<ToastContainer />
</main>
</NextAuthProvider>
</PHProvider>
</ConfigProvider>
</IntlProvider>
<WatchUpdateTheme />
</body>
</html>
Expand Down
13 changes: 7 additions & 6 deletions keep-ui/app/(keep)/deduplication/DeduplicationPlaceholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ import { Card, Subtitle, Title } from "@tremor/react";
import Link from "next/link";
import Image from "next/image";
import deduplicationPlaceholder from "./deduplication-placeholder.svg";
import { useTranslations } from "next-intl";

export const DeduplicationPlaceholder = () => {
const t = useTranslations("deduplication");
return (
<>
<Card className="flex flex-col items-center justify-center gap-y-8 h-full">
<div className="text-center space-y-3">
<Title className="text-2xl">No Deduplications Yet</Title>
<Title className="text-2xl">{t("noDeduplicationsYet")}</Title>
<Subtitle className="text-gray-400">
Alert deduplication is the first layer of denoising. It groups
similar alerts from one source.
<br /> To connect alerts across sources into incidents, check{" "}
{t("deduplicationDescription")}
<br /> {t("checkCorrelations")}{" "}
<Link href="/rules" className="underline text-orange-500">
Correlations
{t("correlations")}
</Link>
</Subtitle>
<Subtitle className="text-gray-400">
This page will become active once the first alerts are registered.
{t("pageWillBecomeActive")}
</Subtitle>
</div>
<Image
Expand Down
87 changes: 35 additions & 52 deletions keep-ui/app/(keep)/deduplication/DeduplicationSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { KeepApiError } from "@/shared/api";
import { Providers } from "@/shared/api/providers";
import SidePanel from "@/components/SidePanel";
import { useConfig } from "@/utils/hooks/useConfig";
import { useTranslations } from "next-intl";

interface ProviderOption {
value: string;
Expand All @@ -50,6 +51,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
mutateDeduplicationRules,
providers,
}) => {
const t = useTranslations("deduplication");
const {
control,
handleSubmit,
Expand Down Expand Up @@ -176,12 +178,12 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
if (error instanceof KeepApiError) {
setError("root.serverError", {
type: "manual",
message: error.message || "Failed to save deduplication rule",
message: error.message || t("failedToSaveRule"),
});
} else {
setError("root.serverError", {
type: "manual",
message: "An unexpected error occurred",
message: t("unexpectedError"),
});
}
} finally {
Expand All @@ -195,11 +197,11 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
<div>
<Dialog.Title className="font-bold" as={Title}>
{selectedDeduplicationRule
? `Edit ${selectedDeduplicationRule.name}`
: "Add deduplication rule"}
? t("editRule", { name: selectedDeduplicationRule.name })
: t("addDeduplicationRule")}
{selectedDeduplicationRule?.default && (
<Badge className="ml-2" color="orange">
Default Rule
{t("defaultRule")}
</Badge>
)}
</Dialog.Title>
Expand All @@ -215,16 +217,11 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
<div className="flex flex-col">
<Callout
className="mb-4 py-8"
title="Editing a Default Rule"
title={t("editingDefaultRule")}
icon={ExclamationTriangleIcon}
color="orange"
>
Editing a default deduplication rule requires advanced knowledge.
Default rules are carefully designed to provide optimal
deduplication for specific alert types. Modifying these rules may
impact the efficiency of your alert processing. If you&apos;re
unsure about making changes, we recommend creating a new custom rule
instead of modifying the default one.
{t("editingDefaultRuleDescription")}
<br></br>
<a
href={`${
Expand All @@ -233,7 +230,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
target="_blank"
className="text-orange-600 hover:underline mt-4"
>
Learn more about deduplication rules
{t("learnMoreDeduplication")}
</a>
</Callout>
</div>
Expand All @@ -243,13 +240,12 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
<div className="flex flex-col">
<Callout
className="mb-4 py-8"
title="Editing a Provisioned Rule"
title={t("editingProvisionedRule")}
icon={ExclamationTriangleIcon}
color="orange"
>
<Text>
Editing a provisioned deduplication rule is not allowed. Please
contact your system administrator for more information.
{t("editingProvisionedRuleDescription")}
</Text>
</Callout>
</div>
Expand All @@ -264,12 +260,12 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
<div className="space-y-4">
<div>
<Text className="block text-sm font-medium text-gray-700 mb-2">
Rule name
{t("ruleName")}
</Text>
<Controller
name="name"
control={control}
rules={{ required: "Rule name is required" }}
rules={{ required: t("ruleNameRequired") }}
disabled={!!selectedDeduplicationRule?.is_provisioned}
render={({ field }) => (
<TextInput
Expand All @@ -282,12 +278,12 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
</div>
<div>
<Text className="block text-sm font-medium text-gray-700 mb-2">
Description
{t("description")}
</Text>
<Controller
name="description"
control={control}
rules={{ required: "Description is required" }}
rules={{ required: t("descriptionRequired") }}
disabled={!!selectedDeduplicationRule?.is_provisioned}
render={({ field }) => (
<TextInput
Expand All @@ -300,25 +296,23 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
</div>
<div>
<span className="text-sm font-medium text-gray-700 flex items-center mb-2">
Provider
{t("provider")}
<span className="ml-1 relative inline-flex items-center">
<span className="group relative flex items-center">
<Icon
icon={InformationCircleIcon}
className="w-[1em] h-[1em] text-gray-500"
/>
<span className="absolute bottom-full left-full p-2 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity duration-300 w-80 text-center pointer-events-none group-hover:pointer-events-auto">
Select the provider for which this deduplication rule
will apply. This determines the source of alerts that
will be processed by this rule.
{t("providerTooltip")}
</span>
</span>
</span>
</span>
<Controller
name="provider_type"
control={control}
rules={{ required: "Provider is required" }}
rules={{ required: t("providerRequired") }}
render={({ field }) => (
<Select
{...field}
Expand All @@ -334,7 +328,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
provider.details?.name || provider.id || "main",
logoUrl: `/icons/${provider.type}-icon.png`,
}))}
placeholder="Select provider"
placeholder={t("selectProvider")}
onChange={(selectedOption) => {
if (selectedOption) {
const [providerType, providerId] =
Expand Down Expand Up @@ -376,19 +370,15 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
</div>
<div>
<span className="text-sm font-medium text-gray-700 flex items-center mb-2">
Fields to use for fingerprint
{t("fingerprintFields")}
<span className="ml-1 relative inline-flex items-center">
<span className="group relative flex items-center">
<Icon
icon={InformationCircleIcon}
className="w-[1em] h-[1em] text-gray-500"
/>
<span className="absolute bottom-full left-1/2 transform -translate-x-1/2 p-2 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity duration-300 w-80 text-center pointer-events-none group-hover:pointer-events-auto">
Fingerprint fields are used to identify and group
similar alerts. Choose fields that uniquely identify an
alert type, such as &apos;service&apos;,
&apos;error_type&apos;, or
&apos;affected_component&apos;.
{t("fingerprintFieldsTooltip")}
</span>
</span>
</span>
Expand All @@ -397,7 +387,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
name="fingerprint_fields"
control={control}
rules={{
required: "At least one fingerprint field is required",
required: t("fingerprintFieldsRequired"),
}}
render={({ field }) => (
<Select
Expand All @@ -408,7 +398,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
value: fieldName,
label: fieldName,
}))}
placeholder="Select fingerprint fields"
placeholder={t("selectFingerprintFields")}
value={field.value?.map((value: string) => ({
value,
label: value,
Expand All @@ -422,8 +412,8 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
}}
noOptionsMessage={() =>
selectedProviderType
? "No options"
: "Please choose provider to see available fields"
? t("noOptions")
: t("chooseProviderFirst")
}
/>
)}
Expand All @@ -448,22 +438,15 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
)}
/>
<Text className="text-sm font-medium text-gray-700 flex items-center">
Full deduplication
{t("fullDeduplication")}
<span className="ml-1 relative inline-flex items-center">
<span className="group relative flex items-center">
<Icon
icon={InformationCircleIcon}
className="w-[1em] h-[1em] text-gray-500"
/>
<span className="absolute bottom-full left-1/2 transform -translate-x-1/2 p-2 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity duration-300 w-80 text-center pointer-events-none group-hover:pointer-events-auto">
1. Full deduplication: Keep will discard events if
they are the same (excluding the &apos;Ignore
Fields&apos;).
<br />
2. Partial deduplication (default): Uses specified
fields to correlate alerts. E.g., two alerts with same
&apos;service&apos; and &apos;env&apos; fields will be
deduped into one alert.
{t("fullDeduplicationTooltip")}
</span>
</span>
</span>
Expand All @@ -473,9 +456,9 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({

{fullDeduplication && (
<div>
<Text className="block text-sm font-medium text-gray-700 mb-2">
Ignore fields
</Text>
<Text className="block text-sm font-medium text-gray-700 mb-2">
{t("ignoreFields")}
</Text>
<Controller
name="ignore_fields"
control={control}
Expand All @@ -488,7 +471,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
value: fieldName,
label: fieldName,
}))}
placeholder="Select ignore fields"
placeholder={t("selectIgnoreFields")}
value={field.value?.map((value: string) => ({
value,
label: value,
Expand All @@ -515,7 +498,7 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
{errors.root?.serverError && (
<Callout
className="mt-4"
title="Error while saving rule"
title={t("errorSavingRule")}
color="rose"
>
{errors.root.serverError.message}
Expand All @@ -530,14 +513,14 @@ const DeduplicationSidebar: React.FC<DeduplicationSidebarProps> = ({
type="button"
className="border border-orange-500 text-orange-500"
>
Cancel
{t("cancel")}
</Button>
<Button
color="orange"
type="submit"
disabled={isSubmitting || selectedDeduplicationRule?.is_provisioned}
>
{isSubmitting ? "Saving..." : "Save"}
{isSubmitting ? t("saving") : t("save")}
</Button>
</div>
</form>
Expand Down
Loading
Loading