OBA - kosterm14/portfolio GitHub Wiki

Opdrachtgever

Ik heb bijna wekelijks meetings gehad met mijn OBA opdrachtgever Mark Vos. Dit deden we zowel fysiek als online. In deze meetings bespraken we de voortgang, stelde ik vragen, liet ik mijn code en designs zien en meer.

24/04

Vandaag heb ik de eerste meeting met mijn nieuwe opdrachtgever. Ik kom net van CMD dus ik begin nu voor het eerst aan deze opdracht en ga vandaag mijn opdrachtgever voor het eerst spreken.

Ik heb gewerkt aan een error pagina voor de OBA website, deze pagina is te zien als je een onbekend pad invult en laat de gebruiker zo weten dat er een fout is.

Mark snapte in eerste instantie niet precies hoe je op deze pagina kwam. Hij dacht in eerste instantie dat dit een algemene error pagina was die alle errors afhandelde. Hij was hierna verward tussen server errors en gebruiker errors. Ik heb het hem nogmaals uitgelegd en heb het voor hem verduidelijkt. Het is een pagina die alle errors afhandeld van verkeerd ingevulde paden. Dit handeld gebruikers fouten af in het invullen van een pad en niet server fouten. Na mijn verduidelijking snapte hij het wel.

Ik vroeg Mark hoe hij precies wou dat deze pagina eruit ging zien en hij gaf aan wat hij de belangrijkste punten vond:

  • Een gebruiker moet terug kunnen naar de voorgaande pagina
  • De gebruiker moet feedback krijgen dat het niet aan hem ligt maar aan het systeem
  • Een duidelijke route terug naar de home pagina
  • Een chatomgeving om gebruikers te helpen (Hij gaf aan dat hij me een script zou sturen hiervoor)
  • Het moet gebruiksvriendelijk zijn

Mark vond het erg fijn dat ik meedacht aan nieuwe functionaliteit voor de OBA website en gaf aan dat OBA een doelgroep heeft die niet super digivaardig is dus een manier om fouten af te handelen komt goed van pas.

08/05

Vandaag heb ik weer een meeting met de opdrachtgever. Ik heb alles gedaan waar hij om vroeg en ik ga hem dit laten zien!

Ik heb de error pagina verbeterd door me te focussen op de 5 punten die de opdrachtgever me verteld had vorige keer:

1. Een gebruiker moet terug kunnen naar de voorgaande pagina

  • Ik heb breadcrumbs toegevoegd aan de error pagina om zo de gebruiker feedback te geven wat betreft de pagina waar ze vandaag kwamen. Zo kan de gebruiker ook weer terug navigeren naar de vorige pagina.

2. De gebruiker moet feedback krijgen dat het niet aan hem ligt maar aan het systeem

  • Ik heb de tekst op de error pagina veranderd om het zo wat natuurlijker te maken. De oude tekst was

maar ik heb dit veranderd naar

3. Een duidelijke route terug naar de home pagina

  • Met de toevoeging van de breadcrumbs en de verbeterde tekst heeft de gebruiker twee duidelijke routes terug naar de home pagina.

4. Een chatomgeving om gebruikers te helpen (Hij gaf aan dat hij me een script zou sturen hiervoor)

  • Omdat de opdrachtgever me nog geen script gestuurd heeft heb ik ervoor gekozen een eigen idee uit te werken en deze aan hem te presenteren. Met deze chatbot kan de gebruiker vragen stellen en antwoorden hierop ontvangen zonder dat een medewerker nodig is.

De chatbot reageert met de volgende vijf reacties (in deze volgorde) ongeacht van wat de gebruiker vraagt. Ik heb voor deze opties gekozen omdat het me het meest likely lijkt dat de gebruiker de hulp van de chatbot inschakeld omdat hij of zij op zoek is naar een specifiek boek. Deze opties kunnen daar dan mee helpen.

  1. Hallo! Welkom bij de Openbare Bibliotheek Amsterdam. Waarmee kan ik u vandaag helpen?
  2. Dat klinkt goed! Kunt u mij de titel of het onderwerp geven waarin u geïnteresseerd bent?'
  3. Ik kan daar zeker bij helpen! Zoekt u naar een specifiek boek of wilt u een lijst van aanbevelingen?'
  4. Dat boek is te vinden op de tweede verdieping, bij de sectie Literatuur. Wilt u nog iets anders weten?'
  5. Graag gedaan! Als u nog andere vragen heeft, aarzel niet om contact op te nemen. Fijne dag verder en we hopen u snel weer te zien in de OBA!

5. Het moet gebruiksvriendelijk zijn

  • De vernieuwde tekst op de error pagina is veel gebruiksvriendelijker en natuurlijker dan voorheen.

Ik liet hem zien dat ik alles gedaan heb waar hij om vroeg en hij was daar blij mee.

Hij gaf aan dat de natuurlijke error pagina tekst veel beter was dan de oude "Error - 404" tekst.

Hij vond de breadcrumbs erg goed gelukt. Omdat op dit moment de hele pagina vol zou kunnen raken met breadcrumbs gaf ik het idee om alleen de drie recentste pagina's te laten zien in de breadcrumbs. Dit vond hij een goed idee.

