From d4bb4df40d79ec9eaee6a1e606eca0953b67336c Mon Sep 17 00:00:00 2001 From: debu-2 Date: Tue, 6 Oct 2026 21:22:41 +0530 Subject: [PATCH] handoff ui improvement --- frontend/src/components/HandoffSettings.jsx | 77 +++++-------- frontend/src/components/HumanHandoff.jsx | 119 +++++++++++++------- frontend/src/responsive.css | 10 +- frontend/src/styles.css | 71 ++++++------ 4 files changed, 151 insertions(+), 126 deletions(-) diff --git a/frontend/src/components/HandoffSettings.jsx b/frontend/src/components/HandoffSettings.jsx index c263967..159b6c6 100644 --- a/frontend/src/components/HandoffSettings.jsx +++ b/frontend/src/components/HandoffSettings.jsx @@ -1,4 +1,4 @@ -import { ArrowDown, ArrowUp, MoreHorizontal, Plus, UserRound } from "lucide-react"; +import { MoreHorizontal, UserRound } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { createHandoffRule, @@ -10,13 +10,13 @@ import { } from "../api"; import AgentSpark from "./AgentSpark"; import HumanHandoffDrawer from "./HumanHandoffDrawer"; -import { buildRuleCardChips, EMAIL_TYPE_OPTIONS } from "../utils/handoffUtils"; +import { buildRuleCardChips } from "../utils/handoffUtils"; -function typeLabel(id) { - return EMAIL_TYPE_OPTIONS.find((item) => item.id === id)?.title || id; +function ruleScope(rule) { + return buildRuleCardChips(rule).filter((chip) => chip !== rule.name).join(" · "); } -export default function HandoffSettings() { +export default function HandoffSettings({ createToken = 0 }) { const token = sessionStorage.getItem("agmail_access_token"); const [rules, setRules] = useState([]); const [loading, setLoading] = useState(true); @@ -50,10 +50,11 @@ export default function HandoffSettings() { loadRules(); }, [loadRules]); - function openCreate() { + useEffect(() => { + if (!createToken) return; setEditingRule(null); setDrawerOpen(true); - } + }, [createToken]); function openEdit(rule) { setEditingRule(rule); @@ -145,10 +146,9 @@ export default function HandoffSettings() { return (
-
- +
+

Handoff rules

+

Choose who can take over conversations from your Agent.

{error &&

{error}

} @@ -159,48 +159,32 @@ export default function HandoffSettings() { ) : rules.length === 0 ? (

No handoff rules yet

-

Configure when your Agent should involve a Human Owner.

- +

Add a human owner so your Agent knows who can take over a conversation.

) : (
- {rules.map((rule) => ( + {rules.map((rule, index) => (
- {rule.name} - - {rule.active ? "Active" : "Paused"} - - - Priority {rule.priority} · {rule.owner_name} · {rule.owner_email} - +
+ {rule.name} + + {index === 0 ? "Primary human owner" : "Human owner"} + {" · "} + {rule.owner_email} + +
+

{ruleScope(rule)}

- - + + {rule.active ? "Active" : "Paused"} + @@ -215,6 +199,8 @@ export default function HandoffSettings() { {menuOpenId === rule.id && (
+ +
-
- {buildRuleCardChips(rule).map((chip) => ( - {chip} - ))} -
))}
diff --git a/frontend/src/components/HumanHandoff.jsx b/frontend/src/components/HumanHandoff.jsx index f62ea7e..7d96b97 100644 --- a/frontend/src/components/HumanHandoff.jsx +++ b/frontend/src/components/HumanHandoff.jsx @@ -1,3 +1,4 @@ +import { ChevronDown, Plus } from "lucide-react"; import { useState } from "react"; import HandoffConversation from "./HandoffConversation"; import HandoffInbox from "./HandoffInbox"; @@ -6,61 +7,95 @@ import HandoffSettings from "./HandoffSettings"; export default function HumanHandoff({ onOpenExternal }) { const [tab, setTab] = useState("inbox"); const [selectedHandoffId, setSelectedHandoffId] = useState(null); + const [howOpen, setHowOpen] = useState(false); + const [createToken, setCreateToken] = useState(0); function selectTab(value) { setTab(value); setSelectedHandoffId(null); } + function addRule() { + setTab("rules"); + setSelectedHandoffId(null); + setCreateToken((token) => token + 1); + } + return (
-
-

Human Handoff

-

- Manage conversations that your Agent has handed over to a Human Owner. -

-
- Start the first email yourself -

- From the human mailbox, send a new email to your Gentic address. - Put the word handoff first. Then add the customer’s name, their email address, and the subject they should see. - Separate those four parts with a dot · , a hyphen - , or a bar |. -

-

handoff · Suyash Dixit · suyashdixit091@gmail.com · hello

-

- Gentic sends that as a normal first email. The customer only sees “hello”, from your Gentic address. - Your From address must be the human email saved on an active handoff rule. - When the customer replies to Gentic, that reply is mirrored back to you the same way as any other handoff. -

+

Human Handoff

+

+ When a customer emails your Gentic address, your Agent can hand that conversation to a person. + The person replies from their own inbox. The customer still only sees mail from Gentic. +

+ + {howOpen && ( +
+
+

When a customer writes to you

+

+ A customer emails your Gentic address. If one of your rules matches, Gentic sends that conversation to the human owner. + The owner replies to that email. Gentic then sends the reply to the customer as a normal message from your Gentic address. + The customer never sees the human’s own email address. +

+
+
+

When you write first

+

+ Only the human owner on an active rule can start a conversation. From that mailbox, send a new email to your Gentic address. + Put handoff first, then the customer’s name, their email, and the subject they should see. + Separate the parts with a middle dot (·), a hyphen (-), or a bar (|). +

+

handoff · Suyash Dixit · suyashdixit091@gmail.com · hello

+

+ Gentic sends a normal first email. The customer only sees “hello”, from your Gentic address. + When they reply, it comes back to the human owner the same way as any other handoff. +

+
-
+ )}
- + - - - +
+ {tab === "rules" ? ( - + ) : selectedHandoffId ? (