remove bootstrap
Some checks are pending
PHP Composer / build (push) Waiting to run

This commit is contained in:
Hesabix 2025-09-05 11:52:08 +03:30
parent 06a2fb398d
commit 1467e83ccf
20 changed files with 4867 additions and 2666 deletions

View file

@ -7,9 +7,327 @@ import './bootstrap.js';
*/
// any CSS you import will output into a single css file (app.css in this case)
import './styles/global.scss';
import './styles/tailwind.css';
import './styles/app.css';
// Import Bootstrap and make it globally available
import * as bootstrap from 'bootstrap';
window.bootstrap = bootstrap;
// Notification System
class NotificationSystem {
constructor() {
this.container = null;
this.init();
}
init() {
// Create notification container if it doesn't exist
this.container = document.getElementById('notification-container');
if (!this.container) {
this.container = document.createElement('div');
this.container.id = 'notification-container';
this.container.className = 'notification-container';
document.body.appendChild(this.container);
}
}
show(message, type = 'info', title = '', duration = 5000) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
const icon = this.getIcon(type);
const closeIcon = this.getCloseIcon();
notification.innerHTML = `
<div class="notification-icon">${icon}</div>
<div class="notification-content">
${title ? `<div class="notification-title">${title}</div>` : ''}
<div class="notification-message">${message}</div>
</div>
<button class="notification-close" onclick="this.parentElement.remove()">${closeIcon}</button>
<div class="notification-progress" style="width: 100%; animation: progress ${duration}ms linear forwards;"></div>
`;
// Add progress bar animation
const style = document.createElement('style');
style.textContent = `
@keyframes progress {
from { width: 100%; }
to { width: 0%; }
}
`;
document.head.appendChild(style);
this.container.appendChild(notification);
// Trigger animation
setTimeout(() => {
notification.classList.add('show');
}, 10);
// Auto remove
if (duration > 0) {
setTimeout(() => {
this.remove(notification);
}, duration);
}
return notification;
}
getIcon(type) {
const icons = {
success: `<svg 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 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>`,
warning: `<svg 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>`,
info: `<svg 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>`
};
return icons[type] || icons.info;
}
getCloseIcon() {
return `<svg width="16" height="16" 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>`;
}
remove(notification) {
if (notification && notification.parentElement) {
notification.classList.remove('show');
setTimeout(() => {
if (notification.parentElement) {
notification.remove();
}
}, 300);
}
}
success(message, title = 'موفقیت') {
return this.show(message, 'success', title);
}
error(message, title = 'خطا') {
return this.show(message, 'error', title);
}
warning(message, title = 'هشدار') {
return this.show(message, 'warning', title);
}
info(message, title = 'اطلاعات') {
return this.show(message, 'info', title);
}
}
// Initialize notification system
const notification = new NotificationSystem();
// Make notification available globally
window.notification = notification;
// Test notification system
setTimeout(() => {
if (window.notification) {
console.log('Notification system initialized successfully');
}
}, 1000);
// Blog functionality
document.addEventListener('DOMContentLoaded', function() {
// Blog search enhancement
const searchInput = document.querySelector('input[name="search"]');
if (searchInput) {
searchInput.addEventListener('focus', function() {
this.classList.add('blog-search-input');
});
searchInput.addEventListener('blur', function() {
this.classList.remove('blog-search-input');
});
}
// Blog card hover effects
const blogCards = document.querySelectorAll('.group.bg-white.rounded-2xl');
blogCards.forEach(card => {
card.classList.add('blog-card-hover');
});
// Pagination hover effects
const paginationItems = document.querySelectorAll('nav a, nav span');
paginationItems.forEach(item => {
item.classList.add('pagination-item');
});
// Smooth scroll for anchor links
const anchorLinks = document.querySelectorAll('a[href^="#"]');
anchorLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// Lazy loading for images
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('blog-loading');
img.classList.add('opacity-100');
observer.unobserve(img);
}
});
});
images.forEach(img => {
img.classList.add('blog-loading', 'opacity-0');
imageObserver.observe(img);
});
// Reading progress bar for blog posts
const blogPost = document.querySelector('main.min-h-screen.bg-gray-50');
if (blogPost) {
const progressBar = document.createElement('div');
progressBar.className = 'fixed top-0 left-0 w-0 h-1 bg-gradient-to-r from-blue-500 to-purple-500 z-50 transition-all duration-300';
document.body.appendChild(progressBar);
window.addEventListener('scroll', function() {
const scrollTop = window.pageYOffset;
const docHeight = document.body.scrollHeight - window.innerHeight;
const scrollPercent = (scrollTop / docHeight) * 100;
progressBar.style.width = scrollPercent + '%';
});
}
// Copy code blocks functionality
const codeBlocks = document.querySelectorAll('pre code');
codeBlocks.forEach(block => {
const copyButton = document.createElement('button');
copyButton.className = 'absolute top-2 left-2 px-2 py-1 text-xs bg-gray-800 text-white rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200';
copyButton.textContent = 'کپی';
const wrapper = document.createElement('div');
wrapper.className = 'relative group';
wrapper.style.position = 'relative';
block.parentNode.insertBefore(wrapper, block);
wrapper.appendChild(block);
wrapper.appendChild(copyButton);
copyButton.addEventListener('click', function() {
navigator.clipboard.writeText(block.textContent).then(() => {
copyButton.textContent = 'کپی شد!';
setTimeout(() => {
copyButton.textContent = 'کپی';
}, 2000);
});
});
});
// Q&A functionality
// Add hover effects to Q&A cards
const qaCards = document.querySelectorAll('.bg-white.rounded-2xl.shadow-soft');
qaCards.forEach(card => {
card.classList.add('qa-card-hover');
});
// Add hover effects to Q&A tags
const qaTags = document.querySelectorAll('.inline-flex.items-center.px-3.py-1');
qaTags.forEach(tag => {
tag.classList.add('qa-tag-hover');
});
// Add focus effects to Q&A form inputs
const qaInputs = document.querySelectorAll('input, textarea, select');
qaInputs.forEach(input => {
input.classList.add('qa-form-focus');
});
// Add hover effects to Q&A pagination
const qaPaginationItems = document.querySelectorAll('nav a, nav span');
qaPaginationItems.forEach(item => {
item.classList.add('qa-pagination-item');
});
// Add stagger animation to Q&A cards
qaCards.forEach((card, index) => {
card.classList.add('qa-fade-in-up');
if (index < 4) {
card.classList.add(`qa-stagger-${index + 1}`);
}
});
// Q&A search enhancement
const qaSearchInput = document.querySelector('input[name="search"]');
if (qaSearchInput) {
qaSearchInput.addEventListener('focus', function() {
this.parentElement.classList.add('ring-2', 'ring-blue-500');
});
qaSearchInput.addEventListener('blur', function() {
this.parentElement.classList.remove('ring-2', 'ring-blue-500');
});
}
// Q&A filter enhancement
const qaFilterSelect = document.querySelector('select[name="filter"]');
if (qaFilterSelect) {
qaFilterSelect.addEventListener('change', function() {
this.classList.add('bg-blue-50', 'border-blue-300');
setTimeout(() => {
this.classList.remove('bg-blue-50', 'border-blue-300');
}, 300);
});
}
// Q&A vote button enhancement
const qaVoteButtons = document.querySelectorAll('.vote-btn');
qaVoteButtons.forEach(button => {
button.classList.add('qa-vote-btn');
});
// Q&A tag selection enhancement
const qaTagSuggestions = document.querySelectorAll('.tag-suggestion');
qaTagSuggestions.forEach(tag => {
tag.addEventListener('click', function() {
this.classList.toggle('qa-tag-selected');
this.classList.toggle('qa-tag-unselected');
});
});
// Q&A answer acceptance enhancement
const qaAcceptButtons = document.querySelectorAll('.accept-answer-btn');
qaAcceptButtons.forEach(button => {
button.addEventListener('click', function() {
this.classList.add('bg-green-200', 'text-green-800');
setTimeout(() => {
this.classList.remove('bg-green-200', 'text-green-800');
}, 1000);
});
});
// Q&A loading states
const qaForms = document.querySelectorAll('form');
qaForms.forEach(form => {
form.addEventListener('submit', function() {
const submitButton = this.querySelector('button[type="submit"]');
if (submitButton) {
submitButton.classList.add('qa-loading-shimmer');
submitButton.disabled = true;
}
});
});
});

View file

