diff --git a/ui/package-lock.json b/ui/package-lock.json
index f4fd3895..e2b71062 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -19,7 +19,6 @@
         "@radix-ui/react-radio-group": "^1.2.0",
         "@radix-ui/react-scroll-area": "^1.1.0",
         "@radix-ui/react-select": "^2.1.1",
-        "@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",
@@ -2559,28 +2558,6 @@
         }
       }
     },
-    "node_modules/@radix-ui/react-separator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmmirror.com/@radix-ui/react-separator/-/react-separator-1.1.0.tgz",
-      "integrity": "sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==",
-      "dependencies": {
-        "@radix-ui/react-primitive": "2.0.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-slot": {
       "version": "1.1.0",
       "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
diff --git a/ui/package.json b/ui/package.json
index a08f888e..a7963f08 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -21,7 +21,6 @@
     "@radix-ui/react-radio-group": "^1.2.0",
     "@radix-ui/react-scroll-area": "^1.1.0",
     "@radix-ui/react-select": "^2.1.1",
-    "@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",
diff --git a/ui/src/components/access/AccessProviderSelect.tsx b/ui/src/components/access/AccessProviderSelect.tsx
deleted file mode 100644
index a7186ea9..00000000
--- a/ui/src/components/access/AccessProviderSelect.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { Avatar, Select, Space, Typography, type SelectProps } from "antd";
-
-import { accessProvidersMap } from "@/domain/access";
-
-export type AccessProviderSelectProps = Omit<SelectProps, "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"> & {
-  className?: string;
-};
-
-const AccessProviderSelect = React.memo((props: AccessProviderSelectProps) => {
-  const { t } = useTranslation();
-
-  const options = Array.from(accessProvidersMap.values()).map((item) => ({
-    key: item.type,
-    value: item.type,
-    label: t(item.name),
-  }));
-
-  return (
-    <Select
-      {...props}
-      labelRender={({ label, value }) => {
-        if (label) {
-          return (
-            <Space className="max-w-full truncate" align="center" size={4}>
-              <Avatar src={accessProvidersMap.get(String(value))?.icon} size="small" />
-              {label}
-            </Space>
-          );
-        }
-
-        return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>;
-      }}
-      options={options}
-      optionFilterProp={undefined}
-      optionLabelProp={undefined}
-      optionRender={(option) => (
-        <Space className="max-w-full truncate" align="center" size={4}>
-          <Avatar src={accessProvidersMap.get(option.data.value)?.icon} size="small" />
-          <Typography.Text ellipsis>{t(accessProvidersMap.get(option.data.value)?.name ?? "")}</Typography.Text>
-        </Space>
-      )}
-    />
-  );
-});
-
-export default AccessProviderSelect;
diff --git a/ui/src/components/access/AccessTypeSelect.tsx b/ui/src/components/access/AccessTypeSelect.tsx
new file mode 100644
index 00000000..711d1b22
--- /dev/null
+++ b/ui/src/components/access/AccessTypeSelect.tsx
@@ -0,0 +1,66 @@
+import { memo } from "react";
+import { useTranslation } from "react-i18next";
+import { Avatar, Select, Space, Tag, Typography, type SelectProps } from "antd";
+
+import { accessProvidersMap } from "@/domain/access";
+
+export type AccessTypeSelectProps = Omit<SelectProps, "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender">;
+
+const AccessTypeSelect = memo((props: AccessTypeSelectProps) => {
+  const { t } = useTranslation();
+
+  const options = Array.from(accessProvidersMap.values()).map((item) => ({
+    key: item.type,
+    value: item.type,
+    label: t(item.name),
+  }));
+
+  return (
+    <Select
+      {...props}
+      labelRender={({ label, value }) => {
+        if (label) {
+          return (
+            <Space className="max-w-full truncate" size={4}>
+              <Avatar src={accessProvidersMap.get(String(value))?.icon} size="small" />
+              {label}
+            </Space>
+          );
+        }
+
+        return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>;
+      }}
+      options={options}
+      optionFilterProp={undefined}
+      optionLabelProp={undefined}
+      optionRender={(option) => (
+        <div className="flex items-center justify-between gap-4 max-w-full overflow-hidden">
+          <Space className="flex-grow max-w-full truncate" size={4}>
+            <Avatar src={accessProvidersMap.get(option.data.value)?.icon} size="small" />
+            <Typography.Text ellipsis>{t(accessProvidersMap.get(option.data.value)?.name ?? "")}</Typography.Text>
+          </Space>
+          <div>
+            {accessProvidersMap.get(option.data.value)?.usage === "apply" && (
+              <>
+                <Tag color="orange">{t("access.props.provider.usage.dns")}</Tag>
+              </>
+            )}
+            {accessProvidersMap.get(option.data.value)?.usage === "deploy" && (
+              <>
+                <Tag color="blue">{t("access.props.provider.usage.host")}</Tag>
+              </>
+            )}
+            {accessProvidersMap.get(option.data.value)?.usage === "all" && (
+              <>
+                <Tag color="orange">{t("access.props.provider.usage.dns")}</Tag>
+                <Tag color="blue">{t("access.props.provider.usage.host")}</Tag>
+              </>
+            )}
+          </div>
+        </div>
+      )}
+    />
+  );
+});
+
+export default AccessTypeSelect;
diff --git a/ui/src/components/certificate/CertificateDetail.tsx b/ui/src/components/certificate/CertificateDetail.tsx
index 05b492d3..684784cb 100644
--- a/ui/src/components/certificate/CertificateDetail.tsx
+++ b/ui/src/components/certificate/CertificateDetail.tsx
@@ -8,10 +8,12 @@ import { type CertificateModel } from "@/domain/certificate";
 import { saveFiles2Zip } from "@/utils/file";
 
 type CertificateDetailProps = {
+  className?: string;
+  style?: React.CSSProperties;
   data: CertificateModel;
 };
 
-const CertificateDetail = ({ data }: CertificateDetailProps) => {
+const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
   const { t } = useTranslation();
 
   const [messageApi, MessageContextHolder] = message.useMessage();
@@ -33,7 +35,7 @@ const CertificateDetail = ({ data }: CertificateDetailProps) => {
   };
 
   return (
-    <div>
+    <div {...props}>
       {MessageContextHolder}
 
       <Form layout="vertical">
diff --git a/ui/src/components/certificate/CertificateDetailDrawer.tsx b/ui/src/components/certificate/CertificateDetailDrawer.tsx
index 6aefd7d6..4108cbee 100644
--- a/ui/src/components/certificate/CertificateDetailDrawer.tsx
+++ b/ui/src/components/certificate/CertificateDetailDrawer.tsx
@@ -1,4 +1,5 @@
-import { useEffect, useState } from "react";
+import { cloneElement, useEffect, useMemo, useState } from "react";
+import { useControllableValue } from "ahooks";
 import { Drawer } from "antd";
 
 import { type CertificateModel } from "@/domain/certificate";
@@ -7,19 +8,44 @@ import CertificateDetail from "./CertificateDetail";
 type CertificateDetailDrawerProps = {
   data?: CertificateModel;
   open?: boolean;
-  onClose?: () => void;
+  trigger?: React.ReactElement;
+  onOpenChange?: (open: boolean) => void;
 };
 
-const CertificateDetailDrawer = ({ data, open, onClose }: CertificateDetailDrawerProps) => {
+const CertificateDetailDrawer = ({ data, trigger, ...props }: CertificateDetailDrawerProps) => {
+  const [open, setOpen] = useControllableValue<boolean>(props, {
+    valuePropName: "open",
+    defaultValuePropName: "defaultOpen",
+    trigger: "onOpenChange",
+  });
+
   const [loading, setLoading] = useState(true);
   useEffect(() => {
     setLoading(data == null);
   }, [data]);
 
+  const triggerDom = useMemo(() => {
+    if (!trigger) {
+      return null;
+    }
+
+    return cloneElement(trigger, {
+      ...trigger.props,
+      onClick: () => {
+        setOpen(true);
+        trigger.props?.onClick?.();
+      },
+    });
+  }, [trigger, setOpen]);
+
   return (
-    <Drawer closable destroyOnClose open={open} loading={loading} placement="right" width={480} onClose={onClose}>
-      {data ? <CertificateDetail data={data} /> : <></>}
-    </Drawer>
+    <>
+      {triggerDom}
+
+      <Drawer closable destroyOnClose open={open} loading={loading} placement="right" width={480} onClose={() => setOpen(false)}>
+        {data ? <CertificateDetail data={data} /> : <></>}
+      </Drawer>
+    </>
   );
 };
 
diff --git a/ui/src/components/certimate/AccessEditDialog.tsx b/ui/src/components/certimate/AccessEditDialog.tsx
index c4ac874d..36de6fce 100644
--- a/ui/src/components/certimate/AccessEditDialog.tsx
+++ b/ui/src/components/certimate/AccessEditDialog.tsx
@@ -5,7 +5,8 @@ import { cn } from "@/components/ui/utils";
 import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
 import { Label } from "@/components/ui/label";
 import { ScrollArea } from "@/components/ui/scroll-area";
-import AccessProviderSelect from "@/components/access/AccessProviderSelect";
+import AccessEditForm from "@/components/access/AccessEditForm";
+import AccessTypeSelect from "@/components/access/AccessTypeSelect";
 import AccessAliyunForm from "./AccessAliyunForm";
 import AccessTencentForm from "./AccessTencentForm";
 import AccessHuaweiCloudForm from "./AccessHuaweicloudForm";
@@ -281,10 +282,11 @@ const AccessEditDialog = ({ trigger, op, data, className, outConfigType }: Acces
           </DialogTitle>
         </DialogHeader>
         <ScrollArea className="max-h-[80vh]">
+          <AccessEditForm data={data} />
           <div className="container py-3">
             <div>
               <Label>{t("access.authorization.form.type.label")}</Label>
-              <AccessProviderSelect
+              <AccessTypeSelect
                 className="w-full mt-3"
                 placeholder={t("access.authorization.form.type.placeholder")}
                 value={configType}
diff --git a/ui/src/components/ui/alert.tsx b/ui/src/components/ui/alert.tsx
deleted file mode 100644
index e5e48af8..00000000
--- a/ui/src/components/ui/alert.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as React from "react";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "./utils";
-
-const alertVariants = cva(
-  "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
-      },
-    },
-    defaultVariants: {
-      variant: "default",
-    },
-  }
-);
-
-const Alert = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>>(
-  ({ className, variant, ...props }, ref) => <div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
-);
-Alert.displayName = "Alert";
-
-const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(({ className, ...props }, ref) => (
-  <h5 ref={ref} className={cn("mb-1 font-medium leading-none tracking-tight", className)} {...props} />
-));
-AlertTitle.displayName = "AlertTitle";
-
-const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(({ className, ...props }, ref) => (
-  <div ref={ref} className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} />
-));
-AlertDescription.displayName = "AlertDescription";
-
-export { Alert, AlertTitle, AlertDescription };
diff --git a/ui/src/components/ui/separator.tsx b/ui/src/components/ui/separator.tsx
deleted file mode 100644
index 57070755..00000000
--- a/ui/src/components/ui/separator.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import * as React from "react";
-import * as SeparatorPrimitive from "@radix-ui/react-separator";
-
-import { cn } from "./utils";
-
-const Separator = React.forwardRef<React.ElementRef<typeof SeparatorPrimitive.Root>, React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>>(
-  ({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
-    <SeparatorPrimitive.Root
-      ref={ref}
-      decorative={decorative}
-      orientation={orientation}
-      className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className)}
-      {...props}
-    />
-  )
-);
-Separator.displayName = SeparatorPrimitive.Root.displayName;
-
-export { Separator };
diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json
index e36fba19..8fa38ae0 100644
--- a/ui/src/i18n/locales/en/nls.access.json
+++ b/ui/src/i18n/locales/en/nls.access.json
@@ -11,15 +11,17 @@
 
   "access.props.name": "Name",
   "access.props.provider": "Provider",
+  "access.props.provider.usage.dns": "DNS Provider",
+  "access.props.provider.usage.host": "Hos Provider",
   "access.props.created_at": "Created At",
   "access.props.updated_at": "Updated At",
 
+  "access.authorization.form.name.label": "Name",
+  "access.authorization.form.name.placeholder": "Please enter authorization name",
   "access.authorization.form.type.label": "Provider",
   "access.authorization.form.type.placeholder": "Please select a provider",
   "access.authorization.form.type.search.notfound": "Provider not found",
   "access.authorization.form.type.list": "Authorization List",
-  "access.authorization.form.name.label": "Name",
-  "access.authorization.form.name.placeholder": "Please enter authorization name",
   "access.authorization.form.config.label": "Configuration Type",
   "access.authorization.form.region.label": "Region",
   "access.authorization.form.region.placeholder": "Please enter Region",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index 3c27acaf..068f80a1 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -11,6 +11,8 @@
 
   "access.props.name": "名称",
   "access.props.provider": "服务商",
+  "access.props.provider.usage.dns": "DNS 服务商",
+  "access.props.provider.usage.host": "主机服务商",
   "access.props.created_at": "创建时间",
   "access.props.updated_at": "更新时间",
 
diff --git a/ui/src/pages/ConsoleLayout.tsx b/ui/src/pages/ConsoleLayout.tsx
index c21ab1bf..9929aa5c 100644
--- a/ui/src/pages/ConsoleLayout.tsx
+++ b/ui/src/pages/ConsoleLayout.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import { memo, useEffect, useState } from "react";
 import { Link, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom";
 import { useTranslation } from "react-i18next";
 import { Button, Drawer, Dropdown, Layout, Menu, Tooltip, theme, type ButtonProps, type MenuProps } from "antd";
@@ -109,7 +109,7 @@ const ConsoleLayout = () => {
   );
 };
 
-const SiderMenu = React.memo(({ onSelect }: { onSelect?: (key: string) => void }) => {
+const SiderMenu = memo(({ onSelect }: { onSelect?: (key: string) => void }) => {
   const location = useLocation();
   const navigate = useNavigate();
 
@@ -179,7 +179,7 @@ const SiderMenu = React.memo(({ onSelect }: { onSelect?: (key: string) => void }
   );
 });
 
-const ThemeToggleButton = React.memo(({ size }: { size?: ButtonProps["size"] }) => {
+const ThemeToggleButton = memo(({ size }: { size?: ButtonProps["size"] }) => {
   const { t } = useTranslation();
 
   const { theme, setThemeMode } = useTheme();
@@ -206,7 +206,7 @@ const ThemeToggleButton = React.memo(({ size }: { size?: ButtonProps["size"] })
   );
 });
 
-const LocaleToggleButton = React.memo(({ size }: { size?: ButtonProps["size"] }) => {
+const LocaleToggleButton = memo(({ size }: { size?: ButtonProps["size"] }) => {
   const { i18n } = useTranslation();
 
   const items: Required<MenuProps>["items"] = Object.keys(i18n.store.data).map((key) => {
diff --git a/ui/src/pages/accesses/AccessList.tsx b/ui/src/pages/accesses/AccessList.tsx
index df915bcb..e29f357b 100644
--- a/ui/src/pages/accesses/AccessList.tsx
+++ b/ui/src/pages/accesses/AccessList.tsx
@@ -40,7 +40,7 @@ const AccessList = () => {
       ellipsis: true,
       render: (_, record) => {
         return (
-          <Space className="max-w-full truncate" align="center" size={4}>
+          <Space className="max-w-full truncate" size={4}>
             <Avatar src={accessProvidersMap.get(record.configType)?.icon} size="small" />
             <Typography.Text ellipsis>{t(accessProvidersMap.get(record.configType)?.name ?? "")}</Typography.Text>
           </Space>
diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx
index aa744fff..5e533675 100644
--- a/ui/src/pages/certificates/CertificateList.tsx
+++ b/ui/src/pages/certificates/CertificateList.tsx
@@ -152,15 +152,14 @@ const CertificateList = () => {
       width: 120,
       render: (_, record) => (
         <Space size={0}>
-          <Tooltip title={t("certificate.action.view")}>
-            <Button
-              type="link"
-              icon={<EyeIcon size={16} />}
-              onClick={() => {
-                handleViewClick(record);
-              }}
-            />
-          </Tooltip>
+          <CertificateDetailDrawer
+            data={record}
+            trigger={
+              <Tooltip title={t("certificate.action.view")}>
+                <Button type="link" icon={<EyeIcon size={16} />} />
+              </Tooltip>
+            }
+          />
         </Space>
       ),
     },
@@ -177,10 +176,6 @@ const CertificateList = () => {
   const [page, setPage] = useState<number>(() => parseInt(+searchParams.get("page")! + "") || 1);
   const [pageSize, setPageSize] = useState<number>(() => parseInt(+searchParams.get("perPage")! + "") || 10);
 
-  const [currentRecord, setCurrentRecord] = useState<CertificateModel>();
-
-  const [drawerOpen, setDrawerOpen] = useState(false);
-
   const fetchTableData = useCallback(async () => {
     if (loading) return;
     setLoading(true);
@@ -210,11 +205,6 @@ const CertificateList = () => {
     fetchTableData();
   }, [fetchTableData]);
 
-  const handleViewClick = (certificate: CertificateModel) => {
-    setDrawerOpen(true);
-    setCurrentRecord(certificate);
-  };
-
   return (
     <>
       {NotificationContextHolder}
@@ -244,15 +234,6 @@ const CertificateList = () => {
         rowKey={(record: CertificateModel) => record.id}
         scroll={{ x: "max(100%, 960px)" }}
       />
-
-      <CertificateDetailDrawer
-        data={currentRecord}
-        open={drawerOpen}
-        onClose={() => {
-          setDrawerOpen(false);
-          setCurrentRecord(undefined);
-        }}
-      />
     </>
   );
 };
diff --git a/ui/src/pages/login/Login.tsx b/ui/src/pages/login/Login.tsx
index 8ae9b371..ce316109 100644
--- a/ui/src/pages/login/Login.tsx
+++ b/ui/src/pages/login/Login.tsx
@@ -19,7 +19,7 @@ const Login = () => {
     password: z.string().min(10, t("login.password.errmsg.invalid")),
   });
   const formRule = createSchemaFieldRule(formSchema);
-  const [form] = Form.useForm();
+  const [form] = Form.useForm<z.infer<typeof formSchema>>();
 
   const [isPending, setIsPending] = useState(false);