move all components to components dir

This commit is contained in:
miloschwartz
2025-09-04 11:18:42 -07:00
parent 4292d3262e
commit df85f13aea
90 changed files with 2166 additions and 86 deletions

View File

@@ -1,34 +0,0 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
}
export function IdpDataTable<TData, TValue>({
columns,
data
}: DataTableProps<TData, TValue>) {
const router = useRouter();
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
persistPageSize="idp-table"
title={t('idp')}
searchPlaceholder={t('idpSearch')}
searchColumn="name"
addButtonText={t('idpAdd')}
onAdd={() => {
router.push("/admin/idp/create");
}}
/>
);
}

View File

@@ -1,208 +0,0 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { IdpDataTable } from "./AdminIdpDataTable";
import { Button } from "@app/components/ui/button";
import { ArrowRight, ArrowUpDown, MoreHorizontal } from "lucide-react";
import { useState } from "react";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { toast } from "@app/hooks/useToast";
import { formatAxiosError } from "@app/lib/api";
import { createApiClient } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { Badge } from "@app/components/ui/badge";
import { useRouter } from "next/navigation";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "@app/components/ui/dropdown-menu";
import Link from "next/link";
import { useTranslations } from "next-intl";
export type IdpRow = {
idpId: number;
name: string;
type: string;
orgCount: number;
};
type Props = {
idps: IdpRow[];
};
export default function IdpTable({ idps }: Props) {
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [selectedIdp, setSelectedIdp] = useState<IdpRow | null>(null);
const api = createApiClient(useEnvContext());
const router = useRouter();
const t = useTranslations();
const deleteIdp = async (idpId: number) => {
try {
await api.delete(`/idp/${idpId}`);
toast({
title: t("success"),
description: t("idpDeletedDescription")
});
setIsDeleteModalOpen(false);
router.refresh();
} catch (e) {
toast({
title: t("error"),
description: formatAxiosError(e),
variant: "destructive"
});
}
};
const getTypeDisplay = (type: string) => {
switch (type) {
case "oidc":
return "OAuth2/OIDC";
default:
return type;
}
};
const columns: ColumnDef<IdpRow>[] = [
{
accessorKey: "idpId",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
ID
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
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: "type",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
{t("type")}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const type = row.original.type;
return (
<Badge variant="secondary">{getTypeDisplay(type)}</Badge>
);
}
},
{
id: "actions",
cell: ({ row }) => {
const siteRow = row.original;
return (
<div className="flex items-center justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">
{t("openMenu")}
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<Link
className="block w-full"
href={`/admin/idp/${siteRow.idpId}/general`}
>
<DropdownMenuItem>
{t("viewSettings")}
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => {
setSelectedIdp(siteRow);
setIsDeleteModalOpen(true);
}}
>
<span className="text-red-500">
{t("delete")}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link href={`/admin/idp/${siteRow.idpId}/general`}>
<Button
variant={"secondary"}
className="ml-2"
size="sm"
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}
}
];
return (
<>
{selectedIdp && (
<ConfirmDeleteDialog
open={isDeleteModalOpen}
setOpen={(val) => {
setIsDeleteModalOpen(val);
setSelectedIdp(null);
}}
dialog={
<div className="space-y-4">
<p>
{t("idpQuestionRemove", {
name: selectedIdp.name
})}
</p>
<p>
<b>{t("idpMessageRemove")}</b>
</p>
<p>{t("idpMessageConfirm")}</p>
</div>
}
buttonText={t("idpConfirmDelete")}
onConfirm={async () => deleteIdp(selectedIdp.idpId)}
string={selectedIdp.name}
title={t("idpDelete")}
/>
)}
<IdpDataTable columns={columns} data={idps} />
</>
);
}

View File

@@ -1,33 +0,0 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useTranslations } from "next-intl";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
onAdd: () => void;
}
export function PolicyDataTable<TData, TValue>({
columns,
data,
onAdd
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
persistPageSize="orgPolicies-table"
title={t('orgPolicies')}
searchPlaceholder={t('orgPoliciesSearch')}
searchColumn="orgId"
addButtonText={t('orgPoliciesAdd')}
onAdd={onAdd}
/>
);
}

View File

@@ -1,156 +0,0 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { Button } from "@app/components/ui/button";
import {
ArrowUpDown,
Trash2,
MoreHorizontal,
Pencil,
ArrowRight
} from "lucide-react";
import { PolicyDataTable } from "./PolicyDataTable";
import { Badge } from "@app/components/ui/badge";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "@app/components/ui/dropdown-menu";
import Link from "next/link";
import { InfoPopup } from "@app/components/ui/info-popup";
import { useTranslations } from "next-intl";
export interface PolicyRow {
orgId: string;
roleMapping?: string;
orgMapping?: string;
}
interface Props {
policies: PolicyRow[];
onDelete: (orgId: string) => void;
onAdd: () => void;
onEdit: (policy: PolicyRow) => void;
}
export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props) {
const t = useTranslations();
const columns: ColumnDef<PolicyRow>[] = [
{
id: "dots",
cell: ({ row }) => {
const r = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">{t('openMenu')}</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
onDelete(r.orgId);
}}
>
<span className="text-red-500">{t('delete')}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
},
{
accessorKey: "orgId",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
{t('orgId')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
}
},
{
accessorKey: "roleMapping",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
{t('roleMapping')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const mapping = row.original.roleMapping;
return mapping ? (
<InfoPopup
text={mapping.length > 50 ? `${mapping.substring(0, 50)}...` : mapping}
info={mapping}
/>
) : (
"--"
);
}
},
{
accessorKey: "orgMapping",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
{t('orgMapping')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const mapping = row.original.orgMapping;
return mapping ? (
<InfoPopup
text={mapping.length > 50 ? `${mapping.substring(0, 50)}...` : mapping}
info={mapping}
/>
) : (
"--"
);
}
},
{
id: "actions",
cell: ({ row }) => {
const policy = row.original;
return (
<div className="flex items-center justify-end">
<Button
variant={"secondary"}
className="ml-2"
onClick={() => onEdit(policy)}
>
{t('edit')}
</Button>
</div>
);
}
}
];
return <PolicyDataTable columns={columns} data={policies} onAdd={onAdd} />;
}

View File

@@ -31,7 +31,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { InfoIcon, ExternalLink, CheckIcon } from "lucide-react";
import PolicyTable, { PolicyRow } from "./PolicyTable";
import PolicyTable, { PolicyRow } from "../../../../../components/PolicyTable";
import { AxiosResponse } from "axios";
import { ListOrgsResponse } from "@server/routers/org";
import {

View File

@@ -2,7 +2,7 @@ import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { AxiosResponse } from "axios";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import IdpTable, { IdpRow } from "./AdminIdpTable";
import IdpTable, { IdpRow } from "../../../components/AdminIdpTable";
import { getTranslations } from "next-intl/server";
export default async function IdpPage() {
@@ -16,7 +16,7 @@ export default async function IdpPage() {
} catch (e) {
console.error(e);
}
const t = await getTranslations();
return (