mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
chore(deps): migrate to tailwindcss v4
This commit is contained in:
+4
-2
@@ -15,7 +15,7 @@ module.exports = {
|
||||
"plugin:import/typescript",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:tailwindcss/recommended",
|
||||
"plugin:better-tailwindcss/recommended-warn",
|
||||
],
|
||||
ignorePatterns: ["dist", ".eslintrc.cjs"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
@@ -88,6 +88,9 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
],
|
||||
"better-tailwindcss": {
|
||||
entryPoint: "src/global.css",
|
||||
},
|
||||
"react-refresh/only-export-components": [
|
||||
"warn",
|
||||
{
|
||||
@@ -100,7 +103,6 @@ module.exports = {
|
||||
ignoreDeclarationSort: true,
|
||||
},
|
||||
],
|
||||
"tailwindcss/no-custom-classname": "off",
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
|
||||
Generated
+647
-834
File diff suppressed because it is too large
Load Diff
+5
-6
@@ -37,11 +37,13 @@
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-router-dom": "^7.6.2",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"zod": "^3.25.67",
|
||||
"zustand": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/node": "^22.15.17",
|
||||
@@ -52,20 +54,17 @@
|
||||
"@typescript-eslint/parser": "^8.34.1",
|
||||
"@vitejs/plugin-legacy": "^6.1.1",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-import-resolver-typescript": "^3.8.3",
|
||||
"eslint-plugin-better-tailwindcss": "^3.4.3",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-prettier": "^5.5.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"eslint-plugin-tailwindcss": "^3.18.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -95,7 +95,7 @@ const AccessFormEmailConfig = ({ form: formInst, formName, disabled, initialValu
|
||||
|
||||
<div className="w-2/5">
|
||||
<Form.Item name="smtpPort" label={t("access.form.email_smtp_port.label")} rules={[formRule]}>
|
||||
<InputNumber className="w-full" placeholder={t("access.form.email_smtp_port.placeholder")} min={1} max={65535} />
|
||||
<InputNumber style={{ width: "100%" }} placeholder={t("access.form.email_smtp_port.placeholder")} min={1} max={65535} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,7 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
||||
|
||||
<div className="w-1/3">
|
||||
<Form.Item name="port" label={t("access.form.ssh_port.label")} rules={[formRule]}>
|
||||
<InputNumber className="w-full" min={1} max={65535} placeholder={t("access.form.ssh_port.placeholder")} />
|
||||
<InputNumber style={{ width: "100%" }} min={1} max={65535} placeholder={t("access.form.ssh_port.placeholder")} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
||||
</div>
|
||||
<div className="w-1/3">
|
||||
<Form.Item name={[field.name, "port"]} label={t("access.form.ssh_port.label")} rules={[formRule]}>
|
||||
<InputNumber className="w-full" placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
||||
<InputNumber style={{ width: "100%" }} placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -304,7 +304,7 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
</Form.Item>
|
||||
|
||||
<Show when={!usage || usage === "deployment"}>
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
@@ -335,7 +335,7 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa
|
||||
</Show>
|
||||
|
||||
<Show when={!usage || usage === "notification"}>
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Avatar, Select, type SelectProps, Space, Typography, theme } from "antd";
|
||||
import { Avatar, Select, type SelectProps, Typography, theme } from "antd";
|
||||
|
||||
import { type AccessModel } from "@/domain/access";
|
||||
import { accessProvidersMap } from "@/domain/provider";
|
||||
@@ -38,23 +38,19 @@ const AccessSelect = ({ filter, ...props }: AccessTypeSelectProps) => {
|
||||
const access = accesses.find((e) => e.id === key);
|
||||
if (!access) {
|
||||
return (
|
||||
<Space className="max-w-full grow truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{key}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text ellipsis>{key}</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const provider = accessProvidersMap.get(access.provider);
|
||||
return (
|
||||
<Space className="max-w-full grow truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{access.name}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text ellipsis>{access.name}</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ const NotifyChannelEditFormEmailFields = () => {
|
||||
|
||||
<div className="w-2/5">
|
||||
<Form.Item name="smtpPort" label={t("settings.notification.channel.form.email_smtp_port.label")} rules={[formRule]}>
|
||||
<InputNumber className="w-full" placeholder={t("settings.notification.channel.form.email_smtp_port.placeholder")} min={1} max={65535} />
|
||||
<InputNumber style={{ width: "100%" }} placeholder={t("settings.notification.channel.form.email_smtp_port.placeholder")} min={1} max={65535} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ const ACMEDns01ProviderPicker = ({ className, style, autoFocus, filter, placehol
|
||||
return (
|
||||
<Col key={index} xs={24} md={12} span={12}>
|
||||
<Card
|
||||
className="h-16 w-full overflow-hidden shadow-sm"
|
||||
className="h-16 w-full overflow-hidden shadow-xs"
|
||||
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
|
||||
hoverable
|
||||
onClick={() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Typography, theme } from "antd";
|
||||
import { Avatar, Select, type SelectProps, Typography, theme } from "antd";
|
||||
|
||||
import { type ACMEDns01Provider, acmeDns01ProvidersMap } from "@/domain/provider";
|
||||
|
||||
@@ -16,29 +16,30 @@ const ACMEDns01ProviderSelect = ({ filter, ...props }: ACMEDns01ProviderSelectPr
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: ACMEDns01Provider }>>([]);
|
||||
useEffect(() => {
|
||||
const allItems = Array.from(acmeDns01ProvidersMap.values());
|
||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||
setOptions(
|
||||
filteredItems.map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
data: item,
|
||||
}))
|
||||
);
|
||||
const options = useMemo<Array<{ key: string; value: string; label: string; data: ACMEDns01Provider }>>(() => {
|
||||
return Array.from(acmeDns01ProvidersMap.values())
|
||||
.filter((provider) => {
|
||||
if (filter) {
|
||||
return filter(provider);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((provider) => ({
|
||||
key: provider.type,
|
||||
value: provider.type,
|
||||
label: t(provider.name),
|
||||
data: provider,
|
||||
}));
|
||||
}, [filter]);
|
||||
|
||||
const renderOption = (key: string) => {
|
||||
const provider = acmeDns01ProvidersMap.get(key);
|
||||
return (
|
||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{t(provider?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text ellipsis>{t(provider?.name ?? "")}</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ const AccessProviderPicker = ({ className, style, autoFocus, filter, placeholder
|
||||
return (
|
||||
<Col key={index} xs={24} md={12} span={8}>
|
||||
<Card
|
||||
className={mergeCls("h-20 w-full overflow-hidden shadow-sm", provider.builtin ? " cursor-not-allowed" : "")}
|
||||
className={mergeCls("h-20 w-full overflow-hidden shadow-xs", provider.builtin ? " cursor-not-allowed" : "")}
|
||||
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
|
||||
hoverable
|
||||
onClick={() => {
|
||||
@@ -91,7 +91,7 @@ const AccessProviderPicker = ({ className, style, autoFocus, filter, placeholder
|
||||
<Typography.Text className="mb-1 line-clamp-1" type={provider.builtin ? "secondary" : undefined}>
|
||||
{t(provider.name)}
|
||||
</Typography.Text>
|
||||
<div className="origin-left scale-[75%]">
|
||||
<div className="origin-left scale-75">
|
||||
<Show when={provider.builtin}>
|
||||
<Tag>{t("access.props.provider.builtin")}</Tag>
|
||||
</Show>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useMemo, useState } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Tag, Typography, theme } from "antd";
|
||||
import { Avatar, Select, type SelectProps, Tag, Typography, theme } from "antd";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { ACCESS_USAGES, type AccessProvider, type AccessUsageType, accessProvidersMap } from "@/domain/provider";
|
||||
@@ -18,19 +18,22 @@ const AccessProviderSelect = ({ filter, showOptionTags, ...props }: AccessProvid
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: AccessProvider }>>([]);
|
||||
useEffect(() => {
|
||||
const allItems = Array.from(accessProvidersMap.values());
|
||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||
setOptions(
|
||||
filteredItems.map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
disabled: item.builtin,
|
||||
data: item,
|
||||
}))
|
||||
);
|
||||
const options = useMemo<Array<{ key: string; value: string; label: string; data: AccessProvider }>>(() => {
|
||||
return Array.from(accessProvidersMap.values())
|
||||
.filter((provider) => {
|
||||
if (filter) {
|
||||
return filter(provider);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((provider) => ({
|
||||
key: provider.type,
|
||||
value: provider.type,
|
||||
label: t(provider.name),
|
||||
disabled: provider.builtin,
|
||||
data: provider,
|
||||
}));
|
||||
}, [filter]);
|
||||
|
||||
const showOptionTagForDNS = useMemo(() => {
|
||||
@@ -50,13 +53,13 @@ const AccessProviderSelect = ({ filter, showOptionTags, ...props }: AccessProvid
|
||||
const provider = accessProvidersMap.get(key) ?? ({ type: "", name: "", icon: "", usages: [] } as unknown as AccessProvider);
|
||||
return (
|
||||
<div className="flex max-w-full items-center justify-between gap-4 overflow-hidden">
|
||||
<Space className="max-w-full grow truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={provider.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" type={provider.builtin ? "secondary" : undefined} ellipsis>
|
||||
<Typography.Text type={provider.builtin ? "secondary" : undefined} ellipsis>
|
||||
{t(provider.name)}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<div className="origin-right scale-[75%]">
|
||||
</div>
|
||||
<div className="origin-right scale-75">
|
||||
<Show when={provider.builtin}>
|
||||
<Tag>{t("access.props.provider.builtin")}</Tag>
|
||||
</Show>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Typography, theme } from "antd";
|
||||
import { Avatar, Select, type SelectProps, Typography, theme } from "antd";
|
||||
|
||||
import { type CAProvider, caProvidersMap } from "@/domain/provider";
|
||||
|
||||
@@ -16,45 +16,49 @@ const CAProviderSelect = ({ filter, ...props }: CAProviderSelectProps) => {
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: CAProvider }>>([]);
|
||||
useEffect(() => {
|
||||
const allItems = Array.from(caProvidersMap.values());
|
||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||
setOptions([
|
||||
{
|
||||
key: "",
|
||||
value: "",
|
||||
label: t("provider.default_ca_provider.label"),
|
||||
data: {} as CAProvider,
|
||||
},
|
||||
...filteredItems.map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
data: item,
|
||||
})),
|
||||
]);
|
||||
const options = useMemo<Array<{ key: string; value: string; label: string; data: CAProvider }>>(() => {
|
||||
const temp = Array.from(caProvidersMap.values())
|
||||
.filter((provider) => {
|
||||
if (filter) {
|
||||
return filter(provider);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((provider) => ({
|
||||
key: provider.type as string,
|
||||
value: provider.type as string,
|
||||
label: t(provider.name),
|
||||
data: provider,
|
||||
}));
|
||||
|
||||
temp.unshift({
|
||||
key: "",
|
||||
value: "",
|
||||
label: t("provider.default_ca_provider.label"),
|
||||
data: {} as CAProvider,
|
||||
});
|
||||
|
||||
return temp;
|
||||
}, [filter]);
|
||||
|
||||
const renderOption = (key: string) => {
|
||||
if (key === "") {
|
||||
return (
|
||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||
<Typography.Text className="italic leading-loose" type="secondary" ellipsis italic>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Typography.Text className="italic" ellipsis italic>
|
||||
{t("provider.default_ca_provider.label")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const provider = caProvidersMap.get(key);
|
||||
return (
|
||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{t(provider?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text ellipsis>{t(provider?.name ?? "")}</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, filter, placeho
|
||||
return (
|
||||
<Col key={index} xs={24} md={12} span={12}>
|
||||
<Card
|
||||
className="h-16 w-full overflow-hidden shadow-sm"
|
||||
className="h-16 w-full overflow-hidden shadow-xs"
|
||||
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
|
||||
hoverable
|
||||
onClick={() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Typography, theme } from "antd";
|
||||
import { Avatar, Select, type SelectProps, Typography, theme } from "antd";
|
||||
|
||||
import { type DeploymentProvider, deploymentProvidersMap } from "@/domain/provider";
|
||||
|
||||
@@ -16,29 +16,30 @@ const DeploymentProviderSelect = ({ filter, ...props }: DeploymentProviderSelect
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: DeploymentProvider }>>([]);
|
||||
useEffect(() => {
|
||||
const allItems = Array.from(deploymentProvidersMap.values());
|
||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||
setOptions(
|
||||
filteredItems.map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
data: item,
|
||||
}))
|
||||
);
|
||||
const options = useMemo<Array<{ key: string; value: string; label: string; data: DeploymentProvider }>>(() => {
|
||||
return Array.from(deploymentProvidersMap.values())
|
||||
.filter((provider) => {
|
||||
if (filter) {
|
||||
return filter(provider);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((provider) => ({
|
||||
key: provider.type,
|
||||
value: provider.type,
|
||||
label: t(provider.name),
|
||||
data: provider,
|
||||
}));
|
||||
}, [filter]);
|
||||
|
||||
const renderOption = (key: string) => {
|
||||
const provider = deploymentProvidersMap.get(key);
|
||||
return (
|
||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{t(provider?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text ellipsis>{t(provider?.name ?? "")}</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Typography, theme } from "antd";
|
||||
import { Avatar, Select, type SelectProps, Typography, theme } from "antd";
|
||||
|
||||
import { type NotificationProvider, notificationProvidersMap } from "@/domain/provider";
|
||||
|
||||
@@ -16,29 +16,30 @@ const NotificationProviderSelect = ({ filter, ...props }: NotificationProviderSe
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const [options, setOptions] = useState<Array<{ key: string; value: string; label: string; data: NotificationProvider }>>([]);
|
||||
useEffect(() => {
|
||||
const allItems = Array.from(notificationProvidersMap.values());
|
||||
const filteredItems = filter != null ? allItems.filter(filter) : allItems;
|
||||
setOptions(
|
||||
filteredItems.map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
data: item,
|
||||
}))
|
||||
);
|
||||
const options = useMemo<Array<{ key: string; value: string; label: string; data: NotificationProvider }>>(() => {
|
||||
return Array.from(notificationProvidersMap.values())
|
||||
.filter((provider) => {
|
||||
if (filter) {
|
||||
return filter(provider);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((provider) => ({
|
||||
key: provider.type,
|
||||
value: provider.type,
|
||||
label: t(provider.name),
|
||||
data: provider,
|
||||
}));
|
||||
}, [filter]);
|
||||
|
||||
const renderOption = (key: string) => {
|
||||
const provider = notificationProvidersMap.get(key);
|
||||
return (
|
||||
<Space className="max-w-full grow overflow-hidden truncate" size={4}>
|
||||
<div className="flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{t(provider?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<Typography.Text ellipsis>{t(provider?.name ?? "")}</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const WorkflowElementsContainer = ({ className, style, disabled }: WorkflowEleme
|
||||
return (
|
||||
<div className={mergeCls("relative size-full overflow-hidden", className)} style={style}>
|
||||
<div className="size-full overflow-auto">
|
||||
<div className="relative z-[1]">
|
||||
<div className="relative z-1">
|
||||
<div className="origin-center transition-transform duration-300" style={{ zoom: `${scale}` }}>
|
||||
<div className="p-4">
|
||||
<WorkflowElements disabled={disabled} />
|
||||
@@ -30,14 +30,16 @@ const WorkflowElementsContainer = ({ className, style, disabled }: WorkflowEleme
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="absolute bottom-4 right-6 z-[2] rounded-lg p-2 shadow-lg" styles={{ body: { padding: 0 } }}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button icon={<MinusOutlinedIcon />} disabled={scale <= MIN_SCALE} onClick={() => setScale((s) => Math.max(MIN_SCALE, s - STEP_SCALE))} />
|
||||
<Typography.Text className="min-w-[3em] text-center">{Math.round(scale * 100)}%</Typography.Text>
|
||||
<Button icon={<PlusOutlinedIcon />} disabled={scale >= MAX_SCALE} onClick={() => setScale((s) => Math.min(MAX_SCALE, s + STEP_SCALE))} />
|
||||
<Button icon={<ExpandOutlinedIcon />} onClick={() => setScale(1)} />
|
||||
</div>
|
||||
</Card>
|
||||
<div className="absolute bottom-4 right-6 z-2">
|
||||
<Card className="rounded-lg p-2 shadow-lg" styles={{ body: { padding: 0 } }}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button icon={<MinusOutlinedIcon />} disabled={scale <= MIN_SCALE} onClick={() => setScale((s) => Math.max(MIN_SCALE, s - STEP_SCALE))} />
|
||||
<Typography.Text className="min-w-12 text-center">{Math.round(scale * 100)}%</Typography.Text>
|
||||
<Button icon={<PlusOutlinedIcon />} disabled={scale >= MAX_SCALE} onClick={() => setScale((s) => Math.min(MAX_SCALE, s + STEP_SCALE))} />
|
||||
<Button icon={<ExpandOutlinedIcon />} onClick={() => setScale(1)} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -65,8 +65,8 @@ const AddNode = ({ node, disabled }: AddNodeProps) => {
|
||||
return (
|
||||
<div className="relative py-6 before:absolute before:left-1/2 before:top-0 before:h-full before:w-[2px] before:-translate-x-1/2 before:bg-stone-200 before:content-['']">
|
||||
<Dropdown menu={{ items: dropdownMenus }} trigger={["click"]}>
|
||||
<div className="relative z-[1] flex size-5 cursor-pointer items-center justify-center rounded-full bg-stone-400 hover:bg-stone-500">
|
||||
<PlusOutlinedIcon className="text-white" />
|
||||
<div className="relative z-1 flex size-5 cursor-pointer items-center justify-center rounded-full text-white bg-stone-400 hover:bg-stone-500">
|
||||
<PlusOutlinedIcon />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
@@ -318,7 +318,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
@@ -375,7 +375,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
</Divider>
|
||||
|
||||
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
@@ -404,7 +404,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null" hidden={!showCAProviderAccess}>
|
||||
<Form.Item hidden={!showCAProviderAccess} noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
|
||||
@@ -40,7 +40,7 @@ const BranchNode = ({ node, disabled }: BrandNodeProps) => {
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
className="absolute left-1/2 z-[1] -translate-x-1/2 -translate-y-1/2 text-xs"
|
||||
className="absolute left-1/2 z-1 -translate-x-1/2 -translate-y-1/2 text-xs"
|
||||
disabled={disabled}
|
||||
size="small"
|
||||
shape="round"
|
||||
@@ -55,38 +55,18 @@ const BranchNode = ({ node, disabled }: BrandNodeProps) => {
|
||||
{node.branches?.map((branch, index) => (
|
||||
<div
|
||||
key={branch.id}
|
||||
className="relative flex flex-col items-center before:absolute before:left-1/2 before:top-0 before:h-full before:w-[2px] before:-translate-x-1/2 before:bg-stone-200 before:content-['']"
|
||||
className="relative flex flex-col items-center before:absolute before:left-1/2 before:top-0 before:h-full before:w-[2px] before:-translate-x-1/2 before:bg-stone-200 before:content-['']"
|
||||
>
|
||||
{index == 0 && (
|
||||
<>
|
||||
<div
|
||||
className="absolute -left-px -top-1 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className="absolute -bottom-1 -left-px z-50 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div className="absolute -left-[1px] -top-1 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
<div className="absolute -bottom-1 -left-[1px] z-50 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
</>
|
||||
)}
|
||||
{node.branches && index == node.branches.length - 1 && (
|
||||
{index == node.branches?.length! - 1 && (
|
||||
<>
|
||||
<div
|
||||
className="absolute -right-px -top-1 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className="absolute -bottom-1 -right-px z-50 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div className="absolute -right-[1px] -top-1 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
<div className="absolute -bottom-1 -right-[1px] z-50 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
</>
|
||||
)}
|
||||
<div className="relative flex flex-col items-center">{renderBranch(branch, node.id, index)}</div>
|
||||
|
||||
@@ -64,24 +64,26 @@ const ConditionNode = ({ node, disabled, branchId, branchIndex }: ConditionNodeP
|
||||
}
|
||||
placement="rightTop"
|
||||
>
|
||||
<Card className="relative z-[1] mt-10 w-[256px] shadow-md" styles={{ body: { padding: 0 } }} hoverable onClick={() => setDrawerOpen(true)}>
|
||||
<div className="flex h-[48px] flex-col items-center justify-center truncate px-4 py-2">
|
||||
<div className="relative w-full overflow-hidden" onClick={(e) => e.stopPropagation()}>
|
||||
<SharedNode.Title
|
||||
className="focus:bg-background focus:text-foreground overflow-hidden outline-slate-200 focus:rounded-sm"
|
||||
node={node}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<div className="absolute right-0 top-1/2 -translate-y-1/2" onClick={() => setDrawerOpen(true)}>
|
||||
{node.config?.expression ? (
|
||||
<Button color="primary" icon={<FilterFilledIcon />} variant="link" />
|
||||
) : (
|
||||
<Button color="default" icon={<FilterOutlinedIcon />} variant="link" />
|
||||
)}
|
||||
<div className="relative w-[256px] z-1 mt-10">
|
||||
<Card className="shadow-md" styles={{ body: { padding: 0 } }} hoverable onClick={() => setDrawerOpen(true)}>
|
||||
<div className="flex h-[48px] flex-col items-center justify-center truncate px-4 py-2">
|
||||
<div className="relative w-full overflow-hidden" onClick={(e) => e.stopPropagation()}>
|
||||
<SharedNode.Title
|
||||
className="focus:bg-background focus:text-foreground overflow-hidden outline-slate-200 focus:rounded-xs"
|
||||
node={node}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<div className="absolute right-0 top-1/2 -translate-y-1/2" onClick={() => setDrawerOpen(true)}>
|
||||
{node.config?.expression ? (
|
||||
<Button color="primary" icon={<FilterFilledIcon />} variant="link" />
|
||||
) : (
|
||||
<Button color="default" icon={<FilterOutlinedIcon />} variant="link" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
<SharedNode.ConfigDrawer
|
||||
|
||||
@@ -269,11 +269,7 @@ const ConditionNodeConfigFormExpressionEditor = forwardRef<ConditionNodeConfigFo
|
||||
onValuesChange={handleFormChange}
|
||||
>
|
||||
<Show when={formModel.conditions?.length > 1}>
|
||||
<Form.Item
|
||||
className="mb-2"
|
||||
name="logicalOperator"
|
||||
rules={[{ required: true, message: t("workflow_node.condition.form.expression.logical_operator.errmsg") }]}
|
||||
>
|
||||
<Form.Item name="logicalOperator" rules={[{ required: true, message: t("workflow_node.condition.form.expression.logical_operator.errmsg") }]}>
|
||||
<Radio.Group block>
|
||||
<Radio.Button value="and">{t("workflow_node.condition.form.expression.logical_operator.option.and.label")}</Radio.Button>
|
||||
<Radio.Button value="or">{t("workflow_node.condition.form.expression.logical_operator.option.or.label")}</Radio.Button>
|
||||
|
||||
@@ -475,7 +475,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null" hidden={!showProviderAccess}>
|
||||
<Form.Item hidden={!showProviderAccess} noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
|
||||
@@ -69,7 +69,7 @@ const DeployNodeConfigFormBaotaWAFSiteConfig = ({
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="sitePort" label={t("workflow_node.deploy.form.baotawaf_site_port.label")} rules={[formRule]}>
|
||||
<InputNumber className="w-full" placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
||||
<InputNumber style={{ width: "100%" }} placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
||||
@@ -406,7 +406,7 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
@@ -442,7 +442,7 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
|
||||
@@ -431,7 +431,7 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
|
||||
</Form.Item>
|
||||
</Show>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
@@ -467,7 +467,7 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
|
||||
@@ -28,45 +28,23 @@ const ExecuteResultBranchNode = ({ node, disabled }: BrandNodeProps) => {
|
||||
<>
|
||||
<div
|
||||
className="relative flex gap-x-16 before:absolute before:inset-x-[128px] before:top-0 before:h-[2px] before:bg-stone-200 before:content-[''] after:absolute after:inset-x-[128px] after:bottom-0 after:h-[2px] after:bg-stone-200 after:content-['']"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
style={{ backgroundColor: themeToken.colorBgContainer }}
|
||||
>
|
||||
{node.branches?.map((branch, index) => (
|
||||
<div
|
||||
key={branch.id}
|
||||
className="relative flex flex-col items-center before:absolute before:left-1/2 before:top-0 before:h-full before:w-[2px] before:-translate-x-1/2 before:bg-stone-200 before:content-['']"
|
||||
className="relative flex flex-col items-center before:absolute before:left-1/2 before:top-0 before:h-full before:w-[2px] before:-translate-x-1/2 before:bg-stone-200 before:content-['']"
|
||||
>
|
||||
{index == 0 && (
|
||||
<>
|
||||
<div
|
||||
className="absolute -left-px -top-1 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className="absolute -bottom-1 -left-px z-50 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div className="absolute -left-[1px] -top-1 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
<div className="absolute -bottom-1 -left-[1px] z-50 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
</>
|
||||
)}
|
||||
{node.branches && index == node.branches.length - 1 && (
|
||||
<>
|
||||
<div
|
||||
className="absolute -right-px -top-1 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
className="absolute -bottom-1 -right-px z-50 h-2 w-1/2"
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
}}
|
||||
></div>
|
||||
<div className="absolute -right-[1px] -top-1 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
<div className="absolute -bottom-1 -right-[1px] z-50 h-2 w-1/2" style={{ backgroundColor: themeToken.colorBgContainer }}></div>
|
||||
</>
|
||||
)}
|
||||
<div className="relative flex flex-col items-center">{renderBranch(branch, node.id, index)}</div>
|
||||
|
||||
@@ -35,22 +35,24 @@ const ExecuteResultNode = ({ node, disabled, branchId, branchIndex }: ConditionN
|
||||
}
|
||||
placement="rightTop"
|
||||
>
|
||||
<Card className="relative z-[1] mt-10 w-[256px] shadow-md" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="flex h-[48px] flex-col items-center justify-center truncate px-4 py-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
{node.type === WorkflowNodeType.ExecuteSuccess ? (
|
||||
<CheckCircleOutlinedIcon style={{ color: themeToken.colorSuccess }} />
|
||||
) : (
|
||||
<CloseCircleOutlinedIcon style={{ color: themeToken.colorError }} />
|
||||
)}
|
||||
<SharedNode.Title
|
||||
className="focus:bg-background focus:text-foreground overflow-hidden outline-slate-200 focus:rounded-sm"
|
||||
node={node}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<div className="relative z-1 mt-10 w-[256px]">
|
||||
<Card className="shadow-md" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="flex h-[48px] flex-col items-center justify-center truncate px-4 py-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
{node.type === WorkflowNodeType.ExecuteSuccess ? (
|
||||
<CheckCircleOutlinedIcon style={{ color: themeToken.colorSuccess }} />
|
||||
) : (
|
||||
<CloseCircleOutlinedIcon style={{ color: themeToken.colorError }} />
|
||||
)}
|
||||
<SharedNode.Title
|
||||
className="focus:bg-background focus:text-foreground overflow-hidden outline-slate-200 focus:rounded-xs"
|
||||
node={node}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
<AddNode node={node} disabled={disabled} />
|
||||
|
||||
@@ -91,7 +91,7 @@ const MonitorNodeConfigForm = forwardRef<MonitorNodeConfigFormInstance, MonitorN
|
||||
|
||||
<div className="w-1/3">
|
||||
<Form.Item name="port" label={t("workflow_node.monitor.form.port.label")} rules={[formRule]}>
|
||||
<InputNumber className="w-full" min={1} max={65535} placeholder={t("workflow_node.monitor.form.port.placeholder")} />
|
||||
<InputNumber style={{ width: "100%" }} min={1} max={65535} placeholder={t("workflow_node.monitor.form.port.placeholder")} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -187,7 +187,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
<Input.TextArea autoSize={{ minRows: 3, maxRows: 10 }} placeholder={t("workflow_node.notify.form.message.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate line-through">{t("workflow_node.notify.form.channel.label")}</div>
|
||||
@@ -231,7 +231,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item className="mb-0" htmlFor="null">
|
||||
<Form.Item noStyle>
|
||||
<label className="mb-1 block">
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="max-w-full grow truncate">
|
||||
|
||||
@@ -19,23 +19,25 @@ const UnknownNode = ({ node, disabled }: MonitorNodeProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="relative w-[256px] overflow-hidden shadow-md" styles={{ body: { padding: 0 } }} hoverable variant="borderless">
|
||||
<div className="cursor-pointer ">
|
||||
<Alert
|
||||
type="error"
|
||||
message={
|
||||
<div className="flex items-center justify-between gap-4 overflow-hidden">
|
||||
<div className="flex-1 text-center text-xs">
|
||||
INVALID NODE
|
||||
<br />
|
||||
PLEASE REMOVE
|
||||
<div className="relative w-[256px] overflow-hidden">
|
||||
<Card className="shadow-md" styles={{ body: { padding: 0 } }} hoverable variant="borderless">
|
||||
<div className="cursor-pointer ">
|
||||
<Alert
|
||||
type="error"
|
||||
message={
|
||||
<div className="flex items-center justify-between gap-4 overflow-hidden">
|
||||
<div className="flex-1 text-center text-xs">
|
||||
INVALID NODE
|
||||
<br />
|
||||
PLEASE REMOVE
|
||||
</div>
|
||||
<Button color="primary" icon={<CloseCircleOutlinedIcon />} variant="text" onClick={handleClickRemove} />
|
||||
</div>
|
||||
<Button color="primary" icon={<CloseCircleOutlinedIcon />} variant="text" onClick={handleClickRemove} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<AddNode node={node} disabled={disabled} />
|
||||
</>
|
||||
|
||||
@@ -251,19 +251,21 @@ const SharedNodeBlock = ({ children, node, disabled, onClick }: SharedNodeBlockP
|
||||
content={<SharedNodeMenu node={node} disabled={disabled} trigger={<Button color="primary" icon={<MoreOutlinedIcon />} variant="text" />} />}
|
||||
placement="rightTop"
|
||||
>
|
||||
<Card className="relative w-[256px] overflow-hidden shadow-md" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="bg-primary flex h-[48px] flex-col items-center justify-center truncate px-4 py-2 text-white">
|
||||
<SharedNodeTitle
|
||||
className="focus:bg-background focus:text-foreground overflow-hidden outline-none focus:rounded-sm"
|
||||
node={node}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative w-[256px] overflow-hidden">
|
||||
<Card className="shadow-md" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="bg-primary flex h-[48px] flex-col items-center justify-center truncate px-4 py-2 text-white">
|
||||
<SharedNodeTitle
|
||||
className="focus:bg-background focus:text-foreground overflow-hidden outline-hidden focus:rounded-xs"
|
||||
node={node}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex cursor-pointer flex-col justify-center px-4 py-2" onClick={handleNodeClick}>
|
||||
<div className="overflow-hidden text-sm">{children}</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="flex cursor-pointer flex-col justify-center px-4 py-2" onClick={handleNodeClick}>
|
||||
<div className="overflow-hidden text-sm">{children}</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
<AddNode node={node} disabled={disabled} />
|
||||
|
||||
+2
-3
@@ -1,6 +1,5 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import "tailwindcss";
|
||||
@config "../tailwind.config.js";
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
|
||||
@@ -51,12 +51,12 @@ const ConsoleLayout = () => {
|
||||
</div>
|
||||
</Layout.Sider>
|
||||
|
||||
<Layout className="flex flex-col overflow-hidden pl-[256px] max-md:pl-0">
|
||||
<Layout className="flex flex-col overflow-hidden">
|
||||
<Show when={!isBrowserHappy()}>
|
||||
<Alert message={t("common.text.happy_browser")} type="warning" showIcon closable />
|
||||
</Show>
|
||||
|
||||
<Layout.Header className="p-0 shadow-sm" style={{ background: themeToken.colorBgContainer }}>
|
||||
<Layout.Header className="shadow-xs" style={{ background: themeToken.colorBgContainer, padding: 0 }}>
|
||||
<div className="flex size-full items-center justify-between overflow-hidden px-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<SiderMenuDrawer trigger={<Button className="md:hidden" icon={<MenuOutlinedIcon />} size="large" />} />
|
||||
|
||||
@@ -55,10 +55,10 @@ const AccessList = () => {
|
||||
ellipsis: true,
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Space className="max-w-full truncate" size={4}>
|
||||
<div className="max-w-full flex items-center gap-2 overflow-hidden truncate">
|
||||
<Avatar shape="square" src={accessProvidersMap.get(record.provider)?.icon} size="small" />
|
||||
<Typography.Text ellipsis>{t(accessProvidersMap.get(record.provider)?.name ?? "")}</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -57,7 +57,7 @@ const SettingsAccount = () => {
|
||||
{MessageContextHolder}
|
||||
{NotificationContextHolder}
|
||||
|
||||
<div className="md:max-w-[40rem]">
|
||||
<div className="md:max-w-160">
|
||||
<Form {...formProps} form={formInst} disabled={formPending} layout="vertical">
|
||||
<Form.Item name="username" label={t("settings.account.form.email.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("settings.account.form.email.placeholder")} onChange={handleInputChange} />
|
||||
|
||||
@@ -16,17 +16,17 @@ const SettingsNotification = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card className="shadow" title={t("settings.notification.template.card.title")}>
|
||||
<div className="md:max-w-[40rem]">
|
||||
<Card className="shadow-sm" title={t("settings.notification.template.card.title")}>
|
||||
<div className="md:max-w-160">
|
||||
<NotifyTemplate />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Card className="shadow" styles={{ body: loadedAtOnce ? { padding: 0 } : {} }} title={t("settings.notification.channels.card.title")}>
|
||||
<Card className="shadow-sm" styles={{ body: loadedAtOnce ? { padding: 0 } : {} }} title={t("settings.notification.channels.card.title")}>
|
||||
<Alert type="warning" banner message="本页面相关功能即将在后续版本中废弃,请使用「授权管理」页面来管理通知渠道。" />
|
||||
<NotifyChannels classNames={{ form: "md:max-w-[40rem]" }} />
|
||||
<NotifyChannels classNames={{ form: "md:max-w-160" }} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ const SettingsPassword = () => {
|
||||
{MessageContextHolder}
|
||||
{NotificationContextHolder}
|
||||
|
||||
<div className="md:max-w-[40rem]">
|
||||
<div className="md:max-w-160">
|
||||
<Form {...formProps} form={formInst} disabled={formPending} layout="vertical">
|
||||
<Form.Item name="oldPassword" label={t("settings.password.form.old_password.label")} rules={[formRule]}>
|
||||
<Input.Password placeholder={t("settings.password.form.old_password.placeholder")} onChange={handleInputChange} />
|
||||
|
||||
@@ -83,7 +83,7 @@ const SettingsPersistence = () => {
|
||||
{NotificationContextHolder}
|
||||
|
||||
<Show when={!loading} fallback={<Skeleton active />}>
|
||||
<div className="md:max-w-[40rem]">
|
||||
<div className="md:max-w-160">
|
||||
<Form {...formProps} form={formInst} disabled={formPending} layout="vertical">
|
||||
<Form.Item
|
||||
name="workflowRunsMaxDaysRetention"
|
||||
@@ -92,7 +92,7 @@ const SettingsPersistence = () => {
|
||||
rules={[formRule]}
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
style={{ width: "100%" }}
|
||||
min={0}
|
||||
max={36500}
|
||||
placeholder={t("settings.persistence.form.workflow_runs_max_days.placeholder")}
|
||||
@@ -108,7 +108,7 @@ const SettingsPersistence = () => {
|
||||
rules={[formRule]}
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
style={{ width: "100%" }}
|
||||
min={0}
|
||||
max={36500}
|
||||
placeholder={t("settings.persistence.form.expired_certificates_max_days.placeholder")}
|
||||
|
||||
@@ -430,7 +430,11 @@ const SettingsSSLProvider = () => {
|
||||
|
||||
<Show when={!loading} fallback={<Skeleton active />}>
|
||||
<Form form={formInst} disabled={formPending} layout="vertical" initialValues={{ provider: providerType }}>
|
||||
<Form.Item className="mb-2" name="provider" label={t("settings.sslprovider.form.provider.label")}>
|
||||
<Form.Item>
|
||||
<Alert type="warning" message={<span dangerouslySetInnerHTML={{ __html: t("settings.sslprovider.form.provider.alert") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="provider" label={t("settings.sslprovider.form.provider.label")}>
|
||||
<CheckCard.Group className="w-full" onChange={(value) => setProviderType(value as CAProviderType)}>
|
||||
<CheckCard
|
||||
avatar={<img src={"/imgs/providers/letsencrypt.svg"} className="size-8" />}
|
||||
@@ -476,13 +480,9 @@ const SettingsSSLProvider = () => {
|
||||
/>
|
||||
</CheckCard.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Alert type="warning" message={<span dangerouslySetInnerHTML={{ __html: t("settings.sslprovider.form.provider.alert") }}></span>} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<div className="md:max-w-[40rem]">{providerFormEl}</div>
|
||||
<div className="md:max-w-160">{providerFormEl}</div>
|
||||
</Show>
|
||||
</SSLProviderContext.Provider>
|
||||
);
|
||||
|
||||
@@ -270,7 +270,7 @@ const WorkflowDetail = () => {
|
||||
}}
|
||||
loading={!initialized}
|
||||
>
|
||||
<div className="absolute inset-x-6 top-4 z-[2] flex items-center justify-between gap-4">
|
||||
<div className="absolute inset-x-6 top-4 z-2 flex items-center justify-between gap-4">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Show when={workflow.hasDraft!}>
|
||||
<Alert banner message={<div className="truncate">{t("workflow.detail.orchestration.draft.alert")}</div>} type="warning" />
|
||||
|
||||
@@ -119,7 +119,7 @@ const WorkflowNew = () => {
|
||||
</Card>
|
||||
|
||||
<div className="p-4">
|
||||
<div className="mx-auto max-w-[1600px] px-2">
|
||||
<div className="mx-auto max-w-400 px-2">
|
||||
<Typography.Text type="secondary">
|
||||
<div className="mb-8 mt-4 text-xl">{t("workflow.new.templates.title")}</div>
|
||||
</Typography.Text>
|
||||
|
||||
@@ -7,10 +7,6 @@ module.exports = {
|
||||
container: {
|
||||
center: "true",
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
"3xl": "1920px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
@@ -22,5 +18,4 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
||||
|
||||
+4
-2
@@ -1,6 +1,7 @@
|
||||
import { type SpawnSyncReturns, execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
|
||||
import tailwindcssPlugin from "@tailwindcss/vite";
|
||||
import legacyPlugin from "@vitejs/plugin-legacy";
|
||||
import reactPlugin from "@vitejs/plugin-react";
|
||||
import fs from "fs-extra";
|
||||
@@ -14,7 +15,7 @@ const preserveFilesPlugin = (filesToPreserve: string[]): Plugin => {
|
||||
// 在构建开始时将要保留的文件或目录移动到临时位置
|
||||
filesToPreserve.forEach((file) => {
|
||||
const srcPath = path.resolve(__dirname, file);
|
||||
const tempPath = path.resolve(__dirname, `node_modules`, `.tmp`, `build_${file}`);
|
||||
const tempPath = path.resolve(__dirname, `node_modules/.tmp/build/${file}`);
|
||||
if (fs.existsSync(srcPath)) {
|
||||
fs.moveSync(srcPath, tempPath, { overwrite: true });
|
||||
}
|
||||
@@ -24,7 +25,7 @@ const preserveFilesPlugin = (filesToPreserve: string[]): Plugin => {
|
||||
// 在构建完成后将临时位置的文件或目录移回原来的位置
|
||||
filesToPreserve.forEach((file) => {
|
||||
const srcPath = path.resolve(__dirname, file);
|
||||
const tempPath = path.resolve(__dirname, `node_modules`, `.tmp`, `build_${file}`);
|
||||
const tempPath = path.resolve(__dirname, `node_modules/.tmp/build/${file}`);
|
||||
if (fs.existsSync(tempPath)) {
|
||||
fs.moveSync(tempPath, srcPath, { overwrite: true });
|
||||
}
|
||||
@@ -56,6 +57,7 @@ export default defineConfig(({ mode }) => {
|
||||
legacyPlugin({
|
||||
targets: ["defaults", "not IE 11"],
|
||||
}),
|
||||
tailwindcssPlugin(),
|
||||
preserveFilesPlugin(["dist/.gitkeep"]),
|
||||
],
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user