From 7dcb7e698f60d693fa573e2fb25a16764c5b66a7 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Wed, 3 Apr 2024 18:08:19 -0700 Subject: [PATCH 1/4] WIP data connector redesign --- .../DataConnectors/ConnectorOption/index.jsx | 37 +++ .../Connectors/Github/index.jsx | 269 ++++++++++++++++++ .../Connectors/Youtube/index.jsx | 90 ++++++ .../MangeWorkspace/DataConnectors/index.jsx | 92 ++++++ .../Modals/MangeWorkspace/index.jsx | 45 ++- 5 files changed, 531 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx new file mode 100644 index 00000000000..0f327dfd61a --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx @@ -0,0 +1,37 @@ +export default function DataConnectorOption({ + name, + image, + description, + value, + checked = false, + onClick, +}) { + return ( +
onClick(value)} + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-white/10 ${ + checked ? "bg-white/10" : "" + }`} + > + +
+ {`${name} +
+
{name}
+
{description}
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx new file mode 100644 index 00000000000..0ed9747d85e --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -0,0 +1,269 @@ +import React, { useEffect, useState } from "react"; +import Sidebar from "@/components/SettingsSidebar"; +import { isMobile } from "react-device-detect"; +import { DATA_CONNECTORS } from "@/components/DataConnectorOption"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import pluralize from "pluralize"; +import { TagsInput } from "react-tag-input-component"; +import { Info } from "@phosphor-icons/react"; + +const DEFAULT_BRANCHES = ["main", "master"]; +export default function GithubOptions() { + const { image } = DATA_CONNECTORS.github; + const [loading, setLoading] = useState(false); + const [repo, setRepo] = useState(null); + const [accessToken, setAccessToken] = useState(null); + const [ignores, setIgnores] = useState([]); + + const [settings, setSettings] = useState({ + repo: null, + accessToken: null, + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast( + "Fetching all files for repo - this may take a while.", + "info", + { clear: true, autoClose: false } + ); + const { data, error } = await System.dataConnectors.github.collect({ + repo: form.get("repo"), + accessToken: form.get("accessToken"), + branch: form.get("branch"), + ignorePaths: ignores, + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.files} ${pluralize("file", data.files)} collected from ${ + data.author + }/${data.repo}:${data.branch}. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+ {!accessToken && ( +
+
+
+ +

+ Trying to collect a GitHub repo without a{" "} + + Personal Access Token + {" "} + will fail to collect all files due to GitHub API limits. +

+
+ + Create a temporary Access Token for this data connector → + +
+
+ )} + +
+
+
+
+ +

+ Url of the GitHub repo you wish to collect. +

+
+ setRepo(e.target.value)} + onBlur={() => setSettings({ ...settings, repo })} + spellCheck={false} + /> +
+
+
+ +

+ Access Token to prevent rate limiting. +

+
+ setAccessToken(e.target.value)} + onBlur={() => setSettings({ ...settings, accessToken })} + /> +
+ +
+ +
+
+ +

+ List in .gitignore format to ignore specific files during + collection. Press enter after each entry you want to save. +

+
+ +
+
+ +
+ + {loading && ( +

+ Once complete, all files will be available for embedding into + workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} + +function GitHubBranchSelection({ repo, accessToken }) { + const [allBranches, setAllBranches] = useState(DEFAULT_BRANCHES); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchAllBranches() { + if (!repo) { + setAllBranches(DEFAULT_BRANCHES); + setLoading(false); + return; + } + + setLoading(true); + const { branches } = await System.dataConnectors.github.branches({ + repo, + accessToken, + }); + setAllBranches(branches.length > 0 ? branches : DEFAULT_BRANCHES); + setLoading(false); + } + fetchAllBranches(); + }, [repo, accessToken]); + + if (loading) { + return ( +
+
+ +

+ Branch you wish to collect files of +

+
+ +
+ ); + } + + return ( +
+
+ +

+ Branch you wish to collect files of +

+
+ +
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx new file mode 100644 index 00000000000..398b047d5ea --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx @@ -0,0 +1,90 @@ +import React, { useState } from "react"; +import Sidebar from "@/components/SettingsSidebar"; +import { isMobile } from "react-device-detect"; +import { DATA_CONNECTORS } from "@/components/DataConnectorOption"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; + +export default function YoutubeOptions() { + const { image } = DATA_CONNECTORS["youtube-transcript"]; + const [loading, setLoading] = useState(false); + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast("Fetching transcript for YouTube video.", "info", { + clear: true, + autoClose: false, + }); + const { data, error } = await System.dataConnectors.youtube.transcribe({ + url: form.get("url"), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `${data.title} by ${data.author} transcription completed. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + return; + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( +
+
+
+
+
+
+
+ +
+ +
+
+
+ +
+ + {loading && ( +

+ Once complete, the transcription will be available for embedding + into workspaces in the document picker. +

+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx new file mode 100644 index 00000000000..4167103e141 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx @@ -0,0 +1,92 @@ +import ConnectorImages from "@/components/DataConnectorOption/media"; +import { MagnifyingGlass } from "@phosphor-icons/react"; +import GithubOptions from "./Connectors/Github"; +import YoutubeOptions from "./Connectors/Youtube"; +import { useState } from "react"; + +export default function DataConnectors() { + const [selectedConnector, setSelectedConnector] = useState("github"); + const [searchQuery, setSearchQuery] = useState(""); + + const DATA_CONNECTORS = { + github: { + name: "GitHub Repo", + image: ConnectorImages.github, + description: + "Import an entire public or private Github repository in a single click.", + link: "https://github.com", + options: , + }, + "youtube-transcript": { + name: "YouTube Transcript", + image: ConnectorImages.youtube, + description: + "Import the transcription of an entire YouTube video from a link.", + link: "https://youtube.com", + options: , + }, + }; + + const filteredConnectors = Object.keys(DATA_CONNECTORS).filter((slug) => + DATA_CONNECTORS[slug].name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+ {/* Render options */} +
+ {filteredConnectors.length > 0 ? ( + filteredConnectors.map((slug, index) => ( + + )) + ) : ( +
+ No data connectors found. +
+ )} +
+
+
+ {/* Options */} +
+ {DATA_CONNECTORS[selectedConnector].options} +
+
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/index.jsx b/frontend/src/components/Modals/MangeWorkspace/index.jsx index 4898f531f95..0c6ed5ac35f 100644 --- a/frontend/src/components/Modals/MangeWorkspace/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/index.jsx @@ -6,12 +6,15 @@ import System from "../../../models/system"; import { isMobile } from "react-device-detect"; import useUser from "../../../hooks/useUser"; import DocumentSettings from "./Documents"; +import DataConnector from "@/models/dataConnector"; +import DataConnectors from "./DataConnectors"; const noop = () => {}; const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { const { slug } = useParams(); const [workspace, setWorkspace] = useState(null); const [settings, setSettings] = useState({}); + const [selectedTab, setSelectedTab] = useState("documents"); useEffect(() => { async function getSettings() { @@ -67,7 +70,6 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => {
-
- + + + + {selectedTab === "documents" ? ( + + ) : ( + + )}
@@ -84,6 +96,35 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { }; export default memo(ManageWorkspace); + +const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => { + return ( +
+
+ + +
+
+ ); +}; export function useManageWorkspaceModal() { const { user } = useUser(); const [showing, setShowing] = useState(false); From a94514d283a84c3549d289be8cf9cdbe9eff9eb4 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Thu, 4 Apr 2024 14:51:34 -0700 Subject: [PATCH 2/4] new UI for data connectors complete --- .../DataConnectorOption/media/github.png | Bin 22064 -> 0 bytes .../DataConnectorOption/media/github.svg | 4 + .../DataConnectorOption/media/index.js | 4 +- .../DataConnectorOption/media/youtube.png | Bin 5412 -> 0 bytes .../DataConnectorOption/media/youtube.svg | 10 ++ .../DataConnectors/ConnectorOption/index.jsx | 44 +++--- .../Connectors/Github/index.jsx | 131 +++++++++--------- .../Connectors/Youtube/index.jsx | 21 +-- .../MangeWorkspace/DataConnectors/index.jsx | 39 ++---- 9 files changed, 124 insertions(+), 129 deletions(-) delete mode 100644 frontend/src/components/DataConnectorOption/media/github.png create mode 100644 frontend/src/components/DataConnectorOption/media/github.svg delete mode 100644 frontend/src/components/DataConnectorOption/media/youtube.png create mode 100644 frontend/src/components/DataConnectorOption/media/youtube.svg diff --git a/frontend/src/components/DataConnectorOption/media/github.png b/frontend/src/components/DataConnectorOption/media/github.png deleted file mode 100644 index 835221bab5ec4ca19b03d0b9c25f4948b84a0cd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22064 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 diff --git a/frontend/src/components/DataConnectorOption/media/github.svg b/frontend/src/components/DataConnectorOption/media/github.svg new file mode 100644 index 00000000000..e0172215107 --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/github.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/DataConnectorOption/media/index.js b/frontend/src/components/DataConnectorOption/media/index.js index b3bacc1de87..543bed5f74b 100644 --- a/frontend/src/components/DataConnectorOption/media/index.js +++ b/frontend/src/components/DataConnectorOption/media/index.js @@ -1,5 +1,5 @@ -import Github from "./github.png"; -import YouTube from "./youtube.png"; +import Github from "./github.svg"; +import YouTube from "./youtube.svg"; const ConnectorImages = { github: Github, diff --git a/frontend/src/components/DataConnectorOption/media/youtube.png b/frontend/src/components/DataConnectorOption/media/youtube.png deleted file mode 100644 index aed2b0475cafadf03769b671baeeeaa58581a68f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5412 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 + + + + + + + + + diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx index 0f327dfd61a..e0b10e050eb 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx @@ -1,37 +1,25 @@ -export default function DataConnectorOption({ - name, +export default function ConnectorOption({ + slug, + selectedConnector, + setSelectedConnector, image, + name, description, - value, - checked = false, - onClick, }) { return ( -

onClick(value)} - className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-white/10 ${ - checked ? "bg-white/10" : "" - }`} + ); } diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx index 0ed9747d85e..c8bc649e62a 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -1,12 +1,11 @@ import React, { useEffect, useState } from "react"; -import Sidebar from "@/components/SettingsSidebar"; -import { isMobile } from "react-device-detect"; import { DATA_CONNECTORS } from "@/components/DataConnectorOption"; import System from "@/models/system"; import showToast from "@/utils/toast"; import pluralize from "pluralize"; import { TagsInput } from "react-tag-input-component"; import { Info } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; const DEFAULT_BRANCHES = ["main", "master"]; export default function GithubOptions() { @@ -66,44 +65,14 @@ export default function GithubOptions() {
- {!accessToken && ( -
-
-
- -

- Trying to collect a GitHub repo without a{" "} - - Personal Access Token - {" "} - will fail to collect all files due to GitHub API limits. -

-
- - Create a temporary Access Token for this data connector → - -
-
- )} -
-
-
+
+
-
@@ -119,15 +88,55 @@ export default function GithubOptions() { spellCheck={false} />
-
+
- -

+

Access Token to prevent rate limiting.

@@ -149,12 +158,12 @@ export default function GithubOptions() { />
-
+
-
-
+
{loading && ( -

+

Once complete, all files will be available for embedding into workspaces in the document picker.

@@ -223,11 +230,9 @@ function GitHubBranchSelection({ repo, accessToken }) { return (
- -

- Branch you wish to collect files of + +

+ Branch you wish to collect files from.

@@ -246,9 +251,9 @@ function GitHubBranchSelection({ repo, accessToken }) { return (
- -

- Branch you wish to collect files of + +

+ Branch you wish to collect files from.

-
+
{loading && ( -

+

Once complete, the transcription will be available for embedding into workspaces in the document picker.

diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx index 4167103e141..479f628c736 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx @@ -3,6 +3,7 @@ import { MagnifyingGlass } from "@phosphor-icons/react"; import GithubOptions from "./Connectors/Github"; import YoutubeOptions from "./Connectors/Youtube"; import { useState } from "react"; +import ConnectorOption from "./ConnectorOption"; export default function DataConnectors() { const [selectedConnector, setSelectedConnector] = useState("github"); @@ -14,7 +15,6 @@ export default function DataConnectors() { image: ConnectorImages.github, description: "Import an entire public or private Github repository in a single click.", - link: "https://github.com", options: , }, "youtube-transcript": { @@ -22,7 +22,6 @@ export default function DataConnectors() { image: ConnectorImages.youtube, description: "Import the transcription of an entire YouTube video from a link.", - link: "https://youtube.com", options: , }, }; @@ -32,7 +31,7 @@ export default function DataConnectors() { ); return ( -
+
setSearchQuery(e.target.value)} />
- {/* Render options */} -
+
{filteredConnectors.length > 0 ? ( filteredConnectors.map((slug, index) => ( - + slug={slug} + selectedConnector={selectedConnector} + setSelectedConnector={setSelectedConnector} + image={DATA_CONNECTORS[slug].image} + name={DATA_CONNECTORS[slug].name} + description={DATA_CONNECTORS[slug].description} + /> )) ) : (
@@ -82,8 +68,7 @@ export default function DataConnectors() { )}
-
- {/* Options */} +
{DATA_CONNECTORS[selectedConnector].options}
From 46a346d72a011d1de5004fb8a7482c1d4d17b0a4 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Thu, 4 Apr 2024 15:00:28 -0700 Subject: [PATCH 3/4] remove old data connector page/cleanup imports --- frontend/src/App.jsx | 15 - .../components/DataConnectorOption/index.jsx | 19 -- .../Connectors/Github/index.jsx | 2 - .../Connectors/Youtube/index.jsx | 2 - .../MangeWorkspace/DataConnectors/index.jsx | 38 +-- .../src/components/SettingsSidebar/index.jsx | 8 - .../Connectors/Github/index.jsx | 293 ------------------ .../Connectors/Youtube/index.jsx | 113 ------- .../DataConnectors/Connectors/index.jsx | 21 -- .../GeneralSettings/DataConnectors/index.jsx | 43 --- frontend/src/utils/paths.js | 11 - 11 files changed, 19 insertions(+), 546 deletions(-) delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/Connectors/Github/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/Connectors/Youtube/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/Connectors/index.jsx delete mode 100644 frontend/src/pages/GeneralSettings/DataConnectors/index.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9ef160e72a3..dbd61623db1 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -39,12 +39,6 @@ const GeneralVectorDatabase = lazy( () => import("@/pages/GeneralSettings/VectorDatabase") ); const GeneralSecurity = lazy(() => import("@/pages/GeneralSettings/Security")); -const DataConnectors = lazy( - () => import("@/pages/GeneralSettings/DataConnectors") -); -const DataConnectorSetup = lazy( - () => import("@/pages/GeneralSettings/DataConnectors/Connectors") -); const WorkspaceSettings = lazy(() => import("@/pages/WorkspaceSettings")); const EmbedConfigSetup = lazy( () => import("@/pages/GeneralSettings/EmbedConfigs") @@ -145,15 +139,6 @@ export default function App() { path="/settings/workspaces" element={} /> - } - /> - } - /> - {/* Onboarding Flow */} } /> } /> diff --git a/frontend/src/components/DataConnectorOption/index.jsx b/frontend/src/components/DataConnectorOption/index.jsx index df7fad0f601..06f16d29032 100644 --- a/frontend/src/components/DataConnectorOption/index.jsx +++ b/frontend/src/components/DataConnectorOption/index.jsx @@ -26,22 +26,3 @@ export default function DataConnectorOption({ slug }) { ); } - -export const DATA_CONNECTORS = { - github: { - name: "GitHub Repo", - path: paths.settings.dataConnectors.github(), - image: ConnectorImages.github, - description: - "Import an entire public or private Github repository in a single click.", - link: "https://github.com", - }, - "youtube-transcript": { - name: "YouTube Transcript", - path: paths.settings.dataConnectors.youtubeTranscript(), - image: ConnectorImages.youtube, - description: - "Import the transcription of an entire YouTube video from a link.", - link: "https://youtube.com", - }, -}; diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx index c8bc649e62a..360b560d824 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from "react"; -import { DATA_CONNECTORS } from "@/components/DataConnectorOption"; import System from "@/models/system"; import showToast from "@/utils/toast"; import pluralize from "pluralize"; @@ -9,7 +8,6 @@ import { Tooltip } from "react-tooltip"; const DEFAULT_BRANCHES = ["main", "master"]; export default function GithubOptions() { - const { image } = DATA_CONNECTORS.github; const [loading, setLoading] = useState(false); const [repo, setRepo] = useState(null); const [accessToken, setAccessToken] = useState(null); diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx index 045c0c176dd..ed18dcd4242 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx @@ -1,10 +1,8 @@ import React, { useState } from "react"; -import { DATA_CONNECTORS } from "@/components/DataConnectorOption"; import System from "@/models/system"; import showToast from "@/utils/toast"; export default function YoutubeOptions() { - const { image } = DATA_CONNECTORS["youtube-transcript"]; const [loading, setLoading] = useState(false); const handleSubmit = async (e) => { diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx index 479f628c736..36744e68abb 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx @@ -5,34 +5,34 @@ import YoutubeOptions from "./Connectors/Youtube"; import { useState } from "react"; import ConnectorOption from "./ConnectorOption"; +export const DATA_CONNECTORS = { + github: { + name: "GitHub Repo", + image: ConnectorImages.github, + description: + "Import an entire public or private Github repository in a single click.", + options: , + }, + "youtube-transcript": { + name: "YouTube Transcript", + image: ConnectorImages.youtube, + description: + "Import the transcription of an entire YouTube video from a link.", + options: , + }, +}; + export default function DataConnectors() { const [selectedConnector, setSelectedConnector] = useState("github"); const [searchQuery, setSearchQuery] = useState(""); - const DATA_CONNECTORS = { - github: { - name: "GitHub Repo", - image: ConnectorImages.github, - description: - "Import an entire public or private Github repository in a single click.", - options: , - }, - "youtube-transcript": { - name: "YouTube Transcript", - image: ConnectorImages.youtube, - description: - "Import the transcription of an entire YouTube video from a link.", - options: , - }, - }; - const filteredConnectors = Object.keys(DATA_CONNECTORS).filter((slug) => DATA_CONNECTORS[slug].name.toLowerCase().includes(searchQuery.toLowerCase()) ); return (
-
+
-
+
{DATA_CONNECTORS[selectedConnector].options}
diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx index 66f881ff6a5..fbac9996af5 100644 --- a/frontend/src/components/SettingsSidebar/index.jsx +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -304,14 +304,6 @@ const SidebarOptions = ({ user = null }) => ( flex={true} allowedRole={["admin"]} /> -