mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
Merge branch 'feat/new-workflow-ui' of github.com:fudiwei/certimate into fudiwei-feat/new-workflow-ui
This commit is contained in:
commit
a6c002146c
@ -11,7 +11,10 @@ import (
|
||||
)
|
||||
|
||||
type BarkNotifierConfig struct {
|
||||
// Bark 服务地址。
|
||||
// 零值时默认使用官方服务器。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// Bark 设备密钥。
|
||||
DeviceKey string `json:"deviceKey"`
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,10 @@ import (
|
||||
)
|
||||
|
||||
type DingTalkNotifierConfig struct {
|
||||
// 钉钉机器人的 Token。
|
||||
AccessToken string `json:"accessToken"`
|
||||
Secret string `json:"secret"`
|
||||
// 钉钉机器人的 Secret。
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
type DingTalkNotifier struct {
|
||||
|
@ -13,12 +13,20 @@ import (
|
||||
)
|
||||
|
||||
type EmailNotifierConfig struct {
|
||||
SmtpHost string `json:"smtpHost"`
|
||||
SmtpPort int32 `json:"smtpPort"`
|
||||
SmtpTLS bool `json:"smtpTLS"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
SenderAddress string `json:"senderAddress"`
|
||||
// SMTP 服务器地址。
|
||||
SmtpHost string `json:"smtpHost"`
|
||||
// SMTP 服务器端口。
|
||||
// 零值时根据是否启用 TLS 决定。
|
||||
SmtpPort int32 `json:"smtpPort"`
|
||||
// 是否启用 TLS。
|
||||
SmtpTLS bool `json:"smtpTLS"`
|
||||
// 用户名。
|
||||
Username string `json:"username"`
|
||||
// 密码。
|
||||
Password string `json:"password"`
|
||||
// 发件人邮箱。
|
||||
SenderAddress string `json:"senderAddress"`
|
||||
// 收件人邮箱。
|
||||
ReceiverAddress string `json:"receiverAddress"`
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type LarkNotifierConfig struct {
|
||||
// 飞书 Webhook 地址。
|
||||
WebhookUrl string `json:"webhookUrl"`
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type ServerChanNotifierConfig struct {
|
||||
// ServerChan 服务地址。
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,10 @@ import (
|
||||
)
|
||||
|
||||
type TelegramNotifierConfig struct {
|
||||
// Telegram API Token。
|
||||
ApiToken string `json:"apiToken"`
|
||||
ChatId int64 `json:"chatId"`
|
||||
// Telegram Chat ID。
|
||||
ChatId int64 `json:"chatId"`
|
||||
}
|
||||
|
||||
type TelegramNotifier struct {
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type WebhookNotifierConfig struct {
|
||||
// Webhook URL。
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,12 @@ import (
|
||||
)
|
||||
|
||||
type AliyunCASUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 阿里云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 阿里云 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Region string `json:"region"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type AliyunCASUploader struct {
|
||||
|
@ -20,9 +20,12 @@ import (
|
||||
)
|
||||
|
||||
type AliyunSLBUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 阿里云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 阿里云 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Region string `json:"region"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type AliyunSLBUploader struct {
|
||||
|
@ -19,7 +19,9 @@ import (
|
||||
)
|
||||
|
||||
type ByteplusCDNUploaderConfig struct {
|
||||
// BytePlus AccessKey。
|
||||
AccessKey string `json:"accessKey"`
|
||||
// BytePlus SecretKey。
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,9 @@ import (
|
||||
)
|
||||
|
||||
type DogeCloudUploaderConfig struct {
|
||||
// 多吉云 AccessKey。
|
||||
AccessKey string `json:"accessKey"`
|
||||
// 多吉云 SecretKey。
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,12 @@ import (
|
||||
)
|
||||
|
||||
type HuaweiCloudELBUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 华为云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 华为云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
Region string `json:"region"`
|
||||
// 华为云地域。
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type HuaweiCloudELBUploader struct {
|
||||
|
@ -18,9 +18,12 @@ import (
|
||||
)
|
||||
|
||||
type HuaweiCloudSCMUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 华为云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 华为云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
Region string `json:"region"`
|
||||
// 华为云地域。
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type HuaweiCloudSCMUploader struct {
|
||||
|
@ -15,7 +15,9 @@ import (
|
||||
)
|
||||
|
||||
type QiniuSSLCertUploaderConfig struct {
|
||||
// 七牛云 AccessKey。
|
||||
AccessKey string `json:"accessKey"`
|
||||
// 七牛云 SecretKey。
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,9 @@ import (
|
||||
)
|
||||
|
||||
type TencentCloudSSLUploaderConfig struct {
|
||||
SecretId string `json:"secretId"`
|
||||
// 腾讯云 SecretId。
|
||||
SecretId string `json:"secretId"`
|
||||
// 腾讯云 SecretKey。
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,9 @@ import (
|
||||
)
|
||||
|
||||
type VolcEngineCDNUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 火山引擎 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 火山引擎 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,9 @@ import (
|
||||
)
|
||||
|
||||
type VolcEngineLiveUploaderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 火山引擎 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 火山引擎 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
import (
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core"
|
||||
hcCdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
|
||||
hcCdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@ -21,6 +22,7 @@ func (c *Client) UploadDomainMultiCertificatesEx(request *UpdateDomainMultiCerti
|
||||
if resp, err := c.HcClient.Sync(request, requestDef); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return resp.(*UpdateDomainMultiCertificatesExResponse), nil
|
||||
temp := resp.(*hcCdnModel.UpdateDomainMultiCertificatesResponse)
|
||||
return &UpdateDomainMultiCertificatesExResponse{UpdateDomainMultiCertificatesResponse: *temp}, nil
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Certimate - Your Trusted SSL Automation Partner</title>
|
||||
</head>
|
||||
|
1673
ui/package-lock.json
generated
1673
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,16 +10,14 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/pro-components": "^2.8.2",
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@radix-ui/react-accordion": "^1.2.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.1",
|
||||
"@radix-ui/react-collapsible": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
"@radix-ui/react-radio-group": "^1.2.0",
|
||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
@ -30,6 +28,9 @@
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"ahooks": "^3.8.4",
|
||||
"antd": "^5.22.2",
|
||||
"antd-zod": "^6.0.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
@ -40,17 +41,16 @@
|
||||
"immer": "^10.1.1",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-react": "^0.417.0",
|
||||
"moment": "^2.30.1",
|
||||
"nanoid": "^5.0.7",
|
||||
"pocketbase": "^0.21.4",
|
||||
"react": "^18.3.1",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-i18next": "^15.0.2",
|
||||
"react-router-dom": "^6.25.1",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^0.9.1",
|
||||
"zod": "^3.23.8",
|
||||
"zustand": "^5.0.1"
|
||||
},
|
||||
@ -58,6 +58,7 @@
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
|
2
ui/public/logo.svg
Normal file
2
ui/public/logo.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.92591302712353 216.92591302712353" height="216.92591302712353" width="216.92591302712353"><g><svg /></g><g id="icon-0"><svg viewBox="0 0 216.92591302712353 216.92591302712353" height="216.92591302712353" width="216.92591302712353"><g><path d="M0 108.463c0-59.902 48.561-108.463 108.463-108.463 59.902 0 108.463 48.561 108.463 108.463 0 59.902-48.561 108.463-108.463 108.463-59.902 0-108.463-48.561-108.463-108.463zM108.463 211.779c57.06 0 103.316-46.256 103.316-103.316 0-57.06-46.256-103.316-103.316-103.316-57.06 0-103.316 46.256-103.316 103.316 0 57.06 46.256 103.316 103.316 103.316z" data-fill-palette-color="accent" fill="#f97317" stroke="transparent" /><ellipse rx="107.37832694842615" ry="107.37832694842615" cx="108.46295651356176" cy="108.46295651356176" fill="#f97317" stroke="transparent" stroke-width="0" fill-opacity="1" data-fill-palette-color="accent" /></g><g transform="matrix(1,0,0,1,64.03903745467258,44.049576960135155)"><svg viewBox="0 0 88.84783811777835 128.82675910685322" height="128.82675910685322" width="88.84783811777835"><g><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0" y="0" viewBox="4.99999928747888 13.445 56.29700142504224 81.6290007125211"
|
||||
enable-background="new 0 0 100 100" xml:space="preserve" height="128.82675910685322" width="88.84783811777835" class="icon-dxe-0" data-fill-palette-color="quaternary" id="dxe-0"><path d="M58.482 47.222H55.667V35.963C55.667 23.527 45.587 13.445 33.148 13.445S10.63 23.528 10.63 35.963V47.222H7.815A2.813 2.813 0 0 0 5 50.037V92.259A2.813 2.813 0 0 0 7.815 95.074H58.482A2.813 2.813 0 0 0 61.297 92.259V50.037A2.815 2.815 0 0 0 58.482 47.222M35.963 72.039V86.63H30.333V72.039C27.062 70.876 24.703 67.784 24.703 64.111A8.445 8.445 0 0 1 41.591 64.111C41.593 67.784 39.234 70.876 35.963 72.039M47.222 47.222H19.074V35.963C19.074 28.203 25.388 21.889 33.148 21.889S47.222 28.203 47.222 35.963z" fill="#ffffff" data-fill-palette-color="quaternary" /></svg></g></svg></g></svg></g></svg>
|
After Width: | Height: | Size: 2.0 KiB |
@ -1,28 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.92591302712353 216.92591302712353" height="216.92591302712353"
|
||||
width="216.92591302712353">
|
||||
<g><svg /></g>
|
||||
<g id="icon-0"><svg viewBox="0 0 216.92591302712353 216.92591302712353" height="216.92591302712353"
|
||||
width="216.92591302712353">
|
||||
<g>
|
||||
<path
|
||||
d="M0 108.463c0-59.902 48.561-108.463 108.463-108.463 59.902 0 108.463 48.561 108.463 108.463 0 59.902-48.561 108.463-108.463 108.463-59.902 0-108.463-48.561-108.463-108.463zM108.463 211.779c57.06 0 103.316-46.256 103.316-103.316 0-57.06-46.256-103.316-103.316-103.316-57.06 0-103.316 46.256-103.316 103.316 0 57.06 46.256 103.316 103.316 103.316z"
|
||||
data-fill-palette-color="accent" fill="#f97317" stroke="transparent" />
|
||||
<ellipse rx="107.37832694842615" ry="107.37832694842615" cx="108.46295651356176" cy="108.46295651356176"
|
||||
fill="#f97317" stroke="transparent" stroke-width="0" fill-opacity="1"
|
||||
data-fill-palette-color="accent" />
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,64.03903745467258,44.049576960135155)"><svg
|
||||
viewBox="0 0 88.84783811777835 128.82675910685322" height="128.82675910685322"
|
||||
width="88.84783811777835">
|
||||
<g><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
|
||||
x="0" y="0" viewBox="4.99999928747888 13.445 56.29700142504224 81.6290007125211"
|
||||
enable-background="new 0 0 100 100" xml:space="preserve" height="128.82675910685322"
|
||||
width="88.84783811777835" class="icon-dxe-0" data-fill-palette-color="quaternary"
|
||||
id="dxe-0">
|
||||
<path
|
||||
d="M58.482 47.222H55.667V35.963C55.667 23.527 45.587 13.445 33.148 13.445S10.63 23.528 10.63 35.963V47.222H7.815A2.813 2.813 0 0 0 5 50.037V92.259A2.813 2.813 0 0 0 7.815 95.074H58.482A2.813 2.813 0 0 0 61.297 92.259V50.037A2.815 2.815 0 0 0 58.482 47.222M35.963 72.039V86.63H30.333V72.039C27.062 70.876 24.703 67.784 24.703 64.111A8.445 8.445 0 0 1 41.591 64.111C41.593 67.784 39.234 70.876 35.963 72.039M47.222 47.222H19.074V35.963C19.074 28.203 25.388 21.889 33.148 21.889S47.222 28.203 47.222 35.963z"
|
||||
fill="#ffffff" data-fill-palette-color="quaternary" />
|
||||
</svg></g>
|
||||
</svg></g>
|
||||
</svg></g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
62
ui/src/App.tsx
Normal file
62
ui/src/App.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { useEffect, useLayoutEffect, useState } from "react";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { App, ConfigProvider, theme, type ThemeConfig } from "antd";
|
||||
import { type Locale } from "antd/es/locale";
|
||||
import AntdLocaleEnUs from "antd/locale/en_US";
|
||||
import AntdLocaleZhCN from "antd/locale/zh_CN";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
|
||||
import { localeNames } from "./i18n";
|
||||
import { useTheme } from "./hooks";
|
||||
import { router } from "./router.tsx";
|
||||
|
||||
const RootApp = () => {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const { theme: browserTheme } = useTheme();
|
||||
|
||||
const antdLocalesMap: Record<string, Locale> = {
|
||||
[localeNames.ZH]: AntdLocaleZhCN,
|
||||
[localeNames.EN]: AntdLocaleEnUs,
|
||||
};
|
||||
const [antdLocale, setAntdLocale] = useState(antdLocalesMap[i18n.language]);
|
||||
const handleLanguageChanged = () => {
|
||||
setAntdLocale(antdLocalesMap[i18n.language]);
|
||||
dayjs.locale(i18n.language);
|
||||
};
|
||||
i18n.on("languageChanged", handleLanguageChanged);
|
||||
useLayoutEffect(handleLanguageChanged, [i18n]);
|
||||
|
||||
const antdThemesMap: Record<string, ThemeConfig> = {
|
||||
["light"]: { algorithm: theme.defaultAlgorithm },
|
||||
["dark"]: { algorithm: theme.darkAlgorithm },
|
||||
};
|
||||
const [antdTheme, setAntdTheme] = useState(antdThemesMap[browserTheme]);
|
||||
useEffect(() => {
|
||||
setAntdTheme(antdThemesMap[browserTheme]);
|
||||
|
||||
const root = window.document.documentElement;
|
||||
root.classList.remove("light", "dark");
|
||||
root.classList.add(browserTheme);
|
||||
}, [browserTheme]);
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
locale={antdLocale}
|
||||
theme={{
|
||||
...antdTheme,
|
||||
token: {
|
||||
colorPrimary: "hsl(24.6 95% 53.1%)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<App>
|
||||
<RouterProvider router={router} />
|
||||
</App>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default RootApp;
|
@ -1,7 +1,7 @@
|
||||
import { getPb } from "@/repository/api";
|
||||
import { getPocketBase } from "@/repository/pocketbase";
|
||||
|
||||
export const notifyTest = async (channel: string) => {
|
||||
const pb = getPb();
|
||||
const pb = getPocketBase();
|
||||
|
||||
const resp = await pb.send("/api/notify/test", {
|
||||
method: "POST",
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { getPb } from "@/repository/api";
|
||||
import { Statistics } from "@/domain/statistics";
|
||||
import { getPocketBase } from "@/repository/pocketbase";
|
||||
|
||||
export const get = async () => {
|
||||
const pb = getPb();
|
||||
const pb = getPocketBase();
|
||||
|
||||
const resp = await pb.send("/api/statistics/get", {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
if (resp.code != 0) {
|
||||
if (resp.code !== 0) {
|
||||
throw new Error(resp.msg);
|
||||
}
|
||||
|
||||
return resp.data;
|
||||
return resp.data as Statistics;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { getPb } from "@/repository/api";
|
||||
import { getPocketBase } from "@/repository/pocketbase";
|
||||
|
||||
export const run = async (id: string) => {
|
||||
const pb = getPb();
|
||||
const pb = getPocketBase();
|
||||
|
||||
const resp = await pb.send("/api/workflow/run", {
|
||||
method: "POST",
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Languages } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
|
||||
export default function LocaleToggle() {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Languages className="h-[1.2rem] w-[1.2rem] dark:text-white" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{Object.keys(i18n.store.data).map((key) => (
|
||||
<DropdownMenuItem onClick={() => i18n.changeLanguage(key)}>{i18n.store.data[key].name as string}</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
type Theme = "dark" | "light" | "system";
|
||||
|
||||
type ThemeProviderProps = {
|
||||
children: React.ReactNode;
|
||||
defaultTheme?: Theme;
|
||||
storageKey?: string;
|
||||
};
|
||||
|
||||
type ThemeProviderState = {
|
||||
theme: Theme;
|
||||
setTheme: (theme: Theme) => void;
|
||||
};
|
||||
|
||||
const initialState: ThemeProviderState = {
|
||||
theme: "system",
|
||||
setTheme: () => null,
|
||||
};
|
||||
|
||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
||||
|
||||
export function ThemeProvider({ children, defaultTheme = "system", storageKey = "vite-ui-theme", ...props }: ThemeProviderProps) {
|
||||
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme);
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
|
||||
root.classList.remove("light", "dark");
|
||||
|
||||
if (theme === "system") {
|
||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
|
||||
root.classList.add(systemTheme);
|
||||
return;
|
||||
}
|
||||
|
||||
root.classList.add(theme);
|
||||
}, [theme]);
|
||||
|
||||
const value = {
|
||||
theme,
|
||||
setTheme: (theme: Theme) => {
|
||||
localStorage.setItem(storageKey, theme);
|
||||
setTheme(theme);
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProviderContext.Provider {...props} value={value}>
|
||||
{children}
|
||||
</ThemeProviderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useTheme = () => {
|
||||
const context = useContext(ThemeProviderContext);
|
||||
|
||||
if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider");
|
||||
|
||||
return context;
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Moon, Sun } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { useTheme } from "./ThemeProvider";
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { setTheme } = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 dark:text-white" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => setTheme("light")}>{t("common.theme.light")}</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("dark")}>{t("common.theme.dark")}</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("system")}>{t("common.theme.system")}</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
@ -1,64 +1,114 @@
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "../ui/sheet";
|
||||
|
||||
import { Certificate } from "@/domain/certificate";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Button } from "../ui/button";
|
||||
import { Label } from "../ui/label";
|
||||
import { CustomFile, saveFiles2ZIP } from "@/lib/file";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button, Dropdown, Form, Input, message, Space, Tooltip } from "antd";
|
||||
import { CopyToClipboard } from "react-copy-to-clipboard";
|
||||
import { ChevronDown as ChevronDownIcon, Clipboard as ClipboardIcon, ThumbsUp as ThumbsUpIcon } from "lucide-react";
|
||||
|
||||
type WorkflowLogDetailProps = {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
certificate?: Certificate;
|
||||
import { type CertificateModel } from "@/domain/certificate";
|
||||
import { saveFiles2Zip } from "@/utils/file";
|
||||
|
||||
type CertificateDetailProps = {
|
||||
data: CertificateModel;
|
||||
};
|
||||
const CertificateDetail = ({ open, onOpenChange, certificate }: WorkflowLogDetailProps) => {
|
||||
|
||||
const CertificateDetail = ({ data }: CertificateDetailProps) => {
|
||||
const { t } = useTranslation();
|
||||
const handleDownloadClick = async () => {
|
||||
const zipName = `${certificate?.id}-${certificate?.san}.zip`;
|
||||
const files: CustomFile[] = [
|
||||
|
||||
const [messageApi, MessageContextHolder] = message.useMessage();
|
||||
|
||||
const handleDownloadPEMClick = async () => {
|
||||
const zipName = `${data.id}-${data.san}.zip`;
|
||||
const files = [
|
||||
{
|
||||
name: `${certificate?.san}.pem`,
|
||||
content: certificate?.certificate ? certificate?.certificate : "",
|
||||
name: `${data.san}.pem`,
|
||||
content: data.certificate ?? "",
|
||||
},
|
||||
{
|
||||
name: `${certificate?.san}.key`,
|
||||
content: certificate?.privateKey ? certificate?.privateKey : "",
|
||||
name: `${data.san}.key`,
|
||||
content: data.privateKey ?? "",
|
||||
},
|
||||
];
|
||||
|
||||
await saveFiles2ZIP(zipName, files);
|
||||
await saveFiles2Zip(zipName, files);
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent className="sm:max-w-2xl dark:text-stone-200">
|
||||
<SheetHeader>
|
||||
<SheetTitle></SheetTitle>
|
||||
</SheetHeader>
|
||||
<div>
|
||||
{MessageContextHolder}
|
||||
|
||||
<div className="flex flex-col space-y-5 mt-9">
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size={"sm"}
|
||||
onClick={() => {
|
||||
handleDownloadClick();
|
||||
}}
|
||||
>
|
||||
{t("certificate.action.download")}
|
||||
</Button>
|
||||
<Form layout="vertical">
|
||||
<Form.Item>
|
||||
<div className="flex items-center justify-between w-full mb-2">
|
||||
<label className="font-medium">{t("certificate.props.certificate_chain")}</label>
|
||||
<Tooltip title={t("common.button.copy")}>
|
||||
<CopyToClipboard
|
||||
text={data.certificate}
|
||||
onCopy={() => {
|
||||
messageApi.success(t("common.text.copied"));
|
||||
}}
|
||||
>
|
||||
<Button type="text" icon={<ClipboardIcon size={14} />}></Button>
|
||||
</CopyToClipboard>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-3">
|
||||
<Label>{t("certificate.props.certificate")}</Label>
|
||||
<Textarea value={certificate?.certificate} rows={10} readOnly={true} />
|
||||
<Input.TextArea value={data.certificate} rows={10} autoSize={{ maxRows: 10 }} readOnly />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<div className="flex items-center justify-between w-full mb-2">
|
||||
<label className="font-medium">{t("certificate.props.private_key")}</label>
|
||||
<Tooltip title={t("common.button.copy")}>
|
||||
<CopyToClipboard
|
||||
text={data.privateKey}
|
||||
onCopy={() => {
|
||||
messageApi.success(t("common.text.copied"));
|
||||
}}
|
||||
>
|
||||
<Button type="text" icon={<ClipboardIcon size={14} />}></Button>
|
||||
</CopyToClipboard>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-3">
|
||||
<Label>{t("certificate.props.private.key")}</Label>
|
||||
<Textarea value={certificate?.privateKey} rows={10} readOnly={true} />
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<Input.TextArea value={data.privateKey} rows={10} autoSize={{ maxRows: 10 }} readOnly />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<div className="flex items-center justify-end">
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: "PEM",
|
||||
label: "PEM",
|
||||
extra: <ThumbsUpIcon size="14" />,
|
||||
onClick: () => handleDownloadPEMClick(),
|
||||
},
|
||||
{
|
||||
key: "PFX",
|
||||
label: "PFX",
|
||||
onClick: () => {
|
||||
// TODO: 下载 PFX 格式证书
|
||||
alert("TODO");
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "JKS",
|
||||
label: "JKS",
|
||||
onClick: () => {
|
||||
// TODO: 下载 JKS 格式证书
|
||||
alert("TODO");
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Button type="primary">
|
||||
<Space>
|
||||
<span>{t("certificate.action.download")}</span>
|
||||
<ChevronDownIcon size={14} />
|
||||
</Space>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
26
ui/src/components/certificate/CertificateDetailDrawer.tsx
Normal file
26
ui/src/components/certificate/CertificateDetailDrawer.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Drawer } from "antd";
|
||||
|
||||
import { type CertificateModel } from "@/domain/certificate";
|
||||
import CertificateDetail from "./CertificateDetail";
|
||||
|
||||
type CertificateDetailDrawerProps = {
|
||||
data?: CertificateModel;
|
||||
open?: boolean;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
const CertificateDetailDrawer = ({ data, open, onClose }: CertificateDetailDrawerProps) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
useEffect(() => {
|
||||
setLoading(data == null);
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Drawer closable destroyOnClose open={open} loading={loading} placement="right" width={480} onClose={onClose}>
|
||||
{data ? <CertificateDetail data={data} /> : <></>}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificateDetailDrawer;
|
@ -1,171 +0,0 @@
|
||||
import CertificateDetail from "@/components/certificate/CertificateDetail";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DataTable } from "@/components/workflow/DataTable";
|
||||
import { Certificate as CertificateType } from "@/domain/certificate";
|
||||
import { diffDays, getLeftDays } from "@/lib/time";
|
||||
import { list } from "@/repository/certificate";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
type CertificateListProps = {
|
||||
withPagination?: boolean;
|
||||
};
|
||||
|
||||
const CertificateList = ({ withPagination }: CertificateListProps) => {
|
||||
const [data, setData] = useState<CertificateType[]>([]);
|
||||
const [pageCount, setPageCount] = useState<number>(0);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedCertificate, setSelectedCertificate] = useState<CertificateType>();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const fetchData = async (page: number, pageSize?: number) => {
|
||||
const state = searchParams.get("state");
|
||||
const resp = await list({ page: page, perPage: pageSize, state: state ?? "" });
|
||||
setData(resp.items);
|
||||
setPageCount(resp.totalPages);
|
||||
};
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const columns: ColumnDef<CertificateType>[] = [
|
||||
{
|
||||
accessorKey: "san",
|
||||
header: t("certificate.props.domain"),
|
||||
cell: ({ row }) => {
|
||||
let san: string = row.getValue("san");
|
||||
if (!san) {
|
||||
san = "";
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{san.split(";").map((item, i) => {
|
||||
return (
|
||||
<div key={i} className="max-w-[250px] truncate">
|
||||
{item}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "expireAt",
|
||||
header: t("certificate.props.expiry"),
|
||||
cell: ({ row }) => {
|
||||
const expireAt: string = row.getValue("expireAt");
|
||||
const data = row.original;
|
||||
const leftDays = getLeftDays(expireAt);
|
||||
const allDays = diffDays(data.expireAt, data.created);
|
||||
return (
|
||||
<div className="">
|
||||
{leftDays > 0 ? (
|
||||
<div className="text-green-500">
|
||||
{leftDays} / {allDays} {t("certificate.props.expiry.days")}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-red-500">{t("certificate.props.expiry.expired")}</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
{new Date(expireAt).toLocaleString().split(" ")[0]} {t("certificate.props.expiry.text.expire")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "workflow",
|
||||
header: t("certificate.props.workflow"),
|
||||
cell: ({ row }) => {
|
||||
const name = row.original.expand.workflow?.name;
|
||||
const workflowId: string = row.getValue("workflow");
|
||||
return (
|
||||
<div className="max-w-[200px] truncate">
|
||||
<Button
|
||||
size={"sm"}
|
||||
variant={"link"}
|
||||
onClick={() => {
|
||||
handleWorkflowClick(workflowId);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "created",
|
||||
header: t("certificate.props.created"),
|
||||
cell: ({ row }) => {
|
||||
const date: string = row.getValue("created");
|
||||
return new Date(date).toLocaleString();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={"link"}
|
||||
onClick={() => {
|
||||
handleView(row.original.id);
|
||||
}}
|
||||
>
|
||||
{t("certificate.action.view")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleWorkflowClick = (id: string) => {
|
||||
navigate(`/workflow/detail?id=${id}`);
|
||||
};
|
||||
|
||||
const handleView = (id: string) => {
|
||||
setOpen(true);
|
||||
const certificate = data.find((item) => item.id === id);
|
||||
setSelectedCertificate(certificate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
onPageChange={fetchData}
|
||||
data={data}
|
||||
pageCount={pageCount}
|
||||
withPagination={withPagination}
|
||||
fallback={
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="text-muted-foreground">{t("certificate.nodata")}</div>
|
||||
<Button
|
||||
size={"sm"}
|
||||
className="w-[120px] mt-3"
|
||||
onClick={() => {
|
||||
navigate("/workflow/detail");
|
||||
}}
|
||||
>
|
||||
{t("workflow.action.create")}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<CertificateDetail open={open} onOpenChange={setOpen} certificate={selectedCertificate} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificateList;
|
@ -8,19 +8,21 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type AliyunConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type AliyunConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessAliyunFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z
|
||||
@ -56,7 +58,7 @@ const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -81,7 +83,7 @@ const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -184,7 +186,7 @@ const AccessAliyunForm = ({ data, op, onAfterReq }: AccessAliyunFormProps) => {
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,19 +8,21 @@ import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { Access, accessProvidersMap, accessTypeFormSchema, type AwsConfig } from "@/domain/access";
|
||||
import { AccessModel, accessProvidersMap, accessTypeFormSchema, type AwsConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessAwsFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z
|
||||
@ -68,7 +70,7 @@ const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -94,7 +96,8 @@ const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -227,7 +230,7 @@ const AccessAwsForm = ({ data, op, onAfterReq }: AccessAwsFormProps) => {
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,19 +8,21 @@ import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type BaiduCloudConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type BaiduCloudConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessBaiduCloudFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
name: z
|
||||
@ -56,7 +58,7 @@ const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProp
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -80,7 +82,8 @@ const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProp
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -183,7 +186,7 @@ const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProp
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type ByteplusConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type ByteplusConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessByteplusFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -56,7 +56,7 @@ const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) =
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -80,7 +80,7 @@ const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) =
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -183,7 +183,7 @@ const AccessByteplusForm = ({ data, op, onAfterReq }: AccessByteplusFormProps) =
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type CloudflareConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type CloudflareConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessCloudflareFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -50,7 +50,7 @@ const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProp
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -73,7 +73,7 @@ const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProp
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -157,7 +157,7 @@ const AccessCloudflareForm = ({ data, op, onAfterReq }: AccessCloudflareFormProp
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type DogeCloudConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type DogeCloudConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessDogeCloudFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -50,7 +50,7 @@ const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps)
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -74,7 +74,7 @@ const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps)
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -177,7 +177,7 @@ const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps)
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
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";
|
||||
@ -23,14 +23,14 @@ import AccessWebhookForm from "./AccessWebhookForm";
|
||||
import AccessKubernetesForm from "./AccessKubernetesForm";
|
||||
import AccessVolcengineForm from "./AccessVolcengineForm";
|
||||
import AccessByteplusForm from "./AccessByteplusForm";
|
||||
import { Access } from "@/domain/access";
|
||||
import { AccessModel } from "@/domain/access";
|
||||
import { AccessTypeSelect } from "./AccessTypeSelect";
|
||||
|
||||
type AccessEditProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
className?: string;
|
||||
trigger: React.ReactNode;
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
outConfigType?: string;
|
||||
};
|
||||
|
||||
@ -273,9 +273,9 @@ const AccessEditDialog = ({ trigger, op, data, className, outConfigType }: Acces
|
||||
<DialogTitle>
|
||||
{
|
||||
{
|
||||
["add"]: t("access.authorization.add"),
|
||||
["edit"]: t("access.authorization.edit"),
|
||||
["copy"]: t("access.authorization.copy"),
|
||||
["add"]: t("access.action.add"),
|
||||
["edit"]: t("access.action.edit"),
|
||||
["copy"]: t("access.action.copy"),
|
||||
}[op]
|
||||
}
|
||||
</DialogTitle>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type GodaddyConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type GodaddyConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessGodaddyFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -56,7 +56,7 @@ const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) =>
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -80,7 +80,7 @@ const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) =>
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -179,7 +179,7 @@ const AccessGodaddyForm = ({ data, op, onAfterReq }: AccessGodaddyFormProps) =>
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type HttpreqConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type HttpreqConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessHttpreqFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -62,7 +62,7 @@ const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) =>
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -89,7 +89,7 @@ const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) =>
|
||||
return;
|
||||
}
|
||||
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -222,7 +222,7 @@ const AccessHttpreqForm = ({ data, op, onAfterReq }: AccessHttpreqFormProps) =>
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type HuaweiCloudConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type HuaweiCloudConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessHuaweiCloudFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -62,7 +62,7 @@ const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormPr
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -87,7 +87,7 @@ const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormPr
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -205,7 +205,7 @@ const AccessHuaweiCloudForm = ({ data, op, onAfterReq }: AccessHuaweiCloudFormPr
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,20 +8,20 @@ import { ClientResponseError } from "pocketbase";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { readFileContent } from "@/lib/file";
|
||||
import { readFileContent } from "@/utils/file";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type KubernetesConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type KubernetesConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessKubernetesFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [fileName, setFileName] = useState("");
|
||||
@ -60,7 +60,7 @@ const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProp
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -82,7 +82,7 @@ const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProp
|
||||
if (data.id && op == "edit") {
|
||||
updateAccess(req);
|
||||
} else {
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
@ -182,7 +182,7 @@ const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProp
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
@ -191,4 +191,3 @@ const AccessKubernetesForm = ({ data, op, onAfterReq }: AccessKubernetesFormProp
|
||||
};
|
||||
|
||||
export default AccessKubernetesForm;
|
||||
|
||||
|
@ -8,18 +8,18 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessLocalFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formSchema = z.object({
|
||||
@ -41,7 +41,7 @@ const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -62,7 +62,7 @@ const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
|
||||
if (data.id && op == "edit") {
|
||||
updateAccess(req);
|
||||
} else {
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
@ -136,7 +136,7 @@ const AccessLocalForm = ({ data, op, onAfterReq }: AccessLocalFormProps) => {
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type NamesiloConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type NamesiloConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessNamesiloFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -50,7 +50,7 @@ const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) =
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -73,7 +73,7 @@ const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) =
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -157,7 +157,7 @@ const AccessNamesiloForm = ({ data, op, onAfterReq }: AccessNamesiloFormProps) =
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type PdnsConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type PdnsConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessPdnsFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -53,7 +53,7 @@ const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -78,7 +78,7 @@ const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -181,7 +181,7 @@ const AccessPdnsForm = ({ data, op, onAfterReq }: AccessPdnsFormProps) => {
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type QiniuConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type QiniuConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessQiniuFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -50,7 +50,7 @@ const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -74,7 +74,7 @@ const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -177,7 +177,7 @@ const AccessQiniuForm = ({ data, op, onAfterReq }: AccessQiniuFormProps) => {
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,20 +8,20 @@ import { ClientResponseError } from "pocketbase";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { readFileContent } from "@/lib/file";
|
||||
import { readFileContent } from "@/utils/file";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type SSHConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type SSHConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessSSHFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
@ -103,7 +103,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
|
||||
let group = data.group;
|
||||
if (group == "emptyId") group = "";
|
||||
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -131,7 +131,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
|
||||
if (data.id && op == "edit") {
|
||||
updateAccess(req);
|
||||
} else {
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
@ -337,7 +337,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type TencentConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type TencentConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessTencentFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -56,7 +56,7 @@ const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) =>
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -80,7 +80,7 @@ const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) =>
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -179,7 +179,7 @@ const AccessTencentForm = ({ data, op, onAfterReq }: AccessTencentFormProps) =>
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Check, ChevronsUpDown } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "@/components/ui/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
|
@ -8,18 +8,18 @@ import { Input } from "@/components/ui/input";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type Access, type VolcengineConfig } from "@/domain/access";
|
||||
import { accessProvidersMap, accessTypeFormSchema, type AccessModel, type VolcengineConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessVolcengineFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -56,7 +56,7 @@ const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProp
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -80,7 +80,7 @@ const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProp
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -183,7 +183,7 @@ const AccessVolcengineForm = ({ data, op, onAfterReq }: AccessVolcengineFormProp
|
||||
<FormMessage />
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -8,18 +8,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { Access, accessProvidersMap, accessTypeFormSchema, WebhookConfig } from "@/domain/access";
|
||||
import { AccessModel, accessProvidersMap, accessTypeFormSchema, WebhookConfig } from "@/domain/access";
|
||||
import { save } from "@/repository/access";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
|
||||
type AccessWebhookFormProps = {
|
||||
op: "add" | "edit" | "copy";
|
||||
data?: Access;
|
||||
data?: AccessModel;
|
||||
onAfterReq: () => void;
|
||||
};
|
||||
|
||||
const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) => {
|
||||
const { addAccess, updateAccess } = useConfigContext();
|
||||
const { createAccess, updateAccess } = useAccessStore();
|
||||
const { t } = useTranslation();
|
||||
const formSchema = z.object({
|
||||
id: z.string().optional(),
|
||||
@ -47,7 +47,7 @@ const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) =>
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
const req: Access = {
|
||||
const req: AccessModel = {
|
||||
id: data.id as string,
|
||||
name: data.name,
|
||||
configType: data.configType,
|
||||
@ -70,7 +70,7 @@ const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) =>
|
||||
updateAccess(req);
|
||||
return;
|
||||
}
|
||||
addAccess(req);
|
||||
createAccess(req);
|
||||
} catch (e) {
|
||||
const err = e as ClientResponseError;
|
||||
|
||||
@ -154,7 +154,7 @@ const AccessWebhookForm = ({ data, op, onAfterReq }: AccessWebhookFormProps) =>
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -1,19 +1,17 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
import { cn } from "@/components/ui/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PbErrorData } from "@/domain/base";
|
||||
import { EmailsSetting } from "@/domain/settings";
|
||||
import { update } from "@/repository/settings";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { type PbErrorData } from "@/domain/base";
|
||||
import { useContactStore } from "@/stores/contact";
|
||||
|
||||
type EmailsEditProps = {
|
||||
className?: string;
|
||||
@ -21,10 +19,7 @@ type EmailsEditProps = {
|
||||
};
|
||||
|
||||
const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
const {
|
||||
config: { emails },
|
||||
setEmails,
|
||||
} = useConfigContext();
|
||||
const { emails, setEmails, fetchEmails } = useContactStore();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
@ -40,30 +35,21 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchEmails();
|
||||
}, []);
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
if ((emails.content as EmailsSetting).emails.includes(data.email)) {
|
||||
if (emails.includes(data.email)) {
|
||||
form.setError("email", {
|
||||
message: "common.errmsg.email_duplicate",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存到 config
|
||||
const newEmails = [...(emails.content as EmailsSetting).emails, data.email];
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
...emails,
|
||||
name: "emails",
|
||||
content: {
|
||||
emails: newEmails,
|
||||
},
|
||||
});
|
||||
await setEmails([...emails, data.email]);
|
||||
|
||||
// 更新本地状态
|
||||
setEmails(resp);
|
||||
|
||||
// 关闭弹窗
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
|
||||
@ -115,7 +101,7 @@ const EmailsEdit = ({ className, trigger }: EmailsEditProps) => {
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@ -75,7 +75,7 @@ const KVList = ({ variables, onValueChange }: KVListProps) => {
|
||||
<div className="flex items-center text-primary">
|
||||
<Plus size={16} className="cursor-pointer " />
|
||||
|
||||
<div className="text-sm ">{t("common.add")}</div>
|
||||
<div className="text-sm ">{t("common.button.add")}</div>
|
||||
</div>
|
||||
}
|
||||
onSave={(variable) => {
|
||||
@ -96,7 +96,7 @@ const KVList = ({ variables, onValueChange }: KVListProps) => {
|
||||
<div className="flex items-center text-primary">
|
||||
<Plus size={16} className="cursor-pointer " />
|
||||
|
||||
<div className="text-sm ">{t("common.add")}</div>
|
||||
<div className="text-sm ">{t("common.button.add")}</div>
|
||||
</div>
|
||||
}
|
||||
variable={{
|
||||
@ -231,7 +231,7 @@ const KVEdit = ({ variable, trigger, onSave }: KVEditProps) => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
|
@ -3,12 +3,12 @@ import { z } from "zod";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Edit, Plus, Trash2 } from "lucide-react";
|
||||
|
||||
import { cn } from "@/components/ui/utils";
|
||||
import Show from "@/components/Show";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { FormControl, FormItem, FormLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type StringListProps = {
|
||||
className?: string;
|
||||
@ -79,7 +79,7 @@ const StringList = ({ value, className, onValueChange, valueType = "domain" }: S
|
||||
<div className="flex items-center text-primary">
|
||||
<Plus size={16} className="cursor-pointer " />
|
||||
|
||||
<div className="text-sm ">{t("common.add")}</div>
|
||||
<div className="text-sm ">{t("common.button.add")}</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@ -92,7 +92,7 @@ const StringList = ({ value, className, onValueChange, valueType = "domain" }: S
|
||||
<div className="border rounded-md p-3 text-sm flex flex-col items-center">
|
||||
<div className="text-muted-foreground">{t("common.text." + valueType + ".empty")}</div>
|
||||
|
||||
<StringEdit value={""} trigger={t("common.add")} onValueChange={addVal} valueType={valueType} />
|
||||
<StringEdit value={""} trigger={t("common.button.add")} onValueChange={addVal} valueType={valueType} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@ -208,7 +208,7 @@ const StringEdit = ({ trigger, value, onValueChange, op = "add", valueType }: St
|
||||
onSaveClick();
|
||||
}}
|
||||
>
|
||||
{op === "add" ? t("common.add") : t("common.confirm")}
|
||||
{op === "add" ? t("common.button.add") : t("common.button.ok")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BookOpen } from "lucide-react";
|
||||
import { Divider, Space, Typography } from "antd";
|
||||
import { BookOpen as BookOpenIcon } from "lucide-react";
|
||||
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { version } from "@/domain/version";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type VersionProps = {
|
||||
className?: string;
|
||||
@ -13,20 +12,19 @@ const Version = ({ className }: VersionProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={cn("w-full flex pb-5 ", className)}>
|
||||
<div className="text-muted-foreground text-sm hover:text-stone-900 dark:hover:text-stone-200 flex">
|
||||
<a href="https://docs.certimate.me" target="_blank" className="flex items-center">
|
||||
<BookOpen size={16} />
|
||||
<div className="ml-1">{t("common.menu.document")}</div>
|
||||
</a>
|
||||
<Separator orientation="vertical" className="mx-2" />
|
||||
<a href="https://github.com/usual2970/certimate/releases" target="_blank">
|
||||
{version}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Space className={className} size={4}>
|
||||
<Typography.Link type="secondary" href="https://docs.certimate.me" target="_blank">
|
||||
<div className="flex items-center justify-center space-x-1">
|
||||
<BookOpenIcon size={16} />
|
||||
<span>{t("common.menu.document")}</span>
|
||||
</div>
|
||||
</Typography.Link>
|
||||
<Divider type="vertical" />
|
||||
<Typography.Link type="secondary" href="https://github.com/usual2970/certimate/releases" target="_blank">
|
||||
{version}
|
||||
</Typography.Link>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default Version;
|
||||
|
||||
|
@ -1,92 +0,0 @@
|
||||
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink } from "@/components/ui/pagination";
|
||||
|
||||
type PaginationProps = {
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
onPageChange: (page: number) => void;
|
||||
};
|
||||
|
||||
type PageNumber = number | string;
|
||||
|
||||
const XPagination = ({ totalPages, currentPage, onPageChange }: PaginationProps) => {
|
||||
const pageNeighbours = 1; // Number of page numbers to show on either side of the current page
|
||||
|
||||
const getPageNumbers = () => {
|
||||
const totalNumbers = pageNeighbours * 2 + 3; // total pages to display (left + right neighbours + current + 2 for start and end)
|
||||
const totalBlocks = totalNumbers + 2; // adding 2 for the start and end page numbers
|
||||
|
||||
if (totalPages > totalBlocks) {
|
||||
let pages: PageNumber[] = [];
|
||||
|
||||
const leftBound = Math.max(2, currentPage - pageNeighbours);
|
||||
const rightBound = Math.min(totalPages - 1, currentPage + pageNeighbours);
|
||||
|
||||
const beforeLastPage = totalPages - 1;
|
||||
|
||||
pages = range(leftBound, rightBound);
|
||||
|
||||
if (currentPage > pageNeighbours + 2) {
|
||||
pages.unshift("...");
|
||||
}
|
||||
if (currentPage < beforeLastPage - pageNeighbours) {
|
||||
pages.push("...");
|
||||
}
|
||||
|
||||
pages.unshift(1);
|
||||
pages.push(totalPages);
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
return range(1, totalPages);
|
||||
};
|
||||
|
||||
const range = (from: number, to: number, step = 1) => {
|
||||
let i = from;
|
||||
const range = [];
|
||||
|
||||
while (i <= to) {
|
||||
range.push(i);
|
||||
i += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
const pages = getPageNumbers();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Pagination className="dark:text-stone-200 justify-end mt-3">
|
||||
<PaginationContent>
|
||||
{pages.map((page, index) => {
|
||||
if (page === "...") {
|
||||
return (
|
||||
<PaginationItem key={index}>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PaginationItem key={index}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
isActive={currentPage == page}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onPageChange(page as number);
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
);
|
||||
})}
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default XPagination;
|
@ -6,9 +6,9 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { NotifyChannels, NotifyChannelBark } from "@/domain/settings";
|
||||
import { update } from "@/repository/settings";
|
||||
import { save } from "@/repository/settings";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
import Show from "@/components/Show";
|
||||
@ -96,7 +96,7 @@ const Bark = () => {
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -109,14 +109,14 @@ const Bark = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -137,7 +137,7 @@ const Bark = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -160,7 +160,7 @@ const Bark = () => {
|
||||
setBark(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -173,10 +173,10 @@ const Bark = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -238,7 +238,7 @@ const Bark = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -6,10 +6,10 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { NotifyChannelDingTalk, NotifyChannels } from "@/domain/settings";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { update } from "@/repository/settings";
|
||||
import { save } from "@/repository/settings";
|
||||
import Show from "@/components/Show";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
|
||||
@ -96,7 +96,7 @@ const DingTalk = () => {
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -109,14 +109,14 @@ const DingTalk = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -139,7 +139,7 @@ const DingTalk = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -160,7 +160,7 @@ const DingTalk = () => {
|
||||
setDingtalk(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -173,10 +173,10 @@ const DingTalk = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -236,7 +236,7 @@ const DingTalk = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -6,10 +6,10 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { NotifyChannelEmail, NotifyChannels } from "@/domain/settings";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { update } from "@/repository/settings";
|
||||
import { save } from "@/repository/settings";
|
||||
import Show from "@/components/Show";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
|
||||
@ -119,7 +119,7 @@ const Mail = () => {
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -132,14 +132,14 @@ const Mail = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -160,7 +160,7 @@ const Mail = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -183,7 +183,7 @@ const Mail = () => {
|
||||
setMail(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -196,10 +196,10 @@ const Mail = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -360,7 +360,7 @@ const Mail = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -5,8 +5,8 @@ import { Label } from "@/components/ui/label";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { NotifyChannelLark, NotifyChannels } from "@/domain/settings";
|
||||
import { useEffect, useState } from "react";
|
||||
import { update } from "@/repository/settings";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { save } from "@/repository/settings";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
@ -92,7 +92,7 @@ const Lark = () => {
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -105,14 +105,14 @@ const Lark = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -135,7 +135,7 @@ const Lark = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -156,7 +156,7 @@ const Lark = () => {
|
||||
setLark(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -169,10 +169,10 @@ const Lark = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -214,7 +214,7 @@ const Lark = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { defaultNotifyTemplate, NotifyTemplates, NotifyTemplate as NotifyTemplateT } from "@/domain/settings";
|
||||
import { getSetting, update } from "@/repository/settings";
|
||||
import { get, save } from "@/repository/settings";
|
||||
|
||||
const NotifyTemplate = () => {
|
||||
const [id, setId] = useState("");
|
||||
@ -17,7 +17,7 @@ const NotifyTemplate = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const featchData = async () => {
|
||||
const resp = await getSetting("templates");
|
||||
const resp = await get("templates");
|
||||
|
||||
if (resp.content) {
|
||||
setTemplates((resp.content as NotifyTemplates).notifyTemplates);
|
||||
@ -50,7 +50,7 @@ const NotifyTemplate = () => {
|
||||
};
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
id: id,
|
||||
content: {
|
||||
notifyTemplates: templates,
|
||||
@ -63,7 +63,7 @@ const NotifyTemplate = () => {
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.template.saved.message"),
|
||||
});
|
||||
};
|
||||
@ -88,7 +88,7 @@ const NotifyTemplate = () => {
|
||||
></Textarea>
|
||||
<div className="text-muted-foreground text-sm mt-1">{t("settings.notification.template.variables.tips.content")}</div>
|
||||
<div className="flex justify-end mt-2">
|
||||
<Button onClick={handleSaveClick}>{t("common.save")}</Button>
|
||||
<Button onClick={handleSaveClick}>{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -6,10 +6,10 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { isValidURL } from "@/lib/url";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { isValidURL } from "@/utils/url";
|
||||
import { NotifyChannels, NotifyChannelServerChan } from "@/domain/settings";
|
||||
import { update } from "@/repository/settings";
|
||||
import { save } from "@/repository/settings";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
import Show from "@/components/Show";
|
||||
@ -96,14 +96,14 @@ const ServerChan = () => {
|
||||
serverchan.data.url = serverchan.data.url.trim();
|
||||
if (!isValidURL(serverchan.data.url)) {
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: t("common.errmsg.url_invalid"),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -116,14 +116,14 @@ const ServerChan = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -144,7 +144,7 @@ const ServerChan = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -167,7 +167,7 @@ const ServerChan = () => {
|
||||
setServerChan(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -180,10 +180,10 @@ const ServerChan = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -225,7 +225,7 @@ const ServerChan = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -6,9 +6,9 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { NotifyChannels, NotifyChannelTelegram } from "@/domain/settings";
|
||||
import { update } from "@/repository/settings";
|
||||
import { save } from "@/repository/settings";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
import Show from "@/components/Show";
|
||||
@ -96,7 +96,7 @@ const Telegram = () => {
|
||||
|
||||
const handleSaveClick = async () => {
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -109,14 +109,14 @@ const Telegram = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -137,7 +137,7 @@ const Telegram = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -160,7 +160,7 @@ const Telegram = () => {
|
||||
setTelegram(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -173,10 +173,10 @@ const Telegram = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -238,7 +238,7 @@ const Telegram = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -6,10 +6,10 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { getErrMessage } from "@/lib/error";
|
||||
import { isValidURL } from "@/lib/url";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
import { isValidURL } from "@/utils/url";
|
||||
import { NotifyChannels, NotifyChannelWebhook } from "@/domain/settings";
|
||||
import { update } from "@/repository/settings";
|
||||
import { save } from "@/repository/settings";
|
||||
import { useNotifyContext } from "@/providers/notify";
|
||||
import { notifyTest } from "@/api/notify";
|
||||
import Show from "@/components/Show";
|
||||
@ -96,14 +96,14 @@ const Webhook = () => {
|
||||
webhook.data.url = webhook.data.url.trim();
|
||||
if (!isValidURL(webhook.data.url)) {
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: t("common.errmsg.url_invalid"),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -116,14 +116,14 @@ const Webhook = () => {
|
||||
|
||||
setChannels(resp);
|
||||
toast({
|
||||
title: t("common.save.succeeded.message"),
|
||||
title: t("common.text.operation_succeeded"),
|
||||
description: t("settings.notification.config.saved.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -144,7 +144,7 @@ const Webhook = () => {
|
||||
description: t("settings.notification.push_test_message.succeeded.message"),
|
||||
});
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("settings.notification.push_test_message.failed.message"),
|
||||
@ -167,7 +167,7 @@ const Webhook = () => {
|
||||
setWebhook(newData);
|
||||
|
||||
try {
|
||||
const resp = await update({
|
||||
const resp = await save({
|
||||
...config,
|
||||
name: "notifyChannels",
|
||||
content: {
|
||||
@ -180,10 +180,10 @@ const Webhook = () => {
|
||||
|
||||
setChannels(resp);
|
||||
} catch (e) {
|
||||
const msg = getErrMessage(e);
|
||||
const msg = getErrMsg(e);
|
||||
|
||||
toast({
|
||||
title: t("common.save.failed.message"),
|
||||
title: t("common.text.operation_failed"),
|
||||
description: `${t("settings.notification.config.failed.message")}: ${msg}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
@ -225,7 +225,7 @@ const Webhook = () => {
|
||||
handleSaveClick();
|
||||
}}
|
||||
>
|
||||
{t("common.save")}
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Show>
|
||||
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
|
@ -1,94 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root;
|
||||
|
||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
||||
|
||||
const AlertDialogPortal = AlertDialogPrimitive.Portal;
|
||||
|
||||
const AlertDialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
||||
|
||||
const AlertDialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
));
|
||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
||||
|
||||
const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
|
||||
);
|
||||
AlertDialogHeader.displayName = "AlertDialogHeader";
|
||||
|
||||
const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
|
||||
);
|
||||
AlertDialogFooter.displayName = "AlertDialogFooter";
|
||||
|
||||
const AlertDialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => <AlertDialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold", className)} {...props} />);
|
||||
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
||||
|
||||
const AlertDialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => <AlertDialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />);
|
||||
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
||||
|
||||
const AlertDialogAction = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
||||
>(({ className, ...props }, ref) => <AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />);
|
||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
||||
|
||||
const AlertDialogCancel = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Cancel ref={ref} className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)} {...props} />
|
||||
));
|
||||
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
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",
|
||||
|
@ -1,29 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
@ -1,57 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Breadcrumb = React.forwardRef<
|
||||
HTMLElement,
|
||||
React.ComponentPropsWithoutRef<"nav"> & {
|
||||
separator?: React.ReactNode;
|
||||
}
|
||||
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
|
||||
Breadcrumb.displayName = "Breadcrumb";
|
||||
|
||||
const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWithoutRef<"ol">>(({ className, ...props }, ref) => (
|
||||
<ol ref={ref} className={cn("flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", className)} {...props} />
|
||||
));
|
||||
BreadcrumbList.displayName = "BreadcrumbList";
|
||||
|
||||
const BreadcrumbItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<"li">>(({ className, ...props }, ref) => (
|
||||
<li ref={ref} className={cn("inline-flex items-center gap-1.5", className)} {...props} />
|
||||
));
|
||||
BreadcrumbItem.displayName = "BreadcrumbItem";
|
||||
|
||||
const BreadcrumbLink = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
React.ComponentPropsWithoutRef<"a"> & {
|
||||
asChild?: boolean;
|
||||
}
|
||||
>(({ asChild, className, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "a";
|
||||
|
||||
return <Comp ref={ref} className={cn("transition-colors hover:text-foreground", className)} {...props} />;
|
||||
});
|
||||
BreadcrumbLink.displayName = "BreadcrumbLink";
|
||||
|
||||
const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWithoutRef<"span">>(({ className, ...props }, ref) => (
|
||||
<span ref={ref} role="link" aria-disabled="true" aria-current="page" className={cn("font-normal text-foreground", className)} {...props} />
|
||||
));
|
||||
BreadcrumbPage.displayName = "BreadcrumbPage";
|
||||
|
||||
const BreadcrumbSeparator = ({ children, className, ...props }: React.ComponentProps<"li">) => (
|
||||
<li role="presentation" aria-hidden="true" className={cn("[&>svg]:size-3.5", className)} {...props}>
|
||||
{children ?? <ChevronRight />}
|
||||
</li>
|
||||
);
|
||||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
|
||||
|
||||
const BreadcrumbEllipsis = ({ className, ...props }: React.ComponentProps<"span">) => (
|
||||
<span role="presentation" aria-hidden="true" className={cn("flex h-9 w-9 items-center justify-center", className)} {...props}>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
);
|
||||
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
|
||||
|
||||
export { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis };
|
@ -3,7 +3,7 @@ import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
|
@ -1,35 +0,0 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
|
||||
));
|
||||
Card.displayName = "Card";
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
||||
));
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(({ className, ...props }, ref) => (
|
||||
<h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
|
||||
));
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(({ className, ...props }, ref) => (
|
||||
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
||||
));
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
));
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
||||
));
|
||||
CardFooter.displayName = "CardFooter";
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Collapsible = CollapsiblePrimitive.Root;
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { type DialogProps } from "@radix-ui/react-dialog";
|
||||
import { Command as CommandPrimitive } from "cmdk";
|
||||
import { Search } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
|
||||
const Command = React.forwardRef<React.ElementRef<typeof CommandPrimitive>, React.ComponentPropsWithoutRef<typeof CommandPrimitive>>(
|
||||
|
@ -1,122 +1,77 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { X } from "lucide-react"
|
||||
import * as React from "react";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Dialog = DialogPrimitive.Root
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = DialogPrimitive.Portal
|
||||
const DialogPortal = DialogPrimitive.Portal;
|
||||
|
||||
const DialogClose = DialogPrimitive.Close
|
||||
const DialogClose = DialogPrimitive.Close;
|
||||
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
const DialogOverlay = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Overlay>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
))
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||
/>
|
||||
)
|
||||
);
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DialogHeader.displayName = "DialogHeader"
|
||||
const DialogContent = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Content>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
);
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
const DialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DialogFooter.displayName = "DialogFooter"
|
||||
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
|
||||
);
|
||||
DialogHeader.displayName = "DialogHeader";
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
||||
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
|
||||
);
|
||||
DialogFooter.displayName = "DialogFooter";
|
||||
|
||||
const DialogTitle = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Title>, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold leading-none tracking-tight", className)} {...props} />
|
||||
)
|
||||
);
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
||||
|
||||
const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||
>(({ className, ...props }, ref) => <DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />);
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogClose,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
}
|
||||
export { Dialog, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription };
|
||||
|
@ -1,62 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { Drawer as DrawerPrimitive } from "vaul";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Drawer = ({ shouldScaleBackground = true, ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
||||
<DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />
|
||||
);
|
||||
Drawer.displayName = "Drawer";
|
||||
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger;
|
||||
|
||||
const DrawerPortal = DrawerPrimitive.Portal;
|
||||
|
||||
const DrawerClose = DrawerPrimitive.Close;
|
||||
|
||||
const DrawerOverlay = React.forwardRef<React.ElementRef<typeof DrawerPrimitive.Overlay>, React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>>(
|
||||
({ className, ...props }, ref) => <DrawerPrimitive.Overlay ref={ref} className={cn("fixed inset-0 z-50 bg-black/80", className)} {...props} />
|
||||
);
|
||||
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
||||
|
||||
const DrawerContent = React.forwardRef<React.ElementRef<typeof DrawerPrimitive.Content>, React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn("fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background", className)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
)
|
||||
);
|
||||
DrawerContent.displayName = "DrawerContent";
|
||||
|
||||
const DrawerHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)} {...props} />
|
||||
);
|
||||
DrawerHeader.displayName = "DrawerHeader";
|
||||
|
||||
const DrawerFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />
|
||||
);
|
||||
DrawerFooter.displayName = "DrawerFooter";
|
||||
|
||||
const DrawerTitle = React.forwardRef<React.ElementRef<typeof DrawerPrimitive.Title>, React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Title ref={ref} className={cn("text-lg font-semibold leading-none tracking-tight", className)} {...props} />
|
||||
)
|
||||
);
|
||||
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
||||
|
||||
const DrawerDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => <DrawerPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />);
|
||||
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
||||
|
||||
export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription };
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
import { Check, ChevronRight, Circle } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
|
||||
|
@ -4,8 +4,8 @@ import { useTranslation } from "react-i18next";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
|
||||
import { cn } from "./utils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
|
||||
|
||||
|
@ -1,104 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { cva } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const NavigationMenu = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Root ref={ref} className={cn("relative z-10 flex max-w-max flex-1 items-center justify-center", className)} {...props}>
|
||||
{children}
|
||||
<NavigationMenuViewport />
|
||||
</NavigationMenuPrimitive.Root>
|
||||
));
|
||||
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;
|
||||
|
||||
const NavigationMenuList = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.List ref={ref} className={cn("group flex flex-1 list-none items-center justify-center space-x-1", className)} {...props} />
|
||||
));
|
||||
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
|
||||
|
||||
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
|
||||
);
|
||||
|
||||
const NavigationMenuTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Trigger ref={ref} className={cn(navigationMenuTriggerStyle(), "group", className)} {...props}>
|
||||
{children} <ChevronDown className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180" aria-hidden="true" />
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
));
|
||||
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;
|
||||
|
||||
const NavigationMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;
|
||||
|
||||
const NavigationMenuLink = NavigationMenuPrimitive.Link;
|
||||
|
||||
const NavigationMenuViewport = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className={cn("absolute left-0 top-full flex justify-center")}>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
className={cn(
|
||||
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName;
|
||||
|
||||
const NavigationMenuIndicator = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
));
|
||||
NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName;
|
||||
|
||||
export {
|
||||
navigationMenuTriggerStyle,
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
|
||||
|
||||
import { ButtonProps, buttonVariants } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
||||
<nav role="navigation" aria-label="pagination" className={cn("mx-auto flex w-full justify-center", className)} {...props} />
|
||||
);
|
||||
Pagination.displayName = "Pagination";
|
||||
|
||||
const PaginationContent = React.forwardRef<HTMLUListElement, React.ComponentProps<"ul">>(({ className, ...props }, ref) => (
|
||||
<ul ref={ref} className={cn("flex flex-row items-center gap-1", className)} {...props} />
|
||||
));
|
||||
PaginationContent.displayName = "PaginationContent";
|
||||
|
||||
const PaginationItem = React.forwardRef<HTMLLIElement, React.ComponentProps<"li">>(({ className, ...props }, ref) => (
|
||||
<li ref={ref} className={cn("", className)} {...props} />
|
||||
));
|
||||
PaginationItem.displayName = "PaginationItem";
|
||||
|
||||
type PaginationLinkProps = {
|
||||
isActive?: boolean;
|
||||
} & Pick<ButtonProps, "size"> &
|
||||
React.ComponentProps<"a">;
|
||||
|
||||
const PaginationLink = ({ className, isActive, size = "icon", ...props }: PaginationLinkProps) => (
|
||||
<a
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: isActive ? "outline" : "ghost",
|
||||
size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
PaginationLink.displayName = "PaginationLink";
|
||||
|
||||
const PaginationPrevious = ({ className, ...props }: React.ComponentProps<typeof PaginationLink>) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<PaginationLink aria-label="Go to previous page" size="default" className={cn("gap-1 pl-2.5", className)} {...props}>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
<span>{t("common.pagination.prev")}</span>
|
||||
</PaginationLink>
|
||||
);
|
||||
};
|
||||
PaginationPrevious.displayName = "PaginationPrevious";
|
||||
|
||||
const PaginationNext = ({ className, ...props }: React.ComponentProps<typeof PaginationLink>) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<PaginationLink aria-label="Go to next page" size="default" className={cn("gap-1 pr-2.5", className)} {...props}>
|
||||
<span>{t("common.pagination.next")}</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</PaginationLink>
|
||||
);
|
||||
};
|
||||
PaginationNext.displayName = "PaginationNext";
|
||||
|
||||
const PaginationEllipsis = ({ className, ...props }: React.ComponentProps<"span">) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<span aria-hidden className={cn("flex h-9 w-9 items-center justify-center", className)} {...props}>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">{t("common.pagination.more")}</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
PaginationEllipsis.displayName = "PaginationEllipsis";
|
||||
|
||||
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious };
|
@ -1,29 +1,28 @@
|
||||
import * as React from "react"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
import * as React from "react";
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Popover = PopoverPrimitive.Root
|
||||
const Popover = PopoverPrimitive.Root;
|
||||
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
))
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
||||
const PopoverContent = React.forwardRef<React.ElementRef<typeof PopoverPrimitive.Content>, React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>>(
|
||||
({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
)
|
||||
);
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent }
|
||||
export { Popover, PopoverTrigger, PopoverContent };
|
||||
|
@ -1,15 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Progress = React.forwardRef<React.ElementRef<typeof ProgressPrimitive.Root>, React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>>(
|
||||
({ className, value, ...props }, ref) => (
|
||||
<ProgressPrimitive.Root ref={ref} className={cn("relative h-4 w-full overflow-hidden rounded-full bg-secondary", className)} {...props}>
|
||||
<ProgressPrimitive.Indicator className="h-full w-full flex-1 bg-primary transition-all" style={{ transform: `translateX(-${100 - (value || 0)}%)` }} />
|
||||
</ProgressPrimitive.Root>
|
||||
)
|
||||
);
|
||||
Progress.displayName = ProgressPrimitive.Root.displayName;
|
||||
|
||||
export { Progress };
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import { Circle } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const RadioGroup = React.forwardRef<React.ElementRef<typeof RadioGroupPrimitive.Root>, React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>>(
|
||||
({ className, ...props }, ref) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const ScrollArea = React.forwardRef<React.ElementRef<typeof ScrollAreaPrimitive.Root>, React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Select = SelectPrimitive.Root;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
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) => (
|
||||
|
@ -3,7 +3,7 @@ import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Sheet = SheetPrimitive.Root;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Switch = React.forwardRef<React.ElementRef<typeof SwitchPrimitives.Root>, React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>>(
|
||||
({ className, ...props }, ref) => (
|
||||
|
@ -1,117 +1,47 @@
|
||||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(({ className, ...props }, ref) => (
|
||||
<div className="relative w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
<table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
|
||||
</div>
|
||||
))
|
||||
Table.displayName = "Table"
|
||||
));
|
||||
Table.displayName = "Table";
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
||||
))
|
||||
TableHeader.displayName = "TableHeader"
|
||||
));
|
||||
TableHeader.displayName = "TableHeader";
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableBody.displayName = "TableBody"
|
||||
const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(({ className, ...props }, ref) => (
|
||||
<tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
|
||||
));
|
||||
TableBody.displayName = "TableBody";
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableFooter.displayName = "TableFooter"
|
||||
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(({ className, ...props }, ref) => (
|
||||
<tfoot ref={ref} className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)} {...props} />
|
||||
));
|
||||
TableFooter.displayName = "TableFooter";
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableRow.displayName = "TableRow"
|
||||
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(({ className, ...props }, ref) => (
|
||||
<tr ref={ref} className={cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className)} {...props} />
|
||||
));
|
||||
TableRow.displayName = "TableRow";
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableHead.displayName = "TableHead"
|
||||
const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(({ className, ...props }, ref) => (
|
||||
<th ref={ref} className={cn("h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", className)} {...props} />
|
||||
));
|
||||
TableHead.displayName = "TableHead";
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCell.displayName = "TableCell"
|
||||
const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(({ className, ...props }, ref) => (
|
||||
<td ref={ref} className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)} {...props} />
|
||||
));
|
||||
TableCell.displayName = "TableCell";
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCaption.displayName = "TableCaption"
|
||||
const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(({ className, ...props }, ref) => (
|
||||
<caption ref={ref} className={cn("mt-4 text-sm text-muted-foreground", className)} {...props} />
|
||||
));
|
||||
TableCaption.displayName = "TableCaption";
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const Tabs = TabsPrimitive.Root;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import * as ToastPrimitives from "@radix-ui/react-toast";
|
||||
import { X } from "lucide-react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const ToastProvider = ToastPrimitives.Provider;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn } from "./utils";
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React, { useEffect } from "react";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { accessProvidersMap } from "@/domain/access";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
import { deployTargetsMap } from "@/domain/domain";
|
||||
|
||||
type AccessSelectProps = {
|
||||
@ -13,9 +13,7 @@ type AccessSelectProps = {
|
||||
const AccessSelect = ({ value, onValueChange, providerType }: AccessSelectProps) => {
|
||||
const [localValue, setLocalValue] = React.useState<string>("");
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
config: { accesses },
|
||||
} = useConfigContext();
|
||||
const { accesses } = useAccessStore();
|
||||
|
||||
useEffect(() => {
|
||||
setLocalValue(value);
|
||||
|
@ -3,7 +3,7 @@ import { Plus } from "lucide-react";
|
||||
import { BrandNodeProps, NodeProps } from "./types";
|
||||
|
||||
import { newWorkflowNode, workflowNodeDropdownList, WorkflowNodeType } from "@/domain/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/providers/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/stores/workflow";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { memo } from "react";
|
||||
|
||||
import { memo, useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import z from "zod";
|
||||
@ -16,13 +15,12 @@ import EmailsEdit from "@/components/certimate/EmailsEdit";
|
||||
import StringList from "@/components/certimate/StringList";
|
||||
|
||||
import { accessProvidersMap } from "@/domain/access";
|
||||
import { EmailsSetting } from "@/domain/settings";
|
||||
|
||||
import { useConfigContext } from "@/providers/config";
|
||||
import { useAccessStore } from "@/stores/access";
|
||||
import { useContactStore } from "@/stores/contact";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { TooltipFast } from "@/components/ui/tooltip";
|
||||
import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/providers/workflow";
|
||||
import { useWorkflowStore, WorkflowState } from "@/stores/workflow";
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { usePanel } from "./PanelProvider";
|
||||
|
||||
@ -35,9 +33,12 @@ const selectState = (state: WorkflowState) => ({
|
||||
const ApplyForm = ({ data }: ApplyFormProps) => {
|
||||
const { updateNode } = useWorkflowStore(useShallow(selectState));
|
||||
|
||||
const {
|
||||
config: { accesses, emails },
|
||||
} = useConfigContext();
|
||||
const { accesses } = useAccessStore();
|
||||
const { emails, fetchEmails } = useContactStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchEmails();
|
||||
}, []);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -122,7 +123,7 @@ const ApplyForm = ({ data }: ApplyFormProps) => {
|
||||
trigger={
|
||||
<div className="flex items-center font-normal cursor-pointer text-primary hover:underline">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
{t("common.button.add")}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@ -141,7 +142,7 @@ const ApplyForm = ({ data }: ApplyFormProps) => {
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>{t("domain.application.form.email.list")}</SelectLabel>
|
||||
{(emails.content as EmailsSetting).emails.map((item) => (
|
||||
{emails.map((item) => (
|
||||
<SelectItem key={item} value={item}>
|
||||
<div>{item}</div>
|
||||
</SelectItem>
|
||||
@ -168,7 +169,7 @@ const ApplyForm = ({ data }: ApplyFormProps) => {
|
||||
trigger={
|
||||
<div className="flex items-center font-normal cursor-pointer text-primary hover:underline">
|
||||
<Plus size={14} />
|
||||
{t("common.add")}
|
||||
{t("common.button.add")}
|
||||
</div>
|
||||
}
|
||||
op="add"
|
||||
@ -342,7 +343,7 @@ const ApplyForm = ({ data }: ApplyFormProps) => {
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit">{t("common.save")}</Button>
|
||||
<Button type="submit">{t("common.button.save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user