diff --git a/src/app/domains/page.tsx b/src/app/domains/page.tsx index 2a72d2c..2feeb2a 100644 --- a/src/app/domains/page.tsx +++ b/src/app/domains/page.tsx @@ -2,6 +2,7 @@ import ConfirmActionDialog from "@/components/ConfirmActionDialog"; import DashboardLayout from "@/components/DashboardLayout"; +import SearchField from "@/components/SearchField"; import ToastViewport from "@/components/ToastViewport"; import { useCallback, useEffect, useMemo, useState } from "react"; import { Globe, Loader2, Lock, Plus, RefreshCw } from "lucide-react"; @@ -51,6 +52,7 @@ export default function DomainsPage() { const [data, setData] = useState([]); const [serverList, setServerList] = useState([]); const [selectedServerId, setSelectedServerId] = useState(""); + const [search, setSearch] = useState(""); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); const [syncMessage, setSyncMessage] = useState(""); @@ -294,10 +296,28 @@ export default function DomainsPage() { .length, }; const hasPendingSslAction = syncing || renewing !== null || deleting !== null; - const domainsWithSsl = visibleData.filter((d) => d.sslEnabled && d.sslCert); + const filteredData = useMemo(() => { + const query = search.trim().toLowerCase(); + if (!query) return visibleData; + + return visibleData.filter((domain) => + [ + domain.name, + domain.type, + domain.value, + domain.proxy, + domain.server?.name ?? "", + domain.sslCert?.status ?? "", + domain.sslCert?.issuer ?? "", + ].some((value) => value.toLowerCase().includes(query)), + ); + }, [search, visibleData]); + const domainsWithSsl = filteredData.filter( + (domain) => domain.sslEnabled && domain.sslCert, + ); const pagination = useTablePagination({ - items: visibleData, - resetKey: `${activeTab}|${selectedServerId}`, + items: filteredData, + resetKey: `${activeTab}|${selectedServerId}|${search}`, }); return ( @@ -427,6 +447,18 @@ export default function DomainsPage() {

*/} +
+ setSearch(event.target.value)} + containerStyle={{ flex: "1 1 360px", minWidth: 240 }} + /> +
+ {/* Tab switcher */}
@@ -575,9 +607,34 @@ export default function DomainsPage() {
)} - {!loading && visibleData.length > 0 && activeTab === "domains" && ( + {!loading && visibleData.length > 0 && filteredData.length === 0 && ( +
+ +

+ No domain records match your search. +

+
+ )} + + {!loading && filteredData.length > 0 && activeTab === "domains" && ( )} - {!loading && activeTab === "ssl" && ( + {!loading && filteredData.length > 0 && activeTab === "ssl" && ( + +

+ {activeTab === "certs" + ? "No SSL certificates match your search." + : "No domains waiting for SSL match your search."} +

+
+ ); + } + return activeTab === "domains" ? (
([]); const [serverList, setServerList] = useState([]); const [selectedServerId, setSelectedServerId] = useState(""); + const [search, setSearch] = useState(""); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); const [showAdd, setShowAdd] = useState(false); @@ -176,6 +178,34 @@ export default function SSLPage() { [domains, selectedServerId], ); + const filteredCerts = useMemo(() => { + const query = search.trim().toLowerCase(); + if (!query) return visibleCerts; + + return visibleCerts.filter((cert) => + [ + cert.domain?.name ?? "", + cert.domain?.server?.name ?? "", + cert.issuer, + cert.status, + ].some((value) => value.toLowerCase().includes(query)), + ); + }, [search, visibleCerts]); + + const filteredIssueableDomains = useMemo(() => { + const query = search.trim().toLowerCase(); + if (!query) return issueableDomains; + + return issueableDomains.filter((domain) => + [ + domain.name, + domain.value, + domain.proxy, + domain.server?.name ?? "", + ].some((value) => value.toLowerCase().includes(query)), + ); + }, [issueableDomains, search]); + const handleQuickIssue = async (domain: Domain) => { setIssuingDomainId(domain.id); try { @@ -409,6 +439,22 @@ export default function SSLPage() { onChange={handleServerChange} /> +
+ setSearch(event.target.value)} + containerStyle={{ flex: "1 1 360px", minWidth: 240 }} + /> +
+ 0 && search.trim() + ? "no-results" + : "empty" + } activeTab={activeTab} selectedServerId={selectedServerId} /> ) : null} - {!loading && activeTab === "domains" && issueableDomains.length > 0 ? ( + {!loading && activeTab === "domains" && filteredIssueableDomains.length > 0 ? ( ) : null} - {!loading && activeTab === "certs" && visibleCerts.length === 0 ? ( + {!loading && activeTab === "certs" && filteredCerts.length === 0 ? ( 0 && search.trim() + ? "no-results" + : "empty" + } activeTab={activeTab} selectedServerId={selectedServerId} /> ) : null} - {!loading && activeTab === "certs" && visibleCerts.length > 0 ? ( + {!loading && activeTab === "certs" && filteredCerts.length > 0 ? (