Product Biografie - lisannevvliet/so-nuts GitHub Wiki

Farrahton

Sprint 1

Data: Maandag 23 2022 - woensdag 25 mei 2022
Code: GitHub

Week één bestond uit drie dagen vanwege Hemelvaart. Maandag was de kick-off van de meesterproef. In tegenstelling tot andere groepjes hadden wij pas dinsdag ons kennismakingsgesprek met onze opdrachtgever. Gelukkig betekende dit niet dat we niet al aan de slag konden, want Chippr wilde ook niet dat wij met lege handen aankwamen en gaven ons daarom de taak een stijlgids op te stellen. Aangezien ik vanuit mijn opleiding design gerelateerde skills toevoeg aan ons team ten opzichte van Lisanne die meer technisch ingesteld is, heb ik de taak van de stijlgids op mij genomen. Hiervoor heb ik onder andere inspiratie opgedaan op Pinterest en research gedaan op het gebied van design principes voor ouderen, zodat het niet gebaseerd is op eigen voorkeuren. Tevens stond een debriefing schrijven op de planning vanuit school. Dit hebben we in groepsverband gedaan met de andere studenten die aan de slag zijn met de SO-NUTS case.

Dinsdag hadden wij het kennismakingsgesprek met Chippr in Utrecht. Dit nam redelijk veel tijd in beslag, maar wij besloten om erna toch (ook al was het TTH ontruimd) naar Amsterdam te gaan om alvast een begin te maken. Zoals ik al eerder zei, wil ik graag mijn design skills toepassen. Dit betekent niet gelijk dat ik mij alleen focus op iets wat ik wel al ken en mij niet aan het ontwikkelen ben. Het tegendeel zelfs, want ik heb gemerkt tijdens de vakken dat ik mijn design skills heel weinig heb toegepast. Vaak moest ik meer tijd besteden aan het technische gedeelte, waardoor ik CSS liet liggen. In Adobe XD kan ik dan wel vormgeven, maar in CSS valt nog wel veel te leren. Daarom nam ik graag de taak op mij om de questionnaire vorm te geven en een begin te maken in JavaScript om er een multi step form van te maken. Wanneer ik tegen wat errors/bugs aanliep (zoals een custom font die blijkbaar alleen lokaal goed ingeladen was) vroeg ik Lisanne om hulp. Nadat Lisanne de verschillende antwoordopties in HTML-elementen had vertaald heb ik ook deze vormgegeven.

Tijdens Hemelvaart-weekend ben ik evengoed verder gaan coderen. Ik wilde graag nog een onboarding hebben voordat de questionnaire begint. Hiervoor heb ik bijpassende illustraties die wat zelfspot bevatten gebruikt. Er was nog wel wat twijfel over hoeveel schermen en wat voor content ik hierop precies wilde weergeven, aangezien we nog niet de API hadden met alle informatie die er te vinden zal zijn in de web app. Daarom heb ik snel wat verzonnen ‘placeholder’ tekst geschreven. Misschien werkt het en misschien ook niet. Ook was ik verder gegaan aan de multi step form. Ik liep wel tegen aan paar probleempjes aan met een if else waarin de index niet helemaal goed ging. Aangezien het weekend was, besloot ik om hier maandag met Lisanne naar te kijken, want op dit soort momenten is het top dat we een duo zijn. Ik had namelijk het vermoede dat er een quick fix voor zou zijn waar ik overheen keek en met een fris paar ogen komt dit wel opgelost. Ik wilde nog localStorage toepassen, zodat een gebruiker/mens de onboarding maar één keer te zien krijgt, maar zette dit liever op de to-do list voor later. Gezien het feit dat zowel de onboarding als de questionnaire op het eind maar één keer getoond hoeven te worden.

Sprint 2

Data: Maandag 30 mei 2022 - vrijdag 3 juni 2022
Code: GitHub

Het is altijd fijn om meerdere versies van iets te hebben. Daarom heb ik een tweede versie gemaakt van de questionnaire. Eén waar zowel wij als de opdrachtgever meer tevreden over zijn.

We kregen steeds langere JavaScript code, waardoor we toch echt de modules gingen implementeren, zoals we bij WAFS hebben geleerd. Ik begon hier met het maken van nieuwe JS files per pagina in plaats van per functie.

Tijdens de poging tot een eerst concept voor een dashboard merkte ik dat Vasilis zijn uitspraak over de een landingspagina maken voordat je aan de rest van de content begint inderdaad niet een heel handig benadering kan zijn. Ik wel iets neergezet, omdat ik niet met lege handen aan wilde komen, maar ben er niet tevreden mee. Winnie daarentegen vond het wel mooi, ook nadat ik nog een keer zei dat wij het niet mooi vonden en het wilde aanpassen. Ook tijdens de design reviews verschilden de meningen hierover of we dan wel of niet willen focussen.

Ik heb twee soorten navigatiebalken ontworpen. Degene met de 'pop-up' sloeg goed aan bij Winnie.

Op vrijdagavond hebben we samen nog wat dingen aangepast, aangezien ze de JSON structuur weer veranderd hadden. Hierdoor is de ap weer dynamisch en kan het zo gebruikt worden op het moment dat de API daadwerkelijk data heeft.

Vanwege het feit dat ik zoveel moeite had om dingen te maken zonder de API heb ik deze week ook juist veel gebrainstormd over mogelijke concepten voor de app. Dingen die nog niet in de API staan zijn juist van toegevoegde waarde voor de opdrachtgevers. De twee concepten waar we op ons willen richten zijn een streak element voor de doelen en een so nuts community creëren. Mensen die bijvoorbeeld samenwonened/getrouwd of iets dergelijks hebben ingevuld zouden samen met hun partner een soort wedstrijd kunnen houden per week. Hun streaks worden weergegeven in een grafiek en je kan zien wie er het hoogste heeft. Mensen die alleenstaand zijn hoeven niet getreurd te zijn: zij kunnen tegen met andere mensen op de SO-NUTS app spelen. Sockets toepassen om bijvoorbeeld extra motivatie te sturen naar de ander.

Uiteraard heb ik Lisanne haar code weer bestudeerd, zodat ik de hele app natuurlijk begrijp. Om dit nog leerzamer te maken heb ik de comments in de JavaScript gezet, zowel voor mijzelf als voor andere developers.

Sprint 3

Data: Dinsdag 7 juni 2022 - vrijdag 10 juni 2022
Code: GitHub

Deze week heb ik mij vooral gefocust op user story twee.

“Als gebruiker, wil ik na voltooien van de intake 2 lijsten met aanbevelingen krijgen (gericht op bewegen en voeding), zodat je op basis van deze lijst een keuze kan maken aan welke onderwerpen je moet gaan werken.”

