Custom banner

This commit is contained in:
Weves 2024-04-10 12:23:09 -07:00 committed by Chris Weaver
parent 91cf45165f
commit 3466f6d3a4
12 changed files with 195 additions and 10 deletions

View File

@ -304,9 +304,7 @@ def _prepare_index_attempt(db_session: Session, index_attempt_id: int) -> IndexA
return attempt
def run_indexing_entrypoint(
index_attempt_id: int, is_ee: bool = False
) -> None:
def run_indexing_entrypoint(index_attempt_id: int, is_ee: bool = False) -> None:
"""Entrypoint for indexing run when using dask distributed.
Wraps the actual logic in a `try` block so that we can catch any exceptions
and mark the attempt as failed."""

View File

@ -35,10 +35,10 @@ from danswer.db.models import IndexModelStatus
from danswer.db.swap_index import check_index_swap
from danswer.search.search_nlp_models import warm_up_encoders
from danswer.utils.logger import setup_logger
from danswer.utils.variable_functionality import global_version
from shared_configs.configs import INDEXING_MODEL_SERVER_HOST
from shared_configs.configs import LOG_LEVEL
from shared_configs.configs import MODEL_SERVER_PORT
from danswer.utils.variable_functionality import global_version
logger = setup_logger()

View File

@ -9,5 +9,10 @@ class EnterpriseSettings(BaseModel):
application_name: str | None = None
use_custom_logo: bool = False
# custom Chat components
custom_header_content: str | None = None
custom_popup_header: str | None = None
custom_popup_content: str | None = None
def check_validity(self) -> None:
return

View File

