|
|
|
|
|
|
| document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
| if (window.gsap) {
|
| gsap.registerPlugin(ScrollTrigger);
|
| initAnimations();
|
| }
|
|
|
| initSidebar();
|
| initAlerts();
|
| initSearch();
|
| initTooltips();
|
| });
|
|
|
| |
| |
|
|
| function initAnimations() {
|
|
|
|
|
| gsap.from('.topbar', {
|
| y: -64, opacity: 0, duration: .5,
|
| ease: 'power3.out'
|
| });
|
|
|
|
|
| gsap.from('.nav-item', {
|
| x: -24, opacity: 0, duration: .4,
|
| stagger: .04, ease: 'power2.out', delay: .15
|
| });
|
|
|
|
|
| gsap.from('.page-header', {
|
| y: 20, opacity: 0, duration: .5,
|
| ease: 'power3.out', delay: .2
|
| });
|
|
|
|
|
| gsap.from('.stat-card', {
|
| y: 32, opacity: 0, duration: .5,
|
| stagger: .08, ease: 'power3.out', delay: .3,
|
| clearProps: 'all'
|
| });
|
|
|
|
|
| document.querySelectorAll('.stat-value[data-count]').forEach(el => {
|
| const target = parseInt(el.dataset.count, 10);
|
| gsap.from({ val: 0 }, {
|
| val: target, duration: 1.2, delay: .5,
|
| ease: 'power2.out',
|
| onUpdate: function () {
|
| el.textContent = Math.round(this.targets()[0].val).toLocaleString();
|
| }
|
| });
|
| });
|
|
|
|
|
| gsap.utils.toArray('.card').forEach((card, i) => {
|
| gsap.from(card, {
|
| scrollTrigger: {
|
| trigger: card,
|
| start: 'top 88%',
|
| toggleActions: 'play none none none'
|
| },
|
| y: 28, opacity: 0, duration: .5,
|
| delay: i * .04,
|
| ease: 'power3.out',
|
| clearProps: 'all'
|
| });
|
| });
|
|
|
|
|
| gsap.utils.toArray('tbody tr').forEach((row, i) => {
|
| gsap.from(row, {
|
| scrollTrigger: {
|
| trigger: row,
|
| start: 'top 92%',
|
| toggleActions: 'play none none none'
|
| },
|
| x: -16, opacity: 0, duration: .35,
|
| delay: i * .03,
|
| ease: 'power2.out',
|
| clearProps: 'all'
|
| });
|
| });
|
|
|
|
|
| document.querySelectorAll('.btn').forEach(btn => {
|
| btn.addEventListener('mouseenter', () => {
|
| gsap.to(btn, { scale: 1.03, duration: .15, ease: 'power1.out' });
|
| });
|
| btn.addEventListener('mouseleave', () => {
|
| gsap.to(btn, { scale: 1, duration: .15, ease: 'power1.out' });
|
| });
|
| });
|
|
|
|
|
| document.querySelectorAll('.progress-bar').forEach(bar => {
|
| const width = bar.style.width || bar.dataset.width || '0%';
|
| bar.style.width = '0%';
|
| gsap.to(bar, {
|
| scrollTrigger: { trigger: bar, start: 'top 90%' },
|
| width: width, duration: .9, ease: 'power3.out', delay: .1
|
| });
|
| });
|
|
|
|
|
| gsap.utils.toArray('.notice-card').forEach((card, i) => {
|
| gsap.from(card, {
|
| scrollTrigger: { trigger: card, start: 'top 90%' },
|
| y: 20, opacity: 0, duration: .4,
|
| delay: i * .06, ease: 'power2.out', clearProps: 'all'
|
| });
|
| });
|
|
|
|
|
| const loginCard = document.querySelector('.login-card');
|
| if (loginCard) {
|
| gsap.from(loginCard, {
|
| y: 40, opacity: 0, scale: .96,
|
| duration: .6, ease: 'power3.out'
|
| });
|
| gsap.from('.login-card .form-group', {
|
| y: 16, opacity: 0, duration: .4,
|
| stagger: .08, delay: .3, ease: 'power2.out'
|
| });
|
| }
|
|
|
|
|
| const banner = document.querySelector('.dashboard-banner');
|
| if (banner) {
|
| gsap.from(banner, {
|
| y: -20, opacity: 0, duration: .6, ease: 'power3.out', delay: .1
|
| });
|
| gsap.from(banner.querySelectorAll('h1, p, .btn'), {
|
| y: 16, opacity: 0, stagger: .1, duration: .5, delay: .3, ease: 'power2.out'
|
| });
|
| }
|
| }
|
|
|
| |
| |
|
|
| function initSidebar() {
|
| const toggle = document.getElementById('sidebar-toggle');
|
| const sidebar = document.querySelector('.sidebar');
|
| const overlay = document.getElementById('sidebar-overlay');
|
|
|
| if (!toggle || !sidebar) return;
|
|
|
| toggle.addEventListener('click', () => {
|
| const isOpen = sidebar.classList.toggle('open');
|
| if (overlay) overlay.classList.toggle('active', isOpen);
|
| if (window.gsap) {
|
| gsap.from(sidebar, { x: -260, duration: .3, ease: 'power3.out' });
|
| }
|
| });
|
|
|
| if (overlay) {
|
| overlay.addEventListener('click', () => {
|
| sidebar.classList.remove('open');
|
| overlay.classList.remove('active');
|
| });
|
| }
|
| }
|
|
|
| |
| |
|
|
| function initAlerts() {
|
| document.querySelectorAll('.alert').forEach(alert => {
|
| const close = alert.querySelector('.alert-close');
|
| if (close) {
|
| close.addEventListener('click', () => dismissAlert(alert));
|
| }
|
| setTimeout(() => dismissAlert(alert), 5000);
|
| });
|
| }
|
|
|
| function dismissAlert(alert) {
|
| if (!alert.parentNode) return;
|
| if (window.gsap) {
|
| gsap.to(alert, {
|
| opacity: 0, y: -8, height: 0, marginBottom: 0,
|
| padding: 0, duration: .3, ease: 'power2.in',
|
| onComplete: () => alert.remove()
|
| });
|
| } else {
|
| alert.remove();
|
| }
|
| }
|
|
|
| |
| |
|
|
| function initSearch() {
|
| const searchInput = document.getElementById('table-search');
|
| if (!searchInput) return;
|
|
|
| searchInput.addEventListener('input', () => {
|
| const q = searchInput.value.toLowerCase();
|
| document.querySelectorAll('tbody tr').forEach(row => {
|
| const text = row.textContent.toLowerCase();
|
| row.style.display = text.includes(q) ? '' : 'none';
|
| });
|
| });
|
| }
|
|
|
| |
| |
|
|
| function initTooltips() {
|
| document.querySelectorAll('[data-tooltip]').forEach(el => {
|
| el.addEventListener('mouseenter', e => {
|
| const tip = document.createElement('div');
|
| tip.className = 'gsap-tooltip';
|
| tip.textContent = el.dataset.tooltip;
|
| tip.style.cssText = `
|
| position:fixed;background:#0f172a;color:#fff;font-size:12px;
|
| padding:5px 10px;border-radius:6px;pointer-events:none;z-index:9999;
|
| white-space:nowrap;box-shadow:0 4px 12px rgba(0,0,0,.2);
|
| `;
|
| document.body.appendChild(tip);
|
| const rect = el.getBoundingClientRect();
|
| tip.style.left = rect.left + rect.width / 2 - tip.offsetWidth / 2 + 'px';
|
| tip.style.top = rect.top - tip.offsetHeight - 8 + 'px';
|
| if (window.gsap) gsap.from(tip, { opacity: 0, y: 4, duration: .15 });
|
| el._tooltip = tip;
|
| });
|
| el.addEventListener('mouseleave', () => {
|
| if (el._tooltip) { el._tooltip.remove(); el._tooltip = null; }
|
| });
|
| });
|
| } |