@ -1,5 +1,6 @@
/* Custom styles for Hesabix */
body, .lead {
font-family: "Yekan Bakh FaNum";
font-family: "Yekan Bakh FaNum", "Tahoma", "Arial", sans-serif;
font-feature-settings: "kern" on, "liga" on, "dlig" on;
-moz-font-feature-settings: "kern" on, "liga" on, "dlig" on;
-webkit-font-feature-settings: "kern" on, "liga" on, "dlig" on;
@ -7,118 +8,679 @@ body, .lead {
-o-font-feature-settings: "kern" on, "liga" on, "dlig" on;
}
/* استایل‌های منوی ناوبری */
.navbar {
padding: 0.8rem 0;
}
.navbar-brand {
font-size: 1.4rem;
}
.nav-link {
position: relative;
color: #495057;
font-weight: 500;
transition: all 0.3s ease;
}
.nav-link:hover {
color: #1743bb;
background-color: rgba(23, 67, 187, 0.05);
}
.nav-link.active {
color: #1743bb;
background-color: rgba(23, 67, 187, 0.1);
}
.btn-primary {
transition: all 0.3s ease;
box-shadow: 0 2px 5px rgba(23, 67, 187, 0.2);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(23, 67, 187, 0.3);
}
.transition-all {
transition: all 0.3s ease;
/* RTL Support */
[dir="rtl"] {
direction: rtl;
text-align: right;
}
/* Custom utility classes */
.rul {
text-decoration: none;
}
.accordion-button:after {
margin-right: auto;
margin-left: 0;
}
/* Code blocks */
pre {
border-radius: 0.5rem;
direction: ltr;
background-color: #d6d6df;
background-color: #f3f4f6;
padding: 0.9rem 0.8rem;
color: blue;
color: #1f2937;
font-family: 'Courier New', monospace;
}
/* استایل‌های RTL برای Modal */
#walletModal {
direction: rtl;
text-align: right;
/* Trust seals animation */
.trust-seal-loading {
animation: pulse 1.5s ease-in-out infinite;
}
#walletModal .modal-content {
font-family: 'Yekan Bakh FaNum', 'Tahoma', 'Arial', sans-serif;
direction: rtl;
text-align: right;
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
#walletModal .modal-header {
direction: rtl;
text-align: right;
border-bottom: 1px solid #dee2e6;
/* Trust seals content */
.trust-seals-content img {
transition: opacity 0.3s ease-in-out;
max-height: 50px;
width: auto;
}
#walletModal .modal-title {
direction: rtl;
text-align: right;
font-family: 'Yekan Bakh FaNum', 'Tahoma', 'Arial', sans-serif;
.trust-seals-content img:hover {
opacity: 0.8;
transform: scale(1.05);
}
/* Q&A specific styles */
.qa-card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.qa-card-hover:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.15);
}
/* Tag System Styles */
.tag-suggestion {
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.tag-suggestion::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.tag-suggestion:hover::before {
left: 100%;
}
.tag-suggestion:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.selected-tags {
transition: all 0.3s ease;
}
.selected-tags:empty {
border-color: #e5e7eb;
}
.selected-tags:not(:empty) {
border-color: #3b82f6;
background-color: #f8fafc;
}
.tag-remove {
transition: all 0.2s ease;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.tag-remove:hover {
background-color: rgba(239, 68, 68, 0.1);
transform: scale(1.1);
}
.tag-remove:active {
transform: scale(0.95);
}
/* Tag Input Styles */
#tag-input:focus {
transform: translateY(-1px);
box-shadow: 0 8px 25px -5px rgba(59, 130, 246, 0.3);
}
#add-tag-btn {
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
#add-tag-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
#add-tag-btn:hover::before {
left: 100%;
}
#add-tag-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
#add-tag-btn:active {
transform: translateY(0);
}
/* Loading Spinner for Tags */
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid #e5e7eb;
border-top: 2px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* Tag Count Animation */
#tag-count {
transition: all 0.3s ease;
font-weight: 500;
}
/* Tag Suggestions Container */
#tag-suggestions {
max-height: 200px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #cbd5e0 #f7fafc;
}
#tag-suggestions::-webkit-scrollbar {
width: 6px;
}
#tag-suggestions::-webkit-scrollbar-track {
background: #f7fafc;
border-radius: 3px;
}
#tag-suggestions::-webkit-scrollbar-thumb {
background: #cbd5e0;
border-radius: 3px;
}
#tag-suggestions::-webkit-scrollbar-thumb:hover {
background: #a0aec0;
}
/* Tag Selection Animation */
.tag-suggestion.selected {
animation: tagSelect 0.3s ease;
}
@keyframes tagSelect {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
/* Tag Validation States */
.tag-container.valid {
border-color: #10b981;
background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%);
}
.tag-container.invalid {
border-color: #ef4444;
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
}
/* Responsive Tag System */
@media (max-width: 640px) {
.selected-tags {
min-height: 60px;
}
.tag-suggestion {
font-size: 0.875rem;
padding: 0.5rem 0.75rem;
}
#tag-input {
font-size: 0.875rem;
padding: 0.75rem;
}
#add-tag-btn {
padding: 0.75rem 1rem;
font-size: 0.875rem;
}
}
.qa-vote-btn {
transition: all 0.2s ease-in-out;
}
.qa-vote-btn:hover {
transform: scale(1.1);
}
.qa-vote-btn:active {
transform: scale(0.95);
}
.qa-tag-hover {
transition: all 0.2s ease-in-out;
}
.qa-tag-hover:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.qa-form-focus {
transition: all 0.2s ease-in-out;
}
.qa-form-focus:focus {
transform: translateY(-1px);
box-shadow: 0 8px 25px -5px rgba(59, 130, 246, 0.3);
}
.qa-accepted-answer {
background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%);
border-left: 4px solid #10b981;
}
.qa-question-stats {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
}
.qa-editor-toolbar {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-bottom: 1px solid #e2e8f0;
}
.qa-editor-toolbar button {
transition: all 0.2s ease-in-out;
}
.qa-editor-toolbar button:hover {
background-color: #e2e8f0;
transform: translateY(-1px);
}
.qa-editor-toolbar button:active {
transform: translateY(0);
}
.qa-tag-selected {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
color: white;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.qa-tag-unselected {
background: white;
color: #374151;
border: 1px solid #d1d5db;
}
.qa-tag-unselected:hover {
background: #f3f4f6;
border-color: #9ca3af;
}
.qa-pagination-item {
transition: all 0.2s ease-in-out;
}
.qa-pagination-item:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.qa-loading-shimmer {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: qa-shimmer 1.5s infinite;
}
@keyframes qa-shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
.qa-fade-in-up {
animation: qa-fadeInUp 0.6s ease-out;
}
@keyframes qa-fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.qa-stagger-1 { animation-delay: 0.1s; }
.qa-stagger-2 { animation-delay: 0.2s; }
.qa-stagger-3 { animation-delay: 0.3s; }
.qa-stagger-4 { animation-delay: 0.4s; }
/* Notification System */
.notification-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
max-width: 400px;
}
.notification {
background: white;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
margin-bottom: 12px;
padding: 16px 20px;
display: flex;
align-items: center;
gap: 12px;
transform: translateX(100%);
opacity: 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border-left: 4px solid;
min-width: 300px;
}
.notification.show {
transform: translateX(0);
opacity: 1;
}
.notification.success {
border-left-color: #10b981;
background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%);
}
.notification.error {
border-left-color: #ef4444;
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
}
.notification.warning {
border-left-color: #f59e0b;
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
}
.notification.info {
border-left-color: #3b82f6;
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
}
.notification-icon {
width: 24px;
height: 24px;
flex-shrink: 0;
}
.notification-content {
flex: 1;
}
.notification-title {
font-weight: 600;
font-size: 14px;
margin-bottom: 4px;
color: #1f2937;
}
#walletModal .btn-close {
margin-left: 0;
margin-right: auto;
order: -1;
.notification-message {
font-size: 13px;
color: #6b7280;
line-height: 1.4;
}
#walletModal .modal-body {
direction: rtl;
.notification-close {
background: none;
border: none;
cursor: pointer;
padding: 4px;
border-radius: 4px;
color: #9ca3af;
transition: all 0.2s ease;
flex-shrink: 0;
}
.notification-close:hover {
background: rgba(0, 0, 0, 0.1);
color: #374151;
}
.notification-progress {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background: currentColor;
border-radius: 0 0 12px 12px;
transition: width linear;
}
/* Loading Spinner */
.loading-spinner {
width: 20px;
height: 20px;
border: 2px solid #e5e7eb;
border-top: 2px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Blog specific styles */
.blog-card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.blog-card-hover:hover {
transform: translateY(-8px);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
/* Improved prose styling for blog content */
.prose {
color: #374151;
max-width: none;
}
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
color: #1f2937;
font-weight: 700;
margin-top: 2rem;
margin-bottom: 1rem;
}
.prose h1 {
font-size: 2.25rem;
line-height: 1.2;
}
.prose h2 {
font-size: 1.875rem;
line-height: 1.3;
}
.prose h3 {
font-size: 1.5rem;
line-height: 1.4;
}
.prose p {
margin-bottom: 1.5rem;
line-height: 1.8;
}
.prose ul, .prose ol {
margin-bottom: 1.5rem;
padding-right: 1.5rem;
}
.prose li {
margin-bottom: 0.5rem;
}
.prose blockquote {
border-right: 4px solid #3b82f6;
background-color: #f8fafc;
padding: 1.5rem;
margin: 2rem 0;
border-radius: 0.5rem;
font-style: italic;
color: #4b5563;
}
.prose code {
background-color: #f1f5f9;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.875rem;
color: #e11d48;
font-family: 'Courier New', monospace;
}
.prose pre {
background-color: #1f2937;
color: #f9fafb;
padding: 1.5rem;
border-radius: 0.75rem;
overflow-x: auto;
margin: 2rem 0;
}
.prose pre code {
background-color: transparent;
padding: 0;
color: inherit;
}
.prose img {
border-radius: 0.75rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
margin: 2rem 0;
}
.prose a {
color: #3b82f6;
text-decoration: none;
font-weight: 500;
transition: color 0.2s ease;
}
.prose a:hover {
color: #1d4ed8;
text-decoration: underline;
}
.prose table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.prose th, .prose td {
padding: 0.75rem 1rem;
text-align: right;
font-family: 'Yekan Bakh FaNum', 'Tahoma', 'Arial', sans-serif;
border-bottom: 1px solid #e5e7eb;
}
#walletModal .modal-footer {
direction: rtl;
text-align: right;
border-top: 1px solid #dee2e6;
.prose th {
background-color: #f9fafb;
font-weight: 600;
color: #374151;
}
#walletModal .modal-footer .btn {
font-family: 'Yekan Bakh FaNum', 'Tahoma', 'Arial', sans-serif;
direction: rtl;
.prose tr:hover {
background-color: #f9fafb;
}
#walletModal .alert {
direction: rtl;
text-align: right;
font-family: 'Yekan Bakh FaNum', 'Tahoma', 'Arial', sans-serif;
/* Blog search improvements */
.blog-search-input {
transition: all 0.3s ease;
}
/* استایل‌های backdrop برای RTL */
.modal-backdrop {
direction: rtl;
.blog-search-input:focus {
transform: translateY(-2px);
box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.1);
}
/* Pagination improvements */
.pagination-item {
transition: all 0.2s ease;
}
.pagination-item:hover {
transform: translateY(-1px);
}
/* Loading states */
.blog-loading {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
/* Responsive improvements */
@media (max-width: 640px) {
.prose {
font-size: 0.875rem;
}
.prose h1 {
font-size: 1.875rem;
}
.prose h2 {
font-size: 1.5rem;
}
.prose h3 {
font-size: 1.25rem;
}
}
/* Dark mode support (if needed in future) */
@media (prefers-color-scheme: dark) {
.prose {
color: #d1d5db;
}
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
color: #f9fafb;
}
.prose blockquote {
background-color: #374151;
color: #d1d5db;
}
.prose code {
background-color: #374151;
color: #fbbf24;
}
}

View file

@ -1,11 +0,0 @@
// assets/styles/global.scss
// customize some Bootstrap variables
$primary: #1743bb;
$link-hover-decoration: none;
$accordion-button-bg: rgb(248, 249, 250);
$accordion-bg: rgb(248, 249, 250);
$accordion-button-active-bg: rgb(248, 249, 250);
$accordion-padding-y: 0.6rem;
// the ~ allows you to reference things in node_modules
@import "~bootstrap/scss/bootstrap";

403
assets/styles/tailwind.css Normal file
View file

@ -0,0 +1,403 @@
@import "tailwindcss";
/* RTL Support */
[dir="rtl"] {
direction: rtl;
text-align: right;
}
/* Custom components */
.btn-primary {
background-color: #1743bb;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-weight: 500;
transition: all 0.3s ease;
display: inline-block;
text-decoration: none;
border: none;
cursor: pointer;
}
.btn-primary:hover {
background-color: #0d47a1;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(23, 67, 187, 0.3);
}
.btn-outline-primary {
border: 1px solid #1743bb;
color: #1743bb;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-weight: 500;
transition: all 0.3s ease;
display: inline-block;
text-decoration: none;
background: transparent;
cursor: pointer;
}
.btn-outline-primary:hover {
background-color: #1743bb;
color: white;
}
.card {
background: white;
border-radius: 0.5rem;
box-shadow: 0 2px 15px -3px rgba(0, 0, 0, 0.07), 0 10px 20px -2px rgba(0, 0, 0, 0.04);
padding: 1.5rem;
}
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 0;
border-bottom: 1px solid #e5e7eb;
background-color: #f9fafb;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.nav-link {
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
color: #6b7280;
font-weight: 500;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
}
.nav-link:hover {
color: #1743bb;
background-color: rgba(23, 67, 187, 0.05);
}
.dropdown-menu {
position: absolute;
right: 0;
margin-top: 0.5rem;
width: 12rem;
background: white;
border-radius: 0.5rem;
box-shadow: 0 4px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
border: 1px solid #e5e7eb;
padding: 0.5rem 0;
z-index: 50;
}
.dropdown-item {
display: block;
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: #374151;
transition: background-color 0.2s ease;
text-decoration: none;
}
.dropdown-item:hover {
background-color: #f3f4f6;
}
.form-control {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
outline: none;
transition: all 0.2s ease;
}
.form-control:focus {
outline: none;
ring: 2px;
ring-color: #1743bb;
border-color: transparent;
}
.form-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
margin-bottom: 0.5rem;
}
.alert {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
border: 1px solid;
}
.alert-success {
background-color: #f0fdf4;
border-color: #bbf7d0;
color: #166534;
}
.alert-danger {
background-color: #fef2f2;
border-color: #fecaca;
color: #991b1b;
}
.alert-warning {
background-color: #fffbeb;
border-color: #fed7aa;
color: #92400e;
}
.alert-info {
background-color: #eff6ff;
border-color: #bfdbfe;
color: #1e40af;
}
.spinner-border {
display: inline-block;
width: 1rem;
height: 1rem;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.spinner-border-sm {
width: 0.75rem;
height: 0.75rem;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.modal {
position: fixed;
inset: 0;
z-index: 50;
overflow-y: auto;
}
.modal-backdrop {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
position: relative;
background: white;
border-radius: 0.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
max-width: 32rem;
margin: 5rem auto;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem;
border-bottom: 1px solid #e5e7eb;
}
.modal-body {
padding: 1.5rem;
}
.modal-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.75rem;
padding: 1.5rem;
border-top: 1px solid #e5e7eb;
}
.btn-close {
width: 1.5rem;
height: 1.5rem;
background: transparent;
border: none;
color: #9ca3af;
cursor: pointer;
outline: none;
}
.btn-close:hover {
color: #6b7280;
}
.btn-close::before {
content: "×";
font-size: 1.25rem;
font-weight: bold;
}
/* Custom utilities for RTL */
.text-primary {
color: #1743bb;
}
.bg-primary {
background-color: #1743bb;
}
.border-primary {
border-color: #1743bb;
}
.hover\:bg-primary:hover {
background-color: #1743bb;
}
.hover\:text-primary:hover {
color: #1743bb;
}
.focus\:ring-primary:focus {
--tw-ring-color: #1743bb;
}
/* انیمیشن‌های اضافی */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInLeft {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeInRight {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
/* کلاس‌های انیمیشن */
.animate-fade-in-up {
animation: fadeInUp 0.6s ease-out;
}
.animate-fade-in-left {
animation: fadeInLeft 0.6s ease-out;
}
.animate-fade-in-right {
animation: fadeInRight 0.6s ease-out;
}
.animate-scale-in {
animation: scaleIn 0.6s ease-out;
}
/* بهبود خط‌بندی متن */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* بهبود hover effects */
.hover-lift {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.hover-lift:hover {
transform: translateY(-4px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* بهبود gradient text */
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* بهبود backdrop blur */
.backdrop-blur-custom {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* بهبود responsive spacing */
@media (max-width: 640px) {
.container {
padding-left: 1rem;
padding-right: 1rem;
}
}
/* بهبود focus states */
.focus-ring {
transition: all 0.2s ease;
}
.focus-ring:focus {
outline: none;
ring: 2px;
ring-color: #3b82f6;
ring-offset: 2px;
}
/* بهبود loading states */
.loading-shimmer {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}

834
package-lock.json generated
View file

@ -10,25 +10,41 @@
"@babel/preset-env": "^7.16.0",
"@hotwired/stimulus": "^3.0.0",
"@hotwired/turbo": "^7.1.1 || ^8.0",
"@popperjs/core": "^2.11.8",
"@symfony/stimulus-bridge": "^3.2.0",
"@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets",
"@symfony/webpack-encore": "^5.0.0",
"autoprefixer": "^10.4.20",
"bootstrap": "^5.3.3",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/typography": "^0.5.16",
"autoprefixer": "^10.4.21",
"core-js": "^3.38.0",
"css-loader": "^7.1.2",
"postcss": "^8.5.6",
"postcss-loader": "^8.1.1",
"regenerator-runtime": "^0.13.9",
"sass": "^1.83.1",
"sass-loader": "^16.0.4",
"style-loader": "^4.0.0",
"tailwindcss": "^4.1.13",
"webpack": "^5.74.0",
"webpack-cli": "^5.1.0",
"webpack-dev-server": "^5.2.2",
"webpack-notifier": "^1.15.0"
}
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@ -1584,6 +1600,19 @@
"node": ">= 14"
}
},
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
"dev": true,
"license": "ISC",
"dependencies": {
"minipass": "^7.0.4"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@jest/schemas": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
@ -1706,6 +1735,17 @@
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/remapping": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@ -1738,9 +1778,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
@ -2211,17 +2251,6 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@ -2432,6 +2461,345 @@
"webpack": "^5.0.0"
}
},
"node_modules/@tailwindcss/forms": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
"integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==",
"dev": true,
"license": "MIT",
"dependencies": {
"mini-svg-data-uri": "^1.2.3"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1"
}
},
"node_modules/@tailwindcss/node": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
"integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"enhanced-resolve": "^5.18.3",
"jiti": "^2.5.1",
"lightningcss": "1.30.1",
"magic-string": "^0.30.18",
"source-map-js": "^1.2.1",
"tailwindcss": "4.1.13"
}
},
"node_modules/@tailwindcss/node/node_modules/jiti": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
"integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/@tailwindcss/oxide": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz",
"integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.4",
"tar": "^7.4.3"
},
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@tailwindcss/oxide-android-arm64": "4.1.13",
"@tailwindcss/oxide-darwin-arm64": "4.1.13",
"@tailwindcss/oxide-darwin-x64": "4.1.13",
"@tailwindcss/oxide-freebsd-x64": "4.1.13",
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13",
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.13",
"@tailwindcss/oxide-linux-arm64-musl": "4.1.13",
"@tailwindcss/oxide-linux-x64-gnu": "4.1.13",
"@tailwindcss/oxide-linux-x64-musl": "4.1.13",
"@tailwindcss/oxide-wasm32-wasi": "4.1.13",
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.13",
"@tailwindcss/oxide-win32-x64-msvc": "4.1.13"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz",
"integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz",
"integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz",
"integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz",
"integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz",
"integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz",
"integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz",
"integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz",
"integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz",
"integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz",
"integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util",
"@emnapi/wasi-threads",
"tslib"
],
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.4.5",
"@emnapi/runtime": "^1.4.5",
"@emnapi/wasi-threads": "^1.0.4",
"@napi-rs/wasm-runtime": "^0.2.12",
"@tybys/wasm-util": "^0.10.0",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz",
"integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz",
"integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide/node_modules/detect-libc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/@tailwindcss/postcss": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz",
"integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"@tailwindcss/node": "4.1.13",
"@tailwindcss/oxide": "4.1.13",
"postcss": "^8.4.41",
"tailwindcss": "4.1.13"
}
},
"node_modules/@tailwindcss/typography": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
"integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
}
},
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
"version": "6.0.10",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@ -3122,9 +3490,9 @@
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
"version": "10.4.21",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
"dev": true,
"funding": [
{
@ -3142,11 +3510,11 @@
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"caniuse-lite": "^1.0.30001646",
"browserslist": "^4.24.4",
"caniuse-lite": "^1.0.30001702",
"fraction.js": "^4.3.7",
"normalize-range": "^0.1.2",
"picocolors": "^1.0.1",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
@ -3366,26 +3734,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/bootstrap": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
],
"license": "MIT",
"peerDependencies": {
"@popperjs/core": "^2.11.8"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
@ -3520,9 +3868,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001690",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
"integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
"version": "1.0.30001741",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz",
"integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==",
"dev": true,
"funding": [
{
@ -3571,6 +3919,16 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/chownr": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
}
},
"node_modules/chrome-trace-event": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
@ -4429,9 +4787,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5869,6 +6227,255 @@
"shell-quote": "^1.8.3"
}
},
"node_modules/lightningcss": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-darwin-arm64": "1.30.1",
"lightningcss-darwin-x64": "1.30.1",
"lightningcss-freebsd-x64": "1.30.1",
"lightningcss-linux-arm-gnueabihf": "1.30.1",
"lightningcss-linux-arm64-gnu": "1.30.1",
"lightningcss-linux-arm64-musl": "1.30.1",
"lightningcss-linux-x64-gnu": "1.30.1",
"lightningcss-linux-x64-musl": "1.30.1",
"lightningcss-win32-arm64-msvc": "1.30.1",
"lightningcss-win32-x64-msvc": "1.30.1"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
"integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
"integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
"integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
"integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
"integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
"integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
"integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
"integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
"integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss/node_modules/detect-libc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
@ -5937,6 +6544,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@ -5944,6 +6558,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -5951,6 +6572,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@ -5968,6 +6596,16 @@
"yallist": "^3.0.2"
}
},
"node_modules/magic-string": {
"version": "0.30.18",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
"integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -6172,6 +6810,16 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/mini-svg-data-uri": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
"dev": true,
"license": "MIT",
"bin": {
"mini-svg-data-uri": "cli.js"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@ -6179,6 +6827,45 @@
"dev": true,
"license": "ISC"
},
"node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minizlib": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
"dev": true,
"license": "MIT",
"dependencies": {
"minipass": "^7.1.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/mkdirp": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -6201,9 +6888,9 @@
}
},
"node_modules/nanoid": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@ -6557,9 +7244,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
@ -6577,7 +7264,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@ -8320,6 +9007,13 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/tailwindcss": {
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"dev": true,
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@ -8330,6 +9024,34 @@
"node": ">=6"
}
},
"node_modules/tar": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
"dev": true,
"license": "ISC",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
"minipass": "^7.1.2",
"minizlib": "^3.0.1",
"mkdirp": "^3.0.1",
"yallist": "^5.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/tar/node_modules/yallist": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
}
},
"node_modules/terser": {
"version": "5.37.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",

View file

@ -4,19 +4,22 @@
"@babel/preset-env": "^7.16.0",
"@hotwired/stimulus": "^3.0.0",
"@hotwired/turbo": "^7.1.1 || ^8.0",
"@popperjs/core": "^2.11.8",
"@symfony/stimulus-bridge": "^3.2.0",
"@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets",
"@symfony/webpack-encore": "^5.0.0",
"autoprefixer": "^10.4.20",
"bootstrap": "^5.3.3",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/typography": "^0.5.16",
"autoprefixer": "^10.4.21",
"core-js": "^3.38.0",
"css-loader": "^7.1.2",
"postcss": "^8.5.6",
"postcss-loader": "^8.1.1",
"regenerator-runtime": "^0.13.9",
"sass": "^1.83.1",
"sass-loader": "^16.0.4",
"style-loader": "^4.0.0",
"tailwindcss": "^4.1.13",
"webpack": "^5.74.0",
"webpack-cli": "^5.1.0",
"webpack-dev-server": "^5.2.2",

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
}

