mirror of https://github.com/usual2970/certimate
				
				
				
			refactor(ui): clean code
							parent
							
								
									b5739c663d
								
							
						
					
					
						commit
						70e6920288
					
				| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 };
 | 
			
		||||
| 
						 | 
				
			
			@ -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 };
 | 
			
		||||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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": "更新时间",
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue