diff --git a/App/admin-licenses.html b/App/admin-licenses.html index c327fab..1c5ff97 100644 --- a/App/admin-licenses.html +++ b/App/admin-licenses.html @@ -35,6 +35,33 @@ .toolbar{display:flex;gap:.4rem;margin-bottom:.8rem;flex-wrap:wrap} .msg{font-size:.72rem;padding:.4rem .6rem;border-radius:6px;margin-top:.5rem} .msg.ok{background:#dceadd;color:#5a8a5e} + /* ---- Datenmodell-Sektion ---- */ + .dm-lead{font-size:.74rem;color:#4a4a4a;line-height:1.55;margin-bottom:.6rem} + .dm-sub{font-size:.6rem;font-weight:800;color:#4a7c8a;text-transform:uppercase;letter-spacing:.05em;margin:1.2rem 0 .5rem} + .dm-note{font-size:.66rem;color:#8a6a2a;background:#fdf6e3;border:1px solid #efe0b8;border-radius:6px;padding:.45rem .6rem;margin-top:.5rem;line-height:1.5} + .dm-flow{display:flex;gap:.4rem;align-items:stretch;flex-wrap:wrap} + .dm-step{flex:1;min-width:190px;background:#f7f6f3;border-radius:8px;border-left:4px solid #c4a97a;padding:.65rem .75rem} + .dm-step.s-pool{border-left-color:#b8b2a6} + .dm-step.s-redeem{border-left-color:#4a7c8a} + .dm-step.s-assign{border-left-color:#5a8a5e} + .dm-step .s-top{display:flex;align-items:center;gap:.4rem;margin-bottom:.35rem} + .dm-step .s-n{width:18px;height:18px;border-radius:50%;background:#4a7c8a;color:#fff;font-size:.62rem;font-weight:800;display:flex;align-items:center;justify-content:center;flex:none} + .dm-step.s-pool .s-n{background:#b8b2a6} + .dm-step.s-assign .s-n{background:#5a8a5e} + .dm-step .s-t{font-size:.74rem;font-weight:800;color:#2f3b3f} + .dm-step .s-who{font-size:.6rem;font-weight:700;color:#8a8a8a;text-transform:uppercase;letter-spacing:.03em} + .dm-step .s-d{font-size:.68rem;color:#4a4a4a;line-height:1.45;margin-top:.15rem} + .dm-step code{font-family:'Courier New',monospace;font-size:.66rem;background:rgba(0,0,0,.05);padding:.05rem .3rem;border-radius:4px;color:#c85c4a} + .dm-arrow{display:flex;align-items:center;justify-content:center;color:#c4a97a;font-size:1.4rem;font-weight:900;padding:0 .1rem} + .dm-er{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap;font-size:.68rem} + .dm-ent{background:#fff;border:1.5px solid #4a7c8a;color:#4a7c8a;border-radius:6px;padding:.3rem .55rem;font-weight:800;font-family:'Courier New',monospace} + .dm-ent.e-lic{background:#4a7c8a;color:#fff} + .dm-rel{color:#8a8a8a;display:flex;flex-direction:column;align-items:center;line-height:1;font-family:'Courier New',monospace;font-size:.58rem} + .dm-rel b{color:#c4a97a;font-size:1.1rem} + .dm-derive{background:#f7f6f3;border-radius:8px;padding:.7rem .8rem;font-size:.7rem;line-height:1.7} + .dm-derive .d-code{font-family:'Courier New',monospace;font-weight:800;color:#4a7c8a;letter-spacing:.04em} + .dm-derive .d-hi{background:#e8c547;color:#4a4a4a;padding:.02rem .28rem;border-radius:4px;font-weight:800} + td.mono{font-family:'Courier New',monospace;font-size:.68rem;color:#c85c4a}
@@ -104,6 +131,73 @@ + + +Eine Lizenz = ein Sitzplatz für eine:n Schüler:in in einem Schuljahr. Der Admin erzeugt einen Vorrat an Codes, eine Lehrperson löst Codes ein (sie werden ihrem Konto zugeordnet) und weist sie dann einzelnen Schüler:innen zu. Alles steht in einer Tabelle licenses — die drei Zustände ergeben sich allein aus zwei Feldern: teacher_id und student_id.
+ +| Spalte | Typ | Bedeutung |
|---|---|---|
| id | INT · PK | Interne ID |
| code | VARCHAR · UNIQUE | Der Lizenzcode, Format XXXX-XXXX-XXXX-JAHR |
| school_year | VARCHAR | Schuljahr, z. B. „26/27" — aus dem Jahr abgeleitet |
| teacher_id | FK → teachers | Einlösende Lehrperson · NULL = noch im Pool |
| student_id | FK → students | Platz-Inhaber:in · NULL = noch frei |
| redeemed_at | TIMESTAMP · NULL | Zeitpunkt des Einlösens durch die Lehrperson |
| created_at | TIMESTAMP | Erstellung (Admin-Generierung) |
teacher_id = NULL · student_id = NULL. Frisch erzeugter Code im Pool.generateteacher_id gesetzt · redeemed_at = jetzt · student_id noch NULL. Code gehört nun der Lehrperson.redeem / redeem_batchstudent_id gesetzt. Der Platz ist von einer konkreten Person belegt.assign / auto_assignlicenses.teacher_id merkt sich die einlösende Lehrperson, licenses.student_id den Platz-Inhaber. Über students.class_id → classes.teacher_id hängt jede:r Schüler:in ohnehin an einer Klasse einer Lehrperson — der direkte teacher_id-Eintrag macht aber auch eingelöste, noch nicht zugewiesene Codes eindeutig einer Lehrperson zuordenbar.
+ +