mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 13:39:53 +00:00
Merge authorization group into authorization management
This commit is contained in:
parent
500fce6180
commit
fa85580e35
File diff suppressed because one or more lines are too long
1
ui/dist/assets/index-ChWRjRip.css
vendored
Normal file
1
ui/dist/assets/index-ChWRjRip.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
ui/dist/assets/index-DXJTf3ck.css
vendored
1
ui/dist/assets/index-DXJTf3ck.css
vendored
File diff suppressed because one or more lines are too long
4
ui/dist/index.html
vendored
4
ui/dist/index.html
vendored
@ -5,8 +5,8 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
||||||
<script type="module" crossorigin src="/assets/index-pPAQ4idS.js"></script>
|
<script type="module" crossorigin src="/assets/index-CDJ2jqew.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DXJTf3ck.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-ChWRjRip.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-background">
|
<body class="bg-background">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
30
ui/package-lock.json
generated
30
ui/package-lock.json
generated
@ -20,6 +20,7 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.0",
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-switch": "^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-toast": "^1.2.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"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": {
|
"node_modules/@radix-ui/react-toast": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.2.1.tgz",
|
"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-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-switch": "^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-toast": "^1.2.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
217
ui/src/components/certimate/AccessGroupList.tsx
Normal file
217
ui/src/components/certimate/AccessGroupList.tsx
Normal file
@ -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;
|
53
ui/src/components/ui/tabs.tsx
Normal file
53
ui/src/components/ui/tabs.tsx
Normal file
@ -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,
|
BookOpen,
|
||||||
CircleUser,
|
CircleUser,
|
||||||
Earth,
|
Earth,
|
||||||
Group,
|
|
||||||
History,
|
History,
|
||||||
Home,
|
Home,
|
||||||
Menu,
|
Menu,
|
||||||
@ -101,17 +101,6 @@ export default function Dashboard() {
|
|||||||
授权管理
|
授权管理
|
||||||
</Link>
|
</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
|
<Link
|
||||||
to="/history"
|
to="/history"
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -180,17 +169,6 @@ export default function Dashboard() {
|
|||||||
授权管理
|
授权管理
|
||||||
</Link>
|
</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
|
<Link
|
||||||
to="/history"
|
to="/history"
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -250,7 +228,7 @@ export default function Dashboard() {
|
|||||||
href="https://github.com/usual2970/certimate/releases"
|
href="https://github.com/usual2970/certimate/releases"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Certimate v0.1.6
|
Certimate v0.1.7
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
import { AccessEdit } from "@/components/certimate/AccessEdit";
|
||||||
|
import AccessGroupList from "@/components/certimate/AccessGroupList";
|
||||||
import XPagination from "@/components/certimate/XPagination";
|
import XPagination from "@/components/certimate/XPagination";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Access as AccessType, accessTypeMap } from "@/domain/access";
|
import { Access as AccessType, accessTypeMap } from "@/domain/access";
|
||||||
import { convertZulu2Beijing } from "@/lib/time";
|
import { convertZulu2Beijing } from "@/lib/time";
|
||||||
import { useConfig } from "@/providers/config";
|
import { useConfig } from "@/providers/config";
|
||||||
@ -23,6 +25,8 @@ const Access = () => {
|
|||||||
const page = query.get("page");
|
const page = query.get("page");
|
||||||
const pageNumber = page ? Number(page) : 1;
|
const pageNumber = page ? Number(page) : 1;
|
||||||
|
|
||||||
|
const tab = query.get("tab");
|
||||||
|
|
||||||
const accessGroupId = query.get("accessGroupId");
|
const accessGroupId = query.get("accessGroupId");
|
||||||
|
|
||||||
const startIndex = (pageNumber - 1) * perPage;
|
const startIndex = (pageNumber - 1) * perPage;
|
||||||
@ -33,100 +37,137 @@ const Access = () => {
|
|||||||
deleteAccess(rs.id);
|
deleteAccess(rs.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTabItemClick = (tab: string) => {
|
||||||
|
query.set("tab", tab);
|
||||||
|
navigate({ search: query.toString() });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="text-muted-foreground">授权管理</div>
|
<div className="text-muted-foreground">授权管理</div>
|
||||||
<AccessEdit trigger={<Button>添加授权</Button>} op="add" />
|
<AccessEdit trigger={<Button>添加授权</Button>} op="add" />
|
||||||
</div>
|
</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">
|
<Tabs
|
||||||
请添加授权开始部署证书吧。
|
defaultValue={tab ? tab : "access"}
|
||||||
</div>
|
value={tab ? tab : "access"}
|
||||||
<AccessEdit
|
className="w-full mt-5"
|
||||||
trigger={<Button>添加授权</Button>}
|
>
|
||||||
op="add"
|
<TabsList className="space-x-5 px-3">
|
||||||
className="mt-3"
|
<TabsTrigger
|
||||||
/>
|
value="access"
|
||||||
</div>
|
onClick={() => {
|
||||||
) : (
|
handleTabItemClick("access");
|
||||||
<>
|
|
||||||
<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() });
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</>
|
授权管理
|
||||||
)}
|
</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>
|
</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 Password from "./pages/setting/Password";
|
||||||
import SettingLayout from "./pages/SettingLayout";
|
import SettingLayout from "./pages/SettingLayout";
|
||||||
import Dashboard from "./pages/dashboard/Dashboard";
|
import Dashboard from "./pages/dashboard/Dashboard";
|
||||||
import AccessGroups from "./pages/access_groups/AccessGroups";
|
|
||||||
|
|
||||||
export const router = createHashRouter([
|
export const router = createHashRouter([
|
||||||
{
|
{
|
||||||
@ -33,10 +32,6 @@ export const router = createHashRouter([
|
|||||||
path: "/access",
|
path: "/access",
|
||||||
element: <Access />,
|
element: <Access />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/access_groups",
|
|
||||||
element: <AccessGroups />,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/history",
|
path: "/history",
|
||||||
element: <History />,
|
element: <History />,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user