fix tags filter

This commit is contained in:
mbecker20
2024-04-18 03:17:57 -07:00
parent 9b0e96a59a
commit 17398fc932
16 changed files with 136 additions and 120 deletions
+35 -8
View File
@@ -6,6 +6,11 @@ import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "@ui/dialog";
import { useState } from "react";
import { AlertLevel } from ".";
import { fmt_date_with_minutes } from "@lib/formatting";
import { DialogDescription } from "@radix-ui/react-dialog";
import {
alert_level_intention,
text_color_class_by_intention,
} from "@lib/color";
export const AlertDetailsDialog = ({ id }: { id: string }) => {
const [open, set] = useState(false);
@@ -23,20 +28,42 @@ export const AlertDetailsDialog = ({ id }: { id: string }) => {
<DialogHeader className="flex-row justify-between w-full">
{alert && (
<>
<div className="flex gap-4 items-center">
<ResourceLink
type={alert.target.type as UsableResource}
id={alert.target.id}
/>
<AlertLevel level={alert.level} />
</div>
<ResourceLink
type={alert.target.type as UsableResource}
id={alert.target.id}
/>
<div className="text-muted-foreground">
{fmt_date_with_minutes(new Date(alert.ts))}
</div>
</>
)}
</DialogHeader>
<pre>{JSON.stringify(alert.data, undefined, 2)}</pre>
<DialogDescription>
<div className="flex flex-col gap-2">
<div className="flex gap-4 items-center">
<div className="flex gap-2">
<div className="text-muted-foreground">status:</div>{" "}
<div
className={text_color_class_by_intention(
alert.resolved
? "Good"
: alert_level_intention(alert.level)
)}
>
{alert.resolved ? "RESOLVED" : "OPEN"}
</div>
</div>
<div className="flex gap-2">
<div className="text-muted-foreground">type:</div>{" "}
{alert.data.type}
</div>
<div className="flex gap-2 text-muted-foreground">
level: <AlertLevel level={alert.level} />
</div>
</div>
<pre>{JSON.stringify(alert.data.data, undefined, 2)}</pre>
</div>
</DialogDescription>
</>
)}
</DialogContent>
-5
View File
@@ -1,4 +1,3 @@
import { fmt_date_with_minutes } from "@lib/formatting";
import { Types } from "@monitor/client";
import { DataTable } from "@ui/data-table";
import { AlertLevel } from ".";
@@ -33,10 +32,6 @@ export const AlertsTable = ({ alerts }: { alerts: Types.Alert[] }) => {
header: "Alert Type",
accessorKey: "variant",
},
{
header: "Opened",
accessorFn: ({ ts }) => fmt_date_with_minutes(new Date(ts)),
},
]}
/>
);
@@ -1,5 +1,5 @@
import { NewLayout } from "@components/layouts";
import { useRead, useWrite } from "@lib/hooks";
import { useRead, useTagsFilter, useWrite } from "@lib/hooks";
import { Types } from "@monitor/client";
import {
Select,
@@ -17,7 +17,7 @@ import { DataTable } from "@ui/data-table";
import { Link } from "react-router-dom";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
import { AlerterConfig } from "./config";
import { TagsWithBadge, useTagsFilter } from "@components/tags";
import { TagsWithBadge } from "@components/tags";
import { DeleteResource, ResourceLink } from "../common";
const useAlerter = (id?: string) =>
@@ -92,11 +92,12 @@ export const AlerterComponents: RequiredResourceComponents = {
return (
<DataTable
data={
alerters?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
alerters?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
@@ -1,5 +1,5 @@
import { TagsWithBadge, useTagsFilter } from "@components/tags";
import { useRead } from "@lib/hooks";
import { TagsWithBadge } from "@components/tags";
import { useRead, useTagsFilter } from "@lib/hooks";
import { DataTable } from "@ui/data-table";
import { fmt_date_with_minutes, fmt_version } from "@lib/formatting";
import { ResourceLink } from "../common";
@@ -11,11 +11,12 @@ export const BuildTable = ({ search }: { search?: string }) => {
return (
<DataTable
data={
builds?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
builds?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
@@ -1,6 +1,5 @@
import { NewLayout } from "@components/layouts";
import { useTagsFilter } from "@components/tags";
import { useRead, useWrite } from "@lib/hooks";
import { useRead, useTagsFilter, useWrite } from "@lib/hooks";
import { Types } from "@monitor/client";
import { RequiredResourceComponents } from "@types";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
@@ -92,11 +91,12 @@ export const BuilderComponents: RequiredResourceComponents = {
return (
<DataTable
data={
builders?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
builders?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
@@ -1,7 +1,7 @@
import { TagsWithBadge, useTagsFilter } from "@components/tags";
import { TagsWithBadge } from "@components/tags";
import { Types } from "@monitor/client";
import { DataTable } from "@ui/data-table";
import { useRead } from "@lib/hooks";
import { useRead, useTagsFilter } from "@lib/hooks";
import { ResourceLink } from "../common";
import { DeploymentComponents } from ".";
@@ -17,11 +17,12 @@ export const DeploymentTable = ({
return (
<DataTable
data={
deployments?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
deployments?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
@@ -1,6 +1,6 @@
import { useRead } from "@lib/hooks";
import { useRead, useTagsFilter } from "@lib/hooks";
import { DataTable } from "@ui/data-table";
import { TagsWithBadge, useTagsFilter } from "@components/tags";
import { TagsWithBadge } from "@components/tags";
import { ResourceLink } from "../common";
export const ProcedureTable = ({ search }: { search?: string }) => {
@@ -10,11 +10,12 @@ export const ProcedureTable = ({ search }: { search?: string }) => {
return (
<DataTable
data={
procedures?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
procedures?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
@@ -1,5 +1,5 @@
import { TagsWithBadge, useTagsFilter } from "@components/tags";
import { useRead } from "@lib/hooks";
import { TagsWithBadge } from "@components/tags";
import { useRead, useTagsFilter } from "@lib/hooks";
import { RequiredResourceComponents } from "@types";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
import { DataTable } from "@ui/data-table";
@@ -41,11 +41,12 @@ export const RepoComponents: RequiredResourceComponents = {
return (
<DataTable
data={
repos?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
repos?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
@@ -1,5 +1,5 @@
import { TagsWithBadge, useTagsFilter } from "@components/tags";
import { useRead } from "@lib/hooks";
import { TagsWithBadge } from "@components/tags";
import { useRead, useTagsFilter } from "@lib/hooks";
import { DataTable } from "@ui/data-table";
import { ServerComponents } from ".";
import { ResourceLink } from "../common";
@@ -11,11 +11,12 @@ export const ServerTable = ({ search }: { search?: string }) => {
return (
<DataTable
data={
servers?.filter((resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true
servers?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
+4 -10
View File
@@ -1,4 +1,4 @@
import { useInvalidate, useRead, useWrite } from "@lib/hooks";
import { tagsAtom, useInvalidate, useRead, useWrite } from "@lib/hooks";
import { cn } from "@lib/utils";
import { Types } from "@monitor/client";
import { Badge } from "@ui/badge";
@@ -14,19 +14,11 @@ import {
import { Popover, PopoverContent, PopoverTrigger } from "@ui/popover";
import { useToast } from "@ui/use-toast";
import { useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { MinusCircle, PlusCircle, SearchX, Tag } from "lucide-react";
import { ReactNode, useEffect, useState } from "react";
type TargetExcludingSystem = Exclude<Types.ResourceTarget, { type: "System" }>;
const tagsAtom = atomWithStorage<string[]>("tags-v0", []);
export const useTagsFilter = () => {
const [tags] = useAtom(tagsAtom);
return tags;
};
export const TagsFilter = () => {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
@@ -104,7 +96,7 @@ export const TagsFilterTags = ({
{tag_ids?.map((tag_id) => (
<Badge
key={tag_id}
variant="destructive"
variant="secondary"
className="flex gap-1 px-2 py-1.5 cursor-pointer"
onClick={() => onBadgeClick && onBadgeClick(tag_id)}
>
@@ -125,12 +117,14 @@ export const ResourceTags = ({
click_to_delete?: boolean;
className?: string;
}) => {
const { toast } = useToast();
const inv = useInvalidate();
const { type, id } = target;
const resource = useRead(`List${type}s`, {}).data?.find((d) => d.id === id);
const { mutate } = useWrite("UpdateTagsOnResource", {
onSuccess: () => {
inv([`List${type}s`]);
toast({ title: "Removed tag" });
},
});
+5 -5
View File
@@ -55,15 +55,15 @@ export const stroke_color_class_by_intention = (intention: ColorIntention) => {
export const bg_color_class_by_intention = (intention: ColorIntention) => {
switch (intention) {
case "Good":
return "bg-green-500";
return "bg-green-400 dark:bg-green-700";
case "Neutral":
return "bg-blue-500";
return "bg-blue-400 dark:bg-blue-700";
case "Warning":
return "bg-orange-500";
return "bg-orange-400 dark:bg-orange-700";
case "Critical":
return "bg-red-500";
return "bg-red-400 dark:bg-red-700";
case "Unknown":
return "bg-purple-500";
return "bg-purple-400 dark:bg-purple-700";
case "None":
return "";
}
+10 -1
View File
@@ -15,6 +15,8 @@ import {
} from "@tanstack/react-query";
import { UsableResource } from "@types";
import { useToast } from "@ui/use-toast";
import { useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
@@ -174,4 +176,11 @@ export const useSetTitle = (more?: string) => {
document.title = title;
}
}, [title]);
}
}
export const tagsAtom = atomWithStorage<string[]>("tags-v0", []);
export const useTagsFilter = () => {
const [tags] = useAtom(tagsAtom);
return tags;
};
+14 -4
View File
@@ -1,8 +1,8 @@
import { OpenAlerts } from "@components/alert";
import { Page, Section } from "@components/layouts";
import { ResourceComponents } from "@components/resources";
import { TagsFilter, useTagsFilter } from "@components/tags";
import { useRead } from "@lib/hooks";
import { TagsFilter } from "@components/tags";
import { useRead, useTagsFilter } from "@lib/hooks";
import { RequiredResourceComponents, UsableResource } from "@types";
import { Input } from "@ui/input";
import { useState } from "react";
@@ -28,7 +28,12 @@ export const AllResources = () => {
>
<OpenAlerts />
{Object.entries(ResourceComponents).map(([type, Components]) => (
<TableSection type={type} Components={Components} search={search} />
<TableSection
key={type}
type={type}
Components={Components}
search={search}
/>
))}
</Page>
);
@@ -44,8 +49,13 @@ const TableSection = ({
search?: string;
}) => {
const tags = useTagsFilter();
const searchSplit = search?.split(" ") || [];
const count = useRead(`List${type as UsableResource}s`, {}).data?.filter(
(resource) => tags.every((tag) => resource.tags.includes(tag))
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
).length;
if (!count) return;
+7 -5
View File
@@ -8,6 +8,7 @@ import { ResourceComponents } from "@components/resources";
import { OpenAlerts } from "@components/alert";
import { useUser } from "@lib/hooks";
import { ResourceLink } from "@components/resources/common";
import { Fragment } from "react";
export const Dashboard = () => {
return (
@@ -59,9 +60,9 @@ const RecentlyViewed = () => {
actions=""
>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{recently_viewed?.slice(0, 6).map(
({ type, id }) =>
type !== "System" && (
{recently_viewed?.slice(0, 6).map(({ type, id }) => (
<Fragment key={type + id}>
{type !== "System" && (
<Card
onClick={() => nav(`/${type.toLowerCase()}s/${id}`)}
className="px-3 py-2 h-fit hover:bg-accent/50 group-focus:bg-accent/50 transition-colors cursor-pointer"
@@ -71,8 +72,9 @@ const RecentlyViewed = () => {
{type}
</CardContent>
</Card>
)
)}
)}
</Fragment>
))}
</div>
</Section>
);
+2 -2
View File
@@ -3,8 +3,8 @@ import { Page, Section } from "@components/layouts";
import { ResourceComponents } from "@components/resources";
import { DeploymentTable } from "@components/resources/deployment/table";
import { ServerComponents } from "@components/resources/server";
import { TagsFilter, TagsWithBadge, useTagsFilter } from "@components/tags";
import { useRead } from "@lib/hooks";
import { TagsFilter, TagsWithBadge } from "@components/tags";
import { useRead, useTagsFilter } from "@lib/hooks";
import { Button } from "@ui/button";
import { Card, CardHeader, CardTitle } from "@ui/card";
import { Fragment, useState } from "react";
+4 -31
View File
@@ -1,26 +1,15 @@
import { Page, Section, ResourceCard } from "@components/layouts";
import { Page, Section } from "@components/layouts";
import { ResourceComponents } from "@components/resources";
import { TagsFilter, useTagsFilter } from "@components/tags";
import { useRead, useResourceParamType, useSetTitle } from "@lib/hooks";
import { Button } from "@ui/button";
import { TagsFilter } from "@components/tags";
import { useResourceParamType, useSetTitle } from "@lib/hooks";
import { Input } from "@ui/input";
import { useState } from "react";
import { atomWithStorage } from "jotai/utils";
import { useAtom } from "jotai";
const viewAtom = atomWithStorage<"cards" | "table">("list-show-as-v0", "table");
export const Resources = () => {
const type = useResourceParamType()!;
useSetTitle(type + "s");
const Components = ResourceComponents[type];
const tags = useTagsFilter();
const list = useRead(`List${type}s`, { query: { tags } }).data;
const [search, set] = useState("");
const [view, setView] = useAtom(viewAtom);
return (
<Page
@@ -28,14 +17,6 @@ export const Resources = () => {
actions={
<div className="grid gap-4 justify-items-end">
<div className="flex gap-4">
<Button
variant="outline"
onClick={() =>
setView((v) => (v === "cards" ? "table" : "cards"))
}
>
show as {view === "cards" ? "table" : "cards"}
</Button>
<Input
value={search}
onChange={(e) => set(e.target.value)}
@@ -49,15 +30,7 @@ export const Resources = () => {
}
>
<Section title="">
{view === "cards" ? (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{list?.map(({ id }) => (
<ResourceCard key={id} target={{ type, id }} />
))}
</div>
) : (
<Components.Table />
)}
<Components.Table />
</Section>
</Page>
);