Ik liet naast de uitgewerkte chatbot ook een schets zien van een idee wat ik had. Ik dacht eraan om de chatbot simpeler te maken door de gebruiker een aantal opties te geven in plaats van de vrijheid om te typen wat ze willen. Dit zorgt ervoor dat de chatbot alleen een aantal antwoorden nodig heeft en niet hoeft te kunnen reageren op alle zinnen die de gebruiker kan typen. Hij gaf aan dat het een goed idee was om opties te geven in de chatbot en vond het erg fijn dat ik meedacht.

Hij gaf vorige meeting aan dat hij me een script door zou sturen voor de chatbot maar dit heeft hij nog steeds niet gedaan. Hier ging ik dus ook achteraan. Eerst op teams maar omdat hij daar niet reageerde vroeg ik het tijdens de meeting ook nog een keer.

De opdrachtgever vroeg me ook om een nieuwe feature toe te voegen aan de website. Hij wil dat de gebruiker boeken toe kan voegen aan de leeslijst en boeken kan verwijderen hieruit.

22/05

Vandaag heb ik de derde meeting met de opdrachtgever gehad. Ik liet zien waar ik mee bezig was en kreeg hier feedback op. Ook vroeg ik om wat duidelijkheid op het gebied van details.

We hebben een zoekbalk op de homepage. Hij gaf aan dat hij het fijn zou vinden als de zoektermen van deze zoekbalk ook terug kwamen in mijn breadcrumbs.

Ik heb, na vraag van de opdrachtgever vorige meeting, ervoor gezorgd dat de breadcrumbs alleen de drie meest recente pagina's weergeven. Alles ouder dan dit wordt aangegeven met '...'.

Ik gaf aan het idee te hebben om meer dan drie opties te weergeven in de breadcrumbs. Mijn idee was om de drie recentste opties standaard weer te geven, maar als de gebruiker op de ... drukt het uit te laten klappen om er zo vijf te laten zien. De opdrachtgever was het eens met mijn idee en gaf aan het een goed idee te vinden om, als de gebruiker op de ... drukt, de laatste 16 pagina's te laten zien (onder elkaar uitgeklapt).

Ik stelde ook voor om op error pagina's alleen het pad na de / te weergeven in plaats van het hele pad zoals hieronder. In plaats van 'betalingen/bshjfbshejf' zou er dan 'bshjfbshejf' komen te staan. In de breadcrumbs kun je sowieso al zien dat je van de betalingen pagina kwam dus dit twee keer weergeven is onnodig. De opdrachtgever was het met me eens.

image

Ik vroeg of hij wou dat ik hoofdletters toevoegde aan de breadcrumbs maar hij gaf aan dat hij mijn hoofdletter loze look er goed en gebruiksvriendelijk uit vond zien dus dit was niet nodig.

Ik stelde voor om de homepagina toe te voegen aan de breadcrumbs. Op dit moment worden alleen de aparte pagina's toegevoegd aan de breadcrumbs en hij gaf aan dat de home pagina niet toegevoegd hoefde te worden omdat we een navigatie hadden met een homebutton. Ik stelde toch nogmaals voor om home wel toe te voegen aan de breadcrumbs omdat dit gebruikers zowel visuele feedback geeft dat ze van de homepagina kwamen en gebruikers een klik bespaard omdat ze dan niet helemaal het nav menu hoeven te openen om terug te keren naar de home pagina. Met deze twee redenen heb ik hem toch nog overgehaald.

Dingen toevoegen en verwijderen aan de leeslijst was ik nog niet aan toegekomen vertelde ik hem en hij vroeg me om ook de functionaliteit van dingen toevoegen aan de reserveringen pagina rechtstreeks vanuit de leeslijst toe te voegen. Ook vertelde hij me dat ik het chatbot script nog van hem zal krijgen maar ik wacht hier nu al een maand op dus kleine kans denk ik.

29/05

Ik heb een morphological chart gemaakt in Figma waarin ik nadenk over de vormgeving van de boeken in de leeslijst. Deze ga ik vandaag aan de opdrachtgever laten zien. Ik heb meerdere opties in low fidelity uitgewerkt op het gebied van card design, images, text en interaction.

We brainstormde samen welke opties het beste waren. Hij gaf aan dat het belangrijk is dat de cards verticaal zijn. Dit omdat het visueel moet zijn voor de gebruiker dat het een boek is. Ook moet ik veel ruimte geven aan de image en het zo visueel mogelijk maken. Gebruikers willen zien welk boek het is, niet lezen.

Na wat overleggen kwamen we op het idee om de tekstvelden standaard een lagere opacity te geven en ze on hover pas echt te laten zien. Dit is een goede middle ground omdat je zo en de image veel ruimte geeft en een groot veld kan hebben voor tekst. We kwamen op een van de onderstaande drie als design.

Omdat David voor zijn pagina een soortgelijk ontwerp gebruikt als de derde stelde ik voor om David's ontwerp aan te houden voor consistentie. Hij was het daar mee eens en ik ga dus dat ontwerp gebruiken.

Ik had ook verder gewerkt aan de chatbot maar hij gaf aan dat ik deze kon laten liggen voor nu.

05/06

De vijfde meeting met de opdrachtgever al weer. Ik liet vandaag zien hoe ver ik was met het bouwen van de leeslijst.

Hieronder is het oude ontwerp van de leeslijst te zien.

