sortable resource table

This commit is contained in:
mbecker20
2024-04-21 00:18:47 -07:00
parent a7a7d0552b
commit cb9e0ae252
9 changed files with 210 additions and 154 deletions

View File

@@ -1,5 +1,5 @@
import { NewLayout } from "@components/layouts";
import { useRead, useTagsFilter, useWrite } from "@lib/hooks";
import { useRead, useWrite } from "@lib/hooks";
import { Types } from "@monitor/client";
import {
Select,
@@ -13,12 +13,11 @@ import { RequiredResourceComponents } from "@types";
import { Input } from "@ui/input";
import { AlarmClock } from "lucide-react";
import { useState } from "react";
import { DataTable, SortableHeader } from "@ui/data-table";
import { Link } from "react-router-dom";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
import { AlerterConfig } from "./config";
import { TagsWithBadge } from "@components/tags";
import { DeleteResource, ResourceLink } from "../common";
import { DeleteResource } from "../common";
import { AlerterTable } from "./table";
const useAlerter = (id?: string) =>
useRead("ListAlerters", {}).data?.find((d) => d.id === id);
@@ -85,50 +84,7 @@ export const AlerterComponents: RequiredResourceComponents = {
);
},
Table: ({ search }) => {
const tags = useTagsFilter();
const alerters = useRead("ListAlerters", {}).data;
const searchSplit = search?.split(" ") || [];
return (
<DataTable
tableKey="alerters"
data={
alerters?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
{
accessorKey: "name",
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => (
<ResourceLink type="Alerter" id={row.original.id} />
),
},
{
header: "Type",
accessorKey: "info.alerter_type",
},
{
header: "Tags",
cell: ({ row }) => {
return (
<div className="flex gap-1">
<TagsWithBadge tag_ids={row.original.tags} />
</div>
);
},
},
]}
/>
);
},
Table: AlerterTable,
Name: ({ id }: { id: string }) => <>{useAlerter(id)?.name}</>,
name: (id) => useAlerter(id)?.name,

View File

@@ -0,0 +1,51 @@
import { useRead, useTagsFilter } from "@lib/hooks";
import { DataTable, SortableHeader } from "@ui/data-table";
import { ResourceLink } from "../common";
import { TagsWithBadge } from "@components/tags";
export const AlerterTable = ({ search }: { search?: string }) => {
const tags = useTagsFilter();
const alerters = useRead("ListAlerters", {}).data;
const searchSplit = search?.split(" ") || [];
return (
<DataTable
tableKey="alerters"
data={
alerters?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
{
accessorKey: "name",
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => (
<ResourceLink type="Alerter" id={row.original.id} />
),
},
{
accessorKey: "info.alerter_type",
header: ({ column }) => (
<SortableHeader column={column} title="Type" />
),
},
{
header: "Tags",
cell: ({ row }) => {
return (
<div className="flex gap-1">
<TagsWithBadge tag_ids={row.original.tags} />
</div>
);
},
},
]}
/>
);
};

View File

