mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-24 14:34:20 +00:00
feat(dingtalk): add download link for human input card template and enhance dynamic form configuration
This commit is contained in:
@@ -268,6 +268,10 @@ export default function BotForm({
|
||||
options: item.options,
|
||||
show_if: item.show_if,
|
||||
login_platform: item.login_platform,
|
||||
url: item.url,
|
||||
download_filename: item.download_filename,
|
||||
help_links: item.help_links,
|
||||
help_label: item.help_label,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -20,9 +20,17 @@ import { useTranslation } from 'react-i18next';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Copy, Check, Globe, QrCode } from 'lucide-react';
|
||||
import {
|
||||
Copy,
|
||||
Check,
|
||||
Globe,
|
||||
QrCode,
|
||||
Download,
|
||||
ExternalLink,
|
||||
} from 'lucide-react';
|
||||
import { copyToClipboard } from '@/app/utils/clipboard';
|
||||
import { systemInfo } from '@/app/infra/http';
|
||||
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
|
||||
/**
|
||||
* Resolve the value referenced by a `show_if.field` string.
|
||||
@@ -190,6 +198,50 @@ function WebhookUrlField({
|
||||
);
|
||||
}
|
||||
|
||||
function DownloadLinkField({
|
||||
label,
|
||||
description,
|
||||
url,
|
||||
filename,
|
||||
helpUrl,
|
||||
helpLabel,
|
||||
}: {
|
||||
label: string;
|
||||
description?: string;
|
||||
url: string;
|
||||
filename?: string;
|
||||
helpUrl?: string | null;
|
||||
helpLabel: string;
|
||||
}) {
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL || window.location.origin;
|
||||
const downloadUrl = url.startsWith('http') ? url : `${baseUrl}${url}`;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<a href={downloadUrl} download={filename}>
|
||||
<Download className="h-4 w-4" />
|
||||
{label}
|
||||
</a>
|
||||
</Button>
|
||||
{helpUrl && (
|
||||
<Button asChild variant="ghost" size="sm">
|
||||
<a href={helpUrl} target="_blank" rel="noopener noreferrer">
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
{helpLabel}
|
||||
</a>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{description && (
|
||||
<p className="text-sm text-muted-foreground max-w-2xl">{description}</p>
|
||||
)}
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DynamicFormComponent({
|
||||
itemConfigList,
|
||||
onSubmit,
|
||||
@@ -215,7 +267,7 @@ export default function DynamicFormComponent({
|
||||
}) {
|
||||
const isInitialMount = useRef(true);
|
||||
const previousInitialValues = useRef(initialValues);
|
||||
const { t } = useTranslation();
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
// Normalize a form value according to its field type.
|
||||
// This ensures legacy/malformed data (e.g. a plain string for
|
||||
@@ -261,7 +313,8 @@ export default function DynamicFormComponent({
|
||||
(item) =>
|
||||
item.type !== 'webhook-url' &&
|
||||
item.type !== 'embed-code' &&
|
||||
item.type !== 'qr-code-login',
|
||||
item.type !== 'qr-code-login' &&
|
||||
item.type !== 'download-link',
|
||||
),
|
||||
[itemConfigList],
|
||||
);
|
||||
@@ -563,6 +616,30 @@ export default function DynamicFormComponent({
|
||||
);
|
||||
}
|
||||
|
||||
if (config.type === 'download-link') {
|
||||
if (!config.url) return null;
|
||||
|
||||
return (
|
||||
<DownloadLinkField
|
||||
key={config.id}
|
||||
label={extractI18nObject(config.label)}
|
||||
description={
|
||||
config.description
|
||||
? extractI18nObject(config.description)
|
||||
: undefined
|
||||
}
|
||||
url={config.url}
|
||||
filename={config.download_filename}
|
||||
helpUrl={getAdapterDocUrl(config.help_links, i18n.language)}
|
||||
helpLabel={
|
||||
config.help_label
|
||||
? extractI18nObject(config.help_label)
|
||||
: t('bots.viewAdapterDocs')
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// QR code login button (e.g. Feishu one-click create, WeChat scan login)
|
||||
if (config.type === 'qr-code-login') {
|
||||
return (
|
||||
|
||||
@@ -17,6 +17,10 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
|
||||
options?: IDynamicFormItemOption[];
|
||||
show_if?: IShowIfCondition;
|
||||
login_platform?: string;
|
||||
url?: string;
|
||||
download_filename?: string;
|
||||
help_links?: Record<string, string>;
|
||||
help_label?: I18nObject;
|
||||
|
||||
constructor(params: IDynamicFormItemSchema) {
|
||||
this.id = params.id;
|
||||
@@ -29,6 +33,10 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
|
||||
this.options = params.options;
|
||||
this.show_if = params.show_if;
|
||||
this.login_platform = params.login_platform;
|
||||
this.url = params.url;
|
||||
this.download_filename = params.download_filename;
|
||||
this.help_links = params.help_links;
|
||||
this.help_label = params.help_label;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ export interface IDynamicFormItemSchema {
|
||||
scopes?: string[];
|
||||
accept?: string; // For file type: accepted MIME types
|
||||
login_platform?: string; // For qr-code-login type: platform identifier (e.g. 'feishu', 'weixin')
|
||||
url?: string; // For download-link type: relative or absolute download URL
|
||||
download_filename?: string; // Optional filename for download-link type
|
||||
help_links?: Record<string, string>; // Optional docs links for display-only fields
|
||||
help_label?: I18nObject; // Optional label for help_links
|
||||
}
|
||||
|
||||
export enum DynamicFormItemType {
|
||||
@@ -48,6 +52,7 @@ export enum DynamicFormItemType {
|
||||
WEBHOOK_URL = 'webhook-url',
|
||||
EMBED_CODE = 'embed-code',
|
||||
QR_CODE_LOGIN = 'qr-code-login',
|
||||
DOWNLOAD_LINK = 'download-link',
|
||||
}
|
||||
|
||||
export interface IFileConfig {
|
||||
|
||||
@@ -229,6 +229,10 @@ export default function WizardPage() {
|
||||
options: item.options,
|
||||
show_if: item.show_if,
|
||||
login_platform: item.login_platform,
|
||||
url: item.url,
|
||||
download_filename: item.download_filename,
|
||||
help_links: item.help_links,
|
||||
help_label: item.help_label,
|
||||
}),
|
||||
);
|
||||
}, [adapters, selectedAdapter]);
|
||||
@@ -249,6 +253,10 @@ export default function WizardPage() {
|
||||
options: item.options,
|
||||
show_if: item.show_if,
|
||||
login_platform: item.login_platform,
|
||||
url: item.url,
|
||||
download_filename: item.download_filename,
|
||||
help_links: item.help_links,
|
||||
help_label: item.help_label,
|
||||
}),
|
||||
);
|
||||
}, [selectedRunnerConfigStage]);
|
||||
|
||||
Reference in New Issue
Block a user