In de vorige meeting heb ik samen met de opdrachtgever overlegd en we kwamen samen uit op een beter ontwerp. Ik heb de oude kaartjes veranderd naar dit nieuwe ontwerp. Deze zijn hieronder te zien.

Ik heb progressive disclosure toegepast door de super lange titels standaard een overflow:hidden te geven en alleen volledig te tonen indien de gebruiker op het kaartje hovert.

Ik heb ook een rode versie ervan voor de andere OBA kleur.

De opdrachtgever was erg blij met mijn leeslijst en vond het goed gelukt. Hij zei "Ziet er heel vet uit" en gaf me een paar pointers.

  • Voeg een 'reserveer nu' knop toe aan de boeken

  • Omdat de titels zo lang zijn kun je de titels zelfs on hover afsnijden na 2 regels

  • Haal de breadcrumbs weg op de login pagina

  • Boeken vanuit de zoekopdracht kunnen toevoegen aan de leeslijst en/of kunnen reserveren

  • Als een boek niet beschikbaar is om te reserveren geef aan "over x dagen beschikbaar" anders "nu af te halen

  • Wensenlijst ipv leeslijst

  • Vanuit de wensenlijst boeken kunnen reserveren en de beschikbaarheid kunnen zien

  • Als het me lukt om alles in het kaartje te stoppen (Boek afbeelding, boek titel, boek auteur, wensenlijst, beschikbaarheid, reserveren knop) en het toch duidelijk te maken voor de gebruiker zou dat geweldig zijn

Vandaag gaf ik echt heel veel eigen ideeën naar mijn mening. Ik ben blij met mijn input vandaag en ik denk echt dat de opdrachtgever dit ook waardeert.

Verwachtingen sturen

Ik zou graag meer gedaan hebben maar gecombineerd met mijn individuele opdracht en het feit dat dit de laatste week van de opleiding is en ik heel veel te doen heb ben ik daar niet aan toegekomen. Ik heb de opdrachtgever dit laten weten om zijn verwachtingen van me een beetje bij te sturen.

We zouden de 19e ons werk presenteren aan de OBA frontenders die ook echt verder gaan werken aan deze website. Na dit gesprek hebben we besloten de deadline te verschuiven naar de 24e.

Onderbouwing ontwerpkeuzes

  • De kaartjes zijn verticaal gemaakt om zo voor de gebruiker duidelijk te maken dat ze boeken voor moeten stellen.
  • Ik geef de afbeelding zo veel mogelijk ruimte om het zo visueel mogelijk te maken. Zo kunnen gebruikers zien welk boek het is en hoeven ze dit niet te lezen.
  • Ik heb de andere kaartjes op de website gebruikt als inspiratie om mijn ontwerp zo consistent mogelijk te houden.
  • Ik heb progressive disclosure toegepast door de super lange titels standaard een overflow:hidden te geven en alleen volledig te tonen indien de gebruiker op het kaartje hovert.

image 337928838-a0622d28-e78f-48fd-8a1a-64dd371ae3ce

Onduidelijkheid

Ik vroeg hierna om wat duidelijkheid. We praten al weken over een leeslijst maar er komen elke keer nieuwe dingen of wijzigingen. Ik ben helemaal kwijt wat nou de bedoeling is.

Mijn idee was dat er een boekenlijst was waar alle boeken te vinden waren. Hier kon je een boek kiezen en deze toevoegen aan je leeslijst, dit is een lijst met boeken die je wilt lezen. De leeslijst is geen aparte pagina maar een uitklapbaar component dat zich bevind op elke pagina als een soort wishlist. Ook hadden we een reserveringen pagina maar voor mij was dit hetzelfde als de boekenlijst en dus een onnodige pagina.

In mijn gedachte zag de website er dus zo uit:

Ik gaf deze onduidelijkheid aan en ik heb samen met mijn opdrachtgever een schets gemaakt van wat er nou precies op de OBA website komt. Hieronder zijn schetsen van de reserveringen en wensenlijst (leeslijst) te zien

Na dit proces werd me eindelijk duidelijk wat de bedoeling was. Er komt helemaal geen boekenlijst. Ook moet de wensenlijst wel een aparte pagina zijn en geen component. Als de gebruiker een boek zoekt doormiddel van de zoekfunctie, krijgt hij de optie dat boek toe te voegen aan een wensenlijst of om het boek te reserveren.

Het grote verschil tussen deze twee is dat het toevoegen aan de wensenlijst iets is van "ik wil dit boek wel lezen, ik voeg het toe aan de leeslijst voor later" maar het toevoegen aan de reserveringen pagina is iets van "Ik ga dit boek sowiezo lezen, ik ga het reserveren". Het verschil is de intentie van de gebruiker.

Ook moeten alle pagina's met elkaar verbonden zijn, vanuit de zoekopdrachten moet je boeken toe kunnen voegen aan zowel de wensenlijst als de reserveringen. Ditzelfde geldt voor de wensenlijst, hieruit moet je ook boeken toe kunnen voegen aan de reserveringen. Tot slot moet je ook op de reserveringen pagina boeken toe kunnen voegen aan de wensenlijst.

Zo ziet het er dus uit:

Verificatie aantekeningen

Omdat David ook bij elke meeting met de opdrachtgever was, heb ik hem gevraagd mijn aantekeningen door te lezen om zeker te zijn niks gemist te hebben.