@ -41,13 +41,14 @@ import { usePopup } from "@/components/admin/connectors/Popup";
import { ResizableSection } from "@/components/resizable/ResizableSection";
import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader";
import { ChatIntro } from "./ChatIntro";
import { HEADER_PADDING } from "@/lib/constants";
import { computeAvailableFilters } from "@/lib/filters";
import { useDocumentSelection } from "./useDocumentSelection";
import { StarterMessage } from "./StarterMessage";
import { ShareChatSessionModal } from "./modal/ShareChatSessionModal";
import { SEARCH_PARAM_NAMES, shouldSubmitOnLoad } from "./searchParams";
import { Persona } from "../admin/assistants/interfaces";
import { ChatBanner } from "./ChatBanner";
import { HEADER_PADDING } from "@/lib/constants";
const MAX_INPUT_HEIGHT = 200;
@ -594,6 +595,10 @@ export const Chat = ({
className={`w-full h-full ${HEADER_PADDING} flex flex-col overflow-y-auto overflow-x-hidden relative`}
ref={scrollableDivRef}
>
{/* ChatBanner is a custom banner that displays a admin-specified message at
the top of the chat page. Only used in the EE version of the app. */}
<ChatBanner />
{livePersona && (
<div className="sticky top-0 left-80 z-10 w-full bg-background/90 flex">
<div className="ml-2 p-1 rounded mt-2 w-fit">

View File

@ -0,0 +1,48 @@
"use client";
import ReactMarkdown from "react-markdown";
import { SettingsContext } from "@/components/settings/SettingsProviderClientSideHelper";
import { useContext } from "react";
import remarkGfm from "remark-gfm";
export function ChatBanner() {
const settings = useContext(SettingsContext);
if (!settings?.enterpriseSettings?.custom_header_content) {
return null;
}
return (
<div
className={`
z-[39]
w-full
h-[30px]
bg-background-custom-header
border-border
border-b
flex`}
>
<div className="mx-auto text-emphasis text-sm flex flex-col">
<div className="my-auto">
<ReactMarkdown
className="prose max-w-full"
components={{
a: ({ node, ...props }) => (
<a
{...props}
className="text-sm text-link hover:text-link-hover"
target="_blank"
rel="noopener noreferrer"
/>
),
p: ({ node, ...props }) => <p {...props} className="text-sm" />,
}}
remarkPlugins={[remarkGfm]}
>
{settings.enterpriseSettings.custom_header_content}
</ReactMarkdown>
</div>
</div>
</div>
);
}

View File

@ -72,6 +72,7 @@ import { useChatContext } from "@/components/context/ChatContext";
import { UserDropdown } from "@/components/UserDropdown";
import { v4 as uuidv4 } from "uuid";
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
import { ChatPopup } from "./ChatPopup";
const MAX_INPUT_HEIGHT = 200;
const TEMP_USER_MESSAGE_ID = -1;
@ -872,6 +873,10 @@ export function ChatPage({
<HealthCheckBanner />
<InstantSSRAutoRefresh />
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
Only used in the EE version of the app. */}
<ChatPopup />
<div className="flex relative bg-background text-default overflow-x-hidden">
<ChatSidebar
existingChats={chatSessions}

View File

@ -0,0 +1,73 @@
"use client";
import { Modal } from "@/components/Modal";
import { SettingsContext } from "@/components/settings/SettingsProviderClientSideHelper";
import { Button } from "@tremor/react";
import { useContext, useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
const ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED =
"allUsersInitialPopupFlowCompleted";
export function ChatPopup() {
const [completedFlow, setCompletedFlow] = useState(true);
useEffect(() => {
setCompletedFlow(
localStorage.getItem(ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED) === "true"
);
});
const settings = useContext(SettingsContext);
if (!settings?.enterpriseSettings?.custom_popup_content || completedFlow) {
return null;
}
let popupTitle = settings.enterpriseSettings.custom_popup_header;
if (!popupTitle) {
popupTitle = `Welcome to ${
settings.enterpriseSettings.application_name || "Danswer"
}!`;
}
return (
<Modal width="w-3/6 xl:w-[700px]" title={popupTitle}>
<>
<ReactMarkdown
className="prose max-w-full"
components={{
a: ({ node, ...props }) => (
<a
{...props}
className="text-link hover:text-link-hover"
target="_blank"
rel="noopener noreferrer"
/>
),
p: ({ node, ...props }) => <p {...props} className="text-sm" />,
}}
remarkPlugins={[remarkGfm]}
>
{settings.enterpriseSettings.custom_popup_content}
</ReactMarkdown>
<div className="flex w-full">
<Button
className="mx-auto mt-4"
size="xs"
onClick={() => {
localStorage.setItem(
ALL_USERS_INITIAL_POPUP_FLOW_COMPLETED,
"true"
);
setCompletedFlow(true);
}}
>
Get started!
</Button>
</div>
</>
</Modal>
);
}

View File

@ -7,8 +7,8 @@ import { SelectedDocumentDisplay } from "./SelectedDocumentDisplay";
import { removeDuplicateDocs } from "@/lib/documentUtils";
import { BasicSelectable } from "@/components/BasicClickable";
import { Message, RetrievalType } from "../interfaces";
import { HEADER_PADDING } from "@/lib/constants";
import { HoverPopup } from "@/components/HoverPopup";
import { HEADER_PADDING } from "@/lib/constants";
function SectionHeader({
name,

View File

@ -11,7 +11,7 @@ import {
SubLabel,
TextFormField,
} from "@/components/admin/connectors/Field";
import { Button } from "@tremor/react";
import { Button, Divider } from "@tremor/react";
import { ImageUpload } from "./ImageUpload";
export function WhitelabelingForm() {
@ -42,7 +42,6 @@ export function WhitelabelingForm() {
alert(`Failed to update settings. ${errorMsg}`);
}
}
console.log(enterpriseSettings);
return (
<div>
@ -50,10 +49,17 @@ export function WhitelabelingForm() {
initialValues={{
application_name: enterpriseSettings?.application_name || null,
use_custom_logo: enterpriseSettings?.use_custom_logo || false,
custom_header_content:
enterpriseSettings?.custom_header_content || "",
custom_popup_header: enterpriseSettings?.custom_popup_header || "",
custom_popup_content: enterpriseSettings?.custom_popup_content || "",
}}
validationSchema={Yup.object().shape({
application_name: Yup.string(),
application_name: Yup.string().nullable(),
use_custom_logo: Yup.boolean().required(),
custom_header_content: Yup.string().nullable(),
custom_popup_header: Yup.string().nullable(),
custom_popup_content: Yup.string().nullable(),
})}
onSubmit={async (values, formikHelpers) => {
formikHelpers.setSubmitting(true);
@ -138,6 +144,44 @@ export function WhitelabelingForm() {
setSelectedFile={setSelectedFile}
/>
<Divider />
<div className="mt-4">
<TextFormField
label="Custom Chat Header Content"
name="custom_header_content"
subtext={`Custom Markdown content that will be displayed as a banner at the top of the Chat page.`}
placeholder="Your header content..."
disabled={isSubmitting}
/>
</div>
<Divider />
<div className="mt-4">
<TextFormField
label="Custom Popup Header"
name="custom_popup_header"
subtext={`The title for the popup that will be displayed for each user on their initial visit
to the application. If left blank AND Custom Popup Content is specified, will use "Welcome to ${
values.application_name || "Danswer"
}!".`}
placeholder="Initial Popup Header"
disabled={isSubmitting}
/>
</div>
<div className="mt-4">
<TextFormField
label="Custom Popup Content"
name="custom_popup_content"
subtext={`Custom Markdown content that will be displayed as a popup on initial visit to the application.`}
placeholder="Your popup content..."
isTextArea
disabled={isSubmitting}
/>
</div>
<Button type="submit" className="mt-4">
Update
</Button>

View File

@ -23,6 +23,7 @@ import { personaComparator } from "../admin/assistants/lib";
import { FullEmbeddingModelResponse } from "../admin/models/embedding/embeddingModels";
import { NoSourcesModal } from "@/components/initialSetup/search/NoSourcesModal";
import { NoCompleteSourcesModal } from "@/components/initialSetup/search/NoCompleteSourceModal";
import { ChatPopup } from "../chat/ChatPopup";
export default async function Home() {
// Disable caching so we always get the up to date connector / document set / persona info
@ -163,6 +164,10 @@ export default async function Home() {
<NoCompleteSourcesModal ccPairs={ccPairs} />
)}
{/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit.
Only used in the EE version of the app. */}
<ChatPopup />
<InstantSSRAutoRefresh />
<div className="px-24 pt-10 flex flex-col items-center min-h-screen">

View File

@ -15,7 +15,7 @@ export const GOOGLE_DRIVE_AUTH_IS_ADMIN_COOKIE_NAME =
export const SEARCH_TYPE_COOKIE_NAME = "search_type";
export const HEADER_PADDING = "pt-[64px]";
export const HEADER_PADDING = `pt-[64px]`;
export const LOGOUT_DISABLED =
process.env.NEXT_PUBLIC_DISABLE_LOGOUT?.toLowerCase() === "true";

View File

@ -42,9 +42,11 @@ module.exports = {
"background-emphasis": "#f6f7f8",
"background-strong": "#eaecef",
"background-search": "#ffffff",
"background-custom-header": "#f3f4f6",
// text or icons
link: "#3b82f6", // blue-500
"link-hover": "#1d4ed8", // blue-700
subtle: "#6b7280", // gray-500
default: "#4b5563", // gray-600
emphasis: "#374151", // gray-700