conquiers-ta-vie-proto/js/views/accueil.js
isUnknown 9fab755cf2 Mes classes : colonne actions avec renommer / supprimer (GMS btn--square, Remix Icons)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 17:17:28 +02:00

235 lines
10 KiB
JavaScript

// ===================== ACCUEIL =====================
function copyCode(code, el) {
navigator.clipboard.writeText(code).then(() => {
const icon = el.querySelector('.gms-code-copy-icon');
if (!icon) return;
icon.src = 'assets/images/Check.svg';
setTimeout(() => { icon.src = 'assets/images/file-copy-line.svg'; }, 1500);
});
}
const CLASS_COLORS = ['#E8922A','#534bac','#2884aa','#1a7c50','#9200a6','#d55600'];
function classInitials(name) {
return name.split(' ').map(w => w[0]).join('').toUpperCase().slice(0,2);
}
function classColor(id) {
const idx = parseInt(id.replace(/\D/g,''), 10) || 0;
return CLASS_COLORS[idx % CLASS_COLORS.length];
}
function classProgressSummary(c) {
const started = c.students.filter(s => {
const p = DB.progression[s.id]; return p && totalSteps(p) > 0;
}).length;
const finished = c.students.filter(s => {
const p = DB.progression[s.id]; return p && totalSteps(p) >= 16;
}).length;
const n = c.students.length;
if (!n) return '<span class="gms-muted">Aucun élève</span>';
if (finished === n) return '<span class="status-badge success">✓ Tous terminés</span>';
if (started === 0) return '<span class="status-badge draft">Pas commencé</span>';
return `<span class="status-badge ongoing">${started}/${n} en cours</span>`;
}
function goToInterface() {
const card = document.getElementById('gmFlipCard');
const acceder = document.getElementById('gmAccederTab');
const tab = document.getElementById('gmGuideTab');
const nav = document.getElementById('gmNavTabs');
if (!card) return;
// Guide overlay ouvert sur une page interface (ex: une-classe) → juste re-flipper
if (S.route !== 'accueil') {
const clsTabs = document.getElementById('gmClassTabs');
if (acceder) acceder.classList.add('tab-down');
setTimeout(() => {
if (acceder) acceder.classList.add('tab-gone');
card.classList.add('is-flipped');
setTimeout(() => {
if (tab) { tab.classList.remove('tab-gone'); tab.classList.remove('tab-down'); }
if (nav) { nav.classList.remove('tab-gone'); nav.classList.remove('tab-down'); }
if (clsTabs) { clsTabs.classList.remove('tab-gone'); clsTabs.classList.remove('tab-down'); }
}, 500);
}, 400);
return;
}
// Page /guide avec historique → retour à la page précédente
if (S.history.length) {
S.back();
return;
}
// Page /guide, première visite → flip + mise à jour URL vers /mes-classes
S.history.push({ route: S.route, params: { ...S.params } });
S.params = { ...S.params, _flipped: true };
history.pushState(
{ route: S.route, params: S.params, hist: [...S.history] },
'',
BASE + '/mes-classes'
);
if (acceder) acceder.classList.add('tab-down');
setTimeout(() => {
if (acceder) acceder.classList.add('tab-gone');
card.classList.add('is-flipped');
setTimeout(() => {
if (tab) { tab.classList.remove('tab-gone'); tab.classList.remove('tab-down'); }
if (nav) { nav.classList.remove('tab-gone'); nav.classList.remove('tab-down'); }
}, 500);
}, 400);
}
function toggleFlip() {
const card = document.getElementById('gmFlipCard');
const tab = document.getElementById('gmGuideTab');
const acceder = document.getElementById('gmAccederTab');
const nav = document.getElementById('gmNavTabs');
const clsTabs = document.getElementById('gmClassTabs');
if (!card) return;
const wasFlipped = !!S.params._flipped;
if (tab) tab.classList.add('tab-down');
if (nav) nav.classList.add('tab-down');
if (clsTabs) clsTabs.classList.add('tab-down');
setTimeout(() => {
if (tab) tab.classList.add('tab-gone');
if (nav) nav.classList.add('tab-gone');
if (clsTabs) clsTabs.classList.add('tab-gone');
card.classList.remove('is-flipped');
setTimeout(() => {
if (acceder) { acceder.classList.remove('tab-gone'); acceder.classList.remove('tab-down'); }
// Si on avait pushé un état pour le flip, revenir en arrière (restaure /guide)
if (wasFlipped) history.back();
}, 500);
}, 400);
}
// Shell GMS partagé — utilisé par viewAccueil et viewUneClasse
// classTabs : { classes: [...], activeClassId: '...', tab: '...' } ou null
function renderGmsShell(frontContent, { startOnInterface = false, activeNavTab = 'classes', guideKey = 'classes', classTabs = null, wrapContent = true } = {}) {
const guideFns = { classes: guideClasses, modules: guideModules, suivi: guideSuivi };
const guideHtml = (guideFns[guideKey] || guideClasses)();
const navItems = [
{ id: 'classes', label: 'Mes classes', onclick: `S.navigate('accueil',{year:S.params.year,_flipped:true})` },
{ id: 'modules', label: 'Mes modules', onclick: `S.navigate('mes-activites',{year:S.params.year})` },
{ id: 'suivi', label: 'Suivi des élèves', onclick: `S.navigate('suivi-eleves',{year:S.params.year})` },
];
const tabHide = startOnInterface ? '' : ' tab-down tab-gone';
const accederHide = startOnInterface ? ' tab-down tab-gone' : '';
const cardClass = startOnInterface ? ' is-flipped' : '';
const classTabsHtml = classTabs ? `
<div class="gms-class-tabs" id="gmClassTabs">
${classTabs.classes.map(c => {
const isActive = c.id === classTabs.activeClassId;
return `<div class="gms-class-tab${isActive ? ' is-active' : ''}"
onclick="S.navigate('une-classe',{classId:'${c.id}',tab:'${classTabs.tab}'})">
<div class="gms-class-tab-name">${escHtml(c.name)}</div>
<div class="gms-class-tab-code">
<span class="gms-tab-code-wrap" onclick="copyCode(${jsSQ(c.code)},this);event.stopPropagation()">
<code>${escHtml(c.code || '—')}</code>
${c.code ? `<img class="gms-code-copy-icon" src="assets/images/file-copy-line.svg" alt="">` : ''}
</span>
</div>
</div>`;
}).join('')}
</div>` : '';
return `
<header class="gms-header">
<div class="gms-header-left">
<img src="assets/images/logo.png" onclick="S.navigate('accueil',{year:S.params.year})" style="cursor:pointer;display:block;height:auto;max-height:90px;object-fit:contain;" alt="Conquiers Ta Vie">
</div>
<div class="gms-header-right">
<button class="gms-user-btn" style="background:transparent;color:var(--gms-grey);border-color:var(--gms-grey-light);">
Alex Lefebvre
</button>
</div>
</header>
<div class="gms-page">
<div class="gms-guide-tab${tabHide}" id="gmGuideTab" onclick="toggleFlip()">
<img src="assets/images/question-line.svg" alt="Guide">
</div>
<div class="gms-guide-tab gms-acceder-tab${accederHide}" id="gmAccederTab" onclick="goToInterface()">
Accéder à l'interface
<img src="assets/images/play-circle-fill.svg" alt="">
</div>
${classTabsHtml}
<div class="gms-content-area">
<div class="gms-nav-tabs${tabHide}" id="gmNavTabs">
${navItems.map(n => `<div class="gms-nav-tab${n.id === activeNavTab ? ' is-active' : ''}" onclick="${n.onclick}">${n.label}</div>`).join('')}
</div>
<div class="gms-flip-card${cardClass}" id="gmFlipCard">
<div class="gms-flip-front">
${wrapContent ? `<div class="gms-page-content">${frontContent}</div>` : frontContent}
</div>
<div class="gms-flip-back">
${guideHtml}
</div>
</div>
</div>
</div>`;
}
function viewAccueil() {
const fc = filteredClasses();
const frontContent = `
<div class="data-table">
<table class="data-table__table">
<thead>
<tr>
<th>Classe</th>
<th>Code</th>
<th>Élèves</th>
<th>Modules assignés</th>
<th>Progression</th>
<th class="actions-column">Actions</th>
</tr>
</thead>
<tbody>
${fc.length === 0 ? `
<tr><td colspan="6" style="text-align:center;padding:3rem;color:var(--color-grey);font-family:'Danzza',sans-serif;">
Aucune classe pour l'année ${S.params.year || '2025-2026'}.
<br><br>
<button class="btn btn--s btn--filled color-palette-ctv" onclick="showNewClassModal()">+ Nouvelle classe</button>
</td></tr>
` : fc.map(c => {
const assignedActs = DB.activities.filter(a => c.activities.includes(a.id) && a.status === 'published');
const color = classColor(c.id);
return `<tr onclick="S.navigate('une-classe',{classId:'${c.id}',tab:'activite'})" style="cursor:pointer;">
<td>
<div class="gms-class-link">
<div class="gms-class-thumbnail" style="background-color:${color};">${classInitials(c.name)}</div>
<span class="gms-class-name">${c.name}</span>
</div>
</td>
<td>
<span class="gms-class-code-wrap" onclick="copyCode(${jsSQ(c.code)}, this); event.stopPropagation()">
<span class="gms-class-code">${c.code || '—'}</span>
<img class="gms-code-copy-icon" src="assets/images/file-copy-line.svg" alt="">
</span>
</td>
<td class="gms-muted">${c.students.length} élève${c.students.length !== 1 ? 's' : ''}</td>
<td class="gms-muted">
${assignedActs.length === 0
? '<span style="opacity:.5;">Aucun</span>'
: assignedActs.map(a => `<span style="margin-right:6px;">${a.name}</span>`).join('<span class="gms-dot">•</span>')}
</td>
<td>${classProgressSummary(c)}</td>
<td class="actions-cell">
<button class="btn btn--square btn--s btn--bordered color-palette-ctv" title="Renommer" onclick="showRenameClassModal('${c.id}');event.stopPropagation()"><i class="ri-pencil-line"></i></button>
<button class="btn btn--square btn--s btn--in-between color-palette-warning" title="Supprimer" onclick="confirmDeleteClass('${c.id}');event.stopPropagation()"><i class="ri-delete-bin-line"></i></button>
</td>
</tr>`;
}).join('')}
</tbody>
</table>
</div>`;
return renderGmsShell(frontContent, { startOnInterface: !!S.params._flipped, activeNavTab: 'classes' });
}