diff --git a/application/src/components/ssl-domain/SSLCertificatesTable.tsx b/application/src/components/ssl-domain/SSLCertificatesTable.tsx
index dd28818..a1e5008 100644
--- a/application/src/components/ssl-domain/SSLCertificatesTable.tsx
+++ b/application/src/components/ssl-domain/SSLCertificatesTable.tsx
@@ -33,11 +33,13 @@ import { AddSSLCertificateForm } from "./AddSSLCertificateForm";
import { EditSSLCertificateForm } from "./EditSSLCertificateForm";
import { SSLCertificateActions } from "./SSLCertificateActions";
import { SSLCertificateDetailDialog } from "./SSLCertificateDetailDialog";
+import { SSLPagination } from "./SSLPagination";
import { fetchSSLCertificates, addSSLCertificate, deleteSSLCertificate } from "@/services/sslCertificateService";
import { pb } from "@/lib/pocketbase";
import { SSLCertificate } from "@/types/ssl.types";
import { useLanguage } from "@/contexts/LanguageContext";
import { useTheme } from "@/contexts/ThemeContext";
+import { useSSLPagination } from "@/hooks/useSSLPagination";
import { toast } from "sonner";
export const SSLCertificatesTable = () => {
@@ -56,6 +58,16 @@ export const SSLCertificatesTable = () => {
queryFn: fetchSSLCertificates,
});
+ const {
+ paginatedCertificates,
+ currentPage,
+ totalPages,
+ pageSize,
+ totalItems,
+ handlePageChange,
+ handlePageSizeChange,
+ } = useSSLPagination({ certificates });
+
if (isLoading) return
Loading...
;
if (isError) return Error loading certificates
;
@@ -67,7 +79,6 @@ export const SSLCertificatesTable = () => {
setShowAddDialog(false);
toast.success(t('certificateAdded'));
} catch (error) {
- // console.error("Error adding certificate:", error);
toast.error(t('failedToAddCertificate'));
} finally {
setIsSubmitting(false);
@@ -89,7 +100,6 @@ export const SSLCertificatesTable = () => {
setSelectedCertificate(null);
toast.success(t('certificateUpdated'));
} catch (error) {
- // console.error("Error updating certificate:", error);
toast.error(t('failedToUpdateCertificate'));
} finally {
setIsSubmitting(false);
@@ -107,7 +117,6 @@ export const SSLCertificatesTable = () => {
setSelectedCertificate(null);
toast.success(t('certificateDeleted'));
} catch (error) {
- // console.error("Error deleting certificate:", error);
toast.error(t('failedToDeleteCertificate'));
} finally {
setIsSubmitting(false);
@@ -136,57 +145,68 @@ export const SSLCertificatesTable = () => {
{t('noCertificatesFound')}
) : (
-
-
-
-
- {t('domain')}
- {t('status')}
- {t('issuer')}
- {t('validUntil')}
- {t('daysLeft')}
- Check Interval
- Actions
-
-
-
- {certificates.map((certificate) => (
- openViewDialog(certificate)}
- >
-
- {certificate.domain}
-
-
-
-
- {certificate.issuer_o || certificate.issuer_cn || 'Unknown'}
-
- {certificate.valid_till ? new Date(certificate.valid_till).toLocaleDateString() : 'N/A'}
-
-
-
- {certificate.days_left} {t('days')}
-
-
-
- {certificate.check_interval || 1} {t('days')}
-
- e.stopPropagation()}>
-
-
+ <>
+
+
+
+
+ {t('domain')}
+ {t('status')}
+ {t('issuer')}
+ {t('validUntil')}
+ {t('daysLeft')}
+ Check Interval
+ Actions
- ))}
-
-
-
+
+
+ {paginatedCertificates.map((certificate) => (
+ openViewDialog(certificate)}
+ >
+
+ {certificate.domain}
+
+
+
+
+ {certificate.issuer_o || certificate.issuer_cn || 'Unknown'}
+
+ {certificate.valid_till ? new Date(certificate.valid_till).toLocaleDateString() : 'N/A'}
+
+
+
+ {certificate.days_left} {t('days')}
+
+
+
+ {certificate.check_interval || 1} {t('days')}
+
+ e.stopPropagation()}>
+
+
+
+ ))}
+
+
+
+
+
+ >
)}
{/* View Certificate Dialog */}
@@ -256,4 +276,4 @@ export const SSLCertificatesTable = () => {
);
-};
+};
\ No newline at end of file
diff --git a/application/src/components/ssl-domain/SSLDomainContent.tsx b/application/src/components/ssl-domain/SSLDomainContent.tsx
index bb38980..6fe1b60 100644
--- a/application/src/components/ssl-domain/SSLDomainContent.tsx
+++ b/application/src/components/ssl-domain/SSLDomainContent.tsx
@@ -30,12 +30,12 @@ export const SSLDomainContent = () => {
queryKey: ['ssl-certificates'],
queryFn: async () => {
try {
- console.log("Fetching SSL certificates from SSLDomainContent...");
+ // console.log("Fetching SSL certificates from SSLDomainContent...");
const result = await fetchSSLCertificates();
- console.log("Received SSL certificates:", result);
+ // console.log("Received SSL certificates:", result);
return result;
} catch (error) {
- console.error("Error fetching certificates:", error);
+ // console.error("Error fetching certificates:", error);
toast.error(t('failedToLoadCertificates'));
throw error;
}
@@ -61,7 +61,7 @@ export const SSLDomainContent = () => {
// Edit certificate mutation - Updated to ensure thresholds are properly updated
const editMutation = useMutation({
mutationFn: async (certificate: SSLCertificate) => {
- console.log("Updating certificate with data:", certificate);
+ // console.log("Updating certificate with data:", certificate);
// Create the update data object
const updateData = {
@@ -70,12 +70,12 @@ export const SSLDomainContent = () => {
notification_channel: certificate.notification_channel,
};
- console.log("Update data to be sent:", updateData);
+ // console.log("Update data to be sent:", updateData);
// Update certificate in the database using PocketBase directly
const updated = await pb.collection('ssl_certificates').update(certificate.id, updateData);
- console.log("PocketBase update response:", updated);
+ // console.log("PocketBase update response:", updated);
// After updating the settings, refresh the certificate to ensure it's up to date
// This will also check if notification needs to be sent based on updated thresholds
@@ -90,7 +90,7 @@ export const SSLDomainContent = () => {
toast.success(t('sslCertificateUpdated'));
},
onError: (error) => {
- console.error("Error updating SSL certificate:", error);
+ // console.error("Error updating SSL certificate:", error);
toast.error(error instanceof Error ? error.message : t('failedToUpdateCertificate'));
}
});
@@ -103,7 +103,7 @@ export const SSLDomainContent = () => {
toast.success(t('sslCertificateDeleted'));
},
onError: (error) => {
- console.error("Error deleting SSL certificate:", error);
+ // console.error("Error deleting SSL certificate:", error);
toast.error(error instanceof Error ? error.message : t('failedToDeleteCertificate'));
}
});
@@ -117,7 +117,7 @@ export const SSLDomainContent = () => {
// Removed individual success toast notification
},
onError: (error) => {
- console.error("Error refreshing SSL certificate:", error);
+ // console.error("Error refreshing SSL certificate:", error);
setRefreshingId(null);
// Still refresh the data to show any partial information that was saved
@@ -142,7 +142,7 @@ export const SSLDomainContent = () => {
}
},
onError: (error) => {
- console.error("Error refreshing all certificates:", error);
+ // console.error("Error refreshing all certificates:", error);
toast.error(t('failedToCheckCertificate'));
setIsRefreshingAll(false);
diff --git a/application/src/components/ssl-domain/SSLPagination.tsx b/application/src/components/ssl-domain/SSLPagination.tsx
new file mode 100644
index 0000000..2ee6721
--- /dev/null
+++ b/application/src/components/ssl-domain/SSLPagination.tsx
@@ -0,0 +1,131 @@
+
+import {
+ Pagination,
+ PaginationContent,
+ PaginationItem,
+ PaginationLink,
+ PaginationNext,
+ PaginationPrevious
+} from "@/components/ui/pagination";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from "@/components/ui/select";
+import { SSLPageSize } from "@/hooks/useSSLPagination";
+import { useLanguage } from "@/contexts/LanguageContext";
+
+interface SSLPaginationProps {
+ currentPage: number;
+ totalPages: number;
+ pageSize: SSLPageSize;
+ totalItems: number;
+ onPageChange: (page: number) => void;
+ onPageSizeChange: (size: SSLPageSize) => void;
+}
+
+export function SSLPagination({
+ currentPage,
+ totalPages,
+ pageSize,
+ totalItems,
+ onPageChange,
+ onPageSizeChange,
+}: SSLPaginationProps) {
+ const { t } = useLanguage();
+
+ // Generate page numbers to display
+ const getPageNumbers = () => {
+ const pages = [];
+ const maxVisiblePages = 5;
+ const halfVisible = Math.floor(maxVisiblePages / 2);
+
+ let startPage = Math.max(1, currentPage - halfVisible);
+ let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
+
+ // Adjust start page if we don't have enough pages at the end
+ if (endPage - startPage + 1 < maxVisiblePages) {
+ startPage = Math.max(1, endPage - maxVisiblePages + 1);
+ }
+
+ for (let i = startPage; i <= endPage; i++) {
+ pages.push(i);
+ }
+
+ return pages;
+ };
+
+ const startItem = Math.min((currentPage - 1) * pageSize + 1, totalItems);
+ const endItem = Math.min(currentPage * pageSize, totalItems);
+
+ return (
+
+
+
+ {t("rowsPerPage") || "Rows per page"}:
+
+
+
+ {totalItems > 0
+ ? `${startItem}-${endItem} of ${totalItems} ${t("certificates") || "certificates"}`
+ : `0 ${t("certificates") || "certificates"}`
+ }
+
+
+
+ {totalPages > 1 && (
+
+
+
+ onPageChange(Math.max(1, currentPage - 1))}
+ className={
+ currentPage === 1
+ ? "pointer-events-none opacity-50"
+ : "cursor-pointer"
+ }
+ />
+
+
+ {getPageNumbers().map((page) => (
+
+ onPageChange(page)}
+ className="cursor-pointer"
+ >
+ {page}
+
+
+ ))}
+
+
+ onPageChange(Math.min(totalPages, currentPage + 1))}
+ className={
+ currentPage === totalPages
+ ? "pointer-events-none opacity-50"
+ : "cursor-pointer"
+ }
+ />
+
+
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/application/src/hooks/useSSLPagination.ts b/application/src/hooks/useSSLPagination.ts
new file mode 100644
index 0000000..25bc9e0
--- /dev/null
+++ b/application/src/hooks/useSSLPagination.ts
@@ -0,0 +1,52 @@
+
+import { useState, useMemo } from 'react';
+import { SSLCertificate } from '@/types/ssl.types';
+
+export type SSLPageSize = 10 | 30 | 50;
+
+interface UseSSLPaginationProps {
+ certificates: SSLCertificate[];
+ initialPageSize?: SSLPageSize;
+}
+
+export const useSSLPagination = ({
+ certificates,
+ initialPageSize = 10
+}: UseSSLPaginationProps) => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize, setPageSize] = useState(initialPageSize);
+
+ const { paginatedCertificates, totalPages } = useMemo(() => {
+ const totalItems = certificates.length;
+ const pages = Math.ceil(totalItems / pageSize);
+ const startIndex = (currentPage - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+
+ return {
+ paginatedCertificates: certificates.slice(startIndex, endIndex),
+ totalPages: Math.max(1, pages)
+ };
+ }, [certificates, currentPage, pageSize]);
+
+ // Reset to first page when page size changes or certificates change
+ const handlePageSizeChange = (newPageSize: SSLPageSize) => {
+ setPageSize(newPageSize);
+ setCurrentPage(1);
+ };
+
+ // Reset to first page if current page exceeds total pages
+ const handlePageChange = (page: number) => {
+ const validPage = Math.min(Math.max(1, page), totalPages);
+ setCurrentPage(validPage);
+ };
+
+ return {
+ paginatedCertificates,
+ currentPage,
+ totalPages,
+ pageSize,
+ totalItems: certificates.length,
+ handlePageChange,
+ handlePageSizeChange,
+ };
+};
\ No newline at end of file