diff --git a/backend/danswer/server/features/prompt/models.py b/backend/danswer/server/features/prompt/models.py index d3062f0ddb..0ae70c58d0 100644 --- a/backend/danswer/server/features/prompt/models.py +++ b/backend/danswer/server/features/prompt/models.py @@ -9,9 +9,9 @@ class CreatePromptRequest(BaseModel): shared: bool system_prompt: str task_prompt: str - include_citations: bool - datetime_aware: bool - persona_ids: list[int] + include_citations: bool = False + datetime_aware: bool = False + persona_ids: list[int] | None = None class PromptSnapshot(BaseModel): diff --git a/backend/danswer/server/query_and_chat/chat_backend.py b/backend/danswer/server/query_and_chat/chat_backend.py index 78607276bf..a2b2c4996d 100644 --- a/backend/danswer/server/query_and_chat/chat_backend.py +++ b/backend/danswer/server/query_and_chat/chat_backend.py @@ -59,6 +59,7 @@ def get_user_chat_sessions( ChatSessionDetails( id=chat.id, name=chat.description, + persona_id=chat.persona_id, time_created=chat.time_created.isoformat(), ) for chat in chat_sessions diff --git a/backend/danswer/server/query_and_chat/models.py b/backend/danswer/server/query_and_chat/models.py index ab1c1dc802..745834c5ba 100644 --- a/backend/danswer/server/query_and_chat/models.py +++ b/backend/danswer/server/query_and_chat/models.py @@ -96,6 +96,7 @@ class RenameChatSessionResponse(BaseModel): class ChatSessionDetails(BaseModel): id: int name: str + persona_id: int time_created: str diff --git a/web/next.config.js b/web/next.config.js index 1730d1ef5d..6f7de34ae4 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -24,20 +24,35 @@ const nextConfig = { // In production, something else (nginx in the one box setup) should take // care of this redirect. TODO (chris): better support setups where // web_server and api_server are on different machines. - if (process.env.NODE_ENV === "production") return []; - - return [ + const defaultRedirects = [ { - source: "/api/stream-direct-qa:params*", - destination: "http://127.0.0.1:8080/stream-direct-qa:params*", // Proxy to Backend - permanent: true, - }, - { - source: "/api/stream-query-validation:params*", - destination: "http://127.0.0.1:8080/stream-query-validation:params*", // Proxy to Backend + source: "/", + destination: "/search", permanent: true, }, ]; + + if (process.env.NODE_ENV === "production") return defaultRedirects; + + return defaultRedirects.concat([ + { + source: "/api/chat/send-message:params*", + destination: "http://127.0.0.1:8080/chat/send-message:params*", // Proxy to Backend + permanent: true, + }, + { + source: "/api/query/stream-answer-with-quote:params*", + destination: + "http://127.0.0.1:8080/query/stream-answer-with-quote:params*", // Proxy to Backend + permanent: true, + }, + { + source: "/api/query/stream-query-validation:params*", + destination: + "http://127.0.0.1:8080/query/stream-query-validation:params*", // Proxy to Backend + permanent: true, + }, + ]); }, publicRuntimeConfig: { version, diff --git a/web/public/Github.png b/web/public/Github.png index 50b8175227..6cb3b705d0 100644 Binary files a/web/public/Github.png and b/web/public/Github.png differ diff --git a/web/public/GithubDarkMode.png b/web/public/GithubDarkMode.png new file mode 100644 index 0000000000..50b8175227 Binary files /dev/null and b/web/public/GithubDarkMode.png differ diff --git a/web/src/app/admin/add-connector/page.tsx b/web/src/app/admin/add-connector/page.tsx index 1302fe6716..d6ad8324ef 100644 --- a/web/src/app/admin/add-connector/page.tsx +++ b/web/src/app/admin/add-connector/page.tsx @@ -13,20 +13,20 @@ function SourceTile({ sourceMetadata }: { sourceMetadata: SourceMetadata }) { flex-col items-center justify-center - bg-dark-tremor-background-muted p-4 rounded-lg w-40 cursor-pointer + bg-hover-light shadow-md - hover:bg-gray-800 + hover:bg-hover `} href={sourceMetadata.adminUrl} > - + {sourceMetadata.displayName} - + ); } @@ -42,26 +42,26 @@ export default function Page() { ); return ( -
+
} title="Add Connector" /> -
+ Connect Danswer to your organization's knowledge sources. We'll automatically sync your data into Danswer, so you can find exactly what you're looking for in one place. -
+
Import Knowledge
-
+ Connect to pieces of knowledge that live outside your apps. Upload files, scrape websites, or connect to your organization's Google Site. -
+
{importedKnowledgeSources.map((source) => { return ( @@ -73,11 +73,11 @@ export default function Page() {
Setup Auto-Syncing from Apps
-
+ Setup auto-syncing from your organization's most used apps and services. Unless otherwise specified during the connector setup, we will pull in the latest updates from the source every 10 minutes. -
+
{appConnectionSources.map((source) => { return ( diff --git a/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx b/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx index fa4f383bff..099452445d 100644 --- a/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx +++ b/web/src/app/admin/bot/SlackBotConfigCreationForm.tsx @@ -17,6 +17,7 @@ import { updateSlackBotConfig, } from "./lib"; import { + Button, Card, Divider, Tab, @@ -52,7 +53,7 @@ export const SlackBotCreationForm = ({ ); return ( -
+
{popup} or Document Sets to control how DanswerBot answers. -
+
  • You should use a Persona if you also want to customize @@ -204,7 +205,7 @@ export const SlackBotCreationForm = ({ which documents DanswerBot uses as references.
-
+ NOTE: whichever tab you are when you submit the form will be the one that is used. For example, if you are on the @@ -245,17 +246,17 @@ export const SlackBotCreationForm = ({ key={documentSet.id} className={ ` - px-3 - py-1 - rounded-lg - border - border-gray-700 - w-fit - flex - cursor-pointer ` + + px-3 + py-1 + rounded-lg + border + border-border + w-fit + flex + cursor-pointer ` + (isSelected - ? " bg-gray-600" - : " bg-gray-900 hover:bg-gray-700") + ? " bg-hover" + : " bg-background hover:bg-hover-light") } onClick={() => { if (isSelected) { @@ -289,7 +290,6 @@ export const SlackBotCreationForm = ({ value: persona.id, }; })} - includeDefault={true} /> @@ -298,17 +298,13 @@ export const SlackBotCreationForm = ({
- +
diff --git a/web/src/app/admin/bot/SlackBotTokensForm.tsx b/web/src/app/admin/bot/SlackBotTokensForm.tsx index 118f2de8c8..37bba48775 100644 --- a/web/src/app/admin/bot/SlackBotTokensForm.tsx +++ b/web/src/app/admin/bot/SlackBotTokensForm.tsx @@ -11,7 +11,7 @@ import { setSlackBotTokens, updateSlackBotConfig, } from "./lib"; -import { Card } from "@tremor/react"; +import { Button, Card } from "@tremor/react"; interface SlackBotTokensFormProps { onClose: () => void; @@ -64,17 +64,9 @@ export const SlackBotTokensForm = ({ type="password" />
- +
)} diff --git a/web/src/app/admin/bot/[id]/page.tsx b/web/src/app/admin/bot/[id]/page.tsx index fd6c3aab84..1fc544e5da 100644 --- a/web/src/app/admin/bot/[id]/page.tsx +++ b/web/src/app/admin/bot/[id]/page.tsx @@ -62,7 +62,7 @@ async function Page({ params }: { params: { id: string } }) { const personas = (await personasResponse.json()) as Persona[]; return ( -
+
diff --git a/web/src/app/admin/bot/new/page.tsx b/web/src/app/admin/bot/new/page.tsx index dd6781a19c..93c9c7f469 100644 --- a/web/src/app/admin/bot/new/page.tsx +++ b/web/src/app/admin/bot/new/page.tsx @@ -33,7 +33,7 @@ async function Page() { const personas = (await personasResponse.json()) as Persona[]; return ( -
+
} diff --git a/web/src/app/admin/bot/page.tsx b/web/src/app/admin/bot/page.tsx index fc27e0187f..257153735a 100644 --- a/web/src/app/admin/bot/page.tsx +++ b/web/src/app/admin/bot/page.tsx @@ -173,7 +173,7 @@ const Main = () => { } return ( -
+
{popup} @@ -181,7 +181,7 @@ const Main = () => { to ask questions to Danswer directly from Slack. Additionally, you can: -
+
  • Setup DanswerBot to automatically answer questions in certain @@ -196,7 +196,7 @@ const Main = () => { UI.
-
+ Follow the{" "} @@ -226,7 +226,7 @@ const Main = () => { setSlackBotTokensModalIsOpen(!slackBotTokensModalIsOpen); console.log(slackBotTokensModalIsOpen); }} - variant="secondary" + color="blue" size="xs" className="mt-2" icon={slackBotTokensModalIsOpen ? FiChevronUp : FiChevronDown} @@ -259,7 +259,7 @@ const Main = () => {
- diff --git a/web/src/app/admin/connector/[ccPairId]/ConfigDisplay.tsx b/web/src/app/admin/connector/[ccPairId]/ConfigDisplay.tsx index 5dfe6b25a9..42da01e184 100644 --- a/web/src/app/admin/connector/[ccPairId]/ConfigDisplay.tsx +++ b/web/src/app/admin/connector/[ccPairId]/ConfigDisplay.tsx @@ -1,6 +1,6 @@ import { getNameFromPath } from "@/lib/fileUtils"; import { ValidSources } from "@/lib/types"; -import { List, ListItem, Card, Title, Divider } from "@tremor/react"; +import { List, ListItem, Card, Title } from "@tremor/react"; function convertObjectToString(obj: any): string | any { // Check if obj is an object and not an array or null diff --git a/web/src/app/admin/connector/[ccPairId]/DeletionButton.tsx b/web/src/app/admin/connector/[ccPairId]/DeletionButton.tsx index cd6edcf474..c8ffd6d953 100644 --- a/web/src/app/admin/connector/[ccPairId]/DeletionButton.tsx +++ b/web/src/app/admin/connector/[ccPairId]/DeletionButton.tsx @@ -30,7 +30,6 @@ export function DeletionButton({ ccPair }: { ccPair: CCPairFullInfo }) {
{popup} ) : (
+ )} -
+

Add a New Space

nameBuilder={(values) => @@ -284,14 +288,14 @@ const Main = () => { refreshFreq={10 * 60} // 10 minutes credentialId={confluenceCredential.id} /> -
+ ) : ( -

+ Please provide your access token in Step 1 first! Once done with that, you can then specify which Confluence spaces you want to make searchable. -

+
)} ); @@ -303,10 +307,9 @@ export default function Page() {
-
- -

Confluence

-
+ + } title="Confluence" /> +
); diff --git a/web/src/app/admin/connectors/document360/page.tsx b/web/src/app/admin/connectors/document360/page.tsx index 357151eb97..8d7c03190e 100644 --- a/web/src/app/admin/connectors/document360/page.tsx +++ b/web/src/app/admin/connectors/document360/page.tsx @@ -21,6 +21,8 @@ import { import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { usePublicCredentials } from "@/lib/hooks"; +import { Title, Text, Card, Divider } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const MainSection = () => { const { mutate } = useSWRConfig(); @@ -71,18 +73,18 @@ const MainSection = () => { return ( <> -

+ Step 1: Provide Credentials - </h2> + {document360Credential ? ( <>
-

Existing Document360 API Token:

-

+ Existing Document360 API Token: + {document360Credential.credential_json.document360_api_token} -

+
); diff --git a/web/src/app/admin/connectors/file/page.tsx b/web/src/app/admin/connectors/file/page.tsx index 8f8206f7b1..b963b2d94d 100644 --- a/web/src/app/admin/connectors/file/page.tsx +++ b/web/src/app/admin/connectors/file/page.tsx @@ -18,6 +18,8 @@ import { Form, Formik } from "formik"; import { TextFormField } from "@/components/admin/connectors/Field"; import { FileUpload } from "@/components/admin/connectors/FileUpload"; import { getNameFromPath } from "@/lib/fileUtils"; +import { Button, Card, Divider, Text } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const Main = () => { const [selectedFiles, setSelectedFiles] = useState([]); @@ -48,13 +50,13 @@ const Main = () => {
{popup} {filesAreUploading && } -

+ Specify files below, click the Upload button, and the contents of these files will be searchable via Danswer! Currently only .txt,{" "} .pdf and .zip files (containing only .txt files) are supported. -

-
+ + NOTE: if the original document is accessible via a link, you can add a line at the very beginning of the file that looks like:
@@ -67,163 +69,172 @@ const Main = () => { search result. More details on this can be found in the{" "} documentation. -
+
-
- { - const uploadCreateAndTriggerConnector = async () => { - const formData = new FormData(); +
+ + { + const uploadCreateAndTriggerConnector = async () => { + const formData = new FormData(); - selectedFiles.forEach((file) => { - formData.append("files", file); - }); - - const response = await fetch( - "/api/manage/admin/connector/file/upload", - { method: "POST", body: formData } - ); - const responseJson = await response.json(); - if (!response.ok) { - setPopup({ - message: `Unable to upload files - ${responseJson.detail}`, - type: "error", + selectedFiles.forEach((file) => { + formData.append("files", file); }); - return; - } - const filePaths = responseJson.file_paths as string[]; - const [connectorErrorMsg, connector] = - await createConnector({ - name: "FileConnector-" + Date.now(), - source: "file", - input_type: "load_state", - connector_specific_config: { - file_locations: filePaths, - }, - refresh_freq: null, - disabled: false, - }); - if (connectorErrorMsg || !connector) { - setPopup({ - message: `Unable to create connector - ${connectorErrorMsg}`, - type: "error", - }); - return; - } - - // Since there is no "real" credential associated with a file connector - // we create a dummy one here so that we can associate the CC Pair with a - // user. This is needed since the user for a CC Pair is found via the credential - // associated with it. - const createCredentialResponse = await createCredential({ - credential_json: {}, - admin_public: true, - }); - if (!createCredentialResponse.ok) { - const errorMsg = await createCredentialResponse.text(); - setPopup({ - message: `Error creating credential for CC Pair - ${errorMsg}`, - type: "error", - }); - formikHelpers.setSubmitting(false); - return; - } - const credentialId = (await createCredentialResponse.json()).id; - - const credentialResponse = await linkCredential( - connector.id, - credentialId, - values.name - ); - if (!credentialResponse.ok) { - const credentialResponseJson = - await credentialResponse.json(); - setPopup({ - message: `Unable to link connector to credential - ${credentialResponseJson.detail}`, - type: "error", - }); - return; - } - - const runConnectorErrorMsg = await runConnector(connector.id, [ - 0, - ]); - if (runConnectorErrorMsg) { - setPopup({ - message: `Unable to run connector - ${runConnectorErrorMsg}`, - type: "error", - }); - return; - } - - mutate("/api/manage/admin/connector/indexing-status"); - setSelectedFiles([]); - formikHelpers.resetForm(); - setPopup({ - type: "success", - message: "Successfully uploaded files!", - }); - }; - - setFilesAreUploading(true); - try { - await uploadCreateAndTriggerConnector(); - } catch (e) { - console.log("Failed to index filels: ", e); - } - setFilesAreUploading(false); - }} - > - {({ values, isSubmitting }) => ( -
-

Upload Files

- - -

Files:

- - - - )} -
+ + // Since there is no "real" credential associated with a file connector + // we create a dummy one here so that we can associate the CC Pair with a + // user. This is needed since the user for a CC Pair is found via the credential + // associated with it. + const createCredentialResponse = await createCredential({ + credential_json: {}, + admin_public: true, + }); + if (!createCredentialResponse.ok) { + const errorMsg = await createCredentialResponse.text(); + setPopup({ + message: `Error creating credential for CC Pair - ${errorMsg}`, + type: "error", + }); + formikHelpers.setSubmitting(false); + return; + } + const credentialId = (await createCredentialResponse.json()) + .id; + + const credentialResponse = await linkCredential( + connector.id, + credentialId, + values.name + ); + if (!credentialResponse.ok) { + const credentialResponseJson = + await credentialResponse.json(); + setPopup({ + message: `Unable to link connector to credential - ${credentialResponseJson.detail}`, + type: "error", + }); + return; + } + + const runConnectorErrorMsg = await runConnector( + connector.id, + [0] + ); + if (runConnectorErrorMsg) { + setPopup({ + message: `Unable to run connector - ${runConnectorErrorMsg}`, + type: "error", + }); + return; + } + + mutate("/api/manage/admin/connector/indexing-status"); + setSelectedFiles([]); + formikHelpers.resetForm(); + setPopup({ + type: "success", + message: "Successfully uploaded files!", + }); + }; + + setFilesAreUploading(true); + try { + await uploadCreateAndTriggerConnector(); + } catch (e) { + console.log("Failed to index filels: ", e); + } + setFilesAreUploading(false); + }} + > + {({ values, isSubmitting }) => ( +
+

+ Upload Files +

+ + +

Files:

+ +
+ +
+ + )} + +
{fileIndexingStatuses.length > 0 && ( -
+
+

Indexed Files

connectorIndexingStatuses={fileIndexingStatuses} @@ -253,10 +264,9 @@ export default function File() {
-
- -

File

-
+ + } title="File" /> +
); diff --git a/web/src/app/admin/connectors/github/page.tsx b/web/src/app/admin/connectors/github/page.tsx index 1e3bd941c9..c5346c73a6 100644 --- a/web/src/app/admin/connectors/github/page.tsx +++ b/web/src/app/admin/connectors/github/page.tsx @@ -18,6 +18,8 @@ import { CredentialForm } from "@/components/admin/connectors/CredentialForm"; import { adminDeleteCredential, linkCredential } from "@/lib/credential"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePublicCredentials } from "@/lib/hooks"; +import { Card, Divider, Text, Title } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const Main = () => { const { mutate } = useSWRConfig(); @@ -66,9 +68,9 @@ const Main = () => { return ( <> -

+ Step 1: Provide your access token - </h2> + {githubCredential ? ( <> {" "} @@ -78,7 +80,7 @@ const Main = () => { {githubCredential.credential_json.github_access_token}

{" "}

); diff --git a/web/src/app/admin/connectors/gong/page.tsx b/web/src/app/admin/connectors/gong/page.tsx index cb5f0547cd..d00beb0e73 100644 --- a/web/src/app/admin/connectors/gong/page.tsx +++ b/web/src/app/admin/connectors/gong/page.tsx @@ -22,6 +22,8 @@ import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePopup } from "@/components/admin/connectors/Popup"; import { usePublicCredentials } from "@/lib/hooks"; +import { Card, Divider, Text, Title } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const Main = () => { const { popup, setPopup } = usePopup(); @@ -75,21 +77,21 @@ const Main = () => { return ( <> {popup} -

+ This connector allows you to sync all your Gong Transcripts into Danswer. More details on how to setup the Gong connector can be found in{" "} this guide. -

+ -

+ Step 1: Provide your API Access info - </h2> + {gongCredential ? ( <> @@ -99,7 +101,7 @@ const Main = () => { {gongCredential.credential_json?.gong_access_key_secret}

); diff --git a/web/src/app/admin/connectors/google-drive/ConnectorEditPopup.tsx b/web/src/app/admin/connectors/google-drive/ConnectorEditPopup.tsx index b4a84b726f..ee8e14a677 100644 --- a/web/src/app/admin/connectors/google-drive/ConnectorEditPopup.tsx +++ b/web/src/app/admin/connectors/google-drive/ConnectorEditPopup.tsx @@ -7,6 +7,8 @@ import { XIcon } from "@/components/icons/icons"; import { Connector, GoogleDriveConfig } from "@/lib/types"; import * as Yup from "yup"; import { googleDriveConnectorNameBuilder } from "./utils"; +import { Modal } from "@/components/Modal"; +import { Divider, Text } from "@tremor/react"; interface Props { existingConnector: Connector; @@ -15,25 +17,28 @@ interface Props { export const ConnectorEditPopup = ({ existingConnector, onSubmit }: Props) => { return ( -
-
event.stopPropagation()} - > -
-

- Update Google Drive Connector -

-
+ +
+

+ Update Google Drive Connector +
-

+

+ + + Modify the selected Google Drive connector by adjusting the values + below! + + + + nameBuilder={googleDriveConnectorNameBuilder} existingConnector={existingConnector} @@ -67,6 +72,6 @@ export const ConnectorEditPopup = ({ existingConnector, onSubmit }: Props) => { onSubmit={onSubmit} />
-
+ ); }; diff --git a/web/src/app/admin/connectors/google-drive/Credential.tsx b/web/src/app/admin/connectors/google-drive/Credential.tsx index d4c5ec005b..206601adf5 100644 --- a/web/src/app/admin/connectors/google-drive/Credential.tsx +++ b/web/src/app/admin/connectors/google-drive/Credential.tsx @@ -15,6 +15,7 @@ import { GOOGLE_DRIVE_AUTH_IS_ADMIN_COOKIE_NAME } from "@/lib/constants"; import Cookies from "js-cookie"; import { TextFormField } from "@/components/admin/connectors/Field"; import { Form, Formik } from "formik"; +import { Card } from "@tremor/react"; type GoogleDriveCredentialJsonTypes = "authorized_user" | "service_account"; @@ -246,7 +247,7 @@ export const DriveJsonUploadSection = ({

Follow the guide{" "} @@ -322,7 +323,7 @@ export const DriveOAuthSection = ({ the documents you want to index with the service account.

-
+ )} -
+
); } diff --git a/web/src/app/admin/connectors/google-drive/GoogleDriveConnectorsTable.tsx b/web/src/app/admin/connectors/google-drive/GoogleDriveConnectorsTable.tsx index 803d9fc70e..5c22158191 100644 --- a/web/src/app/admin/connectors/google-drive/GoogleDriveConnectorsTable.tsx +++ b/web/src/app/admin/connectors/google-drive/GoogleDriveConnectorsTable.tsx @@ -13,6 +13,14 @@ import { useSWRConfig } from "swr"; import { useState } from "react"; import { ConnectorEditPopup } from "./ConnectorEditPopup"; import { DeleteColumn } from "@/components/admin/connectors/table/DeleteColumn"; +import { + Table, + TableHead, + TableRow, + TableHeaderCell, + TableBody, + TableCell, +} from "@tremor/react"; interface EditableColumnProps { connectorIndexingStatus: ConnectorIndexingStatus< @@ -44,7 +52,7 @@ const EditableColumn = ({ connectorIndexingStatus }: EditableColumnProps) => { className="cursor-pointer" >
- +
@@ -74,6 +82,99 @@ export const GoogleDriveConnectorsTable = ({ (a, b) => a.connector.id - b.connector.id ); + return ( +
+ + + + Edit + Folder Paths + Include Shared + Follow Shortcuts + Status + Delete + + + + {sortedGoogleDriveConnectorIndexingStatuses.map( + (connectorIndexingStatus) => { + return ( + + + + + + {( + connectorIndexingStatus.connector + .connector_specific_config.folder_paths || [] + ).length > 0 ? ( +
+ {( + connectorIndexingStatus.connector + .connector_specific_config.folder_paths || [] + ).map((path) => ( +
+ - {path} +
+ ))} +
+ ) : ( + All Folders + )} +
+ +
+ {connectorIndexingStatus.connector + .connector_specific_config.include_shared ? ( + Yes + ) : ( + No + )} +
+
+ +
+ {connectorIndexingStatus.connector + .connector_specific_config.follow_shortcuts ? ( + Yes + ) : ( + No + )} +
+
+ + { + mutate("/api/manage/admin/connector/indexing-status"); + }} + /> + + + + mutate("/api/manage/admin/connector/indexing-status") + } + /> + +
+ ); + } + )} +
+
+
+ ); + return ( ; @@ -54,10 +56,10 @@ const GoogleDriveConnectorManagement = ({ googleDrivePublicCredential || googleDriveServiceAccountCredential; if (!liveCredential) { return ( -

+ Please authenticate with Google Drive as described in Step 2! Once done with that, you can then move on to enable this connector. -

+ ); } @@ -151,7 +153,7 @@ const GoogleDriveConnectorManagement = ({ return (
-
+
{googleDriveConnectorIndexingStatuses.length > 0 ? ( <> @@ -169,7 +171,7 @@ const GoogleDriveConnectorManagement = ({

)}
-
+ {googleDriveConnectorIndexingStatuses.length > 0 && ( <>
Existing Connectors:
@@ -179,13 +181,14 @@ const GoogleDriveConnectorManagement = ({ } setPopup={setPopup} /> + )} {googleDriveConnectorIndexingStatuses.length > 0 && (

Add New Connector:

)} -
+ nameBuilder={googleDriveConnectorNameBuilder} source="google_drive" @@ -239,7 +242,7 @@ const GoogleDriveConnectorManagement = ({ refreshFreq={10 * 60} // 10 minutes credentialId={liveCredential.id} /> -
+
); }; @@ -353,18 +356,18 @@ const Main = () => { return ( <> {popup} -

+ Step 1: Provide your Credentials - </h2> + -

+ Step 2: Authenticate with Danswer - </h2> + { serviceAccountKeyData={serviceAccountKeyData} /> -

+ Step 3: Start Indexing! - </h2> +

-
- -

Google Drive

-
+ + } + title="Google Drive" + />
diff --git a/web/src/app/admin/connectors/google-sites/page.tsx b/web/src/app/admin/connectors/google-sites/page.tsx index 5a9aba9fbd..1bca39c0cd 100644 --- a/web/src/app/admin/connectors/google-sites/page.tsx +++ b/web/src/app/admin/connectors/google-sites/page.tsx @@ -17,6 +17,8 @@ import { linkCredential } from "@/lib/credential"; import { FileUpload } from "@/components/admin/connectors/FileUpload"; import { SingleUseConnectorsTable } from "@/components/admin/connectors/table/SingleUseConnectorsTable"; import { Spinner } from "@/components/Spinner"; +import { AdminPageTitle } from "@/components/admin/Title"; +import { Button, Card, Text, Title } from "@tremor/react"; export default function GoogleSites() { const { mutate } = useSWRConfig(); @@ -50,11 +52,13 @@ export default function GoogleSites() {
-
- -

Google Sites

-
-

+ + } + title="Google Sites" + /> + + For an in-depth guide on how to setup this connector, check out{" "} . -

+
-

Upload Files

-
- { - const uploadCreateAndTriggerConnector = async () => { - const formData = new FormData(); + Upload Files + +
+ { + const uploadCreateAndTriggerConnector = async () => { + const formData = new FormData(); - selectedFiles.forEach((file) => { - formData.append("files", file); - }); - - const response = await fetch( - "/api/manage/admin/connector/file/upload", - { method: "POST", body: formData } - ); - const responseJson = await response.json(); - if (!response.ok) { - setPopup({ - message: `Unable to upload files - ${responseJson.detail}`, - type: "error", + selectedFiles.forEach((file) => { + formData.append("files", file); }); - return; - } - const filePaths = responseJson.file_paths as string[]; - const [connectorErrorMsg, connector] = - await createConnector({ - name: `GoogleSitesConnector-${values.base_url}`, - source: "google_sites", - input_type: "load_state", - connector_specific_config: { - base_url: values.base_url, - zip_path: filePaths[0], - }, - refresh_freq: null, - disabled: false, - }); - if (connectorErrorMsg || !connector) { - setPopup({ - message: `Unable to create connector - ${connectorErrorMsg}`, - type: "error", - }); - return; - } - - const credentialResponse = await linkCredential( - connector.id, - 0, - values.base_url - ); - if (!credentialResponse.ok) { - const credentialResponseJson = - await credentialResponse.json(); - setPopup({ - message: `Unable to link connector to credential - ${credentialResponseJson.detail}`, - type: "error", - }); - return; - } - - const runConnectorErrorMsg = await runConnector( - connector.id, - [0] - ); - if (runConnectorErrorMsg) { - setPopup({ - message: `Unable to run connector - ${runConnectorErrorMsg}`, - type: "error", - }); - return; - } - - mutate("/api/manage/admin/connector/indexing-status"); - setSelectedFiles([]); - formikHelpers.resetForm(); - setPopup({ - type: "success", - message: "Successfully uploaded files!", - }); - }; - - setFilesAreUploading(true); - try { - await uploadCreateAndTriggerConnector(); - } catch (e) { - console.log("Failed to index filels: ", e); - } - setFilesAreUploading(false); - }} - > - {({ values, isSubmitting }) => ( -
- - -

Files:

- - - - )} -
-
+ + const credentialResponse = await linkCredential( + connector.id, + 0, + values.base_url + ); + if (!credentialResponse.ok) { + const credentialResponseJson = + await credentialResponse.json(); + setPopup({ + message: `Unable to link connector to credential - ${credentialResponseJson.detail}`, + type: "error", + }); + return; + } + + const runConnectorErrorMsg = await runConnector( + connector.id, + [0] + ); + if (runConnectorErrorMsg) { + setPopup({ + message: `Unable to run connector - ${runConnectorErrorMsg}`, + type: "error", + }); + return; + } + + mutate("/api/manage/admin/connector/indexing-status"); + setSelectedFiles([]); + formikHelpers.resetForm(); + setPopup({ + type: "success", + message: "Successfully uploaded files!", + }); + }; + + setFilesAreUploading(true); + try { + await uploadCreateAndTriggerConnector(); + } catch (e) { + console.log("Failed to index filels: ", e); + } + setFilesAreUploading(false); + }} + > + {({ values, isSubmitting }) => ( +
+ + +

Files:

+ +
+ +
+ + )} +
+
+

diff --git a/web/src/app/admin/connectors/guru/page.tsx b/web/src/app/admin/connectors/guru/page.tsx index 70d9c0ecea..e302f53891 100644 --- a/web/src/app/admin/connectors/guru/page.tsx +++ b/web/src/app/admin/connectors/guru/page.tsx @@ -19,6 +19,8 @@ import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePopup } from "@/components/admin/connectors/Popup"; import { usePublicCredentials } from "@/lib/hooks"; +import { AdminPageTitle } from "@/components/admin/Title"; +import { Card, Text, Title } from "@tremor/react"; const Main = () => { const { popup, setPopup } = usePopup(); @@ -70,23 +72,23 @@ const Main = () => { return ( <> {popup} -

+ This connector allows you to sync all your Guru Cards into Danswer. -

+ -

+ Step 1: Provide your Credentials - </h2> + {guruCredential ? ( <>
-

Existing Access Token:

-

+ Existing Access Token: + {guruCredential.credential_json?.guru_user_token} -

+
); diff --git a/web/src/app/admin/connectors/linear/page.tsx b/web/src/app/admin/connectors/linear/page.tsx index eb463e1117..76c4a13a8d 100644 --- a/web/src/app/admin/connectors/linear/page.tsx +++ b/web/src/app/admin/connectors/linear/page.tsx @@ -18,6 +18,8 @@ import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePopup } from "@/components/admin/connectors/Popup"; import { usePublicCredentials } from "@/lib/hooks"; +import { Card, Text, Title } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const Main = () => { const { popup, setPopup } = usePopup(); @@ -70,19 +72,19 @@ const Main = () => { return ( <> {popup} -

+ Step 1: Provide your Credentials - </h2> + {linearCredential ? ( <>
-

Existing API Key:

-

+ Existing API Key: + {linearCredential.credential_json?.linear_api_key} -

+
); diff --git a/web/src/app/admin/connectors/notion/page.tsx b/web/src/app/admin/connectors/notion/page.tsx index be8401633f..4ffdde68a9 100644 --- a/web/src/app/admin/connectors/notion/page.tsx +++ b/web/src/app/admin/connectors/notion/page.tsx @@ -19,6 +19,8 @@ import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePopup } from "@/components/admin/connectors/Popup"; import { usePublicCredentials } from "@/lib/hooks"; +import { AdminPageTitle } from "@/components/admin/Title"; +import { Card, Divider, Text, Title } from "@tremor/react"; const Main = () => { const { popup, setPopup } = usePopup(); @@ -69,9 +71,9 @@ const Main = () => { return ( <> {popup} -

+ Step 1: Provide your authorization details - </h2> + {notionCredential ? ( <> @@ -101,7 +103,7 @@ const Main = () => { ) : ( <> -

+ To get started you'll need to create an internal integration in Notion for Danswer. Follow the instructions in the  { token and paste it below. Follow the remaining instructions on the Notion docs to allow Danswer to read Notion Databases and Pages using the new integration. -

-
+ + formBody={ { } }} /> -
+ )} -

+ Step 2: Manage Connectors - </h2> + {notionConnectorIndexingStatuses.length > 0 && ( <> -

+ The latest page updates are fetched from Notion every 10 minutes. -

+
connectorIndexingStatuses={notionConnectorIndexingStatuses} @@ -183,12 +185,13 @@ const Main = () => { } />
+ )} {notionCredential && ( <> -
+

Create New Connection

Press connect below to start the connection to Notion. @@ -226,17 +229,17 @@ const Main = () => { refreshFreq={10 * 60} // 10 minutes credentialId={notionCredential.id} /> -

+ )} {!notionCredential && ( <> -

+ Please provide your integration details in Step 1 first! Once done with that, you'll be able to start the connection then see indexing status. -

+ )} @@ -249,10 +252,9 @@ export default function Page() {
-
- -

Notion

-
+ + } title="Notion" /> +

); diff --git a/web/src/app/admin/connectors/productboard/page.tsx b/web/src/app/admin/connectors/productboard/page.tsx index f6fdd3cc51..7b38ee42aa 100644 --- a/web/src/app/admin/connectors/productboard/page.tsx +++ b/web/src/app/admin/connectors/productboard/page.tsx @@ -19,6 +19,8 @@ import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePopup } from "@/components/admin/connectors/Popup"; import { usePublicCredentials } from "@/lib/hooks"; +import { Card, Text, Title } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const Main = () => { const { popup, setPopup } = usePopup(); @@ -72,29 +74,29 @@ const Main = () => { return ( <> {popup} -

+ This connector allows you to sync all your Features,{" "} Components, Products, and Objectives from Productboard into Danswer. At this time, the Productboard APIs does not support pulling in Releases or Notes. -

+ -

+ Step 1: Provide your Credentials - </h2> + {productboardCredential ? ( <>
-

Existing Access Token:

-

+ Existing Access Token: + { productboardCredential.credential_json ?.productboard_access_token } -

+
); diff --git a/web/src/app/admin/connectors/slack/page.tsx b/web/src/app/admin/connectors/slack/page.tsx index 13a45169b7..ae90a57bde 100644 --- a/web/src/app/admin/connectors/slack/page.tsx +++ b/web/src/app/admin/connectors/slack/page.tsx @@ -21,6 +21,8 @@ import { import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { usePublicCredentials } from "@/lib/hooks"; +import { Button, Card, Divider, Text, Title } from "@tremor/react"; +import { AdminPageTitle } from "@/components/admin/Title"; const MainSection = () => { const { mutate } = useSWRConfig(); @@ -69,25 +71,27 @@ const MainSection = () => { return ( <> -

+ Step 1: Provide Credentials - </h2> + {slackCredential ? ( <>
-

Existing Slack Bot Token:

-

+ Existing Slack Bot Token: + {slackCredential.credential_json.slack_bot_token} -

{" "} - +
) : ( @@ -104,7 +108,7 @@ const MainSection = () => { .

-
+ formBody={ <> @@ -129,20 +133,20 @@ const MainSection = () => { } }} /> -
+ )} -

+ Step 2: Which channels do you want to make searchable? - </h2> + {slackConnectorIndexingStatuses.length > 0 && ( <> -

+ We pull the latest messages from each workspace listed below every{" "} 10 minutes. -

+
connectorIndexingStatuses={slackConnectorIndexingStatuses} @@ -181,11 +185,12 @@ const MainSection = () => { }} />
+ )} {slackCredential ? ( -
+

Connect to a New Workspace

nameBuilder={(values) => @@ -226,13 +231,13 @@ const MainSection = () => { refreshFreq={10 * 60} // 10 minutes credentialId={slackCredential.id} /> -
+ ) : ( -

+ Please provide your slack bot token in Step 1 first! Once done with that, you can then specify which Slack channels you want to make searchable. -

+ )} ); @@ -244,10 +249,9 @@ export default function Page() {
-
- -

Slack

-
+ + } title="Slack" /> +

); diff --git a/web/src/app/admin/connectors/web/page.tsx b/web/src/app/admin/connectors/web/page.tsx index 11d8f9d119..60b90dda64 100644 --- a/web/src/app/admin/connectors/web/page.tsx +++ b/web/src/app/admin/connectors/web/page.tsx @@ -18,7 +18,8 @@ import { HealthCheckBanner } from "@/components/health/healthcheck"; import { ConnectorIndexingStatus, WebConfig } from "@/lib/types"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; -import { createCredential, linkCredential } from "@/lib/credential"; +import { AdminPageTitle } from "@/components/admin/Title"; +import { Card, Title } from "@tremor/react"; const SCRAPE_TYPE_TO_PRETTY_NAME = { recursive: "Recursive", @@ -49,17 +50,16 @@ export default function Web() {
-
- -

Web

-
-

+ + } title="Web" /> + + Step 1: Specify which websites to index - </h2> +

We re-fetch the latest state of the website once a day.

-
+ nameBuilder={(values) => `WebConnector-${values.base_url}`} ccPairNameBuilder={(values) => values.base_url} @@ -118,11 +118,11 @@ export default function Web() { }} refreshFreq={60 * 60 * 24} // 1 day /> -
+ -

+ Already Indexed Websites - </h2> + {isConnectorIndexingStatusesLoading ? ( ) : isConnectorIndexingStatusesError || !connectorIndexingStatuses ? ( diff --git a/web/src/app/admin/connectors/zendesk/page.tsx b/web/src/app/admin/connectors/zendesk/page.tsx index c8692e251d..ec03e4e70f 100644 --- a/web/src/app/admin/connectors/zendesk/page.tsx +++ b/web/src/app/admin/connectors/zendesk/page.tsx @@ -19,6 +19,8 @@ import { ConnectorForm } from "@/components/admin/connectors/ConnectorForm"; import { ConnectorsTable } from "@/components/admin/connectors/table/ConnectorsTable"; import { usePopup } from "@/components/admin/connectors/Popup"; import { usePublicCredentials } from "@/lib/hooks"; +import { AdminPageTitle } from "@/components/admin/Title"; +import { Card, Divider, Text, Title } from "@tremor/react"; const Main = () => { const { popup, setPopup } = usePopup(); @@ -69,9 +71,9 @@ const Main = () => { return ( <> {popup} -

- Step 1: Provide your API details -

+ + Provide your API details + {zendeskCredential ? ( <> @@ -81,7 +83,7 @@ const Main = () => { {zendeskCredential.credential_json?.zendesk_email}

); diff --git a/web/src/app/admin/documents/ScoreEditor.tsx b/web/src/app/admin/documents/ScoreEditor.tsx index c95df3a714..58b16662f5 100644 --- a/web/src/app/admin/documents/ScoreEditor.tsx +++ b/web/src/app/admin/documents/ScoreEditor.tsx @@ -2,6 +2,7 @@ import { PopupSpec } from "@/components/admin/connectors/Popup"; import { useState } from "react"; import { updateBoost } from "./lib"; import { CheckmarkIcon, EditIcon } from "@/components/icons/icons"; +import { FiEdit } from "react-icons/fi"; export const ScoreSection = ({ documentId, @@ -62,7 +63,7 @@ export const ScoreSection = ({ setScore(initialScore.toString()); } }} - className="border bg-slate-700 text-gray-200 border-gray-300 rounded py-1 px-3 w-16 h-5 my-auto" + className="border bg-background-strong border-gray-300 rounded py-1 px-1 w-12 h-4 my-auto" />
@@ -73,15 +74,15 @@ export const ScoreSection = ({ return (
-
-
+
setIsOpen(true)} + > +
{initialScore}
-
setIsOpen(true)} - > - +
+
diff --git a/web/src/app/admin/documents/explorer/Explorer.tsx b/web/src/app/admin/documents/explorer/Explorer.tsx index d289b9a0ab..b0e2433a53 100644 --- a/web/src/app/admin/documents/explorer/Explorer.tsx +++ b/web/src/app/admin/documents/explorer/Explorer.tsx @@ -30,7 +30,7 @@ const DocumentDisplay = ({ return (
-
+

Boost:

{document.hidden ? ( -
Hidden
+
Hidden
) : ( "Visible" )} @@ -95,7 +95,7 @@ const DocumentDisplay = ({
)} -

+

{buildDocumentSummaryDisplay(document.match_highlights, document.blurb)}

@@ -159,11 +159,11 @@ export function Explorer({
{popup}
-
- +
+