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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![CheckCle Platform](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/checkcle-black.png)
![CheckCle Platform](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/instance-server-monitoring.png)

# 🚀 What is CheckCle?

Expand All @@ -13,6 +13,7 @@ CheckCle is an Open Source solution for seamless, real-time monitoring of full-s
- Monitor HTTP, DNS, and Ping protocols
- Monitor TCP-based, API services (e.g., FTP, SMTP, HTTP)
- Track detail uptime, response times, and performance issues
- Distributed Regional Monitoring
- Incident History (UP/DOWN/WARNING/PAUSE)
- SSL & Domain Monitoring (Domain, Issuer, Expiration Date, Days Left, Status, Last Notified)
- Infrastructure Server Monitoring, Supports Linux (🐧 Debian, Ubuntu, CentOS, Red Hat, etc.) and Windows (Beta). And Servers metrics like CPU, RAM, disk usage, and network activity) with an one-line installation angent script.
Expand Down Expand Up @@ -76,9 +77,11 @@ services:
4. Follow the Quick Start Guide at https://docs.checkcle.io

###
![checkcle-collapse-black](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/checkcle-black.png)
![checkcle-collapse-black](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/uptime-monitoring.png)
![Service Detail Page](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/uptime-service-detail.png)
![Schedule Maintenance](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/maintenance-dahboard.png)
![checkcle-server-instance](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/instance-server-monitoring.png)
![Schedule Maintenance](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/checkcle-schedule-maintenance.png)
![SSL Monitoring](https://pub-4a4062303020445f8f289a2fee84f9e8.r2.dev/images/ssl-monitoring.png)

## 📝 Development Roadmap

Expand Down Expand Up @@ -134,6 +137,10 @@ Here are some ways you can help improve CheckCle:
CheckCle is released under the MIT License.

---
## 👥 Contributors

[![](https://contrib.rocks/image?repo=operacle/checkcle)](https://github.com/operacle/checkcle/graphs/contributors)


## Star History

Expand Down
24 changes: 12 additions & 12 deletions application/src/components/dashboard/StatusCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ export const StatusCards = ({ services }: StatusCardsProps) => {
} relative z-10`}
style={{
background: theme === 'dark'
? "linear-gradient(135deg, rgba(67, 160, 71, 0.8) 0%, rgba(102, 187, 106, 0.6) 100%)"
: "linear-gradient(135deg, #43a047 0%, #66bb6a 100%)"
? "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, rgba(102, 187, 106, 0.6) 100%)"
: "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, #66bb6a 100%)"
}}
>
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 z-0 opacity-10">
<div className="w-full h-full"
style={{
backgroundImage: `linear-gradient(#fff 1px, transparent 1px),
backgroundImage: `linear-gradient(#000 1px, transparent 1px),
linear-gradient(90deg, #fff 1px, transparent 1px)`,
backgroundSize: '20px 20px'
}}
Expand All @@ -59,15 +59,15 @@ export const StatusCards = ({ services }: StatusCardsProps) => {
} relative z-10`}
style={{
background: theme === 'dark'
? "linear-gradient(135deg, rgba(229, 57, 53, 0.8) 0%, rgba(239, 83, 80, 0.6) 100%)"
: "linear-gradient(135deg, #e53935 0%, #ef5350 100%)"
? "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, rgba(239, 83, 80, 0.6) 100%)"
: "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, #ef5350 100%)"
}}
>
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 z-0 opacity-10">
<div className="w-full h-full"
style={{
backgroundImage: `linear-gradient(#fff 1px, transparent 1px),
backgroundImage: `linear-gradient(#000 1px, transparent 1px),
linear-gradient(90deg, #fff 1px, transparent 1px)`,
backgroundSize: '20px 20px'
}}
Expand All @@ -91,15 +91,15 @@ export const StatusCards = ({ services }: StatusCardsProps) => {
} relative z-10`}
style={{
background: theme === 'dark'
? "linear-gradient(135deg, rgba(25, 118, 210, 0.8) 0%, rgba(66, 165, 245, 0.6) 100%)"
: "linear-gradient(135deg, #1976d2 0%, #42a5f5 100%)"
? "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, rgba(66, 165, 245, 0.6) 100%)"
: "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, #42a5f5 100%)"
}}
>
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 z-0 opacity-10">
<div className="w-full h-full"
style={{
backgroundImage: `linear-gradient(#fff 1px, transparent 1px),
backgroundImage: `linear-gradient(#000 1px, transparent 1px),
linear-gradient(90deg, #fff 1px, transparent 1px)`,
backgroundSize: '20px 20px'
}}
Expand All @@ -123,15 +123,15 @@ export const StatusCards = ({ services }: StatusCardsProps) => {
} relative z-10`}
style={{
background: theme === 'dark'
? "linear-gradient(135deg, rgba(255, 152, 0, 0.8) 0%, rgba(255, 183, 77, 0.6) 100%)"
: "linear-gradient(135deg, #ff9800 0%, #ffb74d 100%)"
? "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, rgba(255, 183, 77, 0.6) 100%)"
: "linear-gradient(135deg, rgba(65, 59, 55, 0.8) 0%, #ffb74d 100%)"
}}
>
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 z-0 opacity-10">
<div className="w-full h-full"
style={{
backgroundImage: `linear-gradient(#fff 1px, transparent 1px),
backgroundImage: `linear-gradient(#000 1px, transparent 1px),
linear-gradient(90deg, #fff 1px, transparent 1px)`,
backgroundSize: '20px 20px'
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,38 @@

import React, { useEffect } from 'react';
import { useLanguage } from '@/contexts/LanguageContext';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { MaintenanceTable, MaintenanceStatusChecker } from './maintenance';
import { EmptyMaintenanceState } from './maintenance/EmptyMaintenanceState';
import { OverviewCard } from './common/OverviewCard';
import { LoadingState } from '@/components/services/LoadingState';
import { Calendar, Clock, CheckCircle } from 'lucide-react';
import { useMaintenanceData } from './hooks/useMaintenanceData';
import { Skeleton } from '@/components/ui/skeleton';
import { useToast } from '@/hooks/use-toast';
import { LoadingState } from '@/components/services/LoadingState';
import { useTheme } from '@/contexts/ThemeContext';

interface ScheduledMaintenanceTabProps {
refreshTrigger?: number;
}

export const ScheduledMaintenanceTab = ({ refreshTrigger = 0 }: ScheduledMaintenanceTabProps) => {
const { t } = useLanguage();
const { theme } = useTheme();
const { toast } = useToast();
const [manualRefresh, setManualRefresh] = React.useState(0);

// Combine the external refresh trigger with our internal one

const combinedRefreshTrigger = refreshTrigger + manualRefresh;
const {
loading,
filter,
setFilter,
maintenanceData,
overviewStats,

const {
loading,
filter,
setFilter,
maintenanceData,
overviewStats,
fetchMaintenanceData,
isEmpty,
error,
initialized
initialized,
} = useMaintenanceData({ refreshTrigger: combinedRefreshTrigger });

// Display toast when error occurs
useEffect(() => {
if (error) {
toast({
Expand All @@ -47,66 +43,91 @@ export const ScheduledMaintenanceTab = ({ refreshTrigger = 0 }: ScheduledMainten
}
}, [error, toast, t]);

// Force fetch data on initial load
useEffect(() => {
console.log("ScheduledMaintenanceTab is mounted, fetching data");
fetchMaintenanceData(true);
}, [fetchMaintenanceData]);

// Handle maintenance updates
const handleMaintenanceUpdated = () => {
console.log("Maintenance updated, refreshing data");
setManualRefresh(prev => prev + 1);
setManualRefresh((prev) => prev + 1);
};

// Handle tab changes
const handleTabChange = (value: string) => {
setFilter(value);
};

// Show full-page loading state during initial load
if (loading && !initialized) {
return <LoadingState />;
}

const gradientCard = (
title: string,
value: string | number,
icon: JSX.Element,
gradient: string
) => (
<Card
className="border-none rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-xl transform hover:-translate-y-1 relative z-10"
style={{
background: theme === 'dark'
? `linear-gradient(135deg, rgba(65,59,55,0.8) 0%, ${gradient})`
: `linear-gradient(135deg, rgba(65,59,55,0.8) 0%, ${gradient})`,
}}
>
<div className="absolute inset-0 z-0 opacity-10">
<div
className="w-full h-full"
style={{
backgroundImage: `linear-gradient(#000 1px, transparent 1px),
linear-gradient(90deg, #fff 1px, transparent 1px)`,
backgroundSize: '20px 20px',
}}
/>
</div>
<CardHeader className="pb-2 relative z-10">
<CardTitle className="text-sm font-medium text-white">{title}</CardTitle>
</CardHeader>
<CardContent className="flex items-center justify-between relative z-10">
<span className="text-4xl font-bold text-white">{value}</span>
<div className="rounded-full p-3 bg-white/25 backdrop-blur-sm">
{icon}
</div>
</CardContent>
</Card>
);

return (
<>
{/* Status checker for automatic updates */}
<MaintenanceStatusChecker
<MaintenanceStatusChecker
maintenanceData={maintenanceData}
onStatusUpdated={handleMaintenanceUpdated}
/>

{/* Overview Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<OverviewCard
title={t('upcomingMaintenance')}
value={loading ? '...' : overviewStats.upcoming}
icon={<Calendar className="h-5 w-5 text-white" />}
isLoading={loading}
color="blue"
/>
<OverviewCard
title={t('ongoingMaintenance')}
value={loading ? '...' : overviewStats.ongoing}
icon={<Clock className="h-5 w-5 text-white" />}
isLoading={loading}
color="amber"
/>
<OverviewCard
title={t('completedMaintenance')}
value={loading ? '...' : overviewStats.completed}
icon={<CheckCircle className="h-5 w-5 text-white" />}
isLoading={loading}
color="green"
/>
<OverviewCard
title={t('totalScheduledHours')}
value={loading ? '...' : `${overviewStats.totalDuration}h`}
icon={<Clock className="h-5 w-5 text-white" />}
isLoading={loading}
color="purple"
/>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 w-full">
{gradientCard(
t('upcomingMaintenance'),
loading ? '...' : overviewStats.upcoming,
<Calendar className="h-6 w-6 text-white" />,
'rgba(59,130,246,0.6)'
)}
{gradientCard(
t('ongoingMaintenance'),
loading ? '...' : overviewStats.ongoing,
<Clock className="h-6 w-6 text-white" />,
'rgba(251,191,36,0.6)'
)}
{gradientCard(
t('completedMaintenance'),
loading ? '...' : overviewStats.completed,
<CheckCircle className="h-6 w-6 text-white" />,
'rgba(34,197,94,0.6)'
)}
{gradientCard(
t('totalScheduledHours'),
loading ? '...' : `${overviewStats.totalDuration}h`,
<Clock className="h-6 w-6 text-white" />,
'rgba(139,92,246,0.6)'
)}
</div>

<Card>
Expand All @@ -117,10 +138,10 @@ export const ScheduledMaintenanceTab = ({ refreshTrigger = 0 }: ScheduledMainten
</CardDescription>
</CardHeader>
<CardContent>
<Tabs
defaultValue="upcoming"
value={filter}
className="w-full"
<Tabs
defaultValue="upcoming"
value={filter}
className="w-full"
onValueChange={handleTabChange}
>
<TabsList className="mb-6">
Expand All @@ -129,24 +150,24 @@ export const ScheduledMaintenanceTab = ({ refreshTrigger = 0 }: ScheduledMainten
<TabsTrigger value="completed">{t('completedMaintenance')}</TabsTrigger>
</TabsList>

<TabsContent value="upcoming" className="space-y-4">
<MaintenanceTable
<TabsContent value="upcoming">
<MaintenanceTable
data={maintenanceData}
isLoading={loading && initialized}
onMaintenanceUpdated={handleMaintenanceUpdated}
/>
</TabsContent>
<TabsContent value="ongoing" className="space-y-4">
<MaintenanceTable

<TabsContent value="ongoing">
<MaintenanceTable
data={maintenanceData}
isLoading={loading && initialized}
onMaintenanceUpdated={handleMaintenanceUpdated}
/>
</TabsContent>
<TabsContent value="completed" className="space-y-4">
<MaintenanceTable

<TabsContent value="completed">
<MaintenanceTable
data={maintenanceData}
isLoading={loading && initialized}
onMaintenanceUpdated={handleMaintenanceUpdated}
Expand Down
2 changes: 1 addition & 1 deletion application/src/components/servers/OSTypeIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const OSTypeIcon = ({ osType, className = "h-4 w-4" }: OSTypeIconProps) =
const getOSIcon = (os: string) => {
const osLower = os.toLowerCase();

if (osLower.includes('linux') || osLower.includes('ubuntu') || osLower.includes('centos')) {
if (osLower.includes('linux') || osLower.includes('ubuntu') || osLower.includes('centos') || osLower.includes('debian')) {
return <Server className={className} />;
} else if (osLower.includes('windows')) {
return <Monitor className={className} />;
Expand Down
Loading