Design Rationale - kiara1404/so-nuts Wiki

Probleem definitie

Ouder worden heeft gevolgen op mensen hun leefstijl. Onder andere hun eet- en beweeggewoonten veranderen. Soms kan dit betekenen dat mensen extra gewicht krijgen, met name in vetmassa, terwijl de spiermassa juist afneemt. Er zijn al oplossingen om mensen op een aantrekkelijke, gepersonaliseerde en goedkope manier hulp te bieden. Toch ontbreekt het aan effectieve, duurzame eet- en beweeginterventies gericht op onze specifieke doelgroep: 55-plussers.

Mijn oplossing

De applicatie is gebaseerd op de antwoorden uit een questionnaire. De vragenlijst is vrij lang en dit is de reden dat ik de vragen geclusterd heb, vragen die hetzelfde thema hebben staan bij elkaar op de pagina. Hierdoor bestaat de questionnaire uit zes verschillende schermen in totaal. Daarbij heb ik een progress bar toegevoegd om de gebruiker een indicatie te geven waar ze zijn en hoelang het ongeveer nog gaat duren.

Wanneer de questionnaire voltooid is, krijgt de gebruiker een overzichtspagina met verschillende criteria. Bij deze criteria staat of een groen of grijs checkmark. Wanneer de checkmark groen is, betekent dit dat de gebruiker op dit vlak voldoende scoort en hier niet aan hoeft te werken. Ik heb er bewust voor gekozen om beide scores te tonen. Ik ben van mening dat het namelijk meer motiveert wanneer je ook de criteria ziet waar je wel goed op scoort in plaats van alleen de doelen die beter kunnen.
De gebruiker kan uit deze lijst maximaal drie doelen kiezen waar aan zij willen werken.

De volgende pagina is het dashboard. Vanuit daar kan je naar de detail pagina's. Op het dashboard is veel data te zien. Ik wilde dat de gebruiker in één opslag de stand van zaken kon zien. Het verschil tussen voeding en beweging is erg duidelijk gemaakt door gebruik te maken van twee complementaire kleuren. Dit begon al in de resultaten lijst en zet dus door in het gehele ontwerp.

Een van de core functionaliteiten is dat de gebruiker zélf data kan toevoegen. Ik wilde dit graag in mijn ontwerp zodat de gebruiker achteraf hun activiteit kan invoeren in plaats van een geplande workout. Ik denk dat dit helpt om meer realistische data te krijgen en ervoor zorgt dat de gebruiker bewuster kijkt naar hun activiteit of voeding. Ook denk ik dat het motiverend is wanneer de data zich update en je visueel ziet dat je dichter bij je doel komt.

De laatste pagina die ik wil laten zien is de progressie pagina. Hier heeft de gebruiker een overzicht van de doelen, feedback ten opzichte van de vorige week en wat tips. Het idee is dat de tips getoond worden op basis van welke doelen minder goed gaan. Dit om de gebruiker te motiveren om op die doelen te letten.

Uitleg code

Data uit API

Het ophalen van de vragen uit de Chippr API was nog een enige uitdaging. Je moest namelijk eerst een accesToken aanvragen, na veel geklooi was mij dit uiteindelijk gelukt. Alleen later zeiden ze dat het niet meer nodig was omdat ze alle GET requests open hadden gezet.

// getToken
fetch('https://sonuts.chippr.dev/api/Token', {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({ email: '[email protected]' })

}).then(res => res.json())
// .then(data => console.log(data))


let token = process.env.SECRET_KEY

fetch('https://sonuts.chippr.dev/api/questionnaire?categoryId=efc0026f-7799-4f77-9bb4-3bb0bf0c2c5a'', {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
    }

}).then(res => res.json())
.then(data => console.log(data))

Dus uiteindelijk hoefde ik het accesstoken niet meer te gebruiken en kon ik makkelijk binnen een async/await meerdere calls doen. Ik wist eerst niet hoe ik ervoor kon zorgen dat ik meerdere api calls binnen één functie kon doen. Nu herhaal ik eigenlijk voor elke call dezelfde stappen. Ik vroeg nog aan Robert of dit zo kon en hij zei dat het misschien beter zou zijn als ik de urls in een array had gestopt en met Promises.all() had gewerkt maar dit werkte ook en kon ook gewoon. Deze data wordt namelijk uit de nieuwe API gehaald die we een week voor de deadline kregen. Ik sla de data die geparst is op in een variable zodat ik die kan meesturen voor EJS.

app.post('/form_1', getData);

// get questions from API
async function getData(req, res) {
    try {

        const intakeData = await fetch('https://sonuts.chippr.dev/api/questionnaires?categoryId=efc0026f-7799-4f77-9bb4-3bb0bf0c2c5a')
        const intakeDataResponse = await intakeData.json()
        const bewegingData = await fetch('https://sonuts.chippr.dev/api/questionnaires?categoryId=ef2cac2a-5081-4c73-b5ed-36914fd464fa')
        const bewegingDataResponse = await bewegingData.json()
        const voedingData = await fetch('https://sonuts.chippr.dev/api/questionnaires?categoryId=7f276f90-80e7-472e-a7e6-9ccbd37cd5b7')
        const voedingDataResponse = await voedingData.json()

        console.log(voedingDataResponse.questions[0])
        // console.log(data)
        res.render('pages/form_1', {
            personal: intakeDataResponse,
            beweging: bewegingDataResponse,
            voeding: voedingDataResponse
        })
    }
    catch (err) {
        console.log(err)

    }
}