Aangezien de API nog steeds niet gevuld is met genoeg data, maar we het toch dynamisch willen houden, heb ik het gedaan met de twee voorbeelden die er wel in zijn te vinden: peulvruchten en groenten. Vanwege mogelijke merge conflicten heb ik een nieuwe route aangemaakt om hier in te werken. Ook omdat we niet helemaal tevreden zijn met de dashboard leek mij dit een goed idee, want op deze manier kan dit mogelijk de nieuwe dashboard pagina worden.

Ik begon met de JavaScript (en HTML), omdat ik hier het meeste moeite mee heb. Eerst maakte ik comments met de functies die nodig zijn: een doel uitkiezen, HTML renderen en het afvinken wanneer je het doel hebt uitgevoerd.

De doelen wilde ik toevoegen aan een array. Aangezien je uit doelen kiest hebben deze een input type checkbox. Hier loop ik overheen om alleen de chexkboxes die :checked zijn toe te voegen. Omdat we nog niet wisten of de mensen het handmatig invullen of niet heb ik de tijdsduur en herhaling values eruit gehaald en hiet overheen gelooped. Het toevoegen van de index, was iets dat ik met de hulp van Lisanne voor elkaar heb gekregen. Ik was even vergeten dat je dit ook nodig heb naast de querySelectorAll. E

    // Add a goal.
    function add_goal(e) {
        // Prevent the submit from refreshing the page.
        e.preventDefault()
        // Only put the checked checkboxes in an array.
        let checkboxes = $$('input[name="goal"]:checked')
        let total_repetition = $$("[name=repetition]")
        let timeframe = $$("[name=timeframe]")

        // Push the values of the inputs in the array.
        checkboxes.forEach((checkbox, index) => {
            goal_array.push({
                name: checkbox.value,
                repetition: 0,
                total_repetition: total_repetition[index].value,
                timeframe: timeframe[index].value,
                completed: false,
            })
        })

        // Save the goal array into the localStorage JSON.
        put_goal_in_list_item(goal_array, unordered_goal_list)
        localStorage.setItem("JSON_all_goals", JSON.stringify(goal_array))
        // Reset the form.
        this.reset()
        // Close the popup to create a goal.
        $("form").classList.remove("show_popup")
    }

Het renderen van de HTML is iets wat ik tijdens de minor wel al vaker heb gedaan, dus dit ging wat soepeler. Het was meer een ding of ik dit keer voor een forEach ging kiezen of een for loop net zoals ik aan het begin van de minor heb geleerd.

 // Render HTML. 
    function put_goal_in_list_item(goal_array, unordered_goal_list) {
        for (let i = 0; i < goal_array.length; i++) {

            unordered_goal_list.insertAdjacentHTML('beforeend', `
                <li>
                    <article>
                        <strong>${goal_array[i].name}</strong>
                        <p>
                            <span class="repetition_change">${goal_array[i].repetition}</span>
                            /${goal_array[i].total_repetition} ${goal_array[i].timeframe}
                        </p> 
                    </article>
                    <label for="goal_${[i]}">
                        <i>+</i>
                        <input type="checkbox" data-index=${[i]} name="goal_${[i]}" id="goal_${[i]}" ${goal_array[i].completed ? "checked" : ""} />
                    </label>
                    <div id="goal_progress">
                        <div></div>
                    </div>
                </li>`)
        }
    }

Als laatste ging ik aan de slag met het afvinken van het doel. Je wilt dat degene dat degene die je aanklikt overeenkomt met degene die je wilt afvinken. Dit deeed ik op basis van de data-index. Vervolgens wil ik dat de repetition omhoog gaat en er gecheckt wordt met een if statement of het totaal herhalingen al bereikt is of niet, om op basis hiervan de textContent te veranderen. Tijdens de minor kreeg ik een paar keer te horen dat ik beter === kon gebruiken ipv ==, dus dat deed ik dit keer. Zal je zien, was dit de keer dat het niet beter was. Ik had ook geprobeerd om alles op te slaan in localStorage. Dit ging goed totdat ik op een gegeven moment zag dat er meerdere arrays in elkaar kwamen. Toen ik heb ik het maar even opzij gelegd, want dat de functies werken is belangrijker dan het opslaan wat ook achteraf gedaan kan worden.

    // Toggle to complete
    function toggle_complete(e) {
        if (!e.target.matches("input")) return
        const index = e.target.dataset.index

        goal_array[index].repetition += 1

        if (goal_array[index].repetition === goal_array[index].total_repetition) {
            goal_array[index].completed = true
             $$("label i")[index].textContent = "✔︎"
        } else if (goal_array[index].repetition > goal_array[index].total_repetition) {
            goal_array[index].repetition = 0
            goal_array[index].completed = false
            $$("label i")[index].textContent = "+"
        }

        put_goal_in_list_item(goal_array, goal_list)
        localStorage.setItem("goal_array", JSON.stringify(all_goals))
    }

Aan het einde zorgde ik er natuuurlijk voor dat de functies uitgevoerd kunnen worden door de gebruiker met addEventListeners.

    $(".add_goal").addEventListener("submit", add_goal)
    $(".unordered_goal_list").addEventListener("click", toggle_complete)
    $(".unordered_goal_list").addEventListener("click", toggle_complete)

ps. het zou kunnen dat er in de code snippets wat inconsistencies zijn. Ik had die week namelijk wat commitment issues, want ik wilde geen niet-werkende code pushen. Dit heeft helaas als gevolg dat ik ontzettend veel veranderd heb, maar niet al deze veranderingen openbaar op git heb staan. Het neemt niet weg dat ik deze week niet veel heb geleerd over het denken in computer logica en het gebruiken van arrays met meerdere values.

Sprint 4

Data: Maandag 13 juni 2022 - vrijdag 17 juni 2022
Code: GitHub

Maandag heb ik in Adobe XD een nieuw design voor de goals pagina vormgegeven. Uit het feedback moment van Winnie kwam dat zij toch echt weel meerwaarde ziet in het behouden van de navigatiebalk en elementen in de dashboard. Volgens haar zijn het dingen die het meer ons eigen maken, zoals bijvoorbeeld de quote. Deze week ga ik daarom kijken hoe we het gaan incorporeren op een manier waar wij zelf ook tevreden mee zijn. Ook heb ik alvast een beginnetje gemaakt met het Adobe XD design in CSS zetten, wat foto bestanden opgeruimd en de oplossing in het Design Rationale geschreven.

Op dinsdag hebben wij eindelijk de knoop doorgehakt en besloten om toch maar een eigen database op te zetten en op basis daarvan de goals pagina te maken. Zodat we de feedback van Vasilis over wat er gebeurd bij verschillende soorten vragen makkelijker en sneller kunnen toepassen, aangezien de kans klein is dat we nog op tijd een API krijgen die deze kenmerken bevat. Verschillende soorten vragen heb ik vormgegeven in Adobe XD, maar vanwege de tijd gaan we ons toch focussen op één soort.

