mirror of https://github.com/usual2970/certimate
Merge authorization group into authorization management
parent
500fce6180
commit
fa85580e35
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -5,8 +5,8 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
||||
<script type="module" crossorigin src="/assets/index-pPAQ4idS.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DXJTf3ck.css">
|
||||
<script type="module" crossorigin src="/assets/index-CDJ2jqew.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-ChWRjRip.css">
|
||||
</head>
|
||||
<body class="bg-background">
|
||||
<div id="root"></div>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
|
@ -1762,6 +1763,35 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz",
|
||||
"integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-presence": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-roving-focus": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toast": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.2.1.tgz",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
import AccessGroupEdit from "@/components/certimate/AccessGroupEdit";
|
||||
import Show from "@/components/Show";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { getProviderInfo } from "@/domain/access";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { useConfig } from "@/providers/config";
|
||||
import { remove } from "@/repository/access_group";
|
||||
import { Group } from "lucide-react";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const AccessGroupList = () => {
|
||||
const {
|
||||
config: { accessGroups },
|
||||
reloadAccessGroups,
|
||||
} = useConfig();
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
|
||||
const handleRemoveClick = async (id: string) => {
|
||||
try {
|
||||
await remove(id);
|
||||
reloadAccessGroups();
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: "删除失败",
|
||||
description: getErrMessage(e),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddAccess = () => {
|
||||
navigate("/access");
|
||||
};
|
||||
return (
|
||||
<div className="mt-10">
|
||||
<Show when={accessGroups.length == 0}>
|
||||
<>
|
||||
<div className="flex flex-col items-center mt-10">
|
||||
<span className="bg-orange-100 p-5 rounded-full">
|
||||
<Group size={40} className="text-primary" />
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
请添加域名开始部署证书吧。
|
||||
</div>
|
||||
<AccessGroupEdit
|
||||
trigger={<Button>新增授权组</Button>}
|
||||
className="mt-3"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</Show>
|
||||
|
||||
<ScrollArea className="h-[75vh] overflow-hidden">
|
||||
<div className="flex gap-5 flex-wrap">
|
||||
{accessGroups.map((accessGroup) => (
|
||||
<Card className="w-full md:w-[350px]">
|
||||
<CardHeader>
|
||||
<CardTitle>{accessGroup.name}</CardTitle>
|
||||
<CardDescription>
|
||||
共有
|
||||
{accessGroup.expand ? accessGroup.expand.access.length : 0}
|
||||
个部署授权配置
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="min-h-[180px]">
|
||||
{accessGroup.expand ? (
|
||||
<>
|
||||
{accessGroup.expand.access.slice(0, 3).map((access) => (
|
||||
<div key={access.id} className="flex flex-col mb-3">
|
||||
<div className="flex items-center">
|
||||
<div className="">
|
||||
<img
|
||||
src={getProviderInfo(access.configType)![1]}
|
||||
alt="provider"
|
||||
className="w-8 h-8"
|
||||
></img>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<div className="text-sm font-semibold text-gray-700 dark:text-gray-200">
|
||||
{access.name}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{getProviderInfo(access.configType)![0]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex text-gray-700 dark:text-gray-200 items-center">
|
||||
<div>
|
||||
<Group size={40} />
|
||||
</div>
|
||||
<div className="ml-2">
|
||||
暂无部署授权配置,请添加后开始使用吧
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-end w-full">
|
||||
<Show
|
||||
when={
|
||||
accessGroup.expand && accessGroup.expand.access.length > 0
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={"link"}
|
||||
onClick={() => {
|
||||
navigate(
|
||||
`/access?accessGroupId=${accessGroup.id}&tab=access`,
|
||||
{
|
||||
replace: true,
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
所有授权
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show
|
||||
when={
|
||||
!accessGroup.expand ||
|
||||
accessGroup.expand.access.length == 0
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Button size="sm" onClick={handleAddAccess}>
|
||||
新增授权
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div className="ml-3">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={"destructive"} size={"sm"}>
|
||||
删除
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="dark:text-gray-200">
|
||||
删除组
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
确定要删除部署授权组吗?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="dark:text-gray-200">
|
||||
取消
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
handleRemoveClick(
|
||||
accessGroup.id ? accessGroup.id : ""
|
||||
);
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessGroupList;
|
|
@ -0,0 +1,53 @@
|
|||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Tabs = TabsPrimitive.Root
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsList.displayName = TabsPrimitive.List.displayName
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
|
@ -9,7 +9,7 @@ import {
|
|||
BookOpen,
|
||||
CircleUser,
|
||||
Earth,
|
||||
Group,
|
||||
|
||||
History,
|
||||
Home,
|
||||
Menu,
|
||||
|
@ -101,17 +101,6 @@ export default function Dashboard() {
|
|||
授权管理
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/access_groups"
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary",
|
||||
getClass("/access_groups")
|
||||
)}
|
||||
>
|
||||
<Group className="h-4 w-4" />
|
||||
部署授权组
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/history"
|
||||
className={cn(
|
||||
|
@ -180,17 +169,6 @@ export default function Dashboard() {
|
|||
授权管理
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/access_groups"
|
||||
className={cn(
|
||||
"mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 hover:text-foreground",
|
||||
getClass("/access_groups")
|
||||
)}
|
||||
>
|
||||
<Group className="h-5 w-5" />
|
||||
部署授权组
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/history"
|
||||
className={cn(
|
||||
|
@ -250,7 +228,7 @@ export default function Dashboard() {
|
|||
href="https://github.com/usual2970/certimate/releases"
|
||||
target="_blank"
|
||||
>
|
||||
Certimate v0.1.6
|
||||
Certimate v0.1.7
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
||||
import AccessGroupList from "@/components/certimate/AccessGroupList";
|
||||
import XPagination from "@/components/certimate/XPagination";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Access as AccessType, accessTypeMap } from "@/domain/access";
|
||||
import { convertZulu2Beijing } from "@/lib/time";
|
||||
import { useConfig } from "@/providers/config";
|
||||
|
@ -23,6 +25,8 @@ const Access = () => {
|
|||
const page = query.get("page");
|
||||
const pageNumber = page ? Number(page) : 1;
|
||||
|
||||
const tab = query.get("tab");
|
||||
|
||||
const accessGroupId = query.get("accessGroupId");
|
||||
|
||||
const startIndex = (pageNumber - 1) * perPage;
|
||||
|
@ -33,100 +37,137 @@ const Access = () => {
|
|||
deleteAccess(rs.id);
|
||||
};
|
||||
|
||||
const handleTabItemClick = (tab: string) => {
|
||||
query.set("tab", tab);
|
||||
navigate({ search: query.toString() });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-muted-foreground">授权管理</div>
|
||||
<AccessEdit trigger={<Button>添加授权</Button>} op="add" />
|
||||
</div>
|
||||
{accesses.length === 0 ? (
|
||||
<div className="flex flex-col items-center mt-10">
|
||||
<span className="bg-orange-100 p-5 rounded-full">
|
||||
<Key size={40} className="text-primary" />
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
请添加授权开始部署证书吧。
|
||||
</div>
|
||||
<AccessEdit
|
||||
trigger={<Button>添加授权</Button>}
|
||||
op="add"
|
||||
className="mt-3"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
||||
<div className="w-48">名称</div>
|
||||
<div className="w-48">服务商</div>
|
||||
|
||||
<div className="w-52">创建时间</div>
|
||||
<div className="w-52">更新时间</div>
|
||||
<div className="grow">操作</div>
|
||||
</div>
|
||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||
授权列表
|
||||
</div>
|
||||
{accesses
|
||||
.filter((item) => {
|
||||
return accessGroupId ? item.group == accessGroupId : true;
|
||||
})
|
||||
.slice(startIndex, endIndex)
|
||||
.map((access) => (
|
||||
<div
|
||||
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
||||
key={access.id}
|
||||
>
|
||||
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
|
||||
{access.name}
|
||||
</div>
|
||||
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center space-x-2">
|
||||
<img
|
||||
src={accessTypeMap.get(access.configType)?.[1]}
|
||||
className="w-6"
|
||||
/>
|
||||
<div>{accessTypeMap.get(access.configType)?.[0]}</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:w-52 w-full pt-1 sm:pt-0 flex items-center">
|
||||
创建于 {access.created && convertZulu2Beijing(access.created)}
|
||||
</div>
|
||||
<div className="sm:w-52 w-full pt-1 sm:pt-0 flex items-center">
|
||||
更新于 {access.updated && convertZulu2Beijing(access.updated)}
|
||||
</div>
|
||||
<div className="flex items-center grow justify-start pt-1 sm:pt-0">
|
||||
<AccessEdit
|
||||
trigger={
|
||||
<Button variant={"link"} className="p-0">
|
||||
编辑
|
||||
</Button>
|
||||
}
|
||||
op="edit"
|
||||
data={access}
|
||||
/>
|
||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||
<Button
|
||||
variant={"link"}
|
||||
className="p-0"
|
||||
onClick={() => {
|
||||
handleDelete(access);
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<XPagination
|
||||
totalPages={totalPages}
|
||||
currentPage={pageNumber}
|
||||
onPageChange={(page) => {
|
||||
query.set("page", page.toString());
|
||||
navigate({ search: query.toString() });
|
||||
<Tabs
|
||||
defaultValue={tab ? tab : "access"}
|
||||
value={tab ? tab : "access"}
|
||||
className="w-full mt-5"
|
||||
>
|
||||
<TabsList className="space-x-5 px-3">
|
||||
<TabsTrigger
|
||||
value="access"
|
||||
onClick={() => {
|
||||
handleTabItemClick("access");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
授权管理
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="access_group"
|
||||
onClick={() => {
|
||||
handleTabItemClick("access_group");
|
||||
}}
|
||||
>
|
||||
授权组管理
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="access">
|
||||
{accesses.length === 0 ? (
|
||||
<div className="flex flex-col items-center mt-10">
|
||||
<span className="bg-orange-100 p-5 rounded-full">
|
||||
<Key size={40} className="text-primary" />
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
请添加授权开始部署证书吧。
|
||||
</div>
|
||||
<AccessEdit
|
||||
trigger={<Button>添加授权</Button>}
|
||||
op="add"
|
||||
className="mt-3"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="hidden sm:flex sm:flex-row text-muted-foreground text-sm border-b dark:border-stone-500 sm:p-2 mt-5">
|
||||
<div className="w-48">名称</div>
|
||||
<div className="w-48">服务商</div>
|
||||
|
||||
<div className="w-52">创建时间</div>
|
||||
<div className="w-52">更新时间</div>
|
||||
<div className="grow">操作</div>
|
||||
</div>
|
||||
<div className="sm:hidden flex text-sm text-muted-foreground">
|
||||
授权列表
|
||||
</div>
|
||||
{accesses
|
||||
.filter((item) => {
|
||||
return accessGroupId ? item.group == accessGroupId : true;
|
||||
})
|
||||
.slice(startIndex, endIndex)
|
||||
.map((access) => (
|
||||
<div
|
||||
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
|
||||
key={access.id}
|
||||
>
|
||||
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center">
|
||||
{access.name}
|
||||
</div>
|
||||
<div className="sm:w-48 w-full pt-1 sm:pt-0 flex items-center space-x-2">
|
||||
<img
|
||||
src={accessTypeMap.get(access.configType)?.[1]}
|
||||
className="w-6"
|
||||
/>
|
||||
<div>{accessTypeMap.get(access.configType)?.[0]}</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:w-52 w-full pt-1 sm:pt-0 flex items-center">
|
||||
创建于{" "}
|
||||
{access.created && convertZulu2Beijing(access.created)}
|
||||
</div>
|
||||
<div className="sm:w-52 w-full pt-1 sm:pt-0 flex items-center">
|
||||
更新于{" "}
|
||||
{access.updated && convertZulu2Beijing(access.updated)}
|
||||
</div>
|
||||
<div className="flex items-center grow justify-start pt-1 sm:pt-0">
|
||||
<AccessEdit
|
||||
trigger={
|
||||
<Button variant={"link"} className="p-0">
|
||||
编辑
|
||||
</Button>
|
||||
}
|
||||
op="edit"
|
||||
data={access}
|
||||
/>
|
||||
<Separator orientation="vertical" className="h-4 mx-2" />
|
||||
<Button
|
||||
variant={"link"}
|
||||
className="p-0"
|
||||
onClick={() => {
|
||||
handleDelete(access);
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<XPagination
|
||||
totalPages={totalPages}
|
||||
currentPage={pageNumber}
|
||||
onPageChange={(page) => {
|
||||
query.set("page", page.toString());
|
||||
navigate({ search: query.toString() });
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="access_group">
|
||||
<AccessGroupList />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
import AccessGroupEdit from "@/components/certimate/AccessGroupEdit";
|
||||
import Show from "@/components/Show";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { getProviderInfo } from "@/domain/access";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { useConfig } from "@/providers/config";
|
||||
import { remove } from "@/repository/access_group";
|
||||
import { Group } from "lucide-react";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
const AccessGroups = () => {
|
||||
const {
|
||||
config: { accessGroups },
|
||||
reloadAccessGroups,
|
||||
} = useConfig();
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleRemoveClick = async (id: string) => {
|
||||
try {
|
||||
await remove(id);
|
||||
reloadAccessGroups();
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: "删除失败",
|
||||
description: getErrMessage(e),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddAccess = () => {
|
||||
navigate("/access");
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Toaster />
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-muted-foreground">部署授权组</div>
|
||||
|
||||
<AccessGroupEdit trigger={<Button>新增授权组</Button>} />
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<Show when={accessGroups.length == 0}>
|
||||
<>
|
||||
<div className="flex flex-col items-center mt-10">
|
||||
<span className="bg-orange-100 p-5 rounded-full">
|
||||
<Group size={40} className="text-primary" />
|
||||
</span>
|
||||
|
||||
<div className="text-center text-sm text-muted-foreground mt-3">
|
||||
请添加域名开始部署证书吧。
|
||||
</div>
|
||||
<AccessGroupEdit
|
||||
trigger={<Button>新增授权组</Button>}
|
||||
className="mt-3"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</Show>
|
||||
|
||||
<ScrollArea className="h-[75vh] overflow-hidden">
|
||||
<div className="flex gap-5 flex-wrap">
|
||||
{accessGroups.map((accessGroup) => (
|
||||
<Card className="w-full md:w-[350px]">
|
||||
<CardHeader>
|
||||
<CardTitle>{accessGroup.name}</CardTitle>
|
||||
<CardDescription>
|
||||
共有
|
||||
{accessGroup.expand ? accessGroup.expand.access.length : 0}
|
||||
个部署授权配置
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="min-h-[180px]">
|
||||
{accessGroup.expand ? (
|
||||
<>
|
||||
{accessGroup.expand.access.slice(0, 3).map((access) => (
|
||||
<div key={access.id} className="flex flex-col mb-3">
|
||||
<div className="flex items-center">
|
||||
<div className="">
|
||||
<img
|
||||
src={getProviderInfo(access.configType)![1]}
|
||||
alt="provider"
|
||||
className="w-8 h-8"
|
||||
></img>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<div className="text-sm font-semibold text-gray-700 dark:text-gray-200">
|
||||
{access.name}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{getProviderInfo(access.configType)![0]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex text-gray-700 dark:text-gray-200 items-center">
|
||||
<div>
|
||||
<Group size={40} />
|
||||
</div>
|
||||
<div className="ml-2">
|
||||
暂无部署授权配置,请添加后开始使用吧
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-end w-full">
|
||||
<Show
|
||||
when={
|
||||
accessGroup.expand &&
|
||||
accessGroup.expand.access.length > 0
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={"link"}
|
||||
onClick={() => {
|
||||
navigate(`/access?accessGroupId=${accessGroup.id}`);
|
||||
}}
|
||||
>
|
||||
所有授权
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show
|
||||
when={
|
||||
!accessGroup.expand ||
|
||||
accessGroup.expand.access.length == 0
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Button size="sm" onClick={handleAddAccess}>
|
||||
新增授权
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div className="ml-3">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant={"destructive"} size={"sm"}>
|
||||
删除
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="dark:text-gray-200">
|
||||
删除组
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
确定要删除部署授权组吗?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="dark:text-gray-200">
|
||||
取消
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
handleRemoveClick(
|
||||
accessGroup.id ? accessGroup.id : ""
|
||||
);
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccessGroups;
|
|
@ -10,7 +10,6 @@ import LoginLayout from "./pages/LoginLayout";
|
|||
import Password from "./pages/setting/Password";
|
||||
import SettingLayout from "./pages/SettingLayout";
|
||||
import Dashboard from "./pages/dashboard/Dashboard";
|
||||
import AccessGroups from "./pages/access_groups/AccessGroups";
|
||||
|
||||
export const router = createHashRouter([
|
||||
{
|
||||
|
@ -33,10 +32,6 @@ export const router = createHashRouter([
|
|||
path: "/access",
|
||||
element: <Access />,
|
||||
},
|
||||
{
|
||||
path: "/access_groups",
|
||||
element: <AccessGroups />,
|
||||
},
|
||||
{
|
||||
path: "/history",
|
||||
element: <History />,
|
||||
|
|
Loading…
Reference in New Issue