View file

@ -36,13 +36,6 @@ class GeneralController extends AbstractController
], $response);
}
#[Route('/reviews', name: 'app_reviews')]
public function app_reviews(EntityManagerInterface $em): Response
{
return $this->render('reviews/reviews.html.twig', [
'posts' => $em->getRepository(Post::class)->findBycat('blog', 3)
]);
}
#[Route('/professional-support', name: 'app_professional_support')]
public function app_professional_support(): Response

62
tailwind.config.js Normal file
View file

@ -0,0 +1,62 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./templates/**/*.html.twig",
"./assets/**/*.js",
"./src/**/*.php",
"./public/**/*.html"
],
theme: {
extend: {
fontFamily: {
'yekan': ['Yekan Bakh FaNum', 'Tahoma', 'Arial', 'sans-serif'],
},
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#1743bb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
secondary: {
50: '#f8fafc',
100: '#f1f5f9',
200: '#e2e8f0',
300: '#cbd5e1',
400: '#94a3b8',
500: '#64748b',
600: '#475569',
700: '#334155',
800: '#1e293b',
900: '#0f172a',
}
},
spacing: {
'18': '4.5rem',
'88': '22rem',
},
borderRadius: {
'4xl': '2rem',
},
boxShadow: {
'soft': '0 2px 15px -3px rgba(0, 0, 0, 0.07), 0 10px 20px -2px rgba(0, 0, 0, 0.04)',
'medium': '0 4px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
}
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
// RTL Support
corePlugins: {
// Disable default direction utilities
direction: false,
},
}

View file

