- Removed extra blank lines after export default in TokenDetail, CompanyDetail, Templates - Trigger Vite HMR to reload modules with fresh exports
333 lines
10 KiB
JavaScript
333 lines
10 KiB
JavaScript
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;
|