This commit is contained in:
parent
be126d506b
commit
3c7fa1b8a4
|
|
@ -9,6 +9,8 @@ export default class extends Controller {
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
console.log('Wallet Connect Controller connected');
|
console.log('Wallet Connect Controller connected');
|
||||||
|
console.log('Controller targets:', this.targets);
|
||||||
|
console.log('Controller values:', this.values);
|
||||||
this.selectedWallet = null;
|
this.selectedWallet = null;
|
||||||
this.walletAddress = null;
|
this.walletAddress = null;
|
||||||
this.signature = null;
|
this.signature = null;
|
||||||
|
|
@ -130,14 +132,20 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setPrimaryWallet(walletId) {
|
async setPrimaryWallet(event) {
|
||||||
|
const walletId = event.currentTarget.dataset.walletId;
|
||||||
|
if (!walletId) {
|
||||||
|
this.showMessage('شناسه کیف پول یافت نشد', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!confirm('آیا میخواهید این کیف پول را به عنوان اصلی تنظیم کنید؟')) {
|
if (!confirm('آیا میخواهید این کیف پول را به عنوان اصلی تنظیم کنید؟')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/wallet/${walletId}/set-primary`, {
|
const response = await fetch(`/api/wallet/${walletId}/set-primary`, {
|
||||||
method: 'POST',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-CSRF-Token': this.csrfTokenValue || ''
|
'X-CSRF-Token': this.csrfTokenValue || ''
|
||||||
|
|
@ -161,10 +169,19 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleWalletStatus(walletId) {
|
async toggleWalletStatus(event) {
|
||||||
|
console.log('toggleWalletStatus called', event);
|
||||||
|
const walletId = event.currentTarget.dataset.walletId;
|
||||||
|
console.log('Wallet ID:', walletId);
|
||||||
|
if (!walletId) {
|
||||||
|
this.showMessage('شناسه کیف پول یافت نشد', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('Making request to toggle wallet status...');
|
||||||
const response = await fetch(`/api/wallet/${walletId}/toggle-status`, {
|
const response = await fetch(`/api/wallet/${walletId}/toggle-status`, {
|
||||||
method: 'POST',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-CSRF-Token': this.csrfTokenValue || ''
|
'X-CSRF-Token': this.csrfTokenValue || ''
|
||||||
|
|
@ -188,12 +205,21 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteWallet(walletId) {
|
async deleteWallet(event) {
|
||||||
|
console.log('deleteWallet called', event);
|
||||||
|
const walletId = event.currentTarget.dataset.walletId;
|
||||||
|
console.log('Wallet ID:', walletId);
|
||||||
|
if (!walletId) {
|
||||||
|
this.showMessage('شناسه کیف پول یافت نشد', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!confirm('آیا مطمئن هستید که میخواهید این کیف پول را حذف کنید؟')) {
|
if (!confirm('آیا مطمئن هستید که میخواهید این کیف پول را حذف کنید؟')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('Making request to delete wallet...');
|
||||||
const response = await fetch(`/api/wallet/${walletId}`, {
|
const response = await fetch(`/api/wallet/${walletId}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,4 @@ framework:
|
||||||
token_id: submit
|
token_id: submit
|
||||||
|
|
||||||
csrf_protection:
|
csrf_protection:
|
||||||
stateless_token_ids:
|
|
||||||
- submit
|
|
||||||
- authenticate
|
|
||||||
- logout
|
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,18 @@ security:
|
||||||
logout:
|
logout:
|
||||||
path: customer_logout
|
path: customer_logout
|
||||||
target: app_home
|
target: app_home
|
||||||
|
qa:
|
||||||
|
pattern: ^/qa
|
||||||
|
lazy: true
|
||||||
|
provider: app_user_provider
|
||||||
|
remember_me:
|
||||||
|
secret: '%kernel.secret%'
|
||||||
|
lifetime: 604800 # 1 week
|
||||||
|
path: /
|
||||||
|
always_remember_me: false
|
||||||
|
logout:
|
||||||
|
path: customer_logout
|
||||||
|
target: app_home
|
||||||
main:
|
main:
|
||||||
pattern: ^/
|
pattern: ^/
|
||||||
lazy: true
|
lazy: true
|
||||||
|
|
@ -69,6 +81,12 @@ security:
|
||||||
# محافظت از سایر مسیرهای /admin
|
# محافظت از سایر مسیرهای /admin
|
||||||
- { path: ^/admin, roles: ROLE_ADMIN }
|
- { path: ^/admin, roles: ROLE_ADMIN }
|
||||||
- { path: ^/customer/dashboard, roles: ROLE_CUSTOMER }
|
- { path: ^/customer/dashboard, roles: ROLE_CUSTOMER }
|
||||||
|
# محافظت از مسیرهای QA
|
||||||
|
- { path: ^/qa/ask, roles: ROLE_CUSTOMER }
|
||||||
|
- { path: ^/qa/question/.*/answer, roles: ROLE_CUSTOMER }
|
||||||
|
- { path: ^/qa/.*/vote, roles: ROLE_CUSTOMER }
|
||||||
|
- { path: ^/qa/.*/accept, roles: ROLE_CUSTOMER }
|
||||||
|
- { path: ^/qa/tag/create, roles: ROLE_CUSTOMER }
|
||||||
# - { path: ^/profile, roles: ROLE_USER }
|
# - { path: ^/profile, roles: ROLE_USER }
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
|
|
|
||||||
|
|
@ -136,9 +136,82 @@ class QAController extends AbstractController
|
||||||
$form = $this->createForm(QuestionFormType::class, $question);
|
$form = $this->createForm(QuestionFormType::class, $question);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
|
||||||
// مدیریت تگها
|
|
||||||
$selectedTagIds = $request->request->all('question')['tags'] ?? [];
|
if ($form->isSubmitted()) {
|
||||||
|
// بررسی CSRF token - حذف شده چون Symfony خودش بررسی میکند
|
||||||
|
// if (!$this->isCsrfTokenValid('question', $request->request->get('_token'))) {
|
||||||
|
// $this->addFlash('error', 'CSRF token نامعتبر است.');
|
||||||
|
// $availableTags = $this->tagRepository->findActiveTags();
|
||||||
|
// return $this->render('qa/ask_question.html.twig', [
|
||||||
|
// 'form' => $form,
|
||||||
|
// 'availableTags' => $availableTags,
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// دریافت تگهای انتخاب شده از فرم
|
||||||
|
$selectedTags = $form->get('tags')->getData();
|
||||||
|
$selectedTagIds = [];
|
||||||
|
|
||||||
|
if ($selectedTags) {
|
||||||
|
foreach ($selectedTags as $tag) {
|
||||||
|
$selectedTagIds[] = $tag->getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug: بررسی تگهای ارسال شده
|
||||||
|
error_log('Selected tag IDs from form: ' . print_r($selectedTagIds, true));
|
||||||
|
error_log('Form is valid: ' . ($form->isValid() ? 'true' : 'false'));
|
||||||
|
|
||||||
|
// اگر form valid نیست، خطاها را نمایش بده
|
||||||
|
if (!$form->isValid()) {
|
||||||
|
$this->addFlash('error', 'لطفاً تمام فیلدهای الزامی را پر کنید');
|
||||||
|
$availableTags = $this->tagRepository->findActiveTags();
|
||||||
|
return $this->render('qa/ask_question.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
'availableTags' => $availableTags,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// مدیریت پیوست فایلها
|
||||||
|
$attachments = $form->get('attachments')->getData();
|
||||||
|
error_log('Attachments from form: ' . print_r($attachments, true));
|
||||||
|
|
||||||
|
// بررسی فایلهای ارسال شده در request
|
||||||
|
$uploadedFiles = $request->files->get('question_form')['attachments'] ?? [];
|
||||||
|
error_log('Uploaded files from request: ' . print_r($uploadedFiles, true));
|
||||||
|
|
||||||
|
if ($attachments && count($attachments) > 0) {
|
||||||
|
error_log('Processing ' . count($attachments) . ' attachments from form');
|
||||||
|
$uploadedAttachments = $this->attachmentService->uploadAttachments($attachments, $this->getUser(), $question);
|
||||||
|
error_log('Uploaded attachments: ' . print_r($uploadedAttachments, true));
|
||||||
|
|
||||||
|
foreach ($uploadedAttachments as $attachment) {
|
||||||
|
$question->addAttachment($attachment);
|
||||||
|
error_log('Added attachment to question: ' . $attachment->getOriginalFilename());
|
||||||
|
}
|
||||||
|
} elseif ($uploadedFiles && count($uploadedFiles) > 0) {
|
||||||
|
error_log('Processing ' . count($uploadedFiles) . ' files from request');
|
||||||
|
$uploadedAttachments = $this->attachmentService->uploadAttachments($uploadedFiles, $this->getUser(), $question);
|
||||||
|
error_log('Uploaded attachments from request: ' . print_r($uploadedAttachments, true));
|
||||||
|
|
||||||
|
foreach ($uploadedAttachments as $attachment) {
|
||||||
|
$question->addAttachment($attachment);
|
||||||
|
error_log('Added attachment to question: ' . $attachment->getOriginalFilename());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_log('No attachments found in form data or request');
|
||||||
|
}
|
||||||
|
|
||||||
|
// حذف تمام تگهای قبلی برای این سوال
|
||||||
|
$existingRelations = $this->entityManager->getRepository(QuestionTagRelation::class)
|
||||||
|
->findBy(['question' => $question]);
|
||||||
|
|
||||||
|
foreach ($existingRelations as $relation) {
|
||||||
|
$this->entityManager->remove($relation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// اضافه کردن تگهای جدید
|
||||||
foreach ($selectedTagIds as $tagId) {
|
foreach ($selectedTagIds as $tagId) {
|
||||||
$tag = $this->tagRepository->find($tagId);
|
$tag = $this->tagRepository->find($tagId);
|
||||||
if ($tag) {
|
if ($tag) {
|
||||||
|
|
@ -149,18 +222,13 @@ class QAController extends AbstractController
|
||||||
|
|
||||||
// افزایش تعداد استفاده از تگ
|
// افزایش تعداد استفاده از تگ
|
||||||
$tag->incrementUsageCount();
|
$tag->incrementUsageCount();
|
||||||
|
|
||||||
|
error_log('Added tag relation for tag ID: ' . $tagId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// مدیریت پیوست فایلها
|
|
||||||
$attachments = $form->get('attachments')->getData();
|
|
||||||
if ($attachments) {
|
|
||||||
$uploadedAttachments = $this->attachmentService->uploadAttachments($attachments, $this->getUser(), $question);
|
|
||||||
foreach ($uploadedAttachments as $attachment) {
|
|
||||||
$question->addAttachment($attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// ذخیره سوال در دیتابیس
|
||||||
$this->entityManager->persist($question);
|
$this->entityManager->persist($question);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
|
@ -394,7 +462,7 @@ class QAController extends AbstractController
|
||||||
public function createTag(Request $request): JsonResponse
|
public function createTag(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
// بررسی CSRF token
|
// بررسی CSRF token
|
||||||
if (!$this->isCsrfTokenValid('vote', $request->request->get('_token'))) {
|
if (!$this->isCsrfTokenValid('question', $request->request->get('_token'))) {
|
||||||
return new JsonResponse(['error' => 'CSRF token نامعتبر است.'], 403);
|
return new JsonResponse(['error' => 'CSRF token نامعتبر است.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,19 @@ class QuestionFormType extends AbstractType
|
||||||
->add('tags', EntityType::class, [
|
->add('tags', EntityType::class, [
|
||||||
'class' => QuestionTag::class,
|
'class' => QuestionTag::class,
|
||||||
'choice_label' => 'name',
|
'choice_label' => 'name',
|
||||||
|
'choice_value' => 'id',
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => false,
|
'expanded' => false,
|
||||||
'required' => false,
|
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
|
'required' => false,
|
||||||
|
'query_builder' => function (\Doctrine\ORM\EntityRepository $er) {
|
||||||
|
return $er->createQueryBuilder('t')
|
||||||
|
->where('t.isActive = :active')
|
||||||
|
->setParameter('active', true)
|
||||||
|
->orderBy('t.name', 'ASC');
|
||||||
|
},
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'class' => 'form-control',
|
'class' => 'form-control d-none' // مخفی میکنیم چون با JavaScript مدیریت میشود
|
||||||
'style' => 'display: none;' // مخفی کردن چون با JavaScript مدیریت میشود
|
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
new Assert\Count([
|
new Assert\Count([
|
||||||
|
|
|
||||||
|
|
@ -173,21 +173,21 @@
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
{% if not wallet.isPrimary and wallet.isActive %}
|
{% if not wallet.isPrimary and wallet.isActive %}
|
||||||
<button class="inline-flex items-center px-3 py-1 text-xs font-medium text-blue-600 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors duration-200"
|
<button class="wallet-set-primary-btn inline-flex items-center px-3 py-1 text-xs font-medium text-blue-600 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors duration-200"
|
||||||
data-wallet-id="{{ wallet.id }}"
|
data-wallet-id="{{ wallet.id }}"
|
||||||
data-action="click->wallet-connect#setPrimaryWallet">
|
data-action="click->wallet-connect#setPrimaryWallet">
|
||||||
<i class="fas fa-star ml-1"></i> اصلی
|
<i class="fas fa-star ml-1"></i> اصلی
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<button class="inline-flex items-center px-3 py-1 text-xs font-medium text-yellow-600 bg-yellow-50 border border-yellow-200 rounded-lg hover:bg-yellow-100 transition-colors duration-200"
|
<button class="wallet-toggle-btn inline-flex items-center px-3 py-1 text-xs font-medium text-yellow-600 bg-yellow-50 border border-yellow-200 rounded-lg hover:bg-yellow-100 transition-colors duration-200"
|
||||||
data-wallet-id="{{ wallet.id }}"
|
data-wallet-id="{{ wallet.id }}"
|
||||||
data-action="click->wallet-connect#toggleWalletStatus">
|
data-action="click->wallet-connect#toggleWalletStatus">
|
||||||
<i class="fas fa-{{ wallet.isActive ? 'pause' : 'play' }} ml-1"></i>
|
<i class="fas fa-{{ wallet.isActive ? 'pause' : 'play' }} ml-1"></i>
|
||||||
{{ wallet.isActive ? 'غیرفعال' : 'فعال' }}
|
{{ wallet.isActive ? 'غیرفعال' : 'فعال' }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="inline-flex items-center px-3 py-1 text-xs font-medium text-red-600 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition-colors duration-200"
|
<button class="wallet-delete-btn inline-flex items-center px-3 py-1 text-xs font-medium text-red-600 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition-colors duration-200"
|
||||||
data-wallet-id="{{ wallet.id }}"
|
data-wallet-id="{{ wallet.id }}"
|
||||||
data-action="click->wallet-connect#deleteWallet">
|
data-action="click->wallet-connect#deleteWallet">
|
||||||
<i class="fas fa-trash ml-1"></i> حذف
|
<i class="fas fa-trash ml-1"></i> حذف
|
||||||
|
|
@ -244,4 +244,400 @@
|
||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
|
<script>
|
||||||
|
// Beautiful Dialog System
|
||||||
|
class BeautifulDialog {
|
||||||
|
constructor() {
|
||||||
|
this.container = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Wait for DOM to be ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
this.createContainer();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.createContainer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createContainer() {
|
||||||
|
// Create dialog container if it doesn't exist
|
||||||
|
this.container = document.getElementById('dialog-container');
|
||||||
|
if (!this.container && document.body) {
|
||||||
|
this.container = document.createElement('div');
|
||||||
|
this.container.id = 'dialog-container';
|
||||||
|
this.container.className = 'fixed inset-0 z-50 overflow-y-auto';
|
||||||
|
this.container.style.display = 'none';
|
||||||
|
document.body.appendChild(this.container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showConfirm(title, message, confirmText = 'تأیید', cancelText = 'انصراف', onConfirm = null, onCancel = null) {
|
||||||
|
// Ensure container exists
|
||||||
|
if (!this.container) {
|
||||||
|
this.createContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.container) {
|
||||||
|
console.error('Dialog container not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogId = 'confirm-dialog-' + Date.now();
|
||||||
|
const dialogHtml = `
|
||||||
|
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4" id="${dialogId}">
|
||||||
|
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full transform transition-all duration-300 scale-95 opacity-0" id="${dialogId}-content">
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<div class="flex-shrink-0 w-10 h-10 bg-yellow-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="mr-3">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900">${title}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-6">
|
||||||
|
<p class="text-sm text-gray-600 leading-relaxed">${message}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end space-x-3 space-x-reverse">
|
||||||
|
<button type="button" class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 border border-gray-300 rounded-lg hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 transition-colors duration-200" id="${dialogId}-cancel">
|
||||||
|
${cancelText}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="px-4 py-2 text-sm font-medium text-white bg-red-600 border border-transparent rounded-lg hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 transition-colors duration-200" id="${dialogId}-confirm">
|
||||||
|
${confirmText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.container.innerHTML = dialogHtml;
|
||||||
|
this.container.style.display = 'block';
|
||||||
|
|
||||||
|
// Animate in
|
||||||
|
setTimeout(() => {
|
||||||
|
const content = document.getElementById(`${dialogId}-content`);
|
||||||
|
content.classList.remove('scale-95', 'opacity-0');
|
||||||
|
content.classList.add('scale-100', 'opacity-100');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// Event listeners
|
||||||
|
document.getElementById(`${dialogId}-confirm`).addEventListener('click', () => {
|
||||||
|
this.hide();
|
||||||
|
if (onConfirm) onConfirm();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById(`${dialogId}-cancel`).addEventListener('click', () => {
|
||||||
|
this.hide();
|
||||||
|
if (onCancel) onCancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on backdrop click
|
||||||
|
document.getElementById(dialogId).addEventListener('click', (e) => {
|
||||||
|
if (e.target.id === dialogId) {
|
||||||
|
this.hide();
|
||||||
|
if (onCancel) onCancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess(title, message, duration = 3000) {
|
||||||
|
this.showNotification(title, message, 'success', duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(title, message, duration = 5000) {
|
||||||
|
this.showNotification(title, message, 'error', duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo(title, message, duration = 4000) {
|
||||||
|
this.showNotification(title, message, 'info', duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
showNotification(title, message, type = 'info', duration = 4000) {
|
||||||
|
// Ensure document.body is available
|
||||||
|
if (!document.body) {
|
||||||
|
console.error('Document body not available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const notificationId = 'notification-' + Date.now();
|
||||||
|
const icons = {
|
||||||
|
success: `<svg class="w-6 h-6 text-green-600" 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>`,
|
||||||
|
error: `<svg class="w-6 h-6 text-red-600" 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>`,
|
||||||
|
info: `<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||||
|
</svg>`
|
||||||
|
};
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
success: 'bg-green-50 border-green-200',
|
||||||
|
error: 'bg-red-50 border-red-200',
|
||||||
|
info: 'bg-blue-50 border-blue-200'
|
||||||
|
};
|
||||||
|
|
||||||
|
const notificationHtml = `
|
||||||
|
<div class="fixed top-4 right-4 z-50 transform transition-all duration-300 translate-x-full opacity-0" id="${notificationId}">
|
||||||
|
<div class="max-w-sm w-full ${colors[type]} border rounded-lg shadow-lg">
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex items-start">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
${icons[type]}
|
||||||
|
</div>
|
||||||
|
<div class="mr-3 w-0 flex-1">
|
||||||
|
<p class="text-sm font-medium text-gray-900">${title}</p>
|
||||||
|
<p class="mt-1 text-sm text-gray-600">${message}</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4 flex-shrink-0 flex">
|
||||||
|
<button class="bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none" onclick="document.getElementById('${notificationId}').remove()">
|
||||||
|
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.insertAdjacentHTML('beforeend', notificationHtml);
|
||||||
|
|
||||||
|
// Animate in
|
||||||
|
setTimeout(() => {
|
||||||
|
const notification = document.getElementById(notificationId);
|
||||||
|
notification.classList.remove('translate-x-full', 'opacity-0');
|
||||||
|
notification.classList.add('translate-x-0', 'opacity-100');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// Auto remove
|
||||||
|
if (duration > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const notification = document.getElementById(notificationId);
|
||||||
|
if (notification) {
|
||||||
|
notification.classList.add('translate-x-full', 'opacity-0');
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.remove();
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.container.style.display = 'none';
|
||||||
|
this.container.innerHTML = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize dialog system after DOM is ready
|
||||||
|
let dialog;
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
dialog = new BeautifulDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to show dialogs with fallback
|
||||||
|
function showDialog(type, title, message, ...args) {
|
||||||
|
if (dialog && dialog[type]) {
|
||||||
|
dialog[type](title, message, ...args);
|
||||||
|
} else {
|
||||||
|
// Fallback to alert
|
||||||
|
alert(`${title}: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallet Connect Controller
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
console.log('Wallet Connect script loaded');
|
||||||
|
|
||||||
|
// Handle wallet type change
|
||||||
|
const walletTypeSelect = document.querySelector('[data-wallet-connect-target="walletType"]');
|
||||||
|
if (walletTypeSelect) {
|
||||||
|
walletTypeSelect.addEventListener('change', function() {
|
||||||
|
const connectBtn = document.querySelector('[data-wallet-connect-target="connectBtn"]');
|
||||||
|
if (this.value) {
|
||||||
|
connectBtn.disabled = false;
|
||||||
|
connectBtn.textContent = 'اتصال کیف پول';
|
||||||
|
} else {
|
||||||
|
connectBtn.disabled = true;
|
||||||
|
connectBtn.textContent = 'نوع کیف پول را انتخاب کنید';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle toggle wallet status
|
||||||
|
document.addEventListener('click', function(event) {
|
||||||
|
if (event.target.closest('.wallet-toggle-btn')) {
|
||||||
|
event.preventDefault();
|
||||||
|
const button = event.target.closest('.wallet-toggle-btn');
|
||||||
|
const walletId = button.dataset.walletId;
|
||||||
|
console.log('Toggle wallet status clicked, ID:', walletId);
|
||||||
|
|
||||||
|
if (!walletId) {
|
||||||
|
showDialog('showError', 'خطا', 'شناسه کیف پول یافت نشد');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isActive = button.textContent.includes('غیرفعال');
|
||||||
|
const action = isActive ? 'غیرفعال' : 'فعال';
|
||||||
|
|
||||||
|
showDialog('showConfirm',
|
||||||
|
'تأیید تغییر وضعیت',
|
||||||
|
`آیا مطمئن هستید که میخواهید این کیف پول را ${action} کنید؟`,
|
||||||
|
'تأیید',
|
||||||
|
'انصراف',
|
||||||
|
() => {
|
||||||
|
// Show loading
|
||||||
|
button.disabled = true;
|
||||||
|
button.innerHTML = '<i class="fas fa-spinner fa-spin ml-1"></i> در حال پردازش...';
|
||||||
|
|
||||||
|
fetch(`/api/wallet/${walletId}/toggle-status`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-Token': '{{ csrf_token('wallet_connect_form') }}'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
showDialog('showSuccess', 'موفقیت', data.message);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
showDialog('showError', 'خطا', data.message);
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = `<i class="fas fa-${isActive ? 'pause' : 'play'} ml-1"></i> ${isActive ? 'غیرفعال' : 'فعال'}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
showDialog('showError', 'خطا', 'خطا در تغییر وضعیت کیف پول: ' + error.message);
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = `<i class="fas fa-${isActive ? 'pause' : 'play'} ml-1"></i> ${isActive ? 'غیرفعال' : 'فعال'}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle delete wallet
|
||||||
|
document.addEventListener('click', function(event) {
|
||||||
|
if (event.target.closest('.wallet-delete-btn')) {
|
||||||
|
event.preventDefault();
|
||||||
|
const button = event.target.closest('.wallet-delete-btn');
|
||||||
|
const walletId = button.dataset.walletId;
|
||||||
|
console.log('Delete wallet clicked, ID:', walletId);
|
||||||
|
|
||||||
|
if (!walletId) {
|
||||||
|
showDialog('showError', 'خطا', 'شناسه کیف پول یافت نشد');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog('showConfirm',
|
||||||
|
'تأیید حذف کیف پول',
|
||||||
|
'آیا مطمئن هستید که میخواهید این کیف پول را حذف کنید؟ این عمل قابل بازگشت نیست.',
|
||||||
|
'حذف',
|
||||||
|
'انصراف',
|
||||||
|
() => {
|
||||||
|
// Show loading
|
||||||
|
button.disabled = true;
|
||||||
|
button.innerHTML = '<i class="fas fa-spinner fa-spin ml-1"></i> در حال حذف...';
|
||||||
|
|
||||||
|
fetch(`/api/wallet/${walletId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-Token': '{{ csrf_token('wallet_connect_form') }}'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
showDialog('showSuccess', 'موفقیت', data.message);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
showDialog('showError', 'خطا', data.message);
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = '<i class="fas fa-trash ml-1"></i> حذف';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
showDialog('showError', 'خطا', 'خطا در حذف کیف پول: ' + error.message);
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = '<i class="fas fa-trash ml-1"></i> حذف';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle set primary wallet
|
||||||
|
document.addEventListener('click', function(event) {
|
||||||
|
if (event.target.closest('.wallet-set-primary-btn')) {
|
||||||
|
event.preventDefault();
|
||||||
|
const button = event.target.closest('.wallet-set-primary-btn');
|
||||||
|
const walletId = button.dataset.walletId;
|
||||||
|
console.log('Set primary wallet clicked, ID:', walletId);
|
||||||
|
|
||||||
|
if (!walletId) {
|
||||||
|
showDialog('showError', 'خطا', 'شناسه کیف پول یافت نشد');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog('showConfirm',
|
||||||
|
'تنظیم کیف پول اصلی',
|
||||||
|
'آیا میخواهید این کیف پول را به عنوان کیف پول اصلی تنظیم کنید؟',
|
||||||
|
'تأیید',
|
||||||
|
'انصراف',
|
||||||
|
() => {
|
||||||
|
// Show loading
|
||||||
|
button.disabled = true;
|
||||||
|
button.innerHTML = '<i class="fas fa-spinner fa-spin ml-1"></i> در حال تنظیم...';
|
||||||
|
|
||||||
|
fetch(`/api/wallet/${walletId}/set-primary`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-Token': '{{ csrf_token('wallet_connect_form') }}'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
showDialog('showSuccess', 'موفقیت', data.message);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
showDialog('showError', 'خطا', data.message);
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = '<i class="fas fa-star ml-1"></i> اصلی';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
showDialog('showError', 'خطا', 'خطا در تنظیم کیف پول اصلی: ' + error.message);
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = '<i class="fas fa-star ml-1"></i> اصلی';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
<!-- فرم ایجاد سوال -->
|
<!-- فرم ایجاد سوال -->
|
||||||
<div class="bg-white rounded-2xl shadow-soft overflow-hidden">
|
<div class="bg-white rounded-2xl shadow-soft overflow-hidden">
|
||||||
<div class="p-8">
|
<div class="p-8">
|
||||||
{{ form_start(form, {'attr': {'novalidate': 'novalidate', 'class': 'space-y-8', 'data-turbo': 'false'}}) }}
|
{{ form_start(form, {'attr': {'novalidate': 'novalidate', 'class': 'space-y-8', 'data-turbo': 'false', 'enctype': 'multipart/form-data'}}) }}
|
||||||
|
|
||||||
<!-- عنوان سوال -->
|
<!-- عنوان سوال -->
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -98,6 +98,11 @@
|
||||||
تگها
|
تگها
|
||||||
<span class="text-red-500">*</span>
|
<span class="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<!-- فیلد مخفی Symfony برای تگها -->
|
||||||
|
{{ form_widget(form.tags, {'attr': {'class': 'hidden'}}) }}
|
||||||
|
{{ form_errors(form.tags) }}
|
||||||
|
|
||||||
<div class="border border-gray-300 rounded-xl p-6 bg-gradient-to-br from-gray-50 to-blue-50">
|
<div class="border border-gray-300 rounded-xl p-6 bg-gradient-to-br from-gray-50 to-blue-50">
|
||||||
<!-- تگهای انتخاب شده -->
|
<!-- تگهای انتخاب شده -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
|
|
@ -364,10 +369,37 @@ window.QuestionFormClasses.TagManager = class TagManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
this.loadExistingTags();
|
||||||
this.updateTagCount();
|
this.updateTagCount();
|
||||||
this.renderSelectedTags();
|
this.renderSelectedTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadExistingTags() {
|
||||||
|
// بارگذاری تگهای موجود از فرم Symfony
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
const tagsField = form.querySelector('select[name="question_form[tags][]"]') ||
|
||||||
|
form.querySelector('select[id*="tags"]');
|
||||||
|
if (!tagsField) return;
|
||||||
|
|
||||||
|
// اطمینان از اینکه فیلد multiple است
|
||||||
|
tagsField.multiple = true;
|
||||||
|
|
||||||
|
const selectedOptions = tagsField.querySelectorAll('option:checked');
|
||||||
|
selectedOptions.forEach(option => {
|
||||||
|
const tagId = option.value;
|
||||||
|
const tagName = option.textContent;
|
||||||
|
|
||||||
|
// بررسی اینکه تگ قبلاً اضافه نشده باشد
|
||||||
|
if (!this.selectedTags.find(tag => tag.id === tagId)) {
|
||||||
|
this.selectedTags.push({ id: tagId, name: tagName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Loaded existing tags:', this.selectedTags);
|
||||||
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
// حذف event listener های قبلی
|
// حذف event listener های قبلی
|
||||||
this.removeEventListeners();
|
this.removeEventListeners();
|
||||||
|
|
@ -518,7 +550,7 @@ window.QuestionFormClasses.TagManager = class TagManager {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
},
|
},
|
||||||
body: `name=${encodeURIComponent(tagName)}&_token={{ csrf_token('vote') }}`
|
body: `name=${encodeURIComponent(tagName)}&_token={{ csrf_token('question') }}`
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
|
@ -644,18 +676,36 @@ window.QuestionFormClasses.TagManager = class TagManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHiddenInput() {
|
updateHiddenInput() {
|
||||||
// حذف input های قبلی
|
const form = document.querySelector('form');
|
||||||
const existingInputs = document.querySelectorAll('input[name="question[tags][]"]');
|
if (!form) {
|
||||||
existingInputs.forEach(input => input.remove());
|
console.error('Form not found for updating hidden inputs');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// افزودن input های جدید
|
// پیدا کردن فیلد tags در فرم Symfony
|
||||||
|
const tagsField = form.querySelector('select[name="question_form[tags][]"]') ||
|
||||||
|
form.querySelector('select[id*="tags"]');
|
||||||
|
if (!tagsField) {
|
||||||
|
console.error('Tags field not found in form');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// اطمینان از اینکه فیلد multiple است
|
||||||
|
tagsField.multiple = true;
|
||||||
|
|
||||||
|
// پاک کردن گزینههای قبلی
|
||||||
|
tagsField.innerHTML = '';
|
||||||
|
|
||||||
|
// افزودن گزینههای جدید
|
||||||
this.selectedTags.forEach(tag => {
|
this.selectedTags.forEach(tag => {
|
||||||
const input = document.createElement('input');
|
const option = document.createElement('option');
|
||||||
input.type = 'hidden';
|
option.value = tag.id;
|
||||||
input.name = 'question[tags][]';
|
option.textContent = tag.name;
|
||||||
input.value = tag.id;
|
option.selected = true;
|
||||||
document.querySelector('form').appendChild(input);
|
tagsField.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('Updated tags field with', this.selectedTags.length, 'tags');
|
||||||
}
|
}
|
||||||
|
|
||||||
filterSuggestions() {
|
filterSuggestions() {
|
||||||
|
|
@ -794,16 +844,22 @@ window.QuestionFormClasses.FileManager = class FileManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.fileInput = document.getElementById('file-input');
|
// پیدا کردن input file - ممکن است multiple باشد
|
||||||
|
this.fileInput = document.querySelector('input[type="file"][name*="attachments"]');
|
||||||
this.selectedFilesDiv = document.getElementById('selected-files');
|
this.selectedFilesDiv = document.getElementById('selected-files');
|
||||||
this.fileListDiv = document.getElementById('file-list');
|
this.fileListDiv = document.getElementById('file-list');
|
||||||
|
|
||||||
if (!this.fileInput) return;
|
if (!this.fileInput) {
|
||||||
|
console.error('File input not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
|
if (!this.fileInput) return;
|
||||||
|
|
||||||
this.fileInput.addEventListener('change', (e) => this.handleFileSelect(e));
|
this.fileInput.addEventListener('change', (e) => this.handleFileSelect(e));
|
||||||
|
|
||||||
// Drag and drop
|
// Drag and drop
|
||||||
|
|
@ -845,6 +901,11 @@ window.QuestionFormClasses.FileManager = class FileManager {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.updateFileInput();
|
this.updateFileInput();
|
||||||
|
|
||||||
|
// اطمینان از اینکه فایلها در input قرار گرفتهاند
|
||||||
|
setTimeout(() => {
|
||||||
|
this.updateFileInput();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
validateFile(file) {
|
validateFile(file) {
|
||||||
|
|
@ -886,6 +947,9 @@ window.QuestionFormClasses.FileManager = class FileManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.renderFiles();
|
this.renderFiles();
|
||||||
|
|
||||||
|
// اطمینان از اینکه فایل به input اضافه شده است
|
||||||
|
this.updateFileInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFile(fileId) {
|
removeFile(fileId) {
|
||||||
|
|
@ -950,12 +1014,27 @@ window.QuestionFormClasses.FileManager = class FileManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFileInput() {
|
updateFileInput() {
|
||||||
// ایجاد DataTransfer object جدید
|
// بررسی وجود fileInput
|
||||||
const dt = new DataTransfer();
|
if (!this.fileInput) {
|
||||||
this.selectedFiles.forEach(fileData => {
|
console.error('File input is null, cannot update files');
|
||||||
dt.items.add(fileData.file);
|
return;
|
||||||
});
|
}
|
||||||
this.fileInput.files = dt.files;
|
|
||||||
|
// تنظیم فایلها در input اصلی
|
||||||
|
try {
|
||||||
|
const dt = new DataTransfer();
|
||||||
|
this.selectedFiles.forEach(fileData => {
|
||||||
|
dt.items.add(fileData.file);
|
||||||
|
});
|
||||||
|
this.fileInput.files = dt.files;
|
||||||
|
|
||||||
|
console.log('Updated file input with', this.selectedFiles.length, 'files');
|
||||||
|
console.log('File input files:', this.fileInput.files.length);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('DataTransfer not supported:', e);
|
||||||
|
// در صورت عدم پشتیبانی، فایلها را در متغیر سراسری ذخیره میکنیم
|
||||||
|
window.selectedFiles = this.selectedFiles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1023,6 +1102,7 @@ function initializeQuestionForm() {
|
||||||
function handleFormSubmit(e) {
|
function handleFormSubmit(e) {
|
||||||
// اطمینان از اینکه تگها به فرم اضافه شدهاند
|
// اطمینان از اینکه تگها به فرم اضافه شدهاند
|
||||||
if (window.questionFormManager.tagManager) {
|
if (window.questionFormManager.tagManager) {
|
||||||
|
// ابتدا تگها را به فرم اضافه کن
|
||||||
window.questionFormManager.tagManager.updateHiddenInput();
|
window.questionFormManager.tagManager.updateHiddenInput();
|
||||||
|
|
||||||
// اعتبارسنجی تگها
|
// اعتبارسنجی تگها
|
||||||
|
|
@ -1033,11 +1113,24 @@ function handleFormSubmit(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// اطمینان از اینکه فایلها به فرم اضافه شدهاند
|
||||||
|
if (window.questionFormManager.fileManager) {
|
||||||
|
window.questionFormManager.fileManager.updateFileInput();
|
||||||
|
}
|
||||||
|
|
||||||
// غیرفعال کردن Turbo برای این فرم
|
// غیرفعال کردن Turbo برای این فرم
|
||||||
const form = e.target;
|
const form = e.target;
|
||||||
if (form) {
|
if (form) {
|
||||||
form.setAttribute('data-turbo', 'false');
|
form.setAttribute('data-turbo', 'false');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileInput = form.querySelector('input[type="file"]');
|
||||||
|
console.log('Form submitted with files:', fileInput ? fileInput.files.length : 0);
|
||||||
|
|
||||||
|
// بررسی فایلهای انتخاب شده در JavaScript
|
||||||
|
if (window.questionFormManager.fileManager) {
|
||||||
|
console.log('Selected files in JavaScript:', window.questionFormManager.fileManager.selectedFiles.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// اجرا در بارگذاری صفحه
|
// اجرا در بارگذاری صفحه
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue