hesabixSite/templates/qa/answer_question.html.twig

459 lines
26 KiB
Twig
Raw Permalink Normal View History

2025-09-05 09:37:27 +03:30
{% extends 'base.html.twig' %}
{% block title %}پاسخ به سوال: {{ question.title }} - پرسش و پاسخ{% endblock %}
{% block body %}
2025-09-05 18:03:55 +03:30
<main class="min-h-screen bg-gray-50">
<div class="container mx-auto px-4 py-8">
<div class="max-w-4xl mx-auto">
2025-09-05 09:37:27 +03:30
<!-- نمایش سوال -->
2025-09-05 18:03:55 +03:30
<div class="bg-white rounded-2xl shadow-soft mb-8 overflow-hidden">
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 px-8 py-6 border-b border-gray-200">
<h2 class="text-2xl font-bold text-gray-900 flex items-center">
<svg class="w-6 h-6 text-blue-600 ml-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
سوال:
</h2>
2025-09-05 09:37:27 +03:30
</div>
2025-09-05 18:03:55 +03:30
<div class="p-8">
<h3 class="text-2xl font-bold text-gray-900 mb-4">{{ question.title }}</h3>
<div class="prose prose-lg max-w-none text-gray-800 leading-relaxed mb-6">
{{ question.content|markdown|raw }}
2025-09-05 09:37:27 +03:30
</div>
2025-09-05 18:03:55 +03:30
<div class="flex flex-wrap gap-2">
{% for tagRelation in question.tagRelations %}
<a href="{{ path('qa_tag_questions', {'name': tagRelation.tag.name}) }}"
class="inline-flex items-center px-3 py-1 bg-blue-100 text-blue-800 text-sm font-medium rounded-full hover:bg-blue-200 transition-colors duration-200">
{{ tagRelation.tag.name }}
</a>
{% endfor %}
2025-09-05 09:37:27 +03:30
</div>
</div>
</div>
<!-- فرم پاسخ -->
2025-09-05 18:03:55 +03:30
<div class="bg-white rounded-2xl shadow-soft overflow-hidden">
<div class="bg-gradient-to-r from-green-50 to-emerald-50 px-8 py-6 border-b border-gray-200">
<h2 class="text-2xl font-bold text-gray-900 flex items-center">
<svg class="w-6 h-6 text-green-600 ml-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2025-09-05 09:37:27 +03:30
<polyline points="9,17 4,12 9,7"></polyline>
<path d="M20 18v-2a4 4 0 0 0-4-4H4"></path>
2025-09-05 18:03:55 +03:30
</svg>
پاسخ شما
</h2>
2025-09-05 09:37:27 +03:30
</div>
2025-09-05 18:03:55 +03:30
<div class="p-8">
{{ form_start(form, {'attr': {'novalidate': 'novalidate', 'class': 'space-y-6'}}) }}
2025-09-05 09:37:27 +03:30
2025-09-05 18:03:55 +03:30
<div>
{{ form_label(form.content, null, {'label_attr': {'class': 'block text-sm font-medium text-gray-700 mb-2'}}) }}
<div class="qa-editor-toolbar border border-gray-300 border-b-0 rounded-t-xl p-3 bg-gray-50">
<div class="flex flex-wrap gap-2">
<button type="button" class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200" onclick="formatText('bold')" title="پررنگ">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>
</svg>
</button>
<button type="button" class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200" onclick="formatText('italic')" title="کج">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<line x1="19" y1="4" x2="10" y2="4"></line>
<line x1="14" y1="20" x2="5" y2="20"></line>
<line x1="15" y1="4" x2="9" y2="20"></line>
</svg>
</button>
<button type="button" class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200" onclick="formatText('code')" title="کد">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<polyline points="16,18 22,12 16,6"></polyline>
<polyline points="8,6 2,12 8,18"></polyline>
</svg>
</button>
<button type="button" class="px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200" onclick="insertList()" title="لیست">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<line x1="8" y1="6" x2="21" y2="6"></line>
<line x1="8" y1="12" x2="21" y2="12"></line>
<line x1="8" y1="18" x2="21" y2="18"></line>
<line x1="3" y1="6" x2="3.01" y2="6"></line>
<line x1="3" y1="12" x2="3.01" y2="12"></line>
<line x1="3" y1="18" x2="3.01" y2="18"></line>
</svg>
</button>
</div>
2025-09-05 09:37:27 +03:30
</div>
2025-09-05 18:03:55 +03:30
{{ form_widget(form.content, {'attr': {'class': 'w-full px-4 py-3 border border-gray-300 border-t-0 rounded-b-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200', 'rows': 8}}) }}
2025-09-05 09:37:27 +03:30
{{ form_errors(form.content) }}
2025-09-05 18:03:55 +03:30
<div class="mt-2 text-sm text-gray-600">
2025-09-05 09:37:27 +03:30
پاسخ خود را به صورت کامل و مفصل ارائه دهید. هرچه پاسخ شما دقیق‌تر باشد، برای دیگران مفیدتر خواهد بود.
2025-09-05 18:03:55 +03:30
<br><span class="text-gray-500">می‌توانید از دکمه‌های بالا برای فرمت کردن متن استفاده کنید.</span>
2025-09-05 09:37:27 +03:30
</div>
</div>
2025-09-05 18:03:55 +03:30
<!-- پیوست فایل -->
<div>
<label for="{{ form.attachments.vars.id }}" class="block text-sm font-medium text-gray-700 mb-2">
پیوست فایل
<span class="text-gray-500">(اختیاری)</span>
</label>
<div class="border-2 border-dashed border-gray-300 rounded-xl p-6 bg-gray-50 hover:bg-gray-100 transition-colors duration-200">
{{ form_widget(form.attachments, {'attr': {'class': 'w-full', 'id': 'file-input'}}) }}
{{ form_errors(form.attachments) }}
<div class="mt-4 text-center">
<div class="flex items-center justify-center space-x-2 space-x-reverse text-gray-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
</svg>
<span class="text-sm">فایل‌های خود را اینجا بکشید یا کلیک کنید</span>
</div>
<p class="text-xs text-gray-400 mt-2">
فرمت‌های مجاز: JPG, PNG, GIF, PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT
<br>حداکثر 3 فایل، هر کدام 4 مگابایت
</p>
</div>
<!-- نمایش فایل‌های انتخاب شده -->
<div id="selected-files" class="mt-4 hidden">
<h4 class="text-sm font-medium text-gray-700 mb-2">فایل‌های انتخاب شده:</h4>
<div id="file-list" class="space-y-2"></div>
</div>
</div>
</div>
<div class="flex justify-between items-center pt-6">
<a href="{{ path('qa_question_show', {'id': question.id}) }}"
class="inline-flex items-center px-6 py-3 bg-gray-100 text-gray-700 rounded-xl hover:bg-gray-200 transition-all duration-200 font-medium">
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
انصراف
2025-09-05 09:37:27 +03:30
</a>
2025-09-05 18:03:55 +03:30
<button type="submit"
class="inline-flex items-center px-8 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all duration-200 font-medium shadow-lg hover:shadow-xl transform hover:-translate-y-0.5">
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
</svg>
ارسال پاسخ
2025-09-05 09:37:27 +03:30
</button>
</div>
{{ form_end(form) }}
</div>
</div>
<!-- راهنمای پاسخ دادن -->
2025-09-05 18:03:55 +03:30
<div class="bg-white rounded-2xl shadow-soft mt-8 overflow-hidden">
<div class="bg-gradient-to-r from-yellow-50 to-orange-50 px-8 py-6 border-b border-gray-200">
<h3 class="text-xl font-bold text-gray-900 flex items-center">
<svg class="w-6 h-6 text-yellow-600 ml-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>
راهنمای پاسخ دادن
</h3>
2025-09-05 09:37:27 +03:30
</div>
2025-09-05 18:03:55 +03:30
<div class="p-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div>
<h4 class="text-lg font-semibold text-green-700 mb-4 flex items-center">
<svg class="w-5 h-5 text-green-600 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
پاسخ خوب:
</h4>
<ul class="space-y-3">
<li class="flex items-start">
<svg class="w-5 h-5 text-green-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<span class="text-gray-700">مستقیماً به سوال پاسخ دهید</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 text-green-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<span class="text-gray-700">از مثال‌های عملی استفاده کنید</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 text-green-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<span class="text-gray-700">منابع و لینک‌های مفید ارائه دهید</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 text-green-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<span class="text-gray-700">کد و نمونه‌های کد ارائه دهید</span>
</li>
2025-09-05 09:37:27 +03:30
</ul>
</div>
2025-09-05 18:03:55 +03:30
<div>
<h4 class="text-lg font-semibold text-red-700 mb-4 flex items-center">
<svg class="w-5 h-5 text-red-600 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
پاسخ ضعیف:
</h4>
<ul class="space-y-3">
<li class="flex items-start">
<svg class="w-5 h-5 text-red-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span class="text-gray-700">پاسخ مبهم و کلی</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 text-red-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span class="text-gray-700">عدم ارائه راه‌حل عملی</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 text-red-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span class="text-gray-700">تکرار پاسخ‌های قبلی</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 text-red-600 ml-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span class="text-gray-700">پاسخ نامربوط</span>
</li>
2025-09-05 09:37:27 +03:30
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
2025-09-05 18:03:55 +03:30
</main>
2025-09-05 09:37:27 +03:30
<script>
// توابع ادیتور متن
function formatText(command) {
2025-09-05 18:03:55 +03:30
const textarea = document.querySelector('textarea[name="answer[content]"]');
2025-09-05 09:37:27 +03:30
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = textarea.value.substring(start, end);
let formattedText = '';
switch(command) {
case 'bold':
formattedText = `**${selectedText}**`;
break;
case 'italic':
formattedText = `*${selectedText}*`;
break;
case 'code':
formattedText = `\`${selectedText}\``;
break;
}
textarea.value = textarea.value.substring(0, start) + formattedText + textarea.value.substring(end);
textarea.focus();
textarea.setSelectionRange(start + formattedText.length, start + formattedText.length);
}
function insertList() {
2025-09-05 18:03:55 +03:30
const textarea = document.querySelector('textarea[name="answer[content]"]');
2025-09-05 09:37:27 +03:30
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = textarea.value.substring(start, end);
const listText = selectedText.split('\n').map(line => `- ${line}`).join('\n');
textarea.value = textarea.value.substring(0, start) + listText + textarea.value.substring(end);
textarea.focus();
}
2025-09-05 18:03:55 +03:30
// مدیریت فایل‌ها
class FileManager {
constructor() {
this.maxFiles = 3;
this.maxFileSize = 4 * 1024 * 1024; // 4MB
this.allowedTypes = [
'image/jpeg', 'image/png', 'image/gif',
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'text/plain'
];
this.selectedFiles = [];
this.init();
}
init() {
this.fileInput = document.getElementById('file-input');
this.selectedFilesDiv = document.getElementById('selected-files');
this.fileListDiv = document.getElementById('file-list');
if (!this.fileInput) return;
this.bindEvents();
}
bindEvents() {
this.fileInput.addEventListener('change', (e) => this.handleFileSelect(e));
// Drag and drop
const dropZone = this.fileInput.closest('.border-dashed');
if (dropZone) {
dropZone.addEventListener('dragover', (e) => this.handleDragOver(e));
dropZone.addEventListener('drop', (e) => this.handleDrop(e));
dropZone.addEventListener('dragleave', (e) => this.handleDragLeave(e));
}
}
handleFileSelect(event) {
const files = Array.from(event.target.files);
this.processFiles(files);
}
handleDragOver(event) {
event.preventDefault();
event.currentTarget.classList.add('bg-blue-50', 'border-blue-400');
}
handleDragLeave(event) {
event.preventDefault();
event.currentTarget.classList.remove('bg-blue-50', 'border-blue-400');
}
handleDrop(event) {
event.preventDefault();
event.currentTarget.classList.remove('bg-blue-50', 'border-blue-400');
const files = Array.from(event.dataTransfer.files);
this.processFiles(files);
}
processFiles(files) {
files.forEach(file => {
if (this.validateFile(file)) {
this.addFile(file);
}
});
this.updateFileInput();
}
validateFile(file) {
// بررسی تعداد فایل‌ها
if (this.selectedFiles.length >= this.maxFiles) {
alert(`حداکثر ${this.maxFiles} فایل می‌توانید انتخاب کنید`);
return false;
}
// بررسی نوع فایل
if (!this.allowedTypes.includes(file.type)) {
alert('فرمت فایل مجاز نیست');
return false;
}
// بررسی حجم فایل
if (file.size > this.maxFileSize) {
alert('حجم فایل نمی‌تواند بیش از 4 مگابایت باشد');
return false;
}
return true;
}
addFile(file) {
const fileId = Date.now() + Math.random();
this.selectedFiles.push({
id: fileId,
file: file,
name: file.name,
size: file.size,
type: file.type
});
this.renderFiles();
}
removeFile(fileId) {
this.selectedFiles = this.selectedFiles.filter(f => f.id !== fileId);
this.renderFiles();
this.updateFileInput();
}
renderFiles() {
if (this.selectedFiles.length === 0) {
this.selectedFilesDiv.classList.add('hidden');
return;
}
this.selectedFilesDiv.classList.remove('hidden');
this.fileListDiv.innerHTML = '';
this.selectedFiles.forEach(fileData => {
const fileElement = document.createElement('div');
fileElement.className = 'flex items-center justify-between p-3 bg-white rounded-lg border border-gray-200';
fileElement.innerHTML = `
<div class="flex items-center space-x-3 space-x-reverse">
<div class="flex-shrink-0">
${this.getFileIcon(fileData.type)}
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">${fileData.name}</p>
<p class="text-xs text-gray-500">${this.formatFileSize(fileData.size)}</p>
</div>
</div>
<button type="button"
class="flex-shrink-0 text-red-600 hover:text-red-800 transition-colors duration-200"
onclick="fileManager.removeFile(${fileData.id})">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
`;
this.fileListDiv.appendChild(fileElement);
});
}
getFileIcon(type) {
if (type.startsWith('image/')) {
return '<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21,15 16,10 5,21"></polyline></svg>';
} else if (type === 'application/pdf') {
return '<svg class="w-6 h-6 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"></path></svg>';
} else if (type.includes('word') || type.includes('document')) {
return '<svg class="w-6 h-6 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"></path></svg>';
} else {
return '<svg class="w-6 h-6 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"></path></svg>';
}
}
formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
updateFileInput() {
// ایجاد DataTransfer object جدید
const dt = new DataTransfer();
this.selectedFiles.forEach(fileData => {
dt.items.add(fileData.file);
});
this.fileInput.files = dt.files;
}
}
// متغیرهای سراسری
let fileManager;
// مقداردهی اولیه
document.addEventListener('DOMContentLoaded', function() {
fileManager = new FileManager();
});
2025-09-05 09:37:27 +03:30
</script>
{% endblock %}