hesabixSite/templates/qa/question_show.html.twig
Hesabix 1467e83ccf
Some checks are pending
PHP Composer / build (push) Waiting to run
remove bootstrap
2025-09-05 11:52:08 +03:30

435 lines
28 KiB
Twig

{% extends 'base.html.twig' %}
{% block title %}{{ question.title }} - پرسش و پاسخ{% endblock %}
{% block body %}
<main class="min-h-screen bg-gray-50">
<div class="container mx-auto px-4 py-8">
<div class="max-w-6xl mx-auto">
<!-- سوال -->
<div class="bg-white rounded-2xl shadow-soft mb-8 overflow-hidden">
<div class="p-8">
<div class="flex space-x-6 space-x-reverse">
<!-- سیستم رای‌دهی -->
<div class="flex flex-col items-center space-y-2 min-w-0">
<button class="vote-btn w-10 h-10 flex items-center justify-center rounded-lg border-2 border-green-200 text-green-600 hover:bg-green-50 hover:border-green-300 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
data-type="question"
data-id="{{ question.id }}"
data-upvote="true"
{% if not app.user or 'ROLE_CUSTOMER' not in app.user.roles %}disabled{% endif %}>
<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="M5 15l7-7 7 7"></path>
</svg>
</button>
<div class="text-2xl font-bold text-gray-900" id="question-votes-{{ question.id }}">
{{ question.votes }}
</div>
<button class="vote-btn w-10 h-10 flex items-center justify-center rounded-lg border-2 border-red-200 text-red-600 hover:bg-red-50 hover:border-red-300 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
data-type="question"
data-id="{{ question.id }}"
data-upvote="false"
{% if not app.user or 'ROLE_CUSTOMER' not in app.user.roles %}disabled{% endif %}>
<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="M19 9l-7 7-7-7"></path>
</svg>
</button>
</div>
<!-- محتوای سوال -->
<div class="flex-1 min-w-0">
<div class="flex items-start justify-between mb-6">
<h1 class="text-3xl font-bold text-gray-900 leading-tight">{{ question.title }}</h1>
{% if question.isSolved %}
<span class="inline-flex items-center space-x-1 space-x-reverse px-4 py-2 bg-green-100 text-green-800 text-sm font-medium rounded-full">
<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="M5 13l4 4L19 7"></path>
</svg>
<span>حل شده</span>
</span>
{% endif %}
</div>
<div class="prose prose-lg max-w-none text-gray-800 leading-relaxed mb-6">
{{ question.content|markdown|raw }}
</div>
<div class="flex flex-wrap items-center justify-between gap-4 pt-6 border-t border-gray-200">
<!-- تگ‌ها -->
<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 %}
</div>
<!-- اطلاعات سوال -->
<div class="flex items-center space-x-6 space-x-reverse text-sm text-gray-500">
<div class="flex items-center space-x-1 space-x-reverse">
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
<span>{{ question.author.name }}</span>
</div>
<div class="flex items-center space-x-1 space-x-reverse">
<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="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>{{ question.createdAt|date('Y/m/d H:i') }}</span>
</div>
<div class="flex items-center space-x-1 space-x-reverse">
<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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<span>{{ question.views }} بازدید</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- پاسخ‌ها -->
<div class="mb-8">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-900 flex items-center">
<svg class="w-6 h-6 text-blue-600 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
</svg>
پاسخ‌ها
<span class="inline-flex items-center px-3 py-1 bg-blue-100 text-blue-800 text-sm font-medium rounded-full mr-3">
{{ totalAnswers }}
</span>
</h2>
{% if app.user and 'ROLE_CUSTOMER' in app.user.roles %}
<a href="{{ path('qa_answer', {'id': question.id}) }}"
class="inline-flex items-center space-x-2 space-x-reverse px-6 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-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
<span>پاسخ دهید</span>
</a>
{% else %}
<a href="{{ path('customer_login') }}"
class="inline-flex items-center space-x-2 space-x-reverse 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-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
<polyline points="10,17 15,12 10,7"></polyline>
<line x1="15" y1="12" x2="3" y2="12"></line>
</svg>
<span>ورود برای پاسخ دادن</span>
</a>
{% endif %}
</div>
{% if answers is empty %}
<div class="bg-white rounded-2xl shadow-soft p-12 text-center">
<div class="max-w-md mx-auto">
<svg class="w-16 h-16 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
</svg>
<h3 class="text-xl font-semibold text-gray-900 mb-2">هنوز پاسخی داده نشده</h3>
<p class="text-gray-600">اولین کسی باشید که به این سوال پاسخ می‌دهد.</p>
</div>
</div>
{% else %}
<div class="space-y-6">
{% for answer in answers %}
<div class="bg-white rounded-2xl shadow-soft overflow-hidden {{ answer.isAccepted ? 'ring-2 ring-green-200 border-green-200' : '' }}">
<div class="p-8">
<div class="flex space-x-6 space-x-reverse">
<!-- سیستم رای‌دهی پاسخ -->
<div class="flex flex-col items-center space-y-2 min-w-0">
<button class="vote-btn w-10 h-10 flex items-center justify-center rounded-lg border-2 border-green-200 text-green-600 hover:bg-green-50 hover:border-green-300 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
data-type="answer"
data-id="{{ answer.id }}"
data-upvote="true"
{% if not app.user or 'ROLE_CUSTOMER' not in app.user.roles %}disabled{% endif %}>
<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="M5 15l7-7 7 7"></path>
</svg>
</button>
<div class="text-2xl font-bold text-gray-900" id="answer-votes-{{ answer.id }}">
{{ answer.votes }}
</div>
<button class="vote-btn w-10 h-10 flex items-center justify-center rounded-lg border-2 border-red-200 text-red-600 hover:bg-red-50 hover:border-red-300 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
data-type="answer"
data-id="{{ answer.id }}"
data-upvote="false"
{% if not app.user or 'ROLE_CUSTOMER' not in app.user.roles %}disabled{% endif %}>
<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="M19 9l-7 7-7-7"></path>
</svg>
</button>
</div>
<!-- محتوای پاسخ -->
<div class="flex-1 min-w-0">
<div class="flex items-start justify-between mb-4">
<div class="prose prose-lg max-w-none text-gray-800 leading-relaxed">
{{ answer.content|markdown|raw }}
</div>
{% if answer.isAccepted %}
<span class="inline-flex items-center space-x-1 space-x-reverse px-3 py-1 bg-green-100 text-green-800 text-sm font-medium rounded-full mr-4">
<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="M5 13l4 4L19 7"></path>
</svg>
<span>پاسخ پذیرفته شده</span>
</span>
{% endif %}
</div>
<div class="flex items-center justify-between pt-4 border-t border-gray-200">
<div class="flex items-center space-x-4 space-x-reverse text-sm text-gray-500">
<div class="flex items-center space-x-1 space-x-reverse">
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
<span>{{ answer.author.name }}</span>
</div>
<div class="flex items-center space-x-1 space-x-reverse">
<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="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>{{ answer.createdAt|date('Y/m/d H:i') }}</span>
</div>
</div>
{% if app.user and app.user == question.author and not answer.isAccepted %}
<button class="accept-answer-btn inline-flex items-center space-x-2 space-x-reverse px-4 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors duration-200 font-medium"
data-answer-id="{{ answer.id }}">
<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="M5 13l4 4L19 7"></path>
</svg>
<span>پذیرفتن پاسخ</span>
</button>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- صفحه‌بندی پاسخ‌ها -->
{% if totalPages > 1 %}
<div class="mt-8">
<nav class="flex justify-center" aria-label="صفحه‌بندی پاسخ‌ها">
<div class="flex items-center space-x-2 space-x-reverse">
<!-- دکمه صفحه قبل -->
{% if currentPage > 1 %}
<a href="?page={{ currentPage - 1 }}"
class="flex items-center space-x-2 space-x-reverse px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 hover:text-gray-700 transition-all duration-200">
<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="M9 5l7 7-7 7"></path>
</svg>
<span>قبلی</span>
</a>
{% endif %}
<!-- شماره صفحات -->
<div class="flex items-center space-x-1 space-x-reverse">
{% for page in 1..totalPages %}
{% if page == currentPage %}
<span class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-blue-600 rounded-lg">
{{ page }}
</span>
{% elseif page <= currentPage + 2 and page >= currentPage - 2 %}
<a href="?page={{ page }}"
class="px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 hover:text-gray-700 transition-all duration-200">
{{ page }}
</a>
{% endif %}
{% endfor %}
</div>
<!-- دکمه صفحه بعد -->
{% if currentPage < totalPages %}
<a href="?page={{ currentPage + 1 }}"
class="flex items-center space-x-2 space-x-reverse px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 hover:text-gray-700 transition-all duration-200">
<span>بعدی</span>
<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="M15 19l-7-7 7-7"></path>
</svg>
</a>
{% endif %}
</div>
</nav>
</div>
{% endif %}
{% endif %}
</div>
<!-- دکمه بازگشت -->
<div class="text-center mt-8">
<a href="{{ path('qa_index') }}"
class="inline-flex items-center space-x-2 space-x-reverse 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-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
<span>بازگشت به لیست سوالات</span>
</a>
</div>
</div>
</div>
</main>
<script>
function initializeVoteButtons() {
// Wait for notification system to be available
if (typeof window.notification === 'undefined') {
console.log('Waiting for notification system...');
setTimeout(initializeVoteButtons, 100);
return;
}
// رای‌دهی
document.querySelectorAll('.vote-btn').forEach(button => {
button.addEventListener('click', function() {
if (this.disabled) return;
const type = this.dataset.type;
const id = this.dataset.id;
const upvote = this.dataset.upvote === 'true';
fetch(`/qa/${type}/${id}/vote`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: `upvote=${upvote}&_token={{ csrf_token('vote') }}`
})
.then(response => response.json())
.then(data => {
if (data.error) {
if (window.notification) {
window.notification.error(data.error);
}
return;
}
document.getElementById(`${type}-votes-${id}`).textContent = data.votes;
// تغییر رنگ دکمه‌ها
const buttons = document.querySelectorAll(`[data-type="${type}"][data-id="${id}"]`);
buttons.forEach(btn => {
btn.classList.remove('bg-green-500', 'bg-red-500', 'border-green-500', 'border-red-500', 'text-white');
btn.classList.add(btn.dataset.upvote === 'true' ? 'border-green-200 text-green-600' : 'border-red-200 text-red-600');
});
if (data.userVote) {
const activeButton = document.querySelector(`[data-type="${type}"][data-id="${id}"][data-upvote="${data.userVote === 'up' ? 'true' : 'false'}"]`);
if (activeButton) {
activeButton.classList.remove('border-green-200', 'border-red-200', 'text-green-600', 'text-red-600');
activeButton.classList.add(data.userVote === 'up' ? 'bg-green-500 text-white border-green-500' : 'bg-red-500 text-white border-red-500');
}
if (window.notification) {
window.notification.success('رای شما ثبت شد');
}
} else {
// اگر رای حذف شده، همه دکمه‌ها را به حالت عادی برگردان
buttons.forEach(btn => {
btn.classList.remove('bg-green-500', 'bg-red-500', 'text-white', 'border-green-500', 'border-red-500');
btn.classList.add(btn.dataset.upvote === 'true' ? 'border-green-200 text-green-600' : 'border-red-200 text-red-600');
});
if (window.notification) {
window.notification.info('رای شما حذف شد');
}
}
})
.catch(error => {
console.error('Error:', error);
if (window.notification) {
window.notification.error('خطا در ارسال رای. لطفاً دوباره تلاش کنید.');
}
});
});
});
// پذیرفتن پاسخ
document.querySelectorAll('.accept-answer-btn').forEach(button => {
button.addEventListener('click', function() {
const answerId = this.dataset.answerId;
// Show confirmation dialog
const confirmed = confirm('آیا مطمئن هستید که می‌خواهید این پاسخ را بپذیرید؟');
if (!confirmed) {
return;
}
// Show loading state
const originalText = this.innerHTML;
this.innerHTML = '<div class="loading-spinner"></div>';
this.disabled = true;
fetch(`/qa/answer/${answerId}/accept`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: `_token={{ csrf_token('accept') }}`
})
.then(response => response.json())
.then(data => {
if (data.error) {
if (window.notification) {
window.notification.error(data.error);
}
return;
}
if (window.notification) {
window.notification.success('پاسخ با موفقیت پذیرفته شد');
}
setTimeout(() => {
location.reload();
}, 1500);
})
.catch(error => {
console.error('Error:', error);
if (window.notification) {
window.notification.error('خطا در پذیرفتن پاسخ. لطفاً دوباره تلاش کنید.');
}
})
.finally(() => {
// Reset button state
this.innerHTML = originalText;
this.disabled = false;
});
});
});
}
// اجرا در هر دو حالت
document.addEventListener('DOMContentLoaded', function() {
// Wait for notification system to be available
const checkNotification = () => {
if (window.notification) {
initializeVoteButtons();
} else {
setTimeout(checkNotification, 100);
}
};
checkNotification();
});
document.addEventListener('turbo:load', function() {
// Wait for notification system to be available
const checkNotification = () => {
if (window.notification) {
initializeVoteButtons();
} else {
setTimeout(checkNotification, 100);
}
};
checkNotification();
});
</script>
{% endblock %}