feat: remove legacy alert rules, superseded by automations

Alert rules already auto-migrate into automations on boot and the old
engine stands down after. Deletes the rule/firing tables, routes, UI,
and AI tools; keeps notification channels since automations still use
them. Also fixes automations' notify step, which still called the
deleted repository.
This commit is contained in:
LukeGus committed 2026-09-22 10:45:12 -05:00
1 parent e0f14f2ecf
commit e372b0eda7
101 files changed
+29118 -5539

No files matched your search

-9
View File
@@ -129,9 +129,6 @@ const AdminSettingsPanel = lazy(() =>
default: m.AdminSettingsPanel,
})),
);
const AlertsPanel = lazy(() =>
import("@/sidebar/AlertsPanel").then((m) => ({ default: m.AlertsPanel })),
);
const CredentialsPanel = lazy(() =>
import("@/sidebar/CredentialsPanel").then((m) => ({
default: m.CredentialsPanel,
@@ -2898,12 +2895,6 @@ export function AppShell({
/>
</div>
)}
{railView === "alerts" && (
<div className="flex-1 min-h-0 overflow-hidden flex flex-col">
<AlertsPanel />
</div>
)}
</div>
</Suspense>
);
-192
View File
@@ -1,192 +0,0 @@
import { rbacApi } from "@/main-axios";
export interface NotificationChannel {
id: number;
userId: string;
name: string;
type: "webhook" | "ntfy" | "discord";
config: string;
enabled: boolean;
createdAt: string;
}
export interface AlertRule {
id: number;
userId: string;
hostId: number | null;
name: string;
enabled: boolean;
triggerType: string;
thresholdValue: number | null;
thresholdDurationSeconds: number | null;
cooldownMinutes: number;
createdAt: string;
updatedAt: string;
channelIds: number[];
}
export interface AlertFiring {
id: number;
userId: string;
ruleId: number;
hostId: number;
hostName: string;
firedAt: string;
resolvedAt: string | null;
value: number | null;
message: string;
severity: "info" | "warning" | "critical";
acknowledged: boolean;
ruleName?: string;
}
function stringValue(value: unknown, fallback = ""): string {
return typeof value === "string" ? value : fallback;
}
function numberValue(value: unknown, fallback = 0): number {
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
}
function nullableNumberValue(value: unknown): number | null {
return typeof value === "number" && Number.isFinite(value) ? value : null;
}
function boolValue(value: unknown): boolean {
return value === true || value === 1 || value === "1";
}
function severityValue(value: unknown): AlertFiring["severity"] {
return value === "info" || value === "critical" || value === "warning"
? value
: "warning";
}
export async function getNotificationChannels(): Promise<
NotificationChannel[]
> {
const res = await rbacApi.get("/notification-channels");
return res.data;
}
export type NotificationChannelPayload = Partial<
Omit<NotificationChannel, "config">
> & {
// When creating/updating the channel the UI may pass a parsed object
// for `config` (e.g., { url, username }) — the backend stores it as
// a JSON string. Accept either a string or any structured object here.
// Use `unknown` to allow the component-local config types to be passed
// without importing them into this module.
config: string | unknown;
};
export async function createNotificationChannel(
data: NotificationChannelPayload,
): Promise<NotificationChannel> {
const res = await rbacApi.post("/notification-channels", data);
return res.data;
}
export async function updateNotificationChannel(
id: number,
data: NotificationChannelPayload,
): Promise<NotificationChannel> {
const res = await rbacApi.put(`/notification-channels/${id}`, data);
return res.data;
}
export async function deleteNotificationChannel(id: number): Promise<void> {
await rbacApi.delete(`/notification-channels/${id}`);
}
export async function testNotificationChannel(id: number): Promise<void> {
const res = await rbacApi.post(`/notification-channels/${id}/test`);
const data = res.data as { success?: boolean; error?: string };
if (data && data.success === false) {
throw new Error(data.error || "Test notification failed");
}
}
function mapRule(r: Record<string, unknown>): AlertRule {
return {
id: r.id as number,
userId: (r.user_id ?? r.userId) as string,
hostId: (r.host_id ?? r.hostId ?? null) as number | null,
name: r.name as string,
enabled: Boolean(r.enabled),
triggerType: (r.trigger_type ?? r.triggerType) as string,
thresholdValue: (r.threshold_value ?? r.thresholdValue ?? null) as
number | null,
thresholdDurationSeconds: (r.threshold_duration_seconds ??
r.thresholdDurationSeconds ??
null) as number | null,
cooldownMinutes: (r.cooldown_minutes ?? r.cooldownMinutes) as number,
createdAt: (r.created_at ?? r.createdAt) as string,
updatedAt: (r.updated_at ?? r.updatedAt) as string,
channelIds: Array.isArray(r.channels) ? (r.channels as number[]) : [],
};
}
export function mapAlertFiring(r: Record<string, unknown>): AlertFiring {
return {
id: numberValue(r.id),
userId: stringValue(r.userId ?? r.user_id),
ruleId: numberValue(r.ruleId ?? r.rule_id),
hostId: numberValue(r.hostId ?? r.host_id),
hostName: stringValue(r.hostName ?? r.host_name),
firedAt: stringValue(r.firedAt ?? r.fired_at, new Date(0).toISOString()),
resolvedAt: stringValue(r.resolvedAt ?? r.resolved_at) || null,
value: nullableNumberValue(r.value),
message: stringValue(r.message),
severity: severityValue(r.severity),
acknowledged: boolValue(r.acknowledged),
ruleName: stringValue(r.ruleName ?? r.rule_name) || undefined,
};
}
export async function getAlertRules(): Promise<AlertRule[]> {
const res = await rbacApi.get("/alert-rules");
return (res.data as Record<string, unknown>[]).map(mapRule);
}
export async function createAlertRule(
data: Partial<AlertRule> & { channels?: number[] },
): Promise<AlertRule> {
const res = await rbacApi.post("/alert-rules", data);
return mapRule(res.data as Record<string, unknown>);
}
export async function updateAlertRule(
id: number,
data: Partial<AlertRule> & { channels?: number[] },
): Promise<AlertRule> {
const res = await rbacApi.put(`/alert-rules/${id}`, data);
return mapRule(res.data as Record<string, unknown>);
}
export async function deleteAlertRule(id: number): Promise<void> {
await rbacApi.delete(`/alert-rules/${id}`);
}
export async function getAlertFirings(opts?: {
limit?: number;
offset?: number;
acknowledged?: boolean;
}): Promise<AlertFiring[]> {
const res = await rbacApi.get("/alert-firings", { params: opts });
const data = res.data as { firings?: unknown } | unknown[];
const rows = Array.isArray(data)
? data
: Array.isArray(data.firings)
? data.firings
: [];
return rows.map((row) => mapAlertFiring(row as Record<string, unknown>));
}
export async function acknowledgeAlertFiring(id: number): Promise<void> {
await rbacApi.post(`/alert-firings/${id}/acknowledge`);
}
export async function acknowledgeAllAlertFirings(): Promise<void> {
await rbacApi.post("/alert-firings/acknowledge-all");
}
+56
View File
@@ -0,0 +1,56 @@
import { rbacApi } from "@/main-axios";
export interface NotificationChannel {
id: number;
userId: string;
name: string;
type: "webhook" | "ntfy" | "discord";
config: string;
enabled: boolean;
createdAt: string;
}
export async function getNotificationChannels(): Promise<
NotificationChannel[]
> {
const res = await rbacApi.get("/notification-channels");
return res.data;
}
export type NotificationChannelPayload = Partial<
Omit<NotificationChannel, "config">
> & {
// When creating/updating the channel the UI may pass a parsed object
// for `config` (e.g., { url, username }) — the backend stores it as
// a JSON string. Accept either a string or any structured object here.
// Use `unknown` to allow the component-local config types to be passed
// without importing them into this module.
config: string | unknown;
};
export async function createNotificationChannel(
data: NotificationChannelPayload,
): Promise<NotificationChannel> {
const res = await rbacApi.post("/notification-channels", data);
return res.data;
}
export async function updateNotificationChannel(
id: number,
data: NotificationChannelPayload,
): Promise<NotificationChannel> {
const res = await rbacApi.put(`/notification-channels/${id}`, data);
return res.data;
}
export async function deleteNotificationChannel(id: number): Promise<void> {
await rbacApi.delete(`/notification-channels/${id}`);
}
export async function testNotificationChannel(id: number): Promise<void> {
const res = await rbacApi.post(`/notification-channels/${id}/test`);
const data = res.data as { success?: boolean; error?: string };
if (data && data.success === false) {
throw new Error(data.error || "Test notification failed");
}
}
+1 -4
View File
@@ -1,7 +1,7 @@
/**
* Turns the internal identifiers the assistant works in into text a user can
* read. Tool names, proposal kinds and payload keys all surface in the panel,
* and raw ids like "propose_create_alert_rule" or "thresholddurationseconds"
* and raw ids like "propose_create_automation" or "thresholddurationseconds"
* are not something to show anyone.
*/
@@ -14,9 +14,7 @@ const TOOL_LABELS: Record<string, string> = {
list_automations: "Reading automations",
get_automation: "Reading automation",
list_workspaces: "Reading workspaces",
list_alert_rules: "Reading alert rules",
list_notification_channels: "Reading notification channels",
get_alert_firings: "Reading recent alerts",
list_homepage_items: "Reading homepage items",
get_command_history: "Reading command history",
get_network_topology: "Reading network topology",
@@ -28,7 +26,6 @@ const TOOL_LABELS: Record<string, string> = {
propose_delete_snippet: "Delete snippet",
propose_create_automation: "Create automation",
propose_create_fleet: "Create fleet",
propose_create_alert_rule: "Create alert rule",
propose_run_command: "Run command",
};
@@ -39,7 +39,6 @@ import "./widgets/IframeWidget";
import "./widgets/RssFeedWidget";
import "./widgets/MetricsChartWidget";
import "./widgets/HostGridWidget";
import "./widgets/AlertFeedWidget";
import "./widgets/PingStatusWidget";
import "./widgets/RecentActivityWidget";
import "./widgets/TermixUptimeWidget";
@@ -1,46 +0,0 @@
import { useTranslation } from "react-i18next";
import type {
AlertFeedConfig,
WidgetEditFormProps,
} from "@/types/homepage-types";
import { Input } from "@/components/input";
export function AlertFeedEditForm({
config,
onChange,
}: WidgetEditFormProps<AlertFeedConfig>) {
const { t } = useTranslation();
return (
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-muted-foreground">
{t("homepage.maxItems")}
</label>
<Input
type="number"
min={1}
max={50}
value={config.maxItems}
onChange={(e) =>
onChange({
...config,
maxItems: Math.max(1, Number(e.target.value)),
})
}
className="h-8 text-xs"
/>
</div>
<label className="flex items-center gap-2 text-xs text-muted-foreground cursor-pointer">
<input
type="checkbox"
checked={config.showAcknowledged}
onChange={(e) =>
onChange({ ...config, showAcknowledged: e.target.checked })
}
className="accent-accent-brand"
/>
{t("homepage.showAcknowledged")}
</label>
</div>
);
}
@@ -22,7 +22,6 @@ import type {
RssFeedConfig,
MetricsChartConfig,
HostGridConfig,
AlertFeedConfig,
PingStatusConfig,
RecentActivityConfig,
TermixUptimeConfig,
@@ -55,7 +54,6 @@ import { IframeEditForm } from "./IframeEditForm";
import { RssFeedEditForm } from "./RssFeedEditForm";
import { MetricsChartEditForm } from "./MetricsChartEditForm";
import { HostGridEditForm } from "./HostGridEditForm";
import { AlertFeedEditForm } from "./AlertFeedEditForm";
import { PingStatusEditForm } from "./PingStatusEditForm";
import { RecentActivityEditForm } from "./RecentActivityEditForm";
import { TermixUptimeEditForm } from "./TermixUptimeEditForm";
@@ -197,13 +195,6 @@ export function WidgetEditDialog({
onChange={(c) => setConfig(c as unknown as Record<string, unknown>)}
/>
);
case "alert_feed":
return (
<AlertFeedEditForm
config={config as unknown as AlertFeedConfig}
onChange={(c) => setConfig(c as unknown as Record<string, unknown>)}
/>
);
case "ping_status":
return (
<PingStatusEditForm
@@ -1,180 +0,0 @@
import { useEffect, useState } from "react";
import {
Bell,
CheckCircle,
AlertTriangle,
AlertCircle,
Info,
Check,
} from "lucide-react";
import { useTranslation } from "react-i18next";
import { registerWidget } from "./WidgetRegistry";
import type {
AlertFeedConfig,
WidgetComponentProps,
} from "@/types/homepage-types";
import { GRID_SIZE } from "@/types/homepage-types";
import {
acknowledgeAlertFiring,
getAlertFirings,
type AlertFiring,
} from "@/api/alerts-api";
import { WidgetTitle } from "./WidgetTitle";
function relativeTime(ts: string): string {
const diff = Date.now() - new Date(ts).getTime();
const m = Math.floor(diff / 60_000);
if (m < 1) return "just now";
if (m < 60) return `${m}m ago`;
const h = Math.floor(m / 60);
if (h < 24) return `${h}h ago`;
return `${Math.floor(h / 24)}d ago`;
}
function SeverityIcon({ severity }: { severity: AlertFiring["severity"] }) {
if (severity === "critical")
return <AlertCircle size={11} className="shrink-0 text-red-500" />;
if (severity === "warning")
return <AlertTriangle size={11} className="shrink-0 text-amber-500" />;
return <Info size={11} className="shrink-0 text-blue-400" />;
}
function AlertFeedWidget({
widget,
config,
}: WidgetComponentProps<AlertFeedConfig>) {
const { t } = useTranslation();
const { maxItems, showAcknowledged } = config;
const [firings, setFirings] = useState<AlertFiring[]>([]);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
try {
const data = await getAlertFirings({ limit: maxItems * 2 });
const filtered = showAcknowledged
? data
: data.filter((f) => !f.acknowledged);
setFirings(filtered.slice(0, maxItems));
} catch {
/* ignore */
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
let intervalId: ReturnType<typeof setInterval> | null = null;
const start = () => {
if (intervalId !== null) return;
intervalId = setInterval(() => {
if (document.visibilityState === "hidden") return;
void fetchData();
}, 30_000);
};
const stop = () => {
if (intervalId === null) return;
clearInterval(intervalId);
intervalId = null;
};
const onVisibility = () => {
if (document.visibilityState === "hidden") {
stop();
return;
}
void fetchData();
start();
};
if (document.visibilityState !== "hidden") start();
document.addEventListener("visibilitychange", onVisibility);
return () => {
stop();
document.removeEventListener("visibilitychange", onVisibility);
};
}, [maxItems, showAcknowledged]);
const handleAck = async (id: number) => {
try {
await acknowledgeAlertFiring(id);
setFirings((prev) =>
prev.map((f) => (f.id === id ? { ...f, acknowledged: true } : f)),
);
} catch {
/* ignore */
}
};
if (loading) {
return (
<div className="flex items-center justify-center w-full h-full text-xs text-muted-foreground">
{t("homepage.loading")}
</div>
);
}
if (firings.length === 0) {
return (
<div className="flex flex-col items-center justify-center w-full h-full gap-2">
<CheckCircle size={20} className="text-green-500" />
<span className="text-xs text-muted-foreground">
{t("homepage.allClear")}
</span>
</div>
);
}
return (
<div className="flex flex-col w-full h-full overflow-hidden">
<WidgetTitle
title={widget.title || t("homepage.widgetAlertFeedName")}
icon={<Bell size={11} />}
/>
<div className="flex-1 overflow-auto">
{firings.map((f) => (
<div
key={f.id}
className={`flex items-start gap-2 px-2 py-1.5 border-b border-border/30 ${f.acknowledged ? "opacity-50" : ""}`}
>
<SeverityIcon severity={f.severity} />
<div className="flex flex-col min-w-0 flex-1">
<span className="text-[10px] font-semibold text-foreground truncate">
{f.ruleName ?? f.message}
</span>
<span className="text-[9px] text-muted-foreground truncate">
{f.hostName} · {relativeTime(f.firedAt)}
</span>
</div>
{!f.acknowledged && (
<button
onClick={(e) => {
e.stopPropagation();
handleAck(f.id);
}}
className="p-0.5 text-muted-foreground hover:text-green-500 transition-colors shrink-0"
title={t("homepage.acknowledgeAlert")}
>
<Check size={10} />
</button>
)}
</div>
))}
</div>
</div>
);
}
registerWidget<AlertFeedConfig>({
id: "alert_feed",
name: "Alert Feed",
description: "Live feed of active alert firings",
category: "monitoring",
icon: <Bell size={14} />,
defaultConfig: { maxItems: 10, showAcknowledged: false },
defaultSize: { w: GRID_SIZE * 10, h: GRID_SIZE * 8 },
minSize: { w: GRID_SIZE * 4, h: GRID_SIZE * 3 },
component: AlertFeedWidget,
});
export { AlertFeedWidget };
+4 -34
View File
@@ -287,7 +287,6 @@
"loading": "Loading...",
"noData": "No data available",
"allClear": "All clear",
"acknowledgeAlert": "Acknowledge",
"noPingUrls": "No URLs configured",
"pingLabel": "Label",
"addPingUrl": "Add URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historical CPU, memory, disk or network chart for a host",
"widgetHostGridName": "Host Grid",
"widgetHostGridDesc": "Grid view of SSH host statuses",
"widgetAlertFeedName": "Alert Feed",
"widgetAlertFeedDesc": "Live alert firings with acknowledge support",
"widgetPingStatusName": "Ping Status",
"widgetPingStatusDesc": "HTTP ping status for one or more URLs",
"widgetRecentActivityName": "Recent Activity",
@@ -747,7 +744,6 @@
"portForwarding": "Forwarding",
"sftp": "SFTP",
"connections": "Connections",
"alerts": "Alerts",
"serial": "Serial",
"keepSideMenuOpen": "Keep Menu Open",
"collapseSideMenu": "Collapse Menu",
@@ -4950,21 +4946,6 @@
"viewHistory": "View History"
},
"alerts": {
"tabFirings": "Alerts",
"tabRules": "Rules",
"tabChannels": "Channels",
"noFirings": "No unacknowledged alerts",
"noRules": "No alert rules configured",
"noChannels": "No notification channels configured",
"rulesDesc": "Alert rules trigger notifications",
"channelsDesc": "Where alert notifications are sent",
"acknowledge": "Acknowledge",
"ackAll": "Ack All",
"allAcknowledged": "All alerts acknowledged",
"ackFailed": "Failed to acknowledge alert",
"ackAllFailed": "Failed to acknowledge all alerts",
"showAcknowledged": "Show All",
"hideAcknowledged": "Hide Acked",
"addChannel": "Add Channel",
"editChannel": "Edit Channel",
"channelName": "Name",
@@ -4978,21 +4959,10 @@
"ntfyTopicRequired": "Topic is required",
"ntfyToken": "Access Token (optional)",
"channelSaveFailed": "Failed to save channel",
"test": "Test",
"testSent": "Test notification sent",
"testFailed": "Test notification failed",
"testFailedWithReason": "Test notification failed: {{reason}}",
"addRule": "Add Alert Rule",
"editRule": "Edit Alert Rule",
"ruleName": "Rule Name",
"ruleNameRequired": "Name is required",
"triggerType": "Trigger",
"thresholdValue": "Threshold (%)",
"durationSeconds": "Duration (seconds, 0 = fire immediately)",
"cooldownMinutes": "Cooldown (minutes)",
"channels": "Notification Channels",
"noChannelsHint": "Add channels in the Channels tab first",
"ruleSaveFailed": "Failed to save rule"
"discordUrlInvalid": "Discord URL must be a valid Discord webhook URL",
"discordWebhookDesc": "POST a Discord embed to this webhook URL on each alert firing",
"discordUsername": "Display name (optional)",
"discordAvatar": "Avatar URL (optional)"
},
"ai": {
"title": "Assistant",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Laai tans...",
"noData": "Geen data beskikbaar nie",
"allClear": "Alles duidelik",
"acknowledgeAlert": "Erken",
"noPingUrls": "Geen URL'e gekonfigureer nie",
"pingLabel": "Etiket",
"addPingUrl": "Voeg URL by",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historiese SVE-, geheue-, skyf- of netwerkgrafiek vir 'n gasheer",
"widgetHostGridName": "Gasheerrooster",
"widgetHostGridDesc": "Roosteraansig van SSH-gasheerstatusse",
"widgetAlertFeedName": "Waarskuwingsvoer",
"widgetAlertFeedDesc": "Regstreekse waarskuwingsvuurwerke met erkenningsondersteuning",
"widgetPingStatusName": "Ping-status",
"widgetPingStatusDesc": "HTTP-pingstatus vir een of meer URL'e",
"widgetRecentActivityName": "Onlangse Aktiwiteit",
@@ -747,7 +744,6 @@
"portForwarding": "Aanstuur",
"sftp": "SFTP",
"connections": "Verbindings",
"alerts": "Waarskuwings",
"serial": "Reeks",
"keepSideMenuOpen": "Hou Kieslys Oop",
"collapseSideMenu": "Vou Kieslys in",
@@ -4890,21 +4886,6 @@
"viewHistory": "Bekyk Geskiedenis"
},
"alerts": {
"tabFirings": "Waarskuwings",
"tabRules": "Reëls",
"tabChannels": "Kanale",
"noFirings": "Geen onerkende waarskuwings nie",
"noRules": "Geen waarskuwingsreëls opgestel nie",
"noChannels": "Geen kennisgewingkanale gekonfigureer nie",
"rulesDesc": "Waarskuwingsreëls aktiveer kennisgewings",
"channelsDesc": "Waar waarskuwingskennisgewings gestuur word",
"acknowledge": "Erken",
"ackAll": "Bevestig alles",
"allAcknowledged": "Alle waarskuwings erken",
"ackFailed": "Kon nie waarskuwing erken nie",
"ackAllFailed": "Kon nie alle waarskuwings erken nie",
"showAcknowledged": "Wys alles",
"hideAcknowledged": "Versteek Goedgekeur",
"addChannel": "Voeg kanaal by",
"editChannel": "Wysig kanaal",
"channelName": "Naam",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Onderwerp",
"ntfyTopicRequired": "Onderwerp is vereis",
"ntfyToken": "Toegangstoken (opsioneel)",
"channelSaveFailed": "Kon nie kanaal stoor nie",
"test": "Toets",
"testSent": "Toetskennisgewing gestuur",
"testFailed": "Toetskennisgewing het misluk",
"testFailedWithReason": "Toetskennisgewing het misluk: {{reason}}",
"addRule": "Voeg waarskuwingsreël by",
"editRule": "Wysig waarskuwingsreël",
"ruleName": "Reëlnaam",
"ruleNameRequired": "Naam word vereis",
"triggerType": "Sneller",
"thresholdValue": "Drempel (%)",
"durationSeconds": "Duur (sekondes, 0 = onmiddellike vuur)",
"cooldownMinutes": "Afkoeltyd (minute)",
"channels": "Kennisgewingskanale",
"noChannelsHint": "Voeg eers kanale by die Kanale-oortjie",
"ruleSaveFailed": "Kon nie reël stoor nie"
"channelSaveFailed": "Kon nie kanaal stoor nie"
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "جار التحميل...",
"noData": "لا توجد بيانات",
"allClear": "كل شيء طبيعي",
"acknowledgeAlert": "إقرار",
"noPingUrls": "لم يتم تكوين عناوين URL",
"pingLabel": "التسمية",
"addPingUrl": "إضافة URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "مخطط تاريخي لوحدة المعالجة المركزية أو الذاكرة أو القرص أو الشبكة لمضيف",
"widgetHostGridName": "شبكة المضيفات",
"widgetHostGridDesc": "عرض شبكي لحالات مضيفات SSH",
"widgetAlertFeedName": "موجز التنبيهات",
"widgetAlertFeedDesc": "تنبيهات حية مع دعم الإقرار",
"widgetPingStatusName": "حالة Ping",
"widgetPingStatusDesc": "حالة ping HTTP لعنوان URL واحد أو أكثر",
"widgetRecentActivityName": "النشاط الأخير",
@@ -747,7 +744,6 @@
"portForwarding": "إعادة التوجيه",
"sftp": "SFTP",
"connections": "الاتصالات",
"alerts": "التنبيهات",
"serial": "تسلسلي",
"keepSideMenuOpen": "أبقِ قائمة الطعام مفتوحة",
"collapseSideMenu": "قائمة قابلة للطي",
@@ -4890,21 +4886,6 @@
"viewHistory": "عرض السجل"
},
"alerts": {
"tabFirings": "التنبيهات",
"tabRules": "القواعد",
"tabChannels": "القنوات",
"noFirings": "لا توجد تنبيهات غير مُقَرَّة",
"noRules": "لم يتم تكوين قواعد تنبيه",
"noChannels": "لم يتم تكوين قنوات إشعار",
"rulesDesc": "تقوم قواعد التنبيه بتشغيل الإشعارات",
"channelsDesc": "مكان إرسال إشعارات التنبيه",
"acknowledge": "إقرار",
"ackAll": "إقرار الكل",
"allAcknowledged": "تم إقرار جميع التنبيهات",
"ackFailed": "فشل في إقرار التنبيه",
"ackAllFailed": "فشل في إقرار جميع التنبيهات",
"showAcknowledged": "إظهار الكل",
"hideAcknowledged": "إخفاء المُقَرَّة",
"addChannel": "إضافة قناة",
"editChannel": "تعديل القناة",
"channelName": "الاسم",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "الموضوع",
"ntfyTopicRequired": "الموضوع مطلوب",
"ntfyToken": "رمز الوصول (اختياري)",
"channelSaveFailed": "فشل حفظ القناة",
"test": "اختبار",
"testSent": "تم إرسال إشعار الاختبار",
"testFailed": "فشل إشعار الاختبار",
"testFailedWithReason": "فشل إشعار الاختبار: {{reason}}",
"addRule": "إضافة قاعدة تنبيه",
"editRule": "تعديل قاعدة تنبيه",
"ruleName": "اسم القاعدة",
"ruleNameRequired": "الاسم مطلوب",
"triggerType": "المشغّل",
"thresholdValue": "الحد (%)",
"durationSeconds": "المدة (بالثواني، 0 = إطلاق فوري)",
"cooldownMinutes": "فترة التهدئة (بالدقائق)",
"channels": "قنوات الإشعارات",
"noChannelsHint": "أضف قنوات في تبويب القنوات أولاً",
"ruleSaveFailed": "فشل حفظ القاعدة"
"channelSaveFailed": "فشل حفظ القناة"
},
"ai": {
"title": "مساعد",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Зареждане...",
"noData": "Няма налични данни",
"allClear": "Всичко е чисто",
"acknowledgeAlert": "Потвърждаване",
"noPingUrls": "Няма конфигурирани URL адреси",
"pingLabel": "Етикет",
"addPingUrl": "Добавяне на URL адрес",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Историческа диаграма на процесора, паметта, диска или мрежата за хост",
"widgetHostGridName": "Хост мрежа",
"widgetHostGridDesc": "Табелен изглед на състоянията на SSH хоста",
"widgetAlertFeedName": "Канал за предупреждения",
"widgetAlertFeedDesc": "Активиране на аларми в реално време с поддръжка на потвърждение",
"widgetPingStatusName": "Състояние на пинг",
"widgetPingStatusDesc": "HTTP ping статус за един или повече URL адреси",
"widgetRecentActivityName": "Последна активност",
@@ -747,7 +744,6 @@
"portForwarding": "Препращане",
"sftp": "SFTP",
"connections": "Връзки",
"alerts": "Сигнали",
"serial": "Сериен",
"keepSideMenuOpen": "Запази менюто отворено",
"collapseSideMenu": "Свиване на менюто",
@@ -4890,21 +4886,6 @@
"viewHistory": "Преглед на историята"
},
"alerts": {
"tabFirings": "Сигнали",
"tabRules": "Правила",
"tabChannels": "Канали",
"noFirings": "Няма непотвърдени сигнали",
"noRules": "Няма конфигурирани правила за предупреждения",
"noChannels": "Няма конфигурирани канали за известия",
"rulesDesc": "Правилата за известия задействат известия",
"channelsDesc": "Къде се изпращат известия за предупреждения",
"acknowledge": "Потвърждаване",
"ackAll": "Потвърждение на всички",
"allAcknowledged": "Всички сигнали са потвърдени",
"ackFailed": "Неуспешно потвърждаване на предупреждението",
"ackAllFailed": "Неуспешно потвърждаване на всички сигнали",
"showAcknowledged": "Покажи всички",
"hideAcknowledged": "Скриване на одобрено",
"addChannel": "Добавяне на канал",
"editChannel": "Редактиране на канал",
"channelName": "Име",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Тема",
"ntfyTopicRequired": "Темата е задължителна",
"ntfyToken": "Токен за достъп (по избор)",
"channelSaveFailed": "Запазването на канала не бе успешно",
"test": "Тест",
"testSent": "Тестово известие е изпратено",
"testFailed": "Тестовото известие не беше успешно",
"testFailedWithReason": "Тестовото известие не беше успешно: {{reason}}",
"addRule": "Добавяне на правило за предупреждение",
"editRule": "Редактиране на правило за предупреждение",
"ruleName": "Име на правилото",
"ruleNameRequired": "Името е задължително",
"triggerType": "Спусък",
"thresholdValue": "Праг (%)",
"durationSeconds": "Продължителност (секунди, 0 = незабавно задействане)",
"cooldownMinutes": "Охлаждане (минути)",
"channels": "Канали за известия",
"noChannelsHint": "Първо добавете канали в раздела „Канали“",
"ruleSaveFailed": "Запазването на правилото не бе успешно"
"channelSaveFailed": "Запазването на канала не бе успешно"
},
"ai": {
"title": "Асистент",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "লোড হচ্ছে...",
"noData": "কোন ডেটা উপলব্ধ নেই",
"allClear": "সব পরিষ্কার",
"acknowledgeAlert": "স্বীকার করুন",
"noPingUrls": "কোন URL কনফিগার করা নেই",
"pingLabel": "লেবেল",
"addPingUrl": "URL যোগ করুন",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "একটি হোস্টের ঐতিহাসিক সিপিইউ, মেমরি, ডিস্ক বা নেটওয়ার্ক চার্ট",
"widgetHostGridName": "হোস্ট গ্রিড",
"widgetHostGridDesc": "SSH হোস্ট স্ট্যাটাসের গ্রিড ভিউ",
"widgetAlertFeedName": "সতর্কতা ফিড",
"widgetAlertFeedDesc": "স্বীকৃতি সমর্থন সহ লাইভ অ্যালার্ট চালু",
"widgetPingStatusName": "পিং স্ট্যাটাস",
"widgetPingStatusDesc": "এক বা একাধিক URL-এর জন্য HTTP পিং স্ট্যাটাস",
"widgetRecentActivityName": "সাম্প্রতিক কার্যকলাপ",
@@ -747,7 +744,6 @@
"portForwarding": "ফরোয়ার্ডিং",
"sftp": "এসএফটিপি",
"connections": "সংযোগ",
"alerts": "সতর্কতা",
"serial": "সিরিয়াল",
"keepSideMenuOpen": "মেনু খোলা রাখুন",
"collapseSideMenu": "মেনু সংকুচিত করুন",
@@ -4890,21 +4886,6 @@
"viewHistory": "ইতিহাস দেখুন"
},
"alerts": {
"tabFirings": "সতর্কতা",
"tabRules": "নিয়ম",
"tabChannels": "চ্যানেল",
"noFirings": "কোনো অস্বীকৃত সতর্কতা নেই",
"noRules": "কোনো সতর্কতা নিয়ম কনফিগার করা নেই",
"noChannels": "কোন বিজ্ঞপ্তি চ্যানেল কনফিগার করা নেই",
"rulesDesc": "সতর্কতা নিয়মগুলি বিজ্ঞপ্তি ট্রিগার করে",
"channelsDesc": "যেখানে সতর্কবার্তা পাঠানো হয়",
"acknowledge": "স্বীকার করুন",
"ackAll": "সবাইকে ধন্যবাদ",
"allAcknowledged": "সকল সতর্কতা স্বীকার করা হয়েছে",
"ackFailed": "সতর্কতা স্বীকার করতে ব্যর্থ হয়েছে",
"ackAllFailed": "সমস্ত সতর্কতা স্বীকার করতে ব্যর্থ হয়েছে",
"showAcknowledged": "সব দেখান",
"hideAcknowledged": "লুকান অ্যাকড",
"addChannel": "চ্যানেল যোগ করুন",
"editChannel": "চ্যানেল সম্পাদনা করুন",
"channelName": "নাম",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "বিষয়",
"ntfyTopicRequired": "বিষয় আবশ্যক",
"ntfyToken": "অ্যাক্সেস টোকেন (ঐচ্ছিক)",
"channelSaveFailed": "চ্যানেল সংরক্ষণ করতে ব্যর্থ হয়েছে",
"test": "পরীক্ষা",
"testSent": "পরীক্ষার বিজ্ঞপ্তি পাঠানো হয়েছে",
"testFailed": "পরীক্ষার বিজ্ঞপ্তি ব্যর্থ হয়েছে",
"testFailedWithReason": "পরীক্ষার বিজ্ঞপ্তি ব্যর্থ হয়েছে: {{reason}}",
"addRule": "সতর্কতা নিয়ম যোগ করুন",
"editRule": "সতর্কতা নিয়ম সম্পাদনা করুন",
"ruleName": "নিয়মের নাম",
"ruleNameRequired": "নাম আবশ্যক",
"triggerType": "ট্রিগার",
"thresholdValue": "থ্রেশহোল্ড (%)",
"durationSeconds": "সময়কাল (সেকেন্ড, ০ = অবিলম্বে চালু হবে)",
"cooldownMinutes": "কুলডাউন (মিনিট)",
"channels": "বিজ্ঞপ্তি চ্যানেল",
"noChannelsHint": "প্রথমে চ্যানেল ট্যাবে চ্যানেল যোগ করুন।",
"ruleSaveFailed": "নিয়ম সংরক্ষণ করতে ব্যর্থ হয়েছে"
"channelSaveFailed": "চ্যানেল সংরক্ষণ করতে ব্যর্থ হয়েছে"
},
"ai": {
"title": "সহকারী",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "S'està carregant...",
"noData": "No hi ha dades disponibles",
"allClear": "Tot clar",
"acknowledgeAlert": "Reconeix",
"noPingUrls": "No s'han configurat URL",
"pingLabel": "Etiqueta",
"addPingUrl": "Afegeix URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Gràfic històric de CPU, memòria, disc o xarxa per a un host",
"widgetHostGridName": "Graella d'amfitrió",
"widgetHostGridDesc": "Vista de quadrícula dels estats dels hosts SSH",
"widgetAlertFeedName": "Canal d'alertes",
"widgetAlertFeedDesc": "Trets d'alerta en directe amb suport de confirmació",
"widgetPingStatusName": "Estat del ping",
"widgetPingStatusDesc": "Estat del ping HTTP per a una o més URL",
"widgetRecentActivityName": "Activitat recent",
@@ -747,7 +744,6 @@
"portForwarding": "Reenviament",
"sftp": "SFTP",
"connections": "Connexions",
"alerts": "Alertes",
"serial": "Sèrie",
"keepSideMenuOpen": "Mantenir el menú obert",
"collapseSideMenu": "Menú replegable",
@@ -4890,21 +4886,6 @@
"viewHistory": "Veure l'historial"
},
"alerts": {
"tabFirings": "Alertes",
"tabRules": "Regles",
"tabChannels": "Canals",
"noFirings": "Sense alertes no confirmades",
"noRules": "No hi ha cap regla d'alerta configurada",
"noChannels": "No hi ha canals de notificació configurats",
"rulesDesc": "Les regles d'alerta activen les notificacions",
"channelsDesc": "On s'envien les notificacions d'alerta",
"acknowledge": "Reconeix",
"ackAll": "Ack Tot",
"allAcknowledged": "Totes les alertes han estat reconegudes",
"ackFailed": "No s'ha pogut confirmar l'alerta",
"ackAllFailed": "No s'han pogut reconèixer totes les alertes",
"showAcknowledged": "Mostra-ho tot",
"hideAcknowledged": "Amaga l'acusat",
"addChannel": "Afegeix un canal",
"editChannel": "Edita el canal",
"channelName": "Nom",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Tema",
"ntfyTopicRequired": "El tema és obligatori",
"ntfyToken": "Token d'accés (opcional)",
"channelSaveFailed": "No s'ha pogut desar el canal",
"test": "Prova",
"testSent": "Notificació de prova enviada",
"testFailed": "La notificació de prova ha fallat",
"testFailedWithReason": "La notificació de prova ha fallat: {{reason}}",
"addRule": "Afegeix una regla d'alerta",
"editRule": "Edita la regla d'alerta",
"ruleName": "Nom de la regla",
"ruleNameRequired": "El nom és obligatori",
"triggerType": "Disparador",
"thresholdValue": "Llindar (%)",
"durationSeconds": "Durada (segons, 0 = dispara immediatament)",
"cooldownMinutes": "Refredament (minuts)",
"channels": "Canals de notificació",
"noChannelsHint": "Afegiu primer els canals a la pestanya Canals",
"ruleSaveFailed": "No s'ha pogut desar la regla"
"channelSaveFailed": "No s'ha pogut desar el canal"
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Načítání...",
"noData": "Nejsou k dispozici žádná data",
"allClear": "Všechno čisté",
"acknowledgeAlert": "Potvrdit",
"noPingUrls": "Žádné adresy URL nejsou nakonfigurovány",
"pingLabel": "Označení",
"addPingUrl": "Přidat URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historický graf CPU, paměti, disku nebo sítě pro hostitele",
"widgetHostGridName": "Hostitelská mřížka",
"widgetHostGridDesc": "Mřížkové zobrazení stavů hostitelů SSH",
"widgetAlertFeedName": "Zdroj upozornění",
"widgetAlertFeedDesc": "Živé výstrahy s podporou potvrzení",
"widgetPingStatusName": "Stav pingu",
"widgetPingStatusDesc": "Stav HTTP pingu pro jednu nebo více URL adres",
"widgetRecentActivityName": "Nedávná aktivita",
@@ -747,7 +744,6 @@
"portForwarding": "Přeposílání",
"sftp": "SFTP",
"connections": "Připojení",
"alerts": "Upozornění",
"serial": "Seriál",
"keepSideMenuOpen": "Ponechat nabídku otevřenou",
"collapseSideMenu": "Sbalit nabídku",
@@ -4890,21 +4886,6 @@
"viewHistory": "Zobrazit historii"
},
"alerts": {
"tabFirings": "Upozornění",
"tabRules": "Pravidla",
"tabChannels": "Kanály",
"noFirings": "Žádná nepotvrzená upozornění",
"noRules": "Nejsou nakonfigurována žádná pravidla upozornění",
"noChannels": "Žádné nakonfigurované kanály oznámení",
"rulesDesc": "Pravidla upozornění spouštějí oznámení",
"channelsDesc": "Kam se odesílají upozornění",
"acknowledge": "Potvrdit",
"ackAll": "Potvrzení všeho",
"allAcknowledged": "Všechna upozornění potvrzena",
"ackFailed": "Nepodařilo se potvrdit upozornění",
"ackAllFailed": "Nepodařilo se potvrdit všechna upozornění",
"showAcknowledged": "Zobrazit vše",
"hideAcknowledged": "Skrýt potvrzeno",
"addChannel": "Přidat kanál",
"editChannel": "Upravit kanál",
"channelName": "Jméno",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Téma",
"ntfyTopicRequired": "Téma je povinné",
"ntfyToken": "Přístupový token (volitelné)",
"channelSaveFailed": "Uložení kanálu se nezdařilo",
"test": "Test",
"testSent": "Testovací oznámení odesláno",
"testFailed": "Testovací oznámení selhalo",
"testFailedWithReason": "Testovací oznámení selhalo: {{reason}}",
"addRule": "Přidat pravidlo upozornění",
"editRule": "Upravit pravidlo upozornění",
"ruleName": "Název pravidla",
"ruleNameRequired": "Jméno je povinné",
"triggerType": "Spoušť",
"thresholdValue": "Prahová hodnota (%)",
"durationSeconds": "Trvání (sekundy, 0 = okamžitá výstřel)",
"cooldownMinutes": "Doba ochlazování (minuty)",
"channels": "Oznamovací kanály",
"noChannelsHint": "Nejprve přidejte kanály na kartě Kanály",
"ruleSaveFailed": "Uložení pravidla se nezdařilo"
"channelSaveFailed": "Uložení kanálu se nezdařilo"
},
"ai": {
"title": "Asistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Indlæser...",
"noData": "Ingen data tilgængelige",
"allClear": "Alt klart",
"acknowledgeAlert": "Anerkende",
"noPingUrls": "Ingen URL'er konfigureret",
"pingLabel": "Mærke",
"addPingUrl": "Tilføj URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historisk CPU-, hukommelses-, disk- eller netværksdiagram for en vært",
"widgetHostGridName": "Værtgitter",
"widgetHostGridDesc": "Gittervisning af SSH-værtsstatusser",
"widgetAlertFeedName": "Alarmfeed",
"widgetAlertFeedDesc": "Live alarmudløsninger med bekræftelsesstøtte",
"widgetPingStatusName": "Ping-status",
"widgetPingStatusDesc": "HTTP-pingstatus for en eller flere URL'er",
"widgetRecentActivityName": "Seneste aktivitet",
@@ -747,7 +744,6 @@
"portForwarding": "Videresendelse",
"sftp": "SFTP",
"connections": "Forbindelser",
"alerts": "Advarsler",
"serial": "Seriel",
"keepSideMenuOpen": "Hold menuen åben",
"collapseSideMenu": "Skjul menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Vis historik"
},
"alerts": {
"tabFirings": "Advarsler",
"tabRules": "Regler",
"tabChannels": "Kanaler",
"noFirings": "Ingen ubekræftede advarsler",
"noRules": "Ingen alarmregler konfigureret",
"noChannels": "Ingen notifikationskanaler konfigureret",
"rulesDesc": "Alarmregler udløser notifikationer",
"channelsDesc": "Hvor alarmmeddelelser sendes",
"acknowledge": "Anerkende",
"ackAll": "Bekræft alle",
"allAcknowledged": "Alle alarmer bekræftet",
"ackFailed": "Kunne ikke bekræfte advarslen",
"ackAllFailed": "Kunne ikke bekræfte alle advarsler",
"showAcknowledged": "Vis alle",
"hideAcknowledged": "Skjul bekræftet",
"addChannel": "Tilføj kanal",
"editChannel": "Rediger kanal",
"channelName": "Navn",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Emne",
"ntfyTopicRequired": "Emne er påkrævet",
"ntfyToken": "Adgangstoken (valgfrit)",
"channelSaveFailed": "Kanalen kunne ikke gemmes",
"test": "Prøve",
"testSent": "Testnotifikation sendt",
"testFailed": "Testnotifikation mislykkedes",
"testFailedWithReason": "Testnotifikation mislykkedes: {{reason}}",
"addRule": "Tilføj alarmregel",
"editRule": "Rediger alarmregel",
"ruleName": "Regelnavn",
"ruleNameRequired": "Navn er påkrævet",
"triggerType": "Udløser",
"thresholdValue": "Tærskel (%)",
"durationSeconds": "Varighed (sekunder, 0 = affyres øjeblikkeligt)",
"cooldownMinutes": "Nedkølingstid (minutter)",
"channels": "Meddelelseskanaler",
"noChannelsHint": "Tilføj kanaler i fanen Kanaler først",
"ruleSaveFailed": "Reglen kunne ikke gemmes"
"channelSaveFailed": "Kanalen kunne ikke gemmes"
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Wird geladen...",
"noData": "Keine Daten verfügbar",
"allClear": "Alles in Ordnung",
"acknowledgeAlert": "Bestätigen",
"noPingUrls": "Keine URLs konfiguriert",
"pingLabel": "Bezeichnung",
"addPingUrl": "URL hinzufügen",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historisches CPU-, Arbeitsspeicher-, Festplatten- oder Netzwerkdiagramm für einen Host",
"widgetHostGridName": "Host-Raster",
"widgetHostGridDesc": "Rasteransicht der SSH-Host-Status",
"widgetAlertFeedName": "Alarm-Feed",
"widgetAlertFeedDesc": "Live-Alarmauslösungen mit Bestätigungsfunktion",
"widgetPingStatusName": "Ping-Status",
"widgetPingStatusDesc": "HTTP-Ping-Status für eine oder mehrere URLs",
"widgetRecentActivityName": "Letzte Aktivität",
@@ -747,7 +744,6 @@
"portForwarding": "Weiterleitung",
"sftp": "SFTP",
"connections": "Verbindungen",
"alerts": "Benachrichtigungen",
"serial": "Seriell",
"keepSideMenuOpen": "Menü geöffnet lassen",
"collapseSideMenu": "Menü ausklappen",
@@ -4890,21 +4886,6 @@
"viewHistory": "Verlauf anzeigen"
},
"alerts": {
"tabFirings": "Alarme",
"tabRules": "Regeln",
"tabChannels": "Kanäle",
"noFirings": "Keine unbestätigten Alarme",
"noRules": "Keine Alarmregeln konfiguriert",
"noChannels": "Keine Benachrichtigungskanäle konfiguriert",
"rulesDesc": "Alarmregeln lösen Benachrichtigungen aus",
"channelsDesc": "Wohin Alarmbenachrichtigungen gesendet werden",
"acknowledge": "Quittieren",
"ackAll": "Alle quittieren",
"allAcknowledged": "Alle Alarme quittiert",
"ackFailed": "Quittieren des Alarms fehlgeschlagen",
"ackAllFailed": "Quittieren aller Alarme fehlgeschlagen",
"showAcknowledged": "Alle anzeigen",
"hideAcknowledged": "Quittierte ausblenden",
"addChannel": "Kanal hinzufügen",
"editChannel": "Kanal bearbeiten",
"channelName": "Name",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Topic",
"ntfyTopicRequired": "Topic ist erforderlich",
"ntfyToken": "Zugriffstoken (optional)",
"channelSaveFailed": "Kanal konnte nicht gespeichert werden",
"test": "Test",
"testSent": "Testbenachrichtigung gesendet",
"testFailed": "Testbenachrichtigung fehlgeschlagen",
"testFailedWithReason": "Testbenachrichtigung fehlgeschlagen: {{reason}}",
"addRule": "Alarmregel hinzufügen",
"editRule": "Alarmregel bearbeiten",
"ruleName": "Regelname",
"ruleNameRequired": "Name ist erforderlich",
"triggerType": "Auslöser",
"thresholdValue": "Schwellenwert (%)",
"durationSeconds": "Dauer (Sekunden, 0 = sofort auslösen)",
"cooldownMinutes": "Abklingzeit (Minuten)",
"channels": "Benachrichtigungskanäle",
"noChannelsHint": "Fügen Sie zuerst Kanäle im Tab 'Kanäle' hinzu",
"ruleSaveFailed": "Regel konnte nicht gespeichert werden"
"channelSaveFailed": "Kanal konnte nicht gespeichert werden"
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Φόρτωση...",
"noData": "Δεν υπάρχουν διαθέσιμα δεδομένα",
"allClear": "Ορίζοντας είναι σαφής",
"acknowledgeAlert": "Αναγνωρίζω",
"noPingUrls": "Δεν έχουν ρυθμιστεί διευθύνσεις URL",
"pingLabel": "Επιγραφή",
"addPingUrl": "Προσθήκη διεύθυνσης URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Ιστορικό διάγραμμα CPU, μνήμης, δίσκου ή δικτύου για έναν κεντρικό υπολογιστή",
"widgetHostGridName": "Πλέγμα κεντρικού υπολογιστή",
"widgetHostGridDesc": "Προβολή πλέγματος των καταστάσεων κεντρικού υπολογιστή SSH",
"widgetAlertFeedName": "Ροή ειδοποιήσεων",
"widgetAlertFeedDesc": "Ενεργοποιήσεις ζωντανών ειδοποιήσεων με υποστήριξη αναγνώρισης",
"widgetPingStatusName": "Κατάσταση Ping",
"widgetPingStatusDesc": "Κατάσταση ping HTTP για μία ή περισσότερες διευθύνσεις URL",
"widgetRecentActivityName": "Πρόσφατη δραστηριότητα",
@@ -747,7 +744,6 @@
"portForwarding": "Προώθηση",
"sftp": "SFTP",
"connections": "Συνδέσεις",
"alerts": "Ειδοποιήσεις",
"serial": "Σειράς",
"keepSideMenuOpen": "Διατήρηση μενού ανοιχτού",
"collapseSideMenu": "Σύμπτυξη μενού",
@@ -4890,21 +4886,6 @@
"viewHistory": "Προβολή ιστορικού"
},
"alerts": {
"tabFirings": "Ειδοποιήσεις",
"tabRules": "Κανόνες",
"tabChannels": "Κανάλια",
"noFirings": "Δεν υπάρχουν μη επιβεβαιωμένες ειδοποιήσεις",
"noRules": "Δεν έχουν ρυθμιστεί κανόνες ειδοποίησης",
"noChannels": "Δεν έχουν ρυθμιστεί κανάλια ειδοποιήσεων",
"rulesDesc": "Οι κανόνες ειδοποίησης ενεργοποιούν ειδοποιήσεις",
"channelsDesc": "Πού αποστέλλονται ειδοποιήσεις",
"acknowledge": "Αναγνωρίζω",
"ackAll": "Επιβεβαίωση όλων",
"allAcknowledged": "Όλες οι ειδοποιήσεις επιβεβαιώθηκαν",
"ackFailed": "Αποτυχία επιβεβαίωσης ειδοποίησης",
"ackAllFailed": "Αποτυχία επιβεβαίωσης όλων των ειδοποιήσεων",
"showAcknowledged": "Εμφάνιση όλων",
"hideAcknowledged": "Απόκρυψη επικυρωμένων",
"addChannel": "Προσθήκη καναλιού",
"editChannel": "Επεξεργασία καναλιού",
"channelName": "Ονομα",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Θέμα",
"ntfyTopicRequired": "Το θέμα είναι υποχρεωτικό",
"ntfyToken": "Διακριτικό πρόσβασης (προαιρετικό)",
"channelSaveFailed": "Η αποθήκευση του καναλιού απέτυχε",
"test": "Δοκιμή",
"testSent": "Αποστολή ειδοποίησης δοκιμής",
"testFailed": "Η ειδοποίηση δοκιμής απέτυχε.",
"testFailedWithReason": "Η ειδοποίηση δοκιμής απέτυχε: {{reason}}",
"addRule": "Προσθήκη κανόνα ειδοποίησης",
"editRule": "Επεξεργασία κανόνα ειδοποίησης",
"ruleName": "Όνομα κανόνα",
"ruleNameRequired": "Απαιτείται όνομα",
"triggerType": "Σκανδάλη",
"thresholdValue": "Όριο (%)",
"durationSeconds": "Διάρκεια (δευτερόλεπτα, 0 = άμεση πυροδότηση)",
"cooldownMinutes": "Χρόνος αναμονής (λεπτά)",
"channels": "Κανάλια ειδοποιήσεων",
"noChannelsHint": "Προσθέστε πρώτα κανάλια στην καρτέλα Κανάλια",
"ruleSaveFailed": "Η αποθήκευση του κανόνα απέτυχε"
"channelSaveFailed": "Η αποθήκευση του καναλιού απέτυχε"
},
"ai": {
"title": "Βοηθός",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Cargando...",
"noData": "Sin datos disponibles",
"allClear": "Todo correcto",
"acknowledgeAlert": "Reconocer",
"noPingUrls": "No hay URLs configuradas",
"pingLabel": "Etiqueta",
"addPingUrl": "Añadir URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Gráfico histórico de CPU, memoria, disco o red para un host",
"widgetHostGridName": "Cuadrícula de hosts",
"widgetHostGridDesc": "Vista en cuadrícula de los estados de los hosts SSH",
"widgetAlertFeedName": "Feed de alertas",
"widgetAlertFeedDesc": "Alertas en tiempo real con soporte para reconocimiento",
"widgetPingStatusName": "Estado de ping",
"widgetPingStatusDesc": "Estado de ping HTTP para una o más URLs",
"widgetRecentActivityName": "Actividad reciente",
@@ -747,7 +744,6 @@
"portForwarding": "Reenvío",
"sftp": "SFTP",
"connections": "Conexiones",
"alerts": "Alertas",
"serial": "Serial",
"keepSideMenuOpen": "Mantener el menú abierto",
"collapseSideMenu": "Contraer menú",
@@ -4890,21 +4886,6 @@
"viewHistory": "Ver Historial"
},
"alerts": {
"tabFirings": "Alertas",
"tabRules": "Reglas",
"tabChannels": "Canales",
"noFirings": "No hay alertas sin reconocer",
"noRules": "No hay reglas de alerta configuradas",
"noChannels": "No hay canales de notificación configurados",
"rulesDesc": "Las reglas de alerta disparan notificaciones",
"channelsDesc": "Dónde se envían las notificaciones de alerta",
"acknowledge": "Reconocer",
"ackAll": "Reconocer todo",
"allAcknowledged": "Todas las alertas reconocidas",
"ackFailed": "Error al reconocer la alerta",
"ackAllFailed": "Error al reconocer todas las alertas",
"showAcknowledged": "Mostrar todas",
"hideAcknowledged": "Ocultar reconocidas",
"addChannel": "Añadir canal",
"editChannel": "Editar canal",
"channelName": "Nombre",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Tema",
"ntfyTopicRequired": "El tema es obligatorio",
"ntfyToken": "Token de acceso (opcional)",
"channelSaveFailed": "No se pudo guardar el canal",
"test": "Probar",
"testSent": "Notificación de prueba enviada",
"testFailed": "La notificación de prueba falló",
"testFailedWithReason": "La notificación de prueba falló: {{reason}}",
"addRule": "Añadir regla de alerta",
"editRule": "Editar regla de alerta",
"ruleName": "Nombre de la regla",
"ruleNameRequired": "El nombre es obligatorio",
"triggerType": "Disparador",
"thresholdValue": "Umbral (%)",
"durationSeconds": "Duración (segundos, 0 = disparar inmediatamente)",
"cooldownMinutes": "Tiempo de espera (minutos)",
"channels": "Canales de notificación",
"noChannelsHint": "Añade primero canales en la pestaña Canales",
"ruleSaveFailed": "No se pudo guardar la regla"
"channelSaveFailed": "No se pudo guardar el canal"
},
"ai": {
"title": "Asistente",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Ladataan...",
"noData": "Ei tietoja saatavilla",
"allClear": "Kaikki selvää",
"acknowledgeAlert": "Kuittaa",
"noPingUrls": "Ei määritettyjä URL-osoitteita",
"pingLabel": "Tarra",
"addPingUrl": "Lisää URL-osoite",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Isännän historiallinen suorittimen, muistin, levyn tai verkon kaavio",
"widgetHostGridName": "Isäntäverkko",
"widgetHostGridDesc": "SSH-isäntätilojen ruudukkonäkymä",
"widgetAlertFeedName": "Hälytyssyöte",
"widgetAlertFeedDesc": "Reaaliaikaiset hälytyslaukaisut kuittaustuella",
"widgetPingStatusName": "Ping-tila",
"widgetPingStatusDesc": "Yhden tai useamman URL-osoitteen HTTP-ping-tila",
"widgetRecentActivityName": "Viimeaikaiset toiminnot",
@@ -747,7 +744,6 @@
"portForwarding": "Edelleenlähetys",
"sftp": "SFTP",
"connections": "Yhteydet",
"alerts": "Hälytykset",
"serial": "Sarjanumero",
"keepSideMenuOpen": "Pidä valikko auki",
"collapseSideMenu": "Pienennä valikko",
@@ -4890,21 +4886,6 @@
"viewHistory": "Näytä historia"
},
"alerts": {
"tabFirings": "Hälytykset",
"tabRules": "Säännöt",
"tabChannels": "Kanavat",
"noFirings": "Ei kuittaamattomia hälytyksiä",
"noRules": "Ei määritettyjä hälytyssääntöjä",
"noChannels": "Ei määritettyjä ilmoituskanavia",
"rulesDesc": "Hälytyssäännöt käynnistävät ilmoituksia",
"channelsDesc": "Minne hälytysilmoitukset lähetetään",
"acknowledge": "Kuittaa",
"ackAll": "Ack All",
"allAcknowledged": "Kaikki hälytykset kuitattu",
"ackFailed": "Hälytyksen kuittaus epäonnistui",
"ackAllFailed": "Kaikkien hälytysten kuittaus epäonnistui",
"showAcknowledged": "Näytä kaikki",
"hideAcknowledged": "Piilota kuitattu",
"addChannel": "Lisää kanava",
"editChannel": "Muokkaa kanavaa",
"channelName": "Nimi",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Aihe",
"ntfyTopicRequired": "Aihe on pakollinen",
"ntfyToken": "Käyttöoikeustunnus (valinnainen)",
"channelSaveFailed": "Kanavan tallentaminen epäonnistui",
"test": "Testata",
"testSent": "Testi-ilmoitus lähetetty",
"testFailed": "Testi-ilmoitus epäonnistui",
"testFailedWithReason": "Testi-ilmoitus epäonnistui: {{reason}}",
"addRule": "Lisää hälytyssääntö",
"editRule": "Muokkaa hälytyssääntöä",
"ruleName": "Säännön nimi",
"ruleNameRequired": "Nimi on pakollinen",
"triggerType": "Laukaista",
"thresholdValue": "Kynnysarvo (%)",
"durationSeconds": "Kesto (sekuntia, 0 = käynnistyy välittömästi)",
"cooldownMinutes": "Jäähdyttelyaika (minuuttia)",
"channels": "Ilmoituskanavat",
"noChannelsHint": "Lisää kanavat ensin Kanavat-välilehdellä",
"ruleSaveFailed": "Säännön tallentaminen epäonnistui"
"channelSaveFailed": "Kanavan tallentaminen epäonnistui"
},
"ai": {
"title": "Assistentti",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Chargement…",
"noData": "Aucune donnée disponible",
"allClear": "Aucune alerte",
"acknowledgeAlert": "Acquitter",
"noPingUrls": "Aucune URL configurée",
"pingLabel": "Libellé",
"addPingUrl": "Ajouter une URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Graphique historique CPU, mémoire, disque ou réseau pour un hôte",
"widgetHostGridName": "Grille d'hôtes",
"widgetHostGridDesc": "Vue en grille des statuts des hôtes SSH",
"widgetAlertFeedName": "Flux d'alertes",
"widgetAlertFeedDesc": "Déclenchements d'alertes en direct avec acquittement",
"widgetPingStatusName": "Statut du ping",
"widgetPingStatusDesc": "Statut du ping HTTP pour une ou plusieurs URL",
"widgetRecentActivityName": "Activité récente",
@@ -747,7 +744,6 @@
"portForwarding": "Expéditeur",
"sftp": "SFTP",
"connections": "Connexions",
"alerts": "Alertes",
"serial": "Série",
"keepSideMenuOpen": "Garder le menu ouvert",
"collapseSideMenu": "Réduire le menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Voir l'historique"
},
"alerts": {
"tabFirings": "Alertes",
"tabRules": "Règles",
"tabChannels": "Canaux",
"noFirings": "Aucune alerte non acquittée",
"noRules": "Aucune règle d'alerte configurée",
"noChannels": "Aucun canal de notification configuré",
"rulesDesc": "Les règles d'alerte déclenchent des notifications",
"channelsDesc": "Où les notifications d'alerte sont envoyées",
"acknowledge": "Acquitter",
"ackAll": "Tout acquitter",
"allAcknowledged": "Toutes les alertes acquittées",
"ackFailed": "Échec de l'acquittement de l'alerte",
"ackAllFailed": "Échec de l'acquittement de toutes les alertes",
"showAcknowledged": "Tout afficher",
"hideAcknowledged": "Masquer les acquittées",
"addChannel": "Ajouter un canal",
"editChannel": "Modifier le canal",
"channelName": "Nom",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Sujet",
"ntfyTopicRequired": "Le sujet est obligatoire",
"ntfyToken": "Jeton d'accès (facultatif)",
"channelSaveFailed": "Échec de l'enregistrement du canal",
"test": "Tester",
"testSent": "Notification de test envoyée",
"testFailed": "Échec de la notification de test",
"testFailedWithReason": "Échec de la notification de test : {{reason}}",
"addRule": "Ajouter une règle d'alerte",
"editRule": "Modifier la règle d'alerte",
"ruleName": "Nom de la règle",
"ruleNameRequired": "Le nom est obligatoire",
"triggerType": "Déclencheur",
"thresholdValue": "Seuil (%)",
"durationSeconds": "Durée (secondes, 0 = déclenchement immédiat)",
"cooldownMinutes": "Temporisation (minutes)",
"channels": "Canaux de notification",
"noChannelsHint": "Ajoutez d'abord des canaux dans l'onglet Canaux",
"ruleSaveFailed": "Échec de l'enregistrement de la règle"
"channelSaveFailed": "Échec de l'enregistrement du canal"
},
"ai": {
"title": "Assistant",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "טְעִינָה...",
"noData": "אין נתונים זמינים",
"allClear": "הכל ברור",
"acknowledgeAlert": "לְהוֹדוֹת",
"noPingUrls": "לא הוגדרו כתובות URL",
"pingLabel": "מַדבֵּקָה",
"addPingUrl": "הוסף כתובת URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "תרשים היסטורי של מעבד, זיכרון, דיסק או רשת עבור מחשב מארח",
"widgetHostGridName": "רשת מארחים",
"widgetHostGridDesc": "תצוגת רשת של סטטוסי מארח SSH",
"widgetAlertFeedName": "הזנת התראות",
"widgetAlertFeedDesc": "ירי התרעה בזמן אמת עם תמיכה באישור",
"widgetPingStatusName": "סטטוס פינג",
"widgetPingStatusDesc": "סטטוס פינג HTTP עבור כתובת URL אחת או יותר",
"widgetRecentActivityName": "פעילות אחרונה",
@@ -747,7 +744,6 @@
"portForwarding": "העברה",
"sftp": "SFTP",
"connections": "חיבורים",
"alerts": "התראות",
"serial": "סִדרָתִי",
"keepSideMenuOpen": "שמור את התפריט פתוח",
"collapseSideMenu": "כווץ תפריט",
@@ -4890,21 +4886,6 @@
"viewHistory": "הצג היסטוריה"
},
"alerts": {
"tabFirings": "התראות",
"tabRules": "כללים",
"tabChannels": "ערוצים",
"noFirings": "אין התראות שלא אושרו",
"noRules": "לא הוגדרו כללי התראות",
"noChannels": "לא הוגדרו ערוצי התראות",
"rulesDesc": "כללי התראות מפעילים התראות",
"channelsDesc": "לאן נשלחות התראות",
"acknowledge": "לְהוֹדוֹת",
"ackAll": "אשר הכל",
"allAcknowledged": "כל ההתראות אושרו",
"ackFailed": "נכשל אישור ההתראה",
"ackAllFailed": "נכשל אישור כל ההתראות",
"showAcknowledged": "הצג הכל",
"hideAcknowledged": "הסתר אישור",
"addChannel": "הוסף ערוץ",
"editChannel": "עריכת ערוץ",
"channelName": "שֵׁם",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "נוֹשֵׂא",
"ntfyTopicRequired": "נושא נדרש",
"ntfyToken": "אסימון גישה (אופציונלי)",
"channelSaveFailed": "שמירת הערוץ נכשלה",
"test": "מִבְחָן",
"testSent": "הודעת בדיקה נשלחה",
"testFailed": "הודעת הבדיקה נכשלה",
"testFailedWithReason": "הודעת הבדיקה נכשלה: {{reason}}",
"addRule": "הוסף כלל התראה",
"editRule": "עריכת כלל התראה",
"ruleName": "שם הכלל",
"ruleNameRequired": "שם נדרש",
"triggerType": "לְהַפְעִיל",
"thresholdValue": "סף (%)",
"durationSeconds": "משך (שניות, 0 = ירי מיידי)",
"cooldownMinutes": "זמן קירור (דקות)",
"channels": "ערוצי התראות",
"noChannelsHint": "הוסף תחילה ערוצים בכרטיסייה ערוצים",
"ruleSaveFailed": "שמירת הכלל נכשלה"
"channelSaveFailed": "שמירת הערוץ נכשלה"
},
"ai": {
"title": "עוֹזֵר",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "लोड हो रहा है...",
"noData": "कोई डेटा उपलब्ध नहीं",
"allClear": "सब ठीक है",
"acknowledgeAlert": "स्वीकार करें",
"noPingUrls": "कोई URL कॉन्फ़िगर नहीं किया गया",
"pingLabel": "लेबल",
"addPingUrl": "URL जोड़ें",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "होस्ट के लिए ऐतिहासिक CPU, मेमोरी, डिस्क या नेटवर्क चार्ट",
"widgetHostGridName": "होस्ट ग्रिड",
"widgetHostGridDesc": "SSH होस्ट की स्थितियों का ग्रिड दृश्य",
"widgetAlertFeedName": "अलर्ट फीड",
"widgetAlertFeedDesc": "स्वीकृति सहित लाइव अलर्ट फायरिंग",
"widgetPingStatusName": "पिंग स्थिति",
"widgetPingStatusDesc": "एक या अधिक URLs के लिए HTTP पिंग स्थिति",
"widgetRecentActivityName": "हाल की गतिविधि",
@@ -747,7 +744,6 @@
"portForwarding": "अग्रेषित करना",
"sftp": "एसएफटीपी",
"connections": "कनेक्शन्स",
"alerts": "अलर्ट्स",
"serial": "सीरियल",
"keepSideMenuOpen": "मेनू खुला रखें",
"collapseSideMenu": "मेनू को छोटा करें",
@@ -4890,21 +4886,6 @@
"viewHistory": "इतिहास देखें"
},
"alerts": {
"tabFirings": "अलर्ट",
"tabRules": "नियम",
"tabChannels": "चैनल",
"noFirings": "कोई अपुष्ट अलर्ट नहीं",
"noRules": "कोई अलर्ट नियम कॉन्फ़िगर नहीं",
"noChannels": "कोई सूचना चैनल कॉन्फ़िगर नहीं",
"rulesDesc": "अलर्ट नियम सूचनाएं ट्रिगर करते हैं",
"channelsDesc": "जहां अलर्ट सूचनाएं भेजी जाती हैं",
"acknowledge": "पुष्टि करें",
"ackAll": "सभी की पुष्टि करें",
"allAcknowledged": "सभी अलर्ट की पुष्टि हो गई",
"ackFailed": "अलर्ट की पुष्टि करने में विफल",
"ackAllFailed": "सभी अलर्ट की पुष्टि करने में विफल",
"showAcknowledged": "सभी दिखाएं",
"hideAcknowledged": "पुष्टि किए गए छिपाएं",
"addChannel": "चैनल जोड़ें",
"editChannel": "चैनल संपादित करें",
"channelName": "नाम",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "विषय",
"ntfyTopicRequired": "विषय आवश्यक है",
"ntfyToken": "एक्सेस टोकन (वैकल्पिक)",
"channelSaveFailed": "चैनल सहेजने में विफल",
"test": "परीक्षण",
"testSent": "परीक्षण सूचना भेजी गई",
"testFailed": "परीक्षण सूचना विफल",
"testFailedWithReason": "परीक्षण अधिसूचना विफल: {{reason}}",
"addRule": "अलर्ट नियम जोड़ें",
"editRule": "अलर्ट नियम संपादित करें",
"ruleName": "नियम नाम",
"ruleNameRequired": "नाम आवश्यक है",
"triggerType": "ट्रिगर",
"thresholdValue": "सीमा (%)",
"durationSeconds": "अवधि (सेकंड, 0 = तुरंत सक्रिय करें)",
"cooldownMinutes": "कूलडाउन (मिनट)",
"channels": "अधिसूचना चैनल",
"noChannelsHint": "पहले चैनल टैब में चैनल जोड़ें",
"ruleSaveFailed": "नियम सहेजने में विफल"
"channelSaveFailed": "चैनल सहेजने में विफल"
},
"ai": {
"title": "सहायक",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Terhelés...",
"noData": "Nincsenek elérhető adatok",
"allClear": "Minden tiszta",
"acknowledgeAlert": "Elismerés",
"noPingUrls": "Nincsenek konfigurált URL-ek",
"pingLabel": "Címke",
"addPingUrl": "URL hozzáadása",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Egy gazdagép CPU-, memória-, lemez- vagy hálózati előzménydiagramja",
"widgetHostGridName": "Host Grid",
"widgetHostGridDesc": "SSH-gazdagépek állapotainak rácsnézete",
"widgetAlertFeedName": "Értesítési hírcsatorna",
"widgetAlertFeedDesc": "Élő riasztási tüzelések nyugtázási támogatással",
"widgetPingStatusName": "Ping állapot",
"widgetPingStatusDesc": "HTTP ping állapot egy vagy több URL-hez",
"widgetRecentActivityName": "Legutóbbi tevékenységek",
@@ -747,7 +744,6 @@
"portForwarding": "Szállítmányozás",
"sftp": "SFTP",
"connections": "Kapcsolatok",
"alerts": "Riasztások",
"serial": "Sorozatszám",
"keepSideMenuOpen": "Menü nyitva tartása",
"collapseSideMenu": "Menü összecsukása",
@@ -4890,21 +4886,6 @@
"viewHistory": "Előzmények megtekintése"
},
"alerts": {
"tabFirings": "Riasztások",
"tabRules": "Szabályok",
"tabChannels": "Csatornák",
"noFirings": "Nincsenek nyugtázatlan riasztások",
"noRules": "Nincsenek riasztási szabályok konfigurálva",
"noChannels": "Nincsenek értesítési csatornák konfigurálva",
"rulesDesc": "Riasztási szabályok aktiválják az értesítéseket",
"channelsDesc": "Hová küldik a riasztási értesítéseket",
"acknowledge": "Elismerés",
"ackAll": "Mindent elfogad",
"allAcknowledged": "Minden riasztás nyugtázva",
"ackFailed": "Nem sikerült visszaigazolni a riasztást",
"ackAllFailed": "Nem sikerült nyugtázni az összes riasztást",
"showAcknowledged": "Összes megjelenítése",
"hideAcknowledged": "Elrejtés Elfogadva",
"addChannel": "Csatorna hozzáadása",
"editChannel": "Csatorna szerkesztése",
"channelName": "Név",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Téma",
"ntfyTopicRequired": "A téma megadása kötelező",
"ntfyToken": "Hozzáférési token (opcionális)",
"channelSaveFailed": "Nem sikerült menteni a csatornát",
"test": "Teszt",
"testSent": "Tesztértesítés elküldve",
"testFailed": "Tesztértesítés sikertelen",
"testFailedWithReason": "Tesztértesítés sikertelen: {{reason}}",
"addRule": "Riasztási szabály hozzáadása",
"editRule": "Riasztási szabály szerkesztése",
"ruleName": "Szabály neve",
"ruleNameRequired": "Név megadása kötelező",
"triggerType": "Trigger",
"thresholdValue": "Küszöbérték (%)",
"durationSeconds": "Időtartam (másodperc, 0 = azonnali aktiválás)",
"cooldownMinutes": "Lehűlési idő (perc)",
"channels": "Értesítési csatornák",
"noChannelsHint": "Először adj hozzá csatornákat a Csatornák lapon",
"ruleSaveFailed": "Nem sikerült menteni a szabályt"
"channelSaveFailed": "Nem sikerült menteni a csatornát"
},
"ai": {
"title": "Helyettes",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Memuat...",
"noData": "Tidak ada data yang tersedia",
"allClear": "Semuanya aman.",
"acknowledgeAlert": "Mengakui",
"noPingUrls": "Tidak ada URL yang dikonfigurasi",
"pingLabel": "Label",
"addPingUrl": "Tambahkan URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Grafik historis CPU, memori, disk, atau jaringan untuk sebuah host.",
"widgetHostGridName": "Jaringan Host",
"widgetHostGridDesc": "Tampilan grid status host SSH",
"widgetAlertFeedName": "Umpan Peringatan",
"widgetAlertFeedDesc": "Peringatan penembakan langsung dengan dukungan konfirmasi.",
"widgetPingStatusName": "Status Ping",
"widgetPingStatusDesc": "Status ping HTTP untuk satu atau lebih URL",
"widgetRecentActivityName": "Aktivitas Terkini",
@@ -747,7 +744,6 @@
"portForwarding": "Meneruskan",
"sftp": "SFTP",
"connections": "Koneksi",
"alerts": "Peringatan",
"serial": "Serial",
"keepSideMenuOpen": "Biarkan Menu Tetap Terbuka",
"collapseSideMenu": "Ciutkan Menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Lihat Riwayat"
},
"alerts": {
"tabFirings": "Peringatan",
"tabRules": "Aturan",
"tabChannels": "Saluran",
"noFirings": "Tidak ada peringatan yang belum diakui",
"noRules": "Tidak ada aturan peringatan yang dikonfigurasi.",
"noChannels": "Tidak ada saluran notifikasi yang dikonfigurasi.",
"rulesDesc": "Aturan peringatan memicu notifikasi.",
"channelsDesc": "Di mana notifikasi peringatan dikirim",
"acknowledge": "Mengakui",
"ackAll": "Akui semuanya",
"allAcknowledged": "Semua peringatan telah diterima.",
"ackFailed": "Gagal menanggapi peringatan",
"ackAllFailed": "Gagal menanggapi semua peringatan",
"showAcknowledged": "Tampilkan Semua",
"hideAcknowledged": "Sembunyikan Terakui",
"addChannel": "Tambahkan Saluran",
"editChannel": "Edit Saluran",
"channelName": "Nama",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Topik",
"ntfyTopicRequired": "Topik wajib diisi",
"ntfyToken": "Token Akses (opsional)",
"channelSaveFailed": "Gagal menyimpan saluran",
"test": "Tes",
"testSent": "Notifikasi pengujian telah dikirim.",
"testFailed": "Notifikasi pengujian gagal",
"testFailedWithReason": "Notifikasi pengujian gagal: {{reason}}",
"addRule": "Tambahkan Aturan Peringatan",
"editRule": "Edit Aturan Peringatan",
"ruleName": "Nama Aturan",
"ruleNameRequired": "Nama wajib diisi.",
"triggerType": "Pemicu",
"thresholdValue": "Ambang batas (%)",
"durationSeconds": "Durasi (detik, 0 = tembak segera)",
"cooldownMinutes": "Waktu pendinginan (menit)",
"channels": "Saluran Pemberitahuan",
"noChannelsHint": "Tambahkan saluran di tab Saluran terlebih dahulu.",
"ruleSaveFailed": "Gagal menyimpan aturan"
"channelSaveFailed": "Gagal menyimpan saluran"
},
"ai": {
"title": "Asisten",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Caricamento...",
"noData": "Nessun dato disponibile",
"allClear": "Tutto a posto",
"acknowledgeAlert": "Riconosci",
"noPingUrls": "Nessun URL configurato",
"pingLabel": "Etichetta",
"addPingUrl": "Aggiungi URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Grafico storico di CPU, memoria, disco o rete per un host",
"widgetHostGridName": "Griglia host",
"widgetHostGridDesc": "Vista a griglia degli stati degli host SSH",
"widgetAlertFeedName": "Feed degli avvisi",
"widgetAlertFeedDesc": "Avvisi in tempo reale con supporto di riconoscimento",
"widgetPingStatusName": "Stato ping",
"widgetPingStatusDesc": "Stato del ping HTTP per uno o più URL",
"widgetRecentActivityName": "Attività recente",
@@ -747,7 +744,6 @@
"portForwarding": "Inoltro",
"sftp": "SFTP",
"connections": "Connessioni",
"alerts": "Avvisi",
"serial": "Seriale",
"keepSideMenuOpen": "Mantieni il menu aperto",
"collapseSideMenu": "Comprimi menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Visualizza cronologia"
},
"alerts": {
"tabFirings": "Avvisi",
"tabRules": "Regole",
"tabChannels": "Canali",
"noFirings": "Nessun avviso non confermato",
"noRules": "Nessuna regola di avviso configurata",
"noChannels": "Nessun canale di notifica configurato",
"rulesDesc": "Le regole di avviso attivano le notifiche",
"channelsDesc": "Dove vengono inviate le notifiche di avviso",
"acknowledge": "Conferma",
"ackAll": "Conferma tutto",
"allAcknowledged": "Tutti gli avvisi confermati",
"ackFailed": "Conferma dell'avviso non riuscita",
"ackAllFailed": "Conferma di tutti gli avvisi non riuscita",
"showAcknowledged": "Mostra tutti",
"hideAcknowledged": "Nascondi confermati",
"addChannel": "Aggiungi canale",
"editChannel": "Modifica canale",
"channelName": "Nome",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Argomento",
"ntfyTopicRequired": "Argomento obbligatorio",
"ntfyToken": "Token di accesso (facoltativo)",
"channelSaveFailed": "Salvataggio del canale non riuscito",
"test": "Test",
"testSent": "Notifica di test inviata",
"testFailed": "Notifica di test fallita",
"testFailedWithReason": "Notifica di prova non riuscita: {{reason}}",
"addRule": "Aggiungi regola di avviso",
"editRule": "Modifica regola di avviso",
"ruleName": "Nome regola",
"ruleNameRequired": "Nome obbligatorio",
"triggerType": "Trigger",
"thresholdValue": "Soglia (%)",
"durationSeconds": "Durata (secondi, 0 = attiva immediatamente)",
"cooldownMinutes": "Cooldown (minuti)",
"channels": "Canali di notifica",
"noChannelsHint": "Aggiungi prima i canali nella scheda Canali",
"ruleSaveFailed": "Salvataggio della regola non riuscito"
"channelSaveFailed": "Salvataggio del canale non riuscito"
},
"ai": {
"title": "Assistente",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "読み込み中...",
"noData": "利用可能なデータはありません",
"allClear": "すべて正常",
"acknowledgeAlert": "確認",
"noPingUrls": "URLが設定されていません",
"pingLabel": "ラベル",
"addPingUrl": "URLを追加",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "ホストのCPU、メモリ、ディスク、またはネットワークの履歴チャート",
"widgetHostGridName": "ホストグリッド",
"widgetHostGridDesc": "SSHホストステータスのグリッドビュー",
"widgetAlertFeedName": "アラートフィード",
"widgetAlertFeedDesc": "確認機能付きのライブアラート通知",
"widgetPingStatusName": "Pingステータス",
"widgetPingStatusDesc": "1つ以上のURLのHTTP pingステータス",
"widgetRecentActivityName": "最近のアクティビティ",
@@ -747,7 +744,6 @@
"portForwarding": "転送",
"sftp": "SFTP",
"connections": "接続",
"alerts": "アラート",
"serial": "シリアル",
"keepSideMenuOpen": "メニューを開いたままにする",
"collapseSideMenu": "メニューを折りたたむ",
@@ -4890,21 +4886,6 @@
"viewHistory": "履歴を表示"
},
"alerts": {
"tabFirings": "アラート",
"tabRules": "ルール",
"tabChannels": "チャンネル",
"noFirings": "未確認のアラートはありません",
"noRules": "アラートルールが設定されていません",
"noChannels": "通知チャンネルが設定されていません",
"rulesDesc": "アラートルールは通知をトリガーします",
"channelsDesc": "アラート通知の送信先です",
"acknowledge": "確認",
"ackAll": "すべて確認",
"allAcknowledged": "すべてのアラートが確認済みです",
"ackFailed": "アラートの確認に失敗しました",
"ackAllFailed": "すべてのアラートの確認に失敗しました",
"showAcknowledged": "すべて表示",
"hideAcknowledged": "確認済みを非表示",
"addChannel": "チャンネルを追加",
"editChannel": "チャンネルを編集",
"channelName": "名前",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "トピック",
"ntfyTopicRequired": "トピックは必須です",
"ntfyToken": "アクセストークン(任意)",
"channelSaveFailed": "チャンネルの保存に失敗しました",
"test": "テスト",
"testSent": "テスト通知を送信しました",
"testFailed": "テスト通知に失敗しました",
"testFailedWithReason": "テスト通知が失敗しました: {{reason}}",
"addRule": "アラートルールを追加",
"editRule": "アラートルールを編集",
"ruleName": "ルール名",
"ruleNameRequired": "ルール名は必須です",
"triggerType": "トリガー",
"thresholdValue": "しきい値 (%)",
"durationSeconds": "継続時間(秒、0は即時発火)",
"cooldownMinutes": "クールダウン(分)",
"channels": "通知チャンネル",
"noChannelsHint": "先にチャンネルタブでチャンネルを追加してください",
"ruleSaveFailed": "ルールの保存に失敗しました"
"channelSaveFailed": "チャンネルの保存に失敗しました"
},
"ai": {
"title": "アシスタント",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "로딩 중...",
"noData": "데이터 없음",
"allClear": "모두 정상",
"acknowledgeAlert": "확인",
"noPingUrls": "구성된 URL 없음",
"pingLabel": "레이블",
"addPingUrl": "URL 추가",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "호스트의 CPU, 메모리, 디스크, 네트워크 기록 차트",
"widgetHostGridName": "호스트 그리드",
"widgetHostGridDesc": "SSH 호스트 상태 그리드 뷰",
"widgetAlertFeedName": "알림 피드",
"widgetAlertFeedDesc": "실시간 알림 및 확인 지원",
"widgetPingStatusName": "Ping 상태",
"widgetPingStatusDesc": "하나 이상의 URL에 대한 HTTP 핑 상태",
"widgetRecentActivityName": "최근 활동",
@@ -747,7 +744,6 @@
"portForwarding": "전송",
"sftp": "SFTP",
"connections": "연결",
"alerts": "알림",
"serial": "시리얼",
"keepSideMenuOpen": "메뉴를 열어 두세요",
"collapseSideMenu": "메뉴 접기",
@@ -4890,21 +4886,6 @@
"viewHistory": "기록 보기"
},
"alerts": {
"tabFirings": "알림",
"tabRules": "규칙",
"tabChannels": "채널",
"noFirings": "미확인 알림 없음",
"noRules": "구성된 알림 규칙 없음",
"noChannels": "구성된 알림 채널 없음",
"rulesDesc": "알림 규칙은 알림을 트리거합니다",
"channelsDesc": "알림이 전송되는 위치",
"acknowledge": "확인",
"ackAll": "모두 확인",
"allAcknowledged": "모든 알림 확인됨",
"ackFailed": "알림 확인 실패",
"ackAllFailed": "모든 알림 확인 실패",
"showAcknowledged": "모두 표시",
"hideAcknowledged": "확인된 항목 숨기기",
"addChannel": "채널 추가",
"editChannel": "채널 편집",
"channelName": "이름",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "토픽",
"ntfyTopicRequired": "토픽은 필수입니다",
"ntfyToken": "액세스 토큰 (선택 사항)",
"channelSaveFailed": "채널 저장에 실패했습니다",
"test": "테스트",
"testSent": "테스트 알림이 전송되었습니다",
"testFailed": "테스트 알림 전송에 실패했습니다",
"testFailedWithReason": "테스트 알림 실패: {{reason}}",
"addRule": "알람 규칙 추가",
"editRule": "알람 규칙 편집",
"ruleName": "규칙 이름",
"ruleNameRequired": "이름은 필수입니다",
"triggerType": "트리거",
"thresholdValue": "임계값 (%)",
"durationSeconds": "지속 시간 (초, 0 = 즉시 발생)",
"cooldownMinutes": "쿨다운 (분)",
"channels": "알림 채널",
"noChannelsHint": "먼저 채널 탭에서 채널을 추가하세요",
"ruleSaveFailed": "규칙 저장에 실패했습니다"
"channelSaveFailed": "채널 저장에 실패했습니다"
},
"ai": {
"title": "어시스턴트",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Laden...",
"noData": "Geen gegevens beschikbaar",
"allClear": "Alles in orde",
"acknowledgeAlert": "Erkennen",
"noPingUrls": "Geen URL's geconfigureerd",
"pingLabel": "Label",
"addPingUrl": "URL toevoegen",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historische grafiek van CPU, geheugen, schijf of netwerk voor een host.",
"widgetHostGridName": "Host Grid",
"widgetHostGridDesc": "Rasterweergave van de status van SSH-hosts",
"widgetAlertFeedName": "Waarschuwingsfeed",
"widgetAlertFeedDesc": "Live waarschuwingen met bevestigingsondersteuning",
"widgetPingStatusName": "Pingstatus",
"widgetPingStatusDesc": "HTTP-pingstatus voor een of meer URL's",
"widgetRecentActivityName": "Recente activiteit",
@@ -747,7 +744,6 @@
"portForwarding": "Doorsturen",
"sftp": "SFTP",
"connections": "Verbindingen",
"alerts": "Waarschuwingen",
"serial": "Serieel",
"keepSideMenuOpen": "Menu open houden",
"collapseSideMenu": "Menu inklappen",
@@ -4890,21 +4886,6 @@
"viewHistory": "Geschiedenis bekijken"
},
"alerts": {
"tabFirings": "Waarschuwingen",
"tabRules": "Regels",
"tabChannels": "Kanalen",
"noFirings": "Geen niet-bevestigde meldingen",
"noRules": "Er zijn geen waarschuwingsregels geconfigureerd.",
"noChannels": "Er zijn geen notificatiekanalen geconfigureerd.",
"rulesDesc": "Waarschuwingsregels activeren meldingen.",
"channelsDesc": "Waar waarschuwingsmeldingen worden verzonden",
"acknowledge": "Erkennen",
"ackAll": "Bevestig alles",
"allAcknowledged": "Alle meldingen bevestigd",
"ackFailed": "Melding niet ontvangen",
"ackAllFailed": "Niet alle meldingen zijn bevestigd.",
"showAcknowledged": "Alles weergeven",
"hideAcknowledged": "Verberg Acked",
"addChannel": "Kanaal toevoegen",
"editChannel": "Bewerk kanaal",
"channelName": "Naam",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Onderwerp",
"ntfyTopicRequired": "Onderwerp is verplicht",
"ntfyToken": "Toegangstoken (optioneel)",
"channelSaveFailed": "Het opslaan van het kanaal is mislukt.",
"test": "Test",
"testSent": "Testmelding verzonden",
"testFailed": "Testmelding mislukt",
"testFailedWithReason": "Testmelding mislukt: {{reason}}",
"addRule": "Voeg een waarschuwingsregel toe",
"editRule": "Regel voor waarschuwing bewerken",
"ruleName": "Regelnaam",
"ruleNameRequired": "Naam is verplicht",
"triggerType": "Trekker",
"thresholdValue": "Drempelwaarde (%)",
"durationSeconds": "Duur (seconden, 0 = direct vuren)",
"cooldownMinutes": "Afkoeltijd (minuten)",
"channels": "Meldingskanalen",
"noChannelsHint": "Voeg eerst kanalen toe via het tabblad Kanalen.",
"ruleSaveFailed": "Het opslaan van de regel is mislukt."
"channelSaveFailed": "Het opslaan van het kanaal is mislukt."
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Laster inn...",
"noData": "Ingen data tilgjengelig",
"allClear": "Alt klart",
"acknowledgeAlert": "Bekrefte",
"noPingUrls": "Ingen nettadresser konfigurert",
"pingLabel": "Merkelapp",
"addPingUrl": "Legg til nettadresse",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historisk CPU-, minne-, disk- eller nettverksdiagram for en vert",
"widgetHostGridName": "Vertsnett",
"widgetHostGridDesc": "Rutenettvisning av SSH-vertstatuser",
"widgetAlertFeedName": "Varselstrøm",
"widgetAlertFeedDesc": "Live-varslingsutløsninger med bekreftelsesstøtte",
"widgetPingStatusName": "Ping-status",
"widgetPingStatusDesc": "HTTP-pingstatus for én eller flere URL-er",
"widgetRecentActivityName": "Nylig aktivitet",
@@ -747,7 +744,6 @@
"portForwarding": "Videresending",
"sftp": "SFTP",
"connections": "Tilkoblinger",
"alerts": "Varsler",
"serial": "Seriell",
"keepSideMenuOpen": "Hold menyen åpen",
"collapseSideMenu": "Skjul menyen",
@@ -4890,21 +4886,6 @@
"viewHistory": "Vis historikk"
},
"alerts": {
"tabFirings": "Varsler",
"tabRules": "Regler",
"tabChannels": "Kanaler",
"noFirings": "Ingen ubekreftede varsler",
"noRules": "Ingen varslingsregler konfigurert",
"noChannels": "Ingen varslingskanaler konfigurert",
"rulesDesc": "Varslingsregler utløser varsler",
"channelsDesc": "Hvor varsler sendes",
"acknowledge": "Bekrefte",
"ackAll": "Bekreft alle",
"allAcknowledged": "Alle varsler bekreftet",
"ackFailed": "Kunne ikke bekrefte varselet",
"ackAllFailed": "Kunne ikke bekrefte alle varsler",
"showAcknowledged": "Vis alle",
"hideAcknowledged": "Skjul bekreftet",
"addChannel": "Legg til kanal",
"editChannel": "Rediger kanal",
"channelName": "Navn",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Emne",
"ntfyTopicRequired": "Emne er obligatorisk",
"ntfyToken": "Tilgangstoken (valgfritt)",
"channelSaveFailed": "Kunne ikke lagre kanalen",
"test": "Test",
"testSent": "Testvarsel sendt",
"testFailed": "Testvarsel mislyktes",
"testFailedWithReason": "Testvarsling mislyktes: {{reason}}",
"addRule": "Legg til varslingsregel",
"editRule": "Rediger varslingsregel",
"ruleName": "Regelnavn",
"ruleNameRequired": "Navn er obligatorisk",
"triggerType": "Avtrekker",
"thresholdValue": "Terskel (%)",
"durationSeconds": "Varighet (sekunder, 0 = avfyr umiddelbart)",
"cooldownMinutes": "Nedkjølingstid (minutter)",
"channels": "Varslingskanaler",
"noChannelsHint": "Legg først til kanaler i Kanaler-fanen",
"ruleSaveFailed": "Kunne ikke lagre regelen"
"channelSaveFailed": "Kunne ikke lagre kanalen"
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Ładowanie...",
"noData": "Brak dostępnych danych",
"allClear": "Bez alertów",
"acknowledgeAlert": "Potwierdź",
"noPingUrls": "Brak skonfigurowanych adresów URL",
"pingLabel": "Etykieta",
"addPingUrl": "Dodaj adres URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historyczny wykres procesora, pamięci, dysku lub sieci dla hosta",
"widgetHostGridName": "Siatka hostów",
"widgetHostGridDesc": "Widok siatki statusów hostów SSH",
"widgetAlertFeedName": "Kanał alertów",
"widgetAlertFeedDesc": "Wyzwolone alerty na żywo z możliwością potwierdzenia",
"widgetPingStatusName": "Status pingu",
"widgetPingStatusDesc": "Status pingu HTTP dla jednego lub większej liczby adresów URL",
"widgetRecentActivityName": "Ostatnia aktywność",
@@ -747,7 +744,6 @@
"portForwarding": "Przekierowanie",
"sftp": "SFTP",
"connections": "Połączenia",
"alerts": "Alerty",
"serial": "Port szeregowy",
"keepSideMenuOpen": "Zachowaj otwarte menu",
"collapseSideMenu": "Zwiń menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Wyświetl historię"
},
"alerts": {
"tabFirings": "Alerty",
"tabRules": "Reguły",
"tabChannels": "Kanały",
"noFirings": "Brak niepotwierdzonych alertów",
"noRules": "Nie skonfigurowano żadnych reguł alertów",
"noChannels": "Brak skonfigurowanych kanałów powiadomień",
"rulesDesc": "Reguły alertów wyzwalają powiadomienia",
"channelsDesc": "Gdzie wysyłane są powiadomienia o alertach",
"acknowledge": "Potwierdź",
"ackAll": "Potwierdź wszystkie",
"allAcknowledged": "Wszystkie alerty potwierdzone",
"ackFailed": "Nie udało się potwierdzić alertu",
"ackAllFailed": "Nie udało się potwierdzić wszystkich alertów",
"showAcknowledged": "Pokaż wszystkie",
"hideAcknowledged": "Ukryj potwierdzone",
"addChannel": "Dodaj kanał",
"editChannel": "Edytuj kanał",
"channelName": "Nazwa",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Temat",
"ntfyTopicRequired": "Temat jest wymagany",
"ntfyToken": "Token dostępu (opcjonalnie)",
"channelSaveFailed": "Nie udało się zapisać kanału",
"test": "Test",
"testSent": "Wysłano powiadomienie testowe",
"testFailed": "Powiadomienie testowe nie powiodło się",
"testFailedWithReason": "Powiadomienie testowe nie powiodło się: {{reason}}",
"addRule": "Dodaj regułę alertu",
"editRule": "Edytuj regułę alertu",
"ruleName": "Nazwa reguły",
"ruleNameRequired": "Nazwa jest wymagana",
"triggerType": "Wyzwalacz",
"thresholdValue": "Próg (%)",
"durationSeconds": "Czas trwania (sekundy, 0 = wyzwól natychmiast)",
"cooldownMinutes": "Czas wyciszenia (minuty)",
"channels": "Kanały powiadomień",
"noChannelsHint": "Najpierw dodaj kanały na karcie Kanały",
"ruleSaveFailed": "Nie udało się zapisać reguły"
"channelSaveFailed": "Nie udało się zapisać kanału"
},
"ai": {
"title": "Asystent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Carregando...",
"noData": "Não há dados disponíveis.",
"allClear": "Tudo certo",
"acknowledgeAlert": "Reconhecer",
"noPingUrls": "Nenhuma URL configurada",
"pingLabel": "Rótulo",
"addPingUrl": "Adicionar URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Gráfico histórico de CPU, memória, disco ou rede para um host",
"widgetHostGridName": "Grade de anfitriões",
"widgetHostGridDesc": "Visualização em grade dos status dos hosts SSH",
"widgetAlertFeedName": "Feed de alertas",
"widgetAlertFeedDesc": "Disparos de alertas em tempo real com suporte de confirmação",
"widgetPingStatusName": "Status do ping",
"widgetPingStatusDesc": "Status do ping HTTP para um ou mais URLs",
"widgetRecentActivityName": "Atividade recente",
@@ -747,7 +744,6 @@
"portForwarding": "Encaminhamento",
"sftp": "SFTP",
"connections": "Conexões",
"alerts": "Alertas",
"serial": "Serial",
"keepSideMenuOpen": "Mantenha o menu aberto",
"collapseSideMenu": "Recolher menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Ver histórico"
},
"alerts": {
"tabFirings": "Alertas",
"tabRules": "Regras",
"tabChannels": "Canais",
"noFirings": "Nenhum alerta não reconhecido",
"noRules": "Nenhuma regra de alerta configurada",
"noChannels": "Nenhum canal de notificação configurado",
"rulesDesc": "As regras de alerta acionam notificações.",
"channelsDesc": "Para onde são enviadas as notificações de alerta",
"acknowledge": "Reconhecer",
"ackAll": "Concordo com tudo",
"allAcknowledged": "Todos os alertas foram reconhecidos.",
"ackFailed": "Falha em reconhecer o alerta",
"ackAllFailed": "Não reconheceu todos os alertas.",
"showAcknowledged": "Mostrar tudo",
"hideAcknowledged": "Esconder Acked",
"addChannel": "Adicionar canal",
"editChannel": "Editar canal",
"channelName": "Nome",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Tópico",
"ntfyTopicRequired": "É necessário definir o tema.",
"ntfyToken": "Token de acesso (opcional)",
"channelSaveFailed": "Falha ao salvar o canal",
"test": "Teste",
"testSent": "Notificação de teste enviada",
"testFailed": "Falha na notificação de teste",
"testFailedWithReason": "Falha na notificação de teste: {{reason}}",
"addRule": "Adicionar regra de alerta",
"editRule": "Editar regra de alerta",
"ruleName": "Nome da regra",
"ruleNameRequired": "O nome é obrigatório.",
"triggerType": "Acionar",
"thresholdValue": "Limite (%)",
"durationSeconds": "Duração (segundos, 0 = disparar imediatamente)",
"cooldownMinutes": "Tempo de resfriamento (minutos)",
"channels": "Canais de notificação",
"noChannelsHint": "Adicione os canais primeiro na aba Canais.",
"ruleSaveFailed": "Falha ao salvar a regra"
"channelSaveFailed": "Falha ao salvar o canal"
},
"ai": {
"title": "Assistente",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "A carregar...",
"noData": "Sem dados disponíveis",
"allClear": "Tudo em ordem",
"acknowledgeAlert": "Reconhecer",
"noPingUrls": "Nenhum URL configurado",
"pingLabel": "Etiqueta",
"addPingUrl": "Adicionar URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Gráfico histórico de CPU, memória, disco ou rede para um host",
"widgetHostGridName": "Grelha de hosts",
"widgetHostGridDesc": "Vista em grelha dos estados dos hosts SSH",
"widgetAlertFeedName": "Feed de alertas",
"widgetAlertFeedDesc": "Disparos de alertas em tempo real com suporte a reconhecimento",
"widgetPingStatusName": "Estado do ping",
"widgetPingStatusDesc": "Estado do ping HTTP para um ou mais URLs",
"widgetRecentActivityName": "Atividade recente",
@@ -747,7 +744,6 @@
"portForwarding": "Encaminhamento",
"sftp": "SFTP",
"connections": "Ligações",
"alerts": "Alertas",
"serial": "Serial",
"keepSideMenuOpen": "Mantenha o menu aberto",
"collapseSideMenu": "Recolher menu",
@@ -4890,21 +4886,6 @@
"viewHistory": "Ver Histórico"
},
"alerts": {
"tabFirings": "Alertas",
"tabRules": "Regras",
"tabChannels": "Canais",
"noFirings": "Nenhum alerta por reconhecer",
"noRules": "Nenhuma regra de alerta configurada",
"noChannels": "Nenhum canal de notificação configurado",
"rulesDesc": "As regras de alerta disparam notificações",
"channelsDesc": "Onde as notificações de alerta são enviadas",
"acknowledge": "Reconhecer",
"ackAll": "Reconhecer Todos",
"allAcknowledged": "Todos os alertas reconhecidos",
"ackFailed": "Falha ao reconhecer o alerta",
"ackAllFailed": "Falha ao reconhecer todos os alertas",
"showAcknowledged": "Mostrar Todos",
"hideAcknowledged": "Ocultar Reconhecidos",
"addChannel": "Adicionar Canal",
"editChannel": "Editar Canal",
"channelName": "Nome",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Tópico",
"ntfyTopicRequired": "O tópico é obrigatório",
"ntfyToken": "Token de acesso (opcional)",
"channelSaveFailed": "Falha ao guardar o canal",
"test": "Testar",
"testSent": "Notificação de teste enviada",
"testFailed": "Falha na notificação de teste",
"testFailedWithReason": "Falha na notificação de teste: {{reason}}",
"addRule": "Adicionar regra de alerta",
"editRule": "Editar regra de alerta",
"ruleName": "Nome da regra",
"ruleNameRequired": "O nome é obrigatório",
"triggerType": "Gatilho",
"thresholdValue": "Limiar (%)",
"durationSeconds": "Duração (segundos, 0 = disparar imediatamente)",
"cooldownMinutes": "Período de arrefecimento (minutos)",
"channels": "Canais de notificação",
"noChannelsHint": "Adicione canais primeiro no separador Canais",
"ruleSaveFailed": "Falha ao guardar a regra"
"channelSaveFailed": "Falha ao guardar o canal"
},
"ai": {
"title": "Assistente",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Încărcare...",
"noData": "Nu sunt disponibile date",
"allClear": "Totul este clar",
"acknowledgeAlert": "Recunoaște",
"noPingUrls": "Nicio adresă URL configurată",
"pingLabel": "Eticheta",
"addPingUrl": "Adăugați adresa URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Diagramă istorică CPU, memorie, disc sau rețea pentru o gazdă",
"widgetHostGridName": "Grilă gazdă",
"widgetHostGridDesc": "Vizualizare grilă a stărilor gazdelor SSH",
"widgetAlertFeedName": "Flux de alerte",
"widgetAlertFeedDesc": "Declanșări în direct cu asistență pentru confirmare",
"widgetPingStatusName": "Starea ping-ului",
"widgetPingStatusDesc": "Starea ping-ului HTTP pentru una sau mai multe adrese URL",
"widgetRecentActivityName": "Activitate recentă",
@@ -747,7 +744,6 @@
"portForwarding": "Redirecționare",
"sftp": "SFTP",
"connections": "Conexiuni",
"alerts": "Alerte",
"serial": "Serial",
"keepSideMenuOpen": "Mențineți meniul deschis",
"collapseSideMenu": "Restrângeți meniul",
@@ -4890,21 +4886,6 @@
"viewHistory": "Vizualizați istoricul"
},
"alerts": {
"tabFirings": "Alerte",
"tabRules": "Reguli",
"tabChannels": "Canale",
"noFirings": "Nicio alertă neconfirmată",
"noRules": "Nicio regulă de alertă configurată",
"noChannels": "Nu sunt configurate canale de notificare",
"rulesDesc": "Regulile de alertă declanșează notificări",
"channelsDesc": "Unde sunt trimise notificările de alertă",
"acknowledge": "Recunoaște",
"ackAll": "Confirmare tuturor",
"allAcknowledged": "Toate alertele au fost confirmate",
"ackFailed": "Nu s-a putut confirma alerta",
"ackAllFailed": "Nu s-au putut confirma toate alertele",
"showAcknowledged": "Afișează tot",
"hideAcknowledged": "Ascundeți confirmarea",
"addChannel": "Adăugați canal",
"editChannel": "Editați canalul",
"channelName": "Nume",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Subiect",
"ntfyTopicRequired": "Subiectul este obligatoriu",
"ntfyToken": "Token de acces (opțional)",
"channelSaveFailed": "Canalul nu a putut fi salvat.",
"test": "Test",
"testSent": "Notificare de testare trimisă",
"testFailed": "Notificarea de testare a eșuat",
"testFailedWithReason": "Notificarea de testare a eșuat: {{reason}}",
"addRule": "Adăugați o regulă de alertă",
"editRule": "Editați regula de alertă",
"ruleName": "Nume regulă",
"ruleNameRequired": "Numele este obligatoriu",
"triggerType": "Declanșator",
"thresholdValue": "Prag (%)",
"durationSeconds": "Durată (secunde, 0 = declanșează imediat)",
"cooldownMinutes": "Timp de răcire (minute)",
"channels": "Canale de notificare",
"noChannelsHint": "Adăugați mai întâi canale în fila Canale",
"ruleSaveFailed": "Nu s-a putut salva regula"
"channelSaveFailed": "Canalul nu a putut fi salvat."
},
"ai": {
"title": "Asistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Загрузка...",
"noData": "Нет данных",
"allClear": "Всё в порядке",
"acknowledgeAlert": "Подтвердить",
"noPingUrls": "URL-адреса не настроены",
"pingLabel": "Метка",
"addPingUrl": "Добавить URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "График истории использования ЦП, памяти, диска или сети для хоста",
"widgetHostGridName": "Сетка хостов",
"widgetHostGridDesc": "Представление статусов хостов SSH в виде сетки",
"widgetAlertFeedName": "Лента оповещений",
"widgetAlertFeedDesc": "Живые срабатывания оповещений с возможностью подтверждения",
"widgetPingStatusName": "Статус ping",
"widgetPingStatusDesc": "Статус HTTP ping для одного или нескольких URL",
"widgetRecentActivityName": "Недавняя активность",
@@ -747,7 +744,6 @@
"portForwarding": "Пересылка",
"sftp": "SFTP",
"connections": "Подключения",
"alerts": "Оповещения",
"serial": "Последовательный порт",
"keepSideMenuOpen": "Оставить меню открытым",
"collapseSideMenu": "Свернуть меню",
@@ -4890,21 +4886,6 @@
"viewHistory": "Просмотреть историю"
},
"alerts": {
"tabFirings": "Оповещения",
"tabRules": "Правила",
"tabChannels": "Каналы",
"noFirings": "Нет неподтверждённых оповещений",
"noRules": "Не настроены правила оповещений",
"noChannels": "Не настроены каналы уведомлений",
"rulesDesc": "Правила оповещений инициируют уведомления",
"channelsDesc": "Куда отправляются уведомления об оповещениях",
"acknowledge": "Подтвердить",
"ackAll": "Подтвердить все",
"allAcknowledged": "Все оповещения подтверждены",
"ackFailed": "Не удалось подтвердить оповещение",
"ackAllFailed": "Не удалось подтвердить все оповещения",
"showAcknowledged": "Показать все",
"hideAcknowledged": "Скрыть подтверждённые",
"addChannel": "Добавить канал",
"editChannel": "Изменить канал",
"channelName": "Название",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Тема",
"ntfyTopicRequired": "Тема обязательна",
"ntfyToken": "Токен доступа (необязательно)",
"channelSaveFailed": "Не удалось сохранить канал",
"test": "Проверить",
"testSent": "Тестовое уведомление отправлено",
"testFailed": "Ошибка отправки тестового уведомления",
"testFailedWithReason": "Тестовое уведомление не пройдено: {{reason}}",
"addRule": "Добавить правило оповещения",
"editRule": "Редактировать правило оповещения",
"ruleName": "Имя правила",
"ruleNameRequired": "Имя обязательно",
"triggerType": "Триггер",
"thresholdValue": "Порог (%)",
"durationSeconds": "Длительность (секунд, 0 = сработать немедленно)",
"cooldownMinutes": "Задержка повторения (минут)",
"channels": "Каналы уведомлений",
"noChannelsHint": "Сначала добавьте каналы на вкладке «Каналы»",
"ruleSaveFailed": "Не удалось сохранить правило"
"channelSaveFailed": "Не удалось сохранить канал"
},
"ai": {
"title": "Помощник",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Учитавање...",
"noData": "Нема доступних података",
"allClear": "Све је чисто",
"acknowledgeAlert": "Потврди",
"noPingUrls": "Није конфигурисан ниједан URL",
"pingLabel": "Ознака",
"addPingUrl": "Додај URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Историјски графикон процесора, меморије, диска или мреже за хост",
"widgetHostGridName": "Хост мрежа",
"widgetHostGridDesc": "Приказ мреже статуса SSH хоста",
"widgetAlertFeedName": "Фид обавештења",
"widgetAlertFeedDesc": "Укључивање упозорења уживо са подршком за потврду",
"widgetPingStatusName": "Статус пинга",
"widgetPingStatusDesc": "HTTP пинг статус за један или више URL-ова",
"widgetRecentActivityName": "Недавне активности",
@@ -747,7 +744,6 @@
"portForwarding": "Преусмеравање",
"sftp": "СФТП",
"connections": "Везе",
"alerts": "Упозорења",
"serial": "Серијски",
"keepSideMenuOpen": "Остави мени отвореним",
"collapseSideMenu": "Сакриј мени",
@@ -4890,21 +4886,6 @@
"viewHistory": "Прикажи историју"
},
"alerts": {
"tabFirings": "Упозорења",
"tabRules": "Правила",
"tabChannels": "Канали",
"noFirings": "Нема непотврђених упозорења",
"noRules": "Није конфигурисано ниједно правило упозорења",
"noChannels": "Није конфигурисан ниједан канал за обавештења",
"rulesDesc": "Правила упозорења покрећу обавештења",
"channelsDesc": "Где се шаљу обавештења",
"acknowledge": "Потврди",
"ackAll": "Потврди све",
"allAcknowledged": "Сва упозорења су потврђена",
"ackFailed": "Потврђивање упозорења није успело",
"ackAllFailed": "Није успело потврђивање свих упозорења",
"showAcknowledged": "Прикажи све",
"hideAcknowledged": "Сакриј потврђено",
"addChannel": "Додај канал",
"editChannel": "Измени канал",
"channelName": "Име",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Тема",
"ntfyTopicRequired": "Тема је обавезна",
"ntfyToken": "Приступни токен (опционо)",
"channelSaveFailed": "Чување канала није успело",
"test": "Тест",
"testSent": "Послато је пробно обавештење",
"testFailed": "Обавештење о тестирању није успело",
"testFailedWithReason": "Обавештење о тестирању није успело: {{reason}}",
"addRule": "Додај правило упозорења",
"editRule": "Измени правило упозорења",
"ruleName": "Назив правила",
"ruleNameRequired": "Име је обавезно",
"triggerType": "Окидач",
"thresholdValue": "Праг (%)",
"durationSeconds": "Трајање (секунде, 0 = одмах се активира)",
"cooldownMinutes": "Хлађење (минути)",
"channels": "Канали за обавештења",
"noChannelsHint": "Прво додајте канале на картици Канали",
"ruleSaveFailed": "Чување правила није успело"
"channelSaveFailed": "Чување канала није успело"
},
"ai": {
"title": "Асистент",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Belastning...",
"noData": "Inga data tillgängliga",
"allClear": "Faran över",
"acknowledgeAlert": "Erkänna",
"noPingUrls": "Inga webbadresser konfigurerade",
"pingLabel": "Märka",
"addPingUrl": "Lägg till URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Historiskt diagram över CPU, minne, disk eller nätverk för en värd",
"widgetHostGridName": "Värdnät",
"widgetHostGridDesc": "Rutnätsvy över SSH-värdstatusar",
"widgetAlertFeedName": "Aviseringsflöde",
"widgetAlertFeedDesc": "Live-avfyrningar med bekräftelsestöd",
"widgetPingStatusName": "Pingstatus",
"widgetPingStatusDesc": "HTTP-pingstatus för en eller flera URL:er",
"widgetRecentActivityName": "Senaste aktivitet",
@@ -747,7 +744,6 @@
"portForwarding": "Spedition",
"sftp": "SFTP",
"connections": "Anslutningar",
"alerts": "Aviseringar",
"serial": "Serie",
"keepSideMenuOpen": "Håll menyn öppen",
"collapseSideMenu": "Minimera menyn",
@@ -4890,21 +4886,6 @@
"viewHistory": "Visa historik"
},
"alerts": {
"tabFirings": "Aviseringar",
"tabRules": "Regler",
"tabChannels": "Kanaler",
"noFirings": "Inga okvitterade varningar",
"noRules": "Inga varningsregler konfigurerade",
"noChannels": "Inga aviseringskanaler konfigurerade",
"rulesDesc": "Aviseringsregler utlöser aviseringar",
"channelsDesc": "Vart varningsmeddelanden skickas",
"acknowledge": "Erkänna",
"ackAll": "Bekräfta alla",
"allAcknowledged": "Alla varningar bekräftade",
"ackFailed": "Kunde inte bekräfta varningen",
"ackAllFailed": "Kunde inte bekräfta alla varningar",
"showAcknowledged": "Visa alla",
"hideAcknowledged": "Dölj godkända",
"addChannel": "Lägg till kanal",
"editChannel": "Redigera kanal",
"channelName": "Namn",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Ämne",
"ntfyTopicRequired": "Ämne är obligatoriskt",
"ntfyToken": "Åtkomsttoken (valfritt)",
"channelSaveFailed": "Misslyckades med att spara kanalen",
"test": "Testa",
"testSent": "Testmeddelande skickat",
"testFailed": "Testmeddelandet misslyckades",
"testFailedWithReason": "Testmeddelande misslyckades: {{reason}}",
"addRule": "Lägg till varningsregel",
"editRule": "Redigera varningsregel",
"ruleName": "Regelnamn",
"ruleNameRequired": "Namn krävs",
"triggerType": "Utlösare",
"thresholdValue": "Tröskelvärde (%)",
"durationSeconds": "Varaktighet (sekunder, 0 = avfyras omedelbart)",
"cooldownMinutes": "Nedkylningstid (minuter)",
"channels": "Aviseringskanaler",
"noChannelsHint": "Lägg först till kanaler på fliken Kanaler",
"ruleSaveFailed": "Det gick inte att spara regeln"
"channelSaveFailed": "Misslyckades med att spara kanalen"
},
"ai": {
"title": "Assistent",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "กำลังโหลด...",
"noData": "ไม่มีข้อมูล",
"allClear": "ทุกอย่างเรียบร้อย",
"acknowledgeAlert": "รับทราบ",
"noPingUrls": "ไม่มีการกำหนดค่า URL",
"pingLabel": "ฉลาก",
"addPingUrl": "เพิ่ม URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "แผนภูมิแสดงประวัติการใช้งาน CPU, หน่วยความจำ, ดิสก์ หรือเครือข่ายสำหรับโฮสต์",
"widgetHostGridName": "โฮสต์กริด",
"widgetHostGridDesc": "การแสดงผลแบบตารางของสถานะโฮสต์ SSH",
"widgetAlertFeedName": "ฟีดแจ้งเตือน",
"widgetAlertFeedDesc": "การแจ้งเตือนแบบเรียลไทม์พร้อมการยืนยันการรับการแจ้งเตือน",
"widgetPingStatusName": "สถานะการปิง",
"widgetPingStatusDesc": "สถานะการ ping HTTP สำหรับ URL หนึ่งรายการขึ้นไป",
"widgetRecentActivityName": "กิจกรรมล่าสุด",
@@ -747,7 +744,6 @@
"portForwarding": "การส่งต่อ",
"sftp": "เอสเอฟทีพี",
"connections": "การเชื่อมต่อ",
"alerts": "การแจ้งเตือน",
"serial": "ซีเรียล",
"keepSideMenuOpen": "เปิดเมนูค้างไว้",
"collapseSideMenu": "ยุบเมนู",
@@ -4890,21 +4886,6 @@
"viewHistory": "ประวัติการดู"
},
"alerts": {
"tabFirings": "การแจ้งเตือน",
"tabRules": "กฎ",
"tabChannels": "ช่องต่างๆ",
"noFirings": "ไม่มีการแจ้งเตือนที่ยังไม่ได้รับการยืนยัน",
"noRules": "ไม่มีการกำหนดค่ากฎการแจ้งเตือน",
"noChannels": "ไม่ได้กำหนดค่าช่องทางการแจ้งเตือนใดๆ",
"rulesDesc": "กฎการแจ้งเตือนจะเรียกใช้การแจ้งเตือน",
"channelsDesc": "ส่งการแจ้งเตือนไปยังที่ต่างๆ",
"acknowledge": "รับทราบ",
"ackAll": "ยอมรับทั้งหมด",
"allAcknowledged": "รับทราบการแจ้งเตือนทั้งหมดแล้ว",
"ackFailed": "ไม่ตอบรับการแจ้งเตือน",
"ackAllFailed": "ไม่สามารถตอบรับการแจ้งเตือนทั้งหมดได้",
"showAcknowledged": "แสดงทั้งหมด",
"hideAcknowledged": "ซ่อน ตอบรับ",
"addChannel": "เพิ่มช่องทาง",
"editChannel": "แก้ไขช่อง",
"channelName": "ชื่อ",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "หัวข้อ",
"ntfyTopicRequired": "จำเป็นต้องระบุหัวข้อ",
"ntfyToken": "โทเค็นการเข้าถึง (ไม่บังคับ)",
"channelSaveFailed": "ไม่สามารถบันทึกช่องได้",
"test": "ทดสอบ",
"testSent": "ส่งการแจ้งเตือนการทดสอบแล้ว",
"testFailed": "การแจ้งเตือนการทดสอบล้มเหลว",
"testFailedWithReason": "การแจ้งเตือนการทดสอบล้มเหลว: {{reason}}",
"addRule": "เพิ่มกฎการแจ้งเตือน",
"editRule": "แก้ไขกฎการแจ้งเตือน",
"ruleName": "ชื่อกฎ",
"ruleNameRequired": "ต้องระบุชื่อ",
"triggerType": "สิ่งกระตุ้น",
"thresholdValue": "เกณฑ์ (%)",
"durationSeconds": "ระยะเวลา (วินาที, 0 = ยิงทันที)",
"cooldownMinutes": "ระยะเวลาคูลดาวน์ (นาที)",
"channels": "ช่องทางการแจ้งเตือน",
"noChannelsHint": "เพิ่มช่องในแท็บช่องก่อน",
"ruleSaveFailed": "ไม่สามารถบันทึกกฎได้"
"channelSaveFailed": "ไม่สามารถบันทึกช่องได้"
},
"ai": {
"title": "ผู้ช่วย",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Yükleniyor...",
"noData": "Veri yok",
"allClear": "Her şey normal",
"acknowledgeAlert": "Onayla",
"noPingUrls": "Hiçbir URL yapılandırılmamış",
"pingLabel": "Etiket",
"addPingUrl": "URL Ekle",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Bir sunucu için geçmiş CPU, bellek, disk veya ağ grafiği",
"widgetHostGridName": "Sunucu Izgarası",
"widgetHostGridDesc": "SSH sunucu durumlarının ızgara görünümü",
"widgetAlertFeedName": "Uyarı Akışı",
"widgetAlertFeedDesc": "Onay desteği ile canlı uyarı tetiklemeleri",
"widgetPingStatusName": "Ping Durumu",
"widgetPingStatusDesc": "Bir veya daha fazla URL için HTTP ping durumu",
"widgetRecentActivityName": "Son Etkinlikler",
@@ -747,7 +744,6 @@
"portForwarding": "İletme",
"sftp": "SFTP",
"connections": "Bağlantılar",
"alerts": "Uyarılar",
"serial": "Seri",
"keepSideMenuOpen": "Menüyü Açık Tutun",
"collapseSideMenu": "Menüyü Daralt",
@@ -4890,21 +4886,6 @@
"viewHistory": "Geçmişi Görüntüle"
},
"alerts": {
"tabFirings": "Uyarılar",
"tabRules": "Kurallar",
"tabChannels": "Kanallar",
"noFirings": "Onaylanmamış uyarı yok",
"noRules": "Uyarı kuralı yapılandırılmamış",
"noChannels": "Bildirim kanalı yapılandırılmamış",
"rulesDesc": "Uyarı kuralları bildirimleri tetikler",
"channelsDesc": "Uyarı bildirimlerinin gönderildiği yer",
"acknowledge": "Onayla",
"ackAll": "Tümünü Onayla",
"allAcknowledged": "Tüm uyarılar onaylandı",
"ackFailed": "Uyarı onaylanamadı",
"ackAllFailed": "Tüm uyarılar onaylanamadı",
"showAcknowledged": "Tümünü Göster",
"hideAcknowledged": "Onaylananları Gizle",
"addChannel": "Kanal Ekle",
"editChannel": "Kanalı Düzenle",
"channelName": "Ad",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Konu",
"ntfyTopicRequired": "Konu zorunludur",
"ntfyToken": "Erişim Anahtarı (isteğe bağlı)",
"channelSaveFailed": "Kanal kaydedilemedi",
"test": "Test Et",
"testSent": "Test bildirimi gönderildi",
"testFailed": "Test bildirimi başarısız oldu",
"testFailedWithReason": "Test bildirimi başarısız oldu: {{reason}}",
"addRule": "Uyarı Kuralı Ekle",
"editRule": "Uyarı Kuralını Düzenle",
"ruleName": "Kural Adı",
"ruleNameRequired": "Ad zorunludur",
"triggerType": "Tetikleyici",
"thresholdValue": "Eşik (%)",
"durationSeconds": "Süre (saniye, 0 = hemen tetikle)",
"cooldownMinutes": "Bekleme süresi (dakika)",
"channels": "Bildirim Kanalları",
"noChannelsHint": "Önce Kanallar sekmesinde kanal ekleyin",
"ruleSaveFailed": "Kural kaydedilemedi"
"channelSaveFailed": "Kanal kaydedilemedi"
},
"ai": {
"title": "Asistan",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Завантаження...",
"noData": "Дані недоступні",
"allClear": "Все чисто",
"acknowledgeAlert": "Підтвердити",
"noPingUrls": "URL-адреси не налаштовано",
"pingLabel": "Мітка",
"addPingUrl": "Додати URL-адресу",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Історична діаграма процесора, пам'яті, диска або мережі для хоста",
"widgetHostGridName": "Сітка хоста",
"widgetHostGridDesc": "Таблиця статусів хостів SSH",
"widgetAlertFeedName": "Стрічка сповіщень",
"widgetAlertFeedDesc": "Активація тривог у реальному часі з підтримкою підтвердження",
"widgetPingStatusName": "Стан пінгу",
"widgetPingStatusDesc": "Статус HTTP-пінгу для однієї або кількох URL-адрес",
"widgetRecentActivityName": "Нещодавня активність",
@@ -747,7 +744,6 @@
"portForwarding": "Переадресація",
"sftp": "SFTP",
"connections": "З'єднання",
"alerts": "Сповіщення",
"serial": "Серійний",
"keepSideMenuOpen": "Залишити меню відкритим",
"collapseSideMenu": "Згорнути меню",
@@ -4890,21 +4886,6 @@
"viewHistory": "Переглянути історію"
},
"alerts": {
"tabFirings": "Сповіщення",
"tabRules": "Правила",
"tabChannels": "Канали",
"noFirings": "Немає непідтверджених сповіщень",
"noRules": "Правила сповіщень не налаштовано",
"noChannels": "Канали сповіщень не налаштовано",
"rulesDesc": "Правила сповіщень запускають сповіщення",
"channelsDesc": "Куди надсилаються сповіщення",
"acknowledge": "Підтвердити",
"ackAll": "Підтвердження всім",
"allAcknowledged": "Усі сповіщення підтверджено",
"ackFailed": "Не вдалося підтвердити сповіщення",
"ackAllFailed": "Не вдалося підтвердити всі сповіщення",
"showAcknowledged": "Показати все",
"hideAcknowledged": "Приховати підтверджено",
"addChannel": "Додати канал",
"editChannel": "Редагувати канал",
"channelName": "Ім'я",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Тема",
"ntfyTopicRequired": "Тема обов'язкова",
"ntfyToken": "Маркер доступу (необов'язково)",
"channelSaveFailed": "Не вдалося зберегти канал",
"test": "Тест",
"testSent": "Тестове сповіщення надіслано",
"testFailed": "Не вдалося виконати тестове сповіщення",
"testFailedWithReason": "Не вдалося виконати тестове сповіщення: {{reason}}",
"addRule": "Додати правило сповіщення",
"editRule": "Редагувати правило сповіщення",
"ruleName": "Назва правила",
"ruleNameRequired": "Ім'я обов'язкове",
"triggerType": "Тригер",
"thresholdValue": "Поріг (%)",
"durationSeconds": "Тривалість (секунди, 0 = негайно спрацьовувати)",
"cooldownMinutes": "Охолодження (хвилини)",
"channels": "Канали сповіщень",
"noChannelsHint": "Спочатку додайте канали на вкладці «Канали»",
"ruleSaveFailed": "Не вдалося зберегти правило"
"channelSaveFailed": "Не вдалося зберегти канал"
},
"ai": {
"title": "Помічник",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "Đang tải...",
"noData": "Không có dữ liệu",
"allClear": "Tất cả ổn",
"acknowledgeAlert": "Xác nhận",
"noPingUrls": "Chưa cấu hình URL",
"pingLabel": "Nhãn",
"addPingUrl": "Thêm URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "Biểu đồ lịch sử CPU, bộ nhớ, ổ đĩa hoặc mạng cho một máy chủ",
"widgetHostGridName": "Lưới máy chủ",
"widgetHostGridDesc": "Hiển thị trạng thái máy chủ SSH dạng lưới",
"widgetAlertFeedName": "Luồng cảnh báo",
"widgetAlertFeedDesc": "Cảnh báo trực tiếp kèm hỗ trợ xác nhận",
"widgetPingStatusName": "Trạng thái ping",
"widgetPingStatusDesc": "Trạng thái ping HTTP cho một hoặc nhiều URL",
"widgetRecentActivityName": "Hoạt động gần đây",
@@ -747,7 +744,6 @@
"portForwarding": "Chuyển tiếp",
"sftp": "SFTP",
"connections": "Kết nối",
"alerts": "Cảnh báo",
"serial": "Nối tiếp",
"keepSideMenuOpen": "Giữ menu luôn mở",
"collapseSideMenu": "Menu thu gọn",
@@ -4890,21 +4886,6 @@
"viewHistory": "Xem lịch sử"
},
"alerts": {
"tabFirings": "Cảnh báo",
"tabRules": "Quy tắc",
"tabChannels": "Kênh",
"noFirings": "Không có cảnh báo chưa xác nhận",
"noRules": "Chưa cấu hình quy tắc cảnh báo",
"noChannels": "Chưa cấu hình kênh thông báo",
"rulesDesc": "Quy tắc cảnh báo kích hoạt thông báo",
"channelsDesc": "Nơi gửi thông báo cảnh báo",
"acknowledge": "Xác nhận",
"ackAll": "Xác nhận tất cả",
"allAcknowledged": "Tất cả cảnh báo đã được xác nhận",
"ackFailed": "Không thể xác nhận cảnh báo",
"ackAllFailed": "Không thể xác nhận tất cả cảnh báo",
"showAcknowledged": "Hiện tất cả",
"hideAcknowledged": "Ẩn đã xác nhận",
"addChannel": "Thêm kênh",
"editChannel": "Sửa kênh",
"channelName": "Tên",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "Chủ đề",
"ntfyTopicRequired": "Chủ đề là bắt buộc",
"ntfyToken": "Mã truy cập (tùy chọn)",
"channelSaveFailed": "Lỗi khi lưu kênh",
"test": "Kiểm tra",
"testSent": "Đã gửi thông báo kiểm tra",
"testFailed": "Gửi thông báo kiểm tra thất bại",
"testFailedWithReason": "Thông báo kiểm tra thất bại: {{reason}}",
"addRule": "Thêm quy tắc cảnh báo",
"editRule": "Sửa quy tắc cảnh báo",
"ruleName": "Tên quy tắc",
"ruleNameRequired": "Tên là bắt buộc",
"triggerType": "Kích hoạt",
"thresholdValue": "Ngưỡng (%)",
"durationSeconds": "Thời gian (giây, 0 = kích hoạt ngay)",
"cooldownMinutes": "Thời gian chờ (phút)",
"channels": "Kênh thông báo",
"noChannelsHint": "Vui lòng thêm kênh trong tab Kênh trước",
"ruleSaveFailed": "Lỗi khi lưu quy tắc"
"channelSaveFailed": "Lỗi khi lưu kênh"
},
"ai": {
"title": "Trợ lý",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "加载中...",
"noData": "暂无数据",
"allClear": "一切正常",
"acknowledgeAlert": "确认告警",
"noPingUrls": "未配置 URL",
"pingLabel": "标签",
"addPingUrl": "添加 URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "主机的历史 CPU、内存、磁盘或网络图表",
"widgetHostGridName": "主机网格",
"widgetHostGridDesc": "SSH 主机状态的网格视图",
"widgetAlertFeedName": "告警信息流",
"widgetAlertFeedDesc": "实时告警触发信息,支持确认操作",
"widgetPingStatusName": "Ping 状态",
"widgetPingStatusDesc": "一个或多个 URL 的 HTTP ping 状态",
"widgetRecentActivityName": "近期活动",
@@ -747,7 +744,6 @@
"portForwarding": "转发",
"sftp": "SFTP",
"connections": "连接",
"alerts": "告警",
"serial": "串口",
"keepSideMenuOpen": "保持菜单打开",
"collapseSideMenu": "折叠菜单",
@@ -4890,21 +4886,6 @@
"viewHistory": "查看历史"
},
"alerts": {
"tabFirings": "告警",
"tabRules": "规则",
"tabChannels": "通道",
"noFirings": "没有未确认告警",
"noRules": "未配置告警规则",
"noChannels": "未配置通知渠道",
"rulesDesc": "告警规则触发通知",
"channelsDesc": "告警通知的发送渠道",
"acknowledge": "确认告警",
"ackAll": "全部确认",
"allAcknowledged": "所有告警已确认",
"ackFailed": "确认告警失败",
"ackAllFailed": "确认所有告警失败",
"showAcknowledged": "显示全部",
"hideAcknowledged": "隐藏已确认",
"addChannel": "添加渠道",
"editChannel": "编辑渠道",
"channelName": "名称",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "主题",
"ntfyTopicRequired": "主题为必填项",
"ntfyToken": "访问令牌(选填)",
"channelSaveFailed": "保存渠道失败",
"test": "测试",
"testSent": "测试通知已发送",
"testFailed": "测试通知失败",
"testFailedWithReason": "测试通知失败:{{reason}}",
"addRule": "添加告警规则",
"editRule": "编辑告警规则",
"ruleName": "规则名称",
"ruleNameRequired": "名称为必填项",
"triggerType": "触发条件",
"thresholdValue": "阈值(%)",
"durationSeconds": "持续时间(秒,0 表示立即触发)",
"cooldownMinutes": "冷却时间(分钟)",
"channels": "通知渠道",
"noChannelsHint": "请先在渠道标签页中添加渠道",
"ruleSaveFailed": "保存规则失败"
"channelSaveFailed": "保存渠道失败"
},
"ai": {
"title": "助手",
+1 -35
View File
@@ -287,7 +287,6 @@
"loading": "載入中...",
"noData": "無可用資料",
"allClear": "一切正常",
"acknowledgeAlert": "確認",
"noPingUrls": "未設定 URL",
"pingLabel": "標籤",
"addPingUrl": "新增 URL",
@@ -324,8 +323,6 @@
"widgetMetricsChartDesc": "主機的歷史 CPU、記憶體、磁碟或網路圖表",
"widgetHostGridName": "主機方格",
"widgetHostGridDesc": "SSH 主機狀態的方格檢視",
"widgetAlertFeedName": "警報動態",
"widgetAlertFeedDesc": "即時警報觸發並支援確認",
"widgetPingStatusName": "Ping 狀態",
"widgetPingStatusDesc": "一個或多個 URL 的 HTTP ping 狀態",
"widgetRecentActivityName": "近期活動",
@@ -747,7 +744,6 @@
"portForwarding": "轉發",
"sftp": "SFTP",
"connections": "連線",
"alerts": "警示",
"serial": "序列",
"keepSideMenuOpen": "保持菜單打開",
"collapseSideMenu": "折疊菜單",
@@ -4890,21 +4886,6 @@
"viewHistory": "檢視歷史記錄"
},
"alerts": {
"tabFirings": "警示",
"tabRules": "規則",
"tabChannels": "通道",
"noFirings": "沒有未確認的警示",
"noRules": "未設定警示規則",
"noChannels": "未設定通知通道",
"rulesDesc": "警示規則會觸發通知",
"channelsDesc": "警示通知傳送的位置",
"acknowledge": "確認",
"ackAll": "全部確認",
"allAcknowledged": "所有警示已確認",
"ackFailed": "確認警示失敗",
"ackAllFailed": "確認所有警示失敗",
"showAcknowledged": "顯示全部",
"hideAcknowledged": "隱藏已確認",
"addChannel": "新增通道",
"editChannel": "編輯通道",
"channelName": "名稱",
@@ -4917,22 +4898,7 @@
"ntfyTopic": "主題",
"ntfyTopicRequired": "主題為必填",
"ntfyToken": "存取權杖(選填)",
"channelSaveFailed": "無法儲存頻道",
"test": "測試",
"testSent": "已傳送測試通知",
"testFailed": "測試通知失敗",
"testFailedWithReason": "測試通知失敗: {{reason}}",
"addRule": "新增警示規則",
"editRule": "編輯警示規則",
"ruleName": "規則名稱",
"ruleNameRequired": "名稱為必填",
"triggerType": "觸發條件",
"thresholdValue": "閾值(%)",
"durationSeconds": "持續時間(秒,0 = 立即觸發)",
"cooldownMinutes": "冷卻時間(分鐘)",
"channels": "通知頻道",
"noChannelsHint": "請先於「頻道」分頁新增頻道",
"ruleSaveFailed": "無法儲存規則"
"channelSaveFailed": "無法儲存頻道"
},
"ai": {
"title": "助理",
-13
View File
@@ -25,7 +25,6 @@ import {
Fingerprint,
Hammer,
Play,
Plug,
ScrollText,
Sparkles,
Presentation,
@@ -154,9 +153,6 @@ const FleetInventoryTab = lazy(() =>
const TermixIdPanel = lazy(() =>
import("@/sidebar/TermixIdPanel").then((m) => ({ default: m.TermixIdPanel })),
);
const AlertsPanel = lazy(() =>
import("@/sidebar/AlertsPanel").then((m) => ({ default: m.AlertsPanel })),
);
const SessionLogsPanel = lazy(() =>
import("@/sidebar/SessionLogsPanel").then((m) => ({
default: m.SessionLogsPanel,
@@ -391,8 +387,6 @@ export function tabIcon(type: TabType) {
return <Presentation className="size-3.5" />;
case "termix-id":
return <Fingerprint className="size-3.5" />;
case "alerts":
return <Plug className="size-3.5" />;
case "session-logs":
return <ScrollText className="size-3.5" />;
case "snippets":
@@ -721,13 +715,6 @@ export function renderTabContent(
</PanelTabFrame>,
);
case "alerts":
return withTabSuspense(
<PanelTabFrame>
<AlertsPanel />
</PanelTabFrame>,
);
case "session-logs":
return withTabSuspense(
<PanelTabFrame>
-2
View File
@@ -26,7 +26,6 @@ export const WORKSPACE_CAPTURABLE_TYPES: TabType[] = [
"homepage",
"fleet-inventory",
"termix-id",
"alerts",
"session-logs",
"snippets",
"macros",
@@ -44,7 +43,6 @@ export const HOSTLESS_WORKSPACE_TAB_TYPES: TabType[] = [
"fleet-inventory",
"docker",
"termix-id",
"alerts",
"session-logs",
"snippets",
"macros",
-274
View File
@@ -1,274 +0,0 @@
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/dialog";
import { Button } from "@/components/button";
import { Input } from "@/components/input";
import { toast } from "sonner";
import {
createAlertRule,
updateAlertRule,
type AlertRule,
type NotificationChannel,
} from "@/api/alerts-api";
const TRIGGER_TYPES = [
{ value: "host_offline", label: "Host Offline", hasThreshold: false },
{ value: "host_online", label: "Host Online", hasThreshold: false },
{ value: "cpu_threshold", label: "CPU Usage Threshold", hasThreshold: true },
{
value: "memory_threshold",
label: "Memory Usage Threshold",
hasThreshold: true,
},
{
value: "disk_threshold",
label: "Disk Usage Threshold",
hasThreshold: true,
},
{
value: "health_check_failure",
label: "Health Check Failure",
hasThreshold: false,
},
{ value: "user_login", label: "User SSH Login", hasThreshold: false },
];
interface AlertRuleDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
rule: AlertRule | null;
channels: NotificationChannel[];
onSaved: () => void;
}
export function AlertRuleDialog({
open,
onOpenChange,
rule,
channels,
onSaved,
}: AlertRuleDialogProps) {
const { t } = useTranslation();
const [name, setName] = useState("");
const [triggerType, setTriggerType] = useState("host_offline");
const [threshold, setThreshold] = useState("80");
const [durationSeconds, setDurationSeconds] = useState("0");
const [cooldownMinutes, setCooldownMinutes] = useState("15");
const [selectedChannels, setSelectedChannels] = useState<number[]>([]);
const [saving, setSaving] = useState(false);
const triggerDef = TRIGGER_TYPES.find((tt) => tt.value === triggerType);
const hasThreshold = triggerDef?.hasThreshold ?? false;
useEffect(() => {
if (!open) return;
if (rule) {
setName(rule.name);
setTriggerType(rule.triggerType);
setThreshold(
rule.thresholdValue != null ? String(rule.thresholdValue) : "80",
);
setDurationSeconds(
rule.thresholdDurationSeconds != null
? String(rule.thresholdDurationSeconds)
: "0",
);
setCooldownMinutes(String(rule.cooldownMinutes));
setSelectedChannels(rule.channelIds ?? []);
} else {
setName("");
setTriggerType("host_offline");
setThreshold("80");
setDurationSeconds("0");
setCooldownMinutes("15");
setSelectedChannels([]);
}
}, [open, rule]);
function toggleChannel(id: number) {
setSelectedChannels((prev) =>
prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id],
);
}
async function handleSave() {
if (!name.trim()) {
toast.error(t("alerts.ruleNameRequired", "Name is required"));
return;
}
setSaving(true);
try {
const payload = {
name: name.trim(),
triggerType,
thresholdValue: hasThreshold ? Number(threshold) : null,
thresholdDurationSeconds:
hasThreshold && Number(durationSeconds) > 0
? Number(durationSeconds)
: null,
cooldownMinutes: Number(cooldownMinutes) || 15,
channels: selectedChannels,
};
if (rule) {
await updateAlertRule(rule.id, payload);
} else {
await createAlertRule(payload);
}
onSaved();
} catch {
toast.error(t("alerts.ruleSaveFailed", "Failed to save rule"));
} finally {
setSaving(false);
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
{rule
? t("alerts.editRule", "Edit Alert Rule")
: t("alerts.addRule", "Add Alert Rule")}
</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-3 py-2">
<div className="flex flex-col gap-1">
<label className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{t("alerts.ruleName", "Rule Name")}
</label>
<Input
value={name}
onChange={(e) => setName(e.target.value)}
className="text-sm"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{t("alerts.triggerType", "Trigger")}
</label>
<div className="flex flex-col gap-1">
{TRIGGER_TYPES.map((tt) => (
<button
key={tt.value}
onClick={() => setTriggerType(tt.value)}
className={`flex items-center gap-2 px-2 py-1.5 text-xs border transition-colors text-left ${triggerType === tt.value ? "border-accent-brand/40 bg-accent-brand/10 text-accent-brand" : "border-border text-muted-foreground hover:text-foreground"}`}
>
<span
className={`size-2 shrink-0 rounded-full border-2 ${triggerType === tt.value ? "border-accent-brand bg-accent-brand" : "border-muted-foreground"}`}
/>
{tt.label}
</button>
))}
</div>
</div>
{hasThreshold && (
<>
<div className="flex flex-col gap-1">
<label className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{t("alerts.thresholdValue", "Threshold (%)")}
</label>
<div className="flex items-center gap-2">
<Input
type="number"
min={1}
max={100}
value={threshold}
onChange={(e) => setThreshold(e.target.value)}
className="w-20 text-sm"
/>
<span className="text-xs text-muted-foreground">%</span>
</div>
</div>
<div className="flex flex-col gap-1">
<label className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{t(
"alerts.durationSeconds",
"Duration (seconds, 0 = fire immediately)",
)}
</label>
<Input
type="number"
min={0}
value={durationSeconds}
onChange={(e) => setDurationSeconds(e.target.value)}
className="w-24 text-sm"
/>
</div>
</>
)}
<div className="flex flex-col gap-1">
<label className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{t("alerts.cooldownMinutes", "Cooldown (minutes)")}
</label>
<Input
type="number"
min={0}
value={cooldownMinutes}
onChange={(e) => setCooldownMinutes(e.target.value)}
className="w-24 text-sm"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{t("alerts.channels", "Notification Channels")}
</label>
{channels.length === 0 ? (
<span className="text-[10px] text-muted-foreground">
{t(
"alerts.noChannelsHint",
"Add channels in the Channels tab first",
)}
</span>
) : (
<div className="flex flex-col gap-1">
{channels.map((ch) => (
<button
key={ch.id}
onClick={() => toggleChannel(ch.id)}
className={`flex items-center gap-2 px-2 py-1.5 text-xs border transition-colors text-left ${selectedChannels.includes(ch.id) ? "border-accent-brand/40 bg-accent-brand/10 text-accent-brand" : "border-border text-muted-foreground hover:text-foreground"}`}
>
<span
className={`size-2 shrink-0 rounded-sm border-2 ${selectedChannels.includes(ch.id) ? "border-accent-brand bg-accent-brand" : "border-muted-foreground"}`}
/>
{ch.name}
<span className="ml-auto text-[9px] font-mono uppercase opacity-60">
{ch.type}
</span>
</button>
))}
</div>
)}
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={() => onOpenChange(false)}>
{t("common.cancel", "Cancel")}
</Button>
<Button
variant="outline"
onClick={handleSave}
disabled={saving}
className="border-accent-brand/40 text-accent-brand hover:bg-accent-brand/10 hover:text-accent-brand"
>
{saving
? t("common.saving", "Saving...")
: t("common.save", "Save")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
-471
View File
@@ -1,471 +0,0 @@
import { useState, useEffect, useCallback } from "react";
import { useTranslation } from "react-i18next";
import {
Bell,
BellOff,
Check,
CheckCheck,
Trash2,
Plus,
Settings2,
} from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/button";
import {
getAlertFirings,
acknowledgeAlertFiring,
acknowledgeAllAlertFirings,
getAlertRules,
getNotificationChannels,
deleteAlertRule,
deleteNotificationChannel,
testNotificationChannel,
type AlertFiring,
type AlertRule,
type NotificationChannel,
} from "@/api/alerts-api";
import { AlertRuleDialog } from "./AlertRuleDialog";
import { NotificationChannelDialog } from "./NotificationChannelDialog";
type PanelTab = "firings" | "rules" | "channels";
const SEVERITY_CLASS: Record<string, string> = {
info: "bg-blue-500/20 text-blue-400 border-blue-500/30",
warning: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",
critical: "bg-red-500/20 text-red-400 border-red-500/30",
};
function timeAgo(isoStr: string): string {
const ms = Date.now() - new Date(isoStr).getTime();
if (!Number.isFinite(ms)) return "";
const sec = Math.floor(ms / 1000);
if (sec < 60) return `${sec}s ago`;
const min = Math.floor(sec / 60);
if (min < 60) return `${min}m ago`;
const hr = Math.floor(min / 60);
if (hr < 24) return `${hr}h ago`;
return `${Math.floor(hr / 24)}d ago`;
}
export function AlertsPanel() {
const { t } = useTranslation();
const [tab, setTab] = useState<PanelTab>("firings");
// Firings state
const [firings, setFirings] = useState<AlertFiring[]>([]);
const [firingsLoading, setFiringsLoading] = useState(true);
const [showAcknowledged, setShowAcknowledged] = useState(false);
// Rules state
const [rules, setRules] = useState<AlertRule[]>([]);
const [rulesLoading, setRulesLoading] = useState(false);
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
const [editRule, setEditRule] = useState<AlertRule | null>(null);
// Channels state
const [channels, setChannels] = useState<NotificationChannel[]>([]);
const [channelsLoading, setChannelsLoading] = useState(false);
const [channelDialogOpen, setChannelDialogOpen] = useState(false);
const [editChannel, setEditChannel] = useState<NotificationChannel | null>(
null,
);
const loadFirings = useCallback(() => {
setFiringsLoading(true);
getAlertFirings({ limit: 100 })
.then(setFirings)
.catch(() => {})
.finally(() => setFiringsLoading(false));
}, []);
const loadRules = useCallback(() => {
setRulesLoading(true);
getAlertRules()
.then(setRules)
.catch(() => {})
.finally(() => setRulesLoading(false));
}, []);
const loadChannels = useCallback(() => {
setChannelsLoading(true);
getNotificationChannels()
.then(setChannels)
.catch(() => {})
.finally(() => setChannelsLoading(false));
}, []);
useEffect(() => {
loadFirings();
loadChannels();
}, [loadFirings, loadChannels]);
useEffect(() => {
if (tab === "rules") loadRules();
if (tab === "channels") loadChannels();
}, [tab, loadRules, loadChannels]);
const visibleFirings = firings.filter((f) =>
showAcknowledged ? true : !f.acknowledged,
);
async function handleAck(id: number) {
try {
await acknowledgeAlertFiring(id);
setFirings((prev) =>
prev.map((f) => (f.id === id ? { ...f, acknowledged: true } : f)),
);
} catch {
toast.error(t("alerts.ackFailed", "Failed to acknowledge alert"));
}
}
async function handleAckAll() {
try {
await acknowledgeAllAlertFirings();
setFirings((prev) => prev.map((f) => ({ ...f, acknowledged: true })));
toast.success(t("alerts.allAcknowledged", "All alerts acknowledged"));
} catch {
toast.error(t("alerts.ackAllFailed", "Failed to acknowledge all alerts"));
}
}
async function handleDeleteRule(id: number) {
if (!confirm(t("common.confirmDelete", "Delete this rule?"))) return;
try {
await deleteAlertRule(id);
setRules((prev) => prev.filter((r) => r.id !== id));
toast.success(t("common.deleted", "Deleted"));
} catch {
toast.error(t("common.deleteFailed", "Delete failed"));
}
}
async function handleDeleteChannel(id: number) {
if (!confirm(t("common.confirmDelete", "Delete this channel?"))) return;
try {
await deleteNotificationChannel(id);
setChannels((prev) => prev.filter((c) => c.id !== id));
toast.success(t("common.deleted", "Deleted"));
} catch {
toast.error(t("common.deleteFailed", "Delete failed"));
}
}
async function handleTestChannel(id: number) {
try {
await testNotificationChannel(id);
toast.success(t("alerts.testSent", "Test notification sent"));
} catch (err) {
const detail = err instanceof Error ? err.message : undefined;
toast.error(
detail
? t(
"alerts.testFailedWithReason",
"Test notification failed: {{reason}}",
{ reason: detail },
)
: t("alerts.testFailed", "Test notification failed"),
);
}
}
const unreadCount = firings.filter((f) => !f.acknowledged).length;
return (
<div className="flex flex-col h-full">
{/* Tab strip */}
<div className="flex shrink-0 border-b border-border">
{(["firings", "rules", "channels"] as PanelTab[]).map((t_) => (
<button
key={t_}
onClick={() => setTab(t_)}
className={`flex-1 py-2 text-xs font-semibold uppercase tracking-widest transition-colors ${
tab === t_
? "border-b-2 border-accent-brand text-accent-brand"
: "text-muted-foreground hover:text-foreground"
}`}
>
{t_ === "firings"
? t("alerts.tabFirings", "Alerts")
: t_ === "rules"
? t("alerts.tabRules", "Rules")
: t("alerts.tabChannels", "Channels")}
{t_ === "firings" && unreadCount > 0 && (
<span className="ml-1.5 rounded-full bg-destructive px-1 text-[9px] font-bold text-white">
{unreadCount}
</span>
)}
</button>
))}
</div>
{/* Firings tab */}
{tab === "firings" && (
<div className="flex flex-col flex-1 min-h-0">
<div className="flex items-center gap-2 px-3 py-2 border-b border-border shrink-0">
<button
onClick={() => setShowAcknowledged((v) => !v)}
className={`text-[10px] font-semibold px-2 py-1 border transition-colors ${showAcknowledged ? "border-accent-brand/40 bg-accent-brand/10 text-accent-brand" : "border-border text-muted-foreground hover:text-foreground"}`}
>
{showAcknowledged
? t("alerts.hideAcknowledged", "Hide Acked")
: t("alerts.showAcknowledged", "Show All")}
</button>
<div className="flex-1" />
{unreadCount > 0 && (
<Button
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-muted-foreground"
onClick={handleAckAll}
>
<CheckCheck className="size-3 mr-1" />
{t("alerts.ackAll", "Ack All")}
</Button>
)}
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-muted-foreground"
onClick={loadFirings}
title={t("common.refresh", "Refresh")}
>
<Settings2 className="size-3" />
</Button>
</div>
<div className="flex-1 min-h-0 overflow-y-auto">
{firingsLoading && (
<div className="flex items-center justify-center py-12 text-sm text-muted-foreground">
{t("common.loading", "Loading...")}
</div>
)}
{!firingsLoading && visibleFirings.length === 0 && (
<div className="flex flex-col items-center justify-center py-12 gap-2 text-muted-foreground">
<BellOff className="size-8 opacity-40" />
<span className="text-xs">
{t("alerts.noFirings", "No unacknowledged alerts")}
</span>
</div>
)}
{!firingsLoading &&
visibleFirings.map((firing) => (
<div
key={firing.id}
className={`flex flex-col gap-1 px-3 py-2.5 border-b border-border transition-colors ${firing.acknowledged ? "opacity-50" : ""}`}
>
<div className="flex items-center gap-2">
<span
className={`shrink-0 text-[9px] font-bold uppercase px-1.5 py-0.5 border ${SEVERITY_CLASS[firing.severity] ?? "bg-muted text-muted-foreground border-border"}`}
>
{firing.severity}
</span>
<span className="flex-1 truncate text-xs font-semibold">
{firing.hostName}
</span>
{!firing.acknowledged && (
<button
onClick={() => handleAck(firing.id)}
className="shrink-0 text-muted-foreground hover:text-foreground transition-colors"
title={t("alerts.acknowledge", "Acknowledge")}
>
<Check className="size-3.5" />
</button>
)}
</div>
<span className="text-[11px] text-foreground/80">
{firing.message}
</span>
<div className="flex items-center gap-2 text-[10px] text-muted-foreground">
{firing.ruleName && <span>{firing.ruleName}</span>}
<span>{timeAgo(firing.firedAt)}</span>
</div>
</div>
))}
</div>
</div>
)}
{/* Rules tab */}
{tab === "rules" && (
<div className="flex flex-col flex-1 min-h-0">
<div className="flex items-center gap-2 px-3 py-2 border-b border-border shrink-0">
<span className="flex-1 text-xs text-muted-foreground">
{t("alerts.rulesDesc", "Alert rules trigger notifications")}
</span>
<Button
variant="outline"
size="sm"
className="h-6 px-2 text-xs border-accent-brand/40 text-accent-brand hover:bg-accent-brand/10 hover:text-accent-brand"
onClick={() => {
setEditRule(null);
setRuleDialogOpen(true);
}}
>
<Plus className="size-3 mr-1" />
{t("common.add", "Add")}
</Button>
</div>
<div className="flex-1 min-h-0 overflow-y-auto">
{rulesLoading && (
<div className="flex items-center justify-center py-12 text-sm text-muted-foreground">
{t("common.loading", "Loading...")}
</div>
)}
{!rulesLoading && rules.length === 0 && (
<div className="flex flex-col items-center justify-center py-12 gap-2 text-muted-foreground">
<Bell className="size-8 opacity-40" />
<span className="text-xs">
{t("alerts.noRules", "No alert rules configured")}
</span>
</div>
)}
{!rulesLoading &&
rules.map((rule) => (
<div
key={rule.id}
className="flex items-center gap-2 px-3 py-2.5 border-b border-border"
>
<div className="flex-1 min-w-0">
<div className="text-xs font-semibold truncate">
{rule.name}
</div>
<div className="text-[10px] text-muted-foreground">
{(rule.triggerType || "").replace(/_/g, " ")}
</div>
</div>
<div
className={`size-2 shrink-0 rounded-full ${rule.enabled ? "bg-green-500" : "bg-muted"}`}
/>
<button
onClick={() => {
setEditRule(rule);
setRuleDialogOpen(true);
}}
className="shrink-0 text-muted-foreground hover:text-foreground"
title={t("common.edit", "Edit")}
>
<Settings2 className="size-3.5" />
</button>
<button
onClick={() => handleDeleteRule(rule.id)}
className="shrink-0 text-muted-foreground hover:text-destructive"
title={t("common.delete", "Delete")}
>
<Trash2 className="size-3.5" />
</button>
</div>
))}
</div>
</div>
)}
{/* Channels tab */}
{tab === "channels" && (
<div className="flex flex-col flex-1 min-h-0">
<div className="flex items-center gap-2 px-3 py-2 border-b border-border shrink-0">
<span className="flex-1 text-xs text-muted-foreground">
{t("alerts.channelsDesc", "Where alert notifications are sent")}
</span>
<Button
variant="outline"
size="sm"
className="h-6 px-2 text-xs border-accent-brand/40 text-accent-brand hover:bg-accent-brand/10 hover:text-accent-brand"
onClick={() => {
setEditChannel(null);
setChannelDialogOpen(true);
}}
>
<Plus className="size-3 mr-1" />
{t("common.add", "Add")}
</Button>
</div>
<div className="flex-1 min-h-0 overflow-y-auto">
{channelsLoading && (
<div className="flex items-center justify-center py-12 text-sm text-muted-foreground">
{t("common.loading", "Loading...")}
</div>
)}
{!channelsLoading && channels.length === 0 && (
<div className="flex flex-col items-center justify-center py-12 gap-2 text-muted-foreground">
<Bell className="size-8 opacity-40" />
<span className="text-xs">
{t(
"alerts.noChannels",
"No notification channels configured",
)}
</span>
</div>
)}
{!channelsLoading &&
channels.map((channel) => (
<div
key={channel.id}
className="flex items-center gap-2 px-3 py-2.5 border-b border-border"
>
<div className="flex-1 min-w-0">
<div className="text-xs font-semibold truncate">
{channel.name}
</div>
<div className="text-[10px] text-muted-foreground font-mono uppercase">
{channel.type}
</div>
</div>
<div
className={`size-2 shrink-0 rounded-full ${channel.enabled ? "bg-green-500" : "bg-muted"}`}
/>
<button
onClick={() => handleTestChannel(channel.id)}
className="shrink-0 text-[10px] font-semibold text-muted-foreground hover:text-foreground border border-border px-1.5 py-0.5"
title={t("alerts.test", "Test")}
>
{t("alerts.test", "Test")}
</button>
<button
onClick={() => {
setEditChannel(channel);
setChannelDialogOpen(true);
}}
className="shrink-0 text-muted-foreground hover:text-foreground"
title={t("common.edit", "Edit")}
>
<Settings2 className="size-3.5" />
</button>
<button
onClick={() => handleDeleteChannel(channel.id)}
className="shrink-0 text-muted-foreground hover:text-destructive"
title={t("common.delete", "Delete")}
>
<Trash2 className="size-3.5" />
</button>
</div>
))}
</div>
</div>
)}
<AlertRuleDialog
open={ruleDialogOpen}
onOpenChange={setRuleDialogOpen}
rule={editRule}
channels={channels}
onSaved={() => {
setRuleDialogOpen(false);
loadRules();
}}
/>
<NotificationChannelDialog
open={channelDialogOpen}
onOpenChange={setChannelDialogOpen}
channel={editChannel}
onSaved={() => {
setChannelDialogOpen(false);
loadChannels();
}}
/>
</div>
);
}
+24 -74
View File
@@ -1,7 +1,6 @@
import { useState, useEffect } from "react";
import { useState, useEffect, type ReactElement } from "react";
import { useTranslation } from "react-i18next";
import {
Bell,
Check,
LogOut,
PanelRight,
@@ -12,7 +11,6 @@ import {
User,
} from "lucide-react";
import type { SplitMode, TabType, ToolsTab } from "@/types/ui-types";
import { getAlertFirings } from "@/api/alerts-api";
import { isElectron } from "@/lib/electron";
import { readRailPreference, setRailPreference } from "./rail-preferences";
import { visibleRailItems } from "./rail-items";
@@ -31,7 +29,6 @@ export type RailView =
| "session-logs"
| "user-profile"
| "admin-settings"
| "alerts"
| "automations"
| "ai"
| "fleets"
@@ -148,49 +145,6 @@ export function AppRail({
promotable?: boolean;
rightDockable?: boolean;
} | null>(null);
const [unreadAlerts, setUnreadAlerts] = useState(0);
useEffect(() => {
let cancelled = false;
let intervalId: ReturnType<typeof setInterval> | null = null;
const poll = () => {
if (document.visibilityState === "hidden") return;
getAlertFirings({ acknowledged: false, limit: 50 })
.then((firings) => {
if (!cancelled) setUnreadAlerts(firings.length);
})
.catch(() => {});
};
const start = () => {
if (intervalId !== null) return;
intervalId = setInterval(poll, 30000);
};
const stop = () => {
if (intervalId === null) return;
clearInterval(intervalId);
intervalId = null;
};
const onVisibility = () => {
if (document.visibilityState === "hidden") {
stop();
return;
}
poll();
start();
};
poll();
if (document.visibilityState !== "hidden") start();
document.addEventListener("visibilitychange", onVisibility);
return () => {
cancelled = true;
stop();
document.removeEventListener("visibilitychange", onVisibility);
};
}, []);
const [hiddenTabs, setHiddenTabs] = useState<Set<string>>(() => {
try {
const stored = localStorage.getItem("hiddenRailTabs");
@@ -457,28 +411,29 @@ export function AppRail({
style={{ width: railExpanded ? "calc(100% - 16px)" : 20 }}
/>
)}
{[
{
view: "alerts" as RailView,
icon: <Bell size={16} />,
title: t("nav.alerts"),
promotable: true,
},
{
view: "user-profile" as RailView,
icon: <User size={16} />,
title: t("nav.userProfile"),
},
...(isAdmin
? [
{
view: "admin-settings" as RailView,
icon: <Settings size={16} />,
title: t("nav.admin"),
},
]
: []),
].map((item) => (
{(
[
{
view: "user-profile" as RailView,
icon: <User size={16} />,
title: t("nav.userProfile"),
},
...(isAdmin
? [
{
view: "admin-settings" as RailView,
icon: <Settings size={16} />,
title: t("nav.admin"),
},
]
: []),
] as {
view: RailView;
icon: ReactElement;
title: string;
promotable?: boolean;
}[]
).map((item) => (
<button
key={item.view}
onClick={(e) => {
@@ -520,11 +475,6 @@ export function AppRail({
style={{ width: 16, height: 16 }}
>
{item.icon}
{item.view === "alerts" && unreadAlerts > 0 && (
<span className="absolute -top-1 -right-1 flex size-3 items-center justify-center rounded-full bg-destructive text-[8px] font-bold text-white leading-none">
{unreadAlerts > 9 ? "9+" : unreadAlerts}
</span>
)}
</span>
<span
className={`text-xs font-medium whitespace-nowrap overflow-hidden transition-[opacity,width] duration-150 ${railExpanded ? "opacity-100 delay-75" : "opacity-0 w-0"}`}
+1 -1
View File
@@ -14,7 +14,7 @@ import {
createNotificationChannel,
updateNotificationChannel,
type NotificationChannel,
} from "@/api/alerts-api";
} from "@/api/notification-channels-api";
interface NotificationChannelDialogProps {
open: boolean;
-7
View File
@@ -235,13 +235,6 @@ export function visibleRailItems(): RailItemDef[] {
* title and a mobile entry.
*/
export const RAIL_UTILITY_ITEMS: RailItemDef[] = [
{
id: "alerts",
icon: Plug,
labelKey: "nav.alerts",
promotable: true,
rightDockable: true,
},
{ id: "user-profile", icon: User, labelKey: "nav.userProfile" },
{ id: "admin-settings", icon: Settings, labelKey: "nav.admin" },
];
-45
View File
@@ -1,45 +0,0 @@
import { describe, expect, it } from "vitest";
import { mapAlertFiring } from "../../api/alerts-api";
describe("mapAlertFiring", () => {
it("normalizes sqlite snake_case rows for the alerts UI", () => {
expect(
mapAlertFiring({
id: 9,
user_id: "u1",
rule_id: 4,
host_id: 12,
host_name: "db-01",
fired_at: "2026-06-30 12:00:00",
resolved_at: null,
value: 91,
message: "CPU threshold exceeded",
severity: "critical",
acknowledged: 0,
rule_name: "CPU",
}),
).toEqual({
id: 9,
userId: "u1",
ruleId: 4,
hostId: 12,
hostName: "db-01",
firedAt: "2026-06-30 12:00:00",
resolvedAt: null,
value: 91,
message: "CPU threshold exceeded",
severity: "critical",
acknowledged: false,
ruleName: "CPU",
});
});
it("uses safe defaults for malformed rows", () => {
const firing = mapAlertFiring({ acknowledged: 1, severity: "bad" });
expect(firing.id).toBe(0);
expect(firing.hostName).toBe("");
expect(firing.severity).toBe("warning");
expect(firing.acknowledged).toBe(true);
});
});
+3 -3
View File
@@ -16,8 +16,8 @@ describe("humanize", () => {
});
it("splits snake_case into words", () => {
expect(humanize("propose_create_alert_rule")).toBe(
"Propose create alert rule",
expect(humanize("propose_create_automation")).toBe(
"Propose create automation",
);
});
@@ -51,7 +51,7 @@ describe("fieldLabel", () => {
describe("toolLabel", () => {
it("phrases tools as actions rather than function names", () => {
expect(toolLabel("list_hosts")).toBe("Reading hosts");
expect(toolLabel("propose_create_alert_rule")).toBe("Create alert rule");
expect(toolLabel("propose_create_fleet")).toBe("Create fleet");
});
it("covers every tool in the catalog", () => {
-131
View File
@@ -1,131 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import {
render,
screen,
waitFor,
cleanup,
fireEvent,
} from "@testing-library/react";
import type { NotificationChannel } from "@/api/alerts-api";
const alertsApi = vi.hoisted(() => ({
getAlertFirings: vi.fn(async () => []),
acknowledgeAlertFiring: vi.fn(async () => {}),
acknowledgeAllAlertFirings: vi.fn(async () => {}),
getAlertRules: vi.fn(async () => []),
getNotificationChannels: vi.fn(async () => [] as NotificationChannel[]),
deleteAlertRule: vi.fn(async () => {}),
deleteNotificationChannel: vi.fn(async () => {}),
testNotificationChannel: vi.fn(async () => {}),
}));
vi.mock("@/api/alerts-api", () => alertsApi);
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (_key: string, fallback?: string) => fallback ?? _key,
}),
}));
// A real (non-null) fake so tests can see what channels actually reach the
// rule dialog, instead of only asserting the API call count.
vi.mock("../../sidebar/AlertRuleDialog", () => ({
AlertRuleDialog: ({
open,
channels,
}: {
open: boolean;
channels: NotificationChannel[];
}) =>
open ? (
<div data-testid="rule-dialog-channels">
{channels.length === 0
? "no channels"
: channels.map((c) => c.name).join(",")}
</div>
) : null,
}));
vi.mock("../../sidebar/NotificationChannelDialog", () => ({
NotificationChannelDialog: () => null,
}));
import { AlertsPanel } from "../../sidebar/AlertsPanel";
function channel(
overrides: Partial<NotificationChannel> = {},
): NotificationChannel {
return {
id: 1,
userId: "user-1",
name: "ntfy",
type: "ntfy",
config: "{}",
enabled: true,
createdAt: new Date().toISOString(),
...overrides,
};
}
beforeEach(() => {
alertsApi.getAlertFirings.mockReset().mockResolvedValue([]);
alertsApi.getAlertRules.mockReset().mockResolvedValue([]);
alertsApi.getNotificationChannels.mockReset().mockResolvedValue([]);
});
afterEach(() => {
cleanup();
});
describe("AlertsPanel - notification channels", () => {
it("loads notification channels on mount, before the Channels tab is ever opened", async () => {
alertsApi.getNotificationChannels.mockResolvedValue([channel()]);
render(<AlertsPanel />);
// Regression: channels used to only load once the user switched to the
// "channels" tab, so editing a rule before ever visiting that tab showed
// an empty channel picker even though channels existed.
await waitFor(() => {
expect(alertsApi.getNotificationChannels).toHaveBeenCalledTimes(1);
});
});
it("populates the rule dialog's channel picker without ever visiting the Channels tab", async () => {
alertsApi.getNotificationChannels.mockResolvedValue([
channel({ id: 1, name: "ntfy-home" }),
channel({ id: 2, name: "webhook-slack" }),
]);
render(<AlertsPanel />);
await waitFor(() => {
expect(alertsApi.getNotificationChannels).toHaveBeenCalledTimes(1);
});
// Go straight to Rules and open the "new rule" dialog, skipping Channels
// entirely, this is exactly the sequence the original bug broke.
fireEvent.click(screen.getByText("Rules"));
fireEvent.click(screen.getByText("Add"));
expect(screen.getByTestId("rule-dialog-channels").textContent).toBe(
"ntfy-home,webhook-slack",
);
});
it("does not refetch channels again when switching to the Rules tab", async () => {
render(<AlertsPanel />);
await waitFor(() => {
expect(alertsApi.getNotificationChannels).toHaveBeenCalledTimes(1);
});
// Switching tabs only reloads rules here, not channels, since channels
// were already fetched on mount by the fix under test.
fireEvent.click(screen.getByText("Rules"));
await waitFor(() => {
expect(alertsApi.getAlertRules).toHaveBeenCalledTimes(1);
});
expect(alertsApi.getNotificationChannels).toHaveBeenCalledTimes(1);
});
});
@@ -29,10 +29,6 @@ vi.mock("react-i18next", () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));
vi.mock("@/api/alerts-api", () => ({
getAlertFirings: vi.fn().mockResolvedValue([]),
}));
vi.mock("@/hooks/use-ai-availability", () => ({
useAiAvailability: () => ({ userEnabled: false }),
}));
-2
View File
@@ -92,7 +92,6 @@ describe("RAIL_ITEMS", () => {
"ai",
"history",
"session-logs",
"alerts",
]);
});
@@ -138,7 +137,6 @@ describe("RAIL_ITEMS", () => {
"ai",
"history",
"session-logs",
"alerts",
]);
});
});