feat: separate access providers and dns providers

This commit is contained in:
Fu Diwei
2025-01-10 22:41:39 +08:00
parent 8ed2b2475c
commit a0c08e841d
14 changed files with 307 additions and 42 deletions

View File

@@ -0,0 +1,67 @@
import { memo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Avatar, Card, Col, Empty, Flex, Input, Row, Typography } from "antd";
import Show from "@/components/Show";
import { applyDNSProvidersMap } from "@/domain/provider";
export type ApplyDNSProviderPickerProps = {
className?: string;
style?: React.CSSProperties;
placeholder?: string;
onSelect?: (value: string) => void;
};
const ApplyDNSProviderPicker = ({ className, style, placeholder, onSelect }: ApplyDNSProviderPickerProps) => {
const { t } = useTranslation();
const [keyword, setKeyword] = useState<string>();
const providers = Array.from(applyDNSProvidersMap.values());
const filteredProviders = providers.filter((provider) => {
if (keyword) {
const value = keyword.toLowerCase();
return provider.type.toLowerCase().includes(value) || provider.name.toLowerCase().includes(value);
}
return true;
});
const handleProviderTypeSelect = (value: string) => {
onSelect?.(value);
};
return (
<div className={className} style={style}>
<Input.Search placeholder={placeholder} onChange={(e) => setKeyword(e.target.value.trim())} />
<div className="mt-4">
<Show when={filteredProviders.length > 0} fallback={<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}>
<Row gutter={[16, 16]}>
{filteredProviders.map((provider, index) => {
return (
<Col key={index} span={12}>
<Card
className="h-16 w-full overflow-hidden shadow-sm"
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
hoverable
onClick={() => {
handleProviderTypeSelect(provider.type);
}}
>
<Flex className="size-full overflow-hidden" align="center" gap={8}>
<Avatar src={provider.icon} size="small" />
<Typography.Text className="line-clamp-2">{t(provider.name)}</Typography.Text>
</Flex>
</Card>
</Col>
);
})}
</Row>
</Show>
</div>
</div>
);
};
export default memo(ApplyDNSProviderPicker);

View File

@@ -0,0 +1,51 @@
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
import { applyDNSProvidersMap } from "@/domain/provider";
export type ApplyDNSProviderSelectProps = Omit<
SelectProps,
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
>;
const ApplyDNSProviderSelect = (props: ApplyDNSProviderSelectProps) => {
const { t } = useTranslation();
const options = Array.from(applyDNSProvidersMap.values()).map((item) => ({
key: item.type,
value: item.type,
label: t(item.name),
}));
const renderOption = (key: string) => {
const provider = applyDNSProvidersMap.get(key);
return (
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
<Avatar src={provider?.icon} size="small" />
<Typography.Text className="leading-loose" ellipsis>
{t(provider?.name ?? "")}
</Typography.Text>
</Space>
);
};
return (
<Select
{...props}
labelRender={({ label, value }) => {
if (label) {
return renderOption(value as string);
}
return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>;
}}
options={options}
optionFilterProp={undefined}
optionLabelProp={undefined}
optionRender={(option) => renderOption(option.data.value)}
/>
);
};
export default memo(ApplyDNSProviderSelect);

View File

@@ -8,10 +8,11 @@ import { deployProvidersMap } from "@/domain/provider";
export type DeployProviderPickerProps = {
className?: string;
style?: React.CSSProperties;
placeholder?: string;
onSelect?: (value: string) => void;
};
const DeployProviderPicker = ({ className, style, onSelect }: DeployProviderPickerProps) => {
const DeployProviderPicker = ({ className, style, placeholder, onSelect }: DeployProviderPickerProps) => {
const { t } = useTranslation();
const [keyword, setKeyword] = useState<string>();
@@ -32,7 +33,7 @@ const DeployProviderPicker = ({ className, style, onSelect }: DeployProviderPick
return (
<div className={className} style={style}>
<Input.Search placeholder={t("workflow_node.deploy.search.provider.placeholder")} onChange={(e) => setKeyword(e.target.value.trim())} />
<Input.Search placeholder={placeholder} onChange={(e) => setKeyword(e.target.value.trim())} />
<div className="mt-4">
<Show when={filteredProviders.length > 0} fallback={<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}>