Op het moment dat we besloten hadden om toch voor een eigen database te gaan, heb ik meegekeken en zelf even uitgeprobeerd of het mij ook zou hebben gelukt om te doen. Dit was het geval. Daarna nam ik de taak op mij om data op te halen uit onze Supabase database en deze te renderen in HTML. Ik heb een for each geschreven voor ieder doel en deze vormgegeven. De zelfgemaakte database geeft meer ruimte voor variërende dynamische content. Hierdoor had ik de mogelijkheid om bijpassende iconen gelijk al per doel toe te voegen. Dit was nog een heel werkje, omdat ik de iconen eerst in Adobe XD moest maken en exporteren en vervolgens moest converteren naar base64 urls om ze in een column bruikbaar te maken als img urls. Ondertussen heb ik ook alle CSS in één bestand verplaatst. Bovendien heb ik een login pagina gemaakt en Lisanne geholpen (ja, die zagen jullie zeker niet aankomen hé) met wat dingen hierin zoals hoe je de button koppelt als deze buiten de form staat en gerenderde HTML waar de labels niet goed gekoppeld werden. Tussendoor heb ik een paar keer de routes veranderd zodat de customer journey fijn verloopt. De onboarding staat nu ook in een positievere toon op één scherm (3-4 schermen worden aangeraden voor onboardings, maar Koop wilde dit graag in 1 dus speciaal voor hem!)

Ik had al in mijn achterhoofd om anders als oplossing voor de gekke + button voor het zien van de doelen anders onder de andere li’s te plaatsen, maar pas toen Koop dit ook adviseerde ben ik dit doorgezet. Er was wat twijfel over de popup of een fullscreen voor het zien van de lijst met doelen. Het doorslaggevende argument voor de fullscreen was dat het gebruiksvriendelijker is qat betreft het scrollen door een lange lijst. Verder heb ik wat animaties klaarstaan voor op het moment dat je een doel compleet hebt.

Op vrijdag was ik net te laat voor het gesprek dat we graag wilden hebben met een docent dat uiteindelijk Koop werd. Toch heb ik alles wat hierin besproken is van Lisanne gehoord.

Sprint 5