@ -10,13 +10,12 @@
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-K1R1SYQY8E"></script>
<script>
window.dataLayer = window.dataLayer || [] ;
function gtag() {
dataLayer.push(arguments) ;
}
gtag('js', new Date()) ;
gtag('config', 'G-K1R1SYQY8E');
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-K1R1SYQY8E');
</script>
{% if block('des') is not defined %}
<meta content="{{twigFunctions.systemSettings.des}}" name="description"/>
@ -99,6 +98,111 @@ gtag('config', 'G-K1R1SYQY8E');
50% { opacity: 0.5; }
100% { opacity: 1; }
}
/* استایل‌های هدر جدید */
header {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* انیمیشن hover برای لینک‌ها */
.nav-link {
position: relative;
overflow: hidden;
}
.nav-link::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.1), transparent);
transition: left 0.5s;
}
.nav-link:hover::before {
left: 100%;
}
/* بهبود dropdown ها */
.group:hover .group-hover\:opacity-100 {
opacity: 1;
}
.group:hover .group-hover\:visible {
visibility: visible;
}
.group:hover .group-hover\:translate-y-0 {
transform: translateY(0);
}
/* انیمیشن برای دکمه‌ها */
.btn-primary {
position: relative;
overflow: hidden;
}
.btn-primary::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn-primary:hover::before {
left: 100%;
}
/* بهبود responsive */
@media (max-width: 1024px) {
header .container {
padding-left: 1rem;
padding-right: 1rem;
}
}
/* انیمیشن برای لوگو */
.logo-container {
transition: transform 0.3s ease;
}
.logo-container:hover {
transform: scale(1.05);
}
/* مدیریت responsive منو */
#desktopMenu {
display: none !important;
}
@media (min-width: 1024px) {
#desktopMenu {
display: flex !important;
}
/* مخفی کردن دکمه منوی موبایل در دسکتاپ */
#mobileMenuButton {
display: none !important;
}
}
@media (max-width: 1023px) {
#desktopMenu {
display: none !important;
}
/* نمایش دکمه منوی موبایل در موبایل */
#mobileMenuButton {
display: block !important;
}
}
</style>
{% endblock %}
@ -175,149 +279,266 @@ gtag('config', 'G-K1R1SYQY8E');
}
}, 3000);
});
// تابع toggle برای mobile menu
function toggleMobileMenu() {
const menu = document.getElementById('navbarSupportedContent');
if (menu.classList.contains('hidden')) {
menu.classList.remove('hidden');
} else {
menu.classList.add('hidden');
}
}
// مدیریت نمایش منو بر اساس اندازه صفحه
function handleResponsiveMenu() {
const mobileMenu = document.getElementById('navbarSupportedContent');
if (window.innerWidth >= 1024) {
// دسکتاپ - مخفی کردن منوی موبایل
if (mobileMenu) {
mobileMenu.classList.add('hidden');
}
}
}
// اجرا در بارگذاری صفحه
document.addEventListener('DOMContentLoaded', function() {
handleResponsiveMenu();
});
// اجرا در تغییر اندازه صفحه
window.addEventListener('resize', function() {
handleResponsiveMenu();
});
</script>
{% endblock %}
</head>
<body>
<nav class="navbar navbar-expand-lg border-bottom bg-body-tertiary shadow-sm">
<div class="container">
<a class="navbar-brand text-primary d-flex align-items-center gap-2" href="{{path('app_home')}}">
<img src="{{asset('/favicon/favicon.svg')}}" alt="نرم افزار حسابداری آنلاین حسابیکس" width="30" height="30">
<span class="">حسابیکس</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link px-3 py-2 rounded-3 transition-all" href="{{path('app_guide')}}">راهنمای جامع</a>
</li>
<li class="nav-item">
<a class="nav-link px-3 py-2 rounded-3 transition-all" href="{{path('app_professional_support')}}">پشتیبانی سازمانی</a>
</li>
<li class="nav-item">
<a class="nav-link px-3 py-2 rounded-3 transition-all" href="{{path('app_blog_home')}}">وبلاگ</a>
</li>
<li class="nav-item">
<a class="nav-link px-3 py-2 rounded-3 transition-all" href="{{path('qa_index')}}">سوالات</a>
</li>
<li class="nav-item">
<a class="nav-link px-3 py-2 rounded-3 transition-all" href="{{path('app_page',{'url':'sponsors'})}}">حامیان مالی</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link px-3 py-2 rounded-3 transition-all dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
درباره ما
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="{{path('app_page',{'url':'about'})}}">داستان حسابیکس</a>
</li>
<li>
<a class="dropdown-item" href="{{path('app_page',{'url':'contact'})}}">تماس با ما</a>
</li>
</ul>
</li>
</ul>
<div class="d-flex gap-2">
<!-- هدر جدید با طراحی مدرن -->
<header class="bg-white/95 backdrop-blur-md border-b border-gray-100 shadow-lg sticky top-0 z-50">
<div class="container mx-auto px-4 lg:px-6">
<div class="flex items-center justify-between h-16">
<!-- لوگو و نام برند -->
<div class="flex items-center space-x-3 space-x-reverse">
<a href="{{path('app_home')}}" class="flex items-center space-x-3 space-x-reverse group logo-container">
<div class="relative">
<img src="{{asset('/favicon/favicon.svg')}}"
alt="نرم افزار حسابداری آنلاین حسابیکس"
width="36" height="36"
class="transition-transform duration-300 group-hover:scale-110">
<div class="absolute -top-1 -right-1 w-2.5 h-2.5 bg-green-500 rounded-full animate-pulse"></div>
</div>
<div class="flex flex-col">
<span class="text-xl font-bold bg-gradient-to-l from-blue-600 to-purple-600 bg-clip-text text-transparent">
حسابیکس
</span>
<span class="text-xs text-gray-500 -mt-0.5">نرم‌افزار حسابداری آنلاین</span>
</div>
</a>
</div>
<!-- منوی اصلی - دسکتاپ -->
<nav id="desktopMenu" class="items-center space-x-1 space-x-reverse">
<a href="{{path('app_guide')}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
راهنمای جامع
</a>
<a href="{{path('app_professional_support')}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
پشتیبانی سازمانی
</a>
<a href="{{path('app_blog_home')}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
وبلاگ
</a>
<a href="{{path('qa_index')}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
سوالات
</a>
<a href="{{path('app_page',{'url':'sponsors'})}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
حامیان مالی
</a>
<a href="{{path('app_page',{'url':'about'})}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
داستان حسابیکس
</a>
<a href="{{path('app_page',{'url':'contact'})}}"
class="nav-link px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
تماس با ما
</a>
</nav>
<!-- دکمه‌های کاربر -->
<div class="flex items-center space-x-3 space-x-reverse">
{% if app.user and app.user.roles is defined and 'ROLE_CUSTOMER' in app.user.roles %}
{# کاربر وارد شده - نمایش منوی داشبورد #}
<div class="dropdown">
<button class="btn btn-outline-primary rounded-4 px-3 py-2 fw-bold transition-all dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<img src="{{ asset('/img/icons/user.svg') }}" alt="کاربر" class="icon-svg icon-user me-2">{{ app.user.name }}
<!-- کاربر وارد شده -->
<div class="relative group">
<button class="flex items-center space-x-2 space-x-reverse px-3 py-2 bg-gradient-to-l from-blue-50 to-purple-50 text-gray-700 rounded-lg hover:from-blue-100 hover:to-purple-100 transition-all duration-200 border border-blue-200">
<div class="w-7 h-7 bg-gradient-to-l from-blue-500 to-purple-500 rounded-full flex items-center justify-center">
<span class="text-white text-xs font-bold">{{ app.user.name|slice(0,1) }}</span>
</div>
<span class="font-medium text-sm">{{ app.user.name }}</span>
<svg class="w-3 h-3 transition-transform duration-200 group-hover:rotate-180" 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>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="{{ path('customer_dashboard') }}">
<img src="{{ asset('/img/icons/cogs.svg') }}" alt="داشبورد" class="icon-svg icon-cogs me-2">داشبورد
<div class="absolute left-0 mt-2 w-56 bg-white rounded-xl shadow-xl border border-gray-100 py-3 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 transform translate-y-2 group-hover:translate-y-0">
<div class="px-4 py-2 border-b border-gray-100">
<div class="text-sm font-medium text-gray-900">{{ app.user.name }}</div>
<div class="text-xs text-gray-500">عضو باشگاه مشتریان</div>
</div>
<a href="{{ path('customer_dashboard') }}"
class="flex items-center space-x-3 space-x-reverse px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 transition-colors duration-200">
<div class="w-7 h-7 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-3.5 h-3.5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
</svg>
</div>
<span class="text-sm">داشبورد</span>
</a>
<div class="px-4 py-2">
<a href="{{ path('customer_logout') }}"
class="flex items-center space-x-3 space-x-reverse px-3 py-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors duration-200">
<div class="w-7 h-7 bg-red-100 rounded-lg flex items-center justify-center">
<svg class="w-3.5 h-3.5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
</svg>
</div>
<span class="text-sm">خروج</span>
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item text-danger" href="{{ path('customer_logout') }}">
<img src="{{ asset('/img/icons/sign-out.svg') }}" alt="خروج" class="icon-svg icon-sign-out me-2">خروج
</a>
</li>
</ul>
</div>
</div>
</div>
{% else %}
{# کاربر وارد نشده - نمایش دکمه ورود #}
<a class="btn btn-outline-primary rounded-4 px-3 py-2 fw-bold transition-all" href="{{ path('customer_login') }}">
<!-- کاربر وارد نشده -->
<a href="{{ path('customer_login') }}"
class="px-3 py-2 text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-all duration-200 font-medium text-sm">
باشگاه مشتریان
</a>
{% endif %}
<a target="_blank" class="btn btn-primary rounded-4 px-4 py-2 fw-bold transition-all" href="https://app.hesabix.ir">
<!-- دکمه ورود/عضویت -->
<a target="_blank"
href="https://app.hesabix.ir"
class="btn-primary px-5 py-2 bg-gradient-to-l from-blue-600 to-purple-600 text-white rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all duration-200 font-medium text-sm shadow-lg hover:shadow-xl transform hover:-translate-y-0.5">
ورود / عضویت
</a>
<!-- دکمه منوی موبایل -->
<button id="mobileMenuButton" class="lg:hidden p-2 rounded-lg hover:bg-gray-100 transition-colors duration-200"
type="button"
onclick="toggleMobileMenu()"
aria-label="Toggle navigation">
<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="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
</div>
<!-- منوی موبایل -->
<div class="lg:hidden hidden" id="navbarSupportedContent">
<div class="py-4 border-t border-gray-100">
<div class="space-y-1">
<a href="{{path('app_guide')}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
راهنمای جامع
</a>
<a href="{{path('app_professional_support')}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
پشتیبانی سازمانی
</a>
<a href="{{path('app_blog_home')}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
وبلاگ
</a>
<a href="{{path('qa_index')}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
سوالات
</a>
<a href="{{path('app_page',{'url':'sponsors'})}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
حامیان مالی
</a>
<a href="{{path('app_page',{'url':'about'})}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
داستان حسابیکس
</a>
<a href="{{path('app_page',{'url':'contact'})}}"
class="block px-4 py-3 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-lg transition-colors duration-200">
تماس با ما
</a>
</div>
</div>
</div>
</div>
</nav>
</header>
{% block body %}{% endblock %}
<footer class="py-3 my-4">
<div class="d-flex justify-content-center align-items-center gap-3 mb-3" id="trust-seals-container">
<footer class="py-6 my-8">
<div class="flex justify-center items-center gap-3 mb-6" id="trust-seals-container">
<!-- نشانگرهای بارگذاری -->
<div class="trust-seal-loading d-flex gap-3">
<div class="spinner-border spinner-border-sm text-primary" role="status" aria-hidden="true"></div>
<span class="text-muted small">در حال بارگذاری گواهی‌های اعتماد...</span>
<div class="trust-seal-loading flex gap-3">
<div class="spinner-border" role="status" aria-hidden="true"></div>
<span class="text-gray-500 text-sm">در حال بارگذاری گواهی‌های اعتماد...</span>
</div>
<!-- محتوای اصلی که بعد از لود نمایش داده می‌شود -->
<div class="trust-seals-content" style="display: none;">
<div class="trust-seals-content hidden">
<a referrerpolicy='origin' target='_blank' href='https://trustseal.enamad.ir/?id=614357&Code=4ATiNTREoPRD5Lz3zwc9zyz0zWGJiZL3'>
<img referrerpolicy='origin' src='https://trustseal.enamad.ir/logo.aspx?id=614357&Code=4ATiNTREoPRD5Lz3zwc9zyz0zWGJiZL3' alt='گواهی اعتماد اناماد' style='cursor:pointer' code='4ATiNTREoPRD5Lz3zwc9zyz0zWGJiZL3' loading="lazy">
<img referrerpolicy='origin' src='https://trustseal.enamad.ir/logo.aspx?id=614357&Code=4ATiNTREoPRD5Lz3zwc9zyz0zWGJiZL3' alt='گواهی اعتماد اناماد' class='cursor-pointer hover:opacity-80 transition-opacity duration-300 max-h-12 w-auto' code='4ATiNTREoPRD5Lz3zwc9zyz0zWGJiZL3' loading="lazy">
</a>
<a href="https://bitpay.ir/certificate-230498-hesabix.ir" target="_blank">
<img src="https://bitpay.ir/theme/public/images/trusted-logo.svg" alt="گواهی اعتماد بیت‌پی" loading="lazy"/>
<img src="https://bitpay.ir/theme/public/images/trusted-logo.svg" alt="گواهی اعتماد بیت‌پی" class="hover:opacity-80 transition-opacity duration-300 max-h-12 w-auto" loading="lazy"/>
</a>
<img referrerpolicy='origin' id='rgvjoeukesgtapfufukzrgvj' style='cursor:pointer' onclick='window.open("https://logo.samandehi.ir/Verify.aspx?id=380563&p=xlaomcsiobpddshwgvkaxlao", "Popup","toolbar=no, scrollbars=no, location=no, statusbar=no, menubar=no, resizable=0, width=450, height=630, top=30")' alt='گواهی اعتماد ساماندهی' src='https://logo.samandehi.ir/logo.aspx?id=380563&p=qftiaqgwlymaujynwlbqqfti' loading="lazy"/>
<img referrerpolicy='origin' id='rgvjoeukesgtapfufukzrgvj' class='cursor-pointer hover:opacity-80 transition-opacity duration-300 max-h-12 w-auto' onclick='window.open("https://logo.samandehi.ir/Verify.aspx?id=380563&p=xlaomcsiobpddshwgvkaxlao", "Popup","toolbar=no, scrollbars=no, location=no, statusbar=no, menubar=no, resizable=0, width=450, height=630, top=30")' alt='گواهی اعتماد ساماندهی' src='https://logo.samandehi.ir/logo.aspx?id=380563&p=qftiaqgwlymaujynwlbqqfti' loading="lazy"/>
</div>
</div>
<ul class="nav justify-content-center border-bottom pb-3 mb-3">
<li class="nav-item">
<a target="_blank" href="https://azadbeh.ir/projects/%D8%AD%D8%B3%D8%A7%D8%A8%DB%8C%DA%A9%D8%B3" class="nav-link px-2">فرصت‌های شغلی</a>
<ul class="flex justify-center flex-wrap gap-4 border-b border-gray-200 pb-6 mb-6">
<li>
<a target="_blank" href="https://azadbeh.ir/projects/%D8%AD%D8%B3%D8%A7%D8%A8%DB%8C%DA%A9%D8%B3" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">فرصت‌های شغلی</a>
</li>
<li class="nav-item">
<a href="{{path('app_page',{'url':'hsx'})}}" class="nav-link px-2">توکن HSX</a>
<li>
<a href="{{path('app_page',{'url':'hsx'})}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">توکن HSX</a>
</li>
<li class="nav-item">
<a href="{{path('app_api_docs',{'url':'home'})}}" class="nav-link px-2">مستندات API</a>
<li>
<a href="{{path('app_api_docs',{'url':'home'})}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">مستندات API</a>
</li>
<li class="nav-item">
<a href="{{path('app_changes')}}" class="nav-link px-2">تغییرات</a>
<li>
<a href="{{path('app_changes')}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">تغییرات</a>
</li>
<li class="nav-item">
<a href="https://source.hesabix.ir/morrning" target="_blank" class="nav-link px-2">مخازن کد</a>
<li>
<a href="https://source.hesabix.ir/morrning" target="_blank" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">مخازن کد</a>
</li>
<li class="nav-item">
<a href="{{path('app_page',{'url':'open-source'})}}" class="nav-link px-2">متن‌باز</a>
<li>
<a href="{{path('app_page',{'url':'open-source'})}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">متن‌باز</a>
</li>
<li class="nav-item">
<a href="{{path('app_page',{'url':'inMedia'})}}" class="nav-link px-2">در رسانه‌ها</a>
<li>
<a href="{{path('app_page',{'url':'inMedia'})}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">در رسانه‌ها</a>
</li>
<li class="nav-item">
<a href="{{path('app_page',{'url':'terms'})}}" class="nav-link px-2">قوانین ارائه خدمات</a>
<li>
<a href="{{path('app_page',{'url':'terms'})}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">قوانین ارائه خدمات</a>
</li>
<li class="nav-item">
<a href="{{path('app_page',{'url':'privacy'})}}" class="nav-link px-2">حریم خصوصی</a>
<li>
<a href="{{path('app_page',{'url':'privacy'})}}" class="text-gray-600 hover:text-primary-600 px-2 py-1 transition-colors duration-200">حریم خصوصی</a>
</li>
</ul>
<div class="text-center p-1 m-1">
<div class="row mx-0">
<div class="col-sm-12 col-md-6 text-center text-body-tertiary">
<img src={{asset('/img/sp/parspack.svg')}} alt="Parspack Co" width="25" class="img-fluid">
<div class="text-center p-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="text-center text-gray-500">
<img src={{asset('/img/sp/parspack.svg')}} alt="Parspack Co" width="25" class="inline-block">
با خاطری آسوده،استوار بر روی راهکار‌های ابری
<a href="https://parspack.com" target="_blank" class="rul">پارس‌پک</a>
<a href="https://parspack.com" target="_blank" class="text-primary-600 hover:underline">پارس‌پک</a>
</div>
<div class="col-sm-12 col-md-6 text-center text-body-tertiary">
<div class="text-center text-gray-500">
حسابیکس با
<img src={{asset('/img/heart.png')}} alt="Love" width="25" class="img-fluid">
<img src={{asset('/img/heart.png')}} alt="Love" width="25" class="inline-block">
متن باز است
</div>
</div>
</div>
</footer>
</body>

View file

@ -4,353 +4,78 @@
{% block stylesheets %}
{{ parent() }}
<style>
/* تنظیمات کلی برای فارسی */
.customer-dashboard * {
font-family: 'Yekan Bakh FaNum', 'Tahoma', 'Arial', sans-serif;
}
.customer-dashboard {
min-height: 80vh;
padding: 40px 0;
direction: rtl;
text-align: right;
}
.dashboard-header {
background: linear-gradient(135deg, #0d6efd 0%, #6610f2 100%);
color: white;
padding: 40px 0;
margin-bottom: 40px;
direction: rtl;
}
.dashboard-header h1 {
direction: rtl;
text-align: right;
}
.dashboard-header p {
direction: rtl;
text-align: right;
}
.dashboard-card {
background: white;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
padding: 30px;
margin-bottom: 30px;
transition: transform 0.3s ease;
direction: rtl;
}
.dashboard-card:hover {
transform: translateY(-5px);
}
.dashboard-card h3 {
color: #0d6efd;
margin-bottom: 20px;
font-weight: bold;
direction: rtl;
text-align: right;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #e9ecef;
direction: rtl;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-weight: 600;
color: #495057;
direction: rtl;
}
.info-value {
color: #6c757d;
direction: rtl;
}
.status-badge {
padding: 5px 15px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
direction: rtl;
}
.status-active {
background: #d1edff;
color: #0c5460;
}
.status-inactive {
background: #f8d7da;
color: #721c24;
}
.action-buttons {
margin-top: 30px;
direction: rtl;
text-align: center;
}
.btn-customer {
background: linear-gradient(135deg, #0d6efd 0%, #6610f2 100%);
border: none;
border-radius: 10px;
padding: 12px 25px;
color: white;
text-decoration: none;
font-weight: 500;
margin: 5px;
display: inline-block;
transition: all 0.3s ease;
direction: rtl;
}
.btn-customer:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(13, 110, 253, 0.4);
color: white;
}
.welcome-message {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
color: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 30px;
direction: rtl;
text-align: right;
}
.welcome-message h4 {
direction: rtl;
text-align: right;
}
.welcome-message p {
direction: rtl;
text-align: right;
}
/* بهبود نمایش آیکون‌های SVG */
.icon-svg {
width: 16px;
height: 16px;
margin-left: 5px;
margin-right: 0;
display: inline-block;
vertical-align: middle;
}
/* تغییر رنگ آیکون‌های SVG */
.icon-svg svg {
fill: currentColor;
width: 100%;
height: 100%;
}
/* رنگ‌های مختلف برای آیکون‌ها */
.text-primary .icon-svg svg {
fill: #0d6efd;
}
.text-success .icon-svg svg {
fill: #198754;
}
.text-danger .icon-svg svg {
fill: #dc3545;
}
.text-warning .icon-svg svg {
fill: #ffc107;
}
.text-info .icon-svg svg {
fill: #0dcaf0;
}
.text-muted .icon-svg svg {
fill: #6c757d;
}
.text-white .icon-svg svg {
fill: #ffffff;
}
/* بهبود نمایش متن‌های فارسی */
.text-center {
direction: rtl;
}
.text-end {
direction: rtl;
text-align: right;
}
/* استایل‌های کیف پول */
.wallet-connect-form {
background: #ffffff;
padding: 25px;
border-radius: 12px;
margin-bottom: 25px;
border: 1px solid #e9ecef;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.wallet-connect-form h5 {
color: #0d6efd;
margin-bottom: 20px;
direction: rtl;
text-align: right;
font-weight: 600;
}
#walletAddress {
font-family: 'Courier New', monospace;
font-size: 14px;
direction: ltr;
text-align: left;
}
#walletAddress:read-only {
background-color: #f8f9fa;
cursor: not-allowed;
border: 1px solid #e9ecef;
}
.wallet-connect-form .input-group-text {
background-color: #e9ecef;
border: 1px solid #e9ecef;
color: #6c757d;
}
.wallet-connect-form .btn-lg {
padding: 12px 24px;
font-size: 1.1rem;
font-weight: 600;
}
.wallet-connect-form .btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.wallets-list table {
direction: rtl;
margin-top: 20px;
}
.wallets-list th,
.wallets-list td {
text-align: right;
vertical-align: middle;
padding: 12px;
}
.wallets-list th {
background-color: #f8f9fa;
font-weight: 600;
border-bottom: 2px solid #dee2e6;
}
.wallets-list code {
font-family: 'Courier New', monospace;
font-size: 12px;
background: #f8f9fa;
padding: 4px 8px;
border-radius: 6px;
border: 1px solid #e9ecef;
}
.btn-group-sm .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
}
.btn-group-sm .btn i {
margin-left: 4px;
}
</style>
{% endblock %}
{% block body %}
<div class="customer-dashboard">
<div class="container">
<div class="welcome-message">
<h4><img src="{{ asset('/img/icons/heart.svg') }}" alt="قلب" class="icon-svg icon-heart"> از عضویت شما در باشگاه مشتریان حسابیکس سپاسگزاریم!</h4>
<div class="min-h-screen py-10 font-yekan" dir="rtl">
<div class="container mx-auto px-4">
<div class="bg-gradient-to-r from-green-500 to-teal-500 text-white p-6 rounded-xl mb-10">
<h4 class="text-xl font-bold mb-2 flex items-center gap-2">
<img src="{{ asset('/img/icons/heart.svg') }}" alt="قلب" class="w-5 h-5"> از عضویت شما در باشگاه مشتریان حسابیکس سپاسگزاریم!
</h4>
<p class="mb-0">در این بخش می‌توانید اطلاعات حساب کاربری خود را مشاهده و مدیریت کنید.</p>
</div>
<div class="row">
<div class="col-md-6">
<div class="dashboard-card">
<h3><img src="{{ asset('/img/icons/user.svg') }}" alt="کاربر" class="icon-svg icon-user"> اطلاعات شخصی</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="w-full">
<div class="card hover:shadow-lg transition-shadow duration-300">
<h3 class="text-primary-600 mb-6 text-xl font-bold flex items-center gap-2">
<img src="{{ asset('/img/icons/user.svg') }}" alt="کاربر" class="w-5 h-5"> اطلاعات شخصی
</h3>
<div class="info-item">
<span class="info-label">نام و نام خانوادگی:</span>
<span class="info-value">{{ user.name }}</span>
<div class="flex justify-between items-center py-4 border-b border-gray-200">
<span class="font-semibold text-gray-700">نام و نام خانوادگی:</span>
<span class="text-gray-600">{{ user.name }}</span>
</div>
<div class="info-item">
<span class="info-label">پست الکترونیکی:</span>
<span class="info-value">{{ user.email }}</span>
<div class="flex justify-between items-center py-4 border-b border-gray-200">
<span class="font-semibold text-gray-700">پست الکترونیکی:</span>
<span class="text-gray-600">{{ user.email }}</span>
</div>
<div class="info-item">
<span class="info-label">شماره موبایل:</span>
<span class="info-value">{{ user.phone }}</span>
<div class="flex justify-between items-center py-4 border-b border-gray-200">
<span class="font-semibold text-gray-700">شماره موبایل:</span>
<span class="text-gray-600">{{ user.phone }}</span>
</div>
<div class="info-item">
<span class="info-label">وضعیت حساب:</span>
<span class="status-badge {{ user.isActive ? 'status-active' : 'status-inactive' }}">
<div class="flex justify-between items-center py-4">
<span class="font-semibold text-gray-700">وضعیت حساب:</span>
<span class="px-4 py-2 rounded-full text-sm font-bold {{ user.isActive ? 'bg-blue-100 text-blue-800' : 'bg-red-100 text-red-800' }}">
{{ user.isActive ? 'فعال' : 'غیرفعال' }}
</span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="dashboard-card">
<h3><img src="{{ asset('/img/icons/calendar.svg') }}" alt="تقویم" class="icon-svg icon-calendar"> اطلاعات عضویت</h3>
<div class="w-full">
<div class="card hover:shadow-lg transition-shadow duration-300">
<h3 class="text-primary-600 mb-6 text-xl font-bold flex items-center gap-2">
<img src="{{ asset('/img/icons/calendar.svg') }}" alt="تقویم" class="w-5 h-5"> اطلاعات عضویت
</h3>
<div class="info-item">
<span class="info-label">تاریخ عضویت:</span>
<span class="info-value">{{ user.createdAt|date('Y/m/d') }}</span>
<div class="flex justify-between items-center py-4 border-b border-gray-200">
<span class="font-semibold text-gray-700">تاریخ عضویت:</span>
<span class="text-gray-600">{{ user.createdAt|date('Y/m/d') }}</span>
</div>
<div class="info-item">
<span class="info-label">آخرین ورود:</span>
<span class="info-value">
<div class="flex justify-between items-center py-4">
<span class="font-semibold text-gray-700">آخرین ورود:</span>
<span class="text-gray-600">
{{ user.lastLoginAt ? user.lastLoginAt|date('Y/m/d H:i') : 'هنوز وارد نشده' }}
</span>
</div>
<div class="info-item">
<span class="info-label">تایید ایمیل:</span>
<span class="status-badge {{ user.emailVerifiedAt ? 'status-active' : 'status-inactive' }}">
<div class="flex justify-between items-center py-4 border-b border-gray-200">
<span class="font-semibold text-gray-700">تایید ایمیل:</span>
<span class="px-4 py-2 rounded-full text-sm font-bold {{ user.emailVerifiedAt ? 'bg-blue-100 text-blue-800' : 'bg-red-100 text-red-800' }}">
{{ user.emailVerifiedAt ? 'تایید شده' : 'تایید نشده' }}
</span>
</div>
{% if user.subscriptionType %}
<div class="info-item">
<span class="info-label">نوع اشتراک:</span>
<span class="info-value">{{ user.subscriptionType }}</span>
<div class="flex justify-between items-center py-4">
<span class="font-semibold text-gray-700">نوع اشتراک:</span>
<span class="text-gray-600">{{ user.subscriptionType }}</span>
</div>
{% endif %}
</div>
@ -358,151 +83,152 @@
</div>
<!-- بخش کیف پول‌ها -->
<div class="row">
<div class="col-12">
<div class="dashboard-card wallet-section">
<h3><img src="{{ asset('/img/icons/wallet.svg') }}" alt="کیف پول" class="icon-svg icon-wallet"> مدیریت کیف پول‌ها</h3>
<div class="w-full mt-8">
<div class="card">
<h3 class="text-primary-600 mb-6 text-xl font-bold flex items-center gap-2">
<img src="{{ asset('/img/icons/wallet.svg') }}" alt="کیف پول" class="w-5 h-5"> مدیریت کیف پول‌ها
</h3>
<!-- فرم اتصال کیف پول جدید -->
<div class="wallet-connect-form mb-4"
data-controller="wallet-connect"
data-wallet-connect-sign-message-value="{{ signMessage }}"
data-wallet-connect-csrf-token-value="{{ csrf_token('wallet_connect_form') }}">
<h5>اتصال کیف پول جدید</h5>
<div class="row g-3">
<div class="col-md-6">
<label for="walletType" class="form-label">نوع کیف پول</label>
<select class="form-select" id="walletType" data-wallet-connect-target="walletType" data-action="change->wallet-connect#onWalletTypeChange">
<option value="">انتخاب کنید...</option>
<option value="metamask">MetaMask</option>
<option value="trust">Trust Wallet</option>
<option value="walletconnect">WalletConnect</option>
<option value="coinbase">Coinbase Wallet</option>
<option value="other">سایر</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">آدرس کیف پول</label>
<div class="input-group">
<span class="input-group-text">
<i class="fas fa-wallet"></i>
</span>
<input type="text"
class="form-control"
id="walletAddress"
data-wallet-connect-target="walletAddress"
placeholder="آدرس کیف پول پس از اتصال نمایش داده می‌شود"
readonly>
</div>
<div class="form-text">آدرس کیف پول شما پس از اتصال به صورت خودکار پر می‌شود</div>
</div>
<div class="col-12">
<button type="button"
class="btn btn-primary btn-lg w-100"
id="connectBtn"
data-wallet-connect-target="connectBtn"
data-action="click->wallet-connect#connectWallet"
disabled>
نوع کیف پول را انتخاب کنید
</button>
</div>
<!-- فرم اتصال کیف پول جدید -->
<div class="bg-white p-6 rounded-xl border border-gray-200 mb-6"
data-controller="wallet-connect"
data-wallet-connect-sign-message-value="{{ signMessage }}"
data-wallet-connect-csrf-token-value="{{ csrf_token('wallet_connect_form') }}">
<h5 class="text-lg font-semibold mb-4">اتصال کیف پول جدید</h5>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="walletType" class="form-label">نوع کیف پول</label>
<select class="form-control" id="walletType" data-wallet-connect-target="walletType" data-action="change->wallet-connect#onWalletTypeChange">
<option value="">انتخاب کنید...</option>
<option value="metamask">MetaMask</option>
<option value="trust">Trust Wallet</option>
<option value="walletconnect">WalletConnect</option>
<option value="coinbase">Coinbase Wallet</option>
<option value="other">سایر</option>
</select>
</div>
<div>
<label class="form-label">آدرس کیف پول</label>
<div class="relative">
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<i class="fas fa-wallet text-gray-400"></i>
</div>
<input type="text"
class="form-control pr-10"
id="walletAddress"
data-wallet-connect-target="walletAddress"
placeholder="آدرس کیف پول پس از اتصال نمایش داده می‌شود"
readonly>
</div>
<p class="text-sm text-gray-500 mt-2">آدرس کیف پول شما پس از اتصال به صورت خودکار پر می‌شود</p>
</div>
<div class="col-span-2">
<button type="button"
class="btn-primary w-full py-3 text-lg"
id="connectBtn"
data-wallet-connect-target="connectBtn"
data-action="click->wallet-connect#connectWallet"
disabled>
نوع کیف پول را انتخاب کنید
</button>
</div>
<input type="hidden" data-wallet-connect-target="signature">
</div>
<input type="hidden" data-wallet-connect-target="signature">
</div>
<!-- لیست کیف پول‌های متصل -->
<div class="wallets-list">
<h5>کیف پول‌های متصل ({{ user.wallets|length }}/5)</h5>
<!-- لیست کیف پول‌های متصل -->
<div class="mt-6">
<h5 class="text-lg font-semibold mb-4">کیف پول‌های متصل ({{ user.wallets|length }}/5)</h5>
{% if user.wallets|length > 0 %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
{% if user.wallets|length > 0 %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">آدرس کیف پول</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">نوع</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">وضعیت</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">تاریخ اتصال</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">عملیات</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{% for wallet in user.wallets %}
<tr>
<th>آدرس کیف پول</th>
<th>نوع</th>
<th>وضعیت</th>
<th>تاریخ اتصال</th>
<th>عملیات</th>
</tr>
</thead>
<tbody>
{% for wallet in user.wallets %}
<tr>
<td>
<code>{{ wallet.shortAddress }}</code>
{% if wallet.isPrimary %}
<span class="badge bg-primary ms-2">اصلی</span>
<td class="px-6 py-4 whitespace-nowrap">
<code class="text-sm bg-gray-100 px-2 py-1 rounded">{{ wallet.shortAddress }}</code>
{% if wallet.isPrimary %}
<span class="bg-primary-100 text-primary-800 text-xs px-2 py-1 rounded-full mr-2">اصلی</span>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">{{ wallet.walletType }}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="px-2 py-1 rounded-full text-xs font-medium {{ wallet.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}">
{{ wallet.isActive ? 'فعال' : 'غیرفعال' }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ wallet.connectedAt|date('Y/m/d H:i') }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div class="flex gap-2">
{% if not wallet.isPrimary and wallet.isActive %}
<button class="btn-outline-primary text-xs px-3 py-1"
data-wallet-id="{{ wallet.id }}"
data-action="click->wallet-connect#setPrimaryWallet">
<i class="fas fa-star"></i> اصلی
</button>
{% endif %}
</td>
<td>
<span class="badge bg-info">{{ wallet.walletType }}</span>
</td>
<td>
<span class="badge {{ wallet.isActive ? 'bg-success' : 'bg-danger' }}">
{{ wallet.isActive ? 'فعال' : 'غیرفعال' }}
</span>
</td>
<td>{{ wallet.connectedAt|date('Y/m/d H:i') }}</td>
<td>
<div class="btn-group btn-group-sm">
{% if not wallet.isPrimary and wallet.isActive %}
<button class="btn btn-outline-primary btn-sm"
data-wallet-id="{{ wallet.id }}"
data-action="click->wallet-connect#setPrimaryWallet">
<i class="fas fa-star"></i> اصلی
</button>
{% endif %}
<button class="btn btn-outline-warning btn-sm"
data-wallet-id="{{ wallet.id }}"
data-action="click->wallet-connect#toggleWalletStatus">
<i class="fas fa-{{ wallet.isActive ? 'pause' : 'play' }}"></i>
{{ wallet.isActive ? 'غیرفعال' : 'فعال' }}
</button>
<button class="btn-outline-warning text-xs px-3 py-1"
data-wallet-id="{{ wallet.id }}"
data-action="click->wallet-connect#toggleWalletStatus">
<i class="fas fa-{{ wallet.isActive ? 'pause' : 'play' }}"></i>
{{ wallet.isActive ? 'غیرفعال' : 'فعال' }}
</button>
<button class="btn btn-outline-danger btn-sm"
data-wallet-id="{{ wallet.id }}"
data-action="click->wallet-connect#deleteWallet">
<i class="fas fa-trash"></i> حذف
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-muted py-5">
<i class="fas fa-wallet fa-4x mb-4 text-muted"></i>
<h5 class="text-muted">هنوز هیچ کیف پولی متصل نشده است</h5>
<p class="text-muted">برای شروع، نوع کیف پول خود را انتخاب کنید و روی دکمه اتصال کلیک کنید</p>
</div>
{% endif %}
</div>
<button class="btn-outline-danger text-xs px-3 py-1"
data-wallet-id="{{ wallet.id }}"
data-action="click->wallet-connect#deleteWallet">
<i class="fas fa-trash"></i> حذف
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-gray-500 py-12">
<i class="fas fa-wallet text-6xl mb-4 text-gray-300"></i>
<h5 class="text-gray-500 text-lg mb-2">هنوز هیچ کیف پولی متصل نشده است</h5>
<p class="text-gray-400">برای شروع، نوع کیف پول خود را انتخاب کنید و روی دکمه اتصال کلیک کنید</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="dashboard-card">
<h3><img src="{{ asset('/img/icons/cogs.svg') }}" alt="تنظیمات" class="icon-svg icon-cogs"> عملیات</h3>
<div class="w-full mt-8">
<div class="card">
<h3 class="text-primary-600 mb-6 text-xl font-bold flex items-center gap-2">
<img src="{{ asset('/img/icons/cogs.svg') }}" alt="تنظیمات" class="w-5 h-5"> عملیات
</h3>
<div class="action-buttons">
<a href="{{ path('customer_forgot_password') }}" class="btn-customer">
<img src="{{ asset('/img/icons/key.svg') }}" alt="کلید" class="icon-svg icon-key"> بازیابی کلمه عبور
</a>
<div class="flex flex-wrap gap-4 justify-center">
<a href="{{ path('customer_forgot_password') }}" class="btn-primary flex items-center gap-2">
<img src="{{ asset('/img/icons/key.svg') }}" alt="کلید" class="w-4 h-4"> بازیابی کلمه عبور
</a>
<a href="{{ path('app_home') }}" class="btn-customer">
<img src="{{ asset('/img/icons/home.svg') }}" alt="خانه" class="icon-svg icon-home"> بازگشت به صفحه اصلی
</a>
<a href="{{ path('app_home') }}" class="btn-primary flex items-center gap-2">
<img src="{{ asset('/img/icons/home.svg') }}" alt="خانه" class="w-4 h-4"> بازگشت به صفحه اصلی
</a>
<a href="{{ path('customer_logout') }}" class="btn-customer" style="background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);">
<img src="{{ asset('/img/icons/sign-out.svg') }}" alt="خروج" class="icon-svg icon-sign-out"> خروج
</a>
</div>
<a href="{{ path('customer_logout') }}" class="bg-gradient-to-r from-red-500 to-red-600 text-white px-6 py-3 rounded-lg font-medium transition-all duration-300 hover:shadow-lg hover:-translate-y-0.5 flex items-center gap-2">
<img src="{{ asset('/img/icons/sign-out.svg') }}" alt="خروج" class="w-4 h-4"> خروج
</a>
</div>
</div>
</div>

File diff suppressed because it is too large Load diff

View file

@ -8,117 +8,165 @@
{% endblock %}
{% block body %}
<main class="container-fluid px-0">
<div class="blog-header position-relative">
<div class="overlay"></div>
<div class="container position-relative">
<div class="row min-vh-50 align-items-center">
<div class="col-12 text-center text-white">
<h1 class="display-4 fw-bold mb-4">وبلاگ حسابیکس</h1>
<p class="" style="font-family: 'Yekan Bakh FaNum', sans-serif;">جدیدترین اطلاعات و خبرها از دنیای حسابداری</p>
</div>
<main class="min-h-screen bg-gray-50">
<!-- هدر وبلاگ -->
<div class="relative bg-gradient-to-br from-blue-600 via-purple-600 to-indigo-700 overflow-hidden">
<div class="absolute inset-0 bg-black/40"></div>
<div class="relative container mx-auto px-4 py-20 lg:py-24">
<div class="text-center text-white">
<h1 class="text-4xl lg:text-6xl font-bold mb-6 animate-fade-in-up">
وبلاگ حسابیکس
</h1>
<p class="text-lg lg:text-xl text-blue-100 max-w-2xl mx-auto animate-fade-in-up" style="animation-delay: 0.2s;">
جدیدترین اطلاعات و خبرها از دنیای حسابداری
</p>
</div>
</div>
</div>
<div class="container mt-5 mb-5">
<div class="row">
<div class="col-md-12">
<div class="row g-4">
{% for post in posts %}
<div class="col-md-4">
<article class="card h-100 border-0 shadow-sm hover-shadow transition-all">
<div class="position-relative">
<img src="{{asset('uploaded/'~ post.mainPic)}}" alt="{{post.title}}" class="card-img-top object-fit-cover" style="height: 200px;">
<div class="position-absolute top-0 end-0 m-3">
{% for tree in post.tree %}
<span class="badge bg-primary me-1">{{ tree.label }}</span>
{% endfor %}
</div>
</div>
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<small class="text-muted">
<i class="fas fa-calendar-alt me-1"></i>
{{ Jdate.jdate('Y/n/d',post.dateSubmit) }}
</small>
<small class="text-muted">
<i class="fas fa-eye me-1"></i>
{{ post.views }}
</small>
</div>
<h5 class="card-title text-primary fw-bold">{{ post.title }}</h5>
<p class="card-text text-muted">{{ post.intro }}</p>
<a href="{{path('app_blog_post',{'url':post.url})}}" class="btn btn-primary rounded-pill stretched-link">
ادامه مطلب
<i class="fas fa-arrow-left me-2"></i>
</a>
</div>
</article>
</div>
{% endfor %}
<!-- محتوای اصلی -->
<div class="container mx-auto px-4 py-12">
<!-- جستجو -->
<div class="mb-8">
<form method="GET" action="{{ path('app_blog_home') }}" class="max-w-md mx-auto">
<div class="relative">
<input type="text"
name="search"
value="{{ search }}"
placeholder="جستجو در وبلاگ..."
class="w-full px-4 py-3 pr-12 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 text-right">
<button type="submit"
class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-blue-600 transition-colors duration-200">
<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="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
</div>
</form>
</div>
<div class="row mt-5">
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a href="{{ path('app_blog_home',{'page':page -1})}}" class="page-link rounded-pill mx-1">
<i class="fas fa-chevron-right"></i>
صفحه قبل
</a>
</li>
<li class="page-item">
<a class="page-link rounded-pill mx-1 active" href="{{ path('app_blog_home',{'page':page })}}">{{page}}</a>
</li>
{% if (page + 1) <= maxpages %}
<li class="page-item">
<a class="page-link rounded-pill mx-1" href="{{ path('app_blog_home',{'page':page +1})}}">{{page +1}}</a>
</li>
{% endif %}
{% if (page + 2) <= maxpages %}
<li class="page-item">
<a class="page-link rounded-pill mx-1" href="{{ path('app_blog_home',{'page':page +2})}}">{{page + 2}}</a>
</li>
{% endif %}
{% if (page + 3) <= maxpages %}
<li class="page-item">
<a class="page-link rounded-pill mx-1" href="{{ path('app_blog_home',{'page':page +3})}}">{{page + 3}}</a>
</li>
{% endif %}
<li class="page-item">
<a href="{{ path('app_blog_home',{'page':page +1})}}" class="page-link rounded-pill mx-1 {% if (page + 1) > maxpages %}disabled{% endif %}">
صفحه بعدی
<i class="fas fa-chevron-left"></i>
</a>
</li>
</ul>
</nav>
<!-- لیست پست‌ها -->
{% if posts|length > 0 %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-12">
{% for post in posts %}
<article class="group bg-white rounded-2xl shadow-soft hover:shadow-medium transition-all duration-300 overflow-hidden hover-lift">
<!-- تصویر پست -->
<div class="relative overflow-hidden">
<img src="{{asset('uploaded/'~ post.mainPic)}}"
alt="{{post.title}}"
class="w-full h-48 object-cover group-hover:scale-105 transition-transform duration-300">
<div class="absolute top-4 right-4 flex flex-wrap gap-2">
{% for tree in post.tree %}
<span class="px-3 py-1 bg-blue-600 text-white text-xs font-medium rounded-full shadow-lg">
{{ tree.label }}
</span>
{% endfor %}
</div>
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
</div>
<!-- محتوای کارت -->
<div class="p-6">
<!-- متا اطلاعات -->
<div class="flex items-center justify-between text-sm text-gray-500 mb-4">
<div class="flex items-center space-x-2 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="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span>{{ Jdate.jdate('Y/n/d',post.dateSubmit) }}</span>
</div>
<div class="flex items-center space-x-2 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>{{ post.views }}</span>
</div>
</div>
<!-- عنوان و توضیحات -->
<h3 class="text-xl font-bold text-gray-900 mb-3 line-clamp-2 group-hover:text-blue-600 transition-colors duration-200">
{{ post.title }}
</h3>
<p class="text-gray-600 mb-6 line-clamp-3 leading-relaxed">
{{ post.intro }}
</p>
<!-- دکمه ادامه مطلب -->
<a href="{{path('app_blog_post',{'url':post.url})}}"
class="inline-flex items-center space-x-2 space-x-reverse text-blue-600 hover:text-blue-700 font-medium transition-colors duration-200 group">
<span>ادامه مطلب</span>
<svg class="w-4 h-4 transform group-hover:translate-x-1 transition-transform duration-200" 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>
</a>
</div>
</article>
{% endfor %}
</div>
<!-- صفحه‌بندی -->
{% if maxpages > 1 %}
<nav class="flex justify-center" aria-label="صفحه‌بندی">
<div class="flex items-center space-x-2 space-x-reverse">
<!-- دکمه صفحه قبل -->
{% if page > 1 %}
<a href="{{ path('app_blog_home',{'page':page -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 i in range(1, maxpages) %}
{% if i == page %}
<span class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-blue-600 rounded-lg">
{{ i }}
</span>
{% elseif i <= page + 2 and i >= page - 2 %}
<a href="{{ path('app_blog_home',{'page':i})}}"
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">
{{ i }}
</a>
{% endif %}
{% endfor %}
</div>
<!-- دکمه صفحه بعد -->
{% if page < maxpages %}
<a href="{{ path('app_blog_home',{'page':page +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>
{% endif %}
{% else %}
<!-- پیام عدم وجود پست -->
<div class="text-center py-16">
<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="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<h3 class="text-xl font-semibold text-gray-900 mb-2">هیچ پستی یافت نشد</h3>
<p class="text-gray-600">
{% if search %}
برای عبارت "{{ search }}" هیچ نتیجه‌ای یافت نشد.
{% else %}
هنوز هیچ پستی منتشر نشده است.
{% endif %}
</p>
</div>
</div>
</div>
{% endif %}
</div>
</main>
<style>
.blog-header {
background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('{{ asset('img/blog/blog-header.jpg') }}');
background-size: cover;
background-position: center;
min-height: 250px;
}
.hover-shadow {
transition: all 0.3s ease;
}
.hover-shadow:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1) !important;
}
.min-vh-50 {
min-height: 250px;
}
.object-fit-cover {
object-fit: cover;
}
</style>
{% endblock %}

View file

@ -11,119 +11,174 @@
{% endblock %}
{% block body %}
<style>
.blog-post {
transition: all 0.3s ease;
}
.blog-post:hover {
transform: translateY(-5px);
}
.post-image {
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.post-image:hover {
transform: scale(1.02);
}
.post-card {
border: none;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
border-radius: 15px;
}
.post-card:hover {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.recent-posts .card {
border: none;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
border-radius: 15px;
overflow: hidden;
}
.recent-posts .card:hover {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.recent-posts .card img {
transition: transform 0.3s ease;
}
.recent-posts .card:hover img {
transform: scale(1.1);
}
.post-title {
font-size: 1.5rem;
font-weight: 600;
line-height: 1.4;
margin-bottom: 1.5rem;
font-family: inherit;
}
.post-intro {
font-size: 1.1rem;
line-height: 1.8;
color: #555;
}
.post-meta {
font-size: 0.9rem;
color: #666;
}
.post-content {
font-size: 1.1rem;
line-height: 1.8;
}
</style>
{% if item.plain is not null %}
{{ item.plain | raw}}
{% endif %}
{% if item.body is not null %}
<div class="container mt-4">
<div class="row">
<div class="col-sm-12 col-md-8">
<article class="blog-post mb-4">
<img class="img-fluid post-image mb-4" src="{{asset('uploaded/'~ item.mainPic)}}" alt="{{item.title}}"/>
<h1 class="text-primary fs-5">{{item.title}}</h1>
<div class="card post-card bg-body-tertiary mb-4">
<div class="card-body">
<div class="post-intro card-text mb-3">
{{item.intro}}
</div>
<div class="post-meta card-text">
<figure>
<blockquote class="blockquote"></blockquote>
<figcaption class="blockquote-footer">
توسط
<strong>{{item.submitter.name}}</strong>
<cite title="Source Title">
در تاریخ
{{Jdate.jdate('Y/n/d',item.dateSubmit)}}
</cite>
</figcaption>
</figure>
</div>
<main class="min-h-screen bg-gray-50">
<!-- هدر پست -->
<div class="bg-white border-b border-gray-200">
<div class="container mx-auto px-4 py-8">
<div class="max-w-4xl mx-auto">
<!-- برچسب‌ها -->
{% if item.tree|length > 0 %}
<div class="flex flex-wrap gap-2 mb-4">
{% for tree in item.tree %}
<span class="px-3 py-1 bg-blue-100 text-blue-800 text-sm font-medium rounded-full">
{{ tree.label }}
</span>
{% endfor %}
</div>
{% endif %}
<!-- عنوان -->
<h1 class="text-3xl lg:text-4xl font-bold text-gray-900 mb-6 leading-tight">
{{item.title}}
</h1>
<!-- متا اطلاعات -->
<div class="flex flex-wrap items-center gap-6 text-sm text-gray-600 mb-8">
<div class="flex items-center space-x-2 space-x-reverse">
<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="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>توسط <strong class="text-gray-900">{{item.submitter.name}}</strong></span>
</div>
<div class="flex items-center space-x-2 space-x-reverse">
<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="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span>{{Jdate.jdate('Y/n/d',item.dateSubmit)}}</span>
</div>
<div class="flex items-center space-x-2 space-x-reverse">
<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 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>{{ item.views }} بازدید</span>
</div>
</div>
<div class="post-content mt-4">
{{ item.body | raw }}
</div>
</article>
</div>
<div class="col-sm-12 col-md-4">
<div class="recent-posts">
<h3 class="text-primary mb-3">جدیدترین‌ها</h3>
{% for post in posts %}
<div class="card mb-3">
<img src="{{asset('uploaded/'~ post.mainPic)}}" class="card-img-top" alt="{{post.title}}">
<div class="card-body">
<a href="{{path('app_blog_post',{'url':post.url})}}" class="stretched-link text-decoration-none">
<h5 class="card-title text-primary">{{post.title}}</h5>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- محتوای اصلی -->
<div class="container mx-auto px-4 py-12">
<div class="max-w-6xl mx-auto">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
<!-- محتوای پست -->
<div class="lg:col-span-2">
<article class="prose prose-lg max-w-none">
<!-- تصویر اصلی -->
<div class="mb-8">
<img src="{{asset('uploaded/'~ item.mainPic)}}"
alt="{{item.title}}"
class="w-full h-64 lg:h-80 object-cover rounded-2xl shadow-lg hover:shadow-xl transition-shadow duration-300">
</div>
<!-- خلاصه پست -->
{% if item.intro %}
<div class="bg-blue-50 border-r-4 border-blue-500 p-6 rounded-lg mb-8">
<p class="text-lg text-gray-700 leading-relaxed font-medium">
{{item.intro}}
</p>
</div>
{% endif %}
<!-- محتوای پست -->
<div class="prose prose-lg max-w-none text-gray-800 leading-relaxed">
{{ item.body | raw }}
</div>
<!-- اشتراک‌گذاری -->
<div class="mt-12 pt-8 border-t border-gray-200">
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-4">این مطلب را به اشتراک بگذارید</h3>
<div class="flex space-x-3 space-x-reverse">
<a href="https://twitter.com/intent/tweet?text={{item.title|url_encode}}&url={{app.request.uri|url_encode}}"
target="_blank"
class="flex items-center space-x-2 space-x-reverse px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors duration-200">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
</svg>
<span>توییتر</span>
</a>
<a href="https://www.linkedin.com/sharing/share-offsite/?url={{app.request.uri|url_encode}}"
target="_blank"
class="flex items-center space-x-2 space-x-reverse px-4 py-2 bg-blue-700 text-white rounded-lg hover:bg-blue-800 transition-colors duration-200">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
<span>لینکدین</span>
</a>
<a href="https://t.me/share/url?url={{app.request.uri|url_encode}}&text={{item.title|url_encode}}"
target="_blank"
class="flex items-center space-x-2 space-x-reverse px-4 py-2 bg-blue-400 text-white rounded-lg hover:bg-blue-500 transition-colors duration-200">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/>
</svg>
<span>تلگرام</span>
</a>
</div>
</div>
</div>
</div>
</article>
</div>
<!-- سایدبار -->
<div class="lg:col-span-1">
<div class="sticky top-8">
<!-- پست‌های مرتبط -->
{% if posts|length > 0 %}
<div class="bg-white rounded-2xl shadow-soft p-6 mb-8">
<h3 class="text-xl font-bold text-gray-900 mb-6 flex items-center">
<svg class="w-5 h-5 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="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
</svg>
جدیدترین‌ها
</h3>
<div class="space-y-4">
{% for post in posts %}
<a href="{{path('app_blog_post',{'url':post.url})}}"
class="group block bg-gray-50 rounded-xl p-4 hover:bg-gray-100 transition-colors duration-200">
<div class="flex space-x-3 space-x-reverse">
<img src="{{asset('uploaded/'~ post.mainPic)}}"
alt="{{post.title}}"
class="w-16 h-16 object-cover rounded-lg flex-shrink-0">
<div class="flex-1 min-w-0">
<h4 class="text-sm font-semibold text-gray-900 group-hover:text-blue-600 transition-colors duration-200 line-clamp-2">
{{post.title}}
</h4>
<p class="text-xs text-gray-500 mt-1">
{{ Jdate.jdate('Y/n/d',post.dateSubmit) }}
</p>
</div>
</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- دکمه بازگشت به وبلاگ -->
<div class="bg-gradient-to-br from-blue-50 to-purple-50 rounded-2xl p-6 text-center">
<h3 class="text-lg font-semibold text-gray-900 mb-4">بازگشت به وبلاگ</h3>
<p class="text-sm text-gray-600 mb-4">مطالب بیشتری را در وبلاگ ما بخوانید</p>
<a href="{{path('app_blog_home')}}"
class="inline-flex items-center space-x-2 space-x-reverse px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200 font-medium">
<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>
<span>مشاهده همه مطالب</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endif %}
{% endblock %}

View file

@ -15,15 +15,22 @@
{{ item.plain | raw}}
{% endif %}
{% if item.body is not null %}
<div class="container mt-3">
<div class="row">
<div class="col">
<h1 class="text-primary py-4">{{item.title}}</h1>
{{ item.body | raw}}
<main class="min-h-screen bg-gray-50">
<div class="container mx-auto px-4 py-12">
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-2xl shadow-soft p-8 lg:p-12">
<!-- عنوان صفحه -->
<h1 class="text-3xl lg:text-4xl font-bold text-gray-900 mb-8 leading-tight">
{{item.title}}
</h1>
<!-- محتوای صفحه -->
<div class="prose prose-lg max-w-none text-gray-800 leading-relaxed">
{{ item.body | raw}}
</div>
</div>
</div>
</div>
</div>
</main>
{% endif %}
{% endblock %}

File diff suppressed because it is too large Load diff

View file

@ -3,222 +3,299 @@
{% block title %}پرسش و پاسخ - حسابیکس{% endblock %}
{% block body %}
<div class="container my-4">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="text-primary fw-bold">پرسش و پاسخ</h1>
{% if app.user and 'ROLE_CUSTOMER' in app.user.roles %}
<a href="{{ path('qa_ask') }}" class="btn btn-primary">
<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-2">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>سوال جدید
</a>
{% else %}
<a href="{{ path('customer_login') }}" class="btn btn-outline-primary">
<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-2">
<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>ورود برای پرسیدن سوال
</a>
{% endif %}
</div>
<!-- فیلترها و جستجو -->
<div class="card mb-4">
<div class="card-body">
<form method="GET" class="row g-3">
<div class="col-md-4">
<label for="filter" class="form-label">فیلتر:</label>
<select name="filter" id="filter" class="form-select">
<option value="all" {{ currentFilter == 'all' ? 'selected' : '' }}>همه سوالات</option>
<option value="unsolved" {{ currentFilter == 'unsolved' ? 'selected' : '' }}>سوالات حل نشده</option>
<option value="solved" {{ currentFilter == 'solved' ? 'selected' : '' }}>سوالات حل شده</option>
<option value="popular" {{ currentFilter == 'popular' ? 'selected' : '' }}>محبوب‌ترین</option>
</select>
</div>
<div class="col-md-6">
<label for="search" class="form-label">جستجو:</label>
<input type="text" name="search" id="search" class="form-control"
value="{{ currentSearch }}" placeholder="جستجو در سوالات...">
</div>
<div class="col-md-2">
<label class="form-label">&nbsp;</label>
<button type="submit" class="btn btn-primary w-100">
<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">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>جستجو
</button>
</div>
</form>
<main class="min-h-screen bg-gray-50">
<!-- هدر Q&A -->
<div class="bg-gradient-to-br from-blue-600 via-purple-600 to-indigo-700">
<div class="container mx-auto px-4 py-12">
<div class="max-w-6xl mx-auto">
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between">
<div class="text-white mb-6 lg:mb-0">
<h1 class="text-4xl lg:text-5xl font-bold mb-4 animate-fade-in-up">
پرسش و پاسخ
</h1>
<p class="text-lg text-blue-100 animate-fade-in-up" style="animation-delay: 0.2s;">
سوالات خود را بپرسید و از تجربه دیگران استفاده کنید
</p>
</div>
<div class="animate-fade-in-up" style="animation-delay: 0.4s;">
{% if app.user and 'ROLE_CUSTOMER' in app.user.roles %}
<a href="{{ path('qa_ask') }}"
class="inline-flex items-center space-x-2 space-x-reverse px-6 py-3 bg-white text-blue-600 rounded-xl hover:bg-blue-50 transition-all duration-200 font-medium shadow-lg hover:shadow-xl transform hover:-translate-y-1">
<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-blue-500 text-white rounded-xl hover:bg-blue-600 transition-all duration-200 font-medium shadow-lg hover:shadow-xl transform hover:-translate-y-1">
<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>
</div>
</div>
</div>
</div>
<div class="row">
<!-- لیست سوالات -->
<div class="col-lg-9">
<!-- محتوای اصلی -->
<div class="container mx-auto px-4 py-8">
<div class="max-w-6xl mx-auto">
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
<!-- محتوای اصلی -->
<div class="lg:col-span-3">
<!-- فیلترها و جستجو -->
<div class="bg-white rounded-2xl shadow-soft p-6 mb-8">
<form method="GET" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="filter" class="block text-sm font-medium text-gray-700 mb-2">فیلتر:</label>
<select name="filter" id="filter"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200">
<option value="all" {{ currentFilter == 'all' ? 'selected' : '' }}>همه سوالات</option>
<option value="unsolved" {{ currentFilter == 'unsolved' ? 'selected' : '' }}>سوالات حل نشده</option>
<option value="solved" {{ currentFilter == 'solved' ? 'selected' : '' }}>سوالات حل شده</option>
<option value="popular" {{ currentFilter == 'popular' ? 'selected' : '' }}>محبوب‌ترین</option>
</select>
</div>
<div>
<label for="search" class="block text-sm font-medium text-gray-700 mb-2">جستجو:</label>
<input type="text" name="search" id="search"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
value="{{ currentSearch }}" placeholder="جستجو در سوالات...">
</div>
<div class="flex items-end">
<button type="submit"
class="w-full 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">
<div class="flex items-center justify-center space-x-2 space-x-reverse">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
<span>جستجو</span>
</div>
</button>
</div>
</div>
</form>
</div>
<!-- لیست سوالات -->
{% if questions is empty %}
<div class="card">
<div class="card-body text-center py-5">
<i class="fas fa-question-circle fa-3x text-muted mb-3"></i>
<h4 class="text-muted">سوالی یافت نشد</h4>
<p class="text-muted">هنوز سوالی در این دسته‌بندی وجود ندارد.</p>
<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.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>
<h3 class="text-xl font-semibold text-gray-900 mb-2">سوالی یافت نشد</h3>
<p class="text-gray-600 mb-6">هنوز سوالی در این دسته‌بندی وجود ندارد.</p>
{% if app.user and 'ROLE_CUSTOMER' in app.user.roles %}
<a href="{{ path('qa_ask') }}" class="btn btn-primary">اولین سوال را بپرسید</a>
<a href="{{ path('qa_ask') }}"
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>
{% endif %}
</div>
</div>
{% else %}
{% for question in questions %}
<div class="card mb-3 question-card">
<div class="card-body">
<div class="row">
<div class="col-2 col-md-1 text-center">
<div class="d-flex flex-column align-items-center">
<div class="vote-count {{ question.votes > 0 ? 'text-success' : (question.votes < 0 ? 'text-danger' : 'text-muted') }}">
{{ question.votes }}
<div class="space-y-6">
{% for question in questions %}
<div class="bg-white rounded-2xl shadow-soft hover:shadow-medium transition-all duration-300 overflow-hidden hover-lift group">
<div class="p-6">
<div class="flex space-x-4 space-x-reverse">
<!-- آمار سوال -->
<div class="flex flex-col items-center space-y-2 min-w-0">
<div class="text-center">
<div class="text-2xl font-bold {{ question.votes > 0 ? 'text-green-600' : (question.votes < 0 ? 'text-red-600' : 'text-gray-500') }}">
{{ question.votes }}
</div>
<div class="text-xs text-gray-500">رای</div>
</div>
<small class="text-muted">رای</small>
</div>
</div>
<div class="col-2 col-md-1 text-center">
<div class="d-flex flex-column align-items-center">
<div class="answer-count {{ question.answers|length > 0 ? 'text-success' : 'text-muted' }}">
{{ question.answers|length }}
<div class="text-center">
<div class="text-lg font-semibold {{ question.answers|length > 0 ? 'text-green-600' : 'text-gray-500' }}">
{{ question.answers|length }}
</div>
<div class="text-xs text-gray-500">پاسخ</div>
</div>
<small class="text-muted">پاسخ</small>
</div>
</div>
<div class="col-8 col-md-10">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title mb-0">
<a href="{{ path('qa_question_show', {'id': question.id}) }}"
class="text-decoration-none text-dark">
{{ question.title }}
</a>
</h5>
{% if question.isSolved %}
<span class="badge bg-success">
<i class="fas fa-check me-1"></i>حل شده
</span>
{% endif %}
</div>
<div class="card-text text-muted mb-2 question-preview">
{% set content = question.content|markdown|raw %}
{% set plainText = content|striptags %}
{% if plainText|length > 200 %}
{{ plainText|slice(0, 200) }}...
{% else %}
{{ plainText }}
{% endif %}
</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 }}
<!-- محتوای سوال -->
<div class="flex-1 min-w-0">
<div class="flex items-start justify-between mb-3">
<h3 class="text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors duration-200">
<a href="{{ path('qa_question_show', {'id': question.id}) }}"
class="hover:underline">
{{ question.title }}
</a>
{% endfor %}
</h3>
{% if question.isSolved %}
<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">
<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="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 class="text-gray-600 mb-4 line-clamp-3 leading-relaxed">
{% set content = question.content|markdown|raw %}
{% set plainText = content|striptags %}
{% if plainText|length > 200 %}
{{ plainText|slice(0, 200) }}...
{% else %}
{{ plainText }}
{% endif %}
</div>
<div class="flex flex-wrap items-center justify-between gap-4">
<!-- تگ‌ها -->
<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-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>{{ 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>
{% endfor %}
{% endfor %}
</div>
<!-- صفحه‌بندی -->
{% if totalPages > 1 %}
<nav aria-label="صفحه‌بندی سوالات">
<ul class="pagination justify-content-center">
{% if currentPage > 1 %}
<li class="page-item">
<a class="page-link" href="?page={{ currentPage - 1 }}{{ currentFilter != 'all' ? '&filter=' ~ currentFilter : '' }}{{ currentSearch ? '&search=' ~ currentSearch : '' }}{{ currentTag ? '&tag=' ~ currentTag : '' }}">قبلی</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 }}{{ currentFilter != 'all' ? '&filter=' ~ currentFilter : '' }}{{ currentSearch ? '&search=' ~ currentSearch : '' }}{{ currentTag ? '&tag=' ~ currentTag : '' }}">{{ page }}</a>
</li>
<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 }}{{ currentFilter != 'all' ? '&filter=' ~ currentFilter : '' }}{{ currentSearch ? '&search=' ~ currentSearch : '' }}{{ currentTag ? '&tag=' ~ currentTag : '' }}"
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 %}
{% endfor %}
{% if currentPage < totalPages %}
<li class="page-item">
<a class="page-link" href="?page={{ currentPage + 1 }}{{ currentFilter != 'all' ? '&filter=' ~ currentFilter : '' }}{{ currentSearch ? '&search=' ~ currentSearch : '' }}{{ currentTag ? '&tag=' ~ currentTag : '' }}">بعدی</a>
</li>
{% endif %}
</ul>
</nav>
<!-- شماره صفحات -->
<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 }}{{ currentFilter != 'all' ? '&filter=' ~ currentFilter : '' }}{{ currentSearch ? '&search=' ~ currentSearch : '' }}{{ currentTag ? '&tag=' ~ currentTag : '' }}"
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 }}{{ currentFilter != 'all' ? '&filter=' ~ currentFilter : '' }}{{ currentSearch ? '&search=' ~ currentSearch : '' }}{{ currentTag ? '&tag=' ~ currentTag : '' }}"
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="col-lg-3">
<!-- تگ‌های محبوب -->
<div class="card mb-4">
<div class="card-header">
<h6 class="mb-0">
<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-2">
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path>
<line x1="7" y1="7" x2="7.01" y2="7"></line>
</svg>تگ‌های محبوب
</h6>
</div>
<div class="card-body">
{% for tag in popularTags %}
<a href="{{ path('qa_tag_questions', {'name': tag.name}) }}"
class="badge bg-light text-dark text-decoration-none me-1 mb-2 d-inline-block">
{{ tag.name }}
<span class="text-muted">({{ tag.usageCount }})</span>
</a>
{% endfor %}
<div class="mt-3">
<a href="{{ path('qa_tags') }}" class="btn btn-outline-primary btn-sm">
مشاهده همه تگ‌ها
<div class="lg:col-span-1">
<div class="space-y-6">
<!-- تگ‌های محبوب -->
<div class="bg-white rounded-2xl shadow-soft p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4 flex items-center">
<svg class="w-5 h-5 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="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg>
تگ‌های محبوب
</h3>
<div class="flex flex-wrap gap-2">
{% for tag in popularTags %}
<a href="{{ path('qa_tag_questions', {'name': 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">
{{ tag.name }}
<span class="text-blue-600 mr-1">({{ tag.usageCount }})</span>
</a>
{% endfor %}
</div>
<div class="mt-4">
<a href="{{ path('qa_tags') }}"
class="inline-flex items-center space-x-2 space-x-reverse text-blue-600 hover:text-blue-700 font-medium transition-colors 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="M9 5l7 7-7 7"></path>
</svg>
</a>
</div>
</div>
</div>
<!-- آمار -->
<div class="card">
<div class="card-header">
<h6 class="mb-0">
<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-2">
<path d="M18 20V10"></path>
<path d="M12 20V4"></path>
<path d="M6 20v-6"></path>
</svg>آمار
</h6>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-6">
<div class="h4 text-primary">{{ questions|length }}</div>
<small class="text-muted">سوال</small>
<!-- آمار -->
<div class="bg-gradient-to-br from-blue-50 to-purple-50 rounded-2xl p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4 flex items-center">
<svg class="w-5 h-5 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="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
آمار
</h3>
<div class="grid grid-cols-2 gap-4 text-center">
<div>
<div class="text-2xl font-bold text-blue-600">{{ questions|length }}</div>
<div class="text-sm text-gray-600">سوال</div>
</div>
<div class="col-6">
<div class="h4 text-success">{{ popularTags|length }}</div>
<small class="text-muted">تگ</small>
<div>
<div class="text-2xl font-bold text-green-600">{{ popularTags|length }}</div>
<div class="text-sm text-gray-600">تگ</div>
</div>
</div>
</div>
@ -227,80 +304,6 @@
</div>
</div>
</div>
</div>
</main>
<style>
.question-card {
transition: all 0.3s ease;
border-left: 4px solid transparent;
}
.question-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-left-color: #0d6efd;
}
.vote-count, .answer-count {
font-size: 1.2rem;
font-weight: bold;
}
.tags .badge {
font-size: 0.8rem;
transition: all 0.3s ease;
}
.tags .badge:hover {
background-color: #0d6efd !important;
color: white !important;
}
.card-title a:hover {
color: #0d6efd !important;
}
.question-preview {
line-height: 1.6;
max-height: 4.8em; /* حدود 3 خط */
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.question-card {
transition: all 0.3s ease;
border-left: 4px solid transparent;
}
.question-card:hover {
border-left-color: #0d6efd;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.question-stats {
font-size: 0.9rem;
color: #6c757d;
}
.question-stats .stat-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 0.5rem;
}
.question-stats .stat-number {
font-weight: bold;
font-size: 1.1rem;
color: #495057;
}
.question-stats .stat-label {
font-size: 0.8rem;
margin-top: 0.25rem;
}
</style>
{% endblock %}

