Merge pull request #3214 from danswer-ai/fix-slack-ui

cleaned up new slack bot creation
This commit is contained in:
hagen-danswer
2024-11-23 10:53:47 -08:00
committed by GitHub
10 changed files with 217 additions and 211 deletions

View File

@@ -0,0 +1,45 @@
"""remove default bot
Revision ID: 6d562f86c78b
Revises: 177de57c21c9
Create Date: 2024-11-22 11:51:29.331336
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "6d562f86c78b"
down_revision = "177de57c21c9"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute(
sa.text(
"""
DELETE FROM slack_bot
WHERE name = 'Default Bot'
AND bot_token = ''
AND app_token = ''
AND NOT EXISTS (
SELECT 1 FROM slack_channel_config
WHERE slack_channel_config.slack_bot_id = slack_bot.id
)
"""
)
)
def downgrade() -> None:
op.execute(
sa.text(
"""
INSERT INTO slack_bot (name, enabled, bot_token, app_token)
SELECT 'Default Bot', true, '', ''
WHERE NOT EXISTS (SELECT 1 FROM slack_bot)
RETURNING id;
"""
)
)

View File

@@ -1,9 +1,12 @@
"use client";
import CardSection from "@/components/admin/CardSection";
import { usePopup } from "@/components/admin/connectors/Popup";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { SlackTokensForm } from "./SlackTokensForm";
import { SourceIcon } from "@/components/SourceIcon";
import { AdminPageTitle } from "@/components/admin/Title";
export const NewSlackBotForm = ({}: {}) => {
const [formValues] = useState({
@@ -17,15 +20,21 @@ export const NewSlackBotForm = ({}: {}) => {
return (
<div>
{popup}
<div className="p-4">
<SlackTokensForm
isUpdate={false}
initialValues={formValues}
setPopup={setPopup}
router={router}
/>
</div>
<AdminPageTitle
icon={<SourceIcon iconSize={36} sourceType={"slack"} />}
title="New Slack Bot"
/>
<CardSection>
{popup}
<div className="p-4">
<SlackTokensForm
isUpdate={false}
initialValues={formValues}
setPopup={setPopup}
router={router}
/>
</div>
</CardSection>
</div>
);
};

View File

@@ -1,92 +0,0 @@
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { PopupSpec } from "@/components/admin/connectors/Popup";
import { SlackBot } from "@/lib/types";
import { TextFormField } from "@/components/admin/connectors/Field";
import CardSection from "@/components/admin/CardSection";
import { Button } from "@/components/ui/button";
import { updateSlackBot, SlackBotCreationRequest } from "./new/lib";
interface SlackBotTokensFormProps {
onClose: () => void;
setPopup: (popupSpec: PopupSpec | null) => void;
existingSlackApp?: SlackBot;
onTokensSet?: (tokens: { bot_token: string; app_token: string }) => void;
embedded?: boolean;
noForm?: boolean;
}
export const SlackBotTokensForm = ({
onClose,
setPopup,
existingSlackApp,
onTokensSet,
embedded = true,
noForm = true,
}: SlackBotTokensFormProps) => {
const Wrapper = embedded ? "div" : CardSection;
const FormWrapper = noForm ? "div" : Form;
return (
<Wrapper className="w-full">
<Formik
initialValues={existingSlackApp || { app_token: "", bot_token: "" }}
validationSchema={Yup.object().shape({
bot_token: Yup.string().required(),
app_token: Yup.string().required(),
})}
onSubmit={async (values, formikHelpers) => {
if (embedded && onTokensSet) {
onTokensSet(values);
return;
}
formikHelpers.setSubmitting(true);
const response = await updateSlackBot(
existingSlackApp?.id || 0,
values as SlackBotCreationRequest
);
formikHelpers.setSubmitting(false);
if (response.ok) {
setPopup({
message: "Successfully set Slack tokens!",
type: "success",
});
onClose();
} else {
const errorMsg = await response.text();
setPopup({
message: `Error setting Slack tokens - ${errorMsg}`,
type: "error",
});
}
}}
>
{({ isSubmitting }) => (
<FormWrapper className="w-full">
<TextFormField
width="w-full"
name="bot_token"
label="Slack Bot Token"
type="password"
/>
<TextFormField
width="w-full"
name="app_token"
label="Slack App Token"
type="password"
/>
{!embedded && (
<div className="flex w-full">
<Button type="submit" disabled={isSubmitting} variant="submit">
Set Tokens
</Button>
</div>
)}
</FormWrapper>
)}
</Formik>
</Wrapper>
);
};

View File

@@ -75,17 +75,19 @@ export const ExistingSlackBotForm = ({
return (
<div>
{popup}
<div className="flex items-center justify-between">
<div className="flex items-center justify-between h-14">
<div className="flex items-center gap-2">
<div className="my-auto">
<SourceIcon iconSize={36} sourceType={"slack"} />
<SourceIcon iconSize={32} sourceType={"slack"} />
</div>
<div className="ml-1">
<EditableStringFieldDisplay
value={formValues.name}
isEditable={true}
onUpdate={(value) => handleUpdateField("name", value)}
scale={2.1}
/>
</div>
<EditableStringFieldDisplay
value={formValues.name}
isEditable={true}
onUpdate={(value) => handleUpdateField("name", value)}
scale={2.5}
/>
</div>
<div className="flex flex-col" ref={dropdownRef}>
@@ -108,6 +110,7 @@ export const ExistingSlackBotForm = ({
onClick={() => setShowDeleteModal(true)}
icon={FiTrash}
tooltip="Click to delete"
className="border h-[42px]"
>
Delete
</Button>
@@ -123,14 +126,15 @@ export const ExistingSlackBotForm = ({
refreshSlackBot={refreshSlackBot}
setPopup={setPopup}
router={router}
onValuesChange={(values) => setFormValues(values)}
/>
</div>
</div>
)}
</div>
</div>
<div className="mt-4">
<div className="inline-block border rounded-lg border-gray-200 px-2 py-2">
<div className="mt-2">
<div className="inline-block border rounded-lg border-gray-200 p-2">
<Checkbox
label="Enabled"
checked={formValues.enabled}

View File

@@ -5,7 +5,8 @@ import { Form, Formik } from "formik";
import * as Yup from "yup";
import { createSlackBot, updateSlackBot } from "./new/lib";
import { Button } from "@/components/ui/button";
import { SourceIcon } from "@/components/SourceIcon";
import { Separator } from "@/components/ui/separator";
import { useEffect } from "react";
export const SlackTokensForm = ({
isUpdate,
@@ -14,6 +15,7 @@ export const SlackTokensForm = ({
refreshSlackBot,
setPopup,
router,
onValuesChange,
}: {
isUpdate: boolean;
initialValues: any;
@@ -21,88 +23,112 @@ export const SlackTokensForm = ({
refreshSlackBot?: () => void;
setPopup: (popup: { message: string; type: "error" | "success" }) => void;
router: any;
}) => (
<Formik
initialValues={initialValues}
validationSchema={Yup.object().shape({
bot_token: Yup.string().required(),
app_token: Yup.string().required(),
name: Yup.string().required(),
})}
onSubmit={async (values, formikHelpers) => {
formikHelpers.setSubmitting(true);
onValuesChange?: (values: any) => void;
}) => {
useEffect(() => {
if (onValuesChange) {
onValuesChange(initialValues);
}
}, [initialValues]);
let response;
if (isUpdate) {
response = await updateSlackBot(existingSlackBotId!, values);
} else {
response = await createSlackBot(values);
}
formikHelpers.setSubmitting(false);
if (response.ok) {
if (refreshSlackBot) {
refreshSlackBot();
return (
<Formik
initialValues={initialValues}
validationSchema={Yup.object().shape({
bot_token: Yup.string().required(),
app_token: Yup.string().required(),
name: Yup.string().required(),
})}
onSubmit={async (values, formikHelpers) => {
formikHelpers.setSubmitting(true);
let response;
if (isUpdate) {
response = await updateSlackBot(existingSlackBotId!, values);
} else {
response = await createSlackBot(values);
}
const responseJson = await response.json();
const botId = isUpdate ? existingSlackBotId : responseJson.id;
setPopup({
message: isUpdate
? "Successfully updated Slack Bot!"
: "Successfully created Slack Bot!",
type: "success",
});
router.push(`/admin/bots/${encodeURIComponent(botId)}`);
} else {
const responseJson = await response.json();
const errorMsg = responseJson.detail || responseJson.message;
setPopup({
message: isUpdate
? `Error updating Slack Bot - ${errorMsg}`
: `Error creating Slack Bot - ${errorMsg}`,
type: "error",
});
}
}}
enableReinitialize={true}
>
{({ isSubmitting, setFieldValue, values }) => (
<Form className="w-full">
{!isUpdate && (
<div className="flex items-center gap-2 mb-4">
<div className="my-auto">
<SourceIcon iconSize={36} sourceType={"slack"} />
formikHelpers.setSubmitting(false);
if (response.ok) {
if (refreshSlackBot) {
refreshSlackBot();
}
const responseJson = await response.json();
const botId = isUpdate ? existingSlackBotId : responseJson.id;
setPopup({
message: isUpdate
? "Successfully updated Slack Bot!"
: "Successfully created Slack Bot!",
type: "success",
});
router.push(`/admin/bots/${encodeURIComponent(botId)}`);
} else {
const responseJson = await response.json();
const errorMsg = responseJson.detail || responseJson.message;
setPopup({
message: isUpdate
? `Error updating Slack Bot - ${errorMsg}`
: `Error creating Slack Bot - ${errorMsg}`,
type: "error",
});
}
}}
enableReinitialize={true}
>
{({ isSubmitting, setFieldValue, values }) => (
<Form className="w-full">
{!isUpdate && (
<div className="">
<TextFormField
name="name"
label="Name This Slack Bot:"
type="text"
/>
</div>
<TextFormField name="name" label="Slack Bot Name" type="text" />
</div>
)}
)}
{!isUpdate && (
<div className="mb-4">
Please enter your Slack Bot Token and Slack App Token to give
Danswerbot access to your Slack!
{!isUpdate && (
<div className="mt-4">
<Separator />
Please refer to our{" "}
<a
className="text-blue-500 hover:underline"
href="https://docs.danswer.dev/slack_bot_setup"
target="_blank"
rel="noopener noreferrer"
>
guide
</a>{" "}
if you are not sure how to get these tokens!
</div>
)}
<TextFormField
name="bot_token"
label="Slack Bot Token"
type="password"
/>
<TextFormField
name="app_token"
label="Slack App Token"
type="password"
/>
<div className="flex justify-end w-full mt-4">
<Button
type="submit"
disabled={
isSubmitting ||
!values.bot_token ||
!values.app_token ||
!values.name
}
variant="submit"
size="default"
>
{isUpdate ? "Update!" : "Create!"}
</Button>
</div>
)}
<TextFormField
name="bot_token"
label="Slack Bot Token"
type="password"
/>
<TextFormField
name="app_token"
label="Slack App Token"
type="password"
/>
<div className="flex justify-end w-full mt-4">
<Button
type="submit"
disabled={isSubmitting}
variant="submit"
size="default"
>
{isUpdate ? "Update!" : "Create!"}
</Button>
</div>
</Form>
)}
</Formik>
);
</Form>
)}
</Formik>
);
};

View File

@@ -261,10 +261,12 @@ export const SlackChannelConfigCreationForm = ({
</Tabs>
</div>
<AdvancedOptionsToggle
showAdvancedOptions={showAdvancedOptions}
setShowAdvancedOptions={setShowAdvancedOptions}
/>
<div className="mt-6">
<AdvancedOptionsToggle
showAdvancedOptions={showAdvancedOptions}
setShowAdvancedOptions={setShowAdvancedOptions}
/>
</div>
{showAdvancedOptions && (
<div className="mt-4">
@@ -369,7 +371,7 @@ export const SlackChannelConfigCreationForm = ({
<Button
type="submit"
variant="submit"
disabled={isSubmitting}
disabled={isSubmitting || !values.channel_name}
className="mx-auto w-64"
>
{isUpdate ? "Update!" : "Create!"}

View File

@@ -95,7 +95,7 @@ function SlackBotEditPage({
text-sm
w-80
"
href={`/admin/bots/new?slack_bot_id=${unwrappedParams["bot-id"]}`}
href={`/admin/bots/${unwrappedParams["bot-id"]}/channels/new`}
>
<div className="mx-auto flex">
<FiPlusSquare className="my-auto mr-2" />

View File

@@ -119,16 +119,19 @@ function Main({ ccPairId }: { ccPairId: number }) {
<BackButton
behaviorOverride={() => router.push("/admin/indexing/status")}
/>
<div className="pb-1 flex mt-1">
<div className="mr-2 my-auto">
<SourceIcon iconSize={24} sourceType={ccPair.connector.source} />
<div className="flex items-center justify-between h-14">
<div className="my-auto">
<SourceIcon iconSize={32} sourceType={ccPair.connector.source} />
</div>
<EditableStringFieldDisplay
value={ccPair.name}
isEditable={ccPair.is_editable_for_current_user}
onUpdate={handleUpdateName}
/>
<div className="ml-1">
<EditableStringFieldDisplay
value={ccPair.name}
isEditable={ccPair.is_editable_for_current_user}
onUpdate={handleUpdateName}
scale={2.1}
/>
</div>
{ccPair.is_editable_for_current_user && (
<div className="ml-auto flex gap-x-2">

View File

@@ -23,12 +23,12 @@ export function Checkbox({
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}) {
return (
<label className="flex text-sm">
<label className="flex text-sm cursor-pointer">
<input
checked={checked}
onChange={onChange}
type="checkbox"
className="mx-3 px-5 w-3.5 h-3.5 my-auto"
className="mr-2 w-3.5 h-3.5 my-auto"
/>
<div>
<Label>{label}</Label>

View File

@@ -81,13 +81,22 @@ export function EditableStringFieldDisplay({
value={editableValue}
onChange={handleValueChange}
onKeyDown={handleKeyDown}
className={cn(textClassName, isEditing ? "block" : "hidden")}
className={cn(
textClassName,
"text-3xl font-bold text-text-800",
"user-text",
isEditing ? "block" : "hidden"
)}
style={{ fontSize: `${scale}rem` }}
/>
{!isEditing && (
<span
onClick={() => isEditable && setIsEditing(true)}
className={cn(textClassName, "cursor-pointer")}
className={cn(
textClassName,
"text-3xl font-bold text-text-800",
"cursor-pointer user-text"
)}
style={{ fontSize: `${scale}rem` }}
>
{value}
@@ -121,7 +130,7 @@ export function EditableStringFieldDisplay({
style={{ fontSize: `${scale}rem` }}
>
{isEditable && (
<EditIcon className={`visible ml-2`} size={8 * scale} />
<EditIcon className={`visible ml-2`} size={12 * scale} />
)}
</h1>
)}