Add search functionality to the Dashboard targets list
Implemented a search input to filter targets based on name, host, or database fields. Updated the UI to show filtered results and display a message if no targets match the search. Adjusted styles for improved responsiveness and usability.
This commit is contained in:
@@ -6,6 +6,7 @@ import { useAuth } from "../state";
|
|||||||
export function DashboardPage() {
|
export function DashboardPage() {
|
||||||
const { tokens, refresh } = useAuth();
|
const { tokens, refresh } = useAuth();
|
||||||
const [targets, setTargets] = useState([]);
|
const [targets, setTargets] = useState([]);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
@@ -31,6 +32,15 @@ export function DashboardPage() {
|
|||||||
|
|
||||||
const alerts = targets.filter((t) => !t.host || !t.dbname).length;
|
const alerts = targets.filter((t) => !t.host || !t.dbname).length;
|
||||||
const okCount = Math.max(0, targets.length - alerts);
|
const okCount = Math.max(0, targets.length - alerts);
|
||||||
|
const filteredTargets = targets.filter((t) => {
|
||||||
|
const q = search.trim().toLowerCase();
|
||||||
|
if (!q) return true;
|
||||||
|
return (
|
||||||
|
(t.name || "").toLowerCase().includes(q) ||
|
||||||
|
(t.host || "").toLowerCase().includes(q) ||
|
||||||
|
(t.dbname || "").toLowerCase().includes(q)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-page">
|
<div className="dashboard-page">
|
||||||
@@ -56,12 +66,21 @@ export function DashboardPage() {
|
|||||||
|
|
||||||
<div className="card dashboard-targets-card">
|
<div className="card dashboard-targets-card">
|
||||||
<div className="dashboard-targets-head">
|
<div className="dashboard-targets-head">
|
||||||
<h3>Targets</h3>
|
<div>
|
||||||
<span>{targets.length} registered</span>
|
<h3>Targets</h3>
|
||||||
|
<span>{filteredTargets.length} shown of {targets.length} registered</span>
|
||||||
|
</div>
|
||||||
|
<div className="dashboard-target-search">
|
||||||
|
<input
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
placeholder="Search by name, host, or database..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="dashboard-target-list">
|
<div className="dashboard-target-list">
|
||||||
{targets.map((t) => {
|
{filteredTargets.map((t) => {
|
||||||
const hasAlert = !t.host || !t.dbname;
|
const hasAlert = !t.host || !t.dbname;
|
||||||
return (
|
return (
|
||||||
<article className="dashboard-target-card" key={t.id}>
|
<article className="dashboard-target-card" key={t.id}>
|
||||||
@@ -81,6 +100,9 @@ export function DashboardPage() {
|
|||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{filteredTargets.length === 0 && (
|
||||||
|
<div className="dashboard-empty">No targets match your search.</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -291,9 +291,10 @@ button {
|
|||||||
|
|
||||||
.dashboard-targets-head {
|
.dashboard-targets-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-end;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-targets-head h3 {
|
.dashboard-targets-head h3 {
|
||||||
@@ -305,6 +306,18 @@ button {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-target-search {
|
||||||
|
min-width: 320px;
|
||||||
|
max-width: 420px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-target-search input {
|
||||||
|
width: 100%;
|
||||||
|
height: 36px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.kpi-orb {
|
.kpi-orb {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 14px;
|
right: 14px;
|
||||||
@@ -334,7 +347,10 @@ button {
|
|||||||
|
|
||||||
.dashboard-target-list {
|
.dashboard-target-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
|
max-height: 460px;
|
||||||
|
overflow: auto;
|
||||||
|
padding-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-targets-card {
|
.dashboard-targets-card {
|
||||||
@@ -348,7 +364,7 @@ button {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid #2b5b8f;
|
border: 1px solid #2b5b8f;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 16px;
|
padding: 12px 14px;
|
||||||
background: linear-gradient(180deg, #143462, #102a4f);
|
background: linear-gradient(180deg, #143462, #102a4f);
|
||||||
box-shadow: inset 0 1px 0 #6fc5ff1a;
|
box-shadow: inset 0 1px 0 #6fc5ff1a;
|
||||||
transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
|
transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
|
||||||
@@ -362,15 +378,15 @@ button {
|
|||||||
|
|
||||||
.target-main h4 {
|
.target-main h4 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 24px;
|
font-size: 20px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.target-main p {
|
.target-main p {
|
||||||
margin: 6px 0 0 0;
|
margin: 4px 0 0 0;
|
||||||
color: #d5e7ff;
|
color: #d5e7ff;
|
||||||
font-size: 17px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.target-title-row {
|
.target-title-row {
|
||||||
@@ -402,7 +418,7 @@ button {
|
|||||||
|
|
||||||
.details-btn {
|
.details-btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 10px 14px;
|
padding: 7px 12px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
border: 1px solid #4db8f1;
|
border: 1px solid #4db8f1;
|
||||||
@@ -416,6 +432,14 @@ button {
|
|||||||
filter: brightness(1.05);
|
filter: brightness(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-empty {
|
||||||
|
border: 1px dashed #34689f;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px;
|
||||||
|
color: #9fb9d8;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.query-insights-page .query-toolbar {
|
.query-insights-page .query-toolbar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(220px, 320px) minmax(320px, 1fr);
|
grid-template-columns: minmax(220px, 320px) minmax(320px, 1fr);
|
||||||
@@ -1020,4 +1044,15 @@ select:-webkit-autofill {
|
|||||||
height: auto;
|
height: auto;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
.dashboard-target-search {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.dashboard-targets-head {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.dashboard-target-list {
|
||||||
|
max-height: none;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user