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..038624acfab 100644 --- a/frontend/src/components/DataConnectorOption/index.jsx +++ b/frontend/src/components/DataConnectorOption/index.jsx @@ -1,6 +1,3 @@ -import paths from "@/utils/paths"; -import ConnectorImages from "./media"; - export default function DataConnectorOption({ slug }) { if (!DATA_CONNECTORS.hasOwnProperty(slug)) return null; const { path, image, name, description, link } = DATA_CONNECTORS[slug]; @@ -26,22 +23,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/DataConnectorOption/media/github.png b/frontend/src/components/DataConnectorOption/media/github.png deleted file mode 100644 index 835221bab5e..00000000000 Binary files a/frontend/src/components/DataConnectorOption/media/github.png and /dev/null differ 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 aed2b0475ca..00000000000 Binary files a/frontend/src/components/DataConnectorOption/media/youtube.png and /dev/null differ diff --git a/frontend/src/components/DataConnectorOption/media/youtube.svg b/frontend/src/components/DataConnectorOption/media/youtube.svg new file mode 100644 index 00000000000..5fd97768a0d --- /dev/null +++ b/frontend/src/components/DataConnectorOption/media/youtube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + 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..e0b10e050eb --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/ConnectorOption/index.jsx @@ -0,0 +1,25 @@ +export default function ConnectorOption({ + slug, + selectedConnector, + setSelectedConnector, + image, + name, + description, +}) { + return ( + + ); +} 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..de6ed77e1b8 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -0,0 +1,271 @@ +import React, { useEffect, useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import pluralize from "pluralize"; +import { TagsInput } from "react-tag-input-component"; +import { Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +const DEFAULT_BRANCHES = ["main", "master"]; +export default function GithubOptions() { + 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 ( +
+
+
+
+
+
+
+ +

+ 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 from. +

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

+ Branch you wish to collect files from. +

+
+ +
+ ); +} 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..ed18dcd4242 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Youtube/index.jsx @@ -0,0 +1,91 @@ +import React, { useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; + +export default function YoutubeOptions() { + 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 ( +
+
+
+
+
+
+
+ +

+ URL of the YouTube video you wish to transcribe. +

+
+ +
+
+
+ +
+ + {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..419fc1fc9e1 --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx @@ -0,0 +1,77 @@ +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"; +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 filteredConnectors = Object.keys(DATA_CONNECTORS).filter((slug) => + DATA_CONNECTORS[slug].name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ {filteredConnectors.length > 0 ? ( + filteredConnectors.map((slug, index) => ( + + )) + ) : ( +
+ No data connectors found. +
+ )} +
+
+
+
+ {DATA_CONNECTORS[selectedConnector].options} +
+
+ ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/index.jsx b/frontend/src/components/Modals/MangeWorkspace/index.jsx index 4898f531f95..2c6e658b043 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 DataConnectors from "./DataConnectors"; const noop = () => {}; const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { const { slug } = useParams(); + const { user } = useUser(); 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 }) => {
-
- + + {user?.role !== "default" && ( + + )} + + {selectedTab === "documents" ? ( + + ) : ( + + )}
@@ -84,6 +98,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); diff --git a/frontend/src/components/SettingsSidebar/index.jsx b/frontend/src/components/SettingsSidebar/index.jsx index 66f881ff6a5..15e53aa5e3a 100644 --- a/frontend/src/components/SettingsSidebar/index.jsx +++ b/frontend/src/components/SettingsSidebar/index.jsx @@ -15,7 +15,6 @@ import { House, List, FileCode, - Plugs, Notepad, CodeBlock, Barcode, @@ -75,11 +74,10 @@ export default function SettingsSidebar() { className={`z-99 fixed top-0 left-0 transition-all duration-500 w-[100vw] h-[100vh]`} >
setShowSidebar(false)} />
{React.cloneElement(icon, { weight: isActive ? "fill" : "regular" })} @@ -207,9 +204,8 @@ const Option = ({
{!!subOptions && (isActive || hasActiveChild) && (
{subOptions}
@@ -304,14 +300,6 @@ const SidebarOptions = ({ user = null }) => ( flex={true} allowedRole={["admin"]} /> -