mon-site-perso/Static/js/technologies.js
2025-09-12 10:57:48 +02:00

315 lines
12 KiB
JavaScript

// Gestionnaire des technologies pour les projets
class TechnologiesManager {
constructor() {
this.technologies = {
'frontend': {
label: 'Frontend Web',
items: [
'HTML', 'CSS', 'JavaScript', 'TypeScript', 'React', 'Vue.js',
'Angular', 'Svelte', 'Next.js', 'Nuxt.js', 'Sass', 'Tailwind CSS', 'Bootstrap'
]
},
'backend': {
label: 'Backend Web',
items: [
'PHP', 'Laravel', 'Symfony', 'Node.js', 'Express.js', 'Python',
'Django', 'Flask', 'FastAPI', 'Ruby', 'Ruby on Rails', 'Go', 'Rust'
]
},
'languages': {
label: 'Langages de Programmation',
items: [
'Java', 'Spring', 'C#', '.NET', 'C++', 'C', 'Kotlin', 'Swift',
'Dart', 'Scala', 'R', 'MATLAB'
]
},
'databases': {
label: 'Bases de Données',
items: [
'MySQL', 'PostgreSQL', 'MongoDB', 'Redis', 'SQLite', 'Elasticsearch',
'MariaDB', 'Oracle', 'Firebase', 'Supabase'
]
},
'mobile': {
label: 'Développement Mobile',
items: [
'React Native', 'Flutter', 'Ionic', 'Xamarin', 'Apache Cordova', 'Android', 'iOS'
]
},
'games': {
label: 'Développement de Jeux',
items: [
'Unity', 'Unreal Engine', 'Godot', 'Phaser', 'Three.js', 'Babylon.js',
'PixiJS', 'Construct 3', 'GameMaker Studio', 'RPG Maker'
]
},
'devops': {
label: 'DevOps & Cloud',
items: [
'Docker', 'Kubernetes', 'AWS', 'Azure', 'Google Cloud', 'Heroku',
'Vercel', 'Netlify', 'Jenkins', 'GitLab CI', 'GitHub Actions', 'Terraform'
]
},
'tools': {
label: 'Outils & Frameworks',
items: [
'Git', 'GitHub', 'Webpack', 'Vite', 'Babel', 'ESLint', 'Prettier',
'Jest', 'Cypress', 'Storybook'
]
},
'design': {
label: 'Design & Création',
items: [
'Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Canva',
'Blender', 'Maya', '3ds Max'
]
}
};
this.selectedTechs = new Set();
this.init();
}
init() {
this.render();
this.attachEvents();
this.addSearchFunctionality();
this.addQuickActions();
}
generateId(tech) {
return 'tag-' + tech.toLowerCase().replace(/[^a-z0-9]/g, '-');
}
render() {
const container = document.querySelector('.technologies-grid');
if (!container) return;
let html = `
<div class="tech-controls">
<div class="tech-search">
<input type="text" id="tech-search" placeholder="Rechercher une technologie...">
<span class="search-icon">🔍</span>
</div>
<div class="tech-quick-actions">
<button type="button" class="btn-quick" id="select-all">Tout sélectionner</button>
<button type="button" class="btn-quick" id="deselect-all">Tout désélectionner</button>
<button type="button" class="btn-quick" id="toggle-categories">Replier/Déplier</button>
</div>
<div class="selected-count">
<span id="count-display">0 technologie(s) sélectionnée(s)</span>
</div>
</div>
<div class="tech-categories">
`;
for (const [categoryKey, category] of Object.entries(this.technologies)) {
html += `
<div class="tech-category" data-category="${categoryKey}">
<div class="category-header" data-toggle="${categoryKey}">
<h4>${category.label}</h4>
<span class="category-toggle">▼</span>
<span class="category-count">(${category.items.length})</span>
</div>
<div class="category-items" id="category-${categoryKey}">
`;
category.items.forEach(tech => {
const id = this.generateId(tech);
html += `
<div class="tech-item">
<input type="checkbox" id="${id}" name="tags[]" value="${tech}" class="tech-checkbox">
<label for="${id}" class="tech-label">${tech}</label>
</div>
`;
});
html += `
</div>
</div>
`;
}
html += '</div>';
container.innerHTML = html;
}
attachEvents() {
// Événements pour les checkboxes
document.querySelectorAll('.tech-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', (e) => {
if (e.target.checked) {
this.selectedTechs.add(e.target.value);
} else {
this.selectedTechs.delete(e.target.value);
}
this.updateSelectedCount();
this.updateHiddenField();
});
});
// Événements pour replier/déplier les catégories
document.querySelectorAll('.category-header').forEach(header => {
header.addEventListener('click', (e) => {
const categoryKey = e.currentTarget.dataset.toggle;
const categoryItems = document.getElementById(`category-${categoryKey}`);
const toggle = e.currentTarget.querySelector('.category-toggle');
if (categoryItems.style.display === 'none') {
categoryItems.style.display = 'grid';
toggle.textContent = '▼';
} else {
categoryItems.style.display = 'none';
toggle.textContent = '▶';
}
});
});
}
addSearchFunctionality() {
const searchInput = document.getElementById('tech-search');
if (!searchInput) return;
searchInput.addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const techItems = document.querySelectorAll('.tech-item');
techItems.forEach(item => {
const label = item.querySelector('.tech-label').textContent.toLowerCase();
if (label.includes(query)) {
item.style.display = 'flex';
} else {
item.style.display = 'none';
}
});
// Masquer les catégories vides
document.querySelectorAll('.tech-category').forEach(category => {
const visibleItems = category.querySelectorAll('.tech-item[style*="flex"]');
if (query && visibleItems.length === 0) {
category.style.display = 'none';
} else {
category.style.display = 'block';
}
});
});
}
addQuickActions() {
const selectAllBtn = document.getElementById('select-all');
const deselectAllBtn = document.getElementById('deselect-all');
const toggleCategoriesBtn = document.getElementById('toggle-categories');
if (selectAllBtn) {
selectAllBtn.addEventListener('click', () => {
document.querySelectorAll('.tech-checkbox').forEach(checkbox => {
checkbox.checked = true;
this.selectedTechs.add(checkbox.value);
});
this.updateSelectedCount();
this.updateHiddenField();
});
}
if (deselectAllBtn) {
deselectAllBtn.addEventListener('click', () => {
document.querySelectorAll('.tech-checkbox').forEach(checkbox => {
checkbox.checked = false;
});
this.selectedTechs.clear();
this.updateSelectedCount();
this.updateHiddenField();
});
}
if (toggleCategoriesBtn) {
toggleCategoriesBtn.addEventListener('click', () => {
const allCategories = document.querySelectorAll('.category-items');
const allToggles = document.querySelectorAll('.category-toggle');
const firstCategory = allCategories[0];
const isCollapsed = firstCategory.style.display === 'none';
allCategories.forEach(category => {
category.style.display = isCollapsed ? 'grid' : 'none';
});
allToggles.forEach(toggle => {
toggle.textContent = isCollapsed ? '▼' : '▶';
});
});
}
}
updateSelectedCount() {
const countDisplay = document.getElementById('count-display');
if (countDisplay) {
const count = this.selectedTechs.size;
countDisplay.textContent = `${count} technologie(s) sélectionnée(s)`;
// Changer la couleur selon le nombre
if (count === 0) {
countDisplay.className = 'count-empty';
} else if (count <= 3) {
countDisplay.className = 'count-low';
} else if (count <= 6) {
countDisplay.className = 'count-medium';
} else {
countDisplay.className = 'count-high';
}
}
}
// Méthode pour pré-sélectionner des technologies (utile pour l'édition)
selectTechnologies(techArray) {
this.selectedTechs.clear();
techArray.forEach(tech => {
this.selectedTechs.add(tech);
const checkbox = document.querySelector(`input[value="${tech}"]`);
if (checkbox) {
checkbox.checked = true;
}
});
this.updateSelectedCount();
this.updateHiddenField();
}
// Alias pour la compatibilité
preselectTechnologies(techArray) {
this.selectTechnologies(techArray);
}
// Méthode pour effacer toutes les sélections
clearAllSelections() {
document.querySelectorAll('.tech-checkbox').forEach(checkbox => {
checkbox.checked = false;
});
this.selectedTechs.clear();
this.updateSelectedCount();
this.updateHiddenField();
}
// Méthode pour mettre à jour le champ caché
updateHiddenField() {
const hiddenField = document.getElementById('selected-technologies');
if (hiddenField) {
hiddenField.value = Array.from(this.selectedTechs).join(',');
}
}
// Méthode pour obtenir les technologies sélectionnées
getSelectedTechnologies() {
return Array.from(this.selectedTechs);
}
}
// Initialiser le gestionnaire au chargement de la page
document.addEventListener('DOMContentLoaded', () => {
window.techManager = new TechnologiesManager();
});
// Export pour utilisation dans d'autres fichiers
if (typeof module !== 'undefined' && module.exports) {
module.exports = TechnologiesManager;
}