Ideeen

Gedurende de 5 meetings met mijn opdrachtgever heb ik talloze van mijn eigen ideeën en verbeteringen gedeeld met mijn opdrachtgever. Ik heb deze hieronder op een rijtje gezet:

24/04

  • Ik heb een gloednieuwe error pagina toegevoegd. Ik vond dit een goed idee omdat er op de huidige pagina niet nagedacht werd over gebruikersfouten. Dit terwijl de gemiddelde OBA gebruiker niet heel digitaal is.

08/05

  • Als de gebruiker veel pagina's bezocht heeft, nemen de breadcrumbs erg veel ruimte in. Ik stelde daarom voor om de breadcrumbs een max te geven van de recenste drie pagina's.
  • Omdat de chatbot erg ingewikkeld was gaf ik het idee om de gebruiker een aantal vooraf geschreven keuzes te geven in plaats van een inputveld. Zo hoef je niet een oneindige mogelijk van antwoorden voor te bereiden.

22/05

  • Ik stelde voor om de breadcrumbs uitklapbaar te maken om meer dan de recentste drie pagina's te weergeven in de breadcrumbs als de gebruiker op de '...' drukt. Zo voeg je progressive disclosure toe.
  • Op de error pagina is het pad van de pagina erg lang. Omdat dit misschien niet heel gebruiksvriendelijk is stelde ik voor om een deel van dit pad weg te laten.
  • Ik stelde voor om de homepagina toe te voegen aan de breadcrumbs. Op dit moment werden alleen de aparte pagina's toegevoegd aan de breadcrumbs en hij gaf aan dat de home pagina niet toegevoegd hoefde te worden omdat we al een navigatie hadden met een homebutton. Ik stelde toch nogmaals voor om home wel toe te voegen aan de breadcrumbs omdat dit gebruikers zowel visuele feedback geeft dat ze van de homepagina kwamen en gebruikers een klik bespaard omdat ze dan niet helemaal het nav menu hoeven te openen om terug te keren naar de home pagina. Met deze twee redenen heb ik hem toch nog overgehaald.

29/05

  • Ik heb een morphological chart gemaakt in Figma om mijn idee van een verbeterd ontwerp te laten zien. Ik heb hierin heel veel opties mogelijk gemaakt zodat de opdrachtgever veel keuze had. Zowel op het gebied van card design, images, text en interaction.
  • Tijdens het kiezen uit de morphological chart wou de opdrachtgever in eerste instantie blijven bij een veilige vormgeving. Hij dacht er ook aan om een meer speels ontwerp te kiezen maar hij vond het belangrijk dat de gebruiker voornamelijk afbeeling zag en deze optie had minder ruimte daarvoor. Ik stelde daarom voor om de speelse optie te kiezen en het tekstveld standaard transparant te maken tot de gebruiker erop hovert.

05/06

  • We kwamen vorige keer op de conclusie om het tekstveld transparant te maken om de afbeelding meer te kunnen zien. In plaats daarvan stelde ik voor om het tekstveld zowel transparant als kleiner te maken en de tekst af te snijden. Indien de gebruiker erop hovert blijft het transparant maar wordt het veel groter en wordt de volledige tekst weergeven.
  • Ik stelde voor om iconen te gebruiken om aan te geven wat stilteruimtes zijn op de OBA map ipv een legenda
  • Ik stelde voor om het lege input veld waar je de datum in moet vullen een standaard waarde te geven van de huidige dag.
  • Ook gaf ik aan om het lege input veld waar je je OBA id in moet vullen, standaard te vullen met de OBA id van de huidige gebruiker. Dit omdat je al ingelogd moet zijn om überhaupt op deze pagina te komen.
  • Ik gaf ook aan dat ik altijd aan zou geven welke gebruiker ingelogd is. Dit is nu verborgen in een menu maar op elke andere site is het altijd rechtsboven zichtbaar met een profielfoto op iets dergelijks.
  • Ik stelde de optie voor om het mogelijk te maken om ook voor een vriend te kunnen reserveren.
  • Ik stelde voor om de leeslijst en de reserveringen pagina te combineren aangezien ze een soortgelijke functionaliteit hebben.
  • Ook stelde ik voor om in plaats van woorden zoveel mogelijk iconen te gebruiken. Zo wil ik bijvoorbeeld een hartje toevoegen aan de boeken om het toe te voegen aan de wensenlijst. Dit in plaats van wat de opdrachtgever aangaf waar je het toevoegd aan de wensenlijst met een "Voeg toe aan wensenlijst" knop.

Chatbot

Ik kreeg van mijn opdrachtgever de vraag om een een chatomgeving te maken om gebruikers te helpen als ze vragen hadden. Het grootste probleem waar ik meteen tegen aan liep is:

  • Hoe kan ik ervoor zorgen dat ik kan reageren op de miljoenen vragen die de gebruiker me kan stellen?

