384 lines
13 KiB
TypeScript
384 lines
13 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Grid,
|
|
Paper,
|
|
Typography,
|
|
Box,
|
|
Card,
|
|
CardContent,
|
|
Button,
|
|
Chip,
|
|
TextField,
|
|
List,
|
|
ListItem,
|
|
ListItemText,
|
|
ListItemIcon,
|
|
Divider,
|
|
Alert,
|
|
Stepper,
|
|
Step,
|
|
StepLabel,
|
|
StepContent,
|
|
Accordion,
|
|
AccordionSummary,
|
|
AccordionDetails,
|
|
} from '@mui/material';
|
|
import {
|
|
Build,
|
|
Search,
|
|
Timeline,
|
|
CheckCircle,
|
|
Cancel,
|
|
ExpandMore,
|
|
Add,
|
|
} from '@mui/icons-material';
|
|
|
|
const analysisMethods = [
|
|
{
|
|
id: '5why',
|
|
name: '5 Whys Analysis',
|
|
description: 'Ask "why" five times to drill down to the root cause',
|
|
icon: <Search />,
|
|
},
|
|
{
|
|
id: 'fishbone',
|
|
name: 'Fishbone Diagram',
|
|
description: 'Categorize potential causes into main categories',
|
|
icon: <Timeline />,
|
|
},
|
|
{
|
|
id: 'pareto',
|
|
name: 'Pareto Analysis',
|
|
description: 'Identify the 20% of causes that create 80% of problems',
|
|
icon: <Build />,
|
|
},
|
|
{
|
|
id: 'fault-tree',
|
|
name: 'Fault Tree Analysis',
|
|
description: 'Systematic approach to identify all possible causes',
|
|
icon: <Timeline />,
|
|
},
|
|
];
|
|
|
|
const currentAnalysis = {
|
|
problemId: 'PRB001',
|
|
title: 'Recurring Email Server Outages',
|
|
status: 'In Progress',
|
|
method: '5why',
|
|
findings: [
|
|
{
|
|
id: 1,
|
|
question: 'Why did the email server go down?',
|
|
answer: 'The server ran out of disk space',
|
|
evidence: 'Disk usage logs show 100% utilization',
|
|
verified: true,
|
|
},
|
|
{
|
|
id: 2,
|
|
question: 'Why did the server run out of disk space?',
|
|
answer: 'Log files were not being rotated properly',
|
|
evidence: 'Log rotation script failed due to permission issues',
|
|
verified: true,
|
|
},
|
|
{
|
|
id: 3,
|
|
question: 'Why did the log rotation script fail?',
|
|
answer: 'The script was running with incorrect permissions',
|
|
evidence: 'Script was running as root but needed mail user permissions',
|
|
verified: true,
|
|
},
|
|
{
|
|
id: 4,
|
|
question: 'Why was the script running with incorrect permissions?',
|
|
answer: 'The deployment process did not set proper ownership',
|
|
evidence: 'Deployment documentation missing permission setup steps',
|
|
verified: true,
|
|
},
|
|
{
|
|
id: 5,
|
|
question: 'Why was the deployment process missing permission setup?',
|
|
answer: 'No formal deployment checklist or validation process',
|
|
evidence: 'Deployment was done manually without following procedures',
|
|
verified: true,
|
|
},
|
|
],
|
|
rootCause: 'Lack of formal deployment procedures and validation processes',
|
|
recommendations: [
|
|
'Implement formal deployment checklist',
|
|
'Add automated permission validation',
|
|
'Create deployment approval process',
|
|
'Implement monitoring for disk usage',
|
|
],
|
|
};
|
|
|
|
const RootCauseAnalysis: React.FC = () => {
|
|
const [selectedMethod, setSelectedMethod] = useState('5why');
|
|
const [newFinding, setNewFinding] = useState({
|
|
question: '',
|
|
answer: '',
|
|
evidence: '',
|
|
});
|
|
const [showAddForm, setShowAddForm] = useState<boolean>(false);
|
|
|
|
const handleAddFinding = () => {
|
|
console.log('Adding finding:', newFinding);
|
|
setNewFinding({ question: '', answer: '', evidence: '' });
|
|
setShowAddForm(false);
|
|
};
|
|
|
|
const getMethodIcon = (methodId: any) => {
|
|
const method = analysisMethods.find(m => m.id === methodId);
|
|
return method ? method.icon : <Build />;
|
|
};
|
|
|
|
return (
|
|
<Box>
|
|
<Typography variant="h4" gutterBottom>
|
|
Root Cause Analysis
|
|
</Typography>
|
|
<Typography variant="subtitle1" color="text.secondary" gutterBottom>
|
|
Systematic investigation to identify the underlying cause of problems
|
|
</Typography>
|
|
|
|
<Grid container spacing={3}>
|
|
{/* Problem Information */}
|
|
<Grid item xs={12} md={4}>
|
|
<Paper sx={{ p: 2, mb: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
Problem Information
|
|
</Typography>
|
|
<Divider sx={{ mb: 2 }} />
|
|
<Typography><strong>Problem ID:</strong> {currentAnalysis.problemId}</Typography>
|
|
<Typography><strong>Title:</strong> {currentAnalysis.title}</Typography>
|
|
<Typography><strong>Status:</strong>
|
|
<Chip
|
|
label={currentAnalysis.status}
|
|
size="small"
|
|
color="warning"
|
|
sx={{ ml: 1 }}
|
|
/>
|
|
</Typography>
|
|
<Typography><strong>Method:</strong>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mt: 1 }}>
|
|
{getMethodIcon(currentAnalysis.method)}
|
|
<Typography variant="body2" sx={{ ml: 1 }}>
|
|
{analysisMethods.find(m => m.id === currentAnalysis.method)?.name}
|
|
</Typography>
|
|
</Box>
|
|
</Typography>
|
|
</Paper>
|
|
|
|
<Paper sx={{ p: 2 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
Analysis Methods
|
|
</Typography>
|
|
<Divider sx={{ mb: 2 }} />
|
|
{analysisMethods.map((method) => (
|
|
<Card
|
|
key={method.id}
|
|
sx={{
|
|
mb: 1,
|
|
cursor: 'pointer',
|
|
border: selectedMethod === method.id ? 2 : 1,
|
|
borderColor: selectedMethod === method.id ? 'primary.main' : 'divider'
|
|
}}
|
|
onClick={() => setSelectedMethod(method.id)}
|
|
>
|
|
<CardContent sx={{ p: 2, '&:last-child': { pb: 2 } }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
|
<Box sx={{ color: 'primary.main', mr: 1 }}>
|
|
{method.icon}
|
|
</Box>
|
|
<Typography variant="subtitle2">{method.name}</Typography>
|
|
</Box>
|
|
<Typography variant="body2" color="text.secondary">
|
|
{method.description}
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</Paper>
|
|
</Grid>
|
|
|
|
{/* Analysis Content */}
|
|
<Grid item xs={12} md={8}>
|
|
{selectedMethod === '5why' && (
|
|
<Paper sx={{ p: 2, mb: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
5 Whys Analysis
|
|
</Typography>
|
|
<Divider sx={{ mb: 2 }} />
|
|
|
|
<Stepper orientation="vertical">
|
|
{currentAnalysis.findings.map((finding, index) => (
|
|
<Step key={finding.id} active>
|
|
<StepLabel>
|
|
<Typography variant="subtitle1">
|
|
Why #{index + 1}
|
|
</Typography>
|
|
</StepLabel>
|
|
<StepContent>
|
|
<Card sx={{ mb: 2 }}>
|
|
<CardContent>
|
|
<Typography variant="subtitle2" gutterBottom>
|
|
{finding.question}
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ mb: 2 }}>
|
|
<strong>Answer:</strong> {finding.answer}
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
<strong>Evidence:</strong> {finding.evidence}
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
<Chip
|
|
icon={finding.verified ? <CheckCircle /> : <Cancel />}
|
|
label={finding.verified ? 'Verified' : 'Unverified'}
|
|
size="small"
|
|
color={finding.verified ? 'success' : 'error'}
|
|
/>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
</StepContent>
|
|
</Step>
|
|
))}
|
|
</Stepper>
|
|
|
|
{showAddForm && (
|
|
<Card sx={{ mt: 2, p: 2 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
Add New Finding
|
|
</Typography>
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
fullWidth
|
|
label="Question"
|
|
value={newFinding.question}
|
|
onChange={(e) => setNewFinding(prev => ({ ...prev, question: e.target.value }))}
|
|
placeholder="Why did this happen?"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
fullWidth
|
|
label="Answer"
|
|
value={newFinding.answer}
|
|
onChange={(e) => setNewFinding(prev => ({ ...prev, answer: e.target.value }))}
|
|
placeholder="The answer to the question"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
fullWidth
|
|
label="Evidence"
|
|
value={newFinding.evidence}
|
|
onChange={(e) => setNewFinding(prev => ({ ...prev, evidence: e.target.value }))}
|
|
placeholder="Supporting evidence or data"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
|
<Button variant="contained" onClick={handleAddFinding}>
|
|
Add Finding
|
|
</Button>
|
|
<Button onClick={() => setShowAddForm(false)}>
|
|
Cancel
|
|
</Button>
|
|
</Box>
|
|
</Grid>
|
|
</Grid>
|
|
</Card>
|
|
)}
|
|
|
|
{!showAddForm && (
|
|
<Button
|
|
variant="outlined"
|
|
startIcon={<Add />}
|
|
onClick={() => setShowAddForm(true)}
|
|
sx={{ mt: 2 }}
|
|
>
|
|
Add Finding
|
|
</Button>
|
|
)}
|
|
</Paper>
|
|
)}
|
|
|
|
{/* Root Cause Summary */}
|
|
<Paper sx={{ p: 2, mb: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
Root Cause Summary
|
|
</Typography>
|
|
<Divider sx={{ mb: 2 }} />
|
|
<Alert severity="info" sx={{ mb: 2 }}>
|
|
<Typography variant="subtitle2">Identified Root Cause:</Typography>
|
|
<Typography>{currentAnalysis.rootCause}</Typography>
|
|
</Alert>
|
|
|
|
<Typography variant="h6" gutterBottom>
|
|
Recommendations
|
|
</Typography>
|
|
<List>
|
|
{currentAnalysis.recommendations.map((recommendation, index) => (
|
|
<ListItem key={index}>
|
|
<ListItemIcon>
|
|
<CheckCircle color="success" />
|
|
</ListItemIcon>
|
|
<ListItemText primary={recommendation} />
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</Paper>
|
|
|
|
{/* Action Items */}
|
|
<Paper sx={{ p: 2 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
Action Items
|
|
</Typography>
|
|
<Divider sx={{ mb: 2 }} />
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
<Typography>Implement Deployment Checklist</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
<Typography>
|
|
Create a comprehensive deployment checklist that includes permission validation,
|
|
disk space checks, and service health verification.
|
|
</Typography>
|
|
<Box sx={{ mt: 2 }}>
|
|
<Button variant="outlined" size="small" sx={{ mr: 1 }}>
|
|
Assign
|
|
</Button>
|
|
<Button variant="outlined" size="small">
|
|
Set Due Date
|
|
</Button>
|
|
</Box>
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
<Typography>Add Disk Usage Monitoring</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
<Typography>
|
|
Implement automated monitoring for disk usage with alerts when usage exceeds 80%.
|
|
</Typography>
|
|
<Box sx={{ mt: 2 }}>
|
|
<Button variant="outlined" size="small" sx={{ mr: 1 }}>
|
|
Assign
|
|
</Button>
|
|
<Button variant="outlined" size="small">
|
|
Set Due Date
|
|
</Button>
|
|
</Box>
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
</Paper>
|
|
</Grid>
|
|
</Grid>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
export default RootCauseAnalysis;
|