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

334 lines
12 KiB
JavaScript

import './bootstrap.js';
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/
// any CSS you import will output into a single css file (app.css in this case)
import './styles/tailwind.css';
import './styles/app.css';
// 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;
}
});
});
});