Om dit probleem op te lossen besloot ik een chatbot te maken die reageert op basis van een vooraf geschreven script. De chatbot reageert met de volgende vijf reacties (in deze volgorde) ongeacht van wat de gebruiker vraagt. Ik heb voor deze opties gekozen omdat het me het meest likely lijkt dat de gebruiker de hulp van de chatbot inschakeld omdat hij of zij op zoek is naar een specifiek boek. Deze opties kunnen daar dan mee helpen.

  1. Hallo! Welkom bij de Openbare Bibliotheek Amsterdam. Waarmee kan ik u vandaag helpen?
  2. Dat klinkt goed! Kunt u mij de titel of het onderwerp geven waarin u geïnteresseerd bent?'
  3. Ik kan daar zeker bij helpen! Zoekt u naar een specifiek boek of wilt u een lijst van aanbevelingen?'
  4. Dat boek is te vinden op de tweede verdieping, bij de sectie Literatuur. Wilt u nog iets anders weten?'
  5. Graag gedaan! Als u nog andere vragen heeft, aarzel niet om contact op te nemen. Fijne dag verder en we hopen u snel weer te zien in de OBA!

Ik heb gebruik gemaakt van de svelte writable functie om een schrijfbare store te maken, wat een soort state management is. Een writable store is een type store dat zowel gelezen als geschreven kan worden. Dit betekent dat je de waarde van de store kunt bijwerken en dat elke component die zich op deze store abonneert, op de hoogte wordt gebracht van de veranderingen. Het variabel messages maak ik daarna een writable.

Ik zorg ervoor dat message standaard een lege string bevat. Dit zorgt ervoor dat het bericht standaard leeg is. In de functie sendMessage update ik de messages writeable met de verzonden messages. Na het verzenden geef ik message weer de waarde van een lege string om het input veld te resetten na elk bericht.

Na een 1000ms timeout zal de bot reageren op het bericht met een van de vooraf geschreven antwoorden.

<script>
	import { writable } from 'svelte/store';
	let message = '';
	let messages = writable([]);
	let botResponses = [
		'Hallo! Welkom bij de Openbare Bibliotheek Amsterdam. Waarmee kan ik u vandaag helpen?',
		'Dat klinkt goed! Kunt u mij de titel of het onderwerp geven waarin u geïnteresseerd bent?',
		'Ik kan daar zeker bij helpen! Zoekt u naar een specifiek boek of wilt u een lijst van aanbevelingen?',
		'Dat boek is te vinden op de tweede verdieping, bij de sectie Literatuur. Wilt u nog iets anders weten?',
		'Graag gedaan! Als u nog andere vragen heeft, aarzel niet om contact op te nemen. Fijne dag verder en we hopen u snel weer te zien in de 
                 OBA!'
	];
	function sendMessage() {
		messages.update((currentMessages) => {
			let updatedMessages = [...currentMessages, { text: message, sender: 'user' }];
			message = '';
			setTimeout(() => {
				let botResponseIndex = currentMessages.length / 2;
				let botResponse = botResponses[botResponseIndex];
				messages.update((currentMessages) => [
					...currentMessages,
					{ text: botResponse, sender: 'bot' }
				]);
			}, 1000);
			return updatedMessages;
		});
	}
</script>

Het komt er dan zo uit te zien

Ik ben niet helemaal tevreden met het feit dat de bot niet reageert op de gebruiker maar altijd hetzelfde script zal gebruiken ongeacht van wat de gebruiker zegt. Het is bijna onmogelijk om rekening te houden met de oneindige vragen die de gebruiker kan stellen, dus dit moet anders kunnen!

Tijdens een stand-up met mijn opdrachtgever gaf ik dit probleem aan. Ik heb een idee voorgelegd om de gebruiker een aantal knoppen te geven in plaats van een heel input veld. Zo kan je de chatbot antwoorden geven die ook echt iets te maken hebben met het huidige probleem van de gebruiker. Ik liet een aantal voorbeelden zien voor wat ik bedoel. Het ziet er ongeveer uit zoals de drie chatbots hieronder.

Zo zou het er uit komen te zien in de OBA huisstijl

De opdrachtgever was het met me eens! Hij gaf aan dat ik de chatbot voor nu achter wegen kon laten liggen en verder kon met een andere taak.

Breadcrumbs

Ik heb breadcrumbs gemaakt voor de OBA website om zo de gebruiker feedback te geven wat betreft de pagina waar ze vandaag kwamen. Zo kan de gebruiker ook weer terug navigeren naar de vorige pagina. Ik heb ervoor gezorgd dat de breadcrumbs alleen de drie meest recente pagina's weergeven. Alles ouder dan dit wordt aangegeven met '...'.

De variabelen pathParts, previousPages en hasMorePages worden geïnitialiseerd. pathParts en previousPages zijn arrays die de padnamen van de bezochte pagina's zullen bevatten. hasMorePages is een boolean die aangeeft of er meer dan drie pagina's zijn bezocht.

In de onMount haal ik de eerder bezochte pagina's op uit de sessieopslag met behulp van de Web Storage API en voegt de huidige pagina toe aan de lijst als deze nog niet in de lijst staat.

Als er drie of meer pagina's in previousPages staan, wordt hasMorePages op true gezet. Als er meer dan drie pagina's zijn, wordt de oudste pagina uit de lijst verwijderd. Zo weergeef ik alleen de recentste drie pagina's

