mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-28 21:05:17 +02:00
Slack flow improvements (#2366)
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
|||||||
} from "@/lib/types";
|
} from "@/lib/types";
|
||||||
import {
|
import {
|
||||||
BooleanFormField,
|
BooleanFormField,
|
||||||
SectionHeader,
|
Label,
|
||||||
SelectorFormField,
|
SelectorFormField,
|
||||||
SubLabel,
|
SubLabel,
|
||||||
TextArrayField,
|
TextArrayField,
|
||||||
@@ -20,22 +20,14 @@ import {
|
|||||||
isPersonaASlackBotPersona,
|
isPersonaASlackBotPersona,
|
||||||
updateSlackBotConfig,
|
updateSlackBotConfig,
|
||||||
} from "./lib";
|
} from "./lib";
|
||||||
import {
|
import { Button, Card, Divider } from "@tremor/react";
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Divider,
|
|
||||||
Tab,
|
|
||||||
TabGroup,
|
|
||||||
TabList,
|
|
||||||
TabPanel,
|
|
||||||
TabPanels,
|
|
||||||
Text,
|
|
||||||
} from "@tremor/react";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Persona } from "../assistants/interfaces";
|
import { Persona } from "../assistants/interfaces";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { BookmarkIcon, RobotIcon } from "@/components/icons/icons";
|
|
||||||
import MultiSelectDropdown from "@/components/MultiSelectDropdown";
|
import MultiSelectDropdown from "@/components/MultiSelectDropdown";
|
||||||
|
import { AdvancedOptionsToggle } from "@/components/AdvancedOptionsToggle";
|
||||||
|
import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable";
|
||||||
|
import CollapsibleSection from "../assistants/CollapsibleSection";
|
||||||
|
|
||||||
export const SlackBotCreationForm = ({
|
export const SlackBotCreationForm = ({
|
||||||
documentSets,
|
documentSets,
|
||||||
@@ -57,6 +49,9 @@ export const SlackBotCreationForm = ({
|
|||||||
const [usingPersonas, setUsingPersonas] = useState(
|
const [usingPersonas, setUsingPersonas] = useState(
|
||||||
existingSlackBotUsesPersona
|
existingSlackBotUsesPersona
|
||||||
);
|
);
|
||||||
|
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||||
|
|
||||||
|
const knowledgePersona = personas.find((persona) => persona.id === 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -66,7 +61,7 @@ export const SlackBotCreationForm = ({
|
|||||||
initialValues={{
|
initialValues={{
|
||||||
channel_names: existingSlackBotConfig
|
channel_names: existingSlackBotConfig
|
||||||
? existingSlackBotConfig.channel_config.channel_names
|
? existingSlackBotConfig.channel_config.channel_names
|
||||||
: ([] as string[]),
|
: ([""] as string[]),
|
||||||
answer_validity_check_enabled: (
|
answer_validity_check_enabled: (
|
||||||
existingSlackBotConfig?.channel_config?.answer_filters || []
|
existingSlackBotConfig?.channel_config?.answer_filters || []
|
||||||
).includes("well_answered_postfilter"),
|
).includes("well_answered_postfilter"),
|
||||||
@@ -93,11 +88,12 @@ export const SlackBotCreationForm = ({
|
|||||||
(documentSet) => documentSet.id
|
(documentSet) => documentSet.id
|
||||||
)
|
)
|
||||||
: ([] as number[]),
|
: ([] as number[]),
|
||||||
|
// prettier-ignore
|
||||||
persona_id:
|
persona_id:
|
||||||
existingSlackBotConfig?.persona &&
|
existingSlackBotConfig?.persona &&
|
||||||
!isPersonaASlackBotPersona(existingSlackBotConfig.persona)
|
!isPersonaASlackBotPersona(existingSlackBotConfig.persona)
|
||||||
? existingSlackBotConfig.persona.id
|
? existingSlackBotConfig.persona.id
|
||||||
: null,
|
: knowledgePersona?.id ?? null,
|
||||||
response_type: existingSlackBotConfig?.response_type || "citations",
|
response_type: existingSlackBotConfig?.response_type || "citations",
|
||||||
standard_answer_categories: existingSlackBotConfig
|
standard_answer_categories: existingSlackBotConfig
|
||||||
? existingSlackBotConfig.standard_answer_categories
|
? existingSlackBotConfig.standard_answer_categories
|
||||||
@@ -168,227 +164,56 @@ export const SlackBotCreationForm = ({
|
|||||||
>
|
>
|
||||||
{({ isSubmitting, values, setFieldValue }) => (
|
{({ isSubmitting, values, setFieldValue }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<div className="px-6 pb-6">
|
<div className="px-6 pb-6 pt-4 w-full">
|
||||||
<SectionHeader>The Basics</SectionHeader>
|
|
||||||
|
|
||||||
<TextArrayField
|
<TextArrayField
|
||||||
name="channel_names"
|
name="channel_names"
|
||||||
label="Channel Names"
|
label="Channel Names"
|
||||||
values={values}
|
values={values}
|
||||||
subtext={
|
subtext="The names of the Slack channels you want this configuration to apply to.
|
||||||
<div>
|
For example, #ask-danswer."
|
||||||
The names of the Slack channels you want this
|
minFields={1}
|
||||||
configuration to apply to. For example,
|
placeholder="Enter channel name..."
|
||||||
'#ask-danswer'.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<i>NOTE</i>: you still need to add DanswerBot to the
|
|
||||||
channel(s) in Slack itself. Setting this config will not
|
|
||||||
auto-add the bot to the channel.
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectorFormField
|
<div className="mt-6">
|
||||||
name="response_type"
|
<Label>Knowledge Sources</Label>
|
||||||
label="Response Format"
|
|
||||||
subtext={
|
|
||||||
<>
|
|
||||||
If set to Citations, DanswerBot will respond with a direct
|
|
||||||
answer with inline citations. It will also provide links
|
|
||||||
to these cited documents below the answer. When in doubt,
|
|
||||||
choose this option.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
If set to Quotes, DanswerBot will respond with a direct
|
|
||||||
answer as well as with quotes pulled from the context
|
|
||||||
documents to support that answer. DanswerBot will also
|
|
||||||
give a list of relevant documents. Choose this option if
|
|
||||||
you want a very detailed response AND/OR a list of
|
|
||||||
relevant documents would be useful just in case the LLM
|
|
||||||
missed anything.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
options={[
|
|
||||||
{ name: "Citations", value: "citations" },
|
|
||||||
{ name: "Quotes", value: "quotes" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<SectionHeader>When should DanswerBot respond?</SectionHeader>
|
|
||||||
|
|
||||||
<BooleanFormField
|
|
||||||
name="answer_validity_check_enabled"
|
|
||||||
label="Hide Non-Answers"
|
|
||||||
subtext="If set, will only answer questions that the model determines it can answer"
|
|
||||||
/>
|
|
||||||
<BooleanFormField
|
|
||||||
name="questionmark_prefilter_enabled"
|
|
||||||
label="Only respond to questions"
|
|
||||||
subtext="If set, will only respond to messages that contain a question mark"
|
|
||||||
/>
|
|
||||||
<BooleanFormField
|
|
||||||
name="respond_tag_only"
|
|
||||||
label="Respond to @DanswerBot Only"
|
|
||||||
subtext="If set, DanswerBot will only respond when directly tagged"
|
|
||||||
/>
|
|
||||||
<BooleanFormField
|
|
||||||
name="respond_to_bots"
|
|
||||||
label="Responds to Bot messages"
|
|
||||||
subtext="If not set, DanswerBot will always ignore messages from Bots"
|
|
||||||
/>
|
|
||||||
<BooleanFormField
|
|
||||||
name="enable_auto_filters"
|
|
||||||
label="Enable LLM Autofiltering"
|
|
||||||
subtext="If set, the LLM will generate source and time filters based on the user's query"
|
|
||||||
/>
|
|
||||||
<TextArrayField
|
|
||||||
name="respond_member_group_list"
|
|
||||||
label="Team Member Emails Or Slack Group Names/Handles"
|
|
||||||
subtext={`If specified, DanswerBot responses will only be
|
|
||||||
visible to the members or groups in this list. This is
|
|
||||||
useful if you want DanswerBot to operate in an
|
|
||||||
"assistant" mode, where it helps the team members find
|
|
||||||
answers, but let's them build on top of DanswerBot's response / throw
|
|
||||||
out the occasional incorrect answer. Group names and handles are case sensitive.`}
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<SectionHeader>Post Response Behavior</SectionHeader>
|
|
||||||
|
|
||||||
<BooleanFormField
|
|
||||||
name="still_need_help_enabled"
|
|
||||||
label="Should Danswer give a “Still need help?” button?"
|
|
||||||
subtext={`If specified, DanswerBot's response will include a button at the bottom
|
|
||||||
of the response that asks the user if they still need help.`}
|
|
||||||
/>
|
|
||||||
{values.still_need_help_enabled && (
|
|
||||||
<TextArrayField
|
|
||||||
name="follow_up_tags"
|
|
||||||
label="Users to Tag"
|
|
||||||
values={values}
|
|
||||||
subtext={
|
|
||||||
<div>
|
|
||||||
The full email addresses of the Slack users we should
|
|
||||||
tag if the user clicks the "Still need help?"
|
|
||||||
button. For example, 'mark@acme.com'.
|
|
||||||
<br />
|
|
||||||
Or provide a user group by either the name or the
|
|
||||||
handle. For example, 'Danswer Team' or
|
|
||||||
'danswer-team'.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
If no emails are provided, we will not tag anyone and
|
|
||||||
will just react with a 🆘 emoji to the original message.
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<SectionHeader>
|
|
||||||
[Optional] Data Sources and Prompts
|
|
||||||
</SectionHeader>
|
|
||||||
<Text>
|
|
||||||
Use either an Assistant <b>or</b> Document Sets to control
|
|
||||||
how DanswerBot answers.
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
<ul className="list-disc mt-2 ml-4">
|
|
||||||
<li>
|
|
||||||
You should use an Assistant if you also want to
|
|
||||||
customize the prompt and retrieval settings.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
You should use Document Sets if you just want to control
|
|
||||||
which documents DanswerBot uses as references.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</Text>
|
|
||||||
<Text className="mt-2">
|
|
||||||
<b>NOTE:</b> whichever tab you are when you submit the form
|
|
||||||
will be the one that is used. For example, if you are on the
|
|
||||||
"Assistants" tab, then the Assistant and its
|
|
||||||
attached knowledge will be used, even if you have Document
|
|
||||||
Sets selected.
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TabGroup
|
|
||||||
index={usingPersonas ? 1 : 0}
|
|
||||||
onIndexChange={(index) => setUsingPersonas(index === 1)}
|
|
||||||
>
|
|
||||||
<TabList className="mt-3 mb-4">
|
|
||||||
<Tab icon={BookmarkIcon}>Document Sets</Tab>
|
|
||||||
<Tab icon={RobotIcon}>Assistants</Tab>
|
|
||||||
</TabList>
|
|
||||||
<TabPanels>
|
|
||||||
<TabPanel>
|
|
||||||
<FieldArray
|
|
||||||
name="document_sets"
|
|
||||||
render={(arrayHelpers: ArrayHelpers) => (
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<SubLabel>
|
<SubLabel>
|
||||||
The document sets that DanswerBot should search
|
Controls which information DanswerBot will pull from when
|
||||||
through. If left blank, DanswerBot will search
|
answering questions.
|
||||||
through all documents.
|
|
||||||
</SubLabel>
|
</SubLabel>
|
||||||
</div>
|
|
||||||
<div className="mb-3 mt-2 flex gap-2 flex-wrap text-sm">
|
<div className="flex mt-4">
|
||||||
{documentSets.map((documentSet) => {
|
<button
|
||||||
const ind = values.document_sets.indexOf(
|
type="button"
|
||||||
documentSet.id
|
onClick={() => setUsingPersonas(false)}
|
||||||
);
|
className={`p-2 font-bold text-xs mr-3 ${
|
||||||
let isSelected = ind !== -1;
|
!usingPersonas
|
||||||
return (
|
? "rounded bg-background-900 text-text-100 underline"
|
||||||
<div
|
: "hover:underline bg-background-100"
|
||||||
key={documentSet.id}
|
}`}
|
||||||
className={
|
|
||||||
`
|
|
||||||
px-3
|
|
||||||
py-1
|
|
||||||
rounded-lg
|
|
||||||
border
|
|
||||||
border-border
|
|
||||||
w-fit
|
|
||||||
flex
|
|
||||||
cursor-pointer ` +
|
|
||||||
(isSelected
|
|
||||||
? " bg-hover"
|
|
||||||
: " bg-background hover:bg-hover-light")
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
if (isSelected) {
|
|
||||||
arrayHelpers.remove(ind);
|
|
||||||
} else {
|
|
||||||
arrayHelpers.push(documentSet.id);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="my-auto">
|
Document Sets
|
||||||
{documentSet.name}
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setUsingPersonas(true)}
|
||||||
|
className={`p-2 font-bold text-xs ${
|
||||||
|
usingPersonas
|
||||||
|
? "rounded bg-background-900 text-text-100 underline"
|
||||||
|
: "hover:underline bg-background-100"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Assistants
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
<div className="mt-4">
|
||||||
})}
|
{/* TODO: make this look nicer */}
|
||||||
</div>
|
{usingPersonas ? (
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel>
|
|
||||||
<SelectorFormField
|
<SelectorFormField
|
||||||
name="persona_id"
|
name="persona_id"
|
||||||
subtext={`
|
|
||||||
The assistant to use when responding to queries. The "Knowledge" assistant acts
|
|
||||||
as a question-answering assistant and has access to all documents indexed by non-private connectors.
|
|
||||||
`}
|
|
||||||
options={personas.map((persona) => {
|
options={personas.map((persona) => {
|
||||||
return {
|
return {
|
||||||
name: persona.name,
|
name: persona.name,
|
||||||
@@ -396,16 +221,142 @@ export const SlackBotCreationForm = ({
|
|||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
) : (
|
||||||
</TabPanels>
|
<FieldArray
|
||||||
</TabGroup>
|
name="document_sets"
|
||||||
|
render={(arrayHelpers: ArrayHelpers) => (
|
||||||
|
<div>
|
||||||
|
<div className="mb-3 mt-2 flex gap-2 flex-wrap text-sm">
|
||||||
|
{documentSets.map((documentSet) => {
|
||||||
|
const ind = values.document_sets.indexOf(
|
||||||
|
documentSet.id
|
||||||
|
);
|
||||||
|
let isSelected = ind !== -1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DocumentSetSelectable
|
||||||
|
key={documentSet.id}
|
||||||
|
documentSet={documentSet}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onSelect={() => {
|
||||||
|
if (isSelected) {
|
||||||
|
arrayHelpers.remove(ind);
|
||||||
|
} else {
|
||||||
|
arrayHelpers.push(documentSet.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<SubLabel>
|
||||||
|
Note: If left blank, DanswerBot will search
|
||||||
|
through all connected documents.
|
||||||
|
</SubLabel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
<AdvancedOptionsToggle
|
||||||
|
showAdvancedOptions={showAdvancedOptions}
|
||||||
|
setShowAdvancedOptions={setShowAdvancedOptions}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{showAdvancedOptions && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="w-64 mb-4">
|
||||||
|
<SelectorFormField
|
||||||
|
name="response_type"
|
||||||
|
label="Answer Type"
|
||||||
|
tooltip="Controls the format of DanswerBot's responses."
|
||||||
|
options={[
|
||||||
|
{ name: "Standard", value: "citations" },
|
||||||
|
{ name: "Detailed", value: "quotes" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-3 mt-2">
|
||||||
|
<BooleanFormField
|
||||||
|
name="still_need_help_enabled"
|
||||||
|
removeIndent
|
||||||
|
label={'Give a "Still need help?" button'}
|
||||||
|
tooltip={`DanswerBot's response will include a button at the bottom
|
||||||
|
of the response that asks the user if they still need help.`}
|
||||||
|
/>
|
||||||
|
{values.still_need_help_enabled && (
|
||||||
|
<CollapsibleSection prompt="Configure Still Need Help Button">
|
||||||
|
<TextArrayField
|
||||||
|
name="follow_up_tags"
|
||||||
|
label="(Optional) Users / Groups to Tag"
|
||||||
|
values={values}
|
||||||
|
subtext={
|
||||||
<div>
|
<div>
|
||||||
<SectionHeader>
|
The Slack users / groups we should tag if the
|
||||||
[Optional] Standard Answer Categories
|
user clicks the "Still need help?"
|
||||||
</SectionHeader>
|
button. If no emails are provided, we will not
|
||||||
|
tag anyone and will just react with a 🆘 emoji
|
||||||
|
to the original message.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
placeholder="User email or user group name..."
|
||||||
|
/>
|
||||||
|
</CollapsibleSection>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<BooleanFormField
|
||||||
|
name="answer_validity_check_enabled"
|
||||||
|
removeIndent
|
||||||
|
label="Hide Non-Answers"
|
||||||
|
tooltip="If set, will only answer questions that the model determines it can answer"
|
||||||
|
/>
|
||||||
|
<BooleanFormField
|
||||||
|
name="questionmark_prefilter_enabled"
|
||||||
|
removeIndent
|
||||||
|
label="Only respond to questions"
|
||||||
|
tooltip="If set, will only respond to messages that contain a question mark"
|
||||||
|
/>
|
||||||
|
<BooleanFormField
|
||||||
|
name="respond_tag_only"
|
||||||
|
removeIndent
|
||||||
|
label="Respond to @DanswerBot Only"
|
||||||
|
tooltip="If set, DanswerBot will only respond when directly tagged"
|
||||||
|
/>
|
||||||
|
<BooleanFormField
|
||||||
|
name="respond_to_bots"
|
||||||
|
removeIndent
|
||||||
|
label="Respond to Bot messages"
|
||||||
|
tooltip="If not set, DanswerBot will always ignore messages from Bots"
|
||||||
|
/>
|
||||||
|
<BooleanFormField
|
||||||
|
name="enable_auto_filters"
|
||||||
|
removeIndent
|
||||||
|
label="Enable LLM Autofiltering"
|
||||||
|
tooltip="If set, the LLM will generate source and time filters based on the user's query"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="mt-12">
|
||||||
|
<TextArrayField
|
||||||
|
name="respond_member_group_list"
|
||||||
|
label="(Optional) Respond to Certain Users / Groups"
|
||||||
|
subtext={
|
||||||
|
"If specified, DanswerBot responses will only " +
|
||||||
|
"be visible to the members or groups in this list."
|
||||||
|
}
|
||||||
|
values={values}
|
||||||
|
placeholder="User email or user group name..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Label>Standard Answer Categories</Label>
|
||||||
<div className="w-4/12">
|
<div className="w-4/12">
|
||||||
<MultiSelectDropdown
|
<MultiSelectDropdown
|
||||||
name="standard_answer_categories"
|
name="standard_answer_categories"
|
||||||
@@ -438,8 +389,7 @@ export const SlackBotCreationForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Button
|
<Button
|
||||||
|
@@ -2,15 +2,8 @@ import { Form, Formik } from "formik";
|
|||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
||||||
import { SlackBotTokens } from "@/lib/types";
|
import { SlackBotTokens } from "@/lib/types";
|
||||||
import {
|
import { TextFormField } from "@/components/admin/connectors/Field";
|
||||||
TextArrayField,
|
import { setSlackBotTokens } from "./lib";
|
||||||
TextFormField,
|
|
||||||
} from "@/components/admin/connectors/Field";
|
|
||||||
import {
|
|
||||||
createSlackBotConfig,
|
|
||||||
setSlackBotTokens,
|
|
||||||
updateSlackBotConfig,
|
|
||||||
} from "./lib";
|
|
||||||
import { Button, Card } from "@tremor/react";
|
import { Button, Card } from "@tremor/react";
|
||||||
|
|
||||||
interface SlackBotTokensFormProps {
|
interface SlackBotTokensFormProps {
|
||||||
|
@@ -66,11 +66,6 @@ async function Page() {
|
|||||||
title="New Slack Bot Config"
|
title="New Slack Bot Config"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text className="mb-8">
|
|
||||||
Define a new configuration below! This config will determine how
|
|
||||||
DanswerBot behaves in the specified channels.
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<SlackBotCreationForm
|
<SlackBotCreationForm
|
||||||
documentSets={documentSets}
|
documentSets={documentSets}
|
||||||
personas={assistants}
|
personas={assistants}
|
||||||
|
@@ -42,13 +42,30 @@ export function Label({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`block font-medium base ${className} ${small ? "text-sm" : "text-base"}`}
|
className={`block font-medium base ${className} ${
|
||||||
|
small ? "text-sm" : "text-base"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function LabelWithTooltip({
|
||||||
|
children,
|
||||||
|
tooltip,
|
||||||
|
}: {
|
||||||
|
children: string | JSX.Element;
|
||||||
|
tooltip: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<Label>{children}</Label>
|
||||||
|
<ToolTipDetails>{tooltip}</ToolTipDetails>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function SubLabel({ children }: { children: string | JSX.Element }) {
|
export function SubLabel({ children }: { children: string | JSX.Element }) {
|
||||||
return <div className="text-sm text-subtle mb-2">{children}</div>;
|
return <div className="text-sm text-subtle mb-2">{children}</div>;
|
||||||
}
|
}
|
||||||
@@ -378,6 +395,7 @@ interface BooleanFormFieldProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
optional?: boolean;
|
optional?: boolean;
|
||||||
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BooleanFormField = ({
|
export const BooleanFormField = ({
|
||||||
@@ -392,6 +410,7 @@ export const BooleanFormField = ({
|
|||||||
disabled,
|
disabled,
|
||||||
alignTop,
|
alignTop,
|
||||||
checked,
|
checked,
|
||||||
|
tooltip,
|
||||||
}: BooleanFormFieldProps) => {
|
}: BooleanFormFieldProps) => {
|
||||||
const [field, meta, helpers] = useField<boolean>(name);
|
const [field, meta, helpers] = useField<boolean>(name);
|
||||||
const { setValue } = helpers;
|
const { setValue } = helpers;
|
||||||
@@ -417,13 +436,17 @@ export const BooleanFormField = ({
|
|||||||
/>
|
/>
|
||||||
{!noLabel && (
|
{!noLabel && (
|
||||||
<div>
|
<div>
|
||||||
<Label
|
<div className="flex items-center gap-x-2">
|
||||||
small={small}
|
<Label small={small}>{`${label}${
|
||||||
>{`${label}${optional ? " (Optional)" : ""}`}</Label>
|
optional ? " (Optional)" : ""
|
||||||
|
}`}</Label>
|
||||||
|
{tooltip && <ToolTipDetails>{tooltip}</ToolTipDetails>}
|
||||||
|
</div>
|
||||||
{subtext && <SubLabel>{subtext}</SubLabel>}
|
{subtext && <SubLabel>{subtext}</SubLabel>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name={name}
|
name={name}
|
||||||
component="div"
|
component="div"
|
||||||
@@ -439,6 +462,9 @@ interface TextArrayFieldProps<T extends Yup.AnyObject> {
|
|||||||
values: T;
|
values: T;
|
||||||
subtext?: string | JSX.Element;
|
subtext?: string | JSX.Element;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
minFields?: number;
|
||||||
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextArrayField<T extends Yup.AnyObject>({
|
export function TextArrayField<T extends Yup.AnyObject>({
|
||||||
@@ -447,10 +473,16 @@ export function TextArrayField<T extends Yup.AnyObject>({
|
|||||||
values,
|
values,
|
||||||
subtext,
|
subtext,
|
||||||
type,
|
type,
|
||||||
|
tooltip,
|
||||||
|
minFields = 0,
|
||||||
|
placeholder = "",
|
||||||
}: TextArrayFieldProps<T>) {
|
}: TextArrayFieldProps<T>) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
|
<div className="flex gap-x-2 items-center">
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
|
{tooltip && <ToolTipDetails>{tooltip}</ToolTipDetails>}
|
||||||
|
</div>
|
||||||
{subtext && <SubLabel>{subtext}</SubLabel>}
|
{subtext && <SubLabel>{subtext}</SubLabel>}
|
||||||
|
|
||||||
<FieldArray
|
<FieldArray
|
||||||
@@ -478,12 +510,17 @@ export function TextArrayField<T extends Yup.AnyObject>({
|
|||||||
`}
|
`}
|
||||||
// Disable autocomplete since the browser doesn't know how to handle an array of text fields
|
// Disable autocomplete since the browser doesn't know how to handle an array of text fields
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
placeholder={placeholder}
|
||||||
/>
|
/>
|
||||||
<div className="my-auto">
|
<div className="my-auto">
|
||||||
|
{index >= minFields ? (
|
||||||
<FiX
|
<FiX
|
||||||
className="my-auto w-10 h-10 cursor-pointer hover:bg-hover rounded p-2"
|
className="my-auto w-10 h-10 cursor-pointer hover:bg-hover rounded p-2"
|
||||||
onClick={() => arrayHelpers.remove(index)}
|
onClick={() => arrayHelpers.remove(index)}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-10 h-10" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
@@ -518,6 +555,7 @@ interface TextArrayFieldBuilderProps<T extends Yup.AnyObject> {
|
|||||||
label: string;
|
label: string;
|
||||||
subtext?: string | JSX.Element;
|
subtext?: string | JSX.Element;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextArrayFieldBuilder<T extends Yup.AnyObject>(
|
export function TextArrayFieldBuilder<T extends Yup.AnyObject>(
|
||||||
@@ -539,6 +577,7 @@ interface SelectorFormFieldProps {
|
|||||||
maxHeight?: string;
|
maxHeight?: string;
|
||||||
onSelect?: (selected: string | number | null) => void;
|
onSelect?: (selected: string | number | null) => void;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SelectorFormField({
|
export function SelectorFormField({
|
||||||
@@ -551,13 +590,19 @@ export function SelectorFormField({
|
|||||||
maxHeight,
|
maxHeight,
|
||||||
onSelect,
|
onSelect,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
|
tooltip,
|
||||||
}: SelectorFormFieldProps) {
|
}: SelectorFormFieldProps) {
|
||||||
const [field] = useField<string>(name);
|
const [field] = useField<string>(name);
|
||||||
const { setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{label && <Label>{label}</Label>}
|
{label && (
|
||||||
|
<div className="flex gap-x-2 items-center">
|
||||||
|
<Label>{label}</Label>
|
||||||
|
{tooltip && <ToolTipDetails>{tooltip}</ToolTipDetails>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{subtext && <SubLabel>{subtext}</SubLabel>}
|
{subtext && <SubLabel>{subtext}</SubLabel>}
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<DefaultDropdown
|
<DefaultDropdown
|
||||||
|
Reference in New Issue
Block a user