Files
ETB/ETB-FrontEnd/src/pages/KnowledgeManagement/KnowledgeArticles.tsx
Iliyan Angelov 306b20e24a Frontend start
2025-09-14 00:54:48 +03:00

582 lines
19 KiB
TypeScript

import React, { useState } from 'react';
import {
Grid,
Paper,
Typography,
Box,
Card,
CardContent,
Button,
TextField,
InputAdornment,
FormControl,
InputLabel,
Select,
MenuItem,
Chip,
IconButton,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
List,
ListItem,
ListItemText,
Rating,
Tabs,
Tab,
Alert,
Fab,
Tooltip,
} from '@mui/material';
import {
Search,
Add,
Edit,
Visibility,
Star,
TrendingUp,
CheckCircle,
Warning,
Psychology,
FilterList,
Sort,
} from '@mui/icons-material';
import { DataGrid } from '@mui/x-data-grid';
import { motion, AnimatePresence } from 'framer-motion';
import { AnimatedCard } from '../../components/Animated/AnimatedCard';
import { GlassmorphismCard } from '../../components/Animated/GlassmorphismCard';
const knowledgeArticles = [
{
id: 1,
title: 'How to Reset Your Password',
category: 'User Support',
tags: ['password', 'authentication', 'user'],
content: 'Step-by-step guide to reset your password using the self-service portal...',
author: 'John Smith',
created: '2024-01-10',
updated: '2024-01-15',
version: '2.1',
status: 'published',
rating: 4.5,
views: 1250,
helpful: 89,
aiSuggestions: 3,
lastReviewed: '2024-01-14'
},
{
id: 2,
title: 'Troubleshooting Email Issues',
category: 'Infrastructure',
tags: ['email', 'outlook', 'connectivity'],
content: 'Common email problems and their solutions including connectivity issues...',
author: 'Sarah Johnson',
created: '2024-01-08',
updated: '2024-01-12',
version: '1.8',
status: 'published',
rating: 4.2,
views: 890,
helpful: 67,
aiSuggestions: 2,
lastReviewed: '2024-01-10'
},
{
id: 3,
title: 'Setting Up VPN Connection',
category: 'Network',
tags: ['vpn', 'network', 'remote'],
content: 'Complete guide to setting up and using VPN for remote access...',
author: 'Mike Davis',
created: '2024-01-12',
updated: '2024-01-14',
version: '3.0',
status: 'published',
rating: 4.7,
views: 2100,
helpful: 156,
aiSuggestions: 1,
lastReviewed: '2024-01-13'
}
];
const categories = ['User Support', 'Infrastructure', 'Network', 'Hardware', 'Applications', 'Security', 'General'];
const KnowledgeArticles: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('');
const [categoryFilter, setCategoryFilter] = useState('All');
const [statusFilter, setStatusFilter] = useState('All');
const [selectedArticle, setSelectedArticle] = useState<any>(null);
const [articleDialogOpen, setArticleDialogOpen] = useState<boolean>(false);
const [tabValue, setTabValue] = useState<number>(0);
const [newArticle, setNewArticle] = useState({
title: '',
category: '',
tags: [],
content: '',
status: 'draft'
});
const columns = [
{ field: 'id', headerName: 'ID', width: 80 },
{ field: 'title', headerName: 'Title', width: 300 },
{ field: 'category', headerName: 'Category', width: 120 },
{ field: 'author', headerName: 'Author', width: 150 },
{ field: 'version', headerName: 'Version', width: 100 },
{
field: 'status',
headerName: 'Status',
width: 100,
renderCell: (params: any) => (
<Chip
label={params.value}
size="small"
color={params.value === 'published' ? 'success' :
params.value === 'draft' ? 'warning' : 'info'}
/>
),
},
{ field: 'rating', headerName: 'Rating', width: 100 },
{ field: 'views', headerName: 'Views', width: 100 },
{ field: 'updated', headerName: 'Updated', width: 120 },
{
field: 'actions',
headerName: 'Actions',
width: 150,
renderCell: (params: any) => (
<Box>
<IconButton size="small" onClick={() => handleViewArticle(params.row)}>
<Visibility />
</IconButton>
<IconButton size="small" onClick={() => handleEditArticle(params.row)}>
<Edit />
</IconButton>
</Box>
),
},
];
const handleViewArticle = (article: any) => {
setSelectedArticle(article);
setArticleDialogOpen(true);
};
const handleEditArticle = (article: any) => {
setSelectedArticle(article);
setNewArticle(article);
setArticleDialogOpen(true);
};
const handleCreateArticle = () => {
setSelectedArticle(null);
setNewArticle({
title: '',
category: '',
tags: [],
content: '',
status: 'draft'
});
setArticleDialogOpen(true);
};
const handleSaveArticle = () => {
console.log('Saving article:', newArticle);
setArticleDialogOpen(false);
};
const filteredArticles = knowledgeArticles.filter(article => {
const matchesSearch = article.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
article.content.toLowerCase().includes(searchTerm.toLowerCase()) ||
article.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()));
const matchesCategory = categoryFilter === 'All' || article.category === categoryFilter;
const matchesStatus = statusFilter === 'All' || article.status === statusFilter;
return matchesSearch && matchesCategory && matchesStatus;
});
return (
<Box>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Box>
<Typography variant="h4" gutterBottom sx={{ fontWeight: 700 }}>
Knowledge Articles
</Typography>
<Typography variant="subtitle1" color="text.secondary">
Manage and organize your knowledge base with AI-powered insights
</Typography>
</Box>
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Button
variant="contained"
startIcon={<Add />}
onClick={handleCreateArticle}
sx={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: 2,
px: 3,
py: 1.5,
fontWeight: 600,
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: '0 8px 25px rgba(102, 126, 234, 0.4)',
},
transition: 'all 0.2s ease-in-out',
}}
>
Create Article
</Button>
</motion.div>
</Box>
</motion.div>
<AnimatedCard delay={0.2}>
<GlassmorphismCard sx={{ p: 2, mb: 3 }}>
<Tabs
value={tabValue}
onChange={(e, newValue) => setTabValue(newValue)}
sx={{
'& .MuiTab-root': {
fontWeight: 600,
textTransform: 'none',
minHeight: 48,
},
'& .Mui-selected': {
color: 'primary.main',
},
}}
>
<Tab label="All Articles" />
<Tab label="AI Suggestions" />
<Tab label="Analytics" />
</Tabs>
</GlassmorphismCard>
</AnimatedCard>
<AnimatePresence mode="wait">
{tabValue === 0 && (
<motion.div
key="articles"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
{/* Filters */}
<AnimatedCard delay={0.3}>
<GlassmorphismCard sx={{ p: 3, mb: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<FilterList sx={{ mr: 1, color: 'primary.main' }} />
<Typography variant="h6" sx={{ fontWeight: 600 }}>
Filters & Search
</Typography>
</Box>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} md={4}>
<TextField
fullWidth
placeholder="Search articles..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
}}
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: 2,
}
}}
/>
</Grid>
<Grid item xs={12} md={3}>
<FormControl fullWidth>
<InputLabel>Category</InputLabel>
<Select
value={categoryFilter}
label="Category"
onChange={(e) => setCategoryFilter(e.target.value)}
sx={{ borderRadius: 2 }}
>
<MenuItem value="All">All Categories</MenuItem>
{categories.map((category) => (
<MenuItem key={category} value={category}>{category}</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={3}>
<FormControl fullWidth>
<InputLabel>Status</InputLabel>
<Select
value={statusFilter}
label="Status"
onChange={(e) => setStatusFilter(e.target.value)}
sx={{ borderRadius: 2 }}
>
<MenuItem value="All">All Status</MenuItem>
<MenuItem value="published">Published</MenuItem>
<MenuItem value="draft">Draft</MenuItem>
<MenuItem value="review">Under Review</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={2}>
<Button
variant="outlined"
startIcon={<Sort />}
fullWidth
sx={{ borderRadius: 2, py: 1.5 }}
>
Sort
</Button>
</Grid>
</Grid>
</GlassmorphismCard>
</AnimatedCard>
{/* Articles Table */}
<AnimatedCard delay={0.4}>
<GlassmorphismCard sx={{ p: 2 }}>
<DataGrid
rows={filteredArticles}
columns={columns}
pageSize={10}
rowsPerPageOptions={[10, 25, 50]}
checkboxSelection
disableSelectionOnClick
sx={{
height: 500,
border: 'none',
'& .MuiDataGrid-cell': {
borderBottom: '1px solid rgba(0,0,0,0.1)',
},
'& .MuiDataGrid-columnHeaders': {
backgroundColor: 'rgba(0,0,0,0.02)',
borderBottom: '2px solid rgba(0,0,0,0.1)',
},
}}
/>
</GlassmorphismCard>
</AnimatedCard>
</motion.div>
)}
{tabValue === 1 && (
<Grid container spacing={3}>
{knowledgeArticles.map((article: any) => (
<Grid item xs={12} md={6} key={article.id}>
<Card>
<CardContent>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">{article.title}</Typography>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
label={`${article.aiSuggestions} suggestions`}
size="small"
color="primary"
icon={<Psychology />}
/>
</Box>
</Box>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
{article.content.substring(0, 100)}...
</Typography>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', mb: 2 }}>
{article.tags.map((tag: string, index: number) => (
<Chip key={index} label={tag} size="small" variant="outlined" />
))}
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Rating value={article.rating} readOnly size="small" />
<Typography variant="body2" color="text.secondary">
({article.rating})
</Typography>
</Box>
<Typography variant="body2" color="text.secondary">
{article.views} views
</Typography>
</Box>
</CardContent>
</Card>
</Grid>
))}
</Grid>
)}
{tabValue === 2 && (
<Grid container spacing={3}>
<Grid item xs={12} md={6}>
<Paper sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
Article Performance
</Typography>
<List>
<ListItem>
<ListItemText
primary="Most Viewed Article"
secondary="Setting Up VPN Connection (2,100 views)"
/>
<TrendingUp color="success" />
</ListItem>
<ListItem>
<ListItemText
primary="Highest Rated"
secondary="VPN Setup Guide (4.7/5.0)"
/>
<Star color="warning" />
</ListItem>
<ListItem>
<ListItemText
primary="Most Helpful"
secondary="Password Reset Guide (89 helpful votes)"
/>
<CheckCircle color="success" />
</ListItem>
</List>
</Paper>
</Grid>
<Grid item xs={12} md={6}>
<Paper sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
AI Insights
</Typography>
<Alert severity="info" sx={{ mb: 2 }}>
<Typography variant="subtitle2">Knowledge Base Health</Typography>
<Typography variant="body2">
Your knowledge base is performing well with 4.3 average rating and 95% user satisfaction.
</Typography>
</Alert>
<List>
<ListItem>
<ListItemText
primary="Articles Needing Updates"
secondary="3 articles flagged for review"
/>
<Warning color="warning" />
</ListItem>
<ListItem>
<ListItemText
primary="AI Suggestions Generated"
secondary="15 suggestions this week"
/>
<Psychology color="primary" />
</ListItem>
<ListItem>
<ListItemText
primary="Search Success Rate"
secondary="87% of searches find relevant articles"
/>
<CheckCircle color="success" />
</ListItem>
</List>
</Paper>
</Grid>
</Grid>
)}
</AnimatePresence>
{/* Article Dialog */}
<Dialog open={articleDialogOpen} onClose={() => setArticleDialogOpen(false)} maxWidth="md" fullWidth>
<DialogTitle>
{selectedArticle ? 'Edit Article' : 'Create New Article'}
</DialogTitle>
<DialogContent>
<Grid container spacing={2} sx={{ mt: 1 }}>
<Grid item xs={12}>
<TextField
fullWidth
label="Article Title"
value={newArticle.title}
onChange={(e) => setNewArticle(prev => ({ ...prev, title: e.target.value }))}
required
/>
</Grid>
<Grid item xs={12} md={6}>
<FormControl fullWidth required>
<InputLabel>Category</InputLabel>
<Select
value={newArticle.category}
label="Category"
onChange={(e) => setNewArticle(prev => ({ ...prev, category: e.target.value }))}
>
{categories.map((category) => (
<MenuItem key={category} value={category}>{category}</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<FormControl fullWidth>
<InputLabel>Status</InputLabel>
<Select
value={newArticle.status}
label="Status"
onChange={(e) => setNewArticle(prev => ({ ...prev, status: e.target.value }))}
>
<MenuItem value="draft">Draft</MenuItem>
<MenuItem value="review">Under Review</MenuItem>
<MenuItem value="published">Published</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
multiline
rows={8}
label="Content"
value={newArticle.content}
onChange={(e) => setNewArticle(prev => ({ ...prev, content: e.target.value }))}
placeholder="Write your article content here..."
required
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button onClick={() => setArticleDialogOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={handleSaveArticle}>
{selectedArticle ? 'Update' : 'Create'}
</Button>
</DialogActions>
</Dialog>
{/* Floating Action Button */}
<Tooltip title="Create New Article" placement="left">
<Fab
color="primary"
aria-label="add"
onClick={handleCreateArticle}
sx={{
position: 'fixed',
bottom: 24,
right: 24,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
'&:hover': {
transform: 'scale(1.1)',
boxShadow: '0 8px 25px rgba(102, 126, 234, 0.4)',
},
transition: 'all 0.2s ease-in-out',
}}
>
<Add />
</Fab>
</Tooltip>
</Box>
);
}
export default KnowledgeArticles;