Merge pull request #158 from sqkkyzx/develop
refactory(i18n): Improve internationalization configuration and add new translations
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Clock, TimerOff } from "lucide-react";
|
import { Clock, TimerOff } from "lucide-react";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
|
||||||
interface LastCheckedTimeProps {
|
interface LastCheckedTimeProps {
|
||||||
lastCheckedTime: string;
|
lastCheckedTime: string;
|
||||||
@@ -10,6 +11,7 @@ interface LastCheckedTimeProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheckedTimeProps) => {
|
export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheckedTimeProps) => {
|
||||||
|
const { t } = useLanguage();
|
||||||
// Format the time without seconds to display a static time
|
// Format the time without seconds to display a static time
|
||||||
const formatTimeWithoutSeconds = (timeString: string) => {
|
const formatTimeWithoutSeconds = (timeString: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -55,7 +57,7 @@ export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheck
|
|||||||
<Clock className="h-4 w-4" />
|
<Clock className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
<span>
|
<span>
|
||||||
{isPaused ? "Paused at " : ""}
|
{isPaused ? t("pausedAt") : ""}
|
||||||
{formattedTime}
|
{formattedTime}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,14 +68,14 @@ export const LastCheckedTime = ({ lastCheckedTime, status, interval }: LastCheck
|
|||||||
>
|
>
|
||||||
<div className="flex flex-col gap-1 text-xs">
|
<div className="flex flex-col gap-1 text-xs">
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{isPaused ? "Monitoring Paused" : "Last Check Details"}
|
{isPaused ? t("monitoringPaused") : t("lastCheckDetails")}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{isPaused ? "No automatic checks" : `Checked at ${formattedTime}`}
|
{isPaused ? t("noAutomaticChecks") : t("checkedAt") + `${formattedTime}`}
|
||||||
</div>
|
</div>
|
||||||
{interval && !isPaused && (
|
{interval && !isPaused && (
|
||||||
<div>
|
<div>
|
||||||
Check interval: {formatInterval(interval)}
|
{t("checkInterval")}: {formatInterval(interval)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-gray-400 text-[10px]">
|
<div className="text-gray-400 text-[10px]">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Service } from "@/types/service.types";
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
|
||||||
interface ServiceEditDialogProps {
|
interface ServiceEditDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -19,6 +20,7 @@ interface ServiceEditDialogProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ServiceEditDialog({ open, onOpenChange, service }: ServiceEditDialogProps) {
|
export function ServiceEditDialog({ open, onOpenChange, service }: ServiceEditDialogProps) {
|
||||||
|
const { t } = useLanguage();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
@@ -53,9 +55,9 @@ export function ServiceEditDialog({ open, onOpenChange, service }: ServiceEditDi
|
|||||||
}}>
|
}}>
|
||||||
<DialogContent className="sm:max-w-[700px] max-h-[90vh] flex flex-col">
|
<DialogContent className="sm:max-w-[700px] max-h-[90vh] flex flex-col">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="text-xl">Edit Service</DialogTitle>
|
<DialogTitle className="text-xl">{t("editService")}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Update the details of your monitored service.
|
{t("editServiceDesc")}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{open && service && (
|
{open && service && (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Service, UptimeData } from "@/types/service.types";
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { formatDistanceStrict } from "date-fns";
|
import { formatDistanceStrict } from "date-fns";
|
||||||
|
import {useLanguage} from "@/contexts/LanguageContext.tsx";
|
||||||
|
|
||||||
interface ServiceStatsCardsProps {
|
interface ServiceStatsCardsProps {
|
||||||
service: Service;
|
service: Service;
|
||||||
@@ -11,6 +12,8 @@ interface ServiceStatsCardsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProps) {
|
export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProps) {
|
||||||
|
|
||||||
|
const { t } = useLanguage();
|
||||||
// Calculate uptime percentage
|
// Calculate uptime percentage
|
||||||
const calculateUptimePercentage = () => {
|
const calculateUptimePercentage = () => {
|
||||||
if (uptimeData.length === 0) return 100;
|
if (uptimeData.length === 0) return 100;
|
||||||
@@ -109,41 +112,46 @@ export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProp
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||||
<Card className="border-blue-400 dark:border-blue-700 bg-blue-50 dark:bg-blue-900/50 shadow-md">
|
<Card className="border-blue-400 dark:border-blue-700 bg-blue-50 dark:bg-blue-900/50 shadow-md">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-sm font-medium text-blue-800 dark:text-blue-100">Response Time</CardTitle>
|
<CardTitle className="text-sm font-medium text-blue-800 dark:text-blue-100">{t('responseTime')}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold text-blue-700 dark:text-white">
|
<div className="text-2xl font-bold text-blue-700 dark:text-white">
|
||||||
{service.responseTime}ms
|
{service.responseTime}ms
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-blue-600 dark:text-blue-200 mt-1">Last checked at {service.lastChecked}</p>
|
<p className="text-xs text-blue-600 dark:text-blue-200 mt-1">{t('lastCheckedAt').replace('{datetime}', service.lastChecked)}</p>
|
||||||
{avgResponseTime > 0 && avgResponseTime !== service.responseTime && (
|
{avgResponseTime > 0 && avgResponseTime !== service.responseTime && (
|
||||||
<p className="text-xs text-blue-600 dark:text-blue-200 mt-1">Avg: {avgResponseTime}ms (last {upChecks.length} up checks)</p>
|
<p className="text-xs text-blue-600 dark:text-blue-200 mt-1">{t('avg')}: {avgResponseTime}ms ({t('lastUpChecksCount').replace('{count}', String(upChecks.length))})</p>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="border-green-400 dark:border-green-700 bg-green-50 dark:bg-green-900/50 shadow-md">
|
<Card className="border-green-400 dark:border-green-700 bg-green-50 dark:bg-green-900/50 shadow-md">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-sm font-medium text-green-800 dark:text-green-100">Uptime</CardTitle>
|
<CardTitle className="text-sm font-medium text-green-800 dark:text-green-100">{t('uptime')}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold text-green-700 dark:text-white">
|
<div className="text-2xl font-bold text-green-700 dark:text-white">
|
||||||
{uptimePercentage}%
|
{uptimePercentage}%
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-green-600 dark:text-green-200 mt-1">Based on last {uptimeData.length} checks</p>
|
<p className="text-xs text-green-600 dark:text-green-200 mt-1">{t('basedOnlastChecksCount').replace('{count}', String(uptimeData.length))}</p>
|
||||||
<div className="flex flex-col space-y-1 mt-2">
|
<div className="flex flex-col space-y-1 mt-2">
|
||||||
<div className="flex items-center text-xs text-green-600 dark:text-green-200">
|
<div className="flex items-center text-xs text-green-600 dark:text-green-200">
|
||||||
<ArrowUp className="h-3 w-3 mr-1 text-green-600 dark:text-green-400" />
|
<ArrowUp className="h-3 w-3 mr-1 text-green-600 dark:text-green-400" />
|
||||||
<span>Total uptime: {uptimeStats.totalUptimeFormatted}</span>
|
<span>{t("totalUptime")}: {uptimeStats.totalUptimeFormatted}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-xs text-red-600 dark:text-red-200">
|
<div className="flex items-center text-xs text-red-600 dark:text-red-200">
|
||||||
<ArrowDown className="h-3 w-3 mr-1 text-red-600 dark:text-red-400" />
|
<ArrowDown className="h-3 w-3 mr-1 text-red-600 dark:text-red-400" />
|
||||||
<span>Total downtime: {uptimeStats.totalDowntimeFormatted}</span>
|
<span>{t("totalDowntime")}: {uptimeStats.totalDowntimeFormatted}</span>
|
||||||
</div>
|
</div>
|
||||||
{service.status !== "paused" && (
|
{service.status !== "paused" && (
|
||||||
<div className="flex items-center text-xs font-medium mt-1">
|
<div className="flex items-center text-xs font-medium mt-1">
|
||||||
<span className={service.status === "up" ? "text-green-600 dark:text-green-400" : "text-red-600 dark:text-red-400"}>
|
<span className={service.status === "up" ? "text-green-600 dark:text-green-400" : "text-red-600 dark:text-red-400"}>
|
||||||
{service.status === "up" ? "Up" : "Down"} for {uptimeStats.currentStatusDuration}
|
{
|
||||||
|
service.status === "up" ?
|
||||||
|
t('upStatusDuration').replace("{duration}", uptimeStats.currentStatusDuration)
|
||||||
|
:
|
||||||
|
t('downStatusDuration').replace("{duration}", uptimeStats.currentStatusDuration)
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -153,16 +161,16 @@ export function ServiceStatsCards({ service, uptimeData }: ServiceStatsCardsProp
|
|||||||
|
|
||||||
<Card className="border-purple-400 dark:border-purple-700 bg-purple-50 dark:bg-purple-900/50 shadow-md">
|
<Card className="border-purple-400 dark:border-purple-700 bg-purple-50 dark:bg-purple-900/50 shadow-md">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-sm font-medium text-purple-800 dark:text-purple-100">Monitoring Settings</CardTitle>
|
<CardTitle className="text-sm font-medium text-purple-800 dark:text-purple-100">{t('monitoringSettings')}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex items-center text-sm text-purple-700 dark:text-purple-200">
|
<div className="flex items-center text-sm text-purple-700 dark:text-purple-200">
|
||||||
<Clock className="h-4 w-4 mr-2 text-purple-600 dark:text-purple-400" />
|
<Clock className="h-4 w-4 mr-2 text-purple-600 dark:text-purple-400" />
|
||||||
<span>Checked every {service.interval} seconds</span>
|
<span>{t('monitoringSettingsInterval').replace('{interval}', String(service.interval))}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-sm text-purple-700 dark:text-purple-200 mt-1">
|
<div className="flex items-center text-sm text-purple-700 dark:text-purple-200 mt-1">
|
||||||
<Server className="h-4 w-4 mr-2 text-purple-600 dark:text-purple-400" />
|
<Server className="h-4 w-4 mr-2 text-purple-600 dark:text-purple-400" />
|
||||||
<span>{service.type} monitoring</span>
|
<span>{service.type} {t('monitoringSettingsType')}</span>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { TooltipProvider } from '@/components/ui/tooltip';
|
|||||||
import { UptimeStatusItem } from './uptime/UptimeStatusItem';
|
import { UptimeStatusItem } from './uptime/UptimeStatusItem';
|
||||||
import { uptimeService } from '@/services/uptimeService';
|
import { uptimeService } from '@/services/uptimeService';
|
||||||
import { UptimeData } from '@/types/service.types';
|
import { UptimeData } from '@/types/service.types';
|
||||||
|
import {useLanguage} from "@/contexts/LanguageContext.tsx";
|
||||||
|
|
||||||
interface UptimeBarProps {
|
interface UptimeBarProps {
|
||||||
uptime: number;
|
uptime: number;
|
||||||
@@ -15,6 +16,7 @@ interface UptimeBarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const UptimeBarComponent = ({ uptime, status, serviceId, interval, serviceType = "HTTP" }: UptimeBarProps) => {
|
const UptimeBarComponent = ({ uptime, status, serviceId, interval, serviceType = "HTTP" }: UptimeBarProps) => {
|
||||||
|
const { t } = useLanguage();
|
||||||
// Calculate date range for last 20 checks with much more aggressive caching
|
// Calculate date range for last 20 checks with much more aggressive caching
|
||||||
const endDate = useMemo(() => new Date(), []);
|
const endDate = useMemo(() => new Date(), []);
|
||||||
const startDate = useMemo(() => {
|
const startDate = useMemo(() => {
|
||||||
@@ -84,7 +86,7 @@ const UptimeBarComponent = ({ uptime, status, serviceId, interval, serviceType =
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
Last 20 checks
|
{t('last20Checks')}
|
||||||
</span>
|
</span>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { MouseEvent } from "react";
|
import { MouseEvent } from "react";
|
||||||
|
import {useLanguage} from "@/contexts/LanguageContext.tsx";
|
||||||
|
|
||||||
interface ServiceFormActionsProps {
|
interface ServiceFormActionsProps {
|
||||||
isSubmitting: boolean;
|
isSubmitting: boolean;
|
||||||
@@ -14,6 +15,8 @@ export function ServiceFormActions({
|
|||||||
onCancel,
|
onCancel,
|
||||||
submitLabel = "Create Service"
|
submitLabel = "Create Service"
|
||||||
}: ServiceFormActionsProps) {
|
}: ServiceFormActionsProps) {
|
||||||
|
|
||||||
|
const {t} = useLanguage();
|
||||||
const handleCancel = (e: MouseEvent<HTMLButtonElement>) => {
|
const handleCancel = (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!isSubmitting) {
|
if (!isSubmitting) {
|
||||||
@@ -29,7 +32,7 @@ export function ServiceFormActions({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
Cancel
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -38,7 +41,7 @@ export function ServiceFormActions({
|
|||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
Processing...
|
{t("processing")}...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
submitLabel
|
submitLabel
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ export function IncidentTable({ incidents }: IncidentTableProps) {
|
|||||||
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("time")}</TableHead>
|
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("time")}</TableHead>
|
||||||
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("status")}</TableHead>
|
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("status")}</TableHead>
|
||||||
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("responseTime")}</TableHead>
|
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("responseTime")}</TableHead>
|
||||||
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>Error Message</TableHead>
|
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("errorMessage")}</TableHead>
|
||||||
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>Details</TableHead>
|
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>{t("details")}</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import { IncidentTable } from "./IncidentTable";
|
|||||||
import { StatusFilter, PageSize } from "./types";
|
import { StatusFilter, PageSize } from "./types";
|
||||||
import { getStatusChangeEvents } from "./utils";
|
import { getStatusChangeEvents } from "./utils";
|
||||||
import { useTheme } from "@/contexts/ThemeContext";
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
|
||||||
export function LatestChecksTable({ uptimeData }: { uptimeData: UptimeData[] }) {
|
export function LatestChecksTable({ uptimeData }: { uptimeData: UptimeData[] }) {
|
||||||
// Get current theme
|
// Get current theme
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
// Filter state
|
// Filter state
|
||||||
const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
|
const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
|
||||||
@@ -65,7 +67,7 @@ export function LatestChecksTable({ uptimeData }: { uptimeData: UptimeData[] })
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||||
<CardTitle className="text-card-foreground">
|
<CardTitle className="text-card-foreground">
|
||||||
<span>Incident History</span>
|
<span>{t("incidentHistory")}</span>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<StatusFilterTabs statusFilter={statusFilter} onStatusFilterChange={setStatusFilter} />
|
<StatusFilterTabs statusFilter={statusFilter} onStatusFilterChange={setStatusFilter} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
SelectValue
|
SelectValue
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { PageSize } from "./types";
|
import { PageSize } from "./types";
|
||||||
|
import {useLanguage} from "@/contexts/LanguageContext.tsx";
|
||||||
|
|
||||||
interface TablePaginationProps {
|
interface TablePaginationProps {
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@@ -35,6 +36,7 @@ export function TablePagination({
|
|||||||
onPageChange,
|
onPageChange,
|
||||||
onPageSizeChange,
|
onPageSizeChange,
|
||||||
}: TablePaginationProps) {
|
}: TablePaginationProps) {
|
||||||
|
const {t} = useLanguage()
|
||||||
// Generate page numbers
|
// Generate page numbers
|
||||||
const getPageNumbers = () => {
|
const getPageNumbers = () => {
|
||||||
const pages = [];
|
const pages = [];
|
||||||
@@ -58,7 +60,7 @@ export function TablePagination({
|
|||||||
return (
|
return (
|
||||||
<div className="mt-4 flex items-center justify-between">
|
<div className="mt-4 flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span className="text-sm text-muted-foreground">Rows per page:</span>
|
<span className="text-sm text-muted-foreground">{t("rowsPerPage")}:</span>
|
||||||
<Select
|
<Select
|
||||||
value={pageSize}
|
value={pageSize}
|
||||||
onValueChange={(value) => onPageSizeChange(value as PageSize)}
|
onValueChange={(value) => onPageSizeChange(value as PageSize)}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import { Service } from "@/types/service.types";
|
import { Service } from "@/types/service.types";
|
||||||
import { serviceService } from "@/services/serviceService";
|
import { serviceService } from "@/services/serviceService";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
|
||||||
interface ServiceRowActionsProps {
|
interface ServiceRowActionsProps {
|
||||||
service: Service;
|
service: Service;
|
||||||
@@ -31,6 +32,7 @@ export const ServiceRowActions = ({
|
|||||||
onMuteAlerts
|
onMuteAlerts
|
||||||
}: ServiceRowActionsProps) => {
|
}: ServiceRowActionsProps) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
// Handle pause/resume directly from dropdown
|
// Handle pause/resume directly from dropdown
|
||||||
const handlePauseResume = async (e: React.MouseEvent) => {
|
const handlePauseResume = async (e: React.MouseEvent) => {
|
||||||
@@ -122,7 +124,7 @@ export const ServiceRowActions = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Eye className="h-4 w-4" />
|
<Eye className="h-4 w-4" />
|
||||||
<span>View Detail</span>
|
<span>{t("viewDetail")}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="flex items-center gap-2 cursor-pointer text-base py-2.5"
|
className="flex items-center gap-2 cursor-pointer text-base py-2.5"
|
||||||
@@ -131,12 +133,12 @@ export const ServiceRowActions = ({
|
|||||||
{service.status === "paused" ? (
|
{service.status === "paused" ? (
|
||||||
<>
|
<>
|
||||||
<Play className="h-4 w-4" />
|
<Play className="h-4 w-4" />
|
||||||
<span>Resume Monitoring</span>
|
<span>{t("resumeMonitoring")}</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Pause className="h-4 w-4" />
|
<Pause className="h-4 w-4" />
|
||||||
<span>Pause Monitoring</span>
|
<span>{t("pauseMonitoring")}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@@ -148,7 +150,7 @@ export const ServiceRowActions = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Edit className="h-4 w-4" />
|
<Edit className="h-4 w-4" />
|
||||||
<span>Edit</span>
|
<span>{t("edit")}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="flex items-center gap-2 cursor-pointer text-base py-2.5"
|
className="flex items-center gap-2 cursor-pointer text-base py-2.5"
|
||||||
@@ -157,12 +159,12 @@ export const ServiceRowActions = ({
|
|||||||
{alertsMuted ? (
|
{alertsMuted ? (
|
||||||
<>
|
<>
|
||||||
<Bell className="h-4 w-4" />
|
<Bell className="h-4 w-4" />
|
||||||
<span>Unmute Alerts</span>
|
<span>{t("unmuteAlerts")}</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<BellOff className="h-4 w-4" />
|
<BellOff className="h-4 w-4" />
|
||||||
<span>Mute Alerts</span>
|
<span>{t("muteAlerts")}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
@@ -175,7 +177,7 @@ export const ServiceRowActions = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
<span>Delete</span>
|
<span>{t("delete")}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {useLanguage} from "@/contexts/LanguageContext.tsx";
|
||||||
|
|
||||||
interface UptimeSummaryProps {
|
interface UptimeSummaryProps {
|
||||||
uptime: number;
|
uptime: number;
|
||||||
@@ -7,13 +8,14 @@ interface UptimeSummaryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const UptimeSummary = ({ uptime, interval }: UptimeSummaryProps) => {
|
export const UptimeSummary = ({ uptime, interval }: UptimeSummaryProps) => {
|
||||||
|
const { t } = useLanguage();
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between text-xs mt-1">
|
<div className="flex items-center justify-between text-xs mt-1">
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
{Math.round(uptime)}% uptime
|
{Math.round(uptime)}% uptime
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
Last 20 checks
|
{t('last20Checks')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -355,9 +355,9 @@ export const AboutSystem: React.FC = () => {
|
|||||||
<CardHeader className="bg-muted/50 pb-4">
|
<CardHeader className="bg-muted/50 pb-4">
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<Database className={`h-5 w-5 ${theme === 'dark' ? 'text-blue-400' : 'text-blue-600'}`} />
|
<Database className={`h-5 w-5 ${theme === 'dark' ? 'text-blue-400' : 'text-blue-600'}`} />
|
||||||
<span>Update Schema</span>
|
<span>{t('updateSchema')}</span>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="font-medium text-base">Automatic import collections schema</CardDescription>
|
<CardDescription className="font-medium text-base">{t('updateSchemaDesc')}</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4 pt-6">
|
<CardContent className="space-y-4 pt-6">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -368,7 +368,7 @@ export const AboutSystem: React.FC = () => {
|
|||||||
onCheckedChange={(checked) => setMergeFields(checked === true)}
|
onCheckedChange={(checked) => setMergeFields(checked === true)}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="merge-fields" className="text-sm font-medium">
|
<Label htmlFor="merge-fields" className="text-sm font-medium">
|
||||||
Merge fields with existing collections (safe - preserves data)
|
{t('mergeFieldsLabel')}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -382,7 +382,7 @@ export const AboutSystem: React.FC = () => {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Database className={`h-4 w-4 ${isImporting ? 'animate-spin' : ''}`} />
|
<Database className={`h-4 w-4 ${isImporting ? 'animate-spin' : ''}`} />
|
||||||
{isImporting ? 'Importing...' : 'Click to update Schema'}
|
{isImporting ? t('importing') : t('clickToUpdateSchema')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -403,7 +403,7 @@ export const AboutSystem: React.FC = () => {
|
|||||||
? 'text-green-800 dark:text-green-200'
|
? 'text-green-800 dark:text-green-200'
|
||||||
: 'text-red-800 dark:text-red-200'
|
: 'text-red-800 dark:text-red-200'
|
||||||
}`}>
|
}`}>
|
||||||
{importResult.success ? 'Import Successful' : 'Import Failed'}
|
{importResult.success ? t('importSuccessful') : t('importFailed')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={`mt-2 text-sm ${
|
<div className={`mt-2 text-sm ${
|
||||||
@@ -411,21 +411,21 @@ export const AboutSystem: React.FC = () => {
|
|||||||
? 'text-green-700 dark:text-green-300'
|
? 'text-green-700 dark:text-green-300'
|
||||||
: 'text-red-700 dark:text-red-300'
|
: 'text-red-700 dark:text-red-300'
|
||||||
}`}>
|
}`}>
|
||||||
{importResult.created > 0 && `${importResult.created} collections created`}
|
{importResult.created > 0 && t('collectionsCreatedCount').replace('{count}', String(importResult.created))}
|
||||||
{importResult.updated > 0 && (importResult.created > 0 ? ', ' : '') + `${importResult.updated} collections updated`}
|
{importResult.updated > 0 && (importResult.created > 0 ? ', ' : '') + t('collectionsUpdatedCount').replace('{count}', String(importResult.updated))}
|
||||||
{importResult.skipped > 0 && ((importResult.created > 0 || importResult.updated > 0) ? ', ' : '') + `${importResult.skipped} collections skipped`}
|
{importResult.skipped > 0 && ((importResult.created > 0 || importResult.updated > 0) ? ', ' : '') + t('collectionsSkippedCount').replace('{count}', String(importResult.skipped))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
<p className="mb-2">
|
<p className="mb-2">
|
||||||
<strong>Instructions:</strong>
|
<strong>{t('instructions')}:</strong>
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc list-inside space-y-1 ml-2">
|
<ul className="list-disc list-inside space-y-1 ml-2">
|
||||||
<li><strong>Merge fields:</strong> Safely add new fields to existing collections, preserves all data</li>
|
<li><strong>{t('mergeFields')}:</strong> {t('instructionsMergeFields')}</li>
|
||||||
<li>System collections (starting with _) and users collection will be skipped automatically</li>
|
<li>{t('instructionsCollections')}</li>
|
||||||
<li>Only authenticated admins can perform schema imports</li>
|
<li>{t('instructionsImportAuth')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { ScrollArea } from "@/components/ui/scroll-area";
|
|||||||
import { TemplateType, templateTypeConfigs } from "@/services/templateService";
|
import { TemplateType, templateTypeConfigs } from "@/services/templateService";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import {z} from "zod";
|
||||||
|
|
||||||
interface TemplateDialogProps {
|
interface TemplateDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -81,6 +82,7 @@ export const TemplateDialog: React.FC<TemplateDialogProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderPlaceholderGuide = () => {
|
const renderPlaceholderGuide = () => {
|
||||||
|
|
||||||
const config = templateTypeConfigs[selectedTemplateType];
|
const config = templateTypeConfigs[selectedTemplateType];
|
||||||
if (!config) return null;
|
if (!config) return null;
|
||||||
|
|
||||||
|
|||||||
+2
@@ -27,6 +27,7 @@ import { Switch } from "@/components/ui/switch";
|
|||||||
import { Loader2, Copy } from "lucide-react";
|
import { Loader2, Copy } from "lucide-react";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { toast } from "@/hooks/use-toast";
|
import { toast } from "@/hooks/use-toast";
|
||||||
|
import { useLanguage } from "@/contexts/LanguageContext";
|
||||||
|
|
||||||
interface NotificationChannelDialogProps {
|
interface NotificationChannelDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -34,6 +35,7 @@ interface NotificationChannelDialogProps {
|
|||||||
editingConfig: AlertConfiguration | null;
|
editingConfig: AlertConfiguration | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const baseSchema = z.object({
|
const baseSchema = z.object({
|
||||||
notify_name: z.string().min(1, "Name is required"),
|
notify_name: z.string().min(1, "Name is required"),
|
||||||
notification_type: z.enum(["telegram", "discord", "slack", "signal", "google_chat", "email", "ntfy", "pushover", "notifiarr", "gotify", "webhook"]),
|
notification_type: z.enum(["telegram", "discord", "slack", "signal", "google_chat", "email", "ntfy", "pushover", "notifiarr", "gotify", "webhook"]),
|
||||||
|
|||||||
@@ -16,4 +16,19 @@ export const aboutTranslations: AboutTranslations = {
|
|||||||
quickActionsDescription: "Access common monitoring operations and features quickly. Select an action below to get started.",
|
quickActionsDescription: "Access common monitoring operations and features quickly. Select an action below to get started.",
|
||||||
quickTips: "Quick Tips",
|
quickTips: "Quick Tips",
|
||||||
releasedOn: "Released On",
|
releasedOn: "Released On",
|
||||||
|
updateSchema: "Update Schema",
|
||||||
|
updateSchemaDesc: "Automatic import collections schema",
|
||||||
|
mergeFieldsLabel: "Merge fields with existing collections (safe - preserves data)",
|
||||||
|
importing: "Importing...",
|
||||||
|
clickToUpdateSchema: "Click to update Schema",
|
||||||
|
importSuccessful: "Import Successful",
|
||||||
|
importFailed: "Import Failed",
|
||||||
|
instructions: "Instructions",
|
||||||
|
mergeFields: "Merge fields",
|
||||||
|
instructionsMergeFields: "Safely add new fields to existing collections, preserves all data",
|
||||||
|
instructionsCollections: "System collections (starting with _) and users collection will be skipped automatically",
|
||||||
|
instructionsImportAuth: "Only authenticated admins can perform schema imports",
|
||||||
|
collectionsUpdatedCount: "{count} collections updated",
|
||||||
|
collectionsCreatedCount: "{count} collections created",
|
||||||
|
collectionsSkippedCount: "{count} collections created",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { ServicesTranslations } from '../types/services';
|
|||||||
|
|
||||||
export const servicesTranslations: ServicesTranslations = {
|
export const servicesTranslations: ServicesTranslations = {
|
||||||
serviceStatus: "Service Status",
|
serviceStatus: "Service Status",
|
||||||
responseTime: "Response Time",
|
|
||||||
uptime: "Uptime",
|
uptime: "Uptime",
|
||||||
lastChecked: "Last Checked",
|
lastChecked: "Last Checked",
|
||||||
noServices: "No services match your filter criteria.",
|
noServices: "No services match your filter criteria.",
|
||||||
@@ -95,4 +94,46 @@ export const servicesTranslations: ServicesTranslations = {
|
|||||||
downServices: "DOWN SERVICES",
|
downServices: "DOWN SERVICES",
|
||||||
pausedServices: "PAUSED SERVICES",
|
pausedServices: "PAUSED SERVICES",
|
||||||
warningServices: "WARNING SERVICES",
|
warningServices: "WARNING SERVICES",
|
||||||
|
|
||||||
|
// ServiceRowActions.tsx
|
||||||
|
viewDetail: "View Detail",
|
||||||
|
resumeMonitoring: "Resume Monitoring",
|
||||||
|
pauseMonitoring: "Pause Monitoring",
|
||||||
|
|
||||||
|
|
||||||
|
//IncidentTable.tsx
|
||||||
|
responseTime: "Response Time",
|
||||||
|
errorMessage: "Error Message",
|
||||||
|
details: "Details",
|
||||||
|
unmuteAlerts: "Unmute Alerts",
|
||||||
|
muteAlerts: "Mute Alerts",
|
||||||
|
|
||||||
|
//LastCheckedTime.tsx
|
||||||
|
pausedAt: "Paused at ",
|
||||||
|
lastCheckDetails: "Last Check Details",
|
||||||
|
checkedAt: "Checked at ",
|
||||||
|
monitoringPaused: "Monitoring Paused",
|
||||||
|
noAutomaticChecks: "No automatic checks",
|
||||||
|
|
||||||
|
//ServiveEditDialog.tsx
|
||||||
|
editService: "Edit Service",
|
||||||
|
editServiceDesc: "Update the details of your monitored service.",
|
||||||
|
|
||||||
|
//ServiceStatsCards.tsx
|
||||||
|
lastCheckedAt: "Last checked at {datetime}",
|
||||||
|
avg: "Avg",
|
||||||
|
lastUpChecksCount: "last {count} up checks",
|
||||||
|
basedOnlastChecksCount: "Based on last {count} checks",
|
||||||
|
totalUptime: "Total Uptime",
|
||||||
|
totalDowntime: "Total Downtime",
|
||||||
|
monitoringSettings: "Monitoring Settings",
|
||||||
|
monitoringSettingsInterval: "Checked every {interval} seconds",
|
||||||
|
monitoringSettingsType: "monitoring",
|
||||||
|
upStatusDuration: "Up for {duration}",
|
||||||
|
downStatusDuration: "Down for {duration}",
|
||||||
|
|
||||||
|
incidentHistory: "Incident History",
|
||||||
|
processing: "Processing",
|
||||||
|
back: "Back",
|
||||||
|
last20Checks: "Last 20 checks",
|
||||||
};
|
};
|
||||||
@@ -14,4 +14,19 @@ export interface AboutTranslations {
|
|||||||
quickActionsDescription: string;
|
quickActionsDescription: string;
|
||||||
quickTips: string;
|
quickTips: string;
|
||||||
releasedOn: string;
|
releasedOn: string;
|
||||||
|
updateSchema: string;
|
||||||
|
updateSchemaDesc: string;
|
||||||
|
mergeFieldsLabel: string;
|
||||||
|
importing: string;
|
||||||
|
clickToUpdateSchema: string;
|
||||||
|
importSuccessful: string;
|
||||||
|
importFailed: string;
|
||||||
|
instructions: string;
|
||||||
|
mergeFields: string;
|
||||||
|
instructionsMergeFields: string;
|
||||||
|
instructionsCollections: string;
|
||||||
|
instructionsImportAuth: string;
|
||||||
|
collectionsUpdatedCount: string;
|
||||||
|
collectionsCreatedCount: string;
|
||||||
|
collectionsSkippedCount: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,4 +59,5 @@ export interface IncidentTranslations {
|
|||||||
affectedSystems: string;
|
affectedSystems: string;
|
||||||
enterAffectedSystems: string;
|
enterAffectedSystems: string;
|
||||||
separateSystemsWithComma: string;
|
separateSystemsWithComma: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
export interface ServicesTranslations {
|
export interface ServicesTranslations {
|
||||||
|
|
||||||
serviceStatus: string;
|
serviceStatus: string;
|
||||||
responseTime: string;
|
|
||||||
uptime: string;
|
uptime: string;
|
||||||
lastChecked: string;
|
lastChecked: string;
|
||||||
noServices: string;
|
noServices: string;
|
||||||
@@ -93,5 +92,46 @@ export interface ServicesTranslations {
|
|||||||
upServices: string;
|
upServices: string;
|
||||||
downServices: string;
|
downServices: string;
|
||||||
pausedServices: string;
|
pausedServices: string;
|
||||||
warningServices: string;
|
warningServices: string;
|
||||||
|
|
||||||
|
// ServiceRowActions.tsx
|
||||||
|
viewDetail: string;
|
||||||
|
resumeMonitoring: string;
|
||||||
|
pauseMonitoring: string;
|
||||||
|
unmuteAlerts: string;
|
||||||
|
muteAlerts: string;
|
||||||
|
|
||||||
|
//IncidentTable.tsx
|
||||||
|
responseTime: string;
|
||||||
|
errorMessage: string;
|
||||||
|
details: string;
|
||||||
|
|
||||||
|
//LastCheckedTime.tsx
|
||||||
|
pausedAt: string;
|
||||||
|
lastCheckDetails: string;
|
||||||
|
checkedAt: string;
|
||||||
|
monitoringPaused: string;
|
||||||
|
noAutomaticChecks: string;
|
||||||
|
|
||||||
|
//ServiveEditDialog.tsx
|
||||||
|
editService: string;
|
||||||
|
editServiceDesc: string;
|
||||||
|
|
||||||
|
//ServiceStatsCards.tsx
|
||||||
|
lastCheckedAt: string;
|
||||||
|
avg: string;
|
||||||
|
lastUpChecksCount: string;
|
||||||
|
basedOnlastChecksCount: string;
|
||||||
|
totalUptime: string;
|
||||||
|
totalDowntime: string;
|
||||||
|
monitoringSettings: string;
|
||||||
|
monitoringSettingsInterval: string;
|
||||||
|
monitoringSettingsType: string;
|
||||||
|
upStatusDuration: string;
|
||||||
|
downStatusDuration: string;
|
||||||
|
|
||||||
|
incidentHistory: string;
|
||||||
|
processing: string;
|
||||||
|
back: string;
|
||||||
|
last20Checks: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,19 @@ export const aboutTranslations: AboutTranslations = {
|
|||||||
quickActionsDescription: "快速访问常用的监控操作和功能。选择下面的操作开始。",
|
quickActionsDescription: "快速访问常用的监控操作和功能。选择下面的操作开始。",
|
||||||
quickTips: "快速提示",
|
quickTips: "快速提示",
|
||||||
releasedOn: "发布于",
|
releasedOn: "发布于",
|
||||||
|
updateSchema: "更新架构",
|
||||||
|
updateSchemaDesc: "自动导入集合架构",
|
||||||
|
mergeFieldsLabel: "合并字段到现有集合(安全 - 保留数据)",
|
||||||
|
importing: "导入中...",
|
||||||
|
clickToUpdateSchema: "点击更新架构",
|
||||||
|
importSuccessful: "导入成功",
|
||||||
|
importFailed: "导入失败",
|
||||||
|
instructions: "说明",
|
||||||
|
mergeFields: "合并字段",
|
||||||
|
instructionsMergeFields: "安全地向现有集合添加新字段,保留所有数据",
|
||||||
|
instructionsCollections: "系统集合(以_开头的)和用户集合将被自动跳过",
|
||||||
|
instructionsImportAuth: "只有经过身份验证的管理员才能执行架构导入",
|
||||||
|
collectionsUpdatedCount: "已更新 {count} 个集合",
|
||||||
|
collectionsCreatedCount: "已创建 {count} 个集合",
|
||||||
|
collectionsSkippedCount: "已跳过 {count} 个集合",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { ServicesTranslations } from '../types/services';
|
|||||||
|
|
||||||
export const servicesTranslations: ServicesTranslations = {
|
export const servicesTranslations: ServicesTranslations = {
|
||||||
serviceStatus: "服务状态",
|
serviceStatus: "服务状态",
|
||||||
responseTime: "响应时间",
|
|
||||||
uptime: "在线时间",
|
uptime: "在线时间",
|
||||||
lastChecked: "最后检查时间",
|
lastChecked: "最后检查时间",
|
||||||
noServices: "没有符合您筛选条件的服务。",
|
noServices: "没有符合您筛选条件的服务。",
|
||||||
@@ -95,4 +94,45 @@ export const servicesTranslations: ServicesTranslations = {
|
|||||||
downServices: "下线服务",
|
downServices: "下线服务",
|
||||||
pausedServices: "暂停服务",
|
pausedServices: "暂停服务",
|
||||||
warningServices: "告警服务",
|
warningServices: "告警服务",
|
||||||
|
|
||||||
|
// ServiceRowActions.tsx
|
||||||
|
viewDetail: "查看详情",
|
||||||
|
resumeMonitoring: "恢复监控",
|
||||||
|
pauseMonitoring: "暂停监控",
|
||||||
|
unmuteAlerts: "取消静音警报",
|
||||||
|
muteAlerts: "静音警报",
|
||||||
|
|
||||||
|
//IncidentTable.tsx
|
||||||
|
responseTime: "响应时间",
|
||||||
|
errorMessage: "错误信息",
|
||||||
|
details: "详情",
|
||||||
|
|
||||||
|
//LastCheckedTime.tsx
|
||||||
|
pausedAt: "暂停于 ",
|
||||||
|
lastCheckDetails: "最近检查详情",
|
||||||
|
checkedAt: "检查于 ",
|
||||||
|
monitoringPaused: "监控已暂停",
|
||||||
|
noAutomaticChecks: "无自动检查",
|
||||||
|
|
||||||
|
//ServiveEditDialog.tsx
|
||||||
|
editService: "编辑服务",
|
||||||
|
editServiceDesc: "更新您所监控服务的详细信息。",
|
||||||
|
|
||||||
|
//ServiceStatsCards.tsx
|
||||||
|
lastCheckedAt: "最新检查时间 {datetime}",
|
||||||
|
avg: "平均",
|
||||||
|
lastUpChecksCount: "最近 {count} 次检查",
|
||||||
|
basedOnlastChecksCount: "基于最近的 {count} 次检查",
|
||||||
|
totalUptime: "总在线时间",
|
||||||
|
totalDowntime: "总宕机时间",
|
||||||
|
monitoringSettings: "监控设置",
|
||||||
|
monitoringSettingsInterval: "每 {interval} 秒检查一次",
|
||||||
|
monitoringSettingsType: "监控",
|
||||||
|
upStatusDuration: "已在线 {duration}",
|
||||||
|
downStatusDuration: "已宕机 {duration}",
|
||||||
|
|
||||||
|
incidentHistory: "事件历史",
|
||||||
|
processing: "处理中",
|
||||||
|
back: "返回",
|
||||||
|
last20Checks: "最近 20 次检查",
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user