<script>
	import { onMount } from 'svelte';
	import { page } from '$app/stores';

	let pathParts = [];
	let previousPages = [];
	let hasMorePages = false;

	onMount(() => {
		previousPages = JSON.parse(sessionStorage.getItem('previousPages')) || [];
		let currentPage = $page.url.pathname.slice(1);
		if (previousPages[previousPages.length - 1] !== currentPage) {
			previousPages.push(currentPage);
		}
		if (previousPages.length >= 3) {
			hasMorePages = true;
			if (previousPages.length > 3) {
				previousPages.shift();
			}
		} else {
			hasMorePages = false;
		}
		sessionStorage.setItem('previousPages', JSON.stringify(previousPages));
		pathParts = previousPages;
	});

	function isCurrentPage(index) {
		return index === pathParts.length - 1;
	}
</script>

Als isCurrentPage true is wordt de class 'current' toegevoegd aan de link. Dit zorgt ervoor dat de current page een zwarte kleur krijgt en dikgedrukt wordt. Als hasMorePages true is heeft de array meer dan 3 pages en zal de '...' weergeven worden.

De href van het a-element wordt ingesteld op het pad van de huidige pagina. Als de huidige pagina "home" is, wordt de href ingesteld op "/". Anders wordt de href ingesteld op "/" gevolgd door de naam van de huidige pagina (bijvoorbeeld op de leeslijst zal er /leeslijst staan).

