342 lines
17 KiB
Twig
342 lines
17 KiB
Twig
{% extends 'base.html.twig' %}
|
|
|
|
{% block title %}{{ question.title }} - پرسش و پاسخ{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="container my-4">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<!-- سوال -->
|
|
<div class="card mb-4">
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-1 text-center">
|
|
<div class="vote-section">
|
|
<button class="btn btn-outline-success btn-sm vote-btn"
|
|
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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="18,15 12,9 6,15"></polyline>
|
|
</svg>
|
|
</button>
|
|
<div class="vote-count mt-2 mb-2" id="question-votes-{{ question.id }}">
|
|
{{ question.votes }}
|
|
</div>
|
|
<button class="btn btn-outline-danger btn-sm vote-btn"
|
|
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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="6,9 12,15 18,9"></polyline>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-11">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<h1 class="card-title mb-0">{{ question.title }}</h1>
|
|
{% if question.isSolved %}
|
|
<span class="badge bg-success fs-6">
|
|
<i class="fas fa-check me-1"></i>حل شده
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="question-content mb-3">
|
|
{{ question.content|markdown|raw }}
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div class="tags">
|
|
{% for tagRelation in question.tagRelations %}
|
|
<a href="{{ path('qa_tag_questions', {'name': tagRelation.tag.name}) }}"
|
|
class="badge bg-light text-dark text-decoration-none me-1">
|
|
{{ tagRelation.tag.name }}
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="text-muted small">
|
|
<i class="fas fa-user me-1"></i>{{ question.author.name }}
|
|
<i class="fas fa-clock ms-3 me-1"></i>{{ question.createdAt|date('Y/m/d H:i') }}
|
|
<i class="fas fa-eye ms-3 me-1"></i>{{ question.views }} بازدید
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- پاسخها -->
|
|
<div class="mb-4">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h3>
|
|
<i class="fas fa-comments me-2"></i>پاسخها
|
|
<span class="badge bg-primary">{{ totalAnswers }}</span>
|
|
</h3>
|
|
{% if app.user and 'ROLE_CUSTOMER' in app.user.roles %}
|
|
<a href="{{ path('qa_answer', {'id': question.id}) }}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>پاسخ دهید
|
|
</a>
|
|
{% else %}
|
|
<a href="{{ path('customer_login') }}" class="btn btn-outline-primary">
|
|
<i class="fas fa-sign-in-alt me-2"></i>ورود برای پاسخ دادن
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if answers is empty %}
|
|
<div class="card">
|
|
<div class="card-body text-center py-5">
|
|
<i class="fas fa-comment-slash fa-3x text-muted mb-3"></i>
|
|
<h4 class="text-muted">هنوز پاسخی داده نشده</h4>
|
|
<p class="text-muted">اولین کسی باشید که به این سوال پاسخ میدهد.</p>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
{% for answer in answers %}
|
|
<div class="card mb-3 answer-card {{ answer.isAccepted ? 'border-success' : '' }}">
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-1 text-center">
|
|
<div class="vote-section">
|
|
<button class="btn btn-outline-success btn-sm vote-btn"
|
|
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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="18,15 12,9 6,15"></polyline>
|
|
</svg>
|
|
</button>
|
|
<div class="vote-count mt-2 mb-2" id="answer-votes-{{ answer.id }}">
|
|
{{ answer.votes }}
|
|
</div>
|
|
<button class="btn btn-outline-danger btn-sm vote-btn"
|
|
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 xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="6,9 12,15 18,9"></polyline>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-11">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<div class="answer-content">
|
|
{{ answer.content|markdown|raw }}
|
|
</div>
|
|
{% if answer.isAccepted %}
|
|
<span class="badge bg-success ms-2">
|
|
<i class="fas fa-check me-1"></i>پاسخ پذیرفته شده
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div class="text-muted small">
|
|
<i class="fas fa-user me-1"></i>{{ answer.author.name }}
|
|
<i class="fas fa-clock ms-3 me-1"></i>{{ answer.createdAt|date('Y/m/d H:i') }}
|
|
</div>
|
|
|
|
{% if app.user and app.user == question.author and not answer.isAccepted %}
|
|
<button class="btn btn-outline-success btn-sm accept-answer-btn"
|
|
data-answer-id="{{ answer.id }}">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-1">
|
|
<polyline points="20,6 9,17 4,12"></polyline>
|
|
</svg>پذیرفتن پاسخ
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
<!-- صفحهبندی پاسخها -->
|
|
{% if totalPages > 1 %}
|
|
<nav aria-label="صفحهبندی پاسخها" class="mt-4">
|
|
<ul class="pagination justify-content-center">
|
|
{% if currentPage > 1 %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ currentPage - 1 }}">قبلی</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for page in 1..totalPages %}
|
|
{% if page == currentPage %}
|
|
<li class="page-item active">
|
|
<span class="page-link">{{ page }}</span>
|
|
</li>
|
|
{% else %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ page }}">{{ page }}</a>
|
|
</li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if currentPage < totalPages %}
|
|
<li class="page-item">
|
|
<a class="page-link" href="?page={{ currentPage + 1 }}">بعدی</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- دکمه بازگشت -->
|
|
<div class="text-center">
|
|
<a href="{{ path('qa_index') }}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-right me-2"></i>بازگشت به لیست سوالات
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.vote-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.vote-btn {
|
|
width: 30px;
|
|
height: 30px;
|
|
padding: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.vote-count {
|
|
font-size: 1.1rem;
|
|
font-weight: bold;
|
|
min-width: 30px;
|
|
text-align: center;
|
|
}
|
|
|
|
.answer-card.border-success {
|
|
border-left: 4px solid #198754 !important;
|
|
}
|
|
|
|
.question-content, .answer-content {
|
|
line-height: 1.6;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.tags .badge {
|
|
font-size: 0.9rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.tags .badge:hover {
|
|
background-color: #0d6efd !important;
|
|
color: white !important;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
function initializeVoteButtons() {
|
|
// رایدهی
|
|
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) {
|
|
alert(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('btn-success', 'btn-danger', 'btn-outline-success', 'btn-outline-danger');
|
|
btn.classList.add(btn.dataset.upvote === 'true' ? 'btn-outline-success' : 'btn-outline-danger');
|
|
});
|
|
|
|
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('btn-outline-success', 'btn-outline-danger');
|
|
activeButton.classList.add(data.userVote === 'up' ? 'btn-success' : 'btn-danger');
|
|
}
|
|
} else {
|
|
// اگر رای حذف شده، همه دکمهها را به حالت عادی برگردان
|
|
buttons.forEach(btn => {
|
|
btn.classList.remove('btn-success', 'btn-danger');
|
|
btn.classList.add(btn.dataset.upvote === 'true' ? 'btn-outline-success' : 'btn-outline-danger');
|
|
});
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('خطا در ارسال رای');
|
|
});
|
|
});
|
|
});
|
|
|
|
// پذیرفتن پاسخ
|
|
document.querySelectorAll('.accept-answer-btn').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const answerId = this.dataset.answerId;
|
|
|
|
if (!confirm('آیا مطمئن هستید که میخواهید این پاسخ را بپذیرید؟')) {
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
alert(data.error);
|
|
return;
|
|
}
|
|
|
|
location.reload();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert('خطا در پذیرفتن پاسخ');
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// اجرا در هر دو حالت
|
|
document.addEventListener('DOMContentLoaded', initializeVoteButtons);
|
|
document.addEventListener('turbo:load', initializeVoteButtons);
|
|
</script>
|
|
{% endblock %}
|