mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Fix: Modify the style of the user center ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -62,6 +62,7 @@ const AddDataSourceModal = ({
|
||||
sourceData?.id as keyof typeof DataSourceFormDefaultValues
|
||||
] as FieldValues
|
||||
}
|
||||
labelClassName="font-normal"
|
||||
>
|
||||
<div className=" absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6">
|
||||
<DynamicForm.CancelButton
|
||||
|
||||
@ -1,725 +0,0 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useMemo } from 'react';
|
||||
import {
|
||||
DefaultValues,
|
||||
FieldValues,
|
||||
SubmitHandler,
|
||||
useForm,
|
||||
useFormContext,
|
||||
} from 'react-hook-form';
|
||||
import { ZodSchema, z } from 'zod';
|
||||
|
||||
import EditTag from '@/components/edit-tag';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { t } from 'i18next';
|
||||
import { Loader } from 'lucide-react';
|
||||
|
||||
// Field type enumeration
|
||||
export enum FormFieldType {
|
||||
Text = 'text',
|
||||
Email = 'email',
|
||||
Password = 'password',
|
||||
Number = 'number',
|
||||
Textarea = 'textarea',
|
||||
Select = 'select',
|
||||
Checkbox = 'checkbox',
|
||||
Tag = 'tag',
|
||||
}
|
||||
|
||||
// Field configuration interface
|
||||
export interface FormFieldConfig {
|
||||
name: string;
|
||||
label: string;
|
||||
type: FormFieldType;
|
||||
hidden?: boolean;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
options?: { label: string; value: string }[];
|
||||
defaultValue?: any;
|
||||
validation?: {
|
||||
pattern?: RegExp;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
message?: string;
|
||||
};
|
||||
render?: (fieldProps: any) => React.ReactNode;
|
||||
horizontal?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
}
|
||||
|
||||
// Component props interface
|
||||
interface DynamicFormProps<T extends FieldValues> {
|
||||
fields: FormFieldConfig[];
|
||||
onSubmit: SubmitHandler<T>;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
defaultValues?: DefaultValues<T>;
|
||||
}
|
||||
|
||||
// Form ref interface
|
||||
export interface DynamicFormRef {
|
||||
submit: () => void;
|
||||
getValues: () => any;
|
||||
reset: (values?: any) => void;
|
||||
}
|
||||
|
||||
// Generate Zod validation schema based on field configurations
|
||||
const generateSchema = (fields: FormFieldConfig[]): ZodSchema<any> => {
|
||||
const schema: Record<string, ZodSchema> = {};
|
||||
const nestedSchemas: Record<string, Record<string, ZodSchema>> = {};
|
||||
|
||||
fields.forEach((field) => {
|
||||
let fieldSchema: ZodSchema;
|
||||
|
||||
// Create base validation schema based on field type
|
||||
switch (field.type) {
|
||||
case FormFieldType.Email:
|
||||
fieldSchema = z.string().email('Please enter a valid email address');
|
||||
break;
|
||||
case FormFieldType.Number:
|
||||
fieldSchema = z.coerce.number();
|
||||
if (field.validation?.min !== undefined) {
|
||||
fieldSchema = (fieldSchema as z.ZodNumber).min(
|
||||
field.validation.min,
|
||||
field.validation.message ||
|
||||
`Value cannot be less than ${field.validation.min}`,
|
||||
);
|
||||
}
|
||||
if (field.validation?.max !== undefined) {
|
||||
fieldSchema = (fieldSchema as z.ZodNumber).max(
|
||||
field.validation.max,
|
||||
field.validation.message ||
|
||||
`Value cannot be greater than ${field.validation.max}`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case FormFieldType.Checkbox:
|
||||
fieldSchema = z.boolean();
|
||||
break;
|
||||
case FormFieldType.Tag:
|
||||
fieldSchema = z.array(z.string());
|
||||
break;
|
||||
default:
|
||||
fieldSchema = z.string();
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle required fields
|
||||
if (field.required) {
|
||||
if (field.type === FormFieldType.Checkbox) {
|
||||
fieldSchema = (fieldSchema as z.ZodBoolean).refine(
|
||||
(val) => val === true,
|
||||
{
|
||||
message: `${field.label} is required`,
|
||||
},
|
||||
);
|
||||
} else if (field.type === FormFieldType.Tag) {
|
||||
fieldSchema = (fieldSchema as z.ZodArray<z.ZodString>).min(1, {
|
||||
message: `${field.label} is required`,
|
||||
});
|
||||
} else {
|
||||
fieldSchema = (fieldSchema as z.ZodString).min(1, {
|
||||
message: `${field.label} is required`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!field.required) {
|
||||
fieldSchema = fieldSchema.optional();
|
||||
}
|
||||
|
||||
// Handle other validation rules
|
||||
if (
|
||||
field.type !== FormFieldType.Number &&
|
||||
field.type !== FormFieldType.Checkbox &&
|
||||
field.type !== FormFieldType.Tag &&
|
||||
field.required
|
||||
) {
|
||||
fieldSchema = fieldSchema as z.ZodString;
|
||||
|
||||
if (field.validation?.minLength !== undefined) {
|
||||
fieldSchema = (fieldSchema as z.ZodString).min(
|
||||
field.validation.minLength,
|
||||
field.validation.message ||
|
||||
`Enter at least ${field.validation.minLength} characters`,
|
||||
);
|
||||
}
|
||||
|
||||
if (field.validation?.maxLength !== undefined) {
|
||||
fieldSchema = (fieldSchema as z.ZodString).max(
|
||||
field.validation.maxLength,
|
||||
field.validation.message ||
|
||||
`Enter up to ${field.validation.maxLength} characters`,
|
||||
);
|
||||
}
|
||||
|
||||
if (field.validation?.pattern) {
|
||||
fieldSchema = (fieldSchema as z.ZodString).regex(
|
||||
field.validation.pattern,
|
||||
field.validation.message || 'Invalid input format',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (field.name.includes('.')) {
|
||||
const keys = field.name.split('.');
|
||||
const firstKey = keys[0];
|
||||
|
||||
if (!nestedSchemas[firstKey]) {
|
||||
nestedSchemas[firstKey] = {};
|
||||
}
|
||||
|
||||
let currentSchema = nestedSchemas[firstKey];
|
||||
for (let i = 1; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
if (!currentSchema[key]) {
|
||||
currentSchema[key] = {};
|
||||
}
|
||||
currentSchema = currentSchema[key];
|
||||
}
|
||||
|
||||
const lastKey = keys[keys.length - 1];
|
||||
currentSchema[lastKey] = fieldSchema;
|
||||
} else {
|
||||
schema[field.name] = fieldSchema;
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(nestedSchemas).forEach((key) => {
|
||||
const buildNestedSchema = (obj: Record<string, any>): ZodSchema => {
|
||||
const nestedSchema: Record<string, ZodSchema> = {};
|
||||
Object.keys(obj).forEach((subKey) => {
|
||||
if (
|
||||
typeof obj[subKey] === 'object' &&
|
||||
!(obj[subKey] instanceof z.ZodType)
|
||||
) {
|
||||
nestedSchema[subKey] = buildNestedSchema(obj[subKey]);
|
||||
} else {
|
||||
nestedSchema[subKey] = obj[subKey];
|
||||
}
|
||||
});
|
||||
return z.object(nestedSchema);
|
||||
};
|
||||
|
||||
schema[key] = buildNestedSchema(nestedSchemas[key]);
|
||||
});
|
||||
return z.object(schema);
|
||||
};
|
||||
|
||||
// Generate default values based on field configurations
|
||||
const generateDefaultValues = <T extends FieldValues>(
|
||||
fields: FormFieldConfig[],
|
||||
): DefaultValues<T> => {
|
||||
const defaultValues: Record<string, any> = {};
|
||||
|
||||
fields.forEach((field) => {
|
||||
if (field.name.includes('.')) {
|
||||
const keys = field.name.split('.');
|
||||
let current = defaultValues;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
if (!current[key]) {
|
||||
current[key] = {};
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
const lastKey = keys[keys.length - 1];
|
||||
if (field.defaultValue !== undefined) {
|
||||
current[lastKey] = field.defaultValue;
|
||||
} else if (field.type === FormFieldType.Checkbox) {
|
||||
current[lastKey] = false;
|
||||
} else if (field.type === FormFieldType.Tag) {
|
||||
current[lastKey] = [];
|
||||
} else {
|
||||
current[lastKey] = '';
|
||||
}
|
||||
} else {
|
||||
if (field.defaultValue !== undefined) {
|
||||
defaultValues[field.name] = field.defaultValue;
|
||||
} else if (field.type === FormFieldType.Checkbox) {
|
||||
defaultValues[field.name] = false;
|
||||
} else if (field.type === FormFieldType.Tag) {
|
||||
defaultValues[field.name] = [];
|
||||
} else {
|
||||
defaultValues[field.name] = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return defaultValues as DefaultValues<T>;
|
||||
};
|
||||
|
||||
// Dynamic form component
|
||||
const DynamicForm = {
|
||||
Root: forwardRef(
|
||||
<T extends FieldValues>(
|
||||
{
|
||||
fields,
|
||||
onSubmit,
|
||||
className = '',
|
||||
children,
|
||||
defaultValues: formDefaultValues = {} as DefaultValues<T>,
|
||||
}: DynamicFormProps<T>,
|
||||
ref: React.Ref<any>,
|
||||
) => {
|
||||
// Generate validation schema and default values
|
||||
const schema = useMemo(() => generateSchema(fields), [fields]);
|
||||
|
||||
const defaultValues = useMemo(() => {
|
||||
const value = {
|
||||
...generateDefaultValues(fields),
|
||||
...formDefaultValues,
|
||||
};
|
||||
console.log('generateDefaultValues', fields, value);
|
||||
return value;
|
||||
}, [fields, formDefaultValues]);
|
||||
|
||||
// Initialize form
|
||||
const form = useForm<T>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
// Expose form methods via ref
|
||||
useImperativeHandle(ref, () => ({
|
||||
submit: () => form.handleSubmit(onSubmit)(),
|
||||
getValues: () => form.getValues(),
|
||||
reset: (values?: T) => {
|
||||
if (values) {
|
||||
form.reset(values);
|
||||
} else {
|
||||
form.reset();
|
||||
}
|
||||
},
|
||||
setError: form.setError,
|
||||
clearErrors: form.clearErrors,
|
||||
trigger: form.trigger,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (formDefaultValues && Object.keys(formDefaultValues).length > 0) {
|
||||
form.reset({
|
||||
...generateDefaultValues(fields),
|
||||
...formDefaultValues,
|
||||
});
|
||||
}
|
||||
}, [form, formDefaultValues, fields]);
|
||||
|
||||
// Submit handler
|
||||
// const handleSubmit = form.handleSubmit(onSubmit);
|
||||
|
||||
// Render form fields
|
||||
const renderField = (field: FormFieldConfig) => {
|
||||
if (field.render) {
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
required={field.required}
|
||||
horizontal={field.horizontal}
|
||||
>
|
||||
{(fieldProps) => {
|
||||
const finalFieldProps = field.onChange
|
||||
? {
|
||||
...fieldProps,
|
||||
onChange: (e: any) => {
|
||||
fieldProps.onChange(e);
|
||||
field.onChange?.(e.target?.value ?? e);
|
||||
},
|
||||
}
|
||||
: fieldProps;
|
||||
return field.render?.(finalFieldProps);
|
||||
}}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
}
|
||||
switch (field.type) {
|
||||
case FormFieldType.Textarea:
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
required={field.required}
|
||||
horizontal={field.horizontal}
|
||||
>
|
||||
{(fieldProps) => {
|
||||
const finalFieldProps = field.onChange
|
||||
? {
|
||||
...fieldProps,
|
||||
onChange: (e: any) => {
|
||||
fieldProps.onChange(e);
|
||||
field.onChange?.(e.target.value);
|
||||
},
|
||||
}
|
||||
: fieldProps;
|
||||
return (
|
||||
<Textarea
|
||||
{...finalFieldProps}
|
||||
placeholder={field.placeholder}
|
||||
className="resize-none"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
|
||||
case FormFieldType.Select:
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
required={field.required}
|
||||
horizontal={field.horizontal}
|
||||
>
|
||||
{(fieldProps) => {
|
||||
const finalFieldProps = field.onChange
|
||||
? {
|
||||
...fieldProps,
|
||||
onChange: (value: string) => {
|
||||
console.log('select value', value);
|
||||
if (fieldProps.onChange) {
|
||||
fieldProps.onChange(value);
|
||||
}
|
||||
field.onChange?.(value);
|
||||
},
|
||||
}
|
||||
: fieldProps;
|
||||
return (
|
||||
<SelectWithSearch
|
||||
triggerClassName="!shrink"
|
||||
{...finalFieldProps}
|
||||
options={field.options}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
|
||||
case FormFieldType.Checkbox:
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={field.name as any}
|
||||
render={({ field: formField }) => (
|
||||
<FormItem
|
||||
className={cn('flex items-center', {
|
||||
'flex-row items-start space-x-3 space-y-0':
|
||||
!field.horizontal,
|
||||
})}
|
||||
>
|
||||
{field.label && !field.horizontal && (
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel className="font-normal">
|
||||
{field.label}{' '}
|
||||
{field.required && (
|
||||
<span className="text-destructive">*</span>
|
||||
)}
|
||||
</FormLabel>
|
||||
</div>
|
||||
)}
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={formField.value}
|
||||
onCheckedChange={(checked) => {
|
||||
formField.onChange(checked);
|
||||
field.onChange?.(checked);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
{field.label && field.horizontal && (
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel className="font-normal">
|
||||
{field.label}{' '}
|
||||
{field.required && (
|
||||
<span className="text-destructive">*</span>
|
||||
)}
|
||||
</FormLabel>
|
||||
</div>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
case FormFieldType.Tag:
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
required={field.required}
|
||||
horizontal={field.horizontal}
|
||||
>
|
||||
{(fieldProps) => {
|
||||
const finalFieldProps = field.onChange
|
||||
? {
|
||||
...fieldProps,
|
||||
onChange: (value: string[]) => {
|
||||
fieldProps.onChange(value);
|
||||
field.onChange?.(value);
|
||||
},
|
||||
}
|
||||
: fieldProps;
|
||||
return (
|
||||
// <TagInput {...fieldProps} placeholder={field.placeholder} />
|
||||
<div className="w-full">
|
||||
<EditTag {...finalFieldProps}></EditTag>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
required={field.required}
|
||||
horizontal={field.horizontal}
|
||||
>
|
||||
{(fieldProps) => {
|
||||
const finalFieldProps = field.onChange
|
||||
? {
|
||||
...fieldProps,
|
||||
onChange: (e: any) => {
|
||||
fieldProps.onChange(e);
|
||||
field.onChange?.(e.target.value);
|
||||
},
|
||||
}
|
||||
: fieldProps;
|
||||
return (
|
||||
<Input
|
||||
{...finalFieldProps}
|
||||
type={field.type}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className={`space-y-6 ${className}`}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
form.handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{fields.map((field) => (
|
||||
<div key={field.name} className={cn({ hidden: field.hidden })}>
|
||||
{renderField(field)}
|
||||
</div>
|
||||
))}
|
||||
{children}
|
||||
</>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
) as <T extends FieldValues>(
|
||||
props: DynamicFormProps<T> & { ref?: React.Ref<DynamicFormRef> },
|
||||
) => React.ReactElement,
|
||||
|
||||
SavingButton: ({
|
||||
submitLoading,
|
||||
buttonText,
|
||||
submitFunc,
|
||||
}: {
|
||||
submitLoading: boolean;
|
||||
buttonText?: string;
|
||||
submitFunc?: (values: FieldValues) => void;
|
||||
}) => {
|
||||
const form = useFormContext();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
disabled={submitLoading}
|
||||
onClick={() => {
|
||||
console.log('form submit');
|
||||
(async () => {
|
||||
console.log('form submit2');
|
||||
try {
|
||||
let beValid = await form.formControl.trigger();
|
||||
console.log('form valid', beValid, form, form.formControl);
|
||||
if (beValid) {
|
||||
form.handleSubmit(async (values) => {
|
||||
console.log('form values', values);
|
||||
submitFunc?.(values);
|
||||
})();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
console.log('form submit3');
|
||||
}
|
||||
})();
|
||||
}}
|
||||
className={cn(
|
||||
'px-2 py-1 bg-primary text-primary-foreground rounded-md hover:bg-primary/90',
|
||||
)}
|
||||
>
|
||||
{submitLoading && (
|
||||
<Loader className="inline-block mr-2 h-4 w-4 animate-spin" />
|
||||
)}
|
||||
{buttonText ?? t('modal.okText')}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
CancelButton: ({
|
||||
handleCancel,
|
||||
cancelText,
|
||||
}: {
|
||||
handleCancel: () => void;
|
||||
cancelText?: string;
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCancel()}
|
||||
className="px-2 py-1 border border-input rounded-md hover:bg-muted"
|
||||
>
|
||||
{cancelText ?? t('modal.cancelText')}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export { DynamicForm };
|
||||
|
||||
/**
|
||||
* Usage Example 1: Basic Form
|
||||
*
|
||||
* <DynamicForm
|
||||
* fields={[
|
||||
* {
|
||||
* name: "username",
|
||||
* label: "Username",
|
||||
* type: FormFieldType.Text,
|
||||
* required: true,
|
||||
* placeholder: "Please enter username"
|
||||
* },
|
||||
* {
|
||||
* name: "email",
|
||||
* label: "Email",
|
||||
* type: FormFieldType.Email,
|
||||
* required: true,
|
||||
* placeholder: "Please enter email address"
|
||||
* }
|
||||
* ]}
|
||||
* onSubmit={(data) => {
|
||||
* console.log(data); // { username: "...", email: "..." }
|
||||
* }}
|
||||
* />
|
||||
*
|
||||
* Usage Example 2: Nested Object Form
|
||||
*
|
||||
* <DynamicForm
|
||||
* fields={[
|
||||
* {
|
||||
* name: "user.name",
|
||||
* label: "Name",
|
||||
* type: FormFieldType.Text,
|
||||
* required: true,
|
||||
* placeholder: "Please enter name"
|
||||
* },
|
||||
* {
|
||||
* name: "user.email",
|
||||
* label: "Email",
|
||||
* type: FormFieldType.Email,
|
||||
* required: true,
|
||||
* placeholder: "Please enter email address"
|
||||
* },
|
||||
* {
|
||||
* name: "user.profile.age",
|
||||
* label: "Age",
|
||||
* type: FormFieldType.Number,
|
||||
* required: true,
|
||||
* validation: {
|
||||
* min: 18,
|
||||
* max: 100,
|
||||
* message: "Age must be between 18 and 100"
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* name: "user.profile.bio",
|
||||
* label: "Bio",
|
||||
* type: FormFieldType.Textarea,
|
||||
* placeholder: "Please briefly introduce yourself"
|
||||
* },
|
||||
* {
|
||||
* name: "settings.notifications",
|
||||
* label: "Enable Notifications",
|
||||
* type: FormFieldType.Checkbox
|
||||
* }
|
||||
* ]}
|
||||
* onSubmit={(data) => {
|
||||
* console.log(data);
|
||||
* // {
|
||||
* // user: {
|
||||
* // name: "...",
|
||||
* // email: "...",
|
||||
* // profile: {
|
||||
* // age: ...,
|
||||
* // bio: "..."
|
||||
* // }
|
||||
* // },
|
||||
* // settings: {
|
||||
* // notifications: true/false
|
||||
* // }
|
||||
* // }
|
||||
* }}
|
||||
* />
|
||||
*
|
||||
* Usage Example 3: Tag Type Form
|
||||
*
|
||||
* <DynamicForm
|
||||
* fields={[
|
||||
* {
|
||||
* name: "skills",
|
||||
* label: "Skill Tags",
|
||||
* type: FormFieldType.Tag,
|
||||
* required: true,
|
||||
* placeholder: "Enter skill and press Enter to add tag"
|
||||
* },
|
||||
* {
|
||||
* name: "user.hobbies",
|
||||
* label: "Hobbies",
|
||||
* type: FormFieldType.Tag,
|
||||
* placeholder: "Enter hobby and press Enter to add tag"
|
||||
* }
|
||||
* ]}
|
||||
* onSubmit={(data) => {
|
||||
* console.log(data);
|
||||
* // {
|
||||
* // skills: ["JavaScript", "React", "TypeScript"],
|
||||
* // user: {
|
||||
* // hobbies: ["Reading", "Swimming", "Travel"]
|
||||
* // }
|
||||
* // }
|
||||
* }}
|
||||
* />
|
||||
*/
|
||||
@ -387,101 +387,6 @@ export const DataSourceFormFields = {
|
||||
tooltip: t('setting.jiraPasswordTip'),
|
||||
},
|
||||
],
|
||||
// [DataSourceKey.GOOGLE_DRIVE]: [
|
||||
// {
|
||||
// label: 'Primary Admin Email',
|
||||
// name: 'config.credentials.google_primary_admin',
|
||||
// type: FormFieldType.Text,
|
||||
// required: true,
|
||||
// placeholder: 'admin@example.com',
|
||||
// tooltip: t('setting.google_drivePrimaryAdminTip'),
|
||||
// },
|
||||
// {
|
||||
// label: 'OAuth Token JSON',
|
||||
// name: 'config.credentials.google_tokens',
|
||||
// type: FormFieldType.Textarea,
|
||||
// required: true,
|
||||
// render: (fieldProps) => (
|
||||
// <GoogleDriveTokenField
|
||||
// value={fieldProps.value}
|
||||
// onChange={fieldProps.onChange}
|
||||
// placeholder='{ "token": "...", "refresh_token": "...", ... }'
|
||||
// />
|
||||
// ),
|
||||
// tooltip: t('setting.google_driveTokenTip'),
|
||||
// },
|
||||
// {
|
||||
// label: 'My Drive Emails',
|
||||
// name: 'config.my_drive_emails',
|
||||
// type: FormFieldType.Text,
|
||||
// required: true,
|
||||
// placeholder: 'user1@example.com,user2@example.com',
|
||||
// tooltip: t('setting.google_driveMyDriveEmailsTip'),
|
||||
// },
|
||||
// {
|
||||
// label: 'Shared Folder URLs',
|
||||
// name: 'config.shared_folder_urls',
|
||||
// type: FormFieldType.Textarea,
|
||||
// required: true,
|
||||
// placeholder:
|
||||
// 'https://drive.google.com/drive/folders/XXXXX,https://drive.google.com/drive/folders/YYYYY',
|
||||
// tooltip: t('setting.google_driveSharedFoldersTip'),
|
||||
// },
|
||||
// // The fields below are intentionally disabled for now. Uncomment them when we
|
||||
// // reintroduce shared drive controls or advanced impersonation options.
|
||||
// // {
|
||||
// // label: 'Shared Drive URLs',
|
||||
// // name: 'config.shared_drive_urls',
|
||||
// // type: FormFieldType.Text,
|
||||
// // required: false,
|
||||
// // placeholder:
|
||||
// // 'Optional: comma-separated shared drive links if you want to include them.',
|
||||
// // },
|
||||
// // {
|
||||
// // label: 'Specific User Emails',
|
||||
// // name: 'config.specific_user_emails',
|
||||
// // type: FormFieldType.Text,
|
||||
// // required: false,
|
||||
// // placeholder:
|
||||
// // 'Optional: comma-separated list of users to impersonate (overrides defaults).',
|
||||
// // },
|
||||
// // {
|
||||
// // label: 'Include My Drive',
|
||||
// // name: 'config.include_my_drives',
|
||||
// // type: FormFieldType.Checkbox,
|
||||
// // required: false,
|
||||
// // defaultValue: true,
|
||||
// // },
|
||||
// // {
|
||||
// // label: 'Include Shared Drives',
|
||||
// // name: 'config.include_shared_drives',
|
||||
// // type: FormFieldType.Checkbox,
|
||||
// // required: false,
|
||||
// // defaultValue: false,
|
||||
// // },
|
||||
// // {
|
||||
// // label: 'Include “Shared with me”',
|
||||
// // name: 'config.include_files_shared_with_me',
|
||||
// // type: FormFieldType.Checkbox,
|
||||
// // required: false,
|
||||
// // defaultValue: false,
|
||||
// // },
|
||||
// // {
|
||||
// // label: 'Allow Images',
|
||||
// // name: 'config.allow_images',
|
||||
// // type: FormFieldType.Checkbox,
|
||||
// // required: false,
|
||||
// // defaultValue: false,
|
||||
// // },
|
||||
// {
|
||||
// label: '',
|
||||
// name: 'config.credentials.authentication_method',
|
||||
// type: FormFieldType.Text,
|
||||
// required: false,
|
||||
// hidden: true,
|
||||
// defaultValue: 'uploaded',
|
||||
// },
|
||||
// ],
|
||||
};
|
||||
|
||||
export const DataSourceFormDefaultValues = {
|
||||
|
||||
@ -67,11 +67,11 @@ const SourceDetailPage = () => {
|
||||
<div className="flex items-center gap-1 w-full relative">
|
||||
<Input {...fieldProps} type={FormFieldType.Number} />
|
||||
<span className="absolute right-0 -translate-x-[58px] text-text-secondary italic ">
|
||||
minutes
|
||||
{t('setting.minutes')}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="text-text-secondary bg-bg-input rounded-sm text-xs h-full p-2 border border-border-button "
|
||||
className="text-text-secondary bg-bg-input rounded-sm text-xs h-full p-2 border border-border-button hover:bg-border-button hover:text-text-primary"
|
||||
onClick={() => {
|
||||
runSchedule();
|
||||
}}
|
||||
@ -112,7 +112,7 @@ const SourceDetailPage = () => {
|
||||
<div className="flex items-center gap-1 w-full relative">
|
||||
<Input {...fieldProps} type={FormFieldType.Number} />
|
||||
<span className="absolute right-0 -translate-x-6 text-text-secondary italic ">
|
||||
seconds
|
||||
{t('setting.seconds')}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user