Rendering met EJS

Zoals je hierboven hebt kunnen zien, heb ik de data meegestuurd in de res.render(). Dit zorgt ervoor dat ik met EJS die data kan aanspreken. Ik heb wel elke vraag helemaal moeten uitwerken omdat ik de vragen geclusterd heb op thema. Als ik dit niet had gedaan en elke vraag een voor een had laten zien, had ik het een stuk korter en bondiger kunnen maken.

 <!-- persoonsgegevens-->
    <fieldset id="form_personal">
        <legend>
            <h3><%- personal.description%></h3>
        </legend>
        <label for="<%- personal.questions[7].id %>"><%- personal.questions[7].text %></label>
        <input type="text" name="<%- personal.questions[7].id %>" placeholder="Nederland">
        <label for="<%- personal.questions[12].id %>"><%- personal.questions[12].text %></label>
        <input type="text" name="<%- personal.questions[12].id %>" placeholder="Nederland">
        <label for="<%- personal.questions[4].id %>"><%- personal.questions[4].text %></label>
        <input type="text" name="<%- personal.questions[4].id %>" placeholder="Nederland">
        <h3><%- personal.questions[6].text %></h3>

        <select>
            <% for(let i = 0; i < personal.questions[6].answerOptions.length; i++) { %>
            <option value="" id="<%- personal.questions[6].answerOptions[i].id %>">
                <%- personal.questions[6].answerOptions[i].text %>
            </option>
            <% } %>
        </select>

    </fieldset>

Opslaan van gegevens met node FileSystem

Voor elke categorie heb ik een aparte pagina met en apart formulier gemaakt. Wanneer er op submit gedrukt wordt, wordt er een POST request gedaan die ik opvang. Doordat ik fs heb geinstalleerd kan ik die data opvangen en opslaan in een json file. Dat is wat hieronder gebeurd. Je moet eerst definieren welke data je wil opvangen, dat heb in een const variable gedaan in een vorm van een object. Daarna moe je het parsen naar json formaat en de volgende stap is dat fs een nieuw file aanmaakt die in dit geval 'cardio.json' heet. Helaas is het mij niet gelukt om meerdere objecten op te slaan en wordt de data per POST request nu overschreven.

app.post('/added_cardio', (req, res) => {
    let cardioStringData;
    const cardioData = {
        "trainingType": req.body.cardio_type,
        "minTraining": req.body.cardio_duration
    }
    cardioStringData = JSON.stringify(cardioData);


    fs.writeFile('public/json/cardio.json', cardioStringData, (err, data) => {
        if (data) {
            cardioStringData = JSON.parse(data)
        }
        if (err) {
            console.log(err)
        }
    });

    res.redirect('training')

})

Meesturen van data met FileSystem

Nu ik meerdere json files heb, moet ik deze data ook kunnen meesturen zodat EJS de data kan renderen. Nu had ik dat wel al eerder gedaan maar niet met meerdere json files. Dit was wel even puzzelen en zoeken op internet. Uiteindelijk heb ik een functie op StackOverflow gevonden die gebruik maakt van het NPM package async. Nu worden alle json files geladen en kan ik ze parsen om ze mee te sturen. Een ander probleem waar ik tegenaan liep was dat de integer data als string werd opgeslagen in de json files. Nu wilde ik er berekeningen mee doen dus moest ik het omzetten naar integer. Daar is de .parseInt() functie voor.

//dashboard
app.get('/dashboard', (req, res) => {
    const files = ['public/json/cardio.json', 'public/json/kracht.json', 'public/json/kcal.json', 'public/json/eiwitten.json', 'public/json/groente.json'];
    // got this function from stackOverflow: https://stackoverflow.com/questions/58424336/reading-multiple-files-asynchronously-in-node-js
    async.map(files, fs.readFile, function (err, data) {
        let stringDataCardio = JSON.parse(data[0])
        let stringDataKracht = JSON.parse(data[1])
        let stringDataKcal = JSON.parse(data[2]);
        let stringDataEiwitten = JSON.parse(data[3]);
        let stringDataGroente = JSON.parse(data[4]);

        let kcalData = parseInt(stringDataKcal.kcal);
        let eiwitData = parseInt(stringDataEiwitten.eiwitten);
        let groenteData = parseInt(stringDataGroente.groente);
        let krachtData = parseInt(stringDataKracht.minTraining)
        let cardioData = parseInt(stringDataCardio.minTraining)

        res.render('pages/dashboard', {
            cardioData: cardioData,
            krachtData: krachtData,
            cardioType: stringDataCardio,
            kcalData: kcalData,
            eiwitData: eiwitData,
            groenteData: groenteData
        });
    })
});
⚠️ **GitHub.com Fallback** ⚠️