-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
457 lines (409 loc) · 18.9 KB
/
app.js
File metadata and controls
457 lines (409 loc) · 18.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
const playbooks = [
{
id: "shipping-delay",
label: "Delivery issue",
match: ["late", "tracking", "delivery", "shipment", "courier", "parcel", "deliver", "deri", "delayed"],
intent: "Delivery issue",
owner: "Logistics support",
nextStep: "Check shipment and offer recovery option",
resolution:
"Sorry for the delay. I will check the carrier status, mark this as urgent, and offer either a replacement shipment or account credit if the package cannot be recovered quickly.",
hiResolution:
"Delay ke liye sorry. Main carrier status check karunga, case ko urgent mark karunga, aur agar package jaldi recover nahi hota to replacement shipment ya account credit offer karunga.",
actions: ["Carrier status check", "Priority logistics case", "Recovery offer"],
sla: "2 hours",
},
{
id: "billing-refund",
label: "Refund or billing issue",
match: ["charged", "refund", "payment", "invoice", "billing", "subscription", "paid", "charge", "paisa", "refund", "money back", "return my money"],
intent: "Refund or billing issue",
owner: "Billing desk",
nextStep: "Validate payment or refund eligibility and start refund workflow",
resolution:
"I can help with the refund request. I will verify the payment or order status, check refund eligibility, start the refund workflow, and share the expected timeline.",
hiResolution:
"Main refund request me help kar sakta hoon. Main payment ya order status verify karunga, refund eligibility check karunga, refund workflow start karunga, aur timeline share karunga.",
actions: ["Verify payment/order", "Check refund eligibility", "Start refund workflow"],
sla: "4 hours",
},
{
id: "delivery-date-change",
label: "Delivery date change",
match: ["change delivery date", "reschedule delivery", "change delivery", "delivery date", "postpone delivery", "deliver later", "deliver tomorrow", "change address date"],
intent: "Delivery date change",
owner: "Logistics support",
nextStep: "Check shipment stage and update delivery preference",
resolution:
"I can help change the delivery date. I will check whether the shipment can still be rescheduled, update the delivery preference, and confirm the new date with the customer.",
hiResolution:
"Main delivery date change karne me help kar sakta hoon. Main check karunga ki shipment reschedule ho sakta hai ya nahi, delivery preference update karunga, aur new date confirm karunga.",
actions: ["Check shipment stage", "Update delivery preference", "Confirm new date"],
sla: "2 hours",
},
{
id: "damaged-product",
label: "Product replacement",
match: ["damaged", "broken", "replacement", "defective", "return", "damage", "tuta", "kharab", "replace"],
intent: "Product replacement",
owner: "Returns team",
nextStep: "Collect evidence and ship replacement",
resolution:
"I am sorry the product arrived damaged. I will collect the order ID and photo proof, waive return shipping, and reserve a replacement item.",
hiResolution:
"Sorry product damaged aaya. Main order ID aur photo proof collect karunga, return shipping waive karunga, aur replacement item reserve karunga.",
actions: ["Return label", "Replacement stock hold", "Warehouse alert"],
sla: "3 hours",
},
{
id: "wrong-order",
label: "Wrong order received",
match: ["wrong order", "different order", "different item", "wrong item", "not what i ordered", "received different", "galat order", "alag product"],
intent: "Wrong order received",
owner: "Fulfillment team",
nextStep: "Verify order mismatch and arrange replacement pickup",
resolution:
"I am sorry you received the wrong item. I will verify your order details, arrange a pickup if needed, and prioritize sending the correct product.",
hiResolution:
"Sorry aapko galat item mila. Main order details verify karunga, zarurat ho to pickup arrange karunga, aur correct product priority se bhejne ka process start karunga.",
actions: ["Verify order ID", "Create mismatch case", "Arrange correct replacement"],
sla: "3 hours",
},
{
id: "order-cancel",
label: "Order cancellation",
match: ["cancel", "cancellation", "stop order", "cancel my order", "do not ship", "order cancel"],
intent: "Order cancellation",
owner: "Order management",
nextStep: "Check shipment stage and confirm cancellation eligibility",
resolution:
"I can help cancel the order. I will check whether it has shipped, stop fulfillment if possible, and confirm refund or cancellation status.",
hiResolution:
"Main order cancel karne me help kar sakta hoon. Main check karunga ki order ship hua hai ya nahi, possible hua to fulfillment stop karunga, aur refund ya cancellation status confirm karunga.",
actions: ["Check fulfillment status", "Stop shipment if possible", "Confirm refund path"],
sla: "2 hours",
},
{
id: "account-login",
label: "Account or login issue",
match: ["account locked", "login", "password", "cannot login", "can't login", "otp", "sign in", "locked", "account", "reset password"],
intent: "Account or login issue",
owner: "Account support",
nextStep: "Verify identity and start account recovery",
resolution:
"I can help restore access. I will verify the account, guide password or OTP recovery, and escalate if the account is locked.",
hiResolution:
"Main account access restore karne me help karunga. Main account verify karunga, password ya OTP recovery guide karunga, aur account locked hua to escalate karunga.",
actions: ["Verify customer identity", "Start password or OTP recovery", "Escalate locked account"],
sla: "1 hour",
},
{
id: "technical-issue",
label: "App or technical issue",
match: ["app not working", "not working", "bug", "error", "crash", "update", "technical", "website", "page not loading", "issue after update"],
intent: "App or technical issue",
owner: "Technical support",
nextStep: "Collect device details and reproduce issue",
resolution:
"I can help troubleshoot this. I will collect app version, device details, and error information, then route the case to technical support.",
hiResolution:
"Main technical issue troubleshoot karne me help karunga. Main app version, device details, aur error information collect karke case technical support ko route karunga.",
actions: ["Collect device details", "Check known incidents", "Route to technical support"],
sla: "4 hours",
},
{
id: "sales-assist",
label: "Sales assistance",
match: ["plan", "sales", "team", "price", "upgrade", "choose", "pricing", "demo"],
intent: "Sales assistance",
owner: "Growth advisor",
nextStep: "Recommend plan based on team size and workflow",
resolution:
"I can guide the customer to the right plan by asking team size, expected usage, and required integrations, then offering a demo or trial path.",
hiResolution:
"Main customer ko right plan choose karne me guide karunga by asking team size, expected usage, aur required integrations, phir demo ya trial path offer karunga.",
actions: ["Estimate seats", "Recommend plan", "Offer demo booking"],
sla: "1 business day",
},
{
id: "general-support",
label: "General support",
match: ["help", "support", "question", "problem", "issue", "request"],
intent: "General support",
owner: "Support desk",
nextStep: "Ask one clarifying question and route to the right team",
resolution:
"I can help with this. I will collect one more detail about the issue, create a support case, and route it to the correct team.",
hiResolution:
"Main isme help kar sakta hoon. Main issue ke baare me ek aur detail collect karunga, support case create karunga, aur sahi team ko route karunga.",
actions: ["Ask clarifying question", "Create general case", "Route to support desk"],
sla: "6 hours",
},
];
const state = {
messages: [],
tickets: [],
analysis: null,
language: "en",
};
const chatLog = document.querySelector("#chatLog");
const customerInput = document.querySelector("#customerInput");
const chatForm = document.querySelector("#chatForm");
const handoffBrief = document.querySelector("#handoffBrief");
const languageMode = document.querySelector("#languageMode");
const policySelect = document.querySelector("#policySelect");
const policyResponse = document.querySelector("#policyResponse");
const ticketList = document.querySelector("#ticketList");
const sampleStatus = document.querySelector("#sampleStatus");
function activeResolution(playbook) {
return state.language === "hi" ? playbook.hiResolution : playbook.resolution;
}
function bestPlaybook(message) {
const text = message.toLowerCase();
const ranked = playbooks
.map((playbook) => ({
...playbook,
score: playbook.match.reduce((total, word) => {
if (!text.includes(word)) return total;
return total + (word.includes(" ") ? 3 : 1);
}, 0),
}))
.sort((a, b) => b.score - a.score);
return ranked[0].score > 0 ? ranked[0] : playbooks.find((playbook) => playbook.id === "general-support");
}
function detectMood(message) {
const text = message.toLowerCase();
const urgent = ["today", "now", "as soon as possible", "angry", "terrible", "frustrated", "fixed", "urgent", "jaldi", "abhi"];
const polite = ["please", "help", "can you", "thanks", "kripya"];
if (urgent.some((word) => text.includes(word))) {
return { sentiment: "Frustrated", priority: "High", confidence: 96 };
}
if (polite.some((word) => text.includes(word))) {
return { sentiment: "Cooperative", priority: "Medium", confidence: 90 };
}
return { sentiment: "Concerned", priority: "Medium", confidence: 86 };
}
function analyzeMessage(message) {
const playbook = bestPlaybook(message);
const mood = detectMood(message);
return {
...playbook,
...mood,
message,
resolution: activeResolution(playbook),
confidence: Math.min(99, mood.confidence + playbook.score),
};
}
function addMessage(role, text) {
const bubble = document.createElement("div");
bubble.className = `message ${role}`;
bubble.textContent = text;
chatLog.appendChild(bubble);
chatLog.scrollTop = chatLog.scrollHeight;
state.messages.push({ role, text });
}
function botReply(analysis) {
if (state.language === "hi") {
return `${analysis.resolution} Case ko ${analysis.priority.toLowerCase()} priority mark karke ${analysis.owner} ko assign kiya gaya hai.`;
}
return `${analysis.resolution} I have classified this as ${analysis.priority.toLowerCase()} priority and assigned it to ${analysis.owner}.`;
}
function renderAnalysis(analysis) {
state.analysis = analysis;
document.querySelector("#intentValue").textContent = analysis.intent;
document.querySelector("#sentimentValue").textContent = analysis.sentiment;
document.querySelector("#priorityValue").textContent = analysis.priority;
document.querySelector("#slaValue").textContent = analysis.sla;
document.querySelector("#confidenceBadge").textContent = `${analysis.confidence}%`;
document.querySelector("#resolutionText").textContent = analysis.resolution;
document.querySelector("#ticketTitle").textContent = analysis.intent;
document.querySelector("#ticketMeta").textContent = `Owner: ${analysis.owner} | Next: ${analysis.nextStep}`;
renderHandoff(analysis);
}
function renderHandoff(analysis) {
handoffBrief.textContent = [
`Customer issue: ${analysis.intent}`,
`Mood: ${analysis.sentiment}`,
`Priority: ${analysis.priority}`,
`SLA: ${analysis.sla}`,
`Owner: ${analysis.owner}`,
`Language mode: ${state.language === "hi" ? "Hindi + English" : "English"}`,
"",
`Customer said: "${analysis.message}"`,
"",
`Next action: ${analysis.nextStep}`,
`Automation: ${analysis.actions.join(", ")}`,
].join("\n");
}
function processMessage(message) {
addMessage("customer", message);
const analysis = analyzeMessage(message);
renderAnalysis(analysis);
addMessage("bot", botReply(analysis));
document.querySelector("#caseBadge").textContent = "Analyzed";
}
function resetDemo() {
state.messages = [];
chatLog.replaceChildren();
processMessage("My order is late and tracking has not updated for three days. I need this fixed today.");
}
function createTicket() {
if (!state.analysis) return;
const ticket = {
id: `FC-${2001 + state.tickets.length}`,
intent: state.analysis.intent,
priority: state.analysis.priority,
owner: state.analysis.owner,
sla: state.analysis.sla,
status: state.analysis.priority === "High" ? "Escalated" : "Open",
message: state.analysis.message,
};
state.tickets.unshift(ticket);
renderTickets();
document.querySelector("#caseBadge").textContent = `Ticket ${ticket.id}`;
}
function renderTickets() {
document.querySelector("#ticketCount").textContent = `${state.tickets.length} open`;
if (state.tickets.length === 0) {
ticketList.innerHTML = '<article class="ticket-item"><strong>No tickets yet</strong><span>Create a ticket from the AI result panel.</span></article>';
return;
}
ticketList.replaceChildren(...state.tickets.map((ticket) => {
const node = document.createElement("article");
node.className = "ticket-item";
node.innerHTML = `<strong>${ticket.id} - ${ticket.intent}</strong><span>${ticket.priority} priority | ${ticket.sla} SLA | ${ticket.status}</span><span>${ticket.owner}</span>`;
return node;
}));
}
function reportText() {
const analysis = state.analysis || analyzeMessage("Customer needs help");
return `FlowCare AI - Case Report
Project: FlowCare AI
Category: Customer Care Bot
Language Mode: ${state.language === "hi" ? "Hindi + English" : "English"}
Customer Message:
${analysis.message}
AI Analysis:
Intent: ${analysis.intent}
Sentiment: ${analysis.sentiment}
Priority: ${analysis.priority}
SLA: ${analysis.sla}
Owner: ${analysis.owner}
Suggested Response:
${analysis.resolution}
Automation Actions:
${analysis.actions.join(", ")}
Handoff Brief:
${handoffBrief.textContent}
CRM Tickets Created:
${state.tickets.length}
`;
}
function downloadReport() {
const blob = new Blob([reportText()], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "flowcare-ai-case-report.txt";
link.click();
URL.revokeObjectURL(url);
}
function exportSummary() {
const analysis = state.analysis || analyzeMessage("Customer needs help");
document.querySelector("#summaryOutput").value = `Project Name: FlowCare AI
Category: Customer Care Bot
Short Description:
FlowCare AI is an AI support co-pilot for small businesses. It supports typed complaints and sample customer scenarios, understands Hindi/English customer messages, detects urgency, drafts helpful replies, creates support tickets, and prepares handoff notes for human agents.
Problem:
Businesses lose time when support teams manually read every message, identify the issue, search policy documents, write replies, and create tickets. This causes slow service and missed SLA risks.
Solution:
FlowCare AI converts a customer message into a complete support workflow. It identifies the intent, sentiment, priority, SLA, best team owner, recommended response, automation actions, and handoff brief.
AI Workflow:
1. Understand: detect issue type, language, sentiment, urgency, and confidence.
2. Decide: select SLA, owner, response policy, and escalation path.
3. Act: draft reply, create ticket, generate report, and prepare handoff notes.
Advanced Features:
- Hindi + English support mode
- Editable admin knowledge base
- CRM-style ticket dashboard
- Downloadable case report
- Secure OpenAI/API integration path through backend
Business Impact:
- Faster first responses
- Lower manual support triage
- Better SLA prioritization
- More consistent support quality
- Scalable integration with CRM, WhatsApp, email, payment, and shipping APIs
Demo Case:
Intent: ${analysis.intent}
Sentiment: ${analysis.sentiment}
Priority: ${analysis.priority}
SLA: ${analysis.sla}
Owner: ${analysis.owner}
Innovation:
The project is not only a chatbot. It works like an AI support co-pilot that connects conversation, ticketing, SLA routing, multilingual support, admin policies, reporting, and agent assist in one simple flow. It is designed to be practical for real businesses while still being simple enough to demonstrate clearly.`;
document.querySelector("#summaryDialog").showModal();
}
function renderPolicyEditor() {
policySelect.replaceChildren(...playbooks.map((playbook) => {
const option = document.createElement("option");
option.value = playbook.id;
option.textContent = playbook.label;
return option;
}));
loadSelectedPolicy();
}
function loadSelectedPolicy() {
const selected = playbooks.find((playbook) => playbook.id === policySelect.value) || playbooks[0];
policyResponse.value = state.language === "hi" ? selected.hiResolution : selected.resolution;
}
function saveSelectedPolicy() {
const selected = playbooks.find((playbook) => playbook.id === policySelect.value);
if (!selected) return;
if (state.language === "hi") {
selected.hiResolution = policyResponse.value.trim() || selected.hiResolution;
} else {
selected.resolution = policyResponse.value.trim() || selected.resolution;
}
if (state.analysis?.id === selected.id) {
renderAnalysis(analyzeMessage(state.analysis.message));
}
document.querySelector("#caseBadge").textContent = "Policy saved";
}
document.querySelectorAll(".sample-row button").forEach((button) => {
button.addEventListener("click", () => {
document.querySelectorAll(".sample-row button").forEach((item) => item.classList.remove("selected"));
button.classList.add("selected");
customerInput.value = button.dataset.prompt;
sampleStatus.textContent = `${button.textContent.trim()} selected. Now click Analyze.`;
customerInput.focus();
});
});
chatForm.addEventListener("submit", (event) => {
event.preventDefault();
const message = customerInput.value.trim();
if (!message) return;
customerInput.value = "";
sampleStatus.textContent = "Message analyzed. Check the AI result on the right.";
processMessage(message);
});
languageMode.addEventListener("change", () => {
state.language = languageMode.value;
loadSelectedPolicy();
if (state.analysis) {
renderAnalysis(analyzeMessage(state.analysis.message));
}
});
policySelect.addEventListener("change", loadSelectedPolicy);
document.querySelector("#knowledgeForm").addEventListener("submit", (event) => {
event.preventDefault();
saveSelectedPolicy();
});
document.querySelector("#resetDemo").addEventListener("click", resetDemo);
document.querySelector("#createTicket").addEventListener("click", createTicket);
document.querySelector("#exportSummary").addEventListener("click", exportSummary);
document.querySelector("#downloadReport").addEventListener("click", downloadReport);
document.querySelector("#downloadReportTop").addEventListener("click", downloadReport);
renderPolicyEditor();
renderTickets();
resetDemo();