Voeg thematische kleurplaten en creatieve niveaus toe (v0.4.65-beta) #3
4 changed files with 100 additions and 6 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.4.58-beta
|
||||
0.4.59-beta
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue