Skip to content

Feature: Collect contact information from users inside the chatbot #1948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
22f194d
Add leads settings to chatflow configuration
0xi4o Mar 14, 2024
c062d97
Merge branch 'main' of github.com:0xi4o/Flowise into feature/leads
0xi4o Mar 15, 2024
c4a6195
Add leads tab to chatflow configuration with options for lead capture
0xi4o Mar 19, 2024
ba558f0
Add database entity and migrations for leads
0xi4o Mar 19, 2024
42e1db8
Add endpoint for adding and fetching leads
0xi4o Mar 19, 2024
9509ae9
Show lead capture form in UI chat window when enabled
0xi4o Mar 19, 2024
df0d1af
Add view leads dialog
0xi4o Mar 20, 2024
04996f7
Make export leads functional
0xi4o Mar 20, 2024
c3cf5ac
Add input for configuring message on successful lead capture
0xi4o Mar 20, 2024
76d1b5d
Fix merge conflict
0xi4o Mar 20, 2024
3e00ac4
Add migrations for adding lead email in chat message if available
0xi4o Mar 28, 2024
bbd048a
show lead email in view messages
0xi4o Apr 3, 2024
5e5560c
Create leads service, controller and router and fix merge conflicts
0xi4o Apr 8, 2024
ba5e567
Merge branch 'main' of github.com:0xi4o/Flowise into feature/leads
0xi4o Apr 8, 2024
56fb550
Merge branch 'main' into feature/leads
HenryHengZJ Apr 11, 2024
6908906
ui touch up
HenryHengZJ Apr 11, 2024
04cbb48
Merge branch 'main' of github.com:0xi4o/Flowise into feature/leads
0xi4o Apr 18, 2024
fb4e1c6
Remove unused code and update how lead email is shown in view message…
0xi4o Apr 18, 2024
ddbc389
Fix lead not getting saved
0xi4o Apr 18, 2024
0536c1f
Disable input when lead form is shown and save lead info to localstorage
0xi4o Apr 18, 2024
eec0efa
Fix merge conflict
0xi4o Apr 18, 2024
53355d9
Fix lead capture form not working
0xi4o Apr 18, 2024
937fa7f
Merge branch 'main' into feature/leads
HenryHengZJ Apr 20, 2024
3b50fdf
disabled lead save button until at least one form field is turned on,…
HenryHengZJ Apr 21, 2024
cec2ddb
add leads API to as whitelist public endpoint
HenryHengZJ Apr 21, 2024
991ce52
Merge branch 'main' of github.com:0xi4o/Flowise into feature/leads
0xi4o Apr 30, 2024
ac06c88
Merge branch 'feature/leads' of github.com:0xi4o/Flowise into feature…
0xi4o May 1, 2024
d7588e1
Send leadEmail in internal chat inputs
0xi4o May 1, 2024
6e0b1bc
Fix condition for disabling input field and related buttons when lead…
0xi4o May 1, 2024
3ee0e16
update leads ui
HenryHengZJ May 2, 2024
c6ec6da
update error message and alter table add column sqlite migration
HenryHengZJ May 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/server/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface IChatMessage {
memoryType?: string
sessionId?: string
createdDate: Date
leadEmail?: string
}

export interface IChatMessageFeedback {
Expand Down Expand Up @@ -93,6 +94,16 @@ export interface IVariable {
createdDate: Date
}

export interface ILead {
id: string
name?: string
email?: string
phone?: string
chatflowid: string
chatId: string
createdDate: Date
}

export interface IUpsertHistory {
id: string
chatflowid: string
Expand Down Expand Up @@ -200,6 +211,7 @@ export interface IncomingInput {
chatId?: string
stopNodeId?: string
uploads?: IFileUpload[]
leadEmail?: string
}

export interface IActiveChatflows {
Expand Down
32 changes: 32 additions & 0 deletions packages/server/src/controllers/leads/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Request, Response, NextFunction } from 'express'
import leadsService from '../../services/leads'

const getAllLeadsForChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params.id === 'undefined' || req.params.id === '') {
throw new Error(`Error: leadsController.getAllLeadsForChatflow - id not provided!`)
}
const chatflowid = req.params.id
const apiResponse = await leadsService.getAllLeads(chatflowid)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}

const createLeadInChatflow = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.body === 'undefined' || req.body === '') {
throw new Error(`Error: leadsController.createLeadInChatflow - body not provided!`)
}
const apiResponse = await leadsService.createLead(req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}

export default {
createLeadInChatflow,
getAllLeadsForChatflow
}
3 changes: 3 additions & 0 deletions packages/server/src/database/entities/ChatMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ export class ChatMessage implements IChatMessage {
@Column({ type: 'timestamp' })
@CreateDateColumn()
createdDate: Date

@Column({ nullable: true, type: 'text' })
leadEmail?: string
}
27 changes: 27 additions & 0 deletions packages/server/src/database/entities/Lead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable */
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'
import { ILead } from '../../Interface'

