mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-03 03:31:09 +02:00
Add option to select Scrape Type in the web connector UI
This commit is contained in:
parent
0c6077ee7e
commit
1abce83626
@ -6,12 +6,20 @@ import * as Yup from "yup";
|
|||||||
import { LoadingAnimation } from "@/components/Loading";
|
import { LoadingAnimation } from "@/components/Loading";
|
||||||
import { GlobeIcon } from "@/components/icons/icons";
|
import { GlobeIcon } from "@/components/icons/icons";
|
||||||
import { fetcher } from "@/lib/fetcher";
|
import { fetcher } from "@/lib/fetcher";
|
||||||
import { TextFormField } from "@/components/admin/connectors/Field";
|
import {
|
||||||
|
SelectorFormField,
|
||||||
|
TextFormField,
|
||||||
|
} from "@/components/admin/connectors/Field";
|
||||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||||
import { ConnectorIndexingStatus, WebConfig } from "@/lib/types";
|
import { ConnectorIndexingStatus, WebConfig } from "@/lib/types";
|
||||||
import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable";
|
import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable";
|
||||||
import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm";
|
import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm";
|
||||||
import { linkCredential } from "@/lib/credential";
|
|
||||||
|
const SCRAPE_TYPE_TO_PRETTY_NAME = {
|
||||||
|
recursive: "Recursive",
|
||||||
|
single: "Single Page",
|
||||||
|
sitemap: "Sitemap",
|
||||||
|
};
|
||||||
|
|
||||||
export default function Web() {
|
export default function Web() {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
@ -56,15 +64,42 @@ export default function Web() {
|
|||||||
formBody={
|
formBody={
|
||||||
<>
|
<>
|
||||||
<TextFormField name="base_url" label="URL to Index:" />
|
<TextFormField name="base_url" label="URL to Index:" />
|
||||||
|
<SelectorFormField
|
||||||
|
name="web_connector_type"
|
||||||
|
label="Scrape Method:"
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
name: "Recursive",
|
||||||
|
value: "recursive",
|
||||||
|
description:
|
||||||
|
"Recursively index all pages that share the same base URL.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Single Page",
|
||||||
|
value: "single",
|
||||||
|
description: "Index only the specified page.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sitemap",
|
||||||
|
value: "sitemap",
|
||||||
|
description:
|
||||||
|
"Assumes the URL to Index points to a Sitemap. Will try and index all pages that are a mentioned in the sitemap.",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
validationSchema={Yup.object().shape({
|
validationSchema={Yup.object().shape({
|
||||||
base_url: Yup.string().required(
|
base_url: Yup.string().required(
|
||||||
"Please enter the website URL to scrape e.g. https://docs.danswer.dev/"
|
"Please enter the website URL to scrape e.g. https://docs.danswer.dev/"
|
||||||
),
|
),
|
||||||
|
web_connector_type: Yup.string()
|
||||||
|
.oneOf(["recursive", "single", "sitemap"])
|
||||||
|
.optional(),
|
||||||
})}
|
})}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
base_url: "",
|
base_url: "",
|
||||||
|
web_connector_type: undefined,
|
||||||
}}
|
}}
|
||||||
refreshFreq={60 * 60 * 24} // 1 day
|
refreshFreq={60 * 60 * 24} // 1 day
|
||||||
/>
|
/>
|
||||||
@ -93,6 +128,16 @@ export default function Web() {
|
|||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
header: "Scrape Method",
|
||||||
|
key: "web_connector_type",
|
||||||
|
getValue: (connector) =>
|
||||||
|
connector.connector_specific_config.web_connector_type
|
||||||
|
? SCRAPE_TYPE_TO_PRETTY_NAME[
|
||||||
|
connector.connector_specific_config.web_connector_type
|
||||||
|
]
|
||||||
|
: "Recursive",
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
onUpdate={() => mutate("/api/manage/admin/connector/indexing-status")}
|
onUpdate={() => mutate("/api/manage/admin/connector/indexing-status")}
|
||||||
/>
|
/>
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { ArrayHelpers, ErrorMessage, Field, FieldArray } from "formik";
|
import {
|
||||||
|
ArrayHelpers,
|
||||||
|
ErrorMessage,
|
||||||
|
Field,
|
||||||
|
FieldArray,
|
||||||
|
useField,
|
||||||
|
useFormikContext,
|
||||||
|
} from "formik";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { FormBodyBuilder } from "./types";
|
import { FormBodyBuilder } from "./types";
|
||||||
|
import { FC, useEffect, useRef, useState } from "react";
|
||||||
|
import { ChevronDownIcon } from "@/components/icons/icons";
|
||||||
|
|
||||||
interface TextFormFieldProps {
|
interface TextFormFieldProps {
|
||||||
name: string;
|
name: string;
|
||||||
@ -172,3 +181,147 @@ export function TextArrayFieldBuilder<T extends Yup.AnyObject>(
|
|||||||
);
|
);
|
||||||
return _TextArrayField;
|
return _TextArrayField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectorFormFieldProps {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
options: Option[];
|
||||||
|
subtext?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectorFormField({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
options,
|
||||||
|
subtext,
|
||||||
|
}: SelectorFormFieldProps) {
|
||||||
|
const [field] = useField<string>(name);
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="flex mb-2">
|
||||||
|
<div>
|
||||||
|
{label}
|
||||||
|
{subtext && <p className="text-xs">{subtext}</p>}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
options={options}
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={(selected) => setFieldValue(name, selected.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ErrorMessage
|
||||||
|
name={name}
|
||||||
|
component="div"
|
||||||
|
className="text-red-500 text-sm mt-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DropdownProps {
|
||||||
|
options: Option[];
|
||||||
|
selected: string;
|
||||||
|
onSelect: (selected: Option) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Dropdown: FC<DropdownProps> = ({ options, selected, onSelect }) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const selectedName = options.find(
|
||||||
|
(option) => option.value === selected
|
||||||
|
)?.name;
|
||||||
|
|
||||||
|
const handleSelect = (option: Option) => {
|
||||||
|
onSelect(option);
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
if (
|
||||||
|
dropdownRef.current &&
|
||||||
|
!dropdownRef.current.contains(event.target as Node)
|
||||||
|
) {
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative inline-block text-left w-full" ref={dropdownRef}>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`inline-flex
|
||||||
|
justify-center
|
||||||
|
w-full
|
||||||
|
px-4
|
||||||
|
py-3
|
||||||
|
text-sm
|
||||||
|
bg-gray-700
|
||||||
|
border
|
||||||
|
border-gray-300
|
||||||
|
rounded-md
|
||||||
|
shadow-sm
|
||||||
|
hover:bg-gray-700
|
||||||
|
focus:ring focus:ring-offset-0 focus:ring-1 focus:ring-offset-gray-800 focus:ring-blue-800
|
||||||
|
`}
|
||||||
|
id="options-menu"
|
||||||
|
aria-expanded="true"
|
||||||
|
aria-haspopup="true"
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
>
|
||||||
|
{selectedName ? <p>{selectedName}</p> : "Select an option..."}
|
||||||
|
<ChevronDownIcon className="text-gray-400 my-auto ml-auto" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isOpen ? (
|
||||||
|
<div className="origin-top-right absolute left-0 mt-3 w-full rounded-md shadow-lg bg-gray-700 border-2 border-gray-600">
|
||||||
|
<div
|
||||||
|
role="menu"
|
||||||
|
aria-orientation="vertical"
|
||||||
|
aria-labelledby="options-menu"
|
||||||
|
>
|
||||||
|
{options.map((option, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={() => handleSelect(option)}
|
||||||
|
className={
|
||||||
|
`w-full text-left block px-4 py-2.5 text-sm hover:bg-gray-800` +
|
||||||
|
(index !== 0 ? " border-t-2 border-gray-600" : "")
|
||||||
|
}
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
<p className="font-medium">{option.name}</p>
|
||||||
|
{option.description && (
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-gray-300">
|
||||||
|
{option.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -100,7 +100,9 @@ export function StatusRow<ConnectorConfigType, ConnectorCredentialType>({
|
|||||||
interface ColumnSpecification<ConnectorConfigType> {
|
interface ColumnSpecification<ConnectorConfigType> {
|
||||||
header: string;
|
header: string;
|
||||||
key: string;
|
key: string;
|
||||||
getValue: (connector: Connector<ConnectorConfigType>) => JSX.Element | string;
|
getValue: (
|
||||||
|
connector: Connector<ConnectorConfigType>
|
||||||
|
) => JSX.Element | string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConnectorsTableProps<ConnectorConfigType, ConnectorCredentialType> {
|
interface ConnectorsTableProps<ConnectorConfigType, ConnectorCredentialType> {
|
||||||
|
@ -56,6 +56,7 @@ export interface Connector<T> extends ConnectorBase<T> {
|
|||||||
|
|
||||||
export interface WebConfig {
|
export interface WebConfig {
|
||||||
base_url: string;
|
base_url: string;
|
||||||
|
web_connector_type?: "recursive" | "single" | "sitemap";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GithubConfig {
|
export interface GithubConfig {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user