refactor(ui): clean code
This commit is contained in:
@@ -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;
|
||||
66
ui/src/components/access/AccessTypeSelect.tsx
Normal file
66
ui/src/components/access/AccessTypeSelect.tsx
Normal file
@@ -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 };
|
||||
Reference in New Issue
Block a user