@Entity()
export class Lead implements ILead {
@PrimaryGeneratedColumn('uuid')
id: string

@Column()
name?: string

@Column()
email?: string

@Column()
phone?: string

@Column()
chatflowid: string

@Column()
chatId: string

@CreateDateColumn()
createdDate: Date
}
2 changes: 2 additions & 0 deletions packages/server/src/database/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Credential } from './Credential'
import { Tool } from './Tool'
import { Assistant } from './Assistant'
import { Variable } from './Variable'
import { Lead } from './Lead'
import { UpsertHistory } from './UpsertHistory'

export const entities = {
Expand All @@ -15,5 +16,6 @@ export const entities = {
Tool,
Assistant,
Variable,
Lead,
UpsertHistory
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddLead1710832127079 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS \`lead\` (
\`id\` varchar(36) NOT NULL,
\`chatflowid\` varchar(255) NOT NULL,
\`chatId\` varchar(255) NOT NULL,
\`name\` text,
\`email\` text,
\`phone\` text,
\`createdDate\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE lead`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddLeadToChatMessage1711538023578 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
const columnExists = await queryRunner.hasColumn('chat_message', 'leadEmail')
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`leadEmail\` TEXT;`)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`leadEmail\`;`)
}
}
6 changes: 5 additions & 1 deletion packages/server/src/database/migrations/mysql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntit
import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
import { AddFeedback1707213626553 } from './1707213626553-AddFeedback'
import { AddLead1710832127079 } from './1710832127079-AddLead'
import { AddLeadToChatMessage1711538023578 } from './1711538023578-AddLeadToChatMessage'

export const mysqlMigrations = [
Init1693840429259,
Expand All @@ -33,5 +35,7 @@ export const mysqlMigrations = [
AddVariableEntity1699325775451,
AddSpeechToText1706364937060,
AddUpsertHistoryEntity1709814301358,
AddFeedback1707213626553
AddFeedback1707213626553,
AddLead1710832127079,
AddLeadToChatMessage1711538023578
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddLead1710832137905 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS lead (
id uuid NOT NULL DEFAULT uuid_generate_v4(),
"chatflowid" varchar NOT NULL,
"chatId" varchar NOT NULL,
"name" text,
"email" text,
"phone" text,
"createdDate" timestamp NOT NULL DEFAULT now(),
CONSTRAINT "PK_98419043dd704f54-9830ab78f0" PRIMARY KEY (id)
);`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE lead`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddLeadToChatMessage1711538016098 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_message" ADD COLUMN IF NOT EXISTS "leadEmail" TEXT;`)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "leadEmail";`)
}
}
6 changes: 5 additions & 1 deletion packages/server/src/database/migrations/postgres/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
import { AddFeedback1707213601923 } from './1707213601923-AddFeedback'
import { FieldTypes1710497452584 } from './1710497452584-FieldTypes'
import { AddLead1710832137905 } from './1710832137905-AddLead'
import { AddLeadToChatMessage1711538016098 } from './1711538016098-AddLeadToChatMessage'

