This degree project in the subject of web development is about developing a "developer and admin-friendly", modular "Dashboard" with a focus on reusability, readability and scalability for effective future maintenance and further development.
I have limited the scope of this degree project to be able to further explore and practice improving my structure and modularization skills and experience. I believe this is almost as important and interesting as writing efficient functions and is the foundation of all projects. The work with this dashboard is connected to the company "Bidstacker" where I did my internship during the training and is an extension of their web app.
I aim to use modern web development techniques and principles within the realm of the React library, to create flexible and reusable components. As part of writing clean and modular code the concept of S.O.L.I.D. is also studied. The tools used are the React framework Next.js, Tailwind CSS, Node.js and MongoDB.
The goal is to find a natural way to structure and modularize in React.js or in web project structures with reusable functionality.
Projektet kommer av att jag under min praktik såg möjligheten att utveckla en så kallad “Dashboard” som en del av en startsida för en inloggad användare. Idéen med denna funktion är att ge den inloggade användaren en informativ överblicksbild av appens tjänster och administrativa möjligheter.
Utmaningen för mig som utvecklare är att bygga denna funktion:
Läsbar
Skalbar
Och med återanvändbart modulärt tänk
För att skapa förutsättningar för framtida ändringar, utbyggnad och underhåll.
En av de mest intressanta delarna med webbutveckling och de webbprojekt som jag hittills arbetat i och sett, är skillnaden i hur effektivt och modulärt byggda de är. Har sedan man började i skolan verkligen börjat se komplexiteten i webbproduktioner och även hur viktigt det är att modularisera detta, för att på bästa sätt “skiva elefanten” och separera segment som funktioner och moduler/komponenter i mindre beståndsdelar.
Fördelarna är många och uppenbara:
Enklare för utvecklare att hitta och arbeta i strukturen.
Slippa skriva upprepad kod och många gånger uppfinna hjulet på nytt på flera ställen i projektet.
Mindre kodbas och mer konsekvent kod och produktion.
Lättare att testa och förstå mindre kundsegment.
Flexibel återanvändning av komponenter med mera.
Jag har genom utbildningen lite burit med mig att React är något att försöka bli bekväm med, men även att det är det biblioteket “som gäller”. Även om det under senare tid har dykt upp nya liknande bibliotek i stor utsträckning. För att citera en barndomskamrat som jobbar i branchen.
Om du kan React, så har du jobb sen.
Samtidigt är det ett komplext bibliotek med fri struktur på gott och ont, och det finns många sätt att uppnå samma mål med sin kod.
Jag har upplevt att man i komplexiteten i ett så flexibelt bibliotek måste hitta mönster att följa i sitt användande. Gångbara mönster att följa, bli trygg med och återanvända för att inte uppfinna hjulet på nytt varje gång man sätter sig med React.
I konceptet och fördelarna med att hela tiden försöka jobba med modularisering ingår även att försöka skriva så kallad “ren kod”. Regler för detta kan sammanfattas med konceptet kallat S.O.L.I.D.
Har tagit tillfället i akt att sätta målet för projektet till att försöka öka min personliga utveckling i att träna på effektiv modularitet och struktur. Min förmåga att återanvända kod och funktioner, få bättre känsla för överblick, struktur och möjligheter att jobba smartare/snabbare och förhoppningsvis med större trygghet.
Ipsum et cupidatat mollit exercitation enim duis sunt irure aliqua reprehenderit mollit. Pariatur Lorem pariatur laboris do culpa do elit irure. Eiusmod amet nulla voluptate velit culpa et aliqua ad reprehenderit sit ut.
Dashboard:en bör kunna utformas olika för varje användarroll. Jag har förutom detta begränsat mitt arbete till att främst undersöka och ta fram en lämplig fil- och datastruktur för ändamålet. De enskilda widgets som skulle vara intressanta att göra har lite fått stå åt sidan och har inte hunnit göras klara pga tidsbrist eller att projektet i stort inte är utbyggt nog för att registrera viss nödvändig användardata.
En önskan från företaget var om användaren skulle kunna lägga till och ta bort Widgets efter eget tycke och smak och spara denna layout för användaren. Detta skulle vara grundfunktionalitet som så det fick viss prioritering.
Projektet är skrivet i Next.js som är ett React-ramverk. Projektet innehåller ett antal “widgets” (komponenter, moduler) för olika info för respektive användarroll. Någon widget kan vara mer unik för respektive användarroll. Andra kan påminna om varandra men inneha olika data. En användare kan ha 3 olika roller:
En användare registreras med en av följande användarroller
Inköpare
Återförsäljare
Bud
Dashboard:en bör kunna utformas olika för varje användarroll. Jag har förutom detta begränsat mitt arbete till att främst undersöka och ta fram en lämplig fil- och datastruktur för ändamålet. De enskilda widgets som skulle vara intressanta att göra har lite fått stå åt sidan och har inte hunnit göras klara pga tidsbrist eller att projektet i stort inte är utbyggt nog för att registrera viss nödvändig användardata.
En önskan från företaget var om användaren skulle kunna lägga till och ta bort Widgets efter eget tycke och smak och spara denna layout för användaren. Detta skulle vara grundfunktionalitet som så det fick viss prioritering.
Arbetet har gjorts efter en grov planering. Det är alltid svårt att veta hur lång tid vissa saker kan ta och hur det kan komma att påverka planeringen i stort.
Planering
Arbetet har utförts under 4 stycken sprintar där arbetet är uppdelat enligt nedan.
Sprint 1
Research och val kring arbetssätt/pattern i React (Component composition, Higher-Order Components (HOCs), Render Props eller Hooks).
Definiera filstrukturer och dashboard-komponenter.
Definiera några exempel på respektive användarrolls widgets.
Definiera grafiskt återanvändbara komponenter/moduler som är oberoende av widgets innehåll.
Sprint 2
Skapa återanvändbar frontend-struktur och komponenter efter definition.
Fundera kring huvudsakliga funktioner som borde kunna delas. Granulärt återanvändbara funktioner, funktioner i funktioner.
Tillgången till marknads- eller användarundersökning var begränsad och har jobbat med företagets grundare och dennes kännedom om byggbranschen och dess branschfolk. Eftersom inget marknadsmaterial finns så görs väldigt enkla UserStories (finns som Bilaga). Jag tar fram en grundskiss som godkänns.
Jag diskuterar fram några uppslag till Widgets med företagets ena grundare. Dessa är dock mer riktlinjer då fokus har främst lagts på att bygga en modulär och återanvändbar mall och alla Widgets fungerande har fått lite sekundär prio.
Widget
Inköp
ÅF
Bud
Info
Status Numbers
Yes
Yes
Yes
Overall status figures
Monthly Rapport
Yes
Yes
Yes
Graph of customer benefit
Project
Yes
Project page
Best Seller
Yes
Seller top list
Current Deliveries
Yes
Shows current deliveries
More?
TBD
Tabell 1. Möjliga uppslag för Widgets
Jag tänker att en enkel väg att gå är att varje widget som mest ska bestå av 5 komponenter:
<WidgetItem /> Ett “kort” för alla widgets.
<WidgetTitle /> En beskrivande titel
<WidgetButton /> En eventuell knapp som kan se olika ut
<WidgetDropdown /> En eventuell dropdown om knappen är av typen
<WidgetContent /> En sektion för att ladda en valfri Widget
Jag tänkte mig nedan övergripande dataflöde. En fil med metadata-objekt per användarroll där metadatan skapar en datadriven skalbarhet. Finns metadata till en widget, finns en widget. Tänker mig behöva en övergripande kontext som kan tillgodose med funktioner, till exempel knappens funktion om widgeten ska ha en knapp.
Denna metadata returneras till huvudkomponenten där de mappas sedan ut till olika widgets beroende på användarroll.
Senare skapas en ny Array (selectedWidget) med de widgets som användaren lägger till och tar bort med de widgets som finns i useWidgetInfo som grund.
De widget layout användaren vill ha på sin dashboard sparas i databasen samt widgets med CRUD-behov, tex Projekt-widgeten.
Filstruktur för komponenter
Dashboard.jsx = Huvudkomponent, ligger i “pages” i Next och inte bland dessa andra komponenter.
useWidgetInfo = Den metadata och värden för användarroller och deras respektive Widgets.
WidgetComponents = Komponenter som fungerar som återanvändbara platshållare för den inkommande datan.
Widgets är till största del unikt innehåll med specifik metadata och funktionalitet per användarroll kan fortfarande skickas in till dessa, vilket gör att man kan återanvända även dessa med olika data beroende på specifika fall. Tex en widget som ser liknande ut för flera användarroller men som använder annan data. Detta har jag dock inte nått hela vägen med.
Filen var först en ren datafil med objekt för varje widgets. Men då jag vill använda Kontext för att ta emot funktioner tex för widgets knappens funktion görs den om till en hook.
Olika generiska, återanvändbara stilar på knappar och “Cards” ligger med som konstanter.
Objekten byts ut mot ett interface(enligt “Interface segregation principle”, läs “i” i SOLID) Jag definierar vartefter de variabler jag tänker mig behöva för olika sorters metadata i funktionen makeWidget, som efter testande vuxit till 11 i antalet.
Kodexempel 2: Visar i kompakthet det interface som bestämmer innehållet i en Widget.
Därefter listar jag sen metadata för widgets i array:er för varje användarroll. Och kan skapa arrayer uppbyggda efter detta tex för rollen “inköpare” och en widget:
Fastnade tidigt för ett koncept kallat “Component composition” efter diskussion med min handledare. Detta möjliggör skapandet av större komponenter genom att kombinera mindre, återanvändbara komponenter. Komponenter kan skickas som props till andra komponenter, som sedan kan återge dem på ett flexibelt sätt, detta blir flexibelt eftersom den kan ha olika header, footer och children beroende på vilka props som skickas till den. Vanligt vid layout-”komposition” vilket kändes passande.
Där huvudkomponenten propsar komponenter till en underliggande struktur.
Jag försökte strukturera enligt mönstret “Components composing” vilket gav ett fungerande resultat. Jag skickade många värden som props från huvudkomponenten vilket gav dålig läsbarhet och komponenterna blir hårt definierade av alla props för att kännas fullt flexibelt.
Detta mönster gjorde att jag fick bort propsen idén att du har en huvudkomponent (WidgetItem i det här fallet), som delar någon form av gemensam logik eller värde
(i mitt fall den mappade “widget”-variabeln) med sina barn (via Context API) med dess barnkomponenter (WidgetItem.Title, WidgetItem.Button, WidgetItem.Dropdown och WidgetItem.Content). Dessa barnkomponenter kan återanvändas och ordnas om efter behov.
Denna version var jag ganska nöjd med men jag hittade dock ett sätt senare att kombinera Compound components med Component composition vilket jag kände gav det mest flexibla resultatet. Detta redovisar jag under resultat delen.
Jag har använt en kombination av kodmönstren "Composing Components" och "Compound Components" för min dashboard.
Denna kombination kan vara den mest lämpliga för att bygga en flexibel och återanvändbar dashboard. "Composing Components" mönstret används när man bygger upp en komplex komponent genom att kombinera mindre delkomponenter.
Å andra sidan används "Compound Components" mönstret för att dela gemensam funktionalitet och data mellan flera komponenter.
Detta skapar en bra läsbarhet utan propsande då jag endast propsar mitt mappade objekt “widget”. Komponenten lätt återanvändas i olika situationer, antingen använda den som den är med de inbyggda komponenterna, eller du kan passa in egna komponenter via props för att ändra dess beteende.
Funktioner har flyttats ut till utils-specifika filer för varje widget. Beroende på om de kan göras mer allmänt återanvändbara även för andra ställen har jag flyttat dem till ”utils”.
Jag har även velat undersöka återanvändbarheten i enskilda utils-funktioner som kan skrivas små och istället köras “i varandra”. Detta har jag bara i viss utsträckning hunnit med att göra.
En typ av funktion som nästan uppträtt som ett mönster i detta projekt har varit att uppdatera eller mutera state eller existerande metadata med ny data. Här nedan är en funktion som exempel på detta, som handlar om att uppdatera storleken på en widget. Objektet “widget” innehåller en värde(widget.size) i form av en string som byts ut.
Vidare arbete ligger i att skapa widgets där utseende ser liknande ut för flera användarroller men som använder olika funktioner för att hämta specifika data för en specifik användarroll. Detta har jag dock inte hunnit gå hela vägen med.
Diskussion
Återanvändning och kodmönster utgör inte alltid den mest optimala lösningen; i vissa fall krävs en djupgående förståelse av applikationens affärslogik för att kunna skapa meningsfull återanvändbarhet. Det innebär att man behöver förstå de regler och rutiner som styr hur ett företag eller en organisation opererar. Trots detta, kan utvecklingen av återanvändbara komponenter som kan tillämpas på olika delar av applikationen minska upprepning av kod. Detta gör koden lättare att underhålla och minskar risken för fel.
Jag kan inte säga att jag är helt i hamn vad gäller modulärt tänk generellt, känslan är att vanan kommer mer med erfarenhet och systematisk metodik. Det finns fördelar och nackdelar med allt. Det finns lägen där man inte tjänar på att lägga så mycket tid på återanvändbarhet eller fastna i att överarbete kring detta. Erfarenheten här med att skapa en datadriven struktur har berikat mycket, framför allt i ens övergripande perspektiv av en större funktion.
Några punkter att ta med sig:
Ha en plan för ditt arbete och struktur av inkluderande ett framtidsperspektiv om appen växer
Titta på de beprövade designmönster som finns
Modulariserings-vana kommer med erfarenhet
Lättare att hitta i kodbasen med genomtänkt struktur för alla inblandade
Dokumentation viktig för andra att förstå uppbyggnad och funktion för att kunna återanvända den.
Försök skriva “rena funktioner”, det vill säga funktioner som inte lägger till värden förutom de värde de tar in, som då kan fungera modulärt
Att ibland se över och refaktorisera kod kan bidra till att hålla den i gott skick samt kanske även hitta ställen där återanvändning eller förbättrad struktur kan göras
DRY (Don’t Repeat Yourself) och KISS (Keep It Simple, Stupid)
Solid
CoderOne(YouTube). 2022. “This is the Only Right Way to Write React clean-code - SOLID”
This is the Only Right Way to Write React clean-code - SOLID
Affärslogik
Steve McConnell. 2004. “Code Complete: A Practical Handbook of Software Construction, Second Edition” (ISBN-13: 978-0735619678)
Bilagor
Här följer bifogade bilagor.
Github länk
Github-kontot tillhör Bidstacker och delas endast internt.
Rules for Widgets
## Widget11 variables per widget as of now, all are optional except for id and title(optional should be marked as undefined)- id: number- title: string- type: string- style: string- width: string- buttonStyle: string- buttonType: string- buttonDesc: string- dropdownOptions: string[]- buttonAction: function- content: JSX.Element## Widget Types- requestor- supplier- carrier## Widget StylesAre the styles that the widget loads with, can not be changed by the user as of now- CARDS_FORM_STYLE[0] = 'bg-white px-4 py-6 rounded-xl h-full'- CARDS_FORM_STYLE[1] = 'bg-white px-4 py-5 rounded-xl h-full'- CARDS_FORM_STYLE[2] = 'bg-white px-4 py-3 rounded-xl h-full'- CARDS_FORM_STYLE[3] = 'bg-gradient-to-b from-white to-blue-600 px-4 py-3 rounded-xl h-full'## Widget WidthsAre the width that an widget loads with, can be changed by the user- w-full- w-1/2## Widget Button StylesAre the styles that the widget button loads with, can not be changed by the user as of now## Widget Button TypesAre the types that define what the button does, as of now only the following are available- dropdown- addProjectItem## Widget Button DescriptionsAre the descriptions that the primary widget button loads with- 'Nytt projekt' for example## Widget Dropdown OptionsAre the options that the dropdown widget button loads with- ['Kvartalsrapport', 'Månadsrapport', 'Översikt'] for example## Widget Button ActionsAre the actions that the widget button does when clicked, as of now only the following are available- dropdownHandler- addProjectItemHandler## Widget ContentIs the content that the widget loads with. Should be a JSX.Element from the Widgets folder with needed props.Data is loaded from the backend and passed as props to the content component.
User Stories
Inköpare:
User should be able to see the number of created requests, received offers, waiting for approval and received orders
User could overview the quarterly report, monthly report and overview of of various information
User should be able to add and, delete and handle projects
User should be able to see the weather
User should be able to see the latest contacts
Supplier:
User should be able to see the number of active offers, sent offers, received orders and delivered orders
User could overview the quarterly report, monthly report and overview of of various information
User should be able to see the weather
User should be able to see the latest contacts
Bud:
User should be able to see the number of active offers, sent offers, received orders and delivered orders
User could overview the quarterly report, monthly report and overview of of various information