View file

@ -3,65 +3,88 @@
{% block title %}{{ question.title }} - پرسش و پاسخ{% endblock %}
{% block body %}
<div class="container my-4">
<div class="row">
<div class="col-12">
<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="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 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="col-11">
<div class="d-flex justify-content-between align-items-start mb-3">
<h1 class="card-title mb-0">{{ question.title }}</h1>
<!-- محتوای سوال -->
<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="badge bg-success fs-6">
<i class="fas fa-check me-1"></i>حل شده
<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="question-content mb-3">
<div class="prose prose-lg max-w-none text-gray-800 leading-relaxed mb-6">
{{ question.content|markdown|raw }}
</div>
<div class="d-flex justify-content-between align-items-center">
<div class="tags">
<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="badge bg-light text-dark text-decoration-none me-1">
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="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 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>
@ -70,182 +93,201 @@
</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>
<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="btn btn-primary">
<i class="fas fa-plus me-2"></i>پاسخ دهید
<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="btn btn-outline-primary">
<i class="fas fa-sign-in-alt me-2"></i>ورود برای پاسخ دادن
<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="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 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 %}
{% 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"
<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 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 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="vote-count mt-2 mb-2" id="answer-votes-{{ answer.id }}">
<div class="text-2xl font-bold text-gray-900" id="answer-votes-{{ answer.id }}">
{{ answer.votes }}
</div>
<button class="btn btn-outline-danger btn-sm vote-btn"
<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 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 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>
<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 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>
{% 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 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>
</div>
{% endfor %}
{% endfor %}
</div>
<!-- صفحه‌بندی پاسخ‌ها -->
{% 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>
<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 %}
{% endfor %}
{% if currentPage < totalPages %}
<li class="page-item">
<a class="page-link" href="?page={{ currentPage + 1 }}">بعدی</a>
</li>
{% endif %}
</ul>
</nav>
<!-- شماره صفحات -->
<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">
<a href="{{ path('qa_index') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-right me-2"></i>بازگشت به لیست سوالات
<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>
</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>
</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() {
@ -266,7 +308,9 @@ function initializeVoteButtons() {
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
if (window.notification) {
window.notification.error(data.error);
}
return;
}
@ -275,27 +319,35 @@ function initializeVoteButtons() {
// تغییر رنگ دکمه‌ها
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');
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('btn-outline-success', 'btn-outline-danger');
activeButton.classList.add(data.userVote === 'up' ? 'btn-success' : 'btn-danger');
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('btn-success', 'btn-danger');
btn.classList.add(btn.dataset.upvote === 'true' ? 'btn-outline-success' : 'btn-outline-danger');
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);
alert('خطا در ارسال رای');
if (window.notification) {
window.notification.error('خطا در ارسال رای. لطفاً دوباره تلاش کنید.');
}
});
});
});
@ -305,10 +357,17 @@ function initializeVoteButtons() {
button.addEventListener('click', function() {
const answerId = this.dataset.answerId;
if (!confirm('آیا مطمئن هستید که می‌خواهید این پاسخ را بپذیرید؟')) {
// 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: {
@ -320,22 +379,56 @@ function initializeVoteButtons() {
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
if (window.notification) {
window.notification.error(data.error);
}
return;
}
location.reload();
if (window.notification) {
window.notification.success('پاسخ با موفقیت پذیرفته شد');
}
setTimeout(() => {
location.reload();
}, 1500);
})
.catch(error => {
console.error('Error:', error);
alert('خطا در پذیرفتن پاسخ');
if (window.notification) {
window.notification.error('خطا در پذیرفتن پاسخ. لطفاً دوباره تلاش کنید.');
}
})
.finally(() => {
// Reset button state
this.innerHTML = originalText;
this.disabled = false;
});
});
});
}
// اجرا در هر دو حالت
document.addEventListener('DOMContentLoaded', initializeVoteButtons);
document.addEventListener('turbo:load', initializeVoteButtons);
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 %}