export const postgresMigrations = [
Init1693891895163,
Expand All @@ -35,5 +37,7 @@ export const postgresMigrations = [
AddSpeechToText1706364937060,
AddUpsertHistoryEntity1709814301358,
AddFeedback1707213601923,
FieldTypes1710497452584
FieldTypes1710497452584,
AddLead1710832137905,
AddLeadToChatMessage1711538016098
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddLead1710832117612 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE IF NOT EXISTS "lead" ("id" varchar PRIMARY KEY NOT NULL, "chatflowid" varchar NOT NULL, "chatId" varchar NOT NULL, "name" text, "email" text, "phone" text, "createdDate" datetime NOT NULL DEFAULT (datetime('now')));`
)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS "lead";`)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from 'typeorm'

export class AddLeadToChatMessage1711537986113 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "temp_chat_message" ("id" varchar PRIMARY KEY NOT NULL, "role" varchar NOT NULL, "chatflowid" varchar NOT NULL, "content" text NOT NULL, "sourceDocuments" text, "usedTools" text, "fileAnnotations" text, "fileUploads" text, "createdDate" datetime NOT NULL DEFAULT (datetime('now')), "chatType" VARCHAR NOT NULL DEFAULT 'INTERNAL', "chatId" VARCHAR NOT NULL, "memoryType" VARCHAR, "sessionId" VARCHAR, "leadEmail" text);`
)
await queryRunner.query(
`INSERT INTO "temp_chat_message" ("id", "role", "chatflowid", "content", "sourceDocuments", "fileAnnotations", "fileUploads", "usedTools", "createdDate", "chatType", "chatId", "memoryType", "sessionId") SELECT "id", "role", "chatflowid", "content", "sourceDocuments", "usedTools", "fileAnnotations", "fileUploads", "createdDate", "chatType", "chatId", "memoryType", "sessionId" FROM "chat_message";`
)
await queryRunner.query(`DROP TABLE "chat_message";`)
await queryRunner.query(`ALTER TABLE "temp_chat_message" RENAME TO "chat_message";`)
await queryRunner.query(`CREATE INDEX "IDX_e574527322272fd838f4f0f3d3" ON "chat_message" ("chatflowid") ;`)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS "temp_chat_message";`)
await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "leadEmail";`)
}
}
6 changes: 5 additions & 1 deletion packages/server/src/database/migrations/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { AddVariableEntity1699325775451 } from './1702200925471-AddVariableEntit
import { AddSpeechToText1706364937060 } from './1706364937060-AddSpeechToText'
import { AddUpsertHistoryEntity1709814301358 } from './1709814301358-AddUpsertHistoryEntity'
import { AddFeedback1707213619308 } from './1707213619308-AddFeedback'
import { AddLead1710832117612 } from './1710832117612-AddLead'
import { AddLeadToChatMessage1711537986113 } from './1711537986113-AddLeadToChatMessage'

export const sqliteMigrations = [
Init1693835579790,
Expand All @@ -33,5 +35,7 @@ export const sqliteMigrations = [
AddVariableEntity1699325775451,
AddSpeechToText1706364937060,
AddUpsertHistoryEntity1709814301358,
AddFeedback1707213619308
AddFeedback1707213619308,
AddLead1710832117612,
AddLeadToChatMessage1711537986113
]
2 changes: 2 additions & 0 deletions packages/server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import internalPredictionRouter from './internal-predictions'
import ipRouter from './ip'
import getUploadFileRouter from './get-upload-file'
import getUploadPathRouter from './get-upload-path'
import leadsRouter from './leads'
import loadPromptRouter from './load-prompts'
import marketplacesRouter from './marketplaces'
import nodeConfigRouter from './node-configs'
Expand Down Expand Up @@ -56,6 +57,7 @@ router.use('/internal-prediction', internalPredictionRouter)
router.use('/ip', ipRouter)
router.use('/get-upload-file', getUploadFileRouter)
router.use('/get-upload-path', getUploadPathRouter)
router.use('/leads', leadsRouter)
router.use('/load-prompt', loadPromptRouter)
router.use('/marketplaces', marketplacesRouter)
router.use('/node-config', nodeConfigRouter)
Expand Down
11 changes: 11 additions & 0 deletions packages/server/src/routes/leads/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import express from 'express'
import leadsController from '../../controllers/leads'
const router = express.Router()

// CREATE
router.post('/', leadsController.createLeadInChatflow)

// READ
router.get('/:id', leadsController.getAllLeadsForChatflow)

export default router
41 changes: 41 additions & 0 deletions packages/server/src/services/leads/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { v4 as uuidv4 } from 'uuid'

import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { Lead } from '../../database/entities/Lead'
import { ILead } from '../../Interface'

const getAllLeads = async (chatflowid: string) => {
try {
const appServer = getRunningExpressApp()
const dbResponse = await appServer.AppDataSource.getRepository(Lead).find({
where: {
chatflowid
}
})
return dbResponse
} catch (error) {
throw new Error(`Error: leadsService.getAllLeads - ${error}`)
}
}

const createLead = async (body: Partial<ILead>) => {
try {
const chatId = body.chatId ?? uuidv4()

const newLead = new Lead()
Object.assign(newLead, body)
Object.assign(newLead, { chatId })

const appServer = getRunningExpressApp()
const lead = appServer.AppDataSource.getRepository(Lead).create(newLead)
const dbResponse = await appServer.AppDataSource.getRepository(Lead).save(lead)
return dbResponse
} catch (error) {
throw new Error(`Error: leadsService.createLead - ${error}`)
}
}

export default {
createLead,
getAllLeads
}
3 changes: 2 additions & 1 deletion packages/server/src/utils/buildChatflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@ export const utilBuildChatflow = async (req: Request, socketIO?: Server, isInter
memoryType,
sessionId,
createdDate: userMessageDateTime,
fileUploads: incomingInput.uploads ? JSON.stringify(fileUploads) : undefined
fileUploads: incomingInput.uploads ? JSON.stringify(fileUploads) : undefined,
leadEmail: incomingInput.leadEmail
}
await utilAddChatMessage(userMessage)

Expand Down
9 changes: 9 additions & 0 deletions packages/ui/src/api/lead.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import client from './client'

const getLeads = (id) => client.get(`/leads/${id}`)
const addLead = (body) => client.post(`/leads/`, body)

export default {
getLeads,
addLead
}
1 change: 1 addition & 0 deletions packages/ui/src/assets/images/leads_empty.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading