2025-11-10 17:27:19 +03:00
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
|
import {
|
|
|
|
|
|
Box,
|
|
|
|
|
|
Button,
|
|
|
|
|
|
Paper,
|
|
|
|
|
|
Typography,
|
|
|
|
|
|
Table,
|
|
|
|
|
|
TableBody,
|
|
|
|
|
|
TableCell,
|
|
|
|
|
|
TableContainer,
|
|
|
|
|
|
TableHead,
|
|
|
|
|
|
TableRow,
|
|
|
|
|
|
Chip,
|
|
|
|
|
|
CircularProgress,
|
|
|
|
|
|
Dialog,
|
|
|
|
|
|
DialogTitle,
|
|
|
|
|
|
DialogContent,
|
|
|
|
|
|
DialogActions,
|
|
|
|
|
|
TextField,
|
|
|
|
|
|
Switch,
|
|
|
|
|
|
FormControlLabel,
|
|
|
|
|
|
Tabs,
|
|
|
|
|
|
Tab,
|
|
|
|
|
|
} from '@mui/material';
|
|
|
|
|
|
import {
|
|
|
|
|
|
Add,
|
|
|
|
|
|
Edit,
|
|
|
|
|
|
Delete,
|
|
|
|
|
|
Preview,
|
|
|
|
|
|
ContentCopy,
|
2025-11-10 21:13:58 +03:00
|
|
|
|
AutoAwesome,
|
|
|
|
|
|
Send as SendIcon,
|
2025-11-10 17:27:19 +03:00
|
|
|
|
} from '@mui/icons-material';
|
|
|
|
|
|
import { templateService } from '../services/templateService';
|
|
|
|
|
|
import { format } from 'date-fns';
|
2025-11-10 21:13:58 +03:00
|
|
|
|
import axios from 'axios';
|
|
|
|
|
|
|
|
|
|
|
|
const API_URL = import.meta.env.VITE_API_URL;
|
2025-11-10 17:27:19 +03:00
|
|
|
|
|
|
|
|
|
|
const defaultForm = {
|
|
|
|
|
|
name: '',
|
|
|
|
|
|
template_type: '',
|
|
|
|
|
|
subject_template: '',
|
|
|
|
|
|
body_html: '',
|
|
|
|
|
|
description: '',
|
|
|
|
|
|
active: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function Templates() {
|
|
|
|
|
|
const [templates, setTemplates] = useState([]);
|
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
const [form, setForm] = useState(defaultForm);
|
|
|
|
|
|
const [activeTab, setActiveTab] = useState('form');
|
|
|
|
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
|
|
|
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
|
|
|
|
const [previewHtml, setPreviewHtml] = useState('');
|
|
|
|
|
|
const [previewUrl, setPreviewUrl] = useState('');
|
|
|
|
|
|
const [previewLoading, setPreviewLoading] = useState(false);
|
|
|
|
|
|
const [selectedTemplate, setSelectedTemplate] = useState(null);
|
|
|
|
|
|
const [companyPlaceholder, setCompanyPlaceholder] = useState('Örnek Şirket');
|
|
|
|
|
|
const [employeePlaceholder, setEmployeePlaceholder] = useState('Ahmet Yılmaz');
|
2025-11-10 21:13:58 +03:00
|
|
|
|
const [aiDialogOpen, setAiDialogOpen] = useState(false);
|
|
|
|
|
|
const [aiGenerating, setAiGenerating] = useState(false);
|
|
|
|
|
|
const [aiForm, setAiForm] = useState({
|
|
|
|
|
|
company_name: '',
|
|
|
|
|
|
scenario: '',
|
|
|
|
|
|
employee_info: '',
|
|
|
|
|
|
custom_prompt: '',
|
|
|
|
|
|
template_name: '',
|
|
|
|
|
|
template_type: '',
|
|
|
|
|
|
});
|
|
|
|
|
|
const [testMailDialogOpen, setTestMailDialogOpen] = useState(false);
|
|
|
|
|
|
const [testMailAddress, setTestMailAddress] = useState('');
|
2025-11-10 17:27:19 +03:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
loadTemplates();
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
const loadTemplates = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
const response = await templateService.getAll();
|
2025-11-10 19:17:13 +03:00
|
|
|
|
setTemplates(response.data || []);
|
2025-11-10 17:27:19 +03:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to load templates:', error);
|
|
|
|
|
|
alert('Şablonlar yüklenemedi');
|
2025-11-10 19:17:13 +03:00
|
|
|
|
setTemplates([]);
|
2025-11-10 17:27:19 +03:00
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleOpenCreate = () => {
|
|
|
|
|
|
setSelectedTemplate(null);
|
|
|
|
|
|
setForm(defaultForm);
|
|
|
|
|
|
setActiveTab('form');
|
|
|
|
|
|
setDialogOpen(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleOpenEdit = async (template) => {
|
|
|
|
|
|
setSelectedTemplate(template);
|
|
|
|
|
|
setForm({
|
|
|
|
|
|
name: template.name,
|
|
|
|
|
|
template_type: template.template_type,
|
|
|
|
|
|
subject_template: template.subject_template || '',
|
|
|
|
|
|
body_html: template.body_html,
|
|
|
|
|
|
description: template.description || '',
|
|
|
|
|
|
active: Boolean(template.active),
|
|
|
|
|
|
});
|
|
|
|
|
|
setActiveTab('form');
|
|
|
|
|
|
setDialogOpen(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleCloseDialog = () => {
|
|
|
|
|
|
setDialogOpen(false);
|
|
|
|
|
|
setSelectedTemplate(null);
|
|
|
|
|
|
if (previewUrl) {
|
|
|
|
|
|
URL.revokeObjectURL(previewUrl);
|
|
|
|
|
|
setPreviewUrl('');
|
|
|
|
|
|
}
|
|
|
|
|
|
setPreviewHtml('');
|
|
|
|
|
|
setActiveTab('form');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleSave = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (selectedTemplate) {
|
|
|
|
|
|
await templateService.update(selectedTemplate.id, form);
|
|
|
|
|
|
alert('Şablon güncellendi');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await templateService.create(form);
|
|
|
|
|
|
alert('Şablon oluşturuldu');
|
|
|
|
|
|
}
|
|
|
|
|
|
handleCloseDialog();
|
|
|
|
|
|
loadTemplates();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
const message = error.response?.data?.error || 'Şablon kaydedilemedi';
|
|
|
|
|
|
alert(message);
|
|
|
|
|
|
console.error('Failed to save template:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleDelete = async (template) => {
|
|
|
|
|
|
if (!window.confirm(`${template.name} şablonunu silmek istediğinizden emin misiniz?`)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
await templateService.delete(template.id);
|
|
|
|
|
|
alert('Şablon silindi');
|
|
|
|
|
|
loadTemplates();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
const message = error.response?.data?.error || 'Şablon silinemedi';
|
|
|
|
|
|
alert(message);
|
|
|
|
|
|
console.error('Failed to delete template:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-10 21:13:58 +03:00
|
|
|
|
const handleGenerateWithAI = async () => {
|
|
|
|
|
|
if (!aiForm.company_name || !aiForm.scenario || !aiForm.template_name || !aiForm.template_type) {
|
|
|
|
|
|
alert('Lütfen tüm zorunlu alanları doldurun');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setAiGenerating(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await axios.post(
|
|
|
|
|
|
`${API_URL}/api/ollama/generate-template`,
|
|
|
|
|
|
aiForm,
|
|
|
|
|
|
{ withCredentials: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
alert(response.data.message);
|
|
|
|
|
|
setAiDialogOpen(false);
|
|
|
|
|
|
setAiForm({
|
|
|
|
|
|
company_name: '',
|
|
|
|
|
|
scenario: '',
|
|
|
|
|
|
employee_info: '',
|
|
|
|
|
|
custom_prompt: '',
|
|
|
|
|
|
template_name: '',
|
|
|
|
|
|
template_type: '',
|
|
|
|
|
|
});
|
|
|
|
|
|
loadTemplates();
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
const message = error.response?.data?.error || 'AI ile şablon oluşturulamadı';
|
|
|
|
|
|
alert(message);
|
|
|
|
|
|
console.error('AI generation failed:', error);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setAiGenerating(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleSendTestMail = async () => {
|
|
|
|
|
|
if (!testMailAddress || !selectedTemplate) {
|
|
|
|
|
|
alert('Lütfen mail adresi girin');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await axios.post(
|
|
|
|
|
|
`${API_URL}/api/ollama/send-test-mail`,
|
|
|
|
|
|
{
|
|
|
|
|
|
test_email: testMailAddress,
|
|
|
|
|
|
subject: selectedTemplate.subject_template,
|
|
|
|
|
|
body: selectedTemplate.body_html,
|
|
|
|
|
|
company_name: companyPlaceholder,
|
|
|
|
|
|
employee_name: employeePlaceholder,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ withCredentials: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
alert('Test maili gönderildi!');
|
|
|
|
|
|
setTestMailDialogOpen(false);
|
|
|
|
|
|
setTestMailAddress('');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
const message = error.response?.data?.error || 'Test maili gönderilemedi';
|
|
|
|
|
|
alert(message);
|
|
|
|
|
|
console.error('Test mail failed:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-10 17:27:19 +03:00
|
|
|
|
const handlePreview = async (htmlOverride, options = { openModal: false }) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setPreviewLoading(true);
|
|
|
|
|
|
const htmlContent = htmlOverride ?? form.body_html;
|
|
|
|
|
|
if (!htmlContent) {
|
|
|
|
|
|
alert('Önizleme için HTML içeriği gerekli');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const response = await templateService.preview({
|
|
|
|
|
|
template_html: htmlContent,
|
|
|
|
|
|
company_name: companyPlaceholder,
|
|
|
|
|
|
employee_name: employeePlaceholder,
|
|
|
|
|
|
});
|
|
|
|
|
|
const renderedHtml = response.data.data.rendered_html;
|
|
|
|
|
|
setPreviewHtml(renderedHtml);
|
|
|
|
|
|
if (previewUrl) {
|
|
|
|
|
|
URL.revokeObjectURL(previewUrl);
|
|
|
|
|
|
}
|
|
|
|
|
|
const blobUrl = URL.createObjectURL(new Blob([renderedHtml], { type: 'text/html' }));
|
|
|
|
|
|
setPreviewUrl(blobUrl);
|
|
|
|
|
|
setActiveTab('preview');
|
|
|
|
|
|
if (options.openModal) {
|
|
|
|
|
|
setPreviewOpen(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
const message = error.response?.data?.error || 'Önizleme oluşturulamadı';
|
|
|
|
|
|
alert(message);
|
|
|
|
|
|
console.error('Failed to preview template:', error);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setPreviewLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const copyTemplateType = (value) => {
|
|
|
|
|
|
navigator.clipboard.writeText(value);
|
|
|
|
|
|
alert(`Template Type panoya kopyalandı: ${value}`);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
if (previewUrl) {
|
|
|
|
|
|
URL.revokeObjectURL(previewUrl);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}, [previewUrl]);
|
|
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
|
|
|
|
|
<CircularProgress />
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Box>
|
|
|
|
|
|
<Box display="flex" justifyContent="space-between" alignItems="center" mb={3}>
|
|
|
|
|
|
<Typography variant="h4">Mail Şablonları</Typography>
|
2025-11-10 21:13:58 +03:00
|
|
|
|
<Box display="flex" gap={2}>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outlined"
|
|
|
|
|
|
startIcon={<AutoAwesome />}
|
|
|
|
|
|
onClick={() => setAiDialogOpen(true)}
|
|
|
|
|
|
color="secondary"
|
|
|
|
|
|
>
|
|
|
|
|
|
AI ile Oluştur
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button variant="contained" startIcon={<Add />} onClick={handleOpenCreate}>
|
|
|
|
|
|
Yeni Şablon
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Box>
|
2025-11-10 17:27:19 +03:00
|
|
|
|
</Box>
|
|
|
|
|
|
|
|
|
|
|
|
<TableContainer component={Paper}>
|
|
|
|
|
|
<Table>
|
|
|
|
|
|
<TableHead>
|
|
|
|
|
|
<TableRow>
|
|
|
|
|
|
<TableCell>Şablon Adı</TableCell>
|
|
|
|
|
|
<TableCell>Template Type</TableCell>
|
|
|
|
|
|
<TableCell>Durum</TableCell>
|
|
|
|
|
|
<TableCell>Güncelleme</TableCell>
|
|
|
|
|
|
<TableCell align="right">İşlemler</TableCell>
|
|
|
|
|
|
</TableRow>
|
|
|
|
|
|
</TableHead>
|
|
|
|
|
|
<TableBody>
|
|
|
|
|
|
{templates.map((template) => (
|
|
|
|
|
|
<TableRow key={template.id} hover>
|
|
|
|
|
|
<TableCell>
|
|
|
|
|
|
<Typography fontWeight={600}>{template.name}</Typography>
|
|
|
|
|
|
{template.description && (
|
|
|
|
|
|
<Typography variant="body2" color="textSecondary">
|
|
|
|
|
|
{template.description}
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
<TableCell>
|
|
|
|
|
|
<Box display="flex" alignItems="center" gap={1}>
|
|
|
|
|
|
<Chip
|
|
|
|
|
|
label={template.template_type}
|
|
|
|
|
|
color="primary"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
variant="outlined"
|
|
|
|
|
|
startIcon={<ContentCopy fontSize="small" />}
|
|
|
|
|
|
onClick={() => copyTemplateType(template.template_type)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Kopyala
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
<TableCell>
|
|
|
|
|
|
<Chip
|
|
|
|
|
|
label={template.active ? 'Aktif' : 'Pasif'}
|
|
|
|
|
|
color={template.active ? 'success' : 'default'}
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
<TableCell>
|
|
|
|
|
|
{format(new Date(template.updated_at), 'dd/MM/yyyy HH:mm')}
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
<TableCell align="right">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
startIcon={<Preview />}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setSelectedTemplate(template);
|
|
|
|
|
|
setForm({
|
|
|
|
|
|
name: template.name,
|
|
|
|
|
|
template_type: template.template_type,
|
|
|
|
|
|
subject_template: template.subject_template || '',
|
|
|
|
|
|
body_html: template.body_html,
|
|
|
|
|
|
description: template.description || '',
|
|
|
|
|
|
active: Boolean(template.active),
|
|
|
|
|
|
});
|
|
|
|
|
|
setCompanyPlaceholder('Örnek Şirket');
|
|
|
|
|
|
setEmployeePlaceholder('Ahmet Yılmaz');
|
|
|
|
|
|
setActiveTab('preview');
|
|
|
|
|
|
handlePreview(template.body_html, { openModal: true });
|
|
|
|
|
|
}}
|
|
|
|
|
|
sx={{ mr: 1 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
Önizleme
|
|
|
|
|
|
</Button>
|
2025-11-10 21:13:58 +03:00
|
|
|
|
<Button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
color="info"
|
|
|
|
|
|
startIcon={<SendIcon />}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setSelectedTemplate(template);
|
|
|
|
|
|
setTestMailDialogOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
sx={{ mr: 1 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
Test Mail
|
|
|
|
|
|
</Button>
|
2025-11-10 17:27:19 +03:00
|
|
|
|
<Button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
startIcon={<Edit />}
|
|
|
|
|
|
onClick={() => handleOpenEdit(template)}
|
|
|
|
|
|
sx={{ mr: 1 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
Düzenle
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
color="error"
|
|
|
|
|
|
startIcon={<Delete />}
|
|
|
|
|
|
onClick={() => handleDelete(template)}
|
|
|
|
|
|
>
|
|
|
|
|
|
Sil
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</TableCell>
|
|
|
|
|
|
</TableRow>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</TableBody>
|
|
|
|
|
|
</Table>
|
|
|
|
|
|
</TableContainer>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
open={dialogOpen}
|
|
|
|
|
|
onClose={handleCloseDialog}
|
|
|
|
|
|
maxWidth="md"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
>
|
|
|
|
|
|
<DialogTitle>
|
|
|
|
|
|
{selectedTemplate ? 'Şablonu Düzenle' : 'Yeni Şablon Oluştur'}
|
|
|
|
|
|
</DialogTitle>
|
|
|
|
|
|
<DialogContent dividers>
|
|
|
|
|
|
<Tabs
|
|
|
|
|
|
value={activeTab}
|
|
|
|
|
|
onChange={(_, value) => setActiveTab(value)}
|
|
|
|
|
|
sx={{ mb: 2 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Tab label="Form" value="form" />
|
|
|
|
|
|
<Tab label="Önizleme" value="preview" />
|
|
|
|
|
|
</Tabs>
|
|
|
|
|
|
|
|
|
|
|
|
{activeTab === 'form' && (
|
|
|
|
|
|
<Box display="flex" flexDirection="column" gap={2}>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="Şablon Adı"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
required
|
|
|
|
|
|
value={form.name}
|
|
|
|
|
|
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="Template Type"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
required
|
|
|
|
|
|
helperText="Örn: bank, government, internal"
|
|
|
|
|
|
value={form.template_type}
|
|
|
|
|
|
onChange={(e) => setForm({ ...form, template_type: e.target.value.trim().toLowerCase() })}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="Mail Konusu"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
value={form.subject_template}
|
|
|
|
|
|
onChange={(e) => setForm({ ...form, subject_template: e.target.value })}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="Açıklama"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
multiline
|
|
|
|
|
|
minRows={2}
|
|
|
|
|
|
value={form.description}
|
|
|
|
|
|
onChange={(e) => setForm({ ...form, description: e.target.value })}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="HTML İçeriği"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
multiline
|
|
|
|
|
|
minRows={10}
|
|
|
|
|
|
value={form.body_html}
|
|
|
|
|
|
onChange={(e) => setForm({ ...form, body_html: e.target.value })}
|
|
|
|
|
|
helperText="Handlebars değişkenleri: {{company_name}}, {{employee_name}}, {{tracking_url}}"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<FormControlLabel
|
|
|
|
|
|
control={
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
checked={form.active}
|
|
|
|
|
|
onChange={(e) => setForm({ ...form, active: e.target.checked })}
|
|
|
|
|
|
/>
|
|
|
|
|
|
}
|
|
|
|
|
|
label="Aktif"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{activeTab === 'preview' && (
|
|
|
|
|
|
<Box display="flex" flexDirection="column" gap={2}>
|
|
|
|
|
|
<Box display="flex" gap={2}>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="Şirket Adı"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
value={companyPlaceholder}
|
|
|
|
|
|
onChange={(e) => setCompanyPlaceholder(e.target.value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
label="Çalışan Adı"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
value={employeePlaceholder}
|
|
|
|
|
|
onChange={(e) => setEmployeePlaceholder(e.target.value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outlined"
|
|
|
|
|
|
startIcon={<Preview />}
|
|
|
|
|
|
onClick={handlePreview}
|
|
|
|
|
|
disabled={previewLoading || !form.body_html}
|
|
|
|
|
|
>
|
|
|
|
|
|
{previewLoading ? 'Önizleme Oluşturuluyor...' : 'Önizleme Oluştur'}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Paper variant="outlined" sx={{ minHeight: 400 }}>
|
|
|
|
|
|
{previewHtml ? (
|
|
|
|
|
|
<iframe
|
|
|
|
|
|
title="template-preview"
|
|
|
|
|
|
src={previewUrl}
|
|
|
|
|
|
style={{ border: 'none', width: '100%', height: '600px' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Box
|
|
|
|
|
|
display="flex"
|
|
|
|
|
|
justifyContent="center"
|
|
|
|
|
|
alignItems="center"
|
|
|
|
|
|
minHeight="200px"
|
|
|
|
|
|
color="text.secondary"
|
|
|
|
|
|
>
|
|
|
|
|
|
Önizleme için HTML içeriği girin ve \"Önizleme Oluştur\" butonuna basın
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Paper>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
<DialogActions>
|
|
|
|
|
|
<Button onClick={handleCloseDialog}>İptal</Button>
|
|
|
|
|
|
<Button onClick={handleSave} variant="contained" disabled={!form.name || !form.template_type || !form.body_html}>
|
|
|
|
|
|
Kaydet
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogActions>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
open={previewOpen}
|
|
|
|
|
|
onClose={() => setPreviewOpen(false)}
|
|
|
|
|
|
maxWidth="lg"
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
>
|
|
|
|
|
|
<DialogTitle>Önizleme</DialogTitle>
|
|
|
|
|
|
<DialogContent dividers>
|
|
|
|
|
|
<iframe
|
|
|
|
|
|
title="template-preview-full"
|
|
|
|
|
|
src={previewUrl}
|
|
|
|
|
|
style={{ border: 'none', width: '100%', minHeight: '600px' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
<DialogActions>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setPreviewOpen(false);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
Kapat
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogActions>
|
|
|
|
|
|
</Dialog>
|
2025-11-10 21:13:58 +03:00
|
|
|
|
|
|
|
|
|
|
{/* AI Generation Dialog */}
|
|
|
|
|
|
<Dialog open={aiDialogOpen} onClose={() => setAiDialogOpen(false)} maxWidth="md" fullWidth>
|
|
|
|
|
|
<DialogTitle>🤖 AI ile Mail Şablonu Oluştur</DialogTitle>
|
|
|
|
|
|
<DialogContent>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
|
|
|
|
Ollama AI kullanarak otomatik mail şablonu oluşturun. Aşağıdaki bilgileri girin:
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Şablon Adı"
|
|
|
|
|
|
required
|
|
|
|
|
|
value={aiForm.template_name}
|
|
|
|
|
|
onChange={(e) => setAiForm({ ...aiForm, template_name: e.target.value })}
|
|
|
|
|
|
helperText="Şablona verilecek isim"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Template Type"
|
|
|
|
|
|
required
|
|
|
|
|
|
value={aiForm.template_type}
|
|
|
|
|
|
onChange={(e) => setAiForm({ ...aiForm, template_type: e.target.value })}
|
|
|
|
|
|
helperText="Örn: bank, hr, it_support, management"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Şirket Adı"
|
|
|
|
|
|
required
|
|
|
|
|
|
value={aiForm.company_name}
|
|
|
|
|
|
onChange={(e) => setAiForm({ ...aiForm, company_name: e.target.value })}
|
|
|
|
|
|
helperText="Hedef şirket adı (örn: Acme Corporation)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Senaryo"
|
|
|
|
|
|
required
|
|
|
|
|
|
multiline
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
value={aiForm.scenario}
|
|
|
|
|
|
onChange={(e) => setAiForm({ ...aiForm, scenario: e.target.value })}
|
|
|
|
|
|
helperText="Mail senaryosu (örn: 'Şifre sıfırlama maili', 'Yeni güvenlik politikası', 'Ödül programı duyurusu')"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Çalışan Bilgisi (Opsiyonel)"
|
|
|
|
|
|
value={aiForm.employee_info}
|
|
|
|
|
|
onChange={(e) => setAiForm({ ...aiForm, employee_info: e.target.value })}
|
|
|
|
|
|
helperText="Hedef çalışan hakkında bilgi (örn: 'İK departmanı çalışanı', 'Yönetici')"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Ek Talimatlar (Opsiyonel)"
|
|
|
|
|
|
multiline
|
|
|
|
|
|
rows={2}
|
|
|
|
|
|
value={aiForm.custom_prompt}
|
|
|
|
|
|
onChange={(e) => setAiForm({ ...aiForm, custom_prompt: e.target.value })}
|
|
|
|
|
|
helperText="AI'ya özel talimatlar (örn: 'Resmi dil kullan', 'Aciliyet vurgusu yap')"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
<DialogActions>
|
|
|
|
|
|
<Button onClick={() => setAiDialogOpen(false)} disabled={aiGenerating}>
|
|
|
|
|
|
İptal
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleGenerateWithAI}
|
|
|
|
|
|
variant="contained"
|
|
|
|
|
|
disabled={aiGenerating}
|
|
|
|
|
|
startIcon={aiGenerating ? <CircularProgress size={20} /> : <AutoAwesome />}
|
|
|
|
|
|
>
|
|
|
|
|
|
{aiGenerating ? 'Oluşturuluyor...' : 'Oluştur'}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogActions>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Test Mail Dialog */}
|
|
|
|
|
|
<Dialog open={testMailDialogOpen} onClose={() => setTestMailDialogOpen(false)} maxWidth="sm" fullWidth>
|
|
|
|
|
|
<DialogTitle>📧 Test Maili Gönder</DialogTitle>
|
|
|
|
|
|
<DialogContent>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
|
|
|
|
Seçili şablon için test maili gönderin.
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
{selectedTemplate && (
|
|
|
|
|
|
<Box my={2} p={2} bgcolor="grey.100" borderRadius={1}>
|
|
|
|
|
|
<Typography variant="body2" fontWeight="bold">
|
|
|
|
|
|
Şablon: {selectedTemplate.name}
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary">
|
|
|
|
|
|
Tip: {selectedTemplate.template_type}
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Test Mail Adresi"
|
|
|
|
|
|
type="email"
|
|
|
|
|
|
required
|
|
|
|
|
|
value={testMailAddress}
|
|
|
|
|
|
onChange={(e) => setTestMailAddress(e.target.value)}
|
|
|
|
|
|
helperText="Test mailinin gönderileceği adres"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Şirket Adı (Placeholder)"
|
|
|
|
|
|
value={companyPlaceholder}
|
|
|
|
|
|
onChange={(e) => setCompanyPlaceholder(e.target.value)}
|
|
|
|
|
|
helperText="{{company_name}} yerine kullanılacak"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Çalışan Adı (Placeholder)"
|
|
|
|
|
|
value={employeePlaceholder}
|
|
|
|
|
|
onChange={(e) => setEmployeePlaceholder(e.target.value)}
|
|
|
|
|
|
helperText="{{employee_name}} yerine kullanılacak"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
<DialogActions>
|
|
|
|
|
|
<Button onClick={() => setTestMailDialogOpen(false)}>
|
|
|
|
|
|
İptal
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleSendTestMail}
|
|
|
|
|
|
variant="contained"
|
|
|
|
|
|
startIcon={<SendIcon />}
|
|
|
|
|
|
>
|
|
|
|
|
|
Gönder
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogActions>
|
|
|
|
|
|
</Dialog>
|
2025-11-10 17:27:19 +03:00
|
|
|
|
</Box>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default Templates;
|