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
+
+
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