333 lines
10 KiB
React
333 lines
10 KiB
React
|
|
import { useState, useEffect } from 'react';
|
|||
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|||
|
|
import {
|
|||
|
|
Box,
|
|||
|
|
Button,
|
|||
|
|
Paper,
|
|||
|
|
Typography,
|
|||
|
|
Grid,
|
|||
|
|
Card,
|
|||
|
|
CardContent,
|
|||
|
|
Table,
|
|||
|
|
TableBody,
|
|||
|
|
TableCell,
|
|||
|
|
TableContainer,
|
|||
|
|
TableHead,
|
|||
|
|
TableRow,
|
|||
|
|
Chip,
|
|||
|
|
CircularProgress,
|
|||
|
|
Dialog,
|
|||
|
|
DialogTitle,
|
|||
|
|
DialogContent,
|
|||
|
|
DialogActions,
|
|||
|
|
TextField,
|
|||
|
|
IconButton,
|
|||
|
|
} from '@mui/material';
|
|||
|
|
import {
|
|||
|
|
ArrowBack,
|
|||
|
|
Edit,
|
|||
|
|
Delete,
|
|||
|
|
Token as TokenIcon,
|
|||
|
|
CheckCircle,
|
|||
|
|
TrendingUp,
|
|||
|
|
} from '@mui/icons-material';
|
|||
|
|
import { companyService } from '../services/companyService';
|
|||
|
|
import { format } from 'date-fns';
|
|||
|
|
|
|||
|
|
function CompanyDetail() {
|
|||
|
|
const { id } = useParams();
|
|||
|
|
const navigate = useNavigate();
|
|||
|
|
const [company, setCompany] = useState(null);
|
|||
|
|
const [tokens, setTokens] = useState([]);
|
|||
|
|
const [stats, setStats] = useState(null);
|
|||
|
|
const [loading, setLoading] = useState(true);
|
|||
|
|
const [editDialog, setEditDialog] = useState(false);
|
|||
|
|
const [deleteDialog, setDeleteDialog] = useState(false);
|
|||
|
|
const [formData, setFormData] = useState({
|
|||
|
|
name: '',
|
|||
|
|
description: '',
|
|||
|
|
industry: '',
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
loadData();
|
|||
|
|
}, [id]);
|
|||
|
|
|
|||
|
|
const loadData = async () => {
|
|||
|
|
try {
|
|||
|
|
const [companyData, tokensData, statsData] = await Promise.all([
|
|||
|
|
companyService.getById(id),
|
|||
|
|
companyService.getTokens(id),
|
|||
|
|
companyService.getStats(id),
|
|||
|
|
]);
|
|||
|
|
setCompany(companyData.data);
|
|||
|
|
setTokens(tokensData.data);
|
|||
|
|
setStats(statsData.data);
|
|||
|
|
setFormData({
|
|||
|
|
name: companyData.data.name,
|
|||
|
|
description: companyData.data.description || '',
|
|||
|
|
industry: companyData.data.industry || '',
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to load company:', error);
|
|||
|
|
alert('Şirket yüklenemedi');
|
|||
|
|
navigate('/companies');
|
|||
|
|
} finally {
|
|||
|
|
setLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleUpdate = async () => {
|
|||
|
|
try {
|
|||
|
|
await companyService.update(id, formData);
|
|||
|
|
setEditDialog(false);
|
|||
|
|
loadData();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to update company:', error);
|
|||
|
|
alert('Şirket güncellenemedi');
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleDelete = async () => {
|
|||
|
|
try {
|
|||
|
|
await companyService.delete(id);
|
|||
|
|
navigate('/companies');
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to delete company:', error);
|
|||
|
|
alert('Şirket silinemedi: Önce tokenları silmelisiniz');
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (loading) {
|
|||
|
|
return (
|
|||
|
|
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
|||
|
|
<CircularProgress />
|
|||
|
|
</Box>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<Box>
|
|||
|
|
{/* Header */}
|
|||
|
|
<Box display="flex" alignItems="center" mb={3}>
|
|||
|
|
<IconButton onClick={() => navigate('/companies')} sx={{ mr: 2 }}>
|
|||
|
|
<ArrowBack />
|
|||
|
|
</IconButton>
|
|||
|
|
<Typography variant="h4" sx={{ flexGrow: 1 }}>
|
|||
|
|
{company.name}
|
|||
|
|
</Typography>
|
|||
|
|
<Button
|
|||
|
|
startIcon={<Edit />}
|
|||
|
|
onClick={() => setEditDialog(true)}
|
|||
|
|
sx={{ mr: 1 }}
|
|||
|
|
>
|
|||
|
|
Düzenle
|
|||
|
|
</Button>
|
|||
|
|
<Button
|
|||
|
|
startIcon={<Delete />}
|
|||
|
|
color="error"
|
|||
|
|
onClick={() => setDeleteDialog(true)}
|
|||
|
|
>
|
|||
|
|
Sil
|
|||
|
|
</Button>
|
|||
|
|
</Box>
|
|||
|
|
|
|||
|
|
{/* Stats Cards */}
|
|||
|
|
<Grid container spacing={3} sx={{ mb: 3 }}>
|
|||
|
|
<Grid item xs={12} sm={4}>
|
|||
|
|
<Card>
|
|||
|
|
<CardContent>
|
|||
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|||
|
|
<Box>
|
|||
|
|
<Typography color="textSecondary" variant="body2">
|
|||
|
|
Toplam Token
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="h4">{stats.total_tokens}</Typography>
|
|||
|
|
</Box>
|
|||
|
|
<TokenIcon color="primary" sx={{ fontSize: 40 }} />
|
|||
|
|
</Box>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</Grid>
|
|||
|
|
<Grid item xs={12} sm={4}>
|
|||
|
|
<Card>
|
|||
|
|
<CardContent>
|
|||
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|||
|
|
<Box>
|
|||
|
|
<Typography color="textSecondary" variant="body2">
|
|||
|
|
Toplam Tıklama
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="h4">{stats.total_clicks}</Typography>
|
|||
|
|
</Box>
|
|||
|
|
<CheckCircle color="success" sx={{ fontSize: 40 }} />
|
|||
|
|
</Box>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</Grid>
|
|||
|
|
<Grid item xs={12} sm={4}>
|
|||
|
|
<Card>
|
|||
|
|
<CardContent>
|
|||
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|||
|
|
<Box>
|
|||
|
|
<Typography color="textSecondary" variant="body2">
|
|||
|
|
Başarı Oranı
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="h4">{stats.click_rate}%</Typography>
|
|||
|
|
</Box>
|
|||
|
|
<TrendingUp
|
|||
|
|
color={stats.click_rate > 30 ? 'error' : 'warning'}
|
|||
|
|
sx={{ fontSize: 40 }}
|
|||
|
|
/>
|
|||
|
|
</Box>
|
|||
|
|
</CardContent>
|
|||
|
|
</Card>
|
|||
|
|
</Grid>
|
|||
|
|
</Grid>
|
|||
|
|
|
|||
|
|
{/* Company Info */}
|
|||
|
|
<Paper sx={{ p: 3, mb: 3 }}>
|
|||
|
|
<Typography variant="h6" gutterBottom>
|
|||
|
|
Şirket Bilgileri
|
|||
|
|
</Typography>
|
|||
|
|
<Grid container spacing={2}>
|
|||
|
|
<Grid item xs={12} sm={6}>
|
|||
|
|
<Typography variant="body2" color="textSecondary">
|
|||
|
|
Sektör
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body1">
|
|||
|
|
{company.industry || 'Belirtilmemiş'}
|
|||
|
|
</Typography>
|
|||
|
|
</Grid>
|
|||
|
|
<Grid item xs={12} sm={6}>
|
|||
|
|
<Typography variant="body2" color="textSecondary">
|
|||
|
|
Oluşturulma Tarihi
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body1">
|
|||
|
|
{format(new Date(company.created_at), 'dd/MM/yyyy HH:mm')}
|
|||
|
|
</Typography>
|
|||
|
|
</Grid>
|
|||
|
|
{company.description && (
|
|||
|
|
<Grid item xs={12}>
|
|||
|
|
<Typography variant="body2" color="textSecondary">
|
|||
|
|
Açıklama
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body1">{company.description}</Typography>
|
|||
|
|
</Grid>
|
|||
|
|
)}
|
|||
|
|
</Grid>
|
|||
|
|
</Paper>
|
|||
|
|
|
|||
|
|
{/* Tokens Table */}
|
|||
|
|
<Paper sx={{ p: 2 }}>
|
|||
|
|
<Typography variant="h6" gutterBottom>
|
|||
|
|
Tokenlar ({tokens.length})
|
|||
|
|
</Typography>
|
|||
|
|
<TableContainer>
|
|||
|
|
<Table>
|
|||
|
|
<TableHead>
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell>Email</TableCell>
|
|||
|
|
<TableCell>Çalışan</TableCell>
|
|||
|
|
<TableCell>Durum</TableCell>
|
|||
|
|
<TableCell align="right">Tıklama</TableCell>
|
|||
|
|
<TableCell>Tarih</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
</TableHead>
|
|||
|
|
<TableBody>
|
|||
|
|
{tokens.length === 0 ? (
|
|||
|
|
<TableRow>
|
|||
|
|
<TableCell colSpan={5} align="center">
|
|||
|
|
<Typography color="textSecondary">
|
|||
|
|
Henüz token oluşturulmamış
|
|||
|
|
</Typography>
|
|||
|
|
</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
) : (
|
|||
|
|
tokens.map((token) => (
|
|||
|
|
<TableRow key={token.id} hover>
|
|||
|
|
<TableCell>{token.target_email}</TableCell>
|
|||
|
|
<TableCell>{token.employee_name || '-'}</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
<Chip
|
|||
|
|
label={token.clicked ? 'Tıklandı' : 'Bekliyor'}
|
|||
|
|
color={token.clicked ? 'success' : 'default'}
|
|||
|
|
size="small"
|
|||
|
|
/>
|
|||
|
|
</TableCell>
|
|||
|
|
<TableCell align="right">{token.click_count}×</TableCell>
|
|||
|
|
<TableCell>
|
|||
|
|
{format(new Date(token.created_at), 'dd/MM/yyyy HH:mm')}
|
|||
|
|
</TableCell>
|
|||
|
|
</TableRow>
|
|||
|
|
))
|
|||
|
|
)}
|
|||
|
|
</TableBody>
|
|||
|
|
</Table>
|
|||
|
|
</TableContainer>
|
|||
|
|
</Paper>
|
|||
|
|
|
|||
|
|
{/* Edit Dialog */}
|
|||
|
|
<Dialog open={editDialog} onClose={() => setEditDialog(false)} maxWidth="sm" fullWidth>
|
|||
|
|
<DialogTitle>Şirket Düzenle</DialogTitle>
|
|||
|
|
<DialogContent>
|
|||
|
|
<TextField
|
|||
|
|
autoFocus
|
|||
|
|
margin="dense"
|
|||
|
|
label="Şirket Adı"
|
|||
|
|
fullWidth
|
|||
|
|
required
|
|||
|
|
value={formData.name}
|
|||
|
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|||
|
|
/>
|
|||
|
|
<TextField
|
|||
|
|
margin="dense"
|
|||
|
|
label="Açıklama"
|
|||
|
|
fullWidth
|
|||
|
|
multiline
|
|||
|
|
rows={2}
|
|||
|
|
value={formData.description}
|
|||
|
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
|||
|
|
/>
|
|||
|
|
<TextField
|
|||
|
|
margin="dense"
|
|||
|
|
label="Sektör"
|
|||
|
|
fullWidth
|
|||
|
|
value={formData.industry}
|
|||
|
|
onChange={(e) => setFormData({ ...formData, industry: e.target.value })}
|
|||
|
|
/>
|
|||
|
|
</DialogContent>
|
|||
|
|
<DialogActions>
|
|||
|
|
<Button onClick={() => setEditDialog(false)}>İptal</Button>
|
|||
|
|
<Button onClick={handleUpdate} variant="contained">
|
|||
|
|
Güncelle
|
|||
|
|
</Button>
|
|||
|
|
</DialogActions>
|
|||
|
|
</Dialog>
|
|||
|
|
|
|||
|
|
{/* Delete Confirmation */}
|
|||
|
|
<Dialog open={deleteDialog} onClose={() => setDeleteDialog(false)}>
|
|||
|
|
<DialogTitle>Şirketi Sil?</DialogTitle>
|
|||
|
|
<DialogContent>
|
|||
|
|
<Typography>
|
|||
|
|
<strong>{company.name}</strong> şirketini silmek istediğinizden emin misiniz?
|
|||
|
|
Bu işlem geri alınamaz.
|
|||
|
|
</Typography>
|
|||
|
|
{tokens.length > 0 && (
|
|||
|
|
<Typography color="error" sx={{ mt: 2 }}>
|
|||
|
|
⚠️ Bu şirkete ait {tokens.length} token var. Önce tokenları silmelisiniz.
|
|||
|
|
</Typography>
|
|||
|
|
)}
|
|||
|
|
</DialogContent>
|
|||
|
|
<DialogActions>
|
|||
|
|
<Button onClick={() => setDeleteDialog(false)}>İptal</Button>
|
|||
|
|
<Button onClick={handleDelete} color="error" variant="contained">
|
|||
|
|
Sil
|
|||
|
|
</Button>
|
|||
|
|
</DialogActions>
|
|||
|
|
</Dialog>
|
|||
|
|
</Box>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default CompanyDetail;
|