mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-06 18:56:39 +00:00
💄 blueprint data table
This commit is contained in:
@@ -1154,6 +1154,9 @@
|
|||||||
"sidebarBluePrints": "Blueprints",
|
"sidebarBluePrints": "Blueprints",
|
||||||
"blueprints": "Blueprints",
|
"blueprints": "Blueprints",
|
||||||
"blueprintsDescription": "Blueprints are declarative YAML configurations that define your resources and their settings",
|
"blueprintsDescription": "Blueprints are declarative YAML configurations that define your resources and their settings",
|
||||||
|
"blueprintAdd": "Add Blueprint",
|
||||||
|
"searchBlueprintProgress": "Search blueprints...",
|
||||||
|
"source": "Source",
|
||||||
"enableDockerSocket": "Enable Docker Blueprint",
|
"enableDockerSocket": "Enable Docker Blueprint",
|
||||||
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
"enableDockerSocketDescription": "Enable Docker Socket label scraping for blueprint labels. Socket path must be provided to Newt.",
|
||||||
"enableDockerSocketLink": "Learn More",
|
"enableDockerSocketLink": "Learn More",
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ async function queryBlueprints(orgId: string, limit: number, offset: number) {
|
|||||||
blueprintId: blueprints.blueprintId,
|
blueprintId: blueprints.blueprintId,
|
||||||
name: blueprints.name,
|
name: blueprints.name,
|
||||||
source: blueprints.source,
|
source: blueprints.source,
|
||||||
succeeded: blueprints.succeeded
|
succeeded: blueprints.succeeded,
|
||||||
|
orgId: blueprints.orgId
|
||||||
})
|
})
|
||||||
.from(blueprints)
|
.from(blueprints)
|
||||||
.leftJoin(orgs, eq(blueprints.orgId, orgs.orgId))
|
.leftJoin(orgs, eq(blueprints.orgId, orgs.orgId))
|
||||||
@@ -48,8 +49,15 @@ async function queryBlueprints(orgId: string, limit: number, offset: number) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BlueprintData = Omit<
|
||||||
|
Awaited<ReturnType<typeof queryBlueprints>>[number],
|
||||||
|
"source"
|
||||||
|
> & {
|
||||||
|
source: "API" | "WEB" | "CLI";
|
||||||
|
};
|
||||||
|
|
||||||
export type ListBlueprintsResponse = {
|
export type ListBlueprintsResponse = {
|
||||||
blueprints: NonNullable<Awaited<ReturnType<typeof queryBlueprints>>>;
|
blueprints: NonNullable<BlueprintData[]>;
|
||||||
pagination: { total: number; limit: number; offset: number };
|
pagination: { total: number; limit: number; offset: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,7 +116,7 @@ export async function listBlueprints(
|
|||||||
|
|
||||||
return response<ListBlueprintsResponse>(res, {
|
return response<ListBlueprintsResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
blueprints: blueprintsList,
|
blueprints: blueprintsList as BlueprintData[],
|
||||||
pagination: {
|
pagination: {
|
||||||
total: count,
|
total: count,
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -24,13 +24,17 @@ export default async function BluePrintsPage(props: BluePrintsPageProps) {
|
|||||||
try {
|
try {
|
||||||
const res = await internal.get<
|
const res = await internal.get<
|
||||||
AxiosResponse<ListBlueprintsResponse>
|
AxiosResponse<ListBlueprintsResponse>
|
||||||
>(`/org/${params.orgId}/domains`, await authCookieHeader());
|
>(`/org/${params.orgId}/blueprints`, await authCookieHeader());
|
||||||
|
|
||||||
blueprints = res.data.data.blueprints
|
blueprints = res.data.data.blueprints
|
||||||
|
console.log({
|
||||||
|
...res.data.data
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let org = null;
|
let org = null;
|
||||||
try {
|
try {
|
||||||
const getOrg = cache(async () =>
|
const getOrg = cache(async () =>
|
||||||
@@ -49,6 +53,8 @@ export default async function BluePrintsPage(props: BluePrintsPageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const t = await getTranslations();
|
const t = await getTranslations();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<OrgProvider org={org}>
|
<OrgProvider org={org}>
|
||||||
@@ -56,7 +62,7 @@ export default async function BluePrintsPage(props: BluePrintsPageProps) {
|
|||||||
title={t("blueprints")}
|
title={t("blueprints")}
|
||||||
description={t("blueprintsDescription")}
|
description={t("blueprintsDescription")}
|
||||||
/>
|
/>
|
||||||
<BlueprintsTable blueprints={blueprints} />
|
<BlueprintsTable blueprints={blueprints} orgId={params.orgId} />
|
||||||
</OrgProvider>
|
</OrgProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
import { DomainsDataTable } from "@app/components/DomainsDataTable";
|
import { DomainsDataTable } from "@app/components/DomainsDataTable";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import { ArrowUpDown } from "lucide-react";
|
import { ArrowRight, ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState, useTransition } from "react";
|
||||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||||
import { formatAxiosError } from "@app/lib/api";
|
import { formatAxiosError } from "@app/lib/api";
|
||||||
import { createApiClient } from "@app/lib/api";
|
import { createApiClient } from "@app/lib/api";
|
||||||
@@ -15,18 +15,177 @@ import { useTranslations } from "next-intl";
|
|||||||
import CreateDomainForm from "@app/components/CreateDomainForm";
|
import CreateDomainForm from "@app/components/CreateDomainForm";
|
||||||
import { useToast } from "@app/hooks/useToast";
|
import { useToast } from "@app/hooks/useToast";
|
||||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||||
|
import { DataTable } from "./ui/data-table";
|
||||||
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./ui/dropdown-menu";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { ListBlueprintsResponse } from "@server/routers/blueprints";
|
||||||
|
|
||||||
export type BlueprintRow = {
|
export type BlueprintRow = ListBlueprintsResponse['blueprints'][number]
|
||||||
blueprintId: number;
|
|
||||||
source: string;
|
|
||||||
succeeded: boolean;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
blueprints: BlueprintRow[];
|
blueprints: BlueprintRow[];
|
||||||
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function BlueprintsTable({ blueprints }: Props) {
|
export default function BlueprintsTable({ blueprints, orgId }: Props) {
|
||||||
return <></>
|
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
|
const [isRefreshing, startTransition] = useTransition()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
||||||
|
const columns: ColumnDef<BlueprintRow>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "name",
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() =>
|
||||||
|
column.toggleSorting(column.getIsSorted() === "asc")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("name")}
|
||||||
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "source",
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() =>
|
||||||
|
column.toggleSorting(column.getIsSorted() === "asc")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("source")}
|
||||||
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// cell: ({ row }) => {
|
||||||
|
// const originalRow = row.original;
|
||||||
|
// if (
|
||||||
|
// originalRow.type == "newt" ||
|
||||||
|
// originalRow.type == "wireguard"
|
||||||
|
// ) {
|
||||||
|
// if (originalRow.online) {
|
||||||
|
// return (
|
||||||
|
// <span className="text-green-500 flex items-center space-x-2">
|
||||||
|
// <div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||||
|
// <span>{t("online")}</span>
|
||||||
|
// </span>
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// return (
|
||||||
|
// <span className="text-neutral-500 flex items-center space-x-2">
|
||||||
|
// <div className="w-2 h-2 bg-gray-500 rounded-full"></div>
|
||||||
|
// <span>{t("offline")}</span>
|
||||||
|
// </span>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return <span>-</span>;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// accessorKey: "nice",
|
||||||
|
// header: ({ column }) => {
|
||||||
|
// return (
|
||||||
|
// <Button
|
||||||
|
// variant="ghost"
|
||||||
|
// onClick={() =>
|
||||||
|
// column.toggleSorting(column.getIsSorted() === "asc")
|
||||||
|
// }
|
||||||
|
// className="hidden md:flex whitespace-nowrap"
|
||||||
|
// >
|
||||||
|
// {t("site")}
|
||||||
|
// <ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
|
// </Button>
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// // cell: ({ row }) => {
|
||||||
|
// // return (
|
||||||
|
// // <div className="hidden md:block whitespace-nowrap">
|
||||||
|
// // {row.original.nice}
|
||||||
|
// // </div>
|
||||||
|
// // );
|
||||||
|
// // }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: "actions",
|
||||||
|
// cell: ({ row }) => {
|
||||||
|
// const siteRow = row.original;
|
||||||
|
// return (
|
||||||
|
// <div className="flex items-center justify-end gap-2">
|
||||||
|
// <DropdownMenu>
|
||||||
|
// <DropdownMenuTrigger asChild>
|
||||||
|
// <Button variant="ghost" className="h-8 w-8 p-0">
|
||||||
|
// <span className="sr-only">Open menu</span>
|
||||||
|
// <MoreHorizontal className="h-4 w-4" />
|
||||||
|
// </Button>
|
||||||
|
// </DropdownMenuTrigger>
|
||||||
|
// <DropdownMenuContent align="end">
|
||||||
|
// <Link
|
||||||
|
// className="block w-full"
|
||||||
|
// href="#"
|
||||||
|
// // href={`/${siteRow.orgId}/settings/sites/${siteRow.nice}`}
|
||||||
|
// >
|
||||||
|
// <DropdownMenuItem>
|
||||||
|
// {t("viewSettings")}
|
||||||
|
// </DropdownMenuItem>
|
||||||
|
// </Link>
|
||||||
|
// <DropdownMenuItem
|
||||||
|
// onClick={() => {
|
||||||
|
// // setSelectedSite(siteRow);
|
||||||
|
// // setIsDeleteModalOpen(true);
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <span className="text-red-500">
|
||||||
|
// {t("delete")}
|
||||||
|
// </span>
|
||||||
|
// </DropdownMenuItem>
|
||||||
|
// </DropdownMenuContent>
|
||||||
|
// </DropdownMenu>
|
||||||
|
|
||||||
|
// <Link
|
||||||
|
// href="#"
|
||||||
|
// // href={`/${siteRow.orgId}/settings/sites/${siteRow.nice}`}
|
||||||
|
// >
|
||||||
|
// <Button variant={"secondary"} size="sm">
|
||||||
|
// {t("edit")}
|
||||||
|
// <ArrowRight className="ml-2 w-4 h-4" />
|
||||||
|
// </Button>
|
||||||
|
// </Link>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
return <DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={blueprints}
|
||||||
|
persistPageSize="blueprint-table"
|
||||||
|
title={t('blueprints')}
|
||||||
|
searchPlaceholder={t('searchBlueprintProgress')}
|
||||||
|
searchColumn="name"
|
||||||
|
onAdd={() => {
|
||||||
|
router.push(`/${orgId}/settings/blueprints/create`);
|
||||||
|
}}
|
||||||
|
addButtonText={t('blueprintAdd')}
|
||||||
|
onRefresh={() => {
|
||||||
|
startTransition(() => router.refresh())
|
||||||
|
}}
|
||||||
|
isRefreshing={isRefreshing}
|
||||||
|
defaultSort={{
|
||||||
|
id: "name",
|
||||||
|
desc: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user