mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-28 21:05:17 +02:00
Merge pull request #3214 from danswer-ai/fix-slack-ui
cleaned up new slack bot creation
This commit is contained in:
45
backend/alembic/versions/6d562f86c78b_remove_default_bot.py
Normal file
45
backend/alembic/versions/6d562f86c78b_remove_default_bot.py
Normal 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;
|
||||
"""
|
||||
)
|
||||
)
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
@@ -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}
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
@@ -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!"}
|
||||
|
@@ -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" />
|
||||
|
@@ -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">
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user