Skip to content

Commit e941fcf

Browse files
Copilotthegovind
andcommitted
Complete Zava Copilot customization with integrated flows, mobile-first design, and retail/support prompts
Co-authored-by: thegovind <18152044+thegovind@users.noreply.github.com>
1 parent 002049c commit e941fcf

17 files changed

+407
-370
lines changed

src/App/frontend/src/components/Answer/Answer.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ import supersub from 'remark-supersub'
1212
import { AskResponse, Citation, Feedback, historyMessageFeedback } from '../../api'
1313
import { XSSAllowTags } from '../../constants/xssAllowTags'
1414
import { AppStateContext } from '../../state/AppProvider'
15+
import { ProductFlow, WarrantyFlow } from '../ZavaFlows'
16+
import {
17+
extractProductInfo,
18+
extractWarrantyInfo,
19+
getFlowType,
20+
createBackorder,
21+
reserveProduct,
22+
createRMA,
23+
generateReturnLabel
24+
} from '../../helpers/zava/flowHelpers'
1525

1626
import { parseAnswer } from './AnswerParser'
1727

@@ -46,6 +56,11 @@ export const Answer = ({ answer, onCitationClicked }: Props) => {
4656
appStateContext?.state.frontendSettings?.feedback_enabled && appStateContext?.state.isCosmosDBAvailable?.cosmosDB
4757
const SANITIZE_ANSWER = appStateContext?.state.frontendSettings?.sanitize_answer
4858

59+
// Zava flow detection
60+
const flowType = useMemo(() => getFlowType(answer.answer), [answer.answer])
61+
const productInfo = useMemo(() => flowType === 'product' ? extractProductInfo(answer.answer) : null, [answer.answer, flowType])
62+
const warrantyInfo = useMemo(() => flowType === 'warranty' ? extractWarrantyInfo(answer.answer) : null, [answer.answer, flowType])
63+
4964
const handleChevronClick = () => {
5065
setChevronIsExpanded(!chevronIsExpanded)
5166
toggleIsRefAccordionOpen()
@@ -311,6 +326,28 @@ export const Answer = ({ answer, onCitationClicked }: Props) => {
311326
</Stack.Item>
312327
</Stack>
313328
</Stack.Item>
329+
330+
{/* Zava Quick Actions */}
331+
{flowType === 'product' && productInfo && (
332+
<Stack.Item>
333+
<ProductFlow
334+
productInfo={productInfo}
335+
onBackorder={createBackorder}
336+
onReserve={reserveProduct}
337+
/>
338+
</Stack.Item>
339+
)}
340+
341+
{flowType === 'warranty' && warrantyInfo && (
342+
<Stack.Item>
343+
<WarrantyFlow
344+
warrantyInfo={warrantyInfo}
345+
onCreateRMA={createRMA}
346+
onGenerateLabel={generateReturnLabel}
347+
/>
348+
</Stack.Item>
349+
)}
350+
314351
<Stack horizontal className={styles.answerFooter}>
315352
{!!parsedAnswer.citations.length && (
316353
<Stack.Item data-testid="stack-item" onKeyDown={e => (e.key === 'Enter' || e.key === ' ' ? toggleIsRefAccordionOpen() : null)}>

src/App/frontend/src/components/PromptsSection/PromptsSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export type PromptType = {
1212
}
1313

1414
const promptsConfg = [
15-
{ name: 'Top discussion trends', question: 'Top discussion trends', key: 'p1' },
16-
{ name: 'Investment summary', question: 'Investment summary', key: 'p2' },
17-
{ name: 'Previous meeting summary', question: 'Previous meeting summary', key: 'p3' }
15+
{ name: 'Check product availability', question: '/check-inventory', key: 'p1' },
16+
{ name: 'Warranty lookup', question: '/warranty-check', key: 'p2' },
17+
{ name: 'Create RMA', question: '/create-rma', key: 'p3' }
1818
]
1919

2020
export const PromptsSection: React.FC<PromptsSectionProps> = ({ onClickPrompt, isLoading }) => {
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
interface ProductInfo {
2+
name: string
3+
sku: string
4+
availability: number
5+
price: number
6+
}
7+
8+
interface WarrantyInfo {
9+
status: 'active' | 'expired' | 'not_found'
10+
expiryDate?: string
11+
purchaseDate?: string
12+
productName?: string
13+
serialNumber?: string
14+
}
15+
16+
/**
17+
* Parses assistant response to extract product information
18+
*/
19+
export const extractProductInfo = (content: string): ProductInfo | null => {
20+
const productRegex = /(?:product|item|sku)\s*[:]\s*([^\n]+)/i
21+
const availabilityRegex = /(?:availability|in stock|available)\s*[:]\s*(\d+)|(\d+)\s*(?:available|in stock)/i
22+
const priceRegex = /(?:price|cost)\s*[:]\s*\$?(\d+(?:\.\d{2})?)/i
23+
const skuRegex = /(?:sku|part number)\s*[:]\s*([A-Z0-9-]+)/i
24+
25+
const productMatch = content.match(productRegex)
26+
const availabilityMatch = content.match(availabilityRegex)
27+
const priceMatch = content.match(priceRegex)
28+
const skuMatch = content.match(skuRegex)
29+
30+
if (!productMatch && !skuMatch) return null
31+
32+
return {
33+
name: productMatch?.[1]?.trim() || 'Unknown Product',
34+
sku: skuMatch?.[1]?.trim() || 'N/A',
35+
availability: parseInt(availabilityMatch?.[1] || availabilityMatch?.[2] || '0'),
36+
price: parseFloat(priceMatch?.[1] || '0')
37+
}
38+
}
39+
40+
/**
41+
* Parses assistant response to extract warranty information
42+
*/
43+
export const extractWarrantyInfo = (content: string): WarrantyInfo | null => {
44+
const warrantyRegex = /warranty\s*(?:status)?\s*[:]\s*(active|expired|valid|invalid|not found)/i
45+
const expiryRegex = /(?:expires?|expiry)\s*(?:date)?\s*[:]\s*([^\n]+)/i
46+
const purchaseRegex = /(?:purchase|bought)\s*(?:date)?\s*[:]\s*([^\n]+)/i
47+
const serialRegex = /(?:serial|serial number)\s*[:]\s*([A-Z0-9-]+)/i
48+
const productRegex = /(?:product|model)\s*[:]\s*([^\n]+)/i
49+
50+
const warrantyMatch = content.match(warrantyRegex)
51+
const expiryMatch = content.match(expiryRegex)
52+
const purchaseMatch = content.match(purchaseRegex)
53+
const serialMatch = content.match(serialRegex)
54+
const productMatch = content.match(productRegex)
55+
56+
if (!warrantyMatch) return null
57+
58+
let status: 'active' | 'expired' | 'not_found' = 'not_found'
59+
if (warrantyMatch[1].toLowerCase().includes('active') || warrantyMatch[1].toLowerCase().includes('valid')) {
60+
status = 'active'
61+
} else if (warrantyMatch[1].toLowerCase().includes('expired') || warrantyMatch[1].toLowerCase().includes('invalid')) {
62+
status = 'expired'
63+
}
64+
65+
return {
66+
status,
67+
expiryDate: expiryMatch?.[1]?.trim(),
68+
purchaseDate: purchaseMatch?.[1]?.trim(),
69+
productName: productMatch?.[1]?.trim(),
70+
serialNumber: serialMatch?.[1]?.trim()
71+
}
72+
}
73+
74+
/**
75+
* Determines what type of flow should be shown based on the content
76+
*/
77+
export const getFlowType = (content: string): 'product' | 'warranty' | null => {
78+
const productKeywords = ['availability', 'in stock', 'backorder', 'reserve', 'inventory']
79+
const warrantyKeywords = ['warranty', 'rma', 'return', 'defective', 'repair']
80+
81+
const lowerContent = content.toLowerCase()
82+
83+
const hasProductKeywords = productKeywords.some(keyword => lowerContent.includes(keyword))
84+
const hasWarrantyKeywords = warrantyKeywords.some(keyword => lowerContent.includes(keyword))
85+
86+
if (hasWarrantyKeywords) return 'warranty'
87+
if (hasProductKeywords) return 'product'
88+
89+
return null
90+
}
91+
92+
/**
93+
* Mock function to simulate backorder creation
94+
*/
95+
export const createBackorder = async (data: any): Promise<void> => {
96+
// Simulate API call delay
97+
await new Promise(resolve => setTimeout(resolve, 2000))
98+
99+
// In a real implementation, this would call the backend API
100+
console.log('Creating backorder:', data)
101+
}
102+
103+
/**
104+
* Mock function to simulate product reservation
105+
*/
106+
export const reserveProduct = async (data: any): Promise<void> => {
107+
// Simulate API call delay
108+
await new Promise(resolve => setTimeout(resolve, 1500))
109+
110+
// In a real implementation, this would call the backend API
111+
console.log('Reserving product:', data)
112+
}
113+
114+
/**
115+
* Mock function to simulate RMA creation
116+
*/
117+
export const createRMA = async (data: any): Promise<{ rmaNumber: string }> => {
118+
// Simulate API call delay
119+
await new Promise(resolve => setTimeout(resolve, 2000))
120+
121+
// In a real implementation, this would call the backend API
122+
const rmaNumber = `RMA-${Date.now()}`
123+
console.log('Creating RMA:', data, 'RMA Number:', rmaNumber)
124+
125+
return { rmaNumber }
126+
}
127+
128+
/**
129+
* Mock function to simulate return label generation
130+
*/
131+
export const generateReturnLabel = async (data: any): Promise<{ trackingNumber: string }> => {
132+
// Simulate API call delay
133+
await new Promise(resolve => setTimeout(resolve, 1500))
134+
135+
// In a real implementation, this would call the backend API
136+
const trackingNumber = `1Z${Math.random().toString(36).substr(2, 12).toUpperCase()}`
137+
console.log('Generating return label:', data, 'Tracking:', trackingNumber)
138+
139+
return { trackingNumber }
140+
}

src/App/frontend/src/index.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ export default function App() {
1717
<AppStateProvider>
1818
<HashRouter>
1919
<Routes>
20-
<Route path="/" element={<Layout />}>
21-
<Route index element={<Chat />} />
22-
<Route path="*" element={<NoPage />} />
23-
</Route>
20+
<Route path="/" element={<Chat />} />
21+
<Route path="/chat" element={<Chat />} />
22+
<Route path="*" element={<NoPage />} />
2423
</Routes>
2524
</HashRouter>
2625
</AppStateProvider>

0 commit comments

Comments
 (0)