View file

@ -1,335 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}
دیدگاه‌های کاربران
{% endblock %}
{% block des %}
دیدگاه سایر کاربران استفاده کننده از حسابیکس دید بهترین برای انتخاب محصولی عالی را برای شما مهیا می کند.
{% endblock %}
{% block body %}
<div class="container mt-3">
<div class="mb-3">
<h3 class="mb-4">نظرات کاربران درباره حسابیکس</h3>
<div class="row align-items-center">
<div class="col-auto text-center">
<h3 class="display-2 fw-bold">4.5</h3>
<span class="fs-6">
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-half text-warning"></i>
</span>
<p class="mb-0 fs-6">(بر اساس 27 نظر)</p>
</div>
<!-- Progress Bar -->
<div class="col order-3 order-md-2">
<div class="progress mb-3" style="height: 6px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: 90%;" aria-valuenow="90" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="progress mb-3" style="height: 6px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: 80%;" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="progress mb-3" style="height: 6px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: 70%;" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="progress mb-3" style="height: 6px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: 60%;" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="progress mb-0" style="height: 6px;">
<div class="progress-bar bg-warning" role="progressbar" style="width: 50%;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div
class="col-md-auto col-6 order-2 order-md-3">
<!-- Rating -->
<div>
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
<span class="ms-1">53%</span>
</div>
<div>
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
<span class="ms-1">36%</span>
</div>
<div>
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
<span class="ms-1">9%</span>
</div>
<div>
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
<span class="ms-1">3%</span>
</div>
<div>
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-light" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
<span class="ms-1">2%</span>
</div>
</div>
</div>
</div>
<hr class="my-5">
<div class="mb-3">
<div
class="d-lg-flex align-items-center justify-content-between mb-5">
<!-- Reviews -->
<div class="mb-3 mb-lg-0">
<h3 class="mb-0">نظرات کاربران</h3>
</div>
<div>
<form class="form-inline">
<div class="d-flex align-items-center me-2">
<span class="position-absolute ps-3">
<i class="fe fe-search"></i>
</span>
<input type="search" class="form-control ps-6" placeholder="جستجوی نظرات">
</div>
</form>
</div>
</div>
<!-- Rating -->
<div class="d-flex align-items-start border-bottom pb-4 mb-4">
<img src="{{asset('assets/images/avatar/avatar-2.jpg')}}" alt="" class="rounded-circle avatar-lg">
<div class="ms-3">
<h4 class="mb-1">
حمیدرضا تینای تهرانی
<span class="ms-1 fs-6">2 روز پیش</span>
</h4>
<div class="mb-2">
<span class="fs-6">
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
<i class="bi bi-star-fill text-warning"></i>
</span>
</div>
<p>حسابیکس بهترین نرم افزار حسابداری آنلاینی هست که تا حالا استفاده کردم. امکاناتش کامل و پشتیبانی عالی و به‌روزرسانی‌های مداوم باعث شده که همیشه از جدیدترین امکانات بهره‌مند بشم.</p>
<div class="d-lg-flex">
<p class="mb-0">آیا این نظر برای شما مفید بود؟</p>
<a href="#" class="btn btn-xs btn-primary ms-lg-3">بله</a>
<a href="#" class="btn btn-xs btn-outline-secondary ms-1">خیر</a>
</div>
</div>
</div>
<!-- Rating -->
<div class="d-flex align-items-start border-bottom pb-4 mb-4">
<img src="../assets/images/avatar/avatar-3.jpg" alt="" class="rounded-circle avatar-lg">
<div class="ms-3">
<h4 class="mb-1">Arthur Williamson
<span class="ms-1 fs-6 ">3 Days
ago</span>
</h4>
<div class="mb-2">
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
</div>
<p>Its pretty good.Just a reminder that there are also
students with Windows, meaning Figma its a bit different
of yours. Thank you!</p>
<div class="d-lg-flex">
<p class="mb-0">Was this review helpful?</p>
<a href="#" class="btn btn-xs btn-primary ms-lg-3">Yes</a>
<a href="#" class="btn btn-xs btn-outline-secondary ms-1">No</a>
</div>
</div>
</div>
<!-- Rating -->
<div class="d-flex align-items-start border-bottom pb-4 mb-4">
<img src="../assets/images/avatar/avatar-4.jpg" alt="" class="rounded-circle avatar-lg">
<div class="ms-3">
<h4 class="mb-1">Claire Jones
<span class="ms-1 fs-6 ">4 Days
ago</span>
</h4>
<div class="mb-2">
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
</div>
<p>
Great course for learning Figma, the only bad detail
would be that some icons are not included in the assets.
But 90% of the icons needed are included, and the voice
of the instructor was very clear and easy to understood.
</p>
<div class="d-lg-flex">
<p class="mb-0">Was this review helpful?</p>
<a href="#" class="btn btn-xs btn-primary ms-lg-3">Yes</a>
<a href="#" class="btn btn-xs btn-outline-secondary ms-1">No</a>
</div>
</div>
</div>
<!-- Rating -->
<div class="d-flex align-items-start">
<img src="../assets/images/avatar/avatar-5.jpg" alt="" class="rounded-circle avatar-lg">
<div class="ms-3">
<h4 class="mb-1">
Bessie Pena
<span class="ms-1 fs-6 ">5 Days
ago</span>
</h4>
<div class="mb-2">
<span class="fs-6">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" class="bi bi-star-fill text-warning" viewbox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>
</span>
</div>
<p>
I have really enjoyed this class and learned a lot,
found it very inspiring and helpful, thank you!
</p>
<div class="d-lg-flex">
<p class="mb-0">Was this review helpful?</p>
<a href="#" class="btn btn-xs btn-primary ms-lg-3">Yes</a>
<a href="#" class="btn btn-xs btn-outline-secondary ms-1">No</a>
</div>
</div>
</div>
</div>
</div>
<style>
.avatar-lg {
width: 60px;
height: 60px;
object-fit: cover;
}
.progress {
background-color: #e9ecef;
}
.progress-bar {
background-color: #ffc107;
}
.btn-xs {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.border-bottom {
border-bottom: 1px solid #dee2e6 !important;
}
.text-warning {
color: #ffc107 !important;
}
</style>
{% endblock %}

View file

@ -59,6 +59,9 @@ Encore
// enables Sass/SCSS support
.enableSassLoader()
// enables PostCSS support for Tailwind
.enablePostCssLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()