Data: Maandag 20 juni 2022 - vrijdag 24 juni 2022
Code: GitHub

  Op maandag ging ik alleen naar de meeting met Winnie, aangezien het voor mij het relevantste is in dit project. Ik was bezig met het ontwerp voor de profiel pagina. Tussendoor heb ik ook wat optimalisaties gedaan op basis van de lighthouse reports. Zo heb ik een meta description toegevoegd, de font faces als inline css erin gezet voor de first paint en performance te verbeteren en de toegankelijkheid verbeterd met aria-labels.   Ik had geprobeerd om tussendoor even op basis van de enter key de volgende knop te kunne besturen. Dit heb ik echter nooit gepusht, omdat ik dacht dat het niet werkte. Achteraf gezien, nadat Lisanne het op haar manier had toegevoegd bleek dat dit wel gewoon werkte, maar alleen bij de delen met een input type text. Momenten zoals deze zijn vaak voorgekomen tijdens de samenwerking. Ik schreef code en dan werkte het niet helemaal, waardoor ik het deletete, stashde, kwijtraakte tijdens een commit/merge/stash of dat Lisanne het vervolgens op haar laptop typte en pushde. Zo had ik het toevoegen van de category gewoon goed gedaan op een komma na, maar maakte ik vervolgens de fout door het vergeten van een hoofdletter… Het blijft een ding hé interpunctie foutjes tijdens coderen.   Heel belangrijk vinden de opdrachtgevers het om voeding en beweging content goed van elkaar te onderscheiden door middel van bijvoorbeeld kleur. Daarom heb ik dit in de HTML dynamisch gemaakt met een {{#ifCond}} en een classList toegevoegd voor de styling van de bewegingsdoelen.   Op de doelenpagina zouden we nog een verwijder functie en een progress bar per doel toevoegen. Dit was echt een priegelwerkje qua positionering, dus helaas nam dit redelijk wat tijd in beslag. Met de nieuwe aanpak voor het verhogen van de streak refreshed de pagina. Aangezien ik wel animaties wilde toepassen om te indiceren aan de gebruiker dat er geklikt is op de plus knop moest ik dit precies goed timen en opzoeken hoe dit te doen is. Ik kwam window.requestAnimationFrame() tegen en had dit geprobeerd, maar heb dit uiteindelijk niet gebruikt. Als de gebruiker de limiet van 21 heeft gehaald dan wille we natuurlijk een andere animaties om te laten zien dat het compleet voltooid is. Hiervoor heb ik een if else geschreven. Hiervoor twijfelde ik heel erg of ik het uit de streak, value of uit de span wilde halen. Uiteindelijk heb ik gekozen voor de textContent van de span. Het was ook even puzzelen waar ik die if else nou precies moest schrijven ik dacht eerst nog onder de eventListener en wel in de forEach, maar het bleek dat de if boven de eventListener moest en de else erin.  

  $$(".checkmark").forEach((element, index) => {
        // Show the check animation.
        $$(".checkmark")[index].classList.add("checkmark_click_animation")

        // Check if the streak is 21.
        if ($$(".repetition_change")[index].textContent == 21) {
            $$(".checkmark_plus")[index].classList.add("hide_plus")
            $$(".checkmark_check_icon")[index].classList.add("checkmark_check_icon_animation")
            $$(".checkmark")[index].classList.add("no_cursor")
        } else {
            element.addEventListener("click", () => {
                // Submit the hidden form.
                $$(".increase_streak")[index].submit()
            })
        }
    })

De delete goal styling wilde ik met de three dots als een drop down menu maken, omdat dat eerder een minimalistische app ‘look’ heeft dan een vuilnisbakje. Een kruisje voelde ook alsof deze te vaak gebruikt werd voor andere dingen, dus de drie puntjes waren hier een goede oplossing voor.  

De laatste dag heb ik ongebruikte fonts verwijderd, de images folder opgeruimd, responsiveness op Android opgelost, de toegankelijkheid geoptimaliseerd en een favicon in elkaar gezet. Bovendien heb ik de taak van de Design Rationale op mij genomen, om te laten zien dat ik de code begrijp. Ik had het nog wel uitgebreider kunnen doen, maar in de code zelf staan ook al heel veel comments dus het op volgorde van de customer journey uitleggen leek mij het handigste en minst herhalend/overbodig.

Lisanne

Sprint 1

Data: Maandag 23 2022 - woensdag 25 mei 2022
Code: GitHub

De eerste week bestond maar uit drie dagen door Hemelvaart. Op maandag was de kick-off en hadden bepaalde teams al het kennismakingsgesprek met hun bedrijf. Wij waren helaas niet één van die teams. Chippr, ons bedrijf, had ons gevraagd om voorafgaand aan het gesprek per team een stijlgids op te stellen. Daarnaast kregen wij vanuit school de opdracht om een debriefing te schrijven. Dit laatste deden wij samen met de andere drie studenten die voor het project SO-NUTS hadden gekozen. Toen wij hiermee klaar waren, besloot ik om alvast een server-side website op te zetten met Node.js. Zo konden wij de dag erop meteen beginnen met de HTML, CSS en JavaScript bestanden zelf. Ook zorgde ik ervoor dat de website op Heroku gedeployed was.

Dinsdag hadden wij in de ochtend en middag het kennismakingsgesprek met Chippr in Utrecht. Toen wij terug in Amsterdam waren, besloten wij meteen aan de slag te gaan. Wij besloten dat we geen desktop-variant van het project gaan maken, omdat Chippr er een app van maakt. Farrahton wilde graag werken aan de questionnaire, zowel het ontwerp als ervoor zorgen dat elke vraag in een nieuw scherm getoond wordt en het mogelijk is om hierbinnen heen en weer te navigeren. Eerst leek het mij handig om hier gelijktijdig aan te werken, maar overleg besloten we dat het fijner was als zij dit alleen zou doen. Ik begon te werken aan het inladen van de data en hielp tussendoor met het inladen van een custom font. Hoewel wij nog geen URL van de API hadden, kregen wij twee reponses toegestuurd, zowel de questionnaire zelf als de ingevulde antwoorden van een gebuiker. Hiermee kon ik aan de slag. Ik zorgde ervoor dat de data uit de JSON correct werd getoond in de HTML en dat alles klaarstond voor als wij de API URL zouden ontvangen, door o.a. node-fetch alvast te installeren. De verschillende soorten antwoordopties vertaalde ik naar de bijbehorende HTML-elementen, en onder de vragen laadde ik het antwoord in dat opgeslagen was in de JSON, om te oefenen met het koppelen van de antwoorden aan de bijbehorende vragen.

Sprint 2

Data: Maandag 30 mei 2022 - vrijdag 3 juni 2022
Code: GitHub

In de vrije dagen voor de tweede week had Farrahton hard gewerkt aan het verbeteren van het ontwerp, dus we hadden een goede start op maandag. Zij had een aantal JavaScript-bugs, waarvan ze dacht dat het sneller zou zijn om mij te vragen om hulp dan om hier zelf uren aan te zitten. Dat was fijn, want ik had het inderdaad vrij snel opgelost en heb plezier in het puzzelen met code. Toen dat klaar was, zorgde ik ervoor dat de onboarding maar eenmalig word getoond, door na de voltooing hiervan een item aan te maken in local storage en voortaan altijd te controleren of dit item al bestaat. Als dit het geval is, kan er meteen geredirect worden naar de questionnaire. Daarnaast leek het ons fijn als de antwoorden tussentijds worden opgeslagen in local storage, zodat de gebruiker tussendoor een pauze kan nemen en de voortgang niet verloren gaat. Om hiermee te beginnen, heb ik eerst ervoor gezorgd dat de antwoorden in een object variabele worden opgeslagen, en elke keer zodra een tekstveld aangepast wordt en een radio button/checkbox wordt veranderd overschreven wordt. Dit werkte perfect, behalve voor de vragen die zowel uit checkboxen als een tekstveld bestonden. Hiervoor heb ik een if-else statement geschreven die controleert of dit het geval is, en als dit zo is de value een uitgebreidere key geeft, zodat het niet overschreven wordt zodra de andere input aangepast wordt. Tussendoor heb ik ook nog even ervoor gezorgd dat de voortgang opgeslagen wordt in de local storage, en toegepast wordt zodra de pagina geladen wordt. Toen alle antwoorden eenmaal correct opgeslagen werden in de object variabele, zorgde ik ervoor dat deze variabele (bij elke verandering opnieuw) in de local storage wordt gezet en de antwoorden ingeladen worden bij het opnieuw bezoeken van de pagina. Omdat de ervaring met code die Farrahton en ik hebben uiteen loopt, hadden wij vooraf al besloten dat wij het belangrijk vinden dat zowel zij alle code helemaal begrijpt. Hierom hebben wij de code samen doorgelopen en comments toegevoegd. Toen dit klaar was, heb ik alvast één module geimplementeerd, zodat de code klaarstond voor het echte werk. Farrahton heeft vervolgens de code opgedeeld in twee modules, één voor elke pagina. Tegelijkertijd ontdekte ik een bug in de CSS, en heb ik de CSS omgeschreven zodat de pagina met flexbox dynamisch werd gemaakt. Ook heb ik de breedte op de desktop aangepast van mobiel naar tablet.

Dinsdag kregen wij eindelijk de URL van de API, en omdat onze code hier al op voorbereid was, koste ons het maar 5 tot 10 minuten om dit te implementeren. Helaas moesten wij nog wel even de SSL-validatie omzeilen, middels de onderstaande code (tot donderdag, toen was dit verholpen). Om voorbereid te zijn op scenario's waarin de API uitvalt, zorgde ik ervoor dat de code terugvalt op de lokale JSON als het de API niet kan bereiken. Na het gesprek met Winnie te hebben gehad, heb ik op haar advies de counter van de vragen vervangen door een progress bar en de tekst in de buttons groter en dikker gemaakt. Tussendoor heb ik de code netter gemaakt en ongebruikte code verwijderd. Ook kwamen wij erachter dat als het aantal vragen vanuit de API vermindert, en de opgeslagen voortgang in de local storage groter is dan het aantal vragen, er een lege pagina verschijnt. Dit heb ik opgelost middels een simpele if-else statement.

// Get the questionnaire from the API.
const response = await fetch("https://mibplatform.nl:5001/api/Questionnaires/2", {
    // Disable SSL validation.
    agent: new require("https").Agent({
        rejectUnauthorized: false
    })
})
const data = await response.json()

Nadat Farrahton twee modules had aangemaakt, één van elke pagina, heb ik de modules nog verder opgesplitst. Dit was aardig wat werk, omdat twee modules dezelfde variabele moeten kunnen aanpassen, namelijk de antwoorden. Uiteindelijk heb ik drie extra functies kunnen schrijven voor de questionnaire pagina: één om de antwoorden mee op te slaan, één om de antwoorden mee in te laden en één om de progress bar en buttons mee bij te werken. In de laatstgenoemde heb ik ervoor gezorgd dat de "Volgende" knop in de laatste vraag naar "Dashboard" veranderd en een andere kleur aanneemt. Verder heb ik de dashboard module in code verbeterd en de knop laten redirecten naar de dashboard pagina. Tot dan toe hadden we aparte stylesheets voor elke pagina, maar in de code reviews kwam naar voren dat dit niet handig was. Daarom besloot ik om een algemene stylesheet aan te maken, met alle elementen die dubbel voorkomen of sowieso voor elke pagina moeten gelden. Omdat ik er vanuit ging dat de antwoorden op de questionnaire aanpasbaar zouden worden, maakte ik alvast een pagina waarop de ingevulde antwoorden te zien en aan te passen waren. Helaas kwam ik er na wat praten met de back-ender van Chippr achter dat dit niet het geval zou zijn, en mijn werk dus voor niets was geweest. Verder heb ik mij beziggehouden met het gelijktrekken van de formattering van onze code, door bestaande code aan te passen, bestande te verplaatsen, dezelfde formatter te installeren in de Visual Studio Code van Farrahton en deze hetzelfde in te stellen.

Op vrijdag kwam de back-ender van Chippr weer met een nieuwe JSON, waarop ik onze code zo had aangepast dat het dit zou kunnen accommoderen. Toen op vrijdagavond de API eindelijk gevuld werd met gegevens, was de JSON die de API teruggaf toch anders dan degene die de back-ender ons had gestuurd. Farrahton en ik hebben samen gewerkt aan het aanpassen van onze code zodat zowel de locale JSON als die uit de API correct vertaald zou worden in de HTML. Het was de afgelopen even puzzelen aan hoe we de samenwerking zouden aanpakken. We hebben besloten dat Farrahton de grotere primaire taken op haar neemt, terwijl ik de bugs oplos en de optionele taken implementeer. Zo leren wij beiden genoeg van het project en is de taakverdeling eerlijker verdeelt. Voor mij persoonlijk is de grootste uitdaging van dit project dan ook niet het technische aspect, maar het samenwerken. Ons kennisniveau ligt ver uit elkaar, dus is het even zoeken naar een manier die voor ons beide fijn werkt. Afgelopen sprint hebben wij hier veel over gesproken en zijn wij tot afspraken gekomen om de samenwerking soepeler te laten verlopen.

Sprint 3

Data: Dinsdag 7 juni 2022 - vrijdag 10 juni 2022
Code: GitHub

De derde sprint ben ik begonnen met het opruimen en verplaatsen van een aantal CSS-eigenschappen naar de globale stylesheet. Ik kwam erachter dat CSS de onboarding niet dynamisch is, dus heb dit even geïmplementeerd middels flexbox. Verder heb ik nog een aantal details veranderd in het ontwerp, zoals dat de kleur van een knop mee veranderd met de tekst en de functie en dat de margin boven en onder de voortgangsstappen even hoog is.

Waar ik een aantal uur mee bezig ben geweest, is het opslaan van de meest recente data uit de API in een lokaal JSON-bestand. Zodra ik dit implementeerde, lukte het de webserver niet altijd meer om alle benodigde bestanden op te halen. Na lang zoeken kwam ik erachter dat dit kwam omdat nodemon de JSON-bestanden in de gaten hield, en de webserver telkens herstartte zodra hier iets in werd aangepast. Toen ik dit eenmaal wist, duurde het niet lang meer voordat het werkte. De map van de JSON-bestanden is in eerste instantie leeg (op een .gitkeep na, anders verwijdert Git het), en zodra er een response van de API binnen is wordt er een JSON-bestand aangemaakt. Vervolgens wordt dit bestand afgelezen. Als de request niet gelukt is, maar er bestaat wel een lokaal JSON-bestand, valt de webserver hierop terug.

Daarnaast heb ik de voedingsdoelen opgehaald vanuit de API en op de bijbehorende pagina ingeladen. Omdat ik graag boilerplate code wil voorkomen, besloot ik om te onderzoeken of ik het fetchen, opslaan en inladen van de data niet in een aparte functie kon zetten. Hier liep ik tegen het probleem aan dat de fetch asynchroon is, maar de rest niet. Na wat puzzelen is het mij toch gelukt, en sindsdien worden deze acties in een aparte asynchrone functie uitgevoerd.

Tevens heb ik ervoor gezorgd dat het antwoord op een vraag uit de localStorage gehaald wordt als het leeg is, en heb ik het object dat in de localStorage wordt opgeslagen omgeschreven, zodat het de antwoorden niet opslaat met als key de index maar het ID. Zo kon ik dit object meegeven aan de POST-request. Het implementeren hiervan was niet simpel, omdat de manier van vragen met meerdere antwoordopties (zowel checkboxen als een tekstveld) opslaan was veranderd. In de database stond het nu niet meer als extra antwoordoptie, maar als aparte key/value paren. Om tijd te sparen, heb ik besloten om voor nu ons object niet aan te passen, maar daardoor moest er wel wat JavaScript aan te pas komen die het object omzet. Vervolgens liep ik tegen een error aan. Ik probeerde de POST-request client-side uit te voeren, maar hiervoor had ik blijkbaar een proxy nodig. Met wat hulp van de Chippr-developers lukte mij dit, maar ik was niet tevreden. Nu was een deel client-side en een ander deel server-side. Het was niet mogelijk om het als een formulier te versturen, omdat de questionnaire in de HTML eigenlijk geen formulier is. Eén van de developers kwam op het idee om met een verborgen inputveld te werken, en stuurde een voorbeeld door. Dit is de logica die ik uiteindelijk geïmplementeerd heb. De gebruiker vult de questionnaire in, en telkens als hij/zij een antwoord wijzigt, wordt zowel de localStorage als het verborgen inputveld bijgewerkt. Als de gebruiker de questionnaire verstuurd, wordt de waarde van het verborgen inputveld naar de server verstuurd en daar een POST-request uitgevoerd. De gebruiker wordt vervolgens doorverwezen naar de volgende pagina. Als de server ziet dat er geen antwoord ingevuld zijn, slaat het de logica over die de antwoorden in het juiste formaat zet, zodat het niet crashed.

Terwijl ik het bovengenoemde aan het doen was, kwam ik erachter dat we een aantal acties in JavaScript uitvoerden die ook in Handlebars gedaan konden worden. Door deze logica te verplaatsen, heb ik heel wat regels JavaScript kunnen opruimen. Daarnaast heb ik ervoor gezorgd dat de dashboard pagina als standaardpagina wordt geladen. Op deze pagina wordt gekeken of de onboarding en questionnaire al zijn voltooid. Als dit niet het geval is, wordt de gebruiker doorverwezen naar de correponderende pagina's.

Tenslotte heb ik Farrahton geholpen met drie bugs die zij had, waardoor ze niet verder kon werken aan haar user story. Dit kostte mij meer tijd dan nodig, omdat ik graag haar code zo onaangetast mogelijk wilde houden. Als ik het zou herschrijven, zou ze er minder van leren. Ze kwam er zelf al achter dat de logica van haar JavaScript niet optimaal was. We hebben samen besloten om vanaf nu mij de leiding te geven over de JavaScript, en haar over het ontwerp. Dit betekent niet dat zij niet aan de JavaScript werkt en ik niet aan het ontwerp, maar wel dat we tijd besparen door elkaar's werk niet overnieuw te hoeven doen. Het leek ons handig als ze voortaan eerst de logica van de JavaScript met mij doorneemt, om geen tijd te verliezen aan trail-and-error pogingen. Qua ontwerp had ik een andere visie dan dat we op dat moment hadden, en een snelle schets daarvan heb ik naar haar gestuurd. Ze was enthousiast en gaat het (deels) implementeren.

Wat wij wel vervelend vinden, is dat wij de vierde sprint ingaan zonder een volledig werkende API ontvangen te hebben. De questionnaire is nog steeds niet compleet en de andere data is nog helemaal niet ingevuld. Hierdoor worden wij belemmerd in onze voortgang. Ik vraag meerdere keren per week naar een voorbeeld van de data, maar we krijgen maar niets binnen. De woordvoerder van Chippr zegt dat hij dit ook vervelend vindt, en dat dit komt doordat het studenten zijn die aan de API werken.

Sprint 4

Data: Maandag 13 juni 2022 - vrijdag 17 juni 2022
Code: GitHub

De vierde sprint begon ik met het modulariseren van de server-side JavaScript. Zo heb ik de get en post functies in losse bestanden gezet. Ook het omzetten van de antwoorden naar een compitabel formaat gebeurt nu middels een module. Daarnaast heb ik Farraton geholpen met het positioneren van de pop-up waarin nieuwe doelen kunnen worden toegevoegd, door deze bovenop de content te plaatsen en te centreren. De navigatiebalk van het dashboard stond ook niet helemaal onderaan, dus in de CSS heb ik iets aangepast om deze vast te zetten. De opties van de doelen werden, op de eerste na, niet getoond, dus dit heb ik gerepareerd. Daarnaast leek het mij handig als de voorheen geopende opties worden gesloten als de pop-up opnieuw wordt geopend. Maandagavond heb ik de JavaScript van de doelen pagina herschreven, zodat het leesbaarder, doeltreffender en efficiënter is. Terwijl ik hiermee bezig was, heb ik een aantal bugs verholpen. Zo werd er voorheen bij het toevoegen van nieuwe doelen tevens alle bestaande doelen toegevoegd, waardoor doelen er dubbel in kwamen te staan. Ook werkte het ophalen en inladen van de localStorage-doelen niet.

Dinsdag ben ik begonnen met het implementeren van Supabase. Wij hadden nog steeds geen complete API ontvangen, en hadden besloten om zelf maar een database te implementeren. Eerst heb ik wat geexperimenteerd met Firebase, maar dit was complexer dan nodig voor ons project. Supabase bleek heel gemakkelijk te zijn in de installatie. Ik heb drie tabellen aangemaakt, waarvan één tusssentabel die nodig was voor een many-to-many relatie (zie de onderstaande afbeeldingen). Verder heb ik ervoor gezorgd dat de invulvelden moeten worden ingevuld voordat er naar de volgende vraag kan worden genavigeerd. Dit was een kleine uitdaging bij de vragen die bestonden uit meerdere mogelijkheden, zoals radio buttons en een tekstveld.

Woensdag hield ik mij druk met het oplossen van bugs. Zo werden de total_repetition en timeframe van nieuwe doelen, op de eerste na, niet overgenomen in het overzicht. Ook werkte de plus-knop niet voor de doelen, op de eerste na. Omdat we hadden besloten om het dashboard te vervangen door de doelen pagina, heb ik alle onnodige bestanden van het dashboard verwijderd en de doelen pagina als standaard ingesteld. In de avond heb ik de hele HTML, CSS en JavaScript van de onboarding doorgelopen en opgeruimd. Daarnaast heb ik de HTML en JavaScript van de questionnaire schoongemaakt. Tijdens dit proces, kwam ik erachter dat de doelen op de questionnaire nog niet uit de API worden gehaald, en heb ik dit snel even geïmplementeerd. Verder heb ik wat geschoven in de HTML en JavaScript om onderdelen een logischere plek te geven. Ik ben eindeloos bezig geweest met het ervoor zorgen dat bij het toevoegen van nieuwe doelen, enkel die een event listener kregen, zodat er niet meerdere event listeners op één doel stonden. Ook hadden wij besloten om de opties te schappen, in verband met tijdsnood, dus heb ik deze beslissing in de code verwerkt.

Donderdag had Farrahton de login pagina af, en heb ik deze ingesteld als standaard pagina. Daarnaast heb ik wat kleine wijzigingen aangebracht, zoals het type van een e-mailadres aanpassen van text naar email, velden required maken en de submit knoppen laten werken (met de hulp van Farrahton). Wij besloten om een vaste total_repetition en timeframe in te stellen, en ik verwijderde de optie om dit als gebruiker zijnde aan te passen. Verder heb ik Supabase geïmplementeerd op de login pagina. Er wordt eerst gekeken of de gebruiker al bestaat in de database. Als dit zo is, wordt er doorgegeven of die de questionnaire af afgerond heeft en op basis hiervan doorgestuurd naar de juiste pagina. Als de gebruiker nog niet bestaat, wordt deze aangemaakt en doorgestuurd naar de questionnaire pagina. In de HTML van de doelen pagina heb ik een heel stuk code weggehaald, omdat die niet gebruikt werd. Op donderdag werd ik ook helemaal gek van een bug die ervoor zorgde dat niets meer werkte op de doelen pagina. Dit bleek uiteindelijk te komen doordat ik de ID's van doelen had weggehaald in de database, en in plaats daarvan de naam als primary key had ingesteld. De HTML en JavaScript verwezen nog wel naar de ID, waardoor er veel kapot ging. Na het omschrijven van de code, werkte alles weer.

Op vrijdag heb ik de ingevulde naam en e-mailadres laten opslaan in de localStorage, zodat deze kan worden meegestuurd als de questionnaire afgerond is. Het controleren of de questionnaire afgerond is, gebeurt nu compleet server-side en op basis van gegevens in de database in plaats van volledig client-side. Verder ben ik lang bezig geweest met een query die de persoonlijke doelen ophaalt. De tabel user_goals is een tussentabel, met twee foreign keys. Het is mij gelukt om zowel de data uit de tussentabel, foreign key tabel één en foreign key tabel twee op te halen, in één query. Ik wist al hoe dit in SQL moest, namelijk met een inner join, maar met Supabase had ik dit nog nooit gedaan. Omdat er maar één voorbeeld op het internet te vinden was, kostte mij dit meer tijd dan verwacht.

async function read_user_goals(email) {
    const reponse = await supabase
        .from("user_goals")
        .select(`
        id,
        email,
        goal,
        streak,
        user:email ( name ),
        goal:goal ( name )
        `)
        .eq("email", email)

    return reponse.data
}

Op donderdag kwam tevens het nieuws dat Chippr het project van de API uit de handen van de student(en) had genomen, en hier zelf mee aan de slag was gegaan. Zo kregen wij op donderdagavond ineens een complexe API opgeleverd, terwijl wij nog maar 4 dagen voor het project hadden. Onze coach Vasilis was niet bereikbaar, dus heb ik met Koop overlegd wat wij hier nu mee moeten doen. Hij adviseerde ons om verder te gaan met onze eigen database en de oude API, en verzekerde ons ervan dat dit echt niet ten koste zou gaan van het cijfer of de kwaliteit van het product dat wij gaan opleveren.

Tijdens de vierde sprint ben ik elke avond, op die van mijn verjaardag na, nog aan het werk geweest. Voor de vijfde sprint is dit helaas ook het plan, omdat wij in tijdsnood verkeren.

Sprint 5

Data: Maandag 20 juni 2022 - vrijdag 24 juni 2022
Code: GitHub

De laatste sprint ging ik in met een onprettig gevoel. Eigenlijk wilde ik deze laatste week gebruiken om aan optimalisaties, documentaties en kleine verbeteringen te werken, maar de basisfunctionaliteit stond nog steeds niet. Op maandag ben ik hier meteen hard mee aan de slag gegaan, en heb ik ervoor gezorgd dat zowel alle doelen als de persoonlijke doelen worden opgehaald uit de database en in de HTML worden getoond. Ik heb ook met een kritische blik naar de database gekeken en vervolgens de gebruikersnaam hieruit verwijderd. Deze vragen wij namelijk nergens op, alle informatie m.b.t. de gebruiker baseren wij op het e-mailadres. Nu wordt de naam enkel nog gebaseerd op de waarde die de gebruiker invult in het invoerveld op de inlogpagina. De ingevulde naam en e-mailadres worden via URL parameters doorgegeven van pagina op pagina. Ik heb ook geëxperimenteerd met localStorage, maar uiteindelijk waren URL parameters de meest passende manier voor ons om aan de server te laten weten voor welke gebruiker die data uit de database moet gaan halen. Dit was mijn eerste keer werken hiermee, dus ik was wat tijd kwijt aan het uitzoeken hoe ik dit het beste kon implementeren.

Dinsdag heb ik ook weer hard gewerkt. Ik heb de volgorde van de JavaScript op de server omgegooid, zodat het in de volgorde staat van het pad dat de gebruiker doorloopt. Verder was ik kort bezig met het uitzoeken waarom het ophalen van de persoonlijke doelen niet meer werkte, totdat ik er achter kwam dat Farrahton dit had uitgecomment tijdens het testen en was vergeten weer aan te zetten. Hierna maakte ik een drastisch besluit: de JavaScript van de doelenpagina waar ik zo ontzettend lang aan had gewerkt, moest helemaal opnieuw geschreven worden. Alles gebeurde namelijk client-side, terwijl wij de database willen aanspreken vanaf de server. Zoveel mogelijk JavaScript-logica heb ik verplaatst naar de server en Handlebars. Ik ben ook een tijd bezig geweest met het implementeren van de mogelijkheid om persoonlijke doelen toe te kunnen voegen, en om de streak hiervan te kunnen verhogen (tot en met 21). Dit alles gebeurt server-side met gebruik van de database. Ook werkte het doorgeven van de naam en emailadres vanuit de inlogpagina nog niet voor het pad dat niet-geregistreerde gebruikers doorlopen, dus dit heb ik laten werken. De localStorage werd ook nog niet geleegd nadat de questionnaire was verstuurd, dus ook dit heb ik geïmplementeerd. Tenslotte heb ik de categorie vanuit de database opgehaald, zodat Farrahton hiermee aan de slag kon, een typo gerepareerd en de naam meegegeven aan de profielpagina vanuit de doelenpagina.

Woensdag heb ik van 9 uur 's ochtends tot 12 uur 's nachts aan het project gewerkt. Ik begon met het in één module stoppen van de post en get functies, en het aanmaken van een nieuwe module voor de Supabase functies. Van de onboarding naar de questionnaire navigeren ging voorheen via een POST-request naar de webserver, maar dit bleek ook met enkel Handlebars te kunnen, dus dit heb ik aangepast. Op de doelenpagina heb ik voortgangsbalken onder de doelen toegevoegd, waarna Farrahton die heeft gestyled en op de juiste plek heeft gezet. Verder heb ik op de profielpagina de dagelijkse quote van ZenQuotes opgehaald en getoond. Voor de questionnaire had ik al wel een validate functie geschreven, die controleert of er tenminste één antwoord is ingevuld, maar nog niet geïmplementeerd. Dit heb ik gedaan door de knop op disabled te zetten, totdat het antwoord valide is. De styling hiervoor heb ik ook gemaakt. Terwijl ik hiermee bezig was, zag ik dat er nog wat dubbele code kon worden ondergebracht in de validate functie, zodat er geen boilerplate code ontstaat, en heb ik de functie in een nieuwe module gezet. Toen dit was gelukt, besloot ik om mij op wat kleinere taken te richten. Zo zorgde ik ervoor dat de focus automatisch op het invoerveld wordt geplaatst als er een nieuwe vraag in beeld komt, en simuleert de entertoets een klik op de "Volgende" knop. Toen ik hiermee klaar was, stortte ik mij op de optimalisaties. Zo heb ik Justus' advies opgevolgd en de $ en $$ functies als inline JavaScript in de HTML geplaatst, zodat het sneller laadt en er geen hele requests verstuurd hoeven te worden voor zulke kleine bestanden. Daarnaast heb ik de JavaScript van de profielpagina wat opgeschoond. Rond 1 uur 's middags was ik lekker bezig, en besloot ik mij nog te wagen aan een optionele functie: het verwijderen van een doel. Dit kostte minder tijd dan verwacht, en binnen de kortste keren (althans, zo voelde het) had ik dit aan de website toegevoegd. Farrahton heeft deze functie vervolgens gestyled. Toen ging ik weer verder aan de optimalisaties en keek ik met een kritische blik naar de code. Het JSON-bestand waar de webserver terug op kon vallen als de API het niet deed, was niet bij elke API-request geïmplementeerd. Ik vond dit meer een gimmick dan een feature, omdat de webserver op Heroku toch steeds gaat slapen als de website een tijdje niet opgevraagd wordt, en het bestand dan verloren gaat. Als het API dus echt uit zou vallen, is deze oplossing maar goed voor zolang als de Heroku webserver wakker blijft. Daarom besloot ik om deze functie helemaal te verwijderen uit het project. De link naar de profielpagina op de doelenpagina was net zo simpel als die op de onboarding, maar dit moest omgeschreven worden, om via de webserver API-requests uit te kunnen voeren. In de Supabase-functies heb ik de kolommen weggehaald waar vervolgens niets meer mee wordt gedaan, om de requests kleiner te maken, ervoor gezorgd dat er bij sommige functies maar één rij terug wordt gegeven, en een functie toegevoegd waarmee de hoogste streak van een gebruiker kan worden opgehaald (als er nog geen doelen zijn, wordt er 0 teruggegeven). De laatstgenoemde heb ik meteen geïmplementeerd op de profielpagina, want net zoals bij de quote was de layout er al klaar voor.

Afgelopen week had Wouter (lead front-end van Chippr) een pull request gedaan met de suggestie om Promise.all() te gebruiken als er meerdere API-requests achter elkaar worden uitgevoerd, zodat ze niet op elkaar hoeven te wachten. Dit was een zeer waardevolle suggestie, omdat wij er soms wel vier(!) achter elkaar uitvoerden. Alleen als het resultaat van een request wordt gebruikt bij de volgende, kan dit niet, en dit was maar bij één functie van ons het geval. Echter merkte ik dat de res.redirect() soms toch vroegtijdig uitgevoerd werd, en vroeg Wouter om hulp. Hij gaf aan dat dit kwam doordat ik niet bij alle then.() er een functie in had staan, en dit wel nodig is om het geforceerd te laten wachten. Doordat ik eerder op de dag een onzichtbaar formulier had toegevoegd, zette de JavaScript een class op het verkeerde formulier, dus dit heb ik even gerepareerd door het formulier met de bijbehorende class aan te spreken, in plaats van enkel met form. Op de profielpagina was er nog maar één onderdeel dat niet met dynamische data werktte: de ingevulde vragenlijst antwoorden. Om dit te implementeren, moest ik er eerst voor zorgen dat er een kolom aan de gebruikerstabel in Supabase werd toegevoegd, en dat die wordt gevuld met de questionnaireReponse ID die wordt teruggegeven als de questionnaire wordt verstuurd. Hiermee kon een API-request worden verstuurd, die de ingevulde vragenlijst antwoorden voor de juiste gebruiker ophaalt. Omdat de questionnaireReponse alleen de ID's van de vragen en multiple-choice antwoorden teruggeeft, en niet de tekst in een leesbaar formaat, heb ik in Handlebars een aantal loops in loops geschreven die van de ID's die met elkaar overeenkomen uit de questionnaire de tekst ophaalt, en uit de questionnaireReponse het antwoord. Tussendoor heb ik ook even compressie toegepast. Dit was niet veel werk omdat ik dit al bij het vak Progressive Web Apps had gedaan. Justus had ook de suggestie gedaan om routes toe te voegen om de overzichtelijkheid te verbeteren, dus dit heb ik gedaan. De Handlebars van de doelen- en profielpagina en de JavaScript van de doelenpagina heb ik ook opgeruimd. Rond half 10 's avonds heb ik de scripts en styles naar van de static naar een source map verplaatst, zodat ik dit middels een commando automatisch kon laten minifiëren als npm start wordt gerund. Daarnaast heb ik de Cache-control header ingesteld op een jaar, en een service worker toegevoegd. Hierin heb ik mijn eigen caching methode geschreven, namelijk dat de service worker eerst de niet-HTML bestanden uit de cache serveert, en de HTML rechtstreeks van de webserver serveert, maar wel in de cache opslaat. Als er geen internetverbinding is, serveert het de HTML uit de dynamische cache. Dit is alleen mogelijk als de pagina eerder bezocht is.

Donderdag hadden wij de presentatie aan de opdrachtgever om 14:00. Voor die tijd wilde ik nog een offline pagina implementeren. Farrahton had in de ochtend al de ongebruikte fonts verwijderd uit de code, waarna ik de bestanden weggooide, en ze had ook de afbeeldingen uitgezocht en opgeruimd. Ik had nog niet alle statische bestanden toegevoegd aan de service worker (voor in de core-cache), dus dit deed ik in de ochtend nog even. Farrahton de HTML en CSS van de offline pagina geschreven, waarna we samen de tekst nog iets aanpastte, wat CSS erbij schreven en ik deze aan de webserver toevoegde. Vervolgens ben ik een tijd bezig geweest met het ervoor zorgen dat de offline pagina wordt getoond als de aangevraagde pagina niet via het internet bereikt kan worden en niet in de dynamische cache wordt gevonden. Na veel gedoe met await is dat gelukt, nu snap ik het tenminste wel goed. We hebben de knoop doorgehakt om geen confetti te gebruiken, en dus heb ik de code die hiermee te maken had weggehaald. Toen dit achter de rug was, hebben wij ons gestort op de miniscule verbeteringen en de documentatie. Zo heb ik ervoor gezorgd dat de plusknop in de doelenpagina niet meer klikbaar is en er geen formulier wordt verstuurd als de streak al 21 is. Eerder gebeurde dit wel, en werd er pas op de webserver de streak gecontroleerd en op basis hiervan verhoogd. Toen wij de bijeenkomst begonnen, bedankte Kiara mij voor het communiceren met de opdrachtgever en het organiseren van de bijeenkomsten die hiermee gepaard gingen. Het klopte inderdaad dat ik veel contact met Chippr heb gehad, zowel met Kevin, Wouter, Sergio (de student die de API probeerde op te zetten) als Winnie. Vaak deed ik de woordvoering voor Farrahton en mij, maar door de andere teams werd er weinig tot niets in de Discord groep gezegd, dus ik vermoed dat de antwoorden op onze vragen hun ook voorzagen van de benodigde informatie. Alle drie aanwezigen van Chippr reageerden enthousiast op ons product, maar voornamelijk Wouter. Hij kwam nog met de tips om toestemming voor de local storage te vragen en om de pagina in te laden middels een stream. Tijdens de presentatie zagen wij nog twee kleine bugs, namelijk dat de "Dashboard" knop als die disabled is niet lichter is, en dat "squat" geschreven stond als "squad". Dit hebben wij meteen erna aangepast. Verder zijn wij voornamelijk bezig geweest met de documentatie. De uitleg van de code in de Design Rationale heeft Farrahton geschreven (en heb ik gecontroleerd, maar er waren weinig aanpassingen nodig), en aan de README.md hebben wij beiden gewerkt. In de avond kwam ik er nog achter, tijdens het testen op een oude Android, dat er wat kleine bugs in de CSS zaten. Dit heeft Farrahton opgelost en getest. Daarna heb ik nog een aantal keer de nieuwe commits naar Heroku gepushed, en een foutje van de formatter eruit gehaald.

⚠️ **GitHub.com Fallback** ⚠️