@@ -26,20 +26,23 @@ export const BuildTable = ({ search }: { search?: string }) => {
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => (
<ResourceLink type="Build" id={row.original.id} />
),
cell: ({ row }) => <ResourceLink type="Build" id={row.original.id} />,
},
{
header: "Version",
accessorFn: ({ info }) => fmt_version(info.version),
},
{
header: "Repo",
accessorKey: "info.repo",
header: ({ column }) => (
<SortableHeader column={column} title="Repo" />
),
},
{
header: "Last Built",
accessorKey: "info.last_built_at",
header: ({ column }) => (
<SortableHeader column={column} title="Last Built" />
),
accessorFn: ({ info: { last_built_at } }) => {
if (last_built_at > 0) {
return fmt_date_with_minutes(new Date(last_built_at));

View File

@@ -1,9 +1,8 @@
import { NewLayout } from "@components/layouts";
import { useRead, useTagsFilter, useWrite } from "@lib/hooks";
import { useRead, useWrite } from "@lib/hooks";
import { Types } from "@monitor/client";
import { RequiredResourceComponents } from "@types";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
import { DataTable, SortableHeader } from "@ui/data-table";
import { Input } from "@ui/input";
import {
Select,
@@ -17,7 +16,8 @@ import { Cloud, Bot, Factory } from "lucide-react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { BuilderConfig } from "./config";
import { DeleteResource, ResourceLink } from "../common";
import { DeleteResource } from "../common";
import { BuidlerTable } from "./table";
const useBuilder = (id?: string) =>
useRead("ListBuilders", {}).data?.find((d) => d.id === id);
@@ -84,45 +84,7 @@ export const BuilderComponents: RequiredResourceComponents = {
);
},
Table: ({ search }) => {
const tags = useTagsFilter();
const builders = useRead("ListBuilders", {}).data;
const searchSplit = search?.split(" ") || [];
return (
<DataTable
tableKey="builders"
data={
builders?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
{
accessorKey: "name",
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => (
<ResourceLink type="Builder" id={row.original.id} />
),
},
{
header: "Provider",
accessorKey: "info.provider",
},
{
header: "Instance Type",
accessorKey: "info.instance_type",
},
{ header: "Tags", accessorFn: ({ tags }) => tags.join(", ") },
]}
/>
);
},
Table: BuidlerTable,
Name: ({ id }: { id: string }) => <>{useBuilder(id)?.name}</>,
name: (id) => useBuilder(id)?.name,

View File

@@ -0,0 +1,57 @@
import { useRead, useTagsFilter } from "@lib/hooks";
import { DataTable, SortableHeader } from "@ui/data-table";
import { ResourceLink } from "../common";
import { TagsWithBadge } from "@components/tags";
export const BuidlerTable = ({ search }: { search?: string }) => {
const tags = useTagsFilter();
const builders = useRead("ListBuilders", {}).data;
const searchSplit = search?.split(" ") || [];
return (
<DataTable
tableKey="builders"
data={
builders?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
{
accessorKey: "name",
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => (
<ResourceLink type="Builder" id={row.original.id} />
),
},
{
accessorKey: "info.provider",
header: ({ column }) => (
<SortableHeader column={column} title="Provider" />
),
},
{
accessorKey: "info.instance_type",
header: ({ column }) => (
<SortableHeader column={column} title="Instance Type" />
),
},
{
header: "Tags",
cell: ({ row }) => {
return (
<div className="flex gap-1">
<TagsWithBadge tag_ids={row.original.tags} />
</div>
);
},
},
]}
/>
);
};

View File

@@ -30,8 +30,10 @@ export const ProcedureTable = ({ search }: { search?: string }) => {
),
},
{
header: "Type",
accessorKey: "info.procedure_type",
header: ({ column }) => (
<SortableHeader column={column} title="Type" />
),
},
{
header: "Tags",

View File

@@ -1,13 +1,12 @@
import { TagsWithBadge } from "@components/tags";
import { useRead, useTagsFilter } from "@lib/hooks";
import { useRead } from "@lib/hooks";
import { RequiredResourceComponents } from "@types";
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
import { DataTable, SortableHeader } from "@ui/data-table";
import { FolderGit, GitBranch } from "lucide-react";
import { Link } from "react-router-dom";
import { RepoConfig } from "./config";
import { CloneRepo, PullRepo } from "./actions";
import { DeleteResource, NewResource, ResourceLink } from "../common";
import { DeleteResource, NewResource } from "../common";
import { RepoTable } from "./table";
const useRepo = (id?: string) =>
useRead("ListRepos", {}).data?.find((d) => d.id === id);
@@ -34,54 +33,7 @@ export const RepoComponents: RequiredResourceComponents = {
New: () => <NewResource type="Repo" />,
Table: ({ search }) => {
const tags = useTagsFilter();
const repos = useRead("ListRepos", {}).data;
const searchSplit = search?.split(" ") || [];
return (
<DataTable
tableKey="repos"
data={
repos?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
{
accessorKey: "name",
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => (
<ResourceLink type="Repo" id={row.original.id} />
),
},
{
header: "Repo",
accessorKey: "info.repo",
},
{
header: "Branch",
accessorKey: "info.branch",
},
{
header: "Tags",
cell: ({ row }) => {
return (
<div className="flex gap-1">
<TagsWithBadge tag_ids={row.original.tags} />
</div>
);
},
},
]}
/>
);
},
Table: RepoTable,
Name: ({ id }: { id: string }) => <>{useRepo(id)?.name}</>,
name: (id) => useRepo(id)?.name,

View File

@@ -0,0 +1,55 @@
import { useRead, useTagsFilter } from "@lib/hooks";
import { DataTable, SortableHeader } from "@ui/data-table";
import { ResourceLink } from "../common";
import { TagsWithBadge } from "@components/tags";
export const RepoTable = ({ search }: { search?: string }) => {
const tags = useTagsFilter();
const repos = useRead("ListRepos", {}).data;
const searchSplit = search?.split(" ") || [];
return (
<DataTable
tableKey="repos"
data={
repos?.filter(
(resource) =>
tags.every((tag) => resource.tags.includes(tag)) &&
(searchSplit.length > 0
? searchSplit.every((search) => resource.name.includes(search))
: true)
) ?? []
}
columns={[
{
accessorKey: "name",
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
cell: ({ row }) => <ResourceLink type="Repo" id={row.original.id} />,
},
{
accessorKey: "info.repo",
header: ({ column }) => (
<SortableHeader column={column} title="Repo" />
),
},
{
accessorKey: "info.branch",
header: ({ column }) => (
<SortableHeader column={column} title="Branch" />
),
},
{
header: "Tags",
cell: ({ row }) => {
return (
<div className="flex gap-1">
<TagsWithBadge tag_ids={row.original.tags} />
</div>
);
},
},
]}
/>
);
};

View File

@@ -31,7 +31,22 @@ export const ServerTable = ({ search }: { search?: string }) => {
),
},
{
header: "Deployments",
accessorKey: "id",
sortingFn: (a, b) => {
const sa = useDeploymentCount(a.original.id);
const sb = useDeploymentCount(b.original.id);
if (!sa && !sb) return 0;
if (!sa) return -1;
if (!sb) return 1;
if (sa > sb) return 1;
else if (sa < sb) return -1;
else return 0;
},
header: ({ column }) => (
<SortableHeader column={column} title="Deployments" />
),
cell: ({ row }) => <DeploymentCountOnServer id={row.original.id} />,
},
{
@@ -69,9 +84,12 @@ export const ServerTable = ({ search }: { search?: string }) => {
};
const DeploymentCountOnServer = ({ id }: { id: string }) => {
const { data } = useRead("ListDeployments", {
query: { specific: { server_ids: [id] } },
});
return <>{data?.length ?? 0}</>;
const count = useDeploymentCount(id);
return <>{count ?? 0}</>;
};
const useDeploymentCount = (id: string) => {
return useRead("ListDeployments", {}).data?.filter(
(d) => d.info.server_id === id
).length;
};