mirror of
https://github.com/Alvin-Zilverstand/school.git
synced 2026-03-07 05:52:46 +01:00
add challenge 11(portifolio)
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
// Dark mode functionality with enhanced animations
|
||||
function initDarkMode() {
|
||||
|
||||
// Check for saved theme preference or use system preference
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
if (savedTheme === 'dark' || (!savedTheme && systemPrefersDark)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
// Remove existing toggle button if it exists
|
||||
const existingButton = document.getElementById('theme-toggle');
|
||||
if (existingButton) {
|
||||
existingButton.remove();
|
||||
}
|
||||
|
||||
// Create and add the theme toggle button with fallback styling
|
||||
const toggleButton = document.createElement('button');
|
||||
toggleButton.id = 'theme-toggle';
|
||||
toggleButton.setAttribute('aria-label', 'Toggle dark mode');
|
||||
|
||||
// Use inline styles as fallback
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
toggleButton.style.cssText = `
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
z-index: 9999;
|
||||
background: ${isDark ? '#334155' : '#ffffff'};
|
||||
border: 2px solid ${isDark ? '#475569' : '#e2e8f0'};
|
||||
border-radius: 50%;
|
||||
padding: 0.75rem;
|
||||
box-shadow: 0 4px 12px ${isDark ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.1)'};
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
`;
|
||||
|
||||
toggleButton.innerHTML = `
|
||||
<svg class="w-6 h-6 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="color: ${isDark ? '#f1f5f9' : '#1e293b'}">
|
||||
<path class="sun-icon" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
<path class="moon-icon" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" style="display: ${isDark ? 'block' : 'none'}" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
document.body.appendChild(toggleButton);
|
||||
|
||||
// Add click handler with enhanced animation
|
||||
toggleButton.addEventListener('click', () => {
|
||||
|
||||
// Add click animation
|
||||
toggleButton.style.transform = 'scale(0.9)';
|
||||
setTimeout(() => {
|
||||
toggleButton.style.transform = '';
|
||||
}, 150);
|
||||
|
||||
// Toggle theme
|
||||
const newIsDark = document.documentElement.classList.toggle('dark');
|
||||
localStorage.setItem('theme', newIsDark ? 'dark' : 'light');
|
||||
|
||||
// Update button styles
|
||||
toggleButton.style.background = newIsDark ? '#334155' : '#ffffff';
|
||||
toggleButton.style.borderColor = newIsDark ? '#475569' : '#e2e8f0';
|
||||
toggleButton.style.boxShadow = newIsDark ? '0 4px 12px rgba(0,0,0,0.3)' : '0 4px 12px rgba(0,0,0,0.1)';
|
||||
|
||||
const svg = toggleButton.querySelector('svg');
|
||||
svg.style.color = newIsDark ? '#f1f5f9' : '#1e293b';
|
||||
|
||||
const sunIcon = toggleButton.querySelector('.sun-icon');
|
||||
const moonIcon = toggleButton.querySelector('.moon-icon');
|
||||
|
||||
if (newIsDark) {
|
||||
sunIcon.style.display = 'none';
|
||||
moonIcon.style.display = 'block';
|
||||
} else {
|
||||
sunIcon.style.display = 'block';
|
||||
moonIcon.style.display = 'none';
|
||||
}
|
||||
|
||||
// Add theme transition effect
|
||||
addThemeTransitionEffect();
|
||||
});
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||
if (!localStorage.getItem('theme')) {
|
||||
if (e.matches) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addThemeTransitionEffect() {
|
||||
// Create a temporary overlay for smooth transition
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: ${document.documentElement.classList.contains('dark') ? '#0f172a' : '#ffffff'};
|
||||
z-index: 9998;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease;
|
||||
`;
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Fade in and out
|
||||
requestAnimationFrame(() => {
|
||||
overlay.style.opacity = '0.1';
|
||||
setTimeout(() => {
|
||||
overlay.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
if (document.body.contains(overlay)) {
|
||||
document.body.removeChild(overlay);
|
||||
}
|
||||
}, 300);
|
||||
}, 150);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize dark mode when the DOM is loaded
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initDarkMode);
|
||||
} else {
|
||||
// DOM is already loaded
|
||||
initDarkMode();
|
||||
}
|
||||
|
||||
// Also try to initialize after a short delay to ensure everything is ready
|
||||
setTimeout(initDarkMode, 100);
|
||||
188
projects/challenge 11/portifolio(work_in_progress)/js/main.js
Normal file
188
projects/challenge 11/portifolio(work_in_progress)/js/main.js
Normal file
@@ -0,0 +1,188 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const container = document.getElementById('projects');
|
||||
|
||||
// Add loading state with enhanced spinner
|
||||
container.innerHTML = '<div class="col-span-full text-center p-8"><div class="spinner mx-auto mb-4"></div><p class="text-gray-500 dark:text-gray-400">Loading projects...</p></div>';
|
||||
|
||||
// Create modal element with enhanced styling
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay fixed inset-0 hidden items-center justify-center z-50 transition-opacity duration-300';
|
||||
modal.innerHTML = `
|
||||
<div class="modal-content max-w-4xl w-full mx-4 max-h-[90vh] overflow-y-auto transform transition-all duration-300 scale-95 opacity-0">
|
||||
<div class="p-8">
|
||||
<div class="flex justify-between items-start mb-6">
|
||||
<h2 class="text-3xl font-bold"></h2>
|
||||
<button class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700" onclick="closeModal()">
|
||||
<svg class="w-8 h-8" 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>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-image mb-6"></div>
|
||||
<div class="modal-description text-gray-700 dark:text-gray-300 mb-6 text-lg"></div>
|
||||
<div class="modal-tags mb-6"></div>
|
||||
<div class="modal-links"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Add modal functions to window
|
||||
window.openModal = (project) => {
|
||||
const modalTitle = modal.querySelector('h2');
|
||||
const modalImage = modal.querySelector('.modal-image');
|
||||
const modalDescription = modal.querySelector('.modal-description');
|
||||
const modalTags = modal.querySelector('.modal-tags');
|
||||
const modalLinks = modal.querySelector('.modal-links');
|
||||
const modalContent = modal.querySelector('.modal-content');
|
||||
|
||||
modalTitle.textContent = project.title;
|
||||
|
||||
if (project.image_url) {
|
||||
modalImage.innerHTML = `<img src="${project.image_url}" alt="${project.title}" class="project-image w-full h-96 object-cover shadow-lg">`;
|
||||
} else {
|
||||
modalImage.innerHTML = '<div class="w-full h-96 bg-gradient-to-br from-blue-100 to-purple-100 dark:from-blue-900 dark:to-purple-900 rounded-lg flex items-center justify-center"><svg class="w-24 h-24 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg></div>';
|
||||
}
|
||||
|
||||
modalDescription.innerHTML = `
|
||||
<p class="text-lg mb-4 leading-relaxed">${project.description || ''}</p>
|
||||
${project.long_description ? `<p class="mt-6 leading-relaxed text-gray-600 dark:text-gray-400">${project.long_description}</p>` : ''}
|
||||
`;
|
||||
|
||||
if (project.tags) {
|
||||
modalTags.innerHTML = `
|
||||
<div class="flex flex-wrap gap-2">
|
||||
${project.tags.split(',').map(tag =>
|
||||
`<span class="tag">${tag.trim()}</span>`
|
||||
).join('')}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
modalTags.innerHTML = '';
|
||||
}
|
||||
|
||||
modalLinks.innerHTML = project.project_url ?
|
||||
`<a href="${project.project_url}" target="_blank" class="btn-primary inline-block">View Project →</a>` : '';
|
||||
|
||||
// Show modal with animation
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// Trigger animation
|
||||
requestAnimationFrame(() => {
|
||||
modalContent.classList.remove('scale-95', 'opacity-0');
|
||||
modalContent.classList.add('scale-100', 'opacity-100');
|
||||
});
|
||||
};
|
||||
|
||||
window.closeModal = () => {
|
||||
const modalContent = modal.querySelector('.modal-content');
|
||||
|
||||
// Start closing animation
|
||||
modalContent.classList.remove('scale-100', 'opacity-100');
|
||||
modalContent.classList.add('scale-95', 'opacity-0');
|
||||
|
||||
// Wait for animation to finish before hiding
|
||||
setTimeout(() => {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
document.body.style.overflow = '';
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// Close modal when clicking outside
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Close modal with Escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Enhanced project fetching with error handling
|
||||
fetch('php/get_projects.php')
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP error! status: ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(projects => {
|
||||
if (!Array.isArray(projects) || projects.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="col-span-full text-center p-8">
|
||||
<div class="w-24 h-24 mx-auto mb-4 text-gray-400">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" 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>
|
||||
</div>
|
||||
<p class="text-gray-500 dark:text-gray-400 text-lg">No projects found.</p>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500 mt-2">Projects will appear here once added.</p>
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
// Add projects with CSS animations
|
||||
projects.forEach((project) => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'project-card p-5 flex flex-col cursor-pointer hover-lift';
|
||||
|
||||
card.onclick = () => openModal(project);
|
||||
|
||||
if (project.image_url) {
|
||||
card.innerHTML += `<img src="${project.image_url}" alt="${project.title}" class="project-image mb-4 h-40 object-cover w-full">`;
|
||||
} else {
|
||||
card.innerHTML += `<div class="project-image mb-4 h-40 bg-gradient-to-br from-blue-100 to-purple-100 dark:from-blue-900 dark:to-purple-900 rounded-lg flex items-center justify-center"><svg class="w-16 h-16 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg></div>`;
|
||||
}
|
||||
|
||||
card.innerHTML += `
|
||||
<h2 class="text-xl font-semibold mb-2">${project.title}</h2>
|
||||
<p class="text-gray-700 dark:text-gray-300 mb-3 flex-grow">${project.description || ''}</p>
|
||||
${project.tags ? `<div class="mb-3 flex flex-wrap gap-1">${project.tags.split(',').map(tag => `<span class="tag text-xs">${tag.trim()}</span>`).join('')}</div>` : ''}
|
||||
${project.project_url ? `<a href="${project.project_url}" target="_blank" class="mt-auto inline-block text-blue-600 dark:text-blue-400 hover:underline font-medium transition-colors" onclick="event.stopPropagation()">View Project →</a>` : ''}
|
||||
`;
|
||||
|
||||
container.appendChild(card);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error fetching projects:', err);
|
||||
container.innerHTML = `
|
||||
<div class="col-span-full text-center p-8">
|
||||
<div class="w-24 h-24 mx-auto mb-4 text-red-400">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-red-500 dark:text-red-400 mb-2 text-lg">Failed to load projects.</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Error: ${err.message}</p>
|
||||
<button onclick="location.reload()" class="btn-primary mt-4">Try Again</button>
|
||||
</div>`;
|
||||
});
|
||||
});
|
||||
|
||||
// Add fadeInUp animation
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
Reference in New Issue
Block a user