Skip to content

Commit 2d126d1

Browse files
authored
Merge pull request #303 from microsoft/james-dev
Optimize UI
2 parents 538c2d4 + 6537cce commit 2d126d1

File tree

5 files changed

+103
-68
lines changed

5 files changed

+103
-68
lines changed

agentic_ai/workflow/fraud_detection/backend.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@ async def broadcast(self, message: dict):
204204
timestamp=datetime.now().isoformat(),
205205
severity="high",
206206
),
207+
"ALERT-004": SuspiciousActivityAlert(
208+
alert_id="ALERT-004",
209+
customer_id=4,
210+
alert_type="routine_check",
211+
description="Routine security check - password changed from usual device",
212+
timestamp=datetime.now().isoformat(),
213+
severity="low",
214+
),
207215
}
208216

209217

agentic_ai/workflow/fraud_detection/ui/src/App.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ function App() {
6868
try {
6969
const event = lastMessage;
7070

71-
// Add to event log
72-
setEvents((prev) => [...prev, event]);
71+
// Add to event log - prevent duplicates by checking timestamp + type + executor_id
72+
setEvents((prev) => {
73+
const eventKey = `${event.timestamp}-${event.type || event.event_type}-${event.executor_id || ''}`;
74+
const isDuplicate = prev.some(
75+
(e) => `${e.timestamp}-${e.type || e.event_type}-${e.executor_id || ''}` === eventKey
76+
);
77+
return isDuplicate ? prev : [...prev, event];
78+
});
7379

7480
// Handle workflow initialization
7581
if (event.type === 'workflow_initializing') {
@@ -212,7 +218,7 @@ function App() {
212218
</Grid>
213219

214220
{/* Right Column - Event Log */}
215-
<Grid item xs={12} md={3}>
221+
<Grid item xs={12} md={3} sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
216222
<EventLog events={events} />
217223
</Grid>
218224
</Grid>

agentic_ai/workflow/fraud_detection/ui/src/components/AnalystDecisionPanel.jsx

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ function AnalystDecisionPanel({ decision, onSubmit }) {
5858
<Paper
5959
elevation={3}
6060
sx={{
61-
p: 3,
61+
p: 1.5,
6262
display: 'flex',
6363
flexDirection: 'column',
64-
gap: 2,
64+
gap: 1,
6565
border: 3,
6666
borderColor: 'warning.main',
6767
animation: 'pulse 2s ease-in-out infinite',
@@ -71,51 +71,49 @@ function AnalystDecisionPanel({ decision, onSubmit }) {
7171
},
7272
}}
7373
>
74-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
75-
<GavelIcon color="warning" />
76-
<Typography variant="h6">Analyst Review Required</Typography>
74+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
75+
<GavelIcon color="warning" fontSize="small" />
76+
<Typography variant="subtitle1" fontWeight="bold">Analyst Review Required</Typography>
7777
</Box>
7878

79-
<Alert severity="warning" sx={{ mb: 1 }}>
80-
<Typography variant="body2" fontWeight="bold">
79+
<Alert severity="warning" sx={{ py: 0.25, px: 1 }}>
80+
<Typography variant="caption" fontWeight="bold">
8181
Human Decision Needed
8282
</Typography>
83-
<Typography variant="caption">
84-
The workflow is paused pending your review
85-
</Typography>
8683
</Alert>
8784

88-
<Divider />
85+
<Divider sx={{ my: 0.5 }} />
8986

9087
{/* Risk Assessment */}
9188
<Box>
92-
<Typography variant="subtitle2" gutterBottom>
89+
<Typography variant="caption" fontWeight="bold" display="block" sx={{ mb: 0.5 }}>
9390
Risk Assessment
9491
</Typography>
95-
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center', mb: 1 }}>
96-
<Typography variant="body2">Risk Score:</Typography>
92+
<Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center', mb: 0.5 }}>
93+
<Typography variant="caption">Risk Score:</Typography>
9794
<Chip
9895
label={`${(decision.data?.risk_score || 0).toFixed(2)} - ${getRiskLevel(
9996
decision.data?.risk_score || 0
10097
)}`}
10198
color={getRiskColor(decision.data?.risk_score || 0)}
10299
size="small"
100+
sx={{ height: 20, fontSize: '0.7rem' }}
103101
/>
104102
</Box>
105-
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
106-
<Typography variant="body2">Alert ID:</Typography>
107-
<Chip label={decision.data?.alert_id} size="small" variant="outlined" />
103+
<Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center' }}>
104+
<Typography variant="caption">Alert ID:</Typography>
105+
<Chip label={decision.data?.alert_id} size="small" variant="outlined" sx={{ height: 20, fontSize: '0.7rem' }} />
108106
</Box>
109107
</Box>
110108

111109
{/* Reasoning */}
112110
{decision.data?.reasoning && (
113111
<Box>
114-
<Typography variant="subtitle2" gutterBottom>
112+
<Typography variant="caption" fontWeight="bold" display="block" sx={{ mb: 0.5 }}>
115113
AI Analysis
116114
</Typography>
117-
<Paper variant="outlined" sx={{ p: 1.5, bgcolor: 'grey.50', maxHeight: 150, overflow: 'auto' }}>
118-
<Typography variant="caption" sx={{ whiteSpace: 'pre-wrap' }}>
115+
<Paper variant="outlined" sx={{ p: 0.75, bgcolor: 'grey.50', maxHeight: 80, overflow: 'auto' }}>
116+
<Typography variant="caption" sx={{ whiteSpace: 'pre-wrap', fontSize: '0.7rem' }}>
119117
{decision.data.reasoning}
120118
</Typography>
121119
</Paper>
@@ -124,7 +122,7 @@ function AnalystDecisionPanel({ decision, onSubmit }) {
124122

125123
{/* Recommended Action */}
126124
<Box>
127-
<Typography variant="subtitle2" gutterBottom>
125+
<Typography variant="caption" fontWeight="bold" display="block" sx={{ mb: 0.5 }}>
128126
Recommended Action
129127
</Typography>
130128
<Chip
@@ -136,22 +134,24 @@ function AnalystDecisionPanel({ decision, onSubmit }) {
136134
ACTION_OPTIONS.find((opt) => opt.value === decision.data?.recommended_action)
137135
?.color || 'default'
138136
}
139-
size="medium"
137+
size="small"
138+
sx={{ height: 20, fontSize: '0.7rem' }}
140139
/>
141140
</Box>
142141

143-
<Divider />
142+
<Divider sx={{ my: 0.5 }} />
144143

145144
{/* Decision Form */}
146-
<FormControl fullWidth>
147-
<InputLabel>Your Decision</InputLabel>
145+
<FormControl fullWidth size="small" sx={{ minHeight: 40 }}>
146+
<InputLabel sx={{ fontSize: '0.875rem' }}>Your Decision</InputLabel>
148147
<Select
149148
value={selectedAction}
150149
label="Your Decision"
151150
onChange={(e) => setSelectedAction(e.target.value)}
151+
sx={{ fontSize: '0.875rem' }}
152152
>
153153
{ACTION_OPTIONS.map((option) => (
154-
<MenuItem key={option.value} value={option.value}>
154+
<MenuItem key={option.value} value={option.value} sx={{ fontSize: '0.875rem' }}>
155155
{option.label}
156156
</MenuItem>
157157
))}
@@ -161,20 +161,23 @@ function AnalystDecisionPanel({ decision, onSubmit }) {
161161
<TextField
162162
label="Analyst Notes"
163163
multiline
164-
rows={3}
164+
rows={2}
165165
fullWidth
166+
size="small"
166167
value={notes}
167168
onChange={(e) => setNotes(e.target.value)}
168-
placeholder="Add your analysis and reasoning..."
169+
placeholder="Add notes..."
170+
sx={{ '& .MuiInputBase-input': { fontSize: '0.875rem' } }}
169171
/>
170172

171173
<Button
172174
variant="contained"
173175
color="primary"
174-
size="large"
176+
size="small"
175177
fullWidth
176-
startIcon={<SendIcon />}
178+
startIcon={<SendIcon fontSize="small" />}
177179
onClick={handleSubmit}
180+
sx={{ mt: 0.5, py: 0.75 }}
178181
>
179182
Submit Decision
180183
</Button>

agentic_ai/workflow/fraud_detection/ui/src/components/ControlPanel.jsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,35 +53,37 @@ function ControlPanel({ alerts, onStartWorkflow, workflowRunning, selectedAlert
5353
};
5454

5555
return (
56-
<Paper elevation={3} sx={{ p: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
57-
<Typography variant="h6" gutterBottom>
56+
<Paper elevation={3} sx={{ p: 1.5, display: 'flex', flexDirection: 'column', gap: 1 }}>
57+
<Typography variant="subtitle1" fontWeight="bold" sx={{ mb: 0.5 }}>
5858
Workflow Control
5959
</Typography>
6060

61-
<FormControl fullWidth>
62-
<InputLabel>Select Alert</InputLabel>
61+
<FormControl fullWidth size="small">
62+
<InputLabel sx={{ fontSize: '0.875rem' }}>Select Alert</InputLabel>
6363
<Select
6464
value={selectedAlertId}
6565
label="Select Alert"
6666
onChange={(e) => setSelectedAlertId(e.target.value)}
6767
disabled={workflowRunning}
68+
sx={{ fontSize: '0.875rem' }}
6869
>
6970
{alerts.map((alert) => (
70-
<MenuItem key={alert.alert_id} value={alert.alert_id}>
71-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, width: '100%' }}>
71+
<MenuItem key={alert.alert_id} value={alert.alert_id} sx={{ py: 0.75 }}>
72+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, width: '100%' }}>
7273
{getSeverityIcon(alert.severity)}
7374
<Box sx={{ flex: 1 }}>
74-
<Typography variant="body2" fontWeight="bold">
75+
<Typography variant="caption" fontWeight="bold" display="block">
7576
{alert.alert_id}
7677
</Typography>
77-
<Typography variant="caption" color="text.secondary">
78+
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
7879
{alert.alert_type}
7980
</Typography>
8081
</Box>
8182
<Chip
8283
label={alert.severity}
8384
size="small"
8485
color={getSeverityColor(alert.severity)}
86+
sx={{ height: 18, fontSize: '0.7rem' }}
8587
/>
8688
</Box>
8789
</MenuItem>
@@ -90,46 +92,48 @@ function ControlPanel({ alerts, onStartWorkflow, workflowRunning, selectedAlert
9092
</FormControl>
9193

9294
{selectedAlertId && !workflowRunning && (
93-
<Box sx={{ p: 2, bgcolor: 'grey.100', borderRadius: 1 }}>
94-
<Typography variant="body2" color="text.secondary" gutterBottom>
95+
<Box sx={{ p: 1, bgcolor: 'grey.100', borderRadius: 1 }}>
96+
<Typography variant="caption" color="text.secondary" display="block" sx={{ mb: 0.5 }}>
9597
<strong>Description:</strong>
9698
</Typography>
97-
<Typography variant="body2">
99+
<Typography variant="caption" display="block" sx={{ mb: 0.5 }}>
98100
{alerts.find((a) => a.alert_id === selectedAlertId)?.description}
99101
</Typography>
100-
<Box sx={{ mt: 1, display: 'flex', gap: 1 }}>
102+
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
101103
<Chip
102104
label={`Customer ${alerts.find((a) => a.alert_id === selectedAlertId)?.customer_id}`}
103105
size="small"
104106
variant="outlined"
107+
sx={{ height: 18, fontSize: '0.7rem' }}
105108
/>
106109
<Chip
107110
label={alerts.find((a) => a.alert_id === selectedAlertId)?.alert_type}
108111
size="small"
109112
variant="outlined"
113+
sx={{ height: 18, fontSize: '0.7rem' }}
110114
/>
111115
</Box>
112116
</Box>
113117
)}
114118

115119
<Button
116120
variant="contained"
117-
size="large"
121+
size="small"
118122
fullWidth
119-
startIcon={workflowRunning ? <CircularProgress size={20} color="inherit" /> : <PlayArrowIcon />}
123+
startIcon={workflowRunning ? <CircularProgress size={16} color="inherit" /> : <PlayArrowIcon fontSize="small" />}
120124
onClick={handleStartClick}
121125
disabled={!selectedAlertId || workflowRunning}
122-
sx={{ mt: 1 }}
126+
sx={{ mt: 0.5, py: 0.75, fontSize: '0.875rem' }}
123127
>
124-
{workflowRunning ? 'Workflow Running...' : 'Start Workflow'}
128+
{workflowRunning ? 'Running...' : 'Start Workflow'}
125129
</Button>
126130

127131
{selectedAlert && workflowRunning && (
128-
<Box sx={{ p: 2, bgcolor: 'primary.main', color: 'white', borderRadius: 1 }}>
129-
<Typography variant="body2" fontWeight="bold">
132+
<Box sx={{ p: 1, bgcolor: 'primary.main', color: 'white', borderRadius: 1 }}>
133+
<Typography variant="caption" fontWeight="bold" display="block">
130134
Active Workflow
131135
</Typography>
132-
<Typography variant="caption">
136+
<Typography variant="caption" sx={{ fontSize: '0.7rem' }}>
133137
Processing {selectedAlert.alert_id}
134138
</Typography>
135139
</Box>

agentic_ai/workflow/fraud_detection/ui/src/components/EventLog.jsx

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ function EventLog({ events }) {
107107
};
108108

109109
return (
110-
<Paper elevation={3} sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
111-
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
112-
<Typography variant="h6">Event Log</Typography>
113-
<Typography variant="caption" color="text.secondary">
110+
<Paper elevation={3} sx={{ height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
111+
<Box sx={{ p: 1.5, borderBottom: 1, borderColor: 'divider', flexShrink: 0 }}>
112+
<Typography variant="subtitle1" fontWeight="bold">Event Log</Typography>
113+
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
114114
{events.length} events
115115
</Typography>
116116
</Box>
@@ -120,13 +120,27 @@ function EventLog({ events }) {
120120
sx={{
121121
flex: 1,
122122
overflow: 'auto',
123-
px: 1,
123+
px: 0.5,
124124
py: 0,
125+
minHeight: 0,
126+
'&::-webkit-scrollbar': {
127+
width: '8px',
128+
},
129+
'&::-webkit-scrollbar-track': {
130+
backgroundColor: 'grey.100',
131+
},
132+
'&::-webkit-scrollbar-thumb': {
133+
backgroundColor: 'grey.400',
134+
borderRadius: '4px',
135+
'&:hover': {
136+
backgroundColor: 'grey.600',
137+
},
138+
},
125139
}}
126140
>
127141
{events.length === 0 ? (
128-
<Box sx={{ p: 3, textAlign: 'center' }}>
129-
<Typography variant="body2" color="text.secondary">
142+
<Box sx={{ p: 2, textAlign: 'center' }}>
143+
<Typography variant="caption" color="text.secondary">
130144
No events yet. Start a workflow to see events.
131145
</Typography>
132146
</Box>
@@ -135,38 +149,38 @@ function EventLog({ events }) {
135149
<React.Fragment key={index}>
136150
<ListItem
137151
sx={{
138-
py: 1.5,
139-
px: 1,
152+
py: 0.75,
153+
px: 0.75,
140154
'&:hover': {
141155
bgcolor: 'action.hover',
142156
},
143157
}}
144158
>
145-
<ListItemIcon sx={{ minWidth: 40 }}>
146-
{getEventIcon(event)}
159+
<ListItemIcon sx={{ minWidth: 32 }}>
160+
{React.cloneElement(getEventIcon(event), { fontSize: 'small' })}
147161
</ListItemIcon>
148162
<ListItemText
149163
primary={
150-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
151-
<Typography variant="body2" fontWeight="medium">
164+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexWrap: 'wrap' }}>
165+
<Typography variant="caption" fontWeight="medium" sx={{ fontSize: '0.75rem' }}>
152166
{getEventTitle(event)}
153167
</Typography>
154168
<Chip
155169
label={event.event_type || event.type}
156170
size="small"
157171
color={getEventColor(event)}
158-
sx={{ height: 20, fontSize: 10 }}
172+
sx={{ height: 16, fontSize: '0.65rem' }}
159173
/>
160174
</Box>
161175
}
162176
secondary={
163-
<Typography variant="caption" color="text.secondary">
177+
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.65rem' }}>
164178
{formatTime(event.timestamp)}
165179
</Typography>
166180
}
167181
/>
168182
</ListItem>
169-
{index < events.length - 1 && <Divider variant="inset" component="li" />}
183+
{index < events.length - 1 && <Divider variant="inset" component="li" sx={{ ml: 4 }} />}
170184
</React.Fragment>
171185
))
172186
)}

0 commit comments

Comments
 (0)