diff --git a/index.html b/index.html index 4d51dfe..fdf6a91 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ Lukujärjestäjä 0.1 + @@ -37,6 +38,25 @@
Tunnit + +
+ + + +
+ Luokat +
    +
    +
    + Opettajat +
      +
      +
      + Tilat +
        +
        + +
        diff --git a/käyttöliittymä.js b/käyttöliittymä.js index 83aa5ba..3318162 100644 --- a/käyttöliittymä.js +++ b/käyttöliittymä.js @@ -42,6 +42,37 @@ document.getElementById('tilat-uusi').addEventListener('submit', (e) => { })); }); +document.getElementById('tunnit-uusi').addEventListener('submit', (e) => { + e.preventDefault(); + suorita(_tietokanta.transaktio((t) => { + const nimi = document.getElementById('tunnit-uusi-nimi').value; + const kertaa = + Number.parseInt(document.getElementById('tunnit-uusi-kertaa').value); + const luokat = valitutHTMLLuokalla('tunnit-uusi-luokka'); + const opettajat = valitutHTMLLuokalla('tunnit-uusi-opettaja'); + const tilat = valitutHTMLLuokalla('tunnit-uusi-tila'); + t.lisää(taulut.tunnit, { + nimi, luokat, opettajat, tilat, + milloin: new Array(kertaa), + }); + document.getElementById('tunnit-uusi-nimi').value = ''; + document.getElementById('tunnit-uusi-kertaa').value = 1; + for (const valinta of document.getElementsByClassName('tunnit-uusi-valinta')) { + valinta.checked = false; + } + })); +}); + +function valitutHTMLLuokalla(htmlLuokka) { + const valitut = []; + for (const valinta of document.getElementsByClassName(htmlLuokka)) { + if (valinta.checked) { + valitut.push(Number.parseInt(valinta.value)); + } + } + return valitut; +} + function suorita([tietokanta, muutokset]) { for (const muutos of muutokset) { suoritaMuutos(tietokanta, muutos); @@ -59,11 +90,15 @@ function suoritaMuutos(tietokanta, muutos) { // on viimeinen, seuraavaId on undefined, eikä DOM:ssa ole luokkaa // "luokka-undefined". seuraava on siis null silloin kuin tämä luokka // tulee lisätä listan loppuun, joka vastaa insertBefore:n toimintaa - const seuraava = document.getElementById(`luokka-${seuraavaId}`); + let seuraava = document.getElementById(`luokka-${seuraavaId}`); luokatLista.insertBefore(luoLuokka(id, uusi), seuraava); + const tunnitUusiLuokat = document.getElementById('tunnit-uusi-luokat'); + seuraava = document.getElementById(`tunnit-uusi-luokka-${seuraavaId}`); + tunnitUusiLuokat.insertBefore(luoLuokkaValinta(id, uusi), seuraava); } else if (taulu === taulut.luokat && uusi === undefined) { // Luokka poistettu poistaElementti(document.getElementById(`luokka-${id}`)); + poistaElementti(document.getElementById(`tunnit-uusi-luokka-${id}`)); // TODO: luokka muutos } else if (taulu === taulut.opettajat && vanha === undefined) { // Uusi opettaja @@ -72,22 +107,36 @@ function suoritaMuutos(tietokanta, muutos) { ); const opettajatLista = document.getElementById('opettajat-lista'); // ks. kommentti uuden luokan tapauksessa - const seuraava = document.getElementById(`opettaja-${seuraavaId}`); + let seuraava = document.getElementById(`opettaja-${seuraavaId}`); opettajatLista.insertBefore(luoOpettaja(id, uusi), seuraava); + const tunnitUusiOpettajat = document.getElementById('tunnit-uusi-opettajat'); + seuraava = document.getElementById(`tunnit-uusi-opettaja-${seuraavaId}`); + tunnitUusiOpettajat.insertBefore(luoOpettajaValinta(id, uusi), seuraava); } else if (taulu === taulut.opettajat && uusi === undefined) { // Opettaja poistettu poistaElementti(document.getElementById(`opettaja-${id}`)); + poistaElementti(document.getElementById(`tunnit-uusi-opettaja-${id}`)); // TODO: opettaja muutos } else if (taulu === taulut.tilat && vanha === undefined) { // Uusi tila const seuraavaId = idJälkeen(tietokanta, taulu, id, vertaa); const tilatLista = document.getElementById('tilat-lista'); - const seuraava = document.getElementById(`tila-${seuraavaId}`); + let seuraava = document.getElementById(`tila-${seuraavaId}`); tilatLista.insertBefore(luoTila(id, uusi), seuraava); + const tunnitUusiTilat = document.getElementById(`tunnit-uusi-tilat`); + seuraava = document.getElementById(`tunnit-uusi-tila-${seuraavaId}`); + tunnitUusiTilat.insertBefore(luoTilaValinta(id, uusi), seuraava); } else if (taulu === taulut.tilat && uusi === undefined) { // Tila poistettu poistaElementti(document.getElementById(`tila-${id}`)); + poistaElementti(document.getElementById(`tunnit-uusi-tila-${id}`)); // TODO: tila muutos + } else if (taulu === taulut.tunnit && vanha === undefined) { + // Uusi tunti + // TODO: Järjestys + const tunnitLista = document.getElementById('tunnit-lista'); + tunnitLista.appendChild(luoTunti(tietokanta, id, uusi)); + // TODO: tunti poistettu, muutos } else { throw new Error(`Ei toteutettu ${taulu} ${id} ${vanha} ${uusi}`); } @@ -154,3 +203,68 @@ function luoTila(id, nimi) { li.appendChild(document.createTextNode(nimi)); return li; } + +function luoTunti(tietokanta, id, tunti) { + const li = document.createElement('li'); + li.id = `tunti-${id}`; + const poistoPainike = document.createElement('input'); + poistoPainike.type = 'button'; + poistoPainike.value = '-'; + poistoPainike.addEventListener('click', () => { + suorita(_tietokanta.transaktio((t) => { + t.poista(taulut.tunnit, id); + })); + }); + li.appendChild(poistoPainike); + li.appendChild(document.createTextNode(tuntiTeksti(tietokanta, tunti))); + return li; +} + +function tuntiTeksti(tietokanta, tunti) { + const kertaa = tunti.milloin.length; + const nimi = tunti.nimi; + const luokat = tunti.luokat.map((x) => tietokanta.hae(taulut.luokat, x)); + const opettajat = tunti.opettajat + .map((x) => tietokanta.hae(taulut.opettajat, x).lyhenne); + const tilat = tunti.tilat.map((x) => tietokanta.hae(taulut.tilat, x)); + return `${kertaa}× ${nimi} ${luokat} ${opettajat} ${tilat}`; +} + +function luoLuokkaValinta(id, nimi) { + const li = document.createElement('li'); + li.id = `tunnit-uusi-luokka-${id}`; + const valinta = document.createElement('input'); + valinta.type = 'checkbox'; + valinta.classList.add('tunnit-uusi-valinta'); + valinta.classList.add('tunnit-uusi-luokka'); + valinta.value = id; + li.appendChild(valinta); + li.appendChild(document.createTextNode(nimi)); + return li; +} + +function luoOpettajaValinta(id, {nimi, lyhenne}) { + const li = document.createElement('li'); + li.id = `tunnit-uusi-opettaja-${id}`; + const valinta = document.createElement('input'); + valinta.type = 'checkbox'; + valinta.classList.add('tunnit-uusi-valinta'); + valinta.classList.add('tunnit-uusi-opettaja'); + valinta.value = id; + li.appendChild(valinta); + li.appendChild(document.createTextNode(`${nimi} (${lyhenne})`)); + return li; +} + +function luoTilaValinta(id, nimi) { + const li = document.createElement('li'); + li.id = `tunnit-uusi-tila-${id}`; + const valinta = document.createElement('input'); + valinta.type = 'checkbox'; + valinta.classList.add('tunnit-uusi-valinta'); + valinta.classList.add('tunnit-uusi-tila'); + valinta.value = id; + li.appendChild(valinta); + li.appendChild(document.createTextNode(nimi)); + return li; +} diff --git a/tietokanta.js b/tietokanta.js index 7093db2..4f765c5 100644 --- a/tietokanta.js +++ b/tietokanta.js @@ -4,6 +4,7 @@ const taulut = { luokat: 'luokat', opettajat: 'opettajat', tilat: 'tilat', + tunnit: 'tunnit', }; class Transaktio { @@ -67,6 +68,29 @@ class Transaktio { }); this.taulut.get(taulu).set(id, undefined); } + + suodata(taulu, suodatin) { + if (!this.taulut.has(taulu)) { + throw new Error(`ei taulua ${taulu}`); + } + const suodatetut = []; + for (const [id, sisältö] of this.taulut.get(taulu)) { + // Jos sisältö on undefined, rivi on poistettu, eikä sitä tule ottaa + // huomioon suodatettaessa + if (sisältö !== undefined && suodatin(sisältö)) { + suodatetut.push(id); + } + } + for (const [id, sisältö] of this.tietokanta.taulut.get(taulu)) { + // Älä huomio rivejä, jotka löytyvät transaktion tauluista. Ne on + // joko käsitelty jo edellisessä silmukassa (jos ne on päivitetty) + // tai niitä ei tulisi käsitellä ollenkaan (jos ne on poistettu). + if (!this.taulut.get(taulu).has(id) && suodatin(sisältö)) { + suodatetut.push(id); + } + } + return suodatetut; + } } class Tietokanta { @@ -110,6 +134,41 @@ class Tietokanta { // Varmista, että invariantit ovat yhä totta for (const {taulu, id, vanha, uusi} of transaktio.muutokset) { + if (uusi === undefined && taulu !== taulut.tunnit) { + // Poistettu luokka, opettaja tai tila ei ole tunnin käytössä + const roikkuvat = transaktio.suodata(taulut.tunnit, (tunti) => { + if (taulu === taulut.luokat) { + return tunti.luokat.includes(id); + } else if (taulu === taulut.opettajat) { + return tunti.opettajat.includes(id); + } else if (taulu === taulut.tilat) { + return tunti.tilat.includes(id); + } else { + throw new Error(`Ei-tunnettu taulu ${taulu}`); + } + }); + if (roikkuvat.length !== 0) { + throw new Error(`Yritetty poistaa ${taulu}:${id}, joka on ${roikkuvat} käytössä`); + } + } else if (taulu === taulut.tunnit) { + // Uusi tunti käyttää vain olemassaolevia luokkia, opettajia ja + // tiloja + for (const luokka of uusi.luokat) { + if (transaktio.hae(taulut.luokat, luokka) === undefined) { + throw new Error(`Yritetty luoda tunti ${id} olemattomalla luokalla ${luokka}`); + } + } + for (const opettaja of uusi.opettajat) { + if (transaktio.hae(taulut.opettajat, opettaja) === undefined) { + throw new Error(`Yritetty luoda tunti ${id} olemattomalla opettajalla ${opettaja}`); + } + } + for (const tila of uusi.tilat) { + if (transaktio.hae(taulut.tilat, tila) === undefined) { + throw new Error(`Yritetty luoda tunti ${id} olemattomalla tilalla ${tila}`); + } + } + } } // Suorita muutokset