Voeg 28 herkenbare kleurplaten en creatieve niveaus toe (v0.4.66-beta) #4

Open
bes-r wants to merge 207 commits from bes-r/kleurplaten-v0-4-66 into main AGit
4 changed files with 100 additions and 6 deletions
Showing only changes of commit a7180d8390 - Show all commits

View file

@ -1 +1 @@
0.4.58-beta
0.4.59-beta

View file

@ -2475,6 +2475,28 @@ body.hc #settingsModal .am-inp, body.hc #settingsModal .am-sel{border-color:#000
#settingsModal .live-session-widget,#settingsModal .live-session-time,#settingsModal .live-session-score{max-width:100%;color:var(--muted);font-size:11px;line-height:1.35}
#settingsModal .live-session-score{font-weight:850;color:var(--accent-ink)}
#settingsModal .am-combo-shell{display:flex;flex-direction:column;gap:14px}
#settingsModal .am-combo-hero{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr);gap:14px;align-items:stretch;padding:16px;border:1px solid color-mix(in srgb,var(--accent) 18%,var(--line));border-radius:18px;background:linear-gradient(135deg,color-mix(in srgb,var(--accent) 10%,var(--surface)),var(--surface))}
#settingsModal .am-combo-hero-copy{display:flex;flex-direction:column;gap:8px}
#settingsModal .am-combo-kicker{color:var(--accent-ink);font-size:11px;font-weight:900;text-transform:uppercase;letter-spacing:.48px}
#settingsModal .am-combo-title{margin:0;font-size:clamp(18px,3vw,26px);line-height:1.05}
#settingsModal .am-combo-lead{margin:0;max-width:62ch;color:var(--muted);font-size:13px;line-height:1.45}
#settingsModal .am-combo-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-top:4px}
#settingsModal .am-combo-stat{padding:10px 11px;border-radius:12px;background:var(--surface-2);display:flex;flex-direction:column;gap:2px}
#settingsModal .am-combo-stat strong{font-size:19px;line-height:1;color:var(--ink)}
#settingsModal .am-combo-stat span{font-size:10.5px;font-weight:850;color:var(--muted);text-transform:uppercase;letter-spacing:.35px}
#settingsModal .am-combo-chart{width:100%;min-height:140px;align-self:center}
#settingsModal .am-combo-chart rect{filter:drop-shadow(0 3px 8px rgba(20,35,60,.12))}
#settingsModal .am-combo-chart-value{fill:#fff;font-size:18px;font-weight:900}
#settingsModal .am-combo-chart-label{fill:var(--muted);font-size:10px;font-weight:800}
#settingsModal .am-combo-layout{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:12px;align-items:start}
#settingsModal .am-combo-pane{display:flex;flex-direction:column;gap:10px;padding:14px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:0 3px 14px rgba(20,35,60,.05)}
#settingsModal .am-combo-pane-title{font-size:13px;font-weight:900;text-transform:uppercase;letter-spacing:.35px;color:var(--accent-ink)}
#settingsModal .am-combo-pane .teacher-dashboard{margin-top:0}
#settingsModal .am-combo-pane .am-group:first-child{margin-top:0}
@media (max-width:920px){#settingsModal .am-combo-hero{grid-template-columns:1fr}#settingsModal .am-combo-layout{grid-template-columns:1fr}}
@media (max-width:600px){#settingsModal .am-combo-stats{grid-template-columns:1fr 1fr}}
#settingsModal .progress-level-badge{
display:inline-flex; align-self:flex-start; padding:4px 10px; margin-bottom:10px;
border-radius:999px; background:var(--accent-soft); color:var(--accent-ink);

View file

@ -213,6 +213,7 @@
T("amNewPw"), T("amParentCode"), "leerling","pupil","ouder","parent","wachtwoord","password",
"koppelcode","aanmelding","enrolment",
T("amNewClass"), T("amResetClass"), "klas","class","groep","group",
T("amLevelYearGroup"), T("amLevelLevel"), "leerjaar","year group","niveau","level","differentiatie",
T("amNewSchool"), T("amNewSuper"), "school","systeemmanager","system manager"],
search:q=>[
...USERS
@ -229,15 +230,17 @@
.map(s=>({ icon:"🏫", label:s.name, sub:T("amSchools"),
go:()=>{ selSchool=String(s.id); navLevel={type:"overview"}; goSettingsSection("school"); } })),
] },
{ tab:"toewijzingen", icon:"🎯", group:"school", order:12, labelKey:"amAssignments", subKey:"stToewijzSub",
{ tab:"toewijzingen", icon:"🎯", group:"school", order:12, labelKey:"amAssignmentsLevels", subKey:"amAssignmentsLevelsSub",
show:()=>can("assignments.manage"),
keywords:()=>[T("amModeWatch"), T("amModeWork"), T("amDashboardTitle"), T("castPathTitle"),
"bord","board","delen","share","toewijzen","assign","widget","dashboard","opdrachtenreeks","learning path"] },
T("amLevelYearGroup"), T("amLevelLevel"), T("amAssignmentsLevels"),
"bord","board","delen","share","toewijzen","assign","widget","dashboard","opdrachtenreeks","learning path",
"niveau","level","groep","niveaugroep","leerjaar","year group","differentiatie"] },
{ tab:"voortgang", icon:"📈", group:"school", order:13, labelKey:"amProgress", subKey:"stVoortgangSub",
show:()=>can("progress.view"),
keywords:()=>["voortgang","progress","score","resultaten","results","tijdlijn","timeline"] },
{ tab:"klassenmanagement", icon:"🎓", group:"school", order:14, labelKey:"amLevels", subKey:"amLevelsSub",
show:()=>can("assignments.manage"),
show:()=>false,
keywords:()=>[T("amLevelYearGroup"), T("amLevelLevel"), "niveau","level","groep","niveaugroep",
"leerjaar","year group","differentiatie"] },
{ tab:"opslag", icon:"🖼", group:"system", order:32, labelKey:"stOpslag", subKey:"stOpslagSub",
@ -712,6 +715,73 @@
addGroup(T("amNoClass"), byClass.get("none"));
}
function svgEl(tag, attrs){
const el = document.createElementNS("http://www.w3.org/2000/svg", tag);
Object.entries(attrs || {}).forEach(([k,v])=>el.setAttribute(k, String(v)));
return el;
}
function combinedOverviewChart(){
const items = [
{ label: T("amAssignClasses"), value: CLASSES.filter(c=>ASSIGNMENTS[c.id]).length, color: "var(--blue)" },
{ label: T("amAssignPupils"), value: USERS.filter(u=>u.role==="pupil" && !!ASSIGNMENTS[u.id]).length, color: "var(--green)" },
{ label: T("amUnassigned"), value: USERS.filter(u=>u.role==="pupil" && !u.classId).length, color: "var(--orange)" },
{ label: T("amLevels"), value: CLASSES.length, color: "var(--purple)" },
];
const svg = svgEl("svg", { viewBox: "0 0 520 140", class: "am-combo-chart", role: "img", "aria-label": T("amAssignmentsLevels") });
const max = Math.max(1, ...items.map(item => item.value));
items.forEach((item, index) => {
const x = 34 + index * 122;
const height = Math.max(8, Math.round((item.value / max) * 72));
svg.appendChild(svgEl("rect", { x: x, y: 88 - height, width: 72, height, rx: 12, fill: item.color }));
const value = svgEl("text", { x: x + 36, y: 102, class: "am-combo-chart-value", "text-anchor": "middle" });
value.textContent = String(item.value);
svg.appendChild(value);
const label = svgEl("text", { x: x + 36, y: 124, class: "am-combo-chart-label", "text-anchor": "middle" });
label.textContent = item.label;
svg.appendChild(label);
});
return svg;
}
function renderAssignmentsLevelsPanel(panel){
const schoolChosen = currentUser.role!=="super" || !!selSchool;
if(!schoolChosen){
panel.appendChild(h("div","guestnote", T("amPickSchoolHint")));
return;
}
const shell = h("div","am-combo-shell");
const hero = h("section","am-combo-hero");
const heroCopy = h("div","am-combo-hero-copy");
heroCopy.appendChild(h("div","am-combo-kicker", T("amAssignmentsLevels")));
heroCopy.appendChild(h("h2","am-combo-title", T("amAssignmentsLevels")));
heroCopy.appendChild(h("p","am-combo-lead", T("amAssignmentsLevelsSub")));
const statStrip = h("div","am-combo-stats");
const classAssignments = CLASSES.filter(c=>ASSIGNMENTS[c.id]).length;
const pupilOverrides = USERS.filter(u=>u.role==="pupil" && !!ASSIGNMENTS[u.id]).length;
const pupils = USERS.filter(u=>u.role==="pupil");
[[classAssignments, T("amAssignClasses")], [pupilOverrides, T("amAssignPupils")], [pupils.length, T("amPupils")], [CLASSES.length, T("amClasses")]].forEach(([n, label])=>{
const stat = h("div","am-combo-stat");
stat.appendChild(h("strong", null, String(n)));
stat.appendChild(h("span", null, label));
statStrip.appendChild(stat);
});
heroCopy.appendChild(statStrip);
hero.append(heroCopy, combinedOverviewChart());
const layout = h("div","am-combo-layout");
const assignPane = h("section","am-combo-pane am-combo-pane-assign");
assignPane.appendChild(h("div","am-combo-pane-title", T("amAssignments")));
renderToewijzingenPanel(assignPane);
const levelPane = h("section","am-combo-pane am-combo-pane-levels");
levelPane.appendChild(h("div","am-combo-pane-title", T("amLevels")));
renderKlassenmanagementPanel(levelPane);
layout.append(assignPane, levelPane);
shell.append(hero, layout);
panel.appendChild(shell);
}
/* leerling-omgeving: welk eigen bord ziet een klas (standaard), of een individuele
leerling (uitzondering voor wie op een ander niveau werkt) - zie roadmap-memo. */
function renderToewijzingenPanel(panel){
@ -1759,7 +1829,7 @@
const tabDefs = adminTabDefs();
if(!tabDefs.some(([t])=>t===adminTab)) adminTab = tabDefs[0][0];
const panels = { school: renderSchoolNavigator, toewijzingen: renderToewijzingenPanel, voortgang: renderVoortgangPanel, klassenmanagement: renderKlassenmanagementPanel, opslag: renderImageStoragePanel };
const panels = { school: renderSchoolNavigator, toewijzingen: renderAssignmentsLevelsPanel, voortgang: renderVoortgangPanel, opslag: renderImageStoragePanel };
tabDefs.forEach(([t])=>{
const panel = h("div","am-tabpanel");
panel.dataset.panel = t;

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.4.58-beta";
const VERSION = "0.4.59-beta";
/* Safari en oudere mobiele browsers behandelen 100vh/100% soms als de hoogte
achter hun adres- en navigatiebalk. visualViewport geeft de werkelijk
zichtbare hoogte; de CSS gebruikt deze pixelwaarde als betrouwbare bron en
@ -399,6 +399,7 @@ const I18N = {
amPwOptional:"wachtwoord (leeg = koppelcode)",
amOwnPassword:"Eigen wachtwoord ter bevestiging", amOwnPasswordPh:"jouw huidige wachtwoord…",
amAssignments:"Toewijzingen", amAssignClasses:"Per klas (standaard)", amAssignPupils:"Per leerling (uitzondering)",
amAssignmentsLevels:"Toewijzingen & niveaus", amAssignmentsLevelsSub:"Een gecombineerd overzicht van bords-toewijzingen, klasniveaus en live status.",
amPickPupil:"— kies een leerling —", amNoAssignment:"— geen toewijzing —",
amAssignedAs:"Toegewezen:", amAssign:"Toewijzen", amAssignSaved:"Toewijzing opgeslagen.",
amUnlink:"Loskoppelen",
@ -801,6 +802,7 @@ const I18N = {
amPwOptional:"password (empty = link code)",
amOwnPassword:"Your own password, to confirm", amOwnPasswordPh:"your current password…",
amAssignments:"Assignments", amAssignClasses:"Per class (default)", amAssignPupils:"Per pupil (override)",
amAssignmentsLevels:"Assignments & levels", amAssignmentsLevelsSub:"A combined view of board assignments, class levels, and live status.",
amPickPupil:"— pick a pupil —", amNoAssignment:"— no assignment —",
amAssignedAs:"Assigned:", amAssign:"Assign", amAssignSaved:"Assignment saved.",
amUnlink:"Unlink",