Blog Logo

Thesis

Abstract


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.

Table of Contents

  1. Inledning

    1.1 Bakgrund

    1.2 Syfte

    1.3 Avgränsning


Inledning

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.


Super wide

Bild 1. Skisser och kommunicerande.

[Top]


Bakgrund

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.

[Top]

Syfte

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.

[Top]

Avgränsning

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.

Super wide

Bild 2. Skisser av anpassad layout.

[Top]

Table of Contents

  1. Metod och genomförande

    2.1 Planering

    2.2 Kund och användarbehov

    2.3 Dataflöde och struktur

    2.4 Component composition study


Metod

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.

Sprint 3

  • Anpassningsbar layout?
  • Spara användarens layout av Widgets i DB.

Sprint 4

  • Skriva rapport och samla in data.
  • Fundera på förbättringspotential.

[Top]

Kund och användarbehov

Sketch
Bild 3. En färdig idéskiss

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.

WidgetInköpÅFBudInfo
Status NumbersYesYesYesOverall status figures
Monthly RapportYesYesYesGraph of customer benefit
ProjectYesProject page
Best SellerYesSeller top list
Current DeliveriesYesShows current deliveries
More?TBD
Tabell 1. Möjliga uppslag för Widgets

Sketch
Bild 4. Komponentuppbyggnad av Widget

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


[Top]

Dataflöde och struktur

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.

Sketch
Bild 5. Flödesschema för datan, kan ses som ett grovt ER-diagram

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.
Sketch
Bild 4. Visar upplägg av komponenternas filstruktur.
  • 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.

[Top]


useWidgetInfo (custom hook)

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.

const PRIMARY_BUTTON_STYLE = 'inline-block text-gray-800 dark:text-gray-400 hover:bg-gray-100 … rounded-lg text-sm
p-1.5';

const SECONDARY_BUTTON_STYLE = 'flex border border-gray-300 h-12 bg-gray-100 font-semibold … hover:transition
ease-in-out';

const CARDS_FORM_STYLE = [ 'bg-white px-4 py-6 rounded-xl h-full', 'bg-white px-4 py-5 rounded-xl h-full',
'bg-gradient-to-b from-white to-teal-200 px-4 py-3 rounded-xl h-full', ];

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.

const makeWidget = (
   id,
   title,
   role,
   cardForm,
   size,
   buttonStyle,
   buttonType,
   buttonDesc,
   dropdownOptions,
   action,
   content
) => {
   return {
      id,
      title,
      role,
      card: cardForm,
      size,
      buttonStyle,
      buttonType,
      buttonDesc,
      dropdownOptions,
      action,
      content,
   };
};
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:

const requestor = [
   makeWidget(
      1,
      "Kvartalsrapport",
      "requestor",
      CARDS_FORM_STYLE[1],
      "w-1/2",
      PRIMARY_BUTTON_STYLE,
      "dropdown",
      undefined,
      ["Kvartalsrapport", "Månadsrapport", "Översikt"],
      () => dropdownHandler(2),
      <Monthly
         stapleValues={[
            { title: "Rabatt", color: "bg-teal-200" },
            { title: "Månadsbudget", color: "bg-teal-500" },
         ]}
      />
   ),
   makeWidget(
      2
      /* … */
   ),
]; /* … */

return { requestor, reseller, carrier };
Kodexempel 3: Visar ett exempel på en statisk Widget i filen useWidgetInfo.

[Top]



Studie av komponent komposition


Component composition

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.

<Layout
header = {<Header />}
footer = {<Footer />}>
<MainContent />
</Layout>

Vilket ger en väldigt flexibel lösning där man i huvudkomponenten kan “komponera” sitt upplägg av komponenter från den övre strukturen.

const Layout = ({ header, footer, children }) => { return (
<div>{header} {children} {footer}</div>
);};

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.


Huvudkomponent

