Skip to content
112 changes: 51 additions & 61 deletions application/src/components/services/ResponseTimeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,31 @@ export function ResponseTimeChart({ uptimeData }: ResponseTimeChartProps) {
date: format(timestamp, 'MMM dd, yyyy'),
value: data.status === "paused" ? null : data.responseTime,
status: data.status,
// Separate values for different statuses with proper positioning
upValue: data.status === "up" ? data.responseTime : null,
downValue: data.status === "down" ? data.responseTime : null,
warningValue: data.status === "warning" ? data.responseTime : null,
};
});
}, [uptimeData]);

// Calculate Y-axis domain for better positioning
const yAxisDomain = useMemo(() => {
if (!chartData.length) return ['dataMin - 10', 'dataMax + 10'];

const allValues = chartData
.filter(d => d.value !== null && d.status !== 'paused')
.map(d => d.value);

if (allValues.length === 0) return [0, 100];

const minValue = Math.min(...allValues);
const maxValue = Math.max(...allValues);
const padding = (maxValue - minValue) * 0.1 || 10;

return [Math.max(0, minValue - padding), maxValue + padding];
}, [chartData]);

// Create a custom tooltip for the chart
const CustomTooltip = ({ active, payload, label }: any) => {
if (active && payload && payload.length) {
Expand Down Expand Up @@ -79,31 +100,6 @@ export function ResponseTimeChart({ uptimeData }: ResponseTimeChartProps) {
return null;
};

// Compute status segments for different areas
const getStatusSegments = () => {
const segments = {
up: [] as any[],
down: [] as any[],
warning: [] as any[]
};

chartData.forEach(point => {
if (point.status === "paused") return;

if (point.status === "up") {
segments.up.push(point);
} else if (point.status === "down") {
segments.down.push(point);
} else if (point.status === "warning") {
segments.warning.push(point);
}
});

return segments;
};

const segments = getStatusSegments();

// Check if we have any data to display - be more lenient by checking raw uptimeData
const hasData = uptimeData.length > 0;

Expand Down Expand Up @@ -172,46 +168,40 @@ export function ResponseTimeChart({ uptimeData }: ResponseTimeChartProps) {
<YAxis
stroke={theme === 'dark' ? '#666' : '#9ca3af'}
allowDecimals={false}
domain={['dataMin - 10', 'dataMax + 10']}
domain={yAxisDomain}
/>
<Tooltip content={<CustomTooltip />} />

{/* Area charts for different statuses */}
{segments.up.length > 0 && (
<Area
type="monotone"
dataKey="value"
data={segments.up}
stroke="#10b981"
fillOpacity={1}
fill="url(#colorUp)"
connectNulls
/>
)}
{/* Separate area charts for each status - positioned closer together */}
<Area
type="monotone"
dataKey="upValue"
stroke="#10b981"
strokeWidth={2}
fillOpacity={0.4}
fill="url(#colorUp)"
connectNulls={false}
/>

{segments.down.length > 0 && (
<Area
type="monotone"
dataKey="value"
data={segments.down}
stroke="#ef4444"
fillOpacity={1}
fill="url(#colorDown)"
connectNulls
/>
)}
<Area
type="monotone"
dataKey="downValue"
stroke="#ef4444"
strokeWidth={2}
fillOpacity={0.4}
fill="url(#colorDown)"
connectNulls={false}
/>

{segments.warning.length > 0 && (
<Area
type="monotone"
dataKey="value"
data={segments.warning}
stroke="#f59e0b"
fillOpacity={1}
fill="url(#colorWarning)"
connectNulls
/>
)}
<Area
type="monotone"
dataKey="warningValue"
stroke="#f59e0b"
strokeWidth={2}
fillOpacity={0.4}
fill="url(#colorWarning)"
connectNulls={false}
/>

{/* Add reference lines for paused periods */}
{chartData.map((entry, index) =>
Expand All @@ -231,4 +221,4 @@ export function ResponseTimeChart({ uptimeData }: ResponseTimeChartProps) {
</CardContent>
</Card>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export const useRealTimeUpdates = ({
setUptimeData(prev => {
const newData: UptimeData = {
id: e.record.id,
serviceId: e.record.service_id,
service_id: e.record.service_id, // Include service_id
serviceId: e.record.service_id, // Keep for backward compatibility
timestamp: e.record.timestamp,
status: e.record.status,
responseTime: e.record.response_time || 0,
Expand Down Expand Up @@ -84,4 +85,4 @@ export const useRealTimeUpdates = ({
console.error("Error setting up real-time updates:", error);
}
}, [serviceId, startDate, endDate, setService, setUptimeData]);
};
};
6 changes: 4 additions & 2 deletions application/src/components/services/hooks/useUptimeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export const useUptimeData = ({ serviceId, serviceType, status, interval }: UseU

const placeholderHistory: UptimeData[] = Array(20).fill(null).map((_, index) => ({
id: `placeholder-${serviceId}-${index}`,
serviceId: serviceId || "",
service_id: serviceId || "", // Include service_id
serviceId: serviceId || "", // Keep for backward compatibility
timestamp: new Date(Date.now() - (index * interval * 1000)).toISOString(),
status: statusValue as "up" | "down" | "warning" | "paused",
responseTime: 0
Expand Down Expand Up @@ -96,7 +97,8 @@ export const useUptimeData = ({ serviceId, serviceType, status, interval }: UseU

return {
id: `padding-${serviceId}-${index}`,
serviceId: serviceId || "",
service_id: serviceId || "", // Include service_id
serviceId: serviceId || "", // Keep for backward compatibility
timestamp: new Date(baseTime - timeOffset).toISOString(),
status: lastStatus,
responseTime: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +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("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'}>Error Message</TableHead>
<TableHead className={theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}>Details</TableHead>
</TableRow>
</TableHeader>
<TableBody>
Expand Down Expand Up @@ -52,10 +54,28 @@ export function IncidentTable({ incidents }: IncidentTableProps) {
? `${check.responseTime}ms`
: "N/A"}
</TableCell>
<TableCell className={`text-sm ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'} max-w-[200px]`}>
{check.error_message ? (
<div className="truncate" title={check.error_message}>
{check.error_message}
</div>
) : (
<span className={theme === 'dark' ? 'text-gray-500' : 'text-gray-400'}>-</span>
)}
</TableCell>
<TableCell className={`text-sm ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'} max-w-[250px]`}>
{check.details ? (
<div className="truncate" title={check.details}>
{check.details}
</div>
) : (
<span className={theme === 'dark' ? 'text-gray-500' : 'text-gray-400'}>-</span>
)}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
}
}
71 changes: 47 additions & 24 deletions application/src/components/ssl-domain/AddSSLCertificateForm.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
Expand All @@ -18,7 +19,8 @@ const formSchema = z.object({
domain: z.string().min(1, "Domain is required"),
warning_threshold: z.coerce.number().int().min(1).max(365),
expiry_threshold: z.coerce.number().int().min(1).max(30),
notification_channel: z.string().min(1, "Notification channel is required")
notification_channel: z.string().min(1, "Notification channel is required"),
check_interval: z.coerce.number().int().min(1).max(30).optional()
});

interface AddSSLCertificateFormProps {
Expand All @@ -42,7 +44,8 @@ export const AddSSLCertificateForm = ({
domain: "",
warning_threshold: 30,
expiry_threshold: 7,
notification_channel: ""
notification_channel: "",
check_interval: 1
}
});

Expand Down Expand Up @@ -89,7 +92,8 @@ export const AddSSLCertificateForm = ({
domain: values.domain,
warning_threshold: values.warning_threshold,
expiry_threshold: values.expiry_threshold,
notification_channel: values.notification_channel
notification_channel: values.notification_channel,
check_interval: values.check_interval
};

await onSubmit(certData);
Expand Down Expand Up @@ -117,34 +121,53 @@ export const AddSSLCertificateForm = ({
)}
/>

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="warning_threshold"
render={({ field }) => (
<FormItem>
<FormLabel>{t('warningThreshold')}</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormDescription>
{t('getNotifiedExpiration')}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="expiry_threshold"
render={({ field }) => (
<FormItem>
<FormLabel>{t('expiryThreshold')}</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormDescription>
{t('getNotifiedCritical')}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>

<FormField
control={form.control}
name="warning_threshold"
render={({ field }) => (
<FormItem>
<FormLabel>{t('warningThreshold')}</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormDescription>
{t('getNotifiedExpiration')}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="expiry_threshold"
name="check_interval"
render={({ field }) => (
<FormItem>
<FormLabel>{t('expiryThreshold')}</FormLabel>
<FormLabel>Check Interval (Days)</FormLabel>
<FormControl>
<Input type="number" {...field} />
<Input type="number" min="1" max="30" {...field} />
</FormControl>
<FormDescription>
{t('getNotifiedCritical')}
How often to check the SSL certificate (in days)
</FormDescription>
<FormMessage />
</FormItem>
Expand Down
Loading