Mail schemes updated auto system
This commit is contained in:
@@ -2,6 +2,25 @@ const ollamaService = require('../services/ollama.service');
|
||||
const { Settings, MailTemplate } = require('../models');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Get available template themes
|
||||
*/
|
||||
exports.getThemes = async (req, res, next) => {
|
||||
try {
|
||||
const themes = ollamaService.getAvailableThemes();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
themes,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to get themes:', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test Ollama connection
|
||||
*/
|
||||
@@ -96,7 +115,7 @@ exports.updateSettings = async (req, res, next) => {
|
||||
*/
|
||||
exports.generateTemplate = async (req, res, next) => {
|
||||
try {
|
||||
const { company_name, scenario, employee_info, custom_prompt, template_name, template_type } = req.body;
|
||||
const { company_name, scenario, employee_info, custom_prompt, template_name, template_type, generate_html, template_theme } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!company_name || !scenario) {
|
||||
@@ -106,7 +125,7 @@ exports.generateTemplate = async (req, res, next) => {
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`AI template generation requested for: ${company_name} - ${scenario}`);
|
||||
logger.info(`AI template generation requested for: ${company_name} - ${scenario} (HTML: ${generate_html ? 'yes' : 'no'}, Theme: ${template_theme || 'red'})`);
|
||||
|
||||
// Generate template using Ollama
|
||||
const templateData = await ollamaService.generateMailTemplate({
|
||||
@@ -114,6 +133,8 @@ exports.generateTemplate = async (req, res, next) => {
|
||||
scenario,
|
||||
employee_info,
|
||||
custom_prompt,
|
||||
generate_html: generate_html || false,
|
||||
template_theme: template_theme || 'red',
|
||||
});
|
||||
|
||||
// Save to database if template_name is provided
|
||||
|
||||
@@ -6,6 +6,9 @@ const { requireAuth } = require('../middlewares/auth');
|
||||
// All routes require authentication
|
||||
router.use(requireAuth);
|
||||
|
||||
// Get available template themes
|
||||
router.get('/themes', ollamaController.getThemes);
|
||||
|
||||
// Test Ollama connection
|
||||
router.get('/test', ollamaController.testConnection);
|
||||
|
||||
|
||||
@@ -9,32 +9,29 @@ class OllamaService {
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Ollama service with settings from database
|
||||
*/
|
||||
async initialize() {
|
||||
if (this.initialized && this.serverUrl && this.model) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const serverUrlSetting = await Settings.findOne({
|
||||
where: { key: 'ollama_server_url' },
|
||||
});
|
||||
const modelSetting = await Settings.findOne({
|
||||
where: { key: 'ollama_model' },
|
||||
const settings = await Settings.findAll();
|
||||
const settingsMap = {};
|
||||
settings.forEach(s => {
|
||||
settingsMap[s.key] = s.value;
|
||||
});
|
||||
|
||||
this.serverUrl = serverUrlSetting?.value || process.env.OLLAMA_URL || 'http://localhost:11434';
|
||||
this.model = modelSetting?.value || process.env.OLLAMA_MODEL || 'llama3.2';
|
||||
|
||||
this.serverUrl = settingsMap['ollama_server_url'] || 'http://localhost:11434';
|
||||
this.model = settingsMap['ollama_model'] || 'llama3.2:latest';
|
||||
this.initialized = true;
|
||||
logger.info(`Ollama service initialized: ${this.serverUrl} with model ${this.model}`);
|
||||
|
||||
logger.info(`Ollama initialized: ${this.serverUrl}, model: ${this.model}`);
|
||||
} catch (error) {
|
||||
logger.error('Failed to initialize Ollama service:', error);
|
||||
throw error;
|
||||
logger.error('Failed to initialize Ollama:', error.message);
|
||||
throw new Error('Ollama ayarları yüklenemedi');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Ollama connection
|
||||
*/
|
||||
async testConnection() {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
@@ -45,24 +42,22 @@ class OllamaService {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
const models = response.data.models || [];
|
||||
|
||||
return {
|
||||
success: true,
|
||||
models: response.data.models || [],
|
||||
message: 'Ollama bağlantısı başarılı',
|
||||
models: models,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Ollama connection test failed:', error.message);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
message: 'Ollama sunucusuna bağlanılamadı',
|
||||
message: 'Ollama bağlantısı başarısız: ' + error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List available models
|
||||
*/
|
||||
async listModels() {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
@@ -80,23 +75,19 @@ class OllamaService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate mail template using Ollama
|
||||
* @param {Object} params - Template generation parameters
|
||||
* @param {string} params.company_name - Target company name
|
||||
* @param {string} params.scenario - Phishing scenario type
|
||||
* @param {string} params.employee_info - Employee information (optional)
|
||||
* @param {string} params.custom_prompt - Custom instructions (optional)
|
||||
*/
|
||||
async generateMailTemplate(params) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
const { company_name, scenario, employee_info, custom_prompt } = params;
|
||||
const { company_name, scenario, employee_info, custom_prompt, generate_html = false } = params;
|
||||
|
||||
// Build the prompt
|
||||
const systemPrompt = `Sen profesyonel bir siber güvenlik uzmanısın ve şirketler için phishing farkındalık testi mail şablonları oluşturuyorsun.
|
||||
// Build the prompt based on mode
|
||||
let systemPrompt, userPrompt;
|
||||
|
||||
if (generate_html) {
|
||||
// Full HTML generation mode
|
||||
systemPrompt = `Sen profesyonel bir siber güvenlik uzmanısın ve şirketler için phishing farkındalık testi mail şablonları oluşturuyorsun.
|
||||
|
||||
GÖREV: Gerçekçi, ikna edici ve profesyonel phishing test mailleri oluştur.
|
||||
|
||||
@@ -106,42 +97,75 @@ KURALLAR:
|
||||
3. İkna edici ve inandırıcı olmalı
|
||||
4. Türkçe dil bilgisi ve imla kurallarına uygun olmalı
|
||||
5. Kullanıcıyı aciliyet hissi ile harekete geçirmeli
|
||||
6. Şirket logosu/branding için placeholder kullan
|
||||
|
||||
ZORUNLU PLACEHOLDER'LAR:
|
||||
- {{company_name}} - Şirket adı
|
||||
- {{employee_name}} - Çalışan adı (varsa "Sayın {{employee_name}}", yoksa "Sayın Yetkili")
|
||||
- {{tracking_url}} - Tıklama linki (button veya link olarak)
|
||||
- {{employee_name}} - Çalışan adı
|
||||
- {{tracking_url}} - Tıklama linki
|
||||
|
||||
YANIT FORMATI: Sadece ve sadece JSON, hiçbir ek açıklama yok!`;
|
||||
YANIT FORMATI: Sadece JSON!`;
|
||||
|
||||
let userPrompt = `Aşağıdaki bilgilere göre profesyonel bir phishing test mail şablonu oluştur:
|
||||
userPrompt = `Aşağıdaki bilgilere göre profesyonel bir phishing test mail şablonu oluştur:
|
||||
|
||||
📌 HEDEF ŞİRKET: ${company_name}
|
||||
📌 SENARYO: ${scenario}`;
|
||||
|
||||
if (employee_info) {
|
||||
if (employee_info) userPrompt += `\n📌 HEDEF KİTLE: ${employee_info}`;
|
||||
if (custom_prompt) userPrompt += `\n📌 ÖZEL TALİMATLAR: ${custom_prompt}`;
|
||||
|
||||
userPrompt += `
|
||||
📌 HEDEF KİTLE: ${employee_info}`;
|
||||
}
|
||||
|
||||
if (custom_prompt) {
|
||||
userPrompt += `
|
||||
📌 ÖZEL TALİMATLAR: ${custom_prompt}`;
|
||||
}
|
||||
|
||||
userPrompt += `
|
||||
|
||||
JSON YANIT FORMATI (AYNEN BU YAPIDA):
|
||||
JSON YANIT FORMATI:
|
||||
{
|
||||
"subject": "İkna edici mail konusu buraya (max 70 karakter)",
|
||||
"body": "<!DOCTYPE html><html><head><meta charset='UTF-8'><style>body{font-family:Arial,sans-serif;line-height:1.6;color:#333;max-width:600px;margin:0 auto;padding:20px;}h2{color:#d32f2f;}.button{display:inline-block;padding:12px 30px;background:#d32f2f;color:white;text-decoration:none;border-radius:5px;margin:20px 0;}.footer{font-size:12px;color:#666;margin-top:30px;border-top:1px solid #ddd;padding-top:15px;}</style></head><body><h2>Başlık</h2><p>Sayın {{employee_name}},</p><p>Mail içeriği buraya - ikna edici ve aciliyet vurgulu</p><a href='{{tracking_url}}' class='button'>Butona tıklat</a><div class='footer'>{{company_name}} © 2024</div></body></html>"
|
||||
"subject": "İkna edici mail konusu (max 70 karakter)",
|
||||
"body": "<!DOCTYPE html><html><head><meta charset='UTF-8'><style>body{font-family:Arial,sans-serif;line-height:1.6;color:#333;max-width:600px;margin:0 auto;padding:20px;}h2{color:#d32f2f;}.button{display:inline-block;padding:12px 30px;background:#d32f2f;color:white;text-decoration:none;border-radius:5px;margin:20px 0;}</style></head><body><h2>Başlık</h2><p>Sayın {{employee_name}},</p><p>İçerik</p><a href='{{tracking_url}}' class='button'>Tıklayın</a></body></html>"
|
||||
}
|
||||
|
||||
⚠️ SADECE JSON DÖNDÜR! Açıklama, not, yorum YAZMA!
|
||||
⚠️ Body içinde CSS stillendir, responsive yap!
|
||||
⚠️ Konuyu çekici ve acil yap!
|
||||
⚠️ HTML'i tam ve geçerli oluştur!`;
|
||||
⚠️ SADECE JSON DÖNDÜR!`;
|
||||
|
||||
} else {
|
||||
// Text-only generation mode (RECOMMENDED)
|
||||
systemPrompt = `Sen profesyonel bir siber güvenlik uzmanısın ve phishing test mail içerikleri yazıyorsun.
|
||||
|
||||
GÖREV: İkna edici, gerçekçi ve profesyonel mail içerikleri yaz.
|
||||
|
||||
KURALLAR:
|
||||
1. Gerçek şirket maillerine benzer dil ve ton kullan
|
||||
2. İkna edici ve inandırıcı ol
|
||||
3. Türkçe dil bilgisi ve imla kurallarına uygun yaz
|
||||
4. Kullanıcıyı aciliyet hissi ile harekete geçir
|
||||
5. SADECE METİN İÇERİĞİ yaz, HTML TAG'LERİ KULLANMA!
|
||||
|
||||
ZORUNLU PLACEHOLDER'LAR:
|
||||
- {{company_name}} - Şirket adı
|
||||
- {{employee_name}} - Çalışan adı
|
||||
- {{tracking_url}} - Link metni (örn: "buraya tıklayın", "hesabınızı doğrulayın")
|
||||
|
||||
YANIT FORMATI: Sadece JSON!`;
|
||||
|
||||
userPrompt = `Aşağıdaki bilgilere göre profesyonel bir phishing test mail içeriği yaz:
|
||||
|
||||
📌 HEDEF ŞİRKET: ${company_name}
|
||||
📌 SENARYO: ${scenario}`;
|
||||
|
||||
if (employee_info) userPrompt += `\n📌 HEDEF KİTLE: ${employee_info}`;
|
||||
if (custom_prompt) userPrompt += `\n📌 ÖZEL TALİMATLAR: ${custom_prompt}`;
|
||||
|
||||
userPrompt += `
|
||||
|
||||
JSON YANIT FORMATI:
|
||||
{
|
||||
"subject": "İkna edici mail konusu (max 70 karakter)",
|
||||
"body": "Mail içeriği buraya. Sayın {{employee_name}}, [mesajın içeriği]. Link metni için {{tracking_url}} placeholder'ını kullan. İmza kısmı."
|
||||
}
|
||||
|
||||
⚠️ ÖNEMLİ:
|
||||
- Body içinde HTML TAG kullanma (<p>, <div> gibi)!
|
||||
- Sadece düz metin yaz!
|
||||
- Satır atlamak için sadece yeni satır kullan!
|
||||
- {{tracking_url}} placeholder'ını link metni içinde kullan (örn: "{{tracking_url}} buraya tıklayın")
|
||||
- SADECE JSON DÖNDÜR!`;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Generating template for company: ${company_name}, scenario: ${scenario}`);
|
||||
@@ -201,11 +225,20 @@ JSON YANIT FORMATI (AYNEN BU YAPIDA):
|
||||
templateData.subject = subject;
|
||||
templateData.body = body;
|
||||
|
||||
// If generate_html is false, wrap the content in our template
|
||||
let finalBody = body;
|
||||
if (!generate_html) {
|
||||
const theme = params.template_theme || 'red';
|
||||
finalBody = this.wrapInTemplate(body, subject, theme);
|
||||
}
|
||||
|
||||
return {
|
||||
subject_template: templateData.subject,
|
||||
body_template: templateData.body,
|
||||
body_template: finalBody,
|
||||
generated_by: 'ollama',
|
||||
model: this.model,
|
||||
html_generated: generate_html,
|
||||
theme: params.template_theme || 'red',
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Failed to generate template with Ollama:', error.message);
|
||||
@@ -213,9 +246,180 @@ JSON YANIT FORMATI (AYNEN BU YAPIDA):
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a simple text completion
|
||||
*/
|
||||
getTemplateStyles(theme = 'red') {
|
||||
const themes = {
|
||||
red: {
|
||||
name: 'Kırmızı (Acil/Uyarı)',
|
||||
gradient: 'linear-gradient(135deg, #e53935 0%, #c62828 100%)',
|
||||
button: '#e53935',
|
||||
buttonHover: '#c62828',
|
||||
accent: '#e53935',
|
||||
},
|
||||
green: {
|
||||
name: 'Yeşil (Başarı/Onay)',
|
||||
gradient: 'linear-gradient(135deg, #43a047 0%, #2e7d32 100%)',
|
||||
button: '#43a047',
|
||||
buttonHover: '#2e7d32',
|
||||
accent: '#43a047',
|
||||
},
|
||||
blue: {
|
||||
name: 'Mavi (Kurumsal)',
|
||||
gradient: 'linear-gradient(135deg, #1e88e5 0%, #1565c0 100%)',
|
||||
button: '#1e88e5',
|
||||
buttonHover: '#1565c0',
|
||||
accent: '#1e88e5',
|
||||
},
|
||||
purple: {
|
||||
name: 'Mor (Premium)',
|
||||
gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
button: '#667eea',
|
||||
buttonHover: '#5568d3',
|
||||
accent: '#667eea',
|
||||
},
|
||||
orange: {
|
||||
name: 'Turuncu (Dikkat)',
|
||||
gradient: 'linear-gradient(135deg, #fb8c00 0%, #ef6c00 100%)',
|
||||
button: '#fb8c00',
|
||||
buttonHover: '#ef6c00',
|
||||
accent: '#fb8c00',
|
||||
},
|
||||
dark: {
|
||||
name: 'Siyah (Ciddi)',
|
||||
gradient: 'linear-gradient(135deg, #37474f 0%, #263238 100%)',
|
||||
button: '#37474f',
|
||||
buttonHover: '#263238',
|
||||
accent: '#37474f',
|
||||
},
|
||||
};
|
||||
|
||||
return themes[theme] || themes.red;
|
||||
}
|
||||
|
||||
wrapInTemplate(textContent, subject, theme = 'red') {
|
||||
const style = this.getTemplateStyles(theme);
|
||||
|
||||
// Convert text to HTML paragraphs
|
||||
const paragraphs = textContent
|
||||
.split('\n\n')
|
||||
.filter(p => p.trim())
|
||||
.map(p => {
|
||||
// Replace {{tracking_url}} placeholder with styled link
|
||||
if (p.includes('{{tracking_url}}')) {
|
||||
return `<p>${p.replace(/\{\{tracking_url\}\}/g, '<a href="{{tracking_url}}" class="button">Buraya Tıklayın</a>')}</p>`;
|
||||
}
|
||||
return `<p>${p}</p>`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="tr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${subject}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f5f5f5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 20px auto;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.header {
|
||||
background: ${style.gradient};
|
||||
color: white;
|
||||
padding: 30px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.content {
|
||||
padding: 30px;
|
||||
}
|
||||
.content p {
|
||||
margin: 0 0 15px 0;
|
||||
color: #555;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 14px 32px;
|
||||
background: ${style.button};
|
||||
color: white !important;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
font-weight: 600;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
.button:hover {
|
||||
background: ${style.buttonHover};
|
||||
}
|
||||
.footer {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
.warning {
|
||||
background: #fff3cd;
|
||||
border-left: 4px solid ${style.accent};
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.container {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>{{company_name}}</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
${paragraphs}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© 2024 {{company_name}}. Tüm hakları saklıdır.</p>
|
||||
<p style="margin-top: 10px; font-size: 11px;">Bu mail {{company_name}} tarafından gönderilmiştir.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
getAvailableThemes() {
|
||||
return [
|
||||
{ value: 'red', label: '🔴 Kırmızı (Acil/Uyarı)', description: 'Acil durumlar, güvenlik uyarıları' },
|
||||
{ value: 'green', label: '🟢 Yeşil (Başarı/Onay)', description: 'Onay mailleri, başarı bildirimleri' },
|
||||
{ value: 'blue', label: '🔵 Mavi (Kurumsal)', description: 'Resmi kurumsal mailler' },
|
||||
{ value: 'purple', label: '🟣 Mor (Premium)', description: 'Özel teklifler, premium içerik' },
|
||||
{ value: 'orange', label: '🟠 Turuncu (Dikkat)', description: 'Bildirimler, hatırlatmalar' },
|
||||
{ value: 'dark', label: '⚫ Siyah (Ciddi)', description: 'Ciddi/resmi konular' },
|
||||
];
|
||||
}
|
||||
|
||||
async generate(prompt) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
@@ -243,4 +447,3 @@ JSON YANIT FORMATI (AYNEN BU YAPIDA):
|
||||
}
|
||||
|
||||
module.exports = new OllamaService();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
FormControlLabel,
|
||||
Tabs,
|
||||
Tab,
|
||||
MenuItem,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Add,
|
||||
@@ -68,14 +69,38 @@ function Templates() {
|
||||
custom_prompt: '',
|
||||
template_name: '',
|
||||
template_type: '',
|
||||
generate_html: false, // Default: use template, generate text only
|
||||
template_theme: 'red', // Default theme
|
||||
});
|
||||
const [testMailDialogOpen, setTestMailDialogOpen] = useState(false);
|
||||
const [testMailAddress, setTestMailAddress] = useState('');
|
||||
const [availableThemes, setAvailableThemes] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
loadTemplates();
|
||||
loadThemes();
|
||||
}, []);
|
||||
|
||||
const loadThemes = async () => {
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/api/ollama/themes`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
setAvailableThemes(response.data.data.themes);
|
||||
} catch (error) {
|
||||
console.error('Failed to load themes:', error);
|
||||
// Set default themes if API fails
|
||||
setAvailableThemes([
|
||||
{ value: 'red', label: '🔴 Kırmızı (Acil/Uyarı)', description: 'Acil durumlar, güvenlik uyarıları' },
|
||||
{ value: 'green', label: '🟢 Yeşil (Başarı/Onay)', description: 'Onay mailleri, başarı bildirimleri' },
|
||||
{ value: 'blue', label: '🔵 Mavi (Kurumsal)', description: 'Resmi kurumsal mailler' },
|
||||
{ value: 'purple', label: '🟣 Mor (Premium)', description: 'Özel teklifler, premium içerik' },
|
||||
{ value: 'orange', label: '🟠 Turuncu (Dikkat)', description: 'Bildirimler, hatırlatmalar' },
|
||||
{ value: 'dark', label: '⚫ Siyah (Ciddi)', description: 'Ciddi/resmi konular' },
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const loadTemplates = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -178,6 +203,8 @@ function Templates() {
|
||||
custom_prompt: '',
|
||||
template_name: '',
|
||||
template_type: '',
|
||||
generate_html: false,
|
||||
template_theme: 'red',
|
||||
});
|
||||
loadTemplates();
|
||||
} catch (error) {
|
||||
@@ -617,6 +644,47 @@ function Templates() {
|
||||
onChange={(e) => setAiForm({ ...aiForm, custom_prompt: e.target.value })}
|
||||
helperText="AI'ya özel talimatlar (örn: 'Resmi dil kullan', 'Aciliyet vurgusu yap')"
|
||||
/>
|
||||
|
||||
{!aiForm.generate_html && (
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
select
|
||||
label="HTML Şablon Teması"
|
||||
value={aiForm.template_theme}
|
||||
onChange={(e) => setAiForm({ ...aiForm, template_theme: e.target.value })}
|
||||
helperText="Mailin görsel teması (sadece Text modu için)"
|
||||
>
|
||||
{availableThemes.map((theme) => (
|
||||
<MenuItem key={theme.value} value={theme.value}>
|
||||
<Box>
|
||||
<Typography variant="body1">{theme.label}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{theme.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={aiForm.generate_html}
|
||||
onChange={(e) => setAiForm({ ...aiForm, generate_html: e.target.checked })}
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="AI ile Full HTML Oluştur (İleri Seviye)"
|
||||
sx={{ mt: 2 }}
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary" display="block" sx={{ ml: 4, mt: -1 }}>
|
||||
{aiForm.generate_html
|
||||
? '⚠️ AI hem içeriği hem HTML yapısını oluşturacak (bazen hatalı olabilir)'
|
||||
: '✅ Önerilen: AI sadece içerik oluşturur, HTML şablonu hazır kullanılır'
|
||||
}
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setAiDialogOpen(false)} disabled={aiGenerating}>
|
||||
|
||||
Reference in New Issue
Block a user