selectedWidgets.map((widget, index) => {
                return (
                  <WidgetItem
                    key={widget.id}
                    widget={widget}
                    size={widget.size}
                    isPrelAdmin={isPrelAdmin}
                    title={<WidgetTitle title={widget.title} />}
                    button={
                      <WidgetButton
                        index={index}
                        button={widget.button}
                        buttonDesc={widget.buttonDesc}
                        buttonStyle={widget.buttonStyle}
                        buttonFunc={widget.buttonFunc}
                        buttonType={widget.buttonType}
                        showDropdown={widget.showDropdown}
                      />
                    }
                    dropdown={
                      <WidgetDropdown
                        widgetId={widget.id}
                        selectedWidgets={selectedWidgets}
                        setSelectedWidgets={setSelectedWidgets}
                        selectedMonthlyWidget={selectedMonthlyW...
                        setSelectedMonthlyWidget={setSelectedM...
                        dropdownOptions={widget.dropdownOptions}
                        showDropdown={widget.showDropdown}
                      />
                    }
                    content={
                      <WidgetContent
                        loading={loading}

Underliggande kopmponent struktur

const WidgetItem = ({ widget, title, button, content, dropdown }) => { return (
<div className="{`p-2" transition-all duration-500 w-full mobile:${widget.size}`}>
   <div className="{`${widget.card}`}">
      <section
         className="flex justify-between
      items-center"
      >
         {title} {button}
      </section>
      <section>{dropdown}</section>
      <section>{content}</section>
   </div>
</div>
); }; WidgetItem.Title = WidgetTitle; export default WidgetItem;

Compound components

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.


Huvudkomponent

selectedWidgets.map((widget, index) => { return (
<WidgetItem key="{widget.id}" index="{index}" widget="{widget}" loading="{loading}">
   <WidgetItem.Title />
   <WidgetItem.Button />
   <WidgetItem.Dropdown />
   <WidgetItem.Content />
</WidgetItem>
);

Underliggande kopmponent struktur

const WidgetItem = ({ widget, loading, index }) => {

return (
<WidgetInfoContext.Provider value={{ widget }}>
<section
className={`p-2 transition-all duration-500 ...
   <article className={`card ${widget.card}`}>
      <header className="w-full flex justify-between ...
         <WidgetItem.Title />
         <WidgetItem.Button index={index} />
      </header>
      <div className="w-full p-2">
         <WidgetItem.Dropdown />
      </div>
      <div className="w-full p-2">
         <WidgetItem.Content loading={loading} />
      </div>
     </article>
</section>
</WidgetInfoContext.Provider>
);
};

WidgetItem.Title = WidgetTitle;
WidgetItem.Button = WidgetButton;
WidgetItem.Dropdown = WidgetDropdown;
WidgetItem.Content = WidgetContent;

export default WidgetItem;

Resultat


Dashboard
Overview of the dashboard

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.


Huvudkomponent

selectedWidgets.map((widget, index) => {
   return (
      <WidgetItem
         key={widget.id}
         index={index}
         widget={widget}
         loading={loading}
         header={
            <WidgetItem.Header>
               <WidgetItem.Title />
               <WidgetItem.Button />
            </WidgetItem.Header>
         }
         dropdown={<WidgetItem.Dropdown />}
         content={<WidgetItem.Content />}
      />
   );
})}

Underliggande kopmponent struktur

const WidgetItem = ({ widget, index, loading, header, dropdown, content }) => { return (
<WidgetInfoContext.Provider
   value="{{widget}}"
>
   <section className="{`p-2" transition-all duration-500 w-full mobile:${widget.size}`}>
      <article className="{`card" ${widget.card}`}>
         <header
            className="w-full flex justify-   
         between items-center p-2"
         >
            {header}
         </header>
         {dropdown}
         <div className="w-full p-2">{content}</div>
      </article>
   </section>
</WidgetInfoContext.Provider>
); }; WidgetItem.Title = WidgetTitle; WidgetItem.Button = WidgetButton; WidgetItem.Dropdown = WidgetDropdown;
WidgetItem.Content = WidgetContent; WidgetItem.Header = WidgetHeader; export default WidgetItem;

Widget-komponenter

Widget-komponenterna fungerar som platshållare för den metadata som skickas in från useWidgetInfo hooken. Så här ser komponenten WidgetTitle ut tex:

import { useWidgetInfoContext } from './widget-item-context'; const WidgetTitle = () => { const { widget } =
useWidgetInfoContext(); return (
<h5 className="text-xl font-semibold leading-none text-gray-800">{widget.title}</h5>
);}; export default WidgetTitle;

Funktioner

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”.

Dashboard
Utils funktioners struktur

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.

export const findItemsByIndex = (items, index) => items.find((item, i) => i === index);

export const findItemsById = (items, id) => items.find((item, i) => i === id);

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.

export function handleWidgetSize(widget, setSelectedWidgets) {
   setSelectedWidgets((prev) => {
      const newSelectedWidgets = [...prev];
      const index = newSelectedWidgets.findIndex((sel) => sel.id === widget.id);
      newSelectedWidgets[index].size = newSelectedWidgets[index].size === "w-full" ? "w-1/2" : "w-full";
      return newSelectedWidgets;
   });
}

Fortsatt arbete

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)

Källförteckning

React patterns
uxpin.com. 2022. “The Best React Design Patterns You Should Know in 2023“ https://www.uxpin.com/studio/blog/react-design-patterns/ (Hämtad 2023-04-25)


Component composition
Vincas Stonys(YouTube). 2023. “Composable & Compound Components” Un-Suck Your React Components - Composable & Compound Components (Hämtad 2023-04-25)

Felix Gerschau. 2022. “React Component Composition Explained” https://felixgerschau.com/react-component-composition/ (Hämtad 2023-04-25)

Kencdodds.com. 2019. “React Hooks: Compound components” https://kentcdodds.com/blog/compound-components-with-react-hooks (Hämtad 2023-05-02)


Custom hooks
Turing.com. 2023. “Custom React JS Hooks: What Are They and When to Use Them?” https://www.turing.com/blog/custom-react-js-hooks-how-to-use/


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

## Widget

11 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 Styles

Are 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 Widths

Are the width that an widget loads with, can be changed by the user

-  w-full
-  w-1/2

## Widget Button Styles

Are the styles that the widget button loads with, can not be changed by the user as of now

## Widget Button Types

Are the types that define what the button does, as of now only the following are available

-  dropdown
-  addProjectItem

## Widget Button Descriptions

Are the descriptions that the primary widget button loads with

-  'Nytt projekt' for example

## Widget Dropdown Options

Are the options that the dropdown widget button loads with

-  ['Kvartalsrapport', 'Månadsrapport', 'Översikt'] for example

## Widget Button Actions

Are the actions that the widget button does when clicked, as of now only the following are available

-  dropdownHandler
-  addProjectItemHandler

## Widget Content

Is 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:

  1. User should be able to see the number of created requests, received offers, waiting for approval and received orders
  2. User could overview the quarterly report, monthly report and overview of of various information
  3. User should be able to add and, delete and handle projects
  4. User should be able to see the weather
  5. User should be able to see the latest contacts

Supplier:

  1. User should be able to see the number of active offers, sent offers, received orders and delivered orders
  2. User could overview the quarterly report, monthly report and overview of of various information
  3. User should be able to see the weather
  4. User should be able to see the latest contacts

Bud:

  1. User should be able to see the number of active offers, sent offers, received orders and delivered orders
  2. User could overview the quarterly report, monthly report and overview of of various information
  3. User should be able to see the weather
  4. User should be able to see the latest contacts

Bilder


Demo