<nav data-sveltekit-reload>
	<ul>
		{#each pathParts as part, index (index)}
			<li>
				{#if hasMorePages && index === 0}
					<span>...</span>
				{/if}
				<a
					class="breadcrumbs {isCurrentPage(index) ? 'current' : ''}"
					href={part === 'home' ? '/' : '/' + part}>{part}</a
				>
			</li>
		{/each}
	</ul>
</nav>

Ik heb het hierna in een component gezet zodat ik de breadcrumbs op elke pagina kan zetten. (Na een tip van Joost)

import { BreadCrumbs } from '$lib/index.js';

<nav>
	<BreadCrumbs />
</nav>

Zo zien de breadcrumbs er uiteindelijk uit

View transitions API

Ik ging donderdag 16 mei naar fronteers op de FDND locatie. Hier kwam een gastspreker (Jad Joubran) praten over de view transition API en wat je hier mee kunt doen. Dit heeft me geinspireerd om de API zelf ook te gebruiken.

Ik heb Krijn verteld dat ik de API wou gebruiken en hij stuurde me deze getting started with view transitions guide die ik heb doorgelezen. Ook stuurde hij me deze view transition update logs en vertelde hij me het verschil tussen single page en multi page view transitions.

Ik heb de MDN docs een beetje doorgelezen maar ik heb voornamelijk met behulp van de Svelte docs view transitions werkend gekregen in svelte. Ik heb dit gedaan door de volgende code in mijn +layout.svelte te zetten.

Deze code zorgt ervoor dat als view transitions niet gesupport worden door de browser dat de functie niet uitgevoerd wordt. Als view transitions wel gesupport worden door de browser zal het bij elke navigatie de view transition starten.

<script>
    import { onNavigate } from '$app/navigation';

    onNavigate((navigation) => {
        if (!document.startViewTransition) return;

        return new Promise((resolve) => {
            document.startViewTransition(async () => {
	        resolve();
	        await navigation.complete;
	    });
	});
    });
</script>

Ik heb de standaard view transition animatie vervangen door mijn eigen omdat ik het wat opvallender wou maken dan een standaard fade. De transition nu is een fade out van de oude pagina die naar links slide terwijl de nieuwe pagina in fade en vanaf rechts in slide.

viewtrans

        @keyframes fade-in {
		from {
			opacity: 0;
		}
	}

	@keyframes fade-out {
		to {
			opacity: 0;
		}
	}

	@keyframes slide-from-right {
		from {
			transform: translateX(30px);
		}
	}

	@keyframes slide-to-left {
		to {
			transform: translateX(-30px);
		}
	}

	:root::view-transition-old(root) {
		animation:
			180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
			600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
	}

	:root::view-transition-new(root) {
		animation:
			420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
			600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
	}

Progressive enhancements

Prefers reduced motion

Ik kreeg van Ivar de tip dat ik mijn view transitions toegankelijker kon maken door animaties uit te schakelen in mijn css indien de gebruiker animaties heeft uitgeschakeld op zijn of haar apparaat. Ik had hier voorheen al gebruik van gemaakt en vond dit erg handig en heb het hierom ook hier toegevoegd.

@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

Data-sveltekit-reload

Ik had het probleem dat de css die op alle andere pagina's niet meegenomen werd naar de error pagina. Ik heb om dit te fixen gebruik gemaakt van buttons die on:click navigaten naar de pagina. Zo werd de css gerefreshed en werkte het wel weer. Dit is natuurlijk niet hoe het moet en deze links zullen ook niet werken als Javascript niet werkt.

<nav>
    <button on:click={() => navigateTo('/')}>Home</button>
    <button on:click={() => navigateTo('/leeslijst')}>Leeslijst</button>
    <button on:click={() => navigateTo('/uitleningen')}>Uitleningen</button>
    <button on:click={() => navigateTo('/reserveringen')}>Reserveringen</button>
    <button on:click={() => navigateTo('/betalingen')}>Betalingen</button>
</nav>

Het kunnen door- en deeplinken naar url's is een van de belangrijkste principes van het web en ik heb dit daarom ook veranderd. Ik heb, met behulp van Justus, gebruik gemaakt van de svelte link options om ervoor te zorgen dat ik hetzelfde kon bereiken met een a href element. Ik kwam zo achter data-sveltekit-reload. Dit zorgt ervoor dat Sveltekit niet de afhandeling van de link doet, maar de browser zelf. Dit zorgt voor een full refresh bij het klikken van de link. Dit bereikt hetzelfde als de Javascript buttons van voorheen, maar dan semantisch correct!

<nav data-sveltekit-reload>
    <a href="/">Home</a>
    <a href="/leeslijst">Leeslijst</a>
    <a href="/uitleningen">Uitleningen</a>
    <a href="/reserveringen">Reserveringen</a>
    <a href="/betalingen">Betalingen</a>
</nav>

Lazy loading

Op de boekenlijst pagina worden 53 boeken ingeladen. Al deze boeken hebben een eigen image en het inladen van al deze afbeeldingen kan ervoor zorgen dat de website langzaam wordt voor de gebruiker. Ik heb daarom lazy loading toegevoegd aan de afbeeldingen om de performance te verbeteren.

In de huidige code haal ik de afbeeldingen op doormiddel van een inline css background-image met een linear gradient en de cover image van het boek.

<a href={bookDetailLink}>
    <article
        class="background-image carrousel-picture"
        style="background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.5)), url('{bookUrl}');"
    >
	<section class="info">
	    <p class="p__title">{bookTitle}</p>
	    <p>{bookAuthor}</p>
	</section>
    </article>
</a>

Ik heb dit verbeterd door de background image te verplaatsen naar de css en loading="lazy" toe te voegen aan de cover images van de boeken. Lazy loading zorgt ervoor dat de afbeeldingen pas ingeladen worden als ze ook echt nodig zijn en niet alle 53 meteen als de pagina inlaad. Hiernaast geef ik alle afbeeldingen ook een alt tekst.

<a href={bookDetailLink}>
    <div class="background-image carrousel-picture">
        <img src={bookUrl} alt={bookTitle} loading="lazy" class="lazy-load-image" />
        <article class="overlay">
            <section class="info">
                <p class="p__title">{bookTitle}</p>
                <p>{bookAuthor}</p>
            </section>
        </article>
    </div>
</a>

<style>
    .background-image {
        position: relative;
    }

    .lazy-load-image {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    .overlay {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.5));
    }
</style>

Ik heb een network test gedaan en de laadtijden zijn van 9s naar 6s gegaan. Dat is 1/3e sneller!

Image fallback

Ik heb met behulp van het <picture> element ervoor gezorgd dat de cover images van de boeken een fallback hebben (.png) voor het geval dat de browser van de gebruiker het standaard formaat van de afbeeldingen niet ondersteund (.jpeg).

In de nieuwe code wordt in het geval dat .jpeg niet ondersteund wordt, de extensie jpeg vervangen door png. Vrijwel alle browsers supporten png dus zo kan de gebruiker toch nog de website gebruiken.

Standaard zal jpeg gebruikt worden omdat dat aangegeven is door de type="image/jpeg" in het <source> element. Browsers gebruiken het MIME type en niet de file extensie blijkbaar om te bepalen hoe ze de url van de image moeten processen. Het aangegeven MIME type is jpeg dus. (Source: MDN web docs).

Het <picture> element zal de eerste source gebruiken die toepasselijk is en dat is in de meeste gevallen dus de jpeg. Maar in het unieke geval dat jpeg niet mogelijk is zal het dus hierdoor png gaan gebruiken. (Source: W3 schools)

<a href={bookDetailLink}>
    <div class="background-image carrousel-picture">
        <picture>
            <source srcset={bookUrl} type="image/jpeg" />
	    <img src={bookUrl.replace('.jpeg', '.png')} alt={bookTitle} loading="lazy" class="lazy-load-image" />
	</picture>
	<article class="overlay">
	    <section class="info">
	        <p class="p__title">{bookTitle}</p>
		<p>{bookAuthor}</p>
	    </section>
	</article>
    </div>
</a>

Lighthouse

Ik heb een lighthouse test gedaan en kwam zo achter de stats van mijn error pagina op de OBA website:

  • Performance 81
  • Accessibility 100
  • Best practices 96
  • SEO 73

Het grootste probleem met mijn performance is de h1 "Oeps, deze pagina bestaat niet". Ik ben erachter gekomen dat dit komt doordat ik hem perongeluk buiten de juiste section heb gezet. Ik heb hem verplaatst en hiermee de performance verbeterd.

Ik heb mijn SEO verbeterd door het document een meta description te geven in mijn app.html

<meta charset="utf-8" name="description" content="OBA website." />

Na deze verbeteringen zijn dit de nieuwe stats:

  • Performance 100
  • Accessibility 100
  • Best practices 96
  • SEO 82

Validatie

Ik heb mijn HTML gevalideerd met de hulp van de W3C Markup Validation Service.

Ook heb ik mijn CSS gevalideerd met de W3C CSS Validation Service.

Ik heb de feedback die ik hieruit kreeg verwerkt in mijn code.

Responsive

De breadcrumbs zien er op desktop uit zoals de afbeelding hieronder. De lange paden kunnen horizontaal weergegeven worden omdat er op desktop genoeg ruimte is.

Op mobiele apparaten heb ik de breadcrumbs verticaal opgedeeld zodat het beter past op de kleinere schermen.

Ik heb dit bereikt met behulp van media queries zoals het voorbeeld hieronder.

@media (max-width: 600px) {
    nav {
        margin-left: 1rem;
    }

    .breadcrumbs {
	font-size: 1rem;
    }

    li:not(:first-child)::before {
	font-size: 1rem;
    }
}

Atomic design

Voor dit project maken we gebruik van atomic design. Svelte heeft een goede manier ingebouwd om dit te doen.

Any other files inside a route directory are ignored by SvelteKit. This means you can colocate components and utility modules with the routes that need them. If components and modules are needed by multiple routes, it's a good idea to put them in $lib. (Source)

Ik heb alle componenten ingedeeld in atoms, molecules en organisms. Hier heb ik mapjes voor gemaakt in de $lib.

In mijn $lib/index.js heb ik vervolgens alles geexporteerd.

Er zijn comments achtergelaten in mijn code om het zo duidelijk mogelijk te maken voor anderen die deze code lezen.

// Hier export je alle atoms ----------------------------------------------------------------------------------------------------------------
// Hier export je alle atoms ----------------------------------------------------------------------------------------------------------------
// Hier export je alle atoms ----------------------------------------------------------------------------------------------------------------
export { default as LinkButton } from './atoms/linkButton.svelte';
export { default as Button } from './atoms/button.svelte';
export { default as Toggle } from './atoms/toggle.svelte';

// Hier export je alle molecules ------------------------------------------------------------------------------------------------------------
// Hier export je alle molecules ------------------------------------------------------------------------------------------------------------
// Hier export je alle molecules ------------------------------------------------------------------------------------------------------------
export { default as Header } from './molecules/header.svelte';
export { default as Card } from './molecules/card.svelte';
export { default as ReservationCard } from './molecules/reservationCard.svelte';
export { default as Table } from './molecules/table.svelte';
export { default as Nav } from './molecules/nav.svelte';
export { default as Footer } from './molecules/footer.svelte';
export { default as HeroLogo } from './molecules/heroLogo.svelte';
export { default as BreadCrumbs } from './molecules/breadCrumbs.svelte';
export { default as Chatbot } from './molecules/chatbot.svelte';

// Hier export je alle organisms ------------------------------------------------------------------------------------------------------------
// Hier export je alle organisms ------------------------------------------------------------------------------------------------------------
// Hier export je alle organisms ------------------------------------------------------------------------------------------------------------
export { default as SmallCarrousel } from './organisms/smallCarrousel.svelte';

Als ik deze componenten dan op wil halen (zoals bijvoorbeeld in de home page hieronder) dan importeer ik ze op die pagina op deze manier.

<script>
    import { Card, Header, BreadCrumbs, Chatbot } from '$lib/index.js';
</script>

Na het importeren kun je ze zo oproepen

<Card/>
<Header/>
<BreadCrumbs/>
<Chatbot/>

Knelpunten

  • Nieuwe opdracht: Ik heb afgelopen twee jaar voor de FDND agency projecten altijd alleen aan Visual thinking gewerkt. Ik werkte altijd door aan dezelfde website, aan mijn eigen code. Ik moest voor dit project voor het eerst voor de OBA website verder werken aan iemand anders zijn code. Dit was erg uitdagend voor me.
  • Code conventies: Onze code conventies zijn niet duidelijk en uitgebreid genoeg. Deze gaan we moeten verbeteren.
  • Projectboard: Na een klassikaal gesprek met Joost over pull request zijn we er achter gekomen dat de taken in ons projectboard te groot waren voor het dagelijks afmaken van taken.
  • CSS: Ik had het probleem dat de css die op alle andere pagina's niet meegenomen werd naar de error pagina. Ik heb hier heel lang last van gehad.
  • CMD: Ik kom net van een heel semester niet coderen bij CMD. Om nu opeens elke dag te moeten coderen is een grote verandering voor me.

Mogelijke oplossingen

  • Nieuwe opdracht: Ik had tijdens een stand-up aangegeven aan Joost dat ik het jammer vond dat ik verder moest werken aan iemand anders zijn code en dat ik liever van scratch wou beginnen aan een project. Een mogelijke oplossing was dat ik in plaats van de OBA kon werken aan de FDND agency website. Ik besloot uiteindelijk gewoon verder te gaan met de OBA website.
  • Code conventies: Na een uitleg van Joost over het belang van code conventies hebben we besloten opnieuw te kijken naar onze code conventies. We hebben deze hierdoor kunnen veranderen en verbeteren.
  • Projectboard: We hebben dit verbeterd door concrete afspraken te maken over onder anderen wat betreft de tijd die poker punten innemen en een heroverweging van onze MoScoW taakverdeling. Zo kunnen we beter op een lijn zijn bij het maken van nieuwe, en het sluiten van bestaande issues. Ook maken we nu na elke opdrachtgever meeting samen de taken aan.
  • CSS: Ik heb uiteindelijk hulp gevraagd aan Joost die me doorverwees naar Justus. Met zijn hulp kwam ik erachter hoe ik dit kon fixen.
  • CMD: Ik heb geluk gehad dat ik een teamlid had die erg begrijpend was en hij gaf me de vrijheid om de eerste paar weken weer te wennen aan code.
⚠️ **GitHub.com Fallback** ⚠️