diff --git a/transparency_dashboard_frontend/src/@fuse/animations/defaults.ts b/transparency_dashboard_frontend/src/@fuse/animations/defaults.ts
new file mode 100644
index 0000000000000000000000000000000000000000..784fd751ef7f6fbaeb14c335afb0be17bd9bb30d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/defaults.ts
@@ -0,0 +1,14 @@
+export class FuseAnimationCurves
+{
+    static standard = 'cubic-bezier(0.4, 0.0, 0.2, 1)';
+    static deceleration = 'cubic-bezier(0.0, 0.0, 0.2, 1)';
+    static acceleration = 'cubic-bezier(0.4, 0.0, 1, 1)';
+    static sharp = 'cubic-bezier(0.4, 0.0, 0.6, 1)';
+}
+
+export class FuseAnimationDurations
+{
+    static complex = '375ms';
+    static entering = '225ms';
+    static exiting = '195ms';
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/expand-collapse.ts b/transparency_dashboard_frontend/src/@fuse/animations/expand-collapse.ts
new file mode 100644
index 0000000000000000000000000000000000000000..60b6390336529bd40e663eafb9fd8cf33dade2b7
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/expand-collapse.ts
@@ -0,0 +1,34 @@
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
+
+// -----------------------------------------------------------------------------------------------------
+// @ Expand / collapse
+// -----------------------------------------------------------------------------------------------------
+const expandCollapse = trigger('expandCollapse',
+    [
+        state('void, collapsed',
+            style({
+                height: '0'
+            })
+        ),
+
+        state('*, expanded',
+            style('*')
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void <=> false, collapsed <=> false, expanded <=> false', []),
+
+        // Transition
+        transition('void <=> *, collapsed <=> expanded',
+            animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+export { expandCollapse };
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/fade.ts b/transparency_dashboard_frontend/src/@fuse/animations/fade.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a2982b70b4052630fd310fd19839982ef6930eab
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/fade.ts
@@ -0,0 +1,330 @@
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade in
+// -----------------------------------------------------------------------------------------------------
+const fadeIn = trigger('fadeIn',
+    [
+        state('void',
+            style({
+                opacity: 0
+            })
+        ),
+
+        state('*',
+            style({
+                opacity: 1
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade in top
+// -----------------------------------------------------------------------------------------------------
+const fadeInTop = trigger('fadeInTop',
+    [
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(0, -100%, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade in bottom
+// -----------------------------------------------------------------------------------------------------
+const fadeInBottom = trigger('fadeInBottom',
+    [
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(0, 100%, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade in left
+// -----------------------------------------------------------------------------------------------------
+const fadeInLeft = trigger('fadeInLeft',
+    [
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(-100%, 0, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade in right
+// -----------------------------------------------------------------------------------------------------
+const fadeInRight = trigger('fadeInRight',
+    [
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(100%, 0, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade out
+// -----------------------------------------------------------------------------------------------------
+const fadeOut = trigger('fadeOut',
+    [
+        state('*',
+            style({
+                opacity: 1
+            })
+        ),
+
+        state('void',
+            style({
+                opacity: 0
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade out top
+// -----------------------------------------------------------------------------------------------------
+const fadeOutTop = trigger('fadeOutTop',
+    [
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(0, -100%, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade out bottom
+// -----------------------------------------------------------------------------------------------------
+const fadeOutBottom = trigger('fadeOutBottom',
+    [
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(0, 100%, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade out left
+// -----------------------------------------------------------------------------------------------------
+const fadeOutLeft = trigger('fadeOutLeft',
+    [
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(-100%, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Fade out right
+// -----------------------------------------------------------------------------------------------------
+const fadeOutRight = trigger('fadeOutRight',
+    [
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'translate3d(100%, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+export { fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight, fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight };
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/index.ts b/transparency_dashboard_frontend/src/@fuse/animations/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3669783488c0bde6b4de6b4188d33b646c2c3fc
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/animations/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/public-api.ts b/transparency_dashboard_frontend/src/@fuse/animations/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3e7cd5a542bb4745b8050d800929576ce50de708
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/public-api.ts
@@ -0,0 +1,15 @@
+import { expandCollapse } from '@fuse/animations/expand-collapse';
+import { fadeIn, fadeInBottom, fadeInLeft, fadeInRight, fadeInTop, fadeOut, fadeOutBottom, fadeOutLeft, fadeOutRight, fadeOutTop } from '@fuse/animations/fade';
+import { shake } from '@fuse/animations/shake';
+import { slideInBottom, slideInLeft, slideInRight, slideInTop, slideOutBottom, slideOutLeft, slideOutRight, slideOutTop } from '@fuse/animations/slide';
+import { zoomIn, zoomOut } from '@fuse/animations/zoom';
+
+export const fuseAnimations = [
+    expandCollapse,
+    fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight,
+    fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight,
+    shake,
+    slideInTop, slideInBottom, slideInLeft, slideInRight,
+    slideOutTop, slideOutBottom, slideOutLeft, slideOutRight,
+    zoomIn, zoomOut
+];
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/shake.ts b/transparency_dashboard_frontend/src/@fuse/animations/shake.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2742345bafaf7f9cc2b34dc7454c003680c73126
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/shake.ts
@@ -0,0 +1,73 @@
+import { animate, keyframes, style, transition, trigger } from '@angular/animations';
+
+// -----------------------------------------------------------------------------------------------------
+// @ Shake
+// -----------------------------------------------------------------------------------------------------
+const shake = trigger('shake',
+    [
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *, * => true',
+            [
+                animate('{{timings}}',
+                    keyframes([
+                        style({
+                            transform: 'translate3d(0, 0, 0)',
+                            offset   : 0
+                        }),
+                        style({
+                            transform: 'translate3d(-10px, 0, 0)',
+                            offset   : 0.1
+                        }),
+                        style({
+                            transform: 'translate3d(10px, 0, 0)',
+                            offset   : 0.2
+                        }),
+                        style({
+                            transform: 'translate3d(-10px, 0, 0)',
+                            offset   : 0.3
+                        }),
+                        style({
+                            transform: 'translate3d(10px, 0, 0)',
+                            offset   : 0.4
+                        }),
+                        style({
+                            transform: 'translate3d(-10px, 0, 0)',
+                            offset   : 0.5
+                        }),
+                        style({
+                            transform: 'translate3d(10px, 0, 0)',
+                            offset   : 0.6
+                        }),
+                        style({
+                            transform: 'translate3d(-10px, 0, 0)',
+                            offset   : 0.7
+                        }),
+                        style({
+                            transform: 'translate3d(10px, 0, 0)',
+                            offset   : 0.8
+                        }),
+                        style({
+                            transform: 'translate3d(-10px, 0, 0)',
+                            offset   : 0.9
+                        }),
+                        style({
+                            transform: 'translate3d(0, 0, 0)',
+                            offset   : 1
+                        })
+                    ])
+                )
+            ],
+            {
+                params: {
+                    timings: '0.8s cubic-bezier(0.455, 0.03, 0.515, 0.955)'
+                }
+            }
+        )
+    ]
+);
+
+export { shake };
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/slide.ts b/transparency_dashboard_frontend/src/@fuse/animations/slide.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08a80ba7f44b8ec47e49f17700cd0951b2fcfde2
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/slide.ts
@@ -0,0 +1,252 @@
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide in top
+// -----------------------------------------------------------------------------------------------------
+const slideInTop = trigger('slideInTop',
+    [
+        state('void',
+            style({
+                transform: 'translate3d(0, -100%, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide in bottom
+// -----------------------------------------------------------------------------------------------------
+const slideInBottom = trigger('slideInBottom',
+    [
+        state('void',
+            style({
+                transform: 'translate3d(0, 100%, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide in left
+// -----------------------------------------------------------------------------------------------------
+const slideInLeft = trigger('slideInLeft',
+    [
+        state('void',
+            style({
+                transform: 'translate3d(-100%, 0, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide in right
+// -----------------------------------------------------------------------------------------------------
+const slideInRight = trigger('slideInRight',
+    [
+        state('void',
+            style({
+                transform: 'translate3d(100%, 0, 0)'
+            })
+        ),
+
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide out top
+// -----------------------------------------------------------------------------------------------------
+const slideOutTop = trigger('slideOutTop',
+    [
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                transform: 'translate3d(0, -100%, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide out bottom
+// -----------------------------------------------------------------------------------------------------
+const slideOutBottom = trigger('slideOutBottom',
+    [
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                transform: 'translate3d(0, 100%, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide out left
+// -----------------------------------------------------------------------------------------------------
+const slideOutLeft = trigger('slideOutLeft',
+    [
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                transform: 'translate3d(-100%, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Slide out right
+// -----------------------------------------------------------------------------------------------------
+const slideOutRight = trigger('slideOutRight',
+    [
+        state('*',
+            style({
+                transform: 'translate3d(0, 0, 0)'
+            })
+        ),
+
+        state('void',
+            style({
+                transform: 'translate3d(100%, 0, 0)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+export { slideInTop, slideInBottom, slideInLeft, slideInRight, slideOutTop, slideOutBottom, slideOutLeft, slideOutRight };
diff --git a/transparency_dashboard_frontend/src/@fuse/animations/zoom.ts b/transparency_dashboard_frontend/src/@fuse/animations/zoom.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c9742515952f8dad6c76f614f5aba427a315e5dd
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/animations/zoom.ts
@@ -0,0 +1,73 @@
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { FuseAnimationCurves, FuseAnimationDurations } from '@fuse/animations/defaults';
+
+// -----------------------------------------------------------------------------------------------------
+// @ Zoom in
+// -----------------------------------------------------------------------------------------------------
+const zoomIn = trigger('zoomIn',
+    [
+
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'scale(0.5)'
+            })
+        ),
+
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'scale(1)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('void => false', []),
+
+        // Transition
+        transition('void => *', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}`
+                }
+            }
+        )
+    ]
+);
+
+// -----------------------------------------------------------------------------------------------------
+// @ Zoom out
+// -----------------------------------------------------------------------------------------------------
+const zoomOut = trigger('zoomOut',
+    [
+
+        state('*',
+            style({
+                opacity  : 1,
+                transform: 'scale(1)'
+            })
+        ),
+
+        state('void',
+            style({
+                opacity  : 0,
+                transform: 'scale(0.5)'
+            })
+        ),
+
+        // Prevent the transition if the state is false
+        transition('false => void', []),
+
+        // Transition
+        transition('* => void', animate('{{timings}}'),
+            {
+                params: {
+                    timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}`
+                }
+            }
+        )
+    ]
+);
+
+export { zoomIn, zoomOut };
+
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.html b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6efa4c99bbe67147542359344611d1ff3648d147
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.html
@@ -0,0 +1,82 @@
+<div
+    class="fuse-alert-container"
+    *ngIf="!dismissible || dismissible && !dismissed"
+    [@fadeIn]="!dismissed"
+    [@fadeOut]="!dismissed">
+
+    <!-- Border -->
+    <div
+        class="fuse-alert-border"
+        *ngIf="appearance === 'border'"></div>
+
+    <!-- Icon -->
+    <div
+        class="fuse-alert-icon"
+        *ngIf="showIcon">
+
+        <!-- Custom icon -->
+        <div class="fuse-alert-custom-icon">
+            <ng-content select="[fuseAlertIcon]"></ng-content>
+        </div>
+
+        <!-- Default icons -->
+        <div class="fuse-alert-default-icon">
+
+            <mat-icon
+                *ngIf="type === 'primary'"
+                [svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'accent'"
+                [svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'warn'"
+                [svgIcon]="'heroicons_solid:x-circle'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'basic'"
+                [svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'info'"
+                [svgIcon]="'heroicons_solid:information-circle'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'success'"
+                [svgIcon]="'heroicons_solid:check-circle'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'warning'"
+                [svgIcon]="'heroicons_solid:exclamation'"></mat-icon>
+
+            <mat-icon
+                *ngIf="type === 'error'"
+                [svgIcon]="'heroicons_solid:x-circle'"></mat-icon>
+
+        </div>
+
+    </div>
+
+    <!-- Content -->
+    <div class="fuse-alert-content">
+
+        <div class="fuse-alert-title">
+            <ng-content select="[fuseAlertTitle]"></ng-content>
+        </div>
+
+        <div class="fuse-alert-message">
+            <ng-content></ng-content>
+        </div>
+
+    </div>
+
+    <!-- Dismiss button -->
+    <button
+        class="fuse-alert-dismiss-button"
+        mat-icon-button
+        (click)="dismiss()">
+        <mat-icon [svgIcon]="'heroicons_solid:x'"></mat-icon>
+    </button>
+
+</div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.scss b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4b1a86cee847cd706b41653fd5c6e7a54e135d89
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.scss
@@ -0,0 +1,1340 @@
+fuse-alert {
+    display: block;
+
+    /* Common */
+    .fuse-alert-container {
+        position: relative;
+        display: flex;
+        padding: 16px;
+        font-size: 14px;
+        line-height: 1;
+
+        /* All icons */
+        .mat-icon {
+            color: currentColor !important;
+        }
+
+        /* Icon */
+        .fuse-alert-icon {
+            display: flex;
+            align-items: flex-start;
+
+            .fuse-alert-custom-icon,
+            .fuse-alert-default-icon {
+                display: none;
+                align-items: center;
+                justify-content: center;
+                border-radius: 50%;
+
+                &:not(:empty) {
+                    display: flex;
+                    margin-right: 12px;
+                }
+            }
+
+            .fuse-alert-default-icon {
+
+                .mat-icon {
+                    @apply icon-size-5;
+                }
+            }
+
+            .fuse-alert-custom-icon {
+                display: none;
+
+                &:not(:empty) {
+                    display: flex;
+
+                    + .fuse-alert-default-icon {
+                        display: none;
+                    }
+                }
+            }
+        }
+
+        /* Content */
+        .fuse-alert-content {
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            line-height: 1;
+
+            /* Title */
+            .fuse-alert-title {
+                display: none;
+                font-weight: 600;
+                line-height: 20px;
+
+                &:not(:empty) {
+                    display: block;
+
+                    /* Alert that comes after the title */
+                    + .fuse-alert-message {
+
+                        &:not(:empty) {
+                            margin-top: 4px;
+                        }
+                    }
+                }
+            }
+
+            /* Alert */
+            .fuse-alert-message {
+                display: none;
+                line-height: 20px;
+
+                &:not(:empty) {
+                    display: block;
+                }
+            }
+        }
+
+        /* Dismiss button */
+        .fuse-alert-dismiss-button {
+            position: absolute;
+            top: 10px;
+            right: 10px;
+            width: 32px !important;
+            min-width: 32px !important;
+            height: 32px !important;
+            min-height: 32px !important;
+            line-height: 32px !important;
+
+            .mat-icon {
+                @apply icon-size-4;
+            }
+        }
+    }
+
+    /* Dismissible */
+    &.fuse-alert-dismissible {
+
+        .fuse-alert-container {
+
+            .fuse-alert-content {
+                margin-right: 32px;
+            }
+        }
+    }
+
+    &:not(.fuse-alert-dismissible) {
+
+        .fuse-alert-container {
+
+            .fuse-alert-dismiss-button {
+                display: none !important;
+            }
+        }
+    }
+
+    /* Border */
+    &.fuse-alert-appearance-border {
+
+        .fuse-alert-container {
+            position: relative;
+            overflow: hidden;
+            border-radius: 6px;
+            @apply shadow-md bg-card;
+
+            .fuse-alert-border {
+                position: absolute;
+                left: 0;
+                top: 0;
+                bottom: 0;
+                width: 4px;
+            }
+
+            .fuse-alert-message {
+                @apply text-gray-600;
+            }
+        }
+
+        /* Primary */
+        &.fuse-alert-type-primary {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-primary;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-primary;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-primary-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-primary-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Accent */
+        &.fuse-alert-type-accent {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-accent;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-accent;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-accent-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-accent-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Warn */
+        &.fuse-alert-type-warn {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-warn;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-warn;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-warn-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-warn-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Basic */
+        &.fuse-alert-type-basic {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-gray-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-gray-600;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-gray-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-gray-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Info */
+        &.fuse-alert-type-info {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-blue-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-blue-700;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-blue-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-blue-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Success */
+        &.fuse-alert-type-success {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-green-500;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-green-500;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-green-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-green-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Warning */
+        &.fuse-alert-type-warning {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-amber-500;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-amber-500;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-amber-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-amber-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+
+        /* Error */
+        &.fuse-alert-type-error {
+
+            .fuse-alert-container {
+
+                .fuse-alert-border {
+                    @apply bg-red-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-icon {
+                    @apply text-red-700;
+                }
+
+                .dark & {
+                    @apply bg-gray-700;
+
+                    .fuse-alert-border {
+                        @apply bg-red-400;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-icon {
+                        @apply text-red-400;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-300;
+                    }
+
+                    code {
+                        @apply bg-gray-400 text-gray-800;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Fill */
+    &.fuse-alert-appearance-fill {
+
+        .fuse-alert-container {
+            border-radius: 6px;
+
+            .fuse-alert-dismiss-button {
+                @apply text-white;
+            }
+        }
+
+        /* Primary */
+        &.fuse-alert-type-primary {
+
+            .fuse-alert-container {
+                @apply bg-primary-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-primary-100;
+                }
+
+                code {
+                    @apply text-primary-800 bg-primary-200;
+                }
+            }
+        }
+
+        /* Accent */
+        &.fuse-alert-type-accent {
+
+            .fuse-alert-container {
+                @apply bg-accent-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-accent-100;
+                }
+
+                code {
+                    @apply text-accent-800 bg-accent-200;
+                }
+            }
+        }
+
+        /* Warn */
+        &.fuse-alert-type-warn {
+
+            .fuse-alert-container {
+                @apply bg-warn-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-warn-100;
+                }
+
+                code {
+                    @apply text-warn-800 bg-warn-200;
+                }
+            }
+        }
+
+        /* Basic */
+        &.fuse-alert-type-basic {
+
+            .fuse-alert-container {
+                @apply bg-gray-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-gray-100;
+                }
+
+                code {
+                    @apply bg-gray-200 text-gray-800;
+                }
+            }
+        }
+
+        /* Info */
+        &.fuse-alert-type-info {
+
+            .fuse-alert-container {
+                @apply bg-blue-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-blue-100;
+                }
+
+                code {
+                    @apply bg-blue-200 text-blue-800;
+                }
+            }
+        }
+
+        /* Success */
+        &.fuse-alert-type-success {
+
+            .fuse-alert-container {
+                @apply bg-green-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-green-100;
+                }
+
+                code {
+                    @apply bg-green-200 text-gray-800;
+                }
+            }
+        }
+
+        /* Warning */
+        &.fuse-alert-type-warning {
+
+            .fuse-alert-container {
+                @apply bg-amber-500;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-amber-100;
+                }
+
+                code {
+                    @apply bg-amber-200 text-amber-800;
+                }
+            }
+        }
+
+        /* Error */
+        &.fuse-alert-type-error {
+
+            .fuse-alert-container {
+                @apply bg-red-600;
+
+                .fuse-alert-icon {
+                    @apply text-white;
+                }
+
+                .fuse-alert-title {
+                    @apply text-white;
+                }
+
+                .fuse-alert-message {
+                    @apply text-red-100;
+                }
+
+                code {
+                    @apply bg-red-200 text-red-800;
+                }
+            }
+        }
+    }
+
+    /* Outline */
+    &.fuse-alert-appearance-outline {
+
+        .fuse-alert-container {
+            border-radius: 6px;
+        }
+
+        /* Primary */
+        &.fuse-alert-type-primary {
+
+            .fuse-alert-container {
+                @apply bg-primary-50 ring-1 ring-primary-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-primary-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-primary-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-primary-700;
+                }
+
+                code {
+                    @apply text-primary-800 bg-primary-200;
+                }
+
+                .dark & {
+                    @apply bg-primary-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-primary-200;
+                    }
+                }
+            }
+        }
+
+        /* Accent */
+        &.fuse-alert-type-accent {
+
+            .fuse-alert-container {
+                @apply bg-accent-100 ring-1 ring-accent-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-accent-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-accent-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-accent-700;
+                }
+
+                code {
+                    @apply text-accent-800 bg-accent-200;
+                }
+
+                .dark & {
+                    @apply bg-accent-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-accent-200;
+                    }
+                }
+            }
+        }
+
+        /* Warn */
+        &.fuse-alert-type-warn {
+
+            .fuse-alert-container {
+                @apply bg-warn-50 ring-1 ring-warn-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-warn-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-warn-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-warn-700;
+                }
+
+                code {
+                    @apply text-warn-800 bg-warn-200;
+                }
+
+                .dark & {
+                    @apply bg-warn-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-warn-200;
+                    }
+                }
+            }
+        }
+
+        /* Basic */
+        &.fuse-alert-type-basic {
+
+            .fuse-alert-container {
+                @apply bg-gray-100 ring-1 ring-gray-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-gray-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-gray-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-gray-700;
+                }
+
+                code {
+                    @apply bg-gray-200 text-gray-800;
+                }
+
+                .dark & {
+                    @apply bg-gray-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-200;
+                    }
+                }
+            }
+        }
+
+        /* Info */
+        &.fuse-alert-type-info {
+
+            .fuse-alert-container {
+                @apply bg-blue-50 ring-1 ring-blue-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-blue-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-blue-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-blue-700;
+                }
+
+                code {
+                    @apply bg-blue-200 text-blue-800;
+                }
+
+                .dark & {
+                    @apply bg-blue-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-blue-200;
+                    }
+                }
+            }
+        }
+
+        /* Success */
+        &.fuse-alert-type-success {
+
+            .fuse-alert-container {
+                @apply bg-green-50 ring-1 ring-green-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-green-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-green-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-green-700;
+                }
+
+                code {
+                    @apply bg-green-200 text-green-800;
+                }
+
+                .dark & {
+                    @apply bg-green-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-green-200;
+                    }
+                }
+            }
+        }
+
+        /* Warning */
+        &.fuse-alert-type-warning {
+
+            .fuse-alert-container {
+                @apply bg-amber-50 ring-1 ring-amber-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-amber-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-amber-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-amber-700;
+                }
+
+                code {
+                    @apply bg-amber-200 text-amber-800;
+                }
+
+                .dark & {
+                    @apply bg-amber-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-amber-200;
+                    }
+                }
+            }
+        }
+
+        /* Error */
+        &.fuse-alert-type-error {
+
+            .fuse-alert-container {
+                @apply bg-red-50 ring-1 ring-red-400 ring-inset;
+
+                .fuse-alert-icon {
+                    @apply text-red-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-red-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-red-700;
+                }
+
+                code {
+                    @apply bg-red-200 text-red-800;
+                }
+
+                .dark & {
+                    @apply bg-red-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-red-200;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Soft */
+    &.fuse-alert-appearance-soft {
+
+        .fuse-alert-container {
+            border-radius: 6px;
+        }
+
+        /* Primary */
+        &.fuse-alert-type-primary {
+
+            .fuse-alert-container {
+                @apply bg-primary-50;
+
+                .fuse-alert-icon {
+                    @apply text-primary-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-primary-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-primary-700;
+                }
+
+                code {
+                    @apply text-primary-800 bg-primary-200;
+                }
+
+                .dark & {
+                    @apply bg-primary-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-primary-200;
+                    }
+                }
+            }
+        }
+
+        /* Accent */
+        &.fuse-alert-type-accent {
+
+            .fuse-alert-container {
+                @apply bg-accent-100;
+
+                .fuse-alert-icon {
+                    @apply text-accent-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-accent-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-accent-700;
+                }
+
+                code {
+                    @apply text-accent-800 bg-accent-200;
+                }
+
+                .dark & {
+                    @apply bg-accent-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-accent-200;
+                    }
+                }
+            }
+        }
+
+        /* Warn */
+        &.fuse-alert-type-warn {
+
+            .fuse-alert-container {
+                @apply bg-warn-50;
+
+                .fuse-alert-icon {
+                    @apply text-warn-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-warn-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-warn-700;
+                }
+
+                code {
+                    @apply text-warn-800 bg-warn-200;
+                }
+
+                .dark & {
+                    @apply bg-warn-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-warn-200;
+                    }
+                }
+            }
+        }
+
+        /* Basic */
+        &.fuse-alert-type-basic {
+
+            .fuse-alert-container {
+                @apply bg-gray-100;
+
+                .fuse-alert-icon {
+                    @apply text-gray-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-gray-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-gray-700;
+                }
+
+                code {
+                    @apply bg-gray-200 text-gray-800;
+                }
+
+                .dark & {
+                    @apply bg-gray-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-gray-200;
+                    }
+                }
+            }
+        }
+
+        /* Info */
+        &.fuse-alert-type-info {
+
+            .fuse-alert-container {
+                @apply bg-blue-50;
+
+                .fuse-alert-icon {
+                    @apply text-blue-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-blue-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-blue-700;
+                }
+
+                code {
+                    @apply bg-blue-200 text-blue-800;
+                }
+
+                .dark & {
+                    @apply bg-blue-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-blue-200;
+                    }
+                }
+            }
+        }
+
+        /* Success */
+        &.fuse-alert-type-success {
+
+            .fuse-alert-container {
+                @apply bg-green-50;
+
+                .fuse-alert-icon {
+                    @apply text-green-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-green-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-green-700;
+                }
+
+                code {
+                    @apply bg-green-200 text-green-800;
+                }
+
+                .dark & {
+                    @apply bg-green-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-green-200;
+                    }
+                }
+            }
+        }
+
+        /* Warning */
+        &.fuse-alert-type-warning {
+
+            .fuse-alert-container {
+                @apply bg-amber-50;
+
+                .fuse-alert-icon {
+                    @apply text-amber-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-amber-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-amber-700;
+                }
+
+                code {
+                    @apply bg-amber-200 text-amber-800;
+                }
+
+                .dark & {
+                    @apply bg-amber-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-amber-200;
+                    }
+                }
+            }
+        }
+
+        /* Error */
+        &.fuse-alert-type-error {
+
+            .fuse-alert-container {
+                @apply bg-red-50;
+
+                .fuse-alert-icon {
+                    @apply text-red-600;
+                }
+
+                .fuse-alert-title,
+                .fuse-alert-dismiss-button {
+                    @apply text-red-900;
+                }
+
+                .fuse-alert-message {
+                    @apply text-red-700;
+                }
+
+                code {
+                    @apply bg-red-200 text-red-800;
+                }
+
+                .dark & {
+                    @apply bg-red-600;
+
+                    .fuse-alert-icon {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-title,
+                    .fuse-alert-dismiss-button {
+                        @apply text-white;
+                    }
+
+                    .fuse-alert-message {
+                        @apply text-red-200;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.ts b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d8e0fd6dc8566788362fec5bc52a269ef3576b5a
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.component.ts
@@ -0,0 +1,212 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
+import { filter, Subject, takeUntil } from 'rxjs';
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { fuseAnimations } from '@fuse/animations';
+import { FuseAlertAppearance, FuseAlertType } from '@fuse/components/alert/alert.types';
+import { FuseAlertService } from '@fuse/components/alert/alert.service';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+
+@Component({
+    selector       : 'fuse-alert',
+    templateUrl    : './alert.component.html',
+    styleUrls      : ['./alert.component.scss'],
+    encapsulation  : ViewEncapsulation.None,
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    animations     : fuseAnimations,
+    exportAs       : 'fuseAlert'
+})
+export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_dismissible: BooleanInput;
+    static ngAcceptInputType_dismissed: BooleanInput;
+    static ngAcceptInputType_showIcon: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() appearance: FuseAlertAppearance = 'soft';
+    @Input() dismissed: boolean = false;
+    @Input() dismissible: boolean = false;
+    @Input() name: string = this._fuseUtilsService.randomId();
+    @Input() showIcon: boolean = true;
+    @Input() type: FuseAlertType = 'primary';
+    @Output() readonly dismissedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseAlertService: FuseAlertService,
+        private _fuseUtilsService: FuseUtilsService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Host binding for component classes
+     */
+    @HostBinding('class') get classList(): any
+    {
+        return {
+            'fuse-alert-appearance-border' : this.appearance === 'border',
+            'fuse-alert-appearance-fill'   : this.appearance === 'fill',
+            'fuse-alert-appearance-outline': this.appearance === 'outline',
+            'fuse-alert-appearance-soft'   : this.appearance === 'soft',
+            'fuse-alert-dismissed'         : this.dismissed,
+            'fuse-alert-dismissible'       : this.dismissible,
+            'fuse-alert-show-icon'         : this.showIcon,
+            'fuse-alert-type-primary'      : this.type === 'primary',
+            'fuse-alert-type-accent'       : this.type === 'accent',
+            'fuse-alert-type-warn'         : this.type === 'warn',
+            'fuse-alert-type-basic'        : this.type === 'basic',
+            'fuse-alert-type-info'         : this.type === 'info',
+            'fuse-alert-type-success'      : this.type === 'success',
+            'fuse-alert-type-warning'      : this.type === 'warning',
+            'fuse-alert-type-error'        : this.type === 'error'
+        };
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Dismissed
+        if ( 'dismissed' in changes )
+        {
+            // Coerce the value to a boolean
+            this.dismissed = coerceBooleanProperty(changes.dismissed.currentValue);
+
+            // Dismiss/show the alert
+            this._toggleDismiss(this.dismissed);
+        }
+
+        // Dismissible
+        if ( 'dismissible' in changes )
+        {
+            // Coerce the value to a boolean
+            this.dismissible = coerceBooleanProperty(changes.dismissible.currentValue);
+        }
+
+        // Show icon
+        if ( 'showIcon' in changes )
+        {
+            // Coerce the value to a boolean
+            this.showIcon = coerceBooleanProperty(changes.showIcon.currentValue);
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Subscribe to the dismiss calls
+        this._fuseAlertService.onDismiss
+            .pipe(
+                filter(name => this.name === name),
+                takeUntil(this._unsubscribeAll)
+            )
+            .subscribe(() => {
+
+                // Dismiss the alert
+                this.dismiss();
+            });
+
+        // Subscribe to the show calls
+        this._fuseAlertService.onShow
+            .pipe(
+                filter(name => this.name === name),
+                takeUntil(this._unsubscribeAll)
+            )
+            .subscribe(() => {
+
+                // Show the alert
+                this.show();
+            });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Dismiss the alert
+     */
+    dismiss(): void
+    {
+        // Return if the alert is already dismissed
+        if ( this.dismissed )
+        {
+            return;
+        }
+
+        // Dismiss the alert
+        this._toggleDismiss(true);
+    }
+
+    /**
+     * Show the dismissed alert
+     */
+    show(): void
+    {
+        // Return if the alert is already showing
+        if ( !this.dismissed )
+        {
+            return;
+        }
+
+        // Show the alert
+        this._toggleDismiss(false);
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Dismiss/show the alert
+     *
+     * @param dismissed
+     * @private
+     */
+    private _toggleDismiss(dismissed: boolean): void
+    {
+        // Return if the alert is not dismissible
+        if ( !this.dismissible )
+        {
+            return;
+        }
+
+        // Set the dismissed
+        this.dismissed = dismissed;
+
+        // Execute the observable
+        this.dismissedChanged.next(this.dismissed);
+
+        // Notify the change detector
+        this._changeDetectorRef.markForCheck();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/alert.module.ts b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..beecabdd92767a3171224bddfcea7b7b61ac2ff3
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.module.ts
@@ -0,0 +1,22 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { FuseAlertComponent } from '@fuse/components/alert/alert.component';
+
+@NgModule({
+    declarations: [
+        FuseAlertComponent
+    ],
+    imports     : [
+        CommonModule,
+        MatButtonModule,
+        MatIconModule
+    ],
+    exports     : [
+        FuseAlertComponent
+    ]
+})
+export class FuseAlertModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/alert.service.ts b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a04dbe5c7bbb78d16be6f2c894e87dec98c43fdf
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.service.ts
@@ -0,0 +1,77 @@
+import { Injectable } from '@angular/core';
+import { Observable, ReplaySubject } from 'rxjs';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseAlertService
+{
+    private readonly _onDismiss: ReplaySubject<string> = new ReplaySubject<string>(1);
+    private readonly _onShow: ReplaySubject<string> = new ReplaySubject<string>(1);
+
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Getter for onDismiss
+     */
+    get onDismiss(): Observable<any>
+    {
+        return this._onDismiss.asObservable();
+    }
+
+    /**
+     * Getter for onShow
+     */
+    get onShow(): Observable<any>
+    {
+        return this._onShow.asObservable();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Dismiss the alert
+     *
+     * @param name
+     */
+    dismiss(name: string): void
+    {
+        // Return if the name is not provided
+        if ( !name )
+        {
+            return;
+        }
+
+        // Execute the observable
+        this._onDismiss.next(name);
+    }
+
+    /**
+     * Show the dismissed alert
+     *
+     * @param name
+     */
+    show(name: string): void
+    {
+        // Return if the name is not provided
+        if ( !name )
+        {
+            return;
+        }
+
+        // Execute the observable
+        this._onShow.next(name);
+    }
+
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/alert.types.ts b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc3516e1b6dd0800181eb072fa72de1a92e50ac5
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/alert.types.ts
@@ -0,0 +1,15 @@
+export type FuseAlertAppearance =
+    | 'border'
+    | 'fill'
+    | 'outline'
+    | 'soft';
+
+export type FuseAlertType =
+    | 'primary'
+    | 'accent'
+    | 'warn'
+    | 'basic'
+    | 'info'
+    | 'success'
+    | 'warning'
+    | 'error';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/index.ts b/transparency_dashboard_frontend/src/@fuse/components/alert/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c18a807a8095773e19a5d97c0d9bf14776fba703
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/alert/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/alert/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/alert/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fdf984ae59f2bf50eaa4cd484c0272e9c4de7776
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/alert/public-api.ts
@@ -0,0 +1,4 @@
+export * from '@fuse/components/alert/alert.component';
+export * from '@fuse/components/alert/alert.module';
+export * from '@fuse/components/alert/alert.service';
+export * from '@fuse/components/alert/alert.types';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/card.component.html b/transparency_dashboard_frontend/src/@fuse/components/card/card.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5728d204b827ce762a5e4ba5a8ee0cc6b8076705
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/card.component.html
@@ -0,0 +1,30 @@
+<!-- Flippable card -->
+<ng-container *ngIf="flippable">
+
+    <!-- Front -->
+    <div class="fuse-card-front">
+        <ng-content select="[fuseCardFront]"></ng-content>
+    </div>
+
+    <!-- Back -->
+    <div class="fuse-card-back">
+        <ng-content select="[fuseCardBack]"></ng-content>
+    </div>
+
+</ng-container>
+
+<!-- Normal card -->
+<ng-container *ngIf="!flippable">
+
+    <!-- Content -->
+    <ng-content></ng-content>
+
+    <!-- Expansion -->
+    <div
+        class="fuse-card-expansion"
+        *ngIf="expanded"
+        [@expandCollapse]>
+        <ng-content select="[fuseCardExpansion]"></ng-content>
+    </div>
+
+</ng-container>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/card.component.scss b/transparency_dashboard_frontend/src/@fuse/components/card/card.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..2e2719d0dc6a5627d0e19d037a3d41faa5e19b34
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/card.component.scss
@@ -0,0 +1,63 @@
+fuse-card {
+    position: relative;
+    display: flex;
+    overflow: hidden;
+    @apply rounded-2xl shadow bg-card;
+
+    /* Flippable */
+    &.fuse-card-flippable {
+        border-radius: 0;
+        overflow: visible;
+        transform-style: preserve-3d;
+        transition: transform 1s;
+        perspective: 600px;
+        background: transparent;
+        @apply shadow-none;
+
+        &.fuse-card-face-back {
+
+            .fuse-card-front {
+                visibility: hidden;
+                opacity: 0;
+                transform: rotateY(180deg);
+            }
+
+            .fuse-card-back {
+                visibility: visible;
+                opacity: 1;
+                transform: rotateY(360deg);
+            }
+        }
+
+        .fuse-card-front,
+        .fuse-card-back {
+            display: flex;
+            flex-direction: column;
+            flex: 1 1 auto;
+            z-index: 10;
+            transition: transform 0.5s ease-out 0s, visibility 0s ease-in 0.2s, opacity 0s ease-in 0.2s;
+            backface-visibility: hidden;
+            @apply rounded-2xl shadow bg-card;
+        }
+
+        .fuse-card-front {
+            position: relative;
+            opacity: 1;
+            visibility: visible;
+            transform: rotateY(0deg);
+            overflow: hidden;
+        }
+
+        .fuse-card-back {
+            position: absolute;
+            top: 0;
+            right: 0;
+            bottom: 0;
+            left: 0;
+            opacity: 0;
+            visibility: hidden;
+            transform: rotateY(180deg);
+            overflow: hidden auto;
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/card.component.ts b/transparency_dashboard_frontend/src/@fuse/components/card/card.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bd9e0f7d3b6a7b854ca4420852f2210a4ba6fea6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/card.component.ts
@@ -0,0 +1,74 @@
+import { Component, HostBinding, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { fuseAnimations } from '@fuse/animations';
+import { FuseCardFace } from '@fuse/components/card/card.types';
+
+@Component({
+    selector     : 'fuse-card',
+    templateUrl  : './card.component.html',
+    styleUrls    : ['./card.component.scss'],
+    encapsulation: ViewEncapsulation.None,
+    animations   : fuseAnimations,
+    exportAs     : 'fuseCard'
+})
+export class FuseCardComponent implements OnChanges
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_expanded: BooleanInput;
+    static ngAcceptInputType_flippable: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() expanded: boolean = false;
+    @Input() face: FuseCardFace = 'front';
+    @Input() flippable: boolean = false;
+
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Host binding for component classes
+     */
+    @HostBinding('class') get classList(): any
+    {
+        return {
+            'fuse-card-expanded'  : this.expanded,
+            'fuse-card-face-back' : this.flippable && this.face === 'back',
+            'fuse-card-face-front': this.flippable && this.face === 'front',
+            'fuse-card-flippable' : this.flippable
+        };
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Expanded
+        if ( 'expanded' in changes )
+        {
+            // Coerce the value to a boolean
+            this.expanded = coerceBooleanProperty(changes.expanded.currentValue);
+        }
+
+        // Flippable
+        if ( 'flippable' in changes )
+        {
+            // Coerce the value to a boolean
+            this.flippable = coerceBooleanProperty(changes.flippable.currentValue);
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/card.module.ts b/transparency_dashboard_frontend/src/@fuse/components/card/card.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c600e30c3ed3972d6fc3d9914cc6d620884350cb
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/card.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FuseCardComponent } from '@fuse/components/card/card.component';
+
+@NgModule({
+    declarations: [
+        FuseCardComponent
+    ],
+    imports     : [
+        CommonModule
+    ],
+    exports     : [
+        FuseCardComponent
+    ]
+})
+export class FuseCardModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/card.types.ts b/transparency_dashboard_frontend/src/@fuse/components/card/card.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d925642692fee4244aaeb7c692ca54e8986b95d6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/card.types.ts
@@ -0,0 +1,3 @@
+export type FuseCardFace =
+    | 'front'
+    | 'back';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/index.ts b/transparency_dashboard_frontend/src/@fuse/components/card/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac978a7894c28846d19f51cd768ba902826d38e4
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/card/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/card/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/card/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee2ca6e847029c413509d39a8dcf1b8d031a8a65
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/card/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/components/card/card.component';
+export * from '@fuse/components/card/card.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.html b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b3a8acc45612b20241e9348a573208c1d94b1f78
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.html
@@ -0,0 +1,3 @@
+<div class="fuse-drawer-content">
+    <ng-content></ng-content>
+</div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.scss b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e10cad2afd377062cbc90a27a9dbd43d0e4b5b77
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.scss
@@ -0,0 +1,133 @@
+/* Variables */
+:root {
+    --fuse-drawer-width: 320px;
+}
+
+fuse-drawer {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    flex: 1 1 auto;
+    width: var(--fuse-drawer-width);
+    min-width: var(--fuse-drawer-width);
+    max-width: var(--fuse-drawer-width);
+    z-index: 300;
+    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .35);
+    @apply bg-card;
+
+    /* Animations */
+    &.fuse-drawer-animations-enabled {
+        transition-duration: 400ms;
+        transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+        transition-property: visibility, margin-left, margin-right, transform, width, max-width, min-width;
+
+        .fuse-drawer-content {
+            transition-duration: 400ms;
+            transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+            transition-property: width, max-width, min-width;
+        }
+    }
+
+    /* Over mode */
+    &.fuse-drawer-mode-over {
+        position: absolute;
+        top: 0;
+        bottom: 0;
+
+        /* Fixed mode */
+        &.fuse-drawer-fixed {
+            position: fixed;
+        }
+    }
+
+    /* Left position */
+    &.fuse-drawer-position-left {
+
+        /* Side mode */
+        &.fuse-drawer-mode-side {
+            margin-left: calc(var(--fuse-drawer-width) * -1);
+
+            &.fuse-drawer-opened {
+                margin-left: 0;
+            }
+        }
+
+        /* Over mode */
+        &.fuse-drawer-mode-over {
+            left: 0;
+            transform: translate3d(-100%, 0, 0);
+
+            &.fuse-drawer-opened {
+                transform: translate3d(0, 0, 0);
+            }
+        }
+
+        /* Content */
+        .fuse-drawer-content {
+            left: 0;
+        }
+    }
+
+    /* Right position */
+    &.fuse-drawer-position-right {
+
+        /* Side mode */
+        &.fuse-drawer-mode-side {
+            margin-right: calc(var(--fuse-drawer-width) * -1);
+
+            &.fuse-drawer-opened {
+                margin-right: 0;
+            }
+        }
+
+        /* Over mode */
+        &.fuse-drawer-mode-over {
+            right: 0;
+            transform: translate3d(100%, 0, 0);
+
+            &.fuse-drawer-opened {
+                transform: translate3d(0, 0, 0);
+            }
+        }
+
+        /* Content */
+        .fuse-drawer-content {
+            right: 0;
+        }
+    }
+
+    /* Content */
+    .fuse-drawer-content {
+        position: absolute;
+        display: flex;
+        flex: 1 1 auto;
+        top: 0;
+        bottom: 0;
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+        @apply bg-card;
+    }
+}
+
+/* Overlay */
+.fuse-drawer-overlay {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 299;
+    opacity: 1;
+    background-color: rgba(0, 0, 0, 0.6);
+
+    /* Fixed mode */
+    &.fuse-drawer-overlay-fixed {
+        position: fixed;
+    }
+
+    /* Transparent overlay */
+    &.fuse-drawer-overlay-transparent {
+        background-color: transparent;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.ts b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d644c14b15908aa5a39eda27b8484dbf81dcc945
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.component.ts
@@ -0,0 +1,437 @@
+import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, Renderer2, SimpleChanges, ViewEncapsulation } from '@angular/core';
+import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
+import { FuseDrawerMode, FuseDrawerPosition } from '@fuse/components/drawer/drawer.types';
+import { FuseDrawerService } from '@fuse/components/drawer/drawer.service';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+
+@Component({
+    selector     : 'fuse-drawer',
+    templateUrl  : './drawer.component.html',
+    styleUrls    : ['./drawer.component.scss'],
+    encapsulation: ViewEncapsulation.None,
+    exportAs     : 'fuseDrawer'
+})
+export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_fixed: BooleanInput;
+    static ngAcceptInputType_opened: BooleanInput;
+    static ngAcceptInputType_transparentOverlay: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() fixed: boolean = false;
+    @Input() mode: FuseDrawerMode = 'side';
+    @Input() name: string = this._fuseUtilsService.randomId();
+    @Input() opened: boolean = false;
+    @Input() position: FuseDrawerPosition = 'left';
+    @Input() transparentOverlay: boolean = false;
+    @Output() readonly fixedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
+    @Output() readonly modeChanged: EventEmitter<FuseDrawerMode> = new EventEmitter<FuseDrawerMode>();
+    @Output() readonly openedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
+    @Output() readonly positionChanged: EventEmitter<FuseDrawerPosition> = new EventEmitter<FuseDrawerPosition>();
+
+    private _animationsEnabled: boolean = false;
+    private _hovered: boolean = false;
+    private _overlay: HTMLElement;
+    private _player: AnimationPlayer;
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _animationBuilder: AnimationBuilder,
+        private _elementRef: ElementRef,
+        private _renderer2: Renderer2,
+        private _fuseDrawerService: FuseDrawerService,
+        private _fuseUtilsService: FuseUtilsService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Host binding for component classes
+     */
+    @HostBinding('class') get classList(): any
+    {
+        return {
+            'fuse-drawer-animations-enabled'         : this._animationsEnabled,
+            'fuse-drawer-fixed'                      : this.fixed,
+            'fuse-drawer-hover'                      : this._hovered,
+            [`fuse-drawer-mode-${this.mode}`]        : true,
+            'fuse-drawer-opened'                     : this.opened,
+            [`fuse-drawer-position-${this.position}`]: true
+        };
+    }
+
+    /**
+     * Host binding for component inline styles
+     */
+    @HostBinding('style') get styleList(): any
+    {
+        return {
+            'visibility': this.opened ? 'visible' : 'hidden'
+        };
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Decorated methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On mouseenter
+     *
+     * @private
+     */
+    @HostListener('mouseenter')
+    private _onMouseenter(): void
+    {
+        // Enable the animations
+        this._enableAnimations();
+
+        // Set the hovered
+        this._hovered = true;
+    }
+
+    /**
+     * On mouseleave
+     *
+     * @private
+     */
+    @HostListener('mouseleave')
+    private _onMouseleave(): void
+    {
+        // Enable the animations
+        this._enableAnimations();
+
+        // Set the hovered
+        this._hovered = false;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Fixed
+        if ( 'fixed' in changes )
+        {
+            // Coerce the value to a boolean
+            this.fixed = coerceBooleanProperty(changes.fixed.currentValue);
+
+            // Execute the observable
+            this.fixedChanged.next(this.fixed);
+        }
+
+        // Mode
+        if ( 'mode' in changes )
+        {
+            // Get the previous and current values
+            const previousMode = changes.mode.previousValue;
+            const currentMode = changes.mode.currentValue;
+
+            // Disable the animations
+            this._disableAnimations();
+
+            // If the mode changes: 'over -> side'
+            if ( previousMode === 'over' && currentMode === 'side' )
+            {
+                // Hide the overlay
+                this._hideOverlay();
+            }
+
+            // If the mode changes: 'side -> over'
+            if ( previousMode === 'side' && currentMode === 'over' )
+            {
+                // If the drawer is opened
+                if ( this.opened )
+                {
+                    // Show the overlay
+                    this._showOverlay();
+                }
+            }
+
+            // Execute the observable
+            this.modeChanged.next(currentMode);
+
+            // Enable the animations after a delay
+            // The delay must be bigger than the current transition-duration
+            // to make sure nothing will be animated while the mode is changing
+            setTimeout(() => {
+                this._enableAnimations();
+            }, 500);
+        }
+
+        // Opened
+        if ( 'opened' in changes )
+        {
+            // Coerce the value to a boolean
+            const open = coerceBooleanProperty(changes.opened.currentValue);
+
+            // Open/close the drawer
+            this._toggleOpened(open);
+        }
+
+        // Position
+        if ( 'position' in changes )
+        {
+            // Execute the observable
+            this.positionChanged.next(this.position);
+        }
+
+        // Transparent overlay
+        if ( 'transparentOverlay' in changes )
+        {
+            // Coerce the value to a boolean
+            this.transparentOverlay = coerceBooleanProperty(changes.transparentOverlay.currentValue);
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Register the drawer
+        this._fuseDrawerService.registerComponent(this.name, this);
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Finish the animation
+        if ( this._player )
+        {
+            this._player.finish();
+        }
+
+        // Deregister the drawer from the registry
+        this._fuseDrawerService.deregisterComponent(this.name);
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Open the drawer
+     */
+    open(): void
+    {
+        // Return if the drawer has already opened
+        if ( this.opened )
+        {
+            return;
+        }
+
+        // Open the drawer
+        this._toggleOpened(true);
+    }
+
+    /**
+     * Close the drawer
+     */
+    close(): void
+    {
+        // Return if the drawer has already closed
+        if ( !this.opened )
+        {
+            return;
+        }
+
+        // Close the drawer
+        this._toggleOpened(false);
+    }
+
+    /**
+     * Toggle the drawer
+     */
+    toggle(): void
+    {
+        if ( this.opened )
+        {
+            this.close();
+        }
+        else
+        {
+            this.open();
+        }
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Enable the animations
+     *
+     * @private
+     */
+    private _enableAnimations(): void
+    {
+        // Return if the animations are already enabled
+        if ( this._animationsEnabled )
+        {
+            return;
+        }
+
+        // Enable the animations
+        this._animationsEnabled = true;
+    }
+
+    /**
+     * Disable the animations
+     *
+     * @private
+     */
+    private _disableAnimations(): void
+    {
+        // Return if the animations are already disabled
+        if ( !this._animationsEnabled )
+        {
+            return;
+        }
+
+        // Disable the animations
+        this._animationsEnabled = false;
+    }
+
+    /**
+     * Show the backdrop
+     *
+     * @private
+     */
+    private _showOverlay(): void
+    {
+        // Create the backdrop element
+        this._overlay = this._renderer2.createElement('div');
+
+        // Return if overlay couldn't be create for some reason
+        if ( !this._overlay )
+        {
+            return;
+        }
+
+        // Add a class to the backdrop element
+        this._overlay.classList.add('fuse-drawer-overlay');
+
+        // Add a class depending on the fixed option
+        if ( this.fixed )
+        {
+            this._overlay.classList.add('fuse-drawer-overlay-fixed');
+        }
+
+        // Add a class depending on the transparentOverlay option
+        if ( this.transparentOverlay )
+        {
+            this._overlay.classList.add('fuse-drawer-overlay-transparent');
+        }
+
+        // Append the backdrop to the parent of the drawer
+        this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._overlay);
+
+        // Create the enter animation and attach it to the player
+        this._player = this._animationBuilder.build([
+            style({opacity: 0}),
+            animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
+        ]).create(this._overlay);
+
+        // Once the animation is done...
+        this._player.onDone(() => {
+
+            // Destroy the player
+            this._player.destroy();
+            this._player = null;
+        });
+
+        // Play the animation
+        this._player.play();
+
+        // Add an event listener to the overlay
+        this._overlay.addEventListener('click', () => {
+            this.close();
+        });
+    }
+
+    /**
+     * Hide the backdrop
+     *
+     * @private
+     */
+    private _hideOverlay(): void
+    {
+        if ( !this._overlay )
+        {
+            return;
+        }
+
+        // Create the leave animation and attach it to the player
+        this._player = this._animationBuilder.build([
+            animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 0}))
+        ]).create(this._overlay);
+
+        // Play the animation
+        this._player.play();
+
+        // Once the animation is done...
+        this._player.onDone(() => {
+
+            // Destroy the player
+            this._player.destroy();
+            this._player = null;
+
+            // If the backdrop still exists...
+            if ( this._overlay )
+            {
+                // Remove the backdrop
+                this._overlay.parentNode.removeChild(this._overlay);
+                this._overlay = null;
+            }
+        });
+    }
+
+    /**
+     * Open/close the drawer
+     *
+     * @param open
+     * @private
+     */
+    private _toggleOpened(open: boolean): void
+    {
+        // Set the opened
+        this.opened = open;
+
+        // Enable the animations
+        this._enableAnimations();
+
+        // If the mode is 'over'
+        if ( this.mode === 'over' )
+        {
+            // If the drawer opens, show the overlay
+            if ( open )
+            {
+                this._showOverlay();
+            }
+            // Otherwise, close the overlay
+            else
+            {
+                this._hideOverlay();
+            }
+        }
+
+        // Execute the observable
+        this.openedChanged.next(open);
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.module.ts b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d8383a078238e3267be4b26146fc371c708b82b7
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FuseDrawerComponent } from '@fuse/components/drawer/drawer.component';
+
+@NgModule({
+    declarations: [
+        FuseDrawerComponent
+    ],
+    imports     : [
+        CommonModule
+    ],
+    exports     : [
+        FuseDrawerComponent
+    ]
+})
+export class FuseDrawerModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.service.ts b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7de348aa731a9132d97e562b0b137b4c295983d5
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.service.ts
@@ -0,0 +1,52 @@
+import { Injectable } from '@angular/core';
+import { FuseDrawerComponent } from '@fuse/components/drawer/drawer.component';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseDrawerService
+{
+    private _componentRegistry: Map<string, FuseDrawerComponent> = new Map<string, FuseDrawerComponent>();
+
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Register drawer component
+     *
+     * @param name
+     * @param component
+     */
+    registerComponent(name: string, component: FuseDrawerComponent): void
+    {
+        this._componentRegistry.set(name, component);
+    }
+
+    /**
+     * Deregister drawer component
+     *
+     * @param name
+     */
+    deregisterComponent(name: string): void
+    {
+        this._componentRegistry.delete(name);
+    }
+
+    /**
+     * Get drawer component from the registry
+     *
+     * @param name
+     */
+    getComponent(name: string): FuseDrawerComponent | undefined
+    {
+        return this._componentRegistry.get(name);
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.types.ts b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..40e2ee3961f7444801112e962ad8dd744ebc32a4
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/drawer.types.ts
@@ -0,0 +1,7 @@
+export type FuseDrawerMode =
+    | 'over'
+    | 'side';
+
+export type FuseDrawerPosition =
+    | 'left'
+    | 'right';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/index.ts b/transparency_dashboard_frontend/src/@fuse/components/drawer/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3611d47d9dab6df5a30986fa7145c3e3fce6dd55
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/drawer/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/drawer/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/drawer/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2439ec120232d3b4e4ea360623517ec5cc4ed7a2
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/drawer/public-api.ts
@@ -0,0 +1,4 @@
+export * from '@fuse/components/drawer/drawer.component';
+export * from '@fuse/components/drawer/drawer.module';
+export * from '@fuse/components/drawer/drawer.service';
+export * from '@fuse/components/drawer/drawer.types';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.component.html b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..77cb0f1dafc82ce8917684a20e0fe45938e9bdfa
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.component.html
@@ -0,0 +1,12 @@
+<!-- Button -->
+<button
+    mat-icon-button
+    [matTooltip]="tooltip || 'Toggle Fullscreen'"
+    (click)="toggleFullscreen()">
+    <ng-container [ngTemplateOutlet]="iconTpl || defaultIconTpl"></ng-container>
+</button>
+
+<!-- Default icon -->
+<ng-template #defaultIconTpl>
+    <mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon>
+</ng-template>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.component.ts b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38ec191df5c3ba3c317f4179696b4272bb2b246d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.component.ts
@@ -0,0 +1,166 @@
+import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, TemplateRef, ViewEncapsulation } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fullscreen.types';
+
+@Component({
+    selector       : 'fuse-fullscreen',
+    templateUrl    : './fullscreen.component.html',
+    encapsulation  : ViewEncapsulation.None,
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    exportAs       : 'fuseFullscreen'
+})
+export class FuseFullscreenComponent implements OnInit
+{
+    @Input() iconTpl: TemplateRef<any>;
+    @Input() tooltip: string;
+    private _fsDoc: FSDocument;
+    private _fsDocEl: FSDocumentElement;
+    private _isFullscreen: boolean = false;
+
+    /**
+     * Constructor
+     */
+    constructor(@Inject(DOCUMENT) private _document: Document)
+    {
+        this._fsDoc = _document as FSDocument;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        this._fsDocEl = document.documentElement as FSDocumentElement;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Toggle the fullscreen mode
+     */
+    toggleFullscreen(): void
+    {
+        // Check if the fullscreen is open
+        this._isFullscreen = this._getBrowserFullscreenElement() !== null;
+
+        // Toggle the fullscreen
+        if ( this._isFullscreen )
+        {
+            this._closeFullscreen();
+        }
+        else
+        {
+            this._openFullscreen();
+        }
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Get browser's fullscreen element
+     *
+     * @private
+     */
+    private _getBrowserFullscreenElement(): Element
+    {
+        if ( typeof this._fsDoc.fullscreenElement !== 'undefined' )
+        {
+            return this._fsDoc.fullscreenElement;
+        }
+
+        if ( typeof this._fsDoc.mozFullScreenElement !== 'undefined' )
+        {
+            return this._fsDoc.mozFullScreenElement;
+        }
+
+        if ( typeof this._fsDoc.msFullscreenElement !== 'undefined' )
+        {
+            return this._fsDoc.msFullscreenElement;
+        }
+
+        if ( typeof this._fsDoc.webkitFullscreenElement !== 'undefined' )
+        {
+            return this._fsDoc.webkitFullscreenElement;
+        }
+
+        throw new Error('Fullscreen mode is not supported by this browser');
+    }
+
+    /**
+     * Open the fullscreen
+     *
+     * @private
+     */
+    private _openFullscreen(): void
+    {
+        if ( this._fsDocEl.requestFullscreen )
+        {
+            this._fsDocEl.requestFullscreen();
+            return;
+        }
+
+        // Firefox
+        if ( this._fsDocEl.mozRequestFullScreen )
+        {
+            this._fsDocEl.mozRequestFullScreen();
+            return;
+        }
+
+        // Chrome, Safari and Opera
+        if ( this._fsDocEl.webkitRequestFullscreen )
+        {
+            this._fsDocEl.webkitRequestFullscreen();
+            return;
+        }
+
+        // IE/Edge
+        if ( this._fsDocEl.msRequestFullscreen )
+        {
+            this._fsDocEl.msRequestFullscreen();
+            return;
+        }
+    }
+
+    /**
+     * Close the fullscreen
+     *
+     * @private
+     */
+    private _closeFullscreen(): void
+    {
+        if ( this._fsDoc.exitFullscreen )
+        {
+            this._fsDoc.exitFullscreen();
+            return;
+        }
+
+        // Firefox
+        if ( this._fsDoc.mozCancelFullScreen )
+        {
+            this._fsDoc.mozCancelFullScreen();
+            return;
+        }
+
+        // Chrome, Safari and Opera
+        if ( this._fsDoc.webkitExitFullscreen )
+        {
+            this._fsDoc.webkitExitFullscreen();
+            return;
+        }
+
+        // IE/Edge
+        else if ( this._fsDoc.msExitFullscreen )
+        {
+            this._fsDoc.msExitFullscreen();
+            return;
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.module.ts b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2237f2af2b523c55770604c02f5065c8a8ea2a6c
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.module.ts
@@ -0,0 +1,24 @@
+import { NgModule } from '@angular/core';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { FuseFullscreenComponent } from '@fuse/components/fullscreen/fullscreen.component';
+import { CommonModule } from '@angular/common';
+
+@NgModule({
+    declarations: [
+        FuseFullscreenComponent
+    ],
+    imports     : [
+        MatButtonModule,
+        MatIconModule,
+        MatTooltipModule,
+        CommonModule
+    ],
+    exports     : [
+        FuseFullscreenComponent
+    ]
+})
+export class FuseFullscreenModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.types.ts b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0477a1d414c742d9ee8e9c023136019fc4f00f8f
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/fullscreen.types.ts
@@ -0,0 +1,16 @@
+export interface FSDocument extends HTMLDocument
+{
+    mozFullScreenElement?: Element;
+    mozCancelFullScreen?: () => void;
+    msFullscreenElement?: Element;
+    msExitFullscreen?: () => void;
+    webkitFullscreenElement?: Element;
+    webkitExitFullscreen?: () => void;
+}
+
+export interface FSDocumentElement extends HTMLElement
+{
+    mozRequestFullScreen?: () => void;
+    msRequestFullscreen?: () => void;
+    webkitRequestFullscreen?: () => void;
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/fullscreen/index.ts b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..15b41faf99d406fd5343bc83bf8605968f92ff97
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/fullscreen/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/fullscreen/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e6264e3c7c1fe1d57acbf8720a340213f0177413
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/fullscreen/public-api.ts
@@ -0,0 +1,3 @@
+export * from '@fuse/components/fullscreen/fullscreen.component';
+export * from '@fuse/components/fullscreen/fullscreen.module';
+export * from '@fuse/components/fullscreen/fullscreen.types';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.html b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..da345b9bb283a0fe04354a51b6b7d0fe61ff6903
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.html
@@ -0,0 +1,9 @@
+<ng-content></ng-content>
+
+<!-- @formatter:off -->
+<ng-template let-highlightedCode="highlightedCode" let-lang="lang">
+<div class="fuse-highlight fuse-highlight-code-container">
+<pre [ngClass]="'language-' + lang"><code [ngClass]="'language-' + lang" [innerHTML]="highlightedCode"></code></pre>
+</div>
+</ng-template>
+<!-- @formatter:on -->
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.scss b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b433c6ffc787067cd335865d2717f80cc9fbcd34
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.scss
@@ -0,0 +1,3 @@
+textarea[fuse-highlight] {
+    display: none;
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.ts b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13923796eaf8c45d826354907dcfa3e1f4246ead
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.component.ts
@@ -0,0 +1,132 @@
+import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EmbeddedViewRef, Input, OnChanges, Renderer2, SecurityContext, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+import { FuseHighlightService } from '@fuse/components/highlight/highlight.service';
+
+@Component({
+    selector       : 'textarea[fuse-highlight]',
+    templateUrl    : './highlight.component.html',
+    styleUrls      : ['./highlight.component.scss'],
+    encapsulation  : ViewEncapsulation.None,
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    exportAs       : 'fuseHighlight'
+})
+export class FuseHighlightComponent implements OnChanges, AfterViewInit
+{
+    @Input() code: string;
+    @Input() lang: string;
+    @ViewChild(TemplateRef) templateRef: TemplateRef<any>;
+
+    highlightedCode: string;
+    private _viewRef: EmbeddedViewRef<any>;
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _domSanitizer: DomSanitizer,
+        private _elementRef: ElementRef,
+        private _renderer2: Renderer2,
+        private _fuseHighlightService: FuseHighlightService,
+        private _viewContainerRef: ViewContainerRef
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Code & Lang
+        if ( 'code' in changes || 'lang' in changes )
+        {
+            // Return if the viewContainerRef is not available
+            if ( !this._viewContainerRef.length )
+            {
+                return;
+            }
+
+            // Highlight and insert the code
+            this._highlightAndInsert();
+        }
+    }
+
+    /**
+     * After view init
+     */
+    ngAfterViewInit(): void
+    {
+        // Return if there is no language set
+        if ( !this.lang )
+        {
+            return;
+        }
+
+        // If there is no code input, get the code from
+        // the textarea
+        if ( !this.code )
+        {
+            // Get the code
+            this.code = this._elementRef.nativeElement.value;
+        }
+
+        // Highlight and insert
+        this._highlightAndInsert();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Highlight and insert the highlighted code
+     *
+     * @private
+     */
+    private _highlightAndInsert(): void
+    {
+        // Return if the template reference is not available
+        if ( !this.templateRef )
+        {
+            return;
+        }
+
+        // Return if the code or language is not defined
+        if ( !this.code || !this.lang )
+        {
+            return;
+        }
+
+        // Destroy the component if there is already one
+        if ( this._viewRef )
+        {
+            this._viewRef.destroy();
+            this._viewRef = null;
+        }
+
+        // Highlight and sanitize the code just in case
+        this.highlightedCode = this._domSanitizer.sanitize(SecurityContext.HTML, this._fuseHighlightService.highlight(this.code, this.lang));
+
+        // Return if the highlighted code is null
+        if ( this.highlightedCode === null )
+        {
+            return;
+        }
+
+        // Render and insert the template
+        this._viewRef = this._viewContainerRef.createEmbeddedView(this.templateRef, {
+            highlightedCode: this.highlightedCode,
+            lang           : this.lang
+        });
+
+        // Detect the changes
+        this._viewRef.detectChanges();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.module.ts b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a61149d1e3693e7de2cbd28df6adbef4cd9824df
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FuseHighlightComponent } from '@fuse/components/highlight/highlight.component';
+
+@NgModule({
+    declarations: [
+        FuseHighlightComponent
+    ],
+    imports     : [
+        CommonModule
+    ],
+    exports     : [
+        FuseHighlightComponent
+    ]
+})
+export class FuseHighlightModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.service.ts b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bb627bc2f972bc7e588a53bfe7f407d8f618b0e5
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/highlight.service.ts
@@ -0,0 +1,82 @@
+import { Injectable } from '@angular/core';
+import hljs from 'highlight.js';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseHighlightService
+{
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Highlight
+     */
+    highlight(code: string, language: string): string
+    {
+        // Format the code
+        code = this._format(code);
+
+        // Highlight and return the code
+        return hljs.highlight(code, {language}).value;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Remove the empty lines around the code block
+     * and re-align the indentation based on the first
+     * non-whitespace indented character
+     *
+     * @param code
+     * @private
+     */
+    private _format(code: string): string
+    {
+        let indentation = 0;
+
+        // Split the code into lines and store the lines
+        const lines = code.split('\n');
+
+        // Trim the empty lines around the code block
+        while ( lines.length && lines[0].trim() === '' )
+        {
+            lines.shift();
+        }
+
+        while ( lines.length && lines[lines.length - 1].trim() === '' )
+        {
+            lines.pop();
+        }
+
+        // Iterate through the lines
+        lines.filter(line => line.length)
+             .forEach((line, index) => {
+
+                 // Always get the indentation of the first line so we can
+                 // have something to compare with
+                 if ( index === 0 )
+                 {
+                     indentation = line.search(/\S|$/);
+                     return;
+                 }
+
+                 // Look at all the remaining lines to figure out the smallest indentation.
+                 indentation = Math.min(line.search(/\S|$/), indentation);
+             });
+
+        // Iterate through the lines one more time, remove the extra
+        // indentation, join them together and return it
+        return lines.map(line => line.substring(indentation)).join('\n');
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/index.ts b/transparency_dashboard_frontend/src/@fuse/components/highlight/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..46f52e2ef82fc672d88d6dee82541af74dd0a18b
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/highlight/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/highlight/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/highlight/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..15c2edc16a0f9c31d7edaf4d3abc0dcdf4ab8978
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/highlight/public-api.ts
@@ -0,0 +1,3 @@
+export * from '@fuse/components/highlight/highlight.component';
+export * from '@fuse/components/highlight/highlight.module';
+export * from '@fuse/components/highlight/highlight.service';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/loading-bar/index.ts b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..780b263a80304809b232b55fb33cf442c195e168
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/loading-bar/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.html b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3f884fef4634119ad6fdecb6f853dedd914496a3
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.html
@@ -0,0 +1,5 @@
+<ng-container *ngIf="show">
+    <mat-progress-bar
+        [mode]="mode"
+        [value]="progress"></mat-progress-bar>
+</ng-container>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.scss b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..7a46ec0eae3864c8c6612d3ce97ab97c5a770148
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.scss
@@ -0,0 +1,7 @@
+fuse-loading-bar {
+    position: fixed;
+    top: 0;
+    z-index: 999;
+    width: 100%;
+    height: 6px;
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.ts b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..74c0346581f1eda15eb16170fe06a302477a4f77
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.component.ts
@@ -0,0 +1,82 @@
+import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
+import { coerceBooleanProperty } from '@angular/cdk/coercion';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseLoadingService } from '@fuse/services/loading';
+
+@Component({
+    selector     : 'fuse-loading-bar',
+    templateUrl  : './loading-bar.component.html',
+    styleUrls    : ['./loading-bar.component.scss'],
+    encapsulation: ViewEncapsulation.None,
+    exportAs     : 'fuseLoadingBar'
+})
+export class FuseLoadingBarComponent implements OnChanges, OnInit, OnDestroy
+{
+    @Input() autoMode: boolean = true;
+    mode: 'determinate' | 'indeterminate';
+    progress: number = 0;
+    show: boolean = false;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(private _fuseLoadingService: FuseLoadingService)
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Auto mode
+        if ( 'autoMode' in changes )
+        {
+            // Set the auto mode in the service
+            this._fuseLoadingService.setAutoMode(coerceBooleanProperty(changes.autoMode.currentValue));
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Subscribe to the service
+        this._fuseLoadingService.mode$
+            .pipe(takeUntil(this._unsubscribeAll))
+            .subscribe((value) => {
+                this.mode = value;
+            });
+
+        this._fuseLoadingService.progress$
+            .pipe(takeUntil(this._unsubscribeAll))
+            .subscribe((value) => {
+                this.progress = value;
+            });
+
+        this._fuseLoadingService.show$
+            .pipe(takeUntil(this._unsubscribeAll))
+            .subscribe((value) => {
+                this.show = value;
+            });
+
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.module.ts b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b371b8ac3601ecd0166524ee78ea5499a0332737
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/loading-bar.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatProgressBarModule } from '@angular/material/progress-bar';
+import { FuseLoadingBarComponent } from '@fuse/components/loading-bar/loading-bar.component';
+
+@NgModule({
+    declarations: [
+        FuseLoadingBarComponent
+    ],
+    imports     : [
+        CommonModule,
+        MatProgressBarModule
+    ],
+    exports     : [
+        FuseLoadingBarComponent
+    ]
+})
+export class FuseLoadingBarModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/loading-bar/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..471ccd33dfe19a48619d615966abd38718e493e3
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/loading-bar/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/components/loading-bar/loading-bar.component';
+export * from '@fuse/components/loading-bar/loading-bar.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/masonry/index.ts b/transparency_dashboard_frontend/src/@fuse/components/masonry/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b2f1a036d4fac5fb2e5df0333fb01ec0958e70c1
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/masonry/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/masonry/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.component.html b/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4b0a67198f67b33f1b1f36bc1ec428c63f97bb2b
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.component.html
@@ -0,0 +1,3 @@
+<div class="flex">
+    <ng-container *ngTemplateOutlet="columnsTemplate; context: { $implicit: distributedColumns }"></ng-container>
+</div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.component.ts b/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6737ab97f925cdcb0f350c66fe6201a8877f9967
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.component.ts
@@ -0,0 +1,86 @@
+import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core';
+import { fuseAnimations } from '@fuse/animations';
+import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
+
+@Component({
+    selector     : 'fuse-masonry',
+    templateUrl  : './masonry.component.html',
+    encapsulation: ViewEncapsulation.None,
+    animations   : fuseAnimations,
+    exportAs     : 'fuseMasonry'
+})
+export class FuseMasonryComponent implements OnChanges, AfterViewInit
+{
+    @Input() columnsTemplate: TemplateRef<any>;
+    @Input() columns: number;
+    @Input() items: any[] = [];
+    distributedColumns: any[] = [];
+
+    /**
+     * Constructor
+     */
+    constructor(private _fuseMediaWatcherService: FuseMediaWatcherService)
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Columns
+        if ( 'columns' in changes )
+        {
+            // Distribute the items
+            this._distributeItems();
+        }
+
+        // Items
+        if ( 'items' in changes )
+        {
+            // Distribute the items
+            this._distributeItems();
+        }
+    }
+
+    /**
+     * After view init
+     */
+    ngAfterViewInit(): void
+    {
+        // Distribute the items for the first time
+        this._distributeItems();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Distribute items into columns
+     */
+    private _distributeItems(): void
+    {
+        // Return an empty array if there are no items
+        if ( this.items.length === 0 )
+        {
+            this.distributedColumns = [];
+            return;
+        }
+
+        // Prepare the distributed columns array
+        this.distributedColumns = Array.from(Array(this.columns), item => ({items: []}));
+
+        // Distribute the items to columns
+        for ( let i = 0; i < this.items.length; i++ )
+        {
+            this.distributedColumns[i % this.columns].items.push(this.items[i]);
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.module.ts b/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..651c5510a4818a768a96d702dc8d271d2f6172de
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/masonry/masonry.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FuseMasonryComponent } from '@fuse/components/masonry/masonry.component';
+
+@NgModule({
+    declarations: [
+        FuseMasonryComponent
+    ],
+    imports     : [
+        CommonModule
+    ],
+    exports     : [
+        FuseMasonryComponent
+    ]
+})
+export class FuseMasonryModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/masonry/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/masonry/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e074c48cb96ebeb32991d61f71f7d214b3cc3b54
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/masonry/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/components/masonry/masonry.component';
+export * from '@fuse/components/masonry/masonry.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/basic/basic.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/basic/basic.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..48aab97de423ad6941a1abfeb2acd9b77fcaa399
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/basic/basic.component.html
@@ -0,0 +1,126 @@
+<!-- Item wrapper -->
+<div
+    class="fuse-horizontal-navigation-item-wrapper"
+    [class.fuse-horizontal-navigation-item-has-subtitle]="!!item.subtitle"
+    [ngClass]="item.classes?.wrapper">
+
+    <!-- Item with an internal link -->
+    <ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
+        <div
+            class="fuse-horizontal-navigation-item"
+            [ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
+            [routerLink]="[item.link]"
+            [routerLinkActive]="'fuse-horizontal-navigation-item-active'"
+            [routerLinkActiveOptions]="isActiveMatchOptions"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+    <!-- Item with an external link -->
+    <ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
+        <a
+            class="fuse-horizontal-navigation-item"
+            [href]="item.link"
+            [target]="item.target || '_self'"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </a>
+    </ng-container>
+
+    <!-- Item with a function -->
+    <ng-container *ngIf="!item.link && item.function && !item.disabled">
+        <div
+            class="fuse-horizontal-navigation-item"
+            [ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
+            [matTooltip]="item.tooltip || ''"
+            (click)="item.function(item)">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+    <!-- Item with an internal link and function -->
+    <ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
+        <div
+            class="fuse-horizontal-navigation-item"
+            [ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
+            [routerLink]="[item.link]"
+            [routerLinkActive]="'fuse-horizontal-navigation-item-active'"
+            [routerLinkActiveOptions]="isActiveMatchOptions"
+            [matTooltip]="item.tooltip || ''"
+            (click)="item.function(item)">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+    <!-- Item with an external link and function -->
+    <ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
+        <a
+            class="fuse-horizontal-navigation-item"
+            [href]="item.link"
+            [target]="item.target || '_self'"
+            [matTooltip]="item.tooltip || ''"
+            (click)="item.function(item)"
+            mat-menu-item>
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </a>
+    </ng-container>
+
+    <!-- Item with a no link and no function -->
+    <ng-container *ngIf="!item.link && !item.function && !item.disabled">
+        <div
+            class="fuse-horizontal-navigation-item"
+            [ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+    <!-- Item is disabled -->
+    <ng-container *ngIf="item.disabled">
+        <div class="fuse-horizontal-navigation-item fuse-horizontal-navigation-item-disabled">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+</div>
+
+<!-- Item template -->
+<ng-template #itemTemplate>
+
+    <!-- Icon -->
+    <ng-container *ngIf="item.icon">
+        <mat-icon
+            class="fuse-horizontal-navigation-item-icon"
+            [ngClass]="item.classes?.icon"
+            [svgIcon]="item.icon"></mat-icon>
+    </ng-container>
+
+    <!-- Title & Subtitle -->
+    <div class="fuse-horizontal-navigation-item-title-wrapper">
+        <div class="fuse-horizontal-navigation-item-title">
+            <span [ngClass]="item.classes?.title">
+                {{item.title}}
+            </span>
+        </div>
+        <ng-container *ngIf="item.subtitle">
+            <div class="fuse-horizontal-navigation-item-subtitle text-hint">
+                <span [ngClass]="item.classes?.subtitle">
+                    {{item.subtitle}}
+                </span>
+            </div>
+        </ng-container>
+    </div>
+
+    <!-- Badge -->
+    <ng-container *ngIf="item.badge">
+        <div class="fuse-horizontal-navigation-item-badge">
+            <div
+                class="fuse-horizontal-navigation-item-badge-content"
+                [ngClass]="item.badge.classes">
+                {{item.badge.title}}
+            </div>
+        </div>
+    </ng-container>
+
+</ng-template>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/basic/basic.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/basic/basic.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af49292aaddce54bdfd8ad51dcdf49de19ff89ee
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/basic/basic.component.ts
@@ -0,0 +1,81 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { IsActiveMatchOptions } from '@angular/router';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+
+@Component({
+    selector       : 'fuse-horizontal-navigation-basic-item',
+    templateUrl    : './basic.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDestroy
+{
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    isActiveMatchOptions: IsActiveMatchOptions;
+    private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService,
+        private _fuseUtilsService: FuseUtilsService
+    )
+    {
+        // Set the equivalent of {exact: false} as default for active match options.
+        // We are not assigning the item.isActiveMatchOptions directly to the
+        // [routerLinkActiveOptions] because if it's "undefined" initially, the router
+        // will throw an error and stop working.
+        this.isActiveMatchOptions = this._fuseUtilsService.subsetMatchOptions;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Set the "isActiveMatchOptions" either from item's
+        // "isActiveMatchOptions" or the equivalent form of
+        // item's "exactMatch" option
+        this.isActiveMatchOptions =
+            this.item.isActiveMatchOptions ?? this.item.exactMatch
+                ? this._fuseUtilsService.exactMatchOptions
+                : this._fuseUtilsService.subsetMatchOptions;
+
+        // Get the parent navigation component
+        this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseHorizontalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/branch/branch.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/branch/branch.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..87aafa1ee9c24381e9199155c5c6c426d424ce84
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/branch/branch.component.html
@@ -0,0 +1,121 @@
+<ng-container *ngIf="!child">
+    <div
+        [ngClass]="{'fuse-horizontal-navigation-menu-active': trigger.menuOpen,
+                 'fuse-horizontal-navigation-menu-active-forced': item.active}"
+        [matMenuTriggerFor]="matMenu"
+        (onMenuOpen)="triggerChangeDetection()"
+        (onMenuClose)="triggerChangeDetection()"
+        #trigger="matMenuTrigger">
+        <ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
+    </div>
+</ng-container>
+
+<mat-menu
+    class="fuse-horizontal-navigation-menu-panel"
+    [overlapTrigger]="false"
+    #matMenu="matMenu">
+
+    <ng-container *ngFor="let item of item.children; trackBy: trackByFn">
+
+        <!-- Skip the hidden items -->
+        <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+            <!-- Basic -->
+            <ng-container *ngIf="item.type === 'basic'">
+                <div
+                    class="fuse-horizontal-navigation-menu-item"
+                    [disabled]="item.disabled"
+                    mat-menu-item>
+                    <fuse-horizontal-navigation-basic-item
+                        [item]="item"
+                        [name]="name"></fuse-horizontal-navigation-basic-item>
+                </div>
+            </ng-container>
+
+            <!-- Branch: aside, collapsable, group -->
+            <ng-container *ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'">
+                <div
+                    class="fuse-horizontal-navigation-menu-item"
+                    [disabled]="item.disabled"
+                    [matMenuTriggerFor]="branch.matMenu"
+                    mat-menu-item>
+                    <ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
+                    <fuse-horizontal-navigation-branch-item
+                        [child]="true"
+                        [item]="item"
+                        [name]="name"
+                        #branch></fuse-horizontal-navigation-branch-item>
+                </div>
+            </ng-container>
+
+            <!-- Divider -->
+            <ng-container *ngIf="item.type === 'divider'">
+                <div
+                    class="fuse-horizontal-navigation-menu-item"
+                    mat-menu-item>
+                    <fuse-horizontal-navigation-divider-item
+                        [item]="item"
+                        [name]="name"></fuse-horizontal-navigation-divider-item>
+                </div>
+            </ng-container>
+
+        </ng-container>
+
+    </ng-container>
+
+</mat-menu>
+
+<!-- Item template -->
+<ng-template
+    let-item
+    #itemTemplate>
+
+    <div
+        class="fuse-horizontal-navigation-item-wrapper"
+        [class.fuse-horizontal-navigation-item-has-subtitle]="!!item.subtitle"
+        [ngClass]="item.classes?.wrapper">
+
+        <div
+            class="fuse-horizontal-navigation-item"
+            [ngClass]="{'fuse-horizontal-navigation-item-disabled': item.disabled,
+                         'fuse-horizontal-navigation-item-active-forced': item.active}"
+            [matTooltip]="item.tooltip || ''">
+
+            <!-- Icon -->
+            <ng-container *ngIf="item.icon">
+                <mat-icon
+                    class="fuse-horizontal-navigation-item-icon"
+                    [ngClass]="item.classes?.icon"
+                    [svgIcon]="item.icon"></mat-icon>
+            </ng-container>
+
+            <!-- Title & Subtitle -->
+            <div class="fuse-horizontal-navigation-item-title-wrapper">
+                <div class="fuse-horizontal-navigation-item-title">
+                    <span [ngClass]="item.classes?.title">
+                        {{item.title}}
+                    </span>
+                </div>
+                <ng-container *ngIf="item.subtitle">
+                    <div class="fuse-horizontal-navigation-item-subtitle text-hint">
+                        <span [ngClass]="item.classes?.subtitle">
+                            {{item.subtitle}}
+                        </span>
+                    </div>
+                </ng-container>
+            </div>
+
+            <!-- Badge -->
+            <ng-container *ngIf="item.badge">
+                <div class="fuse-horizontal-navigation-item-badge">
+                    <div
+                        class="fuse-horizontal-navigation-item-badge-content"
+                        [ngClass]="item.badge.classes">
+                        {{item.badge.title}}
+                    </div>
+                </div>
+            </ng-container>
+        </div>
+    </div>
+
+</ng-template>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/branch/branch.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/branch/branch.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..69464d8a5c50f9babc8882f3b1919be81acb39d6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/branch/branch.component.ts
@@ -0,0 +1,93 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import { BooleanInput } from '@angular/cdk/coercion';
+import { MatMenu } from '@angular/material/menu';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-horizontal-navigation-branch-item',
+    templateUrl    : './branch.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_child: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() child: boolean = false;
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+    @ViewChild('matMenu', {static: true}) matMenu: MatMenu;
+
+    private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseHorizontalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Trigger the change detection
+     */
+    triggerChangeDetection(): void
+    {
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+    }
+
+    /**
+     * Track by function for ngFor loops
+     *
+     * @param index
+     * @param item
+     */
+    trackByFn(index: number, item: any): any
+    {
+        return item.id || index;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/divider/divider.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/divider/divider.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5675966aad48927f1da89d3c695a95a3e5531542
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/divider/divider.component.html
@@ -0,0 +1,4 @@
+<!-- Divider -->
+<div
+    class="fuse-horizontal-navigation-item-wrapper divider"
+    [ngClass]="item.classes?.wrapper"></div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/divider/divider.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/divider/divider.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06fb7087087e1591f47808ea3a8bad6ada64e597
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/divider/divider.component.ts
@@ -0,0 +1,61 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-horizontal-navigation-divider-item',
+    templateUrl    : './divider.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseHorizontalNavigationDividerItemComponent implements OnInit, OnDestroy
+{
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseHorizontalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/spacer/spacer.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/spacer/spacer.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..97fbd30f5fa6029a262d8be84f0dc83cb9920f3c
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/spacer/spacer.component.html
@@ -0,0 +1,4 @@
+<!-- Spacer -->
+<div
+    class="fuse-horizontal-navigation-item-wrapper"
+    [ngClass]="item.classes?.wrapper"></div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/spacer/spacer.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/spacer/spacer.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f05107d5f0a7d19af8871d8a87ffa7f8c7793b60
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/components/spacer/spacer.component.ts
@@ -0,0 +1,61 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-horizontal-navigation-spacer-item',
+    templateUrl    : './spacer.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseHorizontalNavigationSpacerItemComponent implements OnInit, OnDestroy
+{
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseHorizontalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7db23d9950f8bddcaaa48f827ffa363ee2b7e8b6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.html
@@ -0,0 +1,36 @@
+<div class="fuse-horizontal-navigation-wrapper">
+
+    <ng-container *ngFor="let item of navigation; trackBy: trackByFn">
+
+        <!-- Skip the hidden items -->
+        <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+            <!-- Basic -->
+            <ng-container *ngIf="item.type === 'basic'">
+                <fuse-horizontal-navigation-basic-item
+                    class="fuse-horizontal-navigation-menu-item"
+                    [item]="item"
+                    [name]="name"></fuse-horizontal-navigation-basic-item>
+            </ng-container>
+
+            <!-- Branch: aside, collapsable, group -->
+            <ng-container *ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'">
+                <fuse-horizontal-navigation-branch-item
+                    class="fuse-horizontal-navigation-menu-item"
+                    [item]="item"
+                    [name]="name"></fuse-horizontal-navigation-branch-item>
+            </ng-container>
+
+            <!-- Spacer -->
+            <ng-container *ngIf="item.type === 'spacer'">
+                <fuse-horizontal-navigation-spacer-item
+                    class="fuse-horizontal-navigation-menu-item"
+                    [item]="item"
+                    [name]="name"></fuse-horizontal-navigation-spacer-item>
+            </ng-container>
+
+        </ng-container>
+
+    </ng-container>
+
+</div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.scss b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..78e2efdfb897f6bfb8c766b547bdce98126de8d3
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.scss
@@ -0,0 +1,181 @@
+/* Root navigation specific */
+fuse-horizontal-navigation {
+
+    .fuse-horizontal-navigation-wrapper {
+        display: flex;
+        align-items: center;
+       
+
+        /* Basic, Branch */
+        fuse-horizontal-navigation-basic-item,
+        fuse-horizontal-navigation-branch-item {
+
+            @screen sm {
+
+                &:hover {
+
+                    .fuse-horizontal-navigation-item-wrapper {
+                        @apply bg-hover;
+                    }
+                }
+            }
+
+            .fuse-horizontal-navigation-item-wrapper {
+                border-radius: 4px;
+                overflow: hidden;
+
+                .fuse-horizontal-navigation-item {
+                    padding: 0 16px;
+                    cursor: pointer;
+                    user-select: none;
+
+                    .fuse-horizontal-navigation-item-icon {
+                        margin-right: 12px;
+                    }
+                }
+            }
+        }
+
+        /* Basic - When item active (current link) */
+        fuse-horizontal-navigation-basic-item {
+
+            .fuse-horizontal-navigation-item-active,
+            .fuse-horizontal-navigation-item-active-forced {
+
+                .fuse-horizontal-navigation-item-title {
+                    @apply text-primary #{'!important'};
+                }
+
+                .fuse-horizontal-navigation-item-subtitle {
+                    @apply text-primary-400 #{'!important'};
+
+                    .dark & {
+                        @apply text-primary-600 #{'!important'};
+                    }
+                }
+
+                .fuse-horizontal-navigation-item-icon {
+                    @apply text-primary #{'!important'};
+                }
+            }
+        }
+
+        /* Branch - When menu open */
+        fuse-horizontal-navigation-branch-item {
+
+            .fuse-horizontal-navigation-menu-active,
+            .fuse-horizontal-navigation-menu-active-forced {
+
+                .fuse-horizontal-navigation-item-wrapper {
+                    @apply bg-hover;
+                }
+            }
+        }
+
+        /* Spacer */
+        fuse-horizontal-navigation-spacer-item {
+            margin: 12px 0;
+        }
+    }
+}
+
+/* Menu panel specific */
+.fuse-horizontal-navigation-menu-panel {
+
+    .fuse-horizontal-navigation-menu-item {
+        height: auto;
+        min-height: 0;
+        line-height: normal;
+        white-space: normal;
+
+        /* Basic, Branch */
+        fuse-horizontal-navigation-basic-item,
+        fuse-horizontal-navigation-branch-item,
+        fuse-horizontal-navigation-divider-item {
+            display: flex;
+            flex: 1 1 auto;
+        }
+
+        /* Divider */
+        fuse-horizontal-navigation-divider-item {
+            margin: 8px -16px;
+
+            .fuse-horizontal-navigation-item-wrapper {
+                height: 1px;
+                box-shadow: 0 1px 0 0;
+            }
+        }
+    }
+}
+
+/* Navigation menu item common */
+.fuse-horizontal-navigation-menu-item {
+
+    /* Basic - When item active (current link) */
+    fuse-horizontal-navigation-basic-item {
+
+        .fuse-horizontal-navigation-item-active,
+        .fuse-horizontal-navigation-item-active-forced {
+
+            .fuse-horizontal-navigation-item-title {
+                @apply text-primary #{'!important'};
+            }
+
+            .fuse-horizontal-navigation-item-subtitle {
+                @apply text-primary-400 #{'!important'};
+
+                .dark & {
+                    @apply text-primary-600 #{'!important'};
+                }
+            }
+
+            .fuse-horizontal-navigation-item-icon {
+                @apply text-primary #{'!important'};
+            }
+        }
+    }
+
+    .fuse-horizontal-navigation-item-wrapper {
+        width: 100%;
+
+        &.fuse-horizontal-navigation-item-has-subtitle {
+
+            .fuse-horizontal-navigation-item {
+                min-height: 56px;
+            }
+        }
+
+        .fuse-horizontal-navigation-item {
+            position: relative;
+            display: flex;
+            align-items: center;
+            justify-content: flex-start;
+            min-height: 48px;
+            width: 100%;
+            font-size: 13px;
+            font-weight: 500;
+            text-decoration: none;
+
+            .fuse-horizontal-navigation-item-title-wrapper {
+
+                .fuse-horizontal-navigation-item-subtitle {
+                    font-size: 12px;
+                }
+            }
+
+            .fuse-horizontal-navigation-item-badge {
+                margin-left: auto;
+
+                .fuse-horizontal-navigation-item-badge-content {
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 10px;
+                    font-weight: 600;
+                    white-space: nowrap;
+                    height: 20px;
+                }
+            }
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..96cbd6e9d3b343cf8894ba19c7e2ef4f8c84d93b
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/horizontal/horizontal.component.ts
@@ -0,0 +1,109 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
+import { ReplaySubject, Subject } from 'rxjs';
+import { fuseAnimations } from '@fuse/animations';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+
+@Component({
+    selector       : 'fuse-horizontal-navigation',
+    templateUrl    : './horizontal.component.html',
+    styleUrls      : ['./horizontal.component.scss'],
+    animations     : fuseAnimations,
+    encapsulation  : ViewEncapsulation.None,
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    exportAs       : 'fuseHorizontalNavigation'
+})
+export class FuseHorizontalNavigationComponent implements OnChanges, OnInit, OnDestroy
+{
+    @Input() name: string = this._fuseUtilsService.randomId();
+    @Input() navigation: FuseNavigationItem[];
+
+    onRefreshed: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService,
+        private _fuseUtilsService: FuseUtilsService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Navigation
+        if ( 'navigation' in changes )
+        {
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Make sure the name input is not an empty string
+        if ( this.name === '' )
+        {
+            this.name = this._fuseUtilsService.randomId();
+        }
+
+        // Register the navigation component
+        this._fuseNavigationService.registerComponent(this.name, this);
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Deregister the navigation component from the registry
+        this._fuseNavigationService.deregisterComponent(this.name);
+
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Refresh the component to apply the changes
+     */
+    refresh(): void
+    {
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+
+        // Execute the observable
+        this.onRefreshed.next(true);
+    }
+
+    /**
+     * Track by function for ngFor loops
+     *
+     * @param index
+     * @param item
+     */
+    trackByFn(index: number, item: any): any
+    {
+        return item.id || index;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/index.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2598d801b4309f67c4266f6adbb8fec978bdbe6c
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/components/navigation/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.module.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d93c6b7bb577555338b2f30d9cb0dce704002df1
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.module.ts
@@ -0,0 +1,55 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { FuseScrollbarModule } from '@fuse/directives/scrollbar/public-api';
+import { FuseHorizontalNavigationBasicItemComponent } from '@fuse/components/navigation/horizontal/components/basic/basic.component';
+import { FuseHorizontalNavigationBranchItemComponent } from '@fuse/components/navigation/horizontal/components/branch/branch.component';
+import { FuseHorizontalNavigationDividerItemComponent } from '@fuse/components/navigation/horizontal/components/divider/divider.component';
+import { FuseHorizontalNavigationSpacerItemComponent } from '@fuse/components/navigation/horizontal/components/spacer/spacer.component';
+import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
+import { FuseVerticalNavigationAsideItemComponent } from '@fuse/components/navigation/vertical/components/aside/aside.component';
+import { FuseVerticalNavigationBasicItemComponent } from '@fuse/components/navigation/vertical/components/basic/basic.component';
+import { FuseVerticalNavigationCollapsableItemComponent } from '@fuse/components/navigation/vertical/components/collapsable/collapsable.component';
+import { FuseVerticalNavigationDividerItemComponent } from '@fuse/components/navigation/vertical/components/divider/divider.component';
+import { FuseVerticalNavigationGroupItemComponent } from '@fuse/components/navigation/vertical/components/group/group.component';
+import { FuseVerticalNavigationSpacerItemComponent } from '@fuse/components/navigation/vertical/components/spacer/spacer.component';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+
+@NgModule({
+    declarations: [
+        FuseHorizontalNavigationBasicItemComponent,
+        FuseHorizontalNavigationBranchItemComponent,
+        FuseHorizontalNavigationDividerItemComponent,
+        FuseHorizontalNavigationSpacerItemComponent,
+        FuseHorizontalNavigationComponent,
+        FuseVerticalNavigationAsideItemComponent,
+        FuseVerticalNavigationBasicItemComponent,
+        FuseVerticalNavigationCollapsableItemComponent,
+        FuseVerticalNavigationDividerItemComponent,
+        FuseVerticalNavigationGroupItemComponent,
+        FuseVerticalNavigationSpacerItemComponent,
+        FuseVerticalNavigationComponent
+    ],
+    imports     : [
+        CommonModule,
+        RouterModule,
+        MatButtonModule,
+        MatDividerModule,
+        MatIconModule,
+        MatMenuModule,
+        MatTooltipModule,
+        FuseScrollbarModule
+    ],
+    exports     : [
+        FuseHorizontalNavigationComponent,
+        FuseVerticalNavigationComponent
+    ]
+})
+export class FuseNavigationModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.service.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c5bea807627e2483bc1d048c6c2d0c69118c892
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.service.ts
@@ -0,0 +1,186 @@
+import { Injectable } from '@angular/core';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseNavigationService
+{
+    private _componentRegistry: Map<string, any> = new Map<string, any>();
+    private _navigationStore: Map<string, FuseNavigationItem[]> = new Map<string, any>();
+
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Register navigation component
+     *
+     * @param name
+     * @param component
+     */
+    registerComponent(name: string, component: any): void
+    {
+        this._componentRegistry.set(name, component);
+    }
+
+    /**
+     * Deregister navigation component
+     *
+     * @param name
+     */
+    deregisterComponent(name: string): void
+    {
+        this._componentRegistry.delete(name);
+    }
+
+    /**
+     * Get navigation component from the registry
+     *
+     * @param name
+     */
+    getComponent<T>(name: string): T
+    {
+        return this._componentRegistry.get(name);
+    }
+
+    /**
+     * Store the given navigation with the given key
+     *
+     * @param key
+     * @param navigation
+     */
+    storeNavigation(key: string, navigation: FuseNavigationItem[]): void
+    {
+        // Add to the store
+        this._navigationStore.set(key, navigation);
+    }
+
+    /**
+     * Get navigation from storage by key
+     *
+     * @param key
+     */
+    getNavigation(key: string): FuseNavigationItem[]
+    {
+        return this._navigationStore.get(key) ?? [];
+    }
+
+    /**
+     * Delete the navigation from the storage
+     *
+     * @param key
+     */
+    deleteNavigation(key: string): void
+    {
+        // Check if the navigation exists
+        if ( !this._navigationStore.has(key) )
+        {
+            console.warn(`Navigation with the key '${key}' does not exist in the store.`);
+        }
+
+        // Delete from the storage
+        this._navigationStore.delete(key);
+    }
+
+    /**
+     * Utility function that returns a flattened
+     * version of the given navigation array
+     *
+     * @param navigation
+     * @param flatNavigation
+     */
+    getFlatNavigation(navigation: FuseNavigationItem[], flatNavigation: FuseNavigationItem[] = []): FuseNavigationItem[]
+    {
+        for ( const item of navigation )
+        {
+            if ( item.type === 'basic' )
+            {
+                flatNavigation.push(item);
+                continue;
+            }
+
+            if ( item.type === 'aside' || item.type === 'collapsable' || item.type === 'group' )
+            {
+                if ( item.children )
+                {
+                    this.getFlatNavigation(item.children, flatNavigation);
+                }
+            }
+        }
+
+        return flatNavigation;
+    }
+
+    /**
+     * Utility function that returns the item
+     * with the given id from given navigation
+     *
+     * @param id
+     * @param navigation
+     */
+    getItem(id: string, navigation: FuseNavigationItem[]): FuseNavigationItem | null
+    {
+        for ( const item of navigation )
+        {
+            if ( item.id === id )
+            {
+                return item;
+            }
+
+            if ( item.children )
+            {
+                const childItem = this.getItem(id, item.children);
+
+                if ( childItem )
+                {
+                    return childItem;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Utility function that returns the item's parent
+     * with the given id from given navigation
+     *
+     * @param id
+     * @param navigation
+     * @param parent
+     */
+    getItemParent(
+        id: string,
+        navigation: FuseNavigationItem[],
+        parent: FuseNavigationItem[] | FuseNavigationItem
+    ): FuseNavigationItem[] | FuseNavigationItem | null
+    {
+        for ( const item of navigation )
+        {
+            if ( item.id === id )
+            {
+                return parent;
+            }
+
+            if ( item.children )
+            {
+                const childItem = this.getItemParent(id, item.children, item);
+
+                if ( childItem )
+                {
+                    return childItem;
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.types.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9b6b35045f5d0ba850415d2de979f55916545b12
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/navigation.types.ts
@@ -0,0 +1,57 @@
+import { IsActiveMatchOptions } from '@angular/router';
+
+export interface FuseNavigationItem
+{
+    id?: string;
+    title?: string;
+    subtitle?: string;
+    type:
+        | 'aside'
+        | 'basic'
+        | 'collapsable'
+        | 'divider'
+        | 'group'
+        | 'spacer';
+    hidden?: (item: FuseNavigationItem) => boolean;
+    active?: boolean;
+    disabled?: boolean;
+    tooltip?: string;
+    link?: string;
+    externalLink?: boolean;
+    target?:
+        | '_blank'
+        | '_self'
+        | '_parent'
+        | '_top'
+        | string;
+    exactMatch?: boolean;
+    isActiveMatchOptions?: IsActiveMatchOptions;
+    function?: (item: FuseNavigationItem) => void;
+    classes?: {
+        title?: string;
+        subtitle?: string;
+        icon?: string;
+        wrapper?: string;
+    };
+    icon?: string;
+    badge?: {
+        title?: string;
+        classes?: string;
+    };
+    children?: FuseNavigationItem[];
+    meta?: any;
+}
+
+export type FuseVerticalNavigationAppearance =
+    | 'default'
+    | 'compact'
+    | 'dense'
+    | 'thin';
+
+export type FuseVerticalNavigationMode =
+    | 'over'
+    | 'side';
+
+export type FuseVerticalNavigationPosition =
+    | 'left'
+    | 'right';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/public-api.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..032e3ef674f8821d44baa71e8e8a5ab23269cda7
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/public-api.ts
@@ -0,0 +1,5 @@
+export * from '@fuse/components/navigation/horizontal/horizontal.component';
+export * from '@fuse/components/navigation/vertical/vertical.component';
+export * from '@fuse/components/navigation/navigation.module';
+export * from '@fuse/components/navigation/navigation.service';
+export * from '@fuse/components/navigation/navigation.types';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/aside/aside.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/aside/aside.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..469d7b61a8a6e90053fd27e3cc43878c91bfff52
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/aside/aside.component.html
@@ -0,0 +1,103 @@
+<div
+    class="fuse-vertical-navigation-item-wrapper"
+    [class.fuse-vertical-navigation-item-has-subtitle]="!!item.subtitle"
+    [ngClass]="item.classes?.wrapper">
+
+    <div
+        class="fuse-vertical-navigation-item"
+        [ngClass]="{'fuse-vertical-navigation-item-active': active,
+                    'fuse-vertical-navigation-item-disabled': item.disabled,
+                    'fuse-vertical-navigation-item-active-forced': item.active}"
+        [matTooltip]="item.tooltip || ''">
+
+        <!-- Icon -->
+        <ng-container *ngIf="item.icon">
+            <mat-icon
+                class="fuse-vertical-navigation-item-icon"
+                [ngClass]="item.classes?.icon"
+                [svgIcon]="item.icon"></mat-icon>
+        </ng-container>
+
+        <!-- Title & Subtitle -->
+        <div class="fuse-vertical-navigation-item-title-wrapper">
+            <div class="fuse-vertical-navigation-item-title">
+                <span [ngClass]="item.classes?.title">
+                    {{item.title}}
+                </span>
+            </div>
+            <ng-container *ngIf="item.subtitle">
+                <div class="fuse-vertical-navigation-item-subtitle">
+                    <span [ngClass]="item.classes?.subtitle">
+                        {{item.subtitle}}
+                    </span>
+                </div>
+            </ng-container>
+        </div>
+
+        <!-- Badge -->
+        <ng-container *ngIf="item.badge">
+            <div class="fuse-vertical-navigation-item-badge">
+                <div
+                    class="fuse-vertical-navigation-item-badge-content"
+                    [ngClass]="item.badge.classes">
+                    {{item.badge.title}}
+                </div>
+            </div>
+        </ng-container>
+
+    </div>
+
+</div>
+
+<ng-container *ngIf="!skipChildren">
+
+    <div class="fuse-vertical-navigation-item-children">
+
+        <ng-container *ngFor="let item of item.children; trackBy: trackByFn">
+
+            <!-- Skip the hidden items -->
+            <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+                <!-- Basic -->
+                <ng-container *ngIf="item.type === 'basic'">
+                    <fuse-vertical-navigation-basic-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-basic-item>
+                </ng-container>
+
+                <!-- Collapsable -->
+                <ng-container *ngIf="item.type === 'collapsable'">
+                    <fuse-vertical-navigation-collapsable-item
+                        [item]="item"
+                        [name]="name"
+                        [autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
+                </ng-container>
+
+                <!-- Divider -->
+                <ng-container *ngIf="item.type === 'divider'">
+                    <fuse-vertical-navigation-divider-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-divider-item>
+                </ng-container>
+
+                <!-- Group -->
+                <ng-container *ngIf="item.type === 'group'">
+                    <fuse-vertical-navigation-group-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-group-item>
+                </ng-container>
+
+                <!-- Spacer -->
+                <ng-container *ngIf="item.type === 'spacer'">
+                    <fuse-vertical-navigation-spacer-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-spacer-item>
+                </ng-container>
+
+            </ng-container>
+
+        </ng-container>
+
+    </div>
+
+</ng-container>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/aside/aside.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/aside/aside.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de4c51527e56361f5cfd65b86315297425210219
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/aside/aside.component.ts
@@ -0,0 +1,186 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
+import { NavigationEnd, Router } from '@angular/router';
+import { BooleanInput } from '@angular/cdk/coercion';
+import { filter, Subject, takeUntil } from 'rxjs';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-vertical-navigation-aside-item',
+    templateUrl    : './aside.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_autoCollapse: BooleanInput;
+    static ngAcceptInputType_skipChildren: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() activeItemId: string;
+    @Input() autoCollapse: boolean;
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+    @Input() skipChildren: boolean;
+
+    active: boolean = false;
+    private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _router: Router,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Active item id
+        if ( 'activeItemId' in changes )
+        {
+            // Mark if active
+            this._markIfActive(this._router.url);
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Mark if active
+        this._markIfActive(this._router.url);
+
+        // Attach a listener to the NavigationEnd event
+        this._router.events
+            .pipe(
+                filter((event): event is NavigationEnd => event instanceof NavigationEnd),
+                takeUntil(this._unsubscribeAll)
+            )
+            .subscribe((event: NavigationEnd) => {
+
+                // Mark if active
+                this._markIfActive(event.urlAfterRedirects);
+            });
+
+        // Get the parent navigation component
+        this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseVerticalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Track by function for ngFor loops
+     *
+     * @param index
+     * @param item
+     */
+    trackByFn(index: number, item: any): any
+    {
+        return item.id || index;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Check if the given item has the given url
+     * in one of its children
+     *
+     * @param item
+     * @param currentUrl
+     * @private
+     */
+    private _hasActiveChild(item: FuseNavigationItem, currentUrl: string): boolean
+    {
+        const children = item.children;
+
+        if ( !children )
+        {
+            return false;
+        }
+
+        for ( const child of children )
+        {
+            if ( child.children )
+            {
+                if ( this._hasActiveChild(child, currentUrl) )
+                {
+                    return true;
+                }
+            }
+
+            // Skip items other than 'basic'
+            if ( child.type !== 'basic' )
+            {
+                continue;
+            }
+
+            // Check if the child has a link and is active
+            if ( child.link && this._router.isActive(child.link, child.exactMatch || false) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Decide and mark if the item is active
+     *
+     * @private
+     */
+    private _markIfActive(currentUrl: string): void
+    {
+        // Check if the activeItemId is equals to this item id
+        this.active = this.activeItemId === this.item.id;
+
+        // If the aside has a children that is active,
+        // always mark it as active
+        if ( this._hasActiveChild(this.item, currentUrl) )
+        {
+            this.active = true;
+        }
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/basic/basic.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/basic/basic.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a9a9dd98f48a95b5a2879beba46d2d7a2f39f3a8
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/basic/basic.component.html
@@ -0,0 +1,127 @@
+<!-- Item wrapper -->
+<div
+    class="fuse-vertical-navigation-item-wrapper"
+    [class.fuse-vertical-navigation-item-has-subtitle]="!!item.subtitle"
+    [ngClass]="item.classes?.wrapper">
+
+    <!-- Item with an internal link -->
+    <ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
+        <a
+            class="fuse-vertical-navigation-item"
+            [ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
+            [routerLink]="[item.link]"
+            [routerLinkActive]="'fuse-vertical-navigation-item-active'"
+            [routerLinkActiveOptions]="isActiveMatchOptions"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </a>
+    </ng-container>
+
+    <!-- Item with an external link -->
+    <ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
+        <a
+            class="fuse-vertical-navigation-item"
+            [href]="item.link"
+            [target]="item.target || '_self'"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </a>
+    </ng-container>
+
+    <!-- Item with a function -->
+    <ng-container *ngIf="!item.link && item.function && !item.disabled">
+        <div
+            class="fuse-vertical-navigation-item"
+            [ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
+            [matTooltip]="item.tooltip || ''"
+            (click)="item.function(item)">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+    <!-- Item with an internal link and function -->
+    <ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
+        <a
+            class="fuse-vertical-navigation-item"
+            [ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
+            [routerLink]="[item.link]"
+            [routerLinkActive]="'fuse-vertical-navigation-item-active'"
+            [routerLinkActiveOptions]="isActiveMatchOptions"
+            [matTooltip]="item.tooltip || ''"
+            (click)="item.function(item)">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </a>
+    </ng-container>
+
+    <!-- Item with an external link and function -->
+    <ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
+        <a
+            class="fuse-vertical-navigation-item"
+            [href]="item.link"
+            [target]="item.target || '_self'"
+            [matTooltip]="item.tooltip || ''"
+            (click)="item.function(item)">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </a>
+    </ng-container>
+
+    <!-- Item with a no link and no function -->
+    <ng-container *ngIf="!item.link && !item.function && !item.disabled">
+        <div
+            class="fuse-vertical-navigation-item"
+            [ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+    <!-- Item is disabled -->
+    <ng-container *ngIf="item.disabled">
+        <div
+            class="fuse-vertical-navigation-item fuse-vertical-navigation-item-disabled"
+            [matTooltip]="item.tooltip || ''">
+            <ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
+        </div>
+    </ng-container>
+
+</div>
+
+<!-- Item template -->
+<ng-template #itemTemplate>
+
+    <!-- Icon -->
+    <ng-container *ngIf="item.icon">
+        <mat-icon
+            class="fuse-vertical-navigation-item-icon"
+            [ngClass]="item.classes?.icon"
+            [svgIcon]="item.icon"></mat-icon>
+    </ng-container>
+
+    <!-- Title & Subtitle -->
+    <div class="fuse-vertical-navigation-item-title-wrapper">
+        <div class="fuse-vertical-navigation-item-title">
+            <span [ngClass]="item.classes?.title">
+                {{item.title}}
+            </span>
+        </div>
+        <ng-container *ngIf="item.subtitle">
+            <div class="fuse-vertical-navigation-item-subtitle">
+                <span [ngClass]="item.classes?.subtitle">
+                    {{item.subtitle}}
+                </span>
+            </div>
+        </ng-container>
+    </div>
+
+    <!-- Badge -->
+    <ng-container *ngIf="item.badge">
+        <div class="fuse-vertical-navigation-item-badge">
+            <div
+                class="fuse-vertical-navigation-item-badge-content"
+                [ngClass]="item.badge.classes">
+                {{item.badge.title}}
+            </div>
+        </div>
+    </ng-container>
+
+</ng-template>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/basic/basic.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/basic/basic.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5cf689a3b3892889357c4c19538b25770f816a36
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/basic/basic.component.ts
@@ -0,0 +1,81 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { IsActiveMatchOptions } from '@angular/router';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+
+@Component({
+    selector       : 'fuse-vertical-navigation-basic-item',
+    templateUrl    : './basic.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestroy
+{
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    isActiveMatchOptions: IsActiveMatchOptions;
+    private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService,
+        private _fuseUtilsService: FuseUtilsService
+    )
+    {
+        // Set the equivalent of {exact: false} as default for active match options.
+        // We are not assigning the item.isActiveMatchOptions directly to the
+        // [routerLinkActiveOptions] because if it's "undefined" initially, the router
+        // will throw an error and stop working.
+        this.isActiveMatchOptions = this._fuseUtilsService.subsetMatchOptions;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Set the "isActiveMatchOptions" either from item's
+        // "isActiveMatchOptions" or the equivalent form of
+        // item's "exactMatch" option
+        this.isActiveMatchOptions =
+            this.item.isActiveMatchOptions ?? this.item.exactMatch
+                ? this._fuseUtilsService.exactMatchOptions
+                : this._fuseUtilsService.subsetMatchOptions;
+
+        // Get the parent navigation component
+        this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseVerticalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/collapsable/collapsable.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/collapsable/collapsable.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..770efdb3ab161c66163e6c7fb501e4138db7ed70
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/collapsable/collapsable.component.html
@@ -0,0 +1,106 @@
+<div
+    class="fuse-vertical-navigation-item-wrapper"
+    [class.fuse-vertical-navigation-item-has-subtitle]="!!item.subtitle"
+    [ngClass]="item.classes?.wrapper">
+
+    <div
+        class="fuse-vertical-navigation-item"
+        [ngClass]="{'fuse-vertical-navigation-item-disabled': item.disabled}"
+        [matTooltip]="item.tooltip || ''"
+        (click)="toggleCollapsable()">
+
+        <!-- Icon -->
+        <ng-container *ngIf="item.icon">
+            <mat-icon
+                class="fuse-vertical-navigation-item-icon"
+                [ngClass]="item.classes?.icon"
+                [svgIcon]="item.icon"></mat-icon>
+        </ng-container>
+
+        <!-- Title & Subtitle -->
+        <div class="fuse-vertical-navigation-item-title-wrapper">
+            <div class="fuse-vertical-navigation-item-title">
+                <span [ngClass]="item.classes?.title">
+                    {{item.title}}
+                </span>
+            </div>
+            <ng-container *ngIf="item.subtitle">
+                <div class="fuse-vertical-navigation-item-subtitle">
+                    <span [ngClass]="item.classes?.subtitle">
+                        {{item.subtitle}}
+                    </span>
+                </div>
+            </ng-container>
+        </div>
+
+        <!-- Badge -->
+        <ng-container *ngIf="item.badge">
+            <div class="fuse-vertical-navigation-item-badge">
+                <div
+                    class="fuse-vertical-navigation-item-badge-content"
+                    [ngClass]="item.badge.classes">
+                    {{item.badge.title}}
+                </div>
+            </div>
+        </ng-container>
+
+        <!-- Arrow -->
+        <mat-icon
+            class="fuse-vertical-navigation-item-arrow icon-size-4"
+            [svgIcon]="'heroicons_solid:chevron-right'"></mat-icon>
+
+    </div>
+
+</div>
+
+<div
+    class="fuse-vertical-navigation-item-children"
+    *ngIf="!isCollapsed"
+    @expandCollapse>
+
+    <ng-container *ngFor="let item of item.children; trackBy: trackByFn">
+
+        <!-- Skip the hidden items -->
+        <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+            <!-- Basic -->
+            <ng-container *ngIf="item.type === 'basic'">
+                <fuse-vertical-navigation-basic-item
+                    [item]="item"
+                    [name]="name"></fuse-vertical-navigation-basic-item>
+            </ng-container>
+
+            <!-- Collapsable -->
+            <ng-container *ngIf="item.type === 'collapsable'">
+                <fuse-vertical-navigation-collapsable-item
+                    [item]="item"
+                    [name]="name"
+                    [autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
+            </ng-container>
+
+            <!-- Divider -->
+            <ng-container *ngIf="item.type === 'divider'">
+                <fuse-vertical-navigation-divider-item
+                    [item]="item"
+                    [name]="name"></fuse-vertical-navigation-divider-item>
+            </ng-container>
+
+            <!-- Group -->
+            <ng-container *ngIf="item.type === 'group'">
+                <fuse-vertical-navigation-group-item
+                    [item]="item"
+                    [name]="name"></fuse-vertical-navigation-group-item>
+            </ng-container>
+
+            <!-- Spacer -->
+            <ng-container *ngIf="item.type === 'spacer'">
+                <fuse-vertical-navigation-spacer-item
+                    [item]="item"
+                    [name]="name"></fuse-vertical-navigation-spacer-item>
+            </ng-container>
+
+        </ng-container>
+
+    </ng-container>
+
+</div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/collapsable/collapsable.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/collapsable/collapsable.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68d6c38be5c9861eaa0081b3f175cfb316105459
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/collapsable/collapsable.component.ts
@@ -0,0 +1,345 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
+import { NavigationEnd, Router } from '@angular/router';
+import { BooleanInput } from '@angular/cdk/coercion';
+import { filter, Subject, takeUntil } from 'rxjs';
+import { fuseAnimations } from '@fuse/animations';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-vertical-navigation-collapsable-item',
+    templateUrl    : './collapsable.component.html',
+    animations     : fuseAnimations,
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_autoCollapse: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() autoCollapse: boolean;
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    isCollapsed: boolean = true;
+    isExpanded: boolean = false;
+    private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _router: Router,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Host binding for component classes
+     */
+    @HostBinding('class') get classList(): any
+    {
+        return {
+            'fuse-vertical-navigation-item-collapsed': this.isCollapsed,
+            'fuse-vertical-navigation-item-expanded' : this.isExpanded
+        };
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // If the item has a children that has a matching url with the current url, expand...
+        if ( this._hasActiveChild(this.item, this._router.url) )
+        {
+            this.expand();
+        }
+        // Otherwise...
+        else
+        {
+            // If the autoCollapse is on, collapse...
+            if ( this.autoCollapse )
+            {
+                this.collapse();
+            }
+        }
+
+        // Listen for the onCollapsableItemCollapsed from the service
+        this._fuseVerticalNavigationComponent.onCollapsableItemCollapsed
+            .pipe(takeUntil(this._unsubscribeAll))
+            .subscribe((collapsedItem) => {
+
+                // Check if the collapsed item is null
+                if ( collapsedItem === null )
+                {
+                    return;
+                }
+
+                // Collapse if this is a children of the collapsed item
+                if ( this._isChildrenOf(collapsedItem, this.item) )
+                {
+                    this.collapse();
+                }
+            });
+
+        // Listen for the onCollapsableItemExpanded from the service if the autoCollapse is on
+        if ( this.autoCollapse )
+        {
+            this._fuseVerticalNavigationComponent.onCollapsableItemExpanded
+                .pipe(takeUntil(this._unsubscribeAll))
+                .subscribe((expandedItem) => {
+
+                    // Check if the expanded item is null
+                    if ( expandedItem === null )
+                    {
+                        return;
+                    }
+
+                    // Check if this is a parent of the expanded item
+                    if ( this._isChildrenOf(this.item, expandedItem) )
+                    {
+                        return;
+                    }
+
+                    // Check if this has a children with a matching url with the current active url
+                    if ( this._hasActiveChild(this.item, this._router.url) )
+                    {
+                        return;
+                    }
+
+                    // Check if this is the expanded item
+                    if ( this.item === expandedItem )
+                    {
+                        return;
+                    }
+
+                    // If none of the above conditions are matched, collapse this item
+                    this.collapse();
+                });
+        }
+
+        // Attach a listener to the NavigationEnd event
+        this._router.events
+            .pipe(
+                filter((event): event is NavigationEnd => event instanceof NavigationEnd),
+                takeUntil(this._unsubscribeAll)
+            )
+            .subscribe((event: NavigationEnd) => {
+
+                // If the item has a children that has a matching url with the current url, expand...
+                if ( this._hasActiveChild(this.item, event.urlAfterRedirects) )
+                {
+                    this.expand();
+                }
+                // Otherwise...
+                else
+                {
+                    // If the autoCollapse is on, collapse...
+                    if ( this.autoCollapse )
+                    {
+                        this.collapse();
+                    }
+                }
+            });
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseVerticalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Collapse
+     */
+    collapse(): void
+    {
+        // Return if the item is disabled
+        if ( this.item.disabled )
+        {
+            return;
+        }
+
+        // Return if the item is already collapsed
+        if ( this.isCollapsed )
+        {
+            return;
+        }
+
+        // Collapse it
+        this.isCollapsed = true;
+        this.isExpanded = !this.isCollapsed;
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+
+        // Execute the observable
+        this._fuseVerticalNavigationComponent.onCollapsableItemCollapsed.next(this.item);
+    }
+
+    /**
+     * Expand
+     */
+    expand(): void
+    {
+        // Return if the item is disabled
+        if ( this.item.disabled )
+        {
+            return;
+        }
+
+        // Return if the item is already expanded
+        if ( !this.isCollapsed )
+        {
+            return;
+        }
+
+        // Expand it
+        this.isCollapsed = false;
+        this.isExpanded = !this.isCollapsed;
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+
+        // Execute the observable
+        this._fuseVerticalNavigationComponent.onCollapsableItemExpanded.next(this.item);
+    }
+
+    /**
+     * Toggle collapsable
+     */
+    toggleCollapsable(): void
+    {
+        // Toggle collapse/expand
+        if ( this.isCollapsed )
+        {
+            this.expand();
+        }
+        else
+        {
+            this.collapse();
+        }
+    }
+
+    /**
+     * Track by function for ngFor loops
+     *
+     * @param index
+     * @param item
+     */
+    trackByFn(index: number, item: any): any
+    {
+        return item.id || index;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Check if the given item has the given url
+     * in one of its children
+     *
+     * @param item
+     * @param currentUrl
+     * @private
+     */
+    private _hasActiveChild(item: FuseNavigationItem, currentUrl: string): boolean
+    {
+        const children = item.children;
+
+        if ( !children )
+        {
+            return false;
+        }
+
+        for ( const child of children )
+        {
+            if ( child.children )
+            {
+                if ( this._hasActiveChild(child, currentUrl) )
+                {
+                    return true;
+                }
+            }
+
+            // Check if the child has a link and is active
+            if ( child.link && this._router.isActive(child.link, child.exactMatch || false) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if this is a children
+     * of the given item
+     *
+     * @param parent
+     * @param item
+     * @private
+     */
+    private _isChildrenOf(parent: FuseNavigationItem, item: FuseNavigationItem): boolean
+    {
+        const children = parent.children;
+
+        if ( !children )
+        {
+            return false;
+        }
+
+        if ( children.indexOf(item) > -1 )
+        {
+            return true;
+        }
+
+        for ( const child of children )
+        {
+            if ( child.children )
+            {
+                if ( this._isChildrenOf(child, item) )
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/divider/divider.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/divider/divider.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f786705fa178f63422c855c879754d6548b93fb4
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/divider/divider.component.html
@@ -0,0 +1,4 @@
+<!-- Divider -->
+<div
+    class="fuse-vertical-navigation-item-wrapper divider"
+    [ngClass]="item.classes?.wrapper"></div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/divider/divider.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/divider/divider.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..15255b389ff4fef801b82599b39a585ba7dbf34d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/divider/divider.component.ts
@@ -0,0 +1,61 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-vertical-navigation-divider-item',
+    templateUrl    : './divider.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseVerticalNavigationDividerItemComponent implements OnInit, OnDestroy
+{
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseVerticalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/group/group.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/group/group.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..49218739797bb2b5765a51a92d79f7aee5f15983
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/group/group.component.html
@@ -0,0 +1,91 @@
+<!-- Item wrapper -->
+<div
+    class="fuse-vertical-navigation-item-wrapper"
+    [class.fuse-vertical-navigation-item-has-subtitle]="!!item.subtitle"
+    [ngClass]="item.classes?.wrapper">
+
+    <div class="fuse-vertical-navigation-item">
+
+        <!-- Icon -->
+        <ng-container *ngIf="item.icon">
+            <mat-icon
+                class="fuse-vertical-navigation-item-icon"
+                [ngClass]="item.classes?.icon"
+                [svgIcon]="item.icon"></mat-icon>
+        </ng-container>
+
+        <!-- Title & Subtitle -->
+        <div class="fuse-vertical-navigation-item-title-wrapper">
+            <div class="fuse-vertical-navigation-item-title">
+                <span [ngClass]="item.classes?.title">
+                    {{item.title}}
+                </span>
+            </div>
+            <ng-container *ngIf="item.subtitle">
+                <div class="fuse-vertical-navigation-item-subtitle">
+                    <span [ngClass]="item.classes?.subtitle">
+                        {{item.subtitle}}
+                    </span>
+                </div>
+            </ng-container>
+        </div>
+
+        <!-- Badge -->
+        <ng-container *ngIf="item.badge">
+            <div class="fuse-vertical-navigation-item-badge">
+                <div
+                    class="fuse-vertical-navigation-item-badge-content"
+                    [ngClass]="item.badge.classes">
+                    {{item.badge.title}}
+                </div>
+            </div>
+        </ng-container>
+
+    </div>
+
+</div>
+
+<ng-container *ngFor="let item of item.children; trackBy: trackByFn">
+
+    <!-- Skip the hidden items -->
+    <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+        <!-- Basic -->
+        <ng-container *ngIf="item.type === 'basic'">
+            <fuse-vertical-navigation-basic-item
+                [item]="item"
+                [name]="name"></fuse-vertical-navigation-basic-item>
+        </ng-container>
+
+        <!-- Collapsable -->
+        <ng-container *ngIf="item.type === 'collapsable'">
+            <fuse-vertical-navigation-collapsable-item
+                [item]="item"
+                [name]="name"
+                [autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
+        </ng-container>
+
+        <!-- Divider -->
+        <ng-container *ngIf="item.type === 'divider'">
+            <fuse-vertical-navigation-divider-item
+                [item]="item"
+                [name]="name"></fuse-vertical-navigation-divider-item>
+        </ng-container>
+
+        <!-- Group -->
+        <ng-container *ngIf="item.type === 'group'">
+            <fuse-vertical-navigation-group-item
+                [item]="item"
+                [name]="name"></fuse-vertical-navigation-group-item>
+        </ng-container>
+
+        <!-- Spacer -->
+        <ng-container *ngIf="item.type === 'spacer'">
+            <fuse-vertical-navigation-spacer-item
+                [item]="item"
+                [name]="name"></fuse-vertical-navigation-spacer-item>
+        </ng-container>
+
+    </ng-container>
+
+</ng-container>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/group/group.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/group/group.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..290daeec60c8889e6649d6440a08864c338db899
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/group/group.component.ts
@@ -0,0 +1,82 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { BooleanInput } from '@angular/cdk/coercion';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-vertical-navigation-group-item',
+    templateUrl    : './group.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseVerticalNavigationGroupItemComponent implements OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_autoCollapse: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() autoCollapse: boolean;
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseVerticalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Track by function for ngFor loops
+     *
+     * @param index
+     * @param item
+     */
+    trackByFn(index: number, item: any): any
+    {
+        return item.id || index;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/spacer/spacer.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/spacer/spacer.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..719ed674c11494f93c1f186e811607daab5ec47d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/spacer/spacer.component.html
@@ -0,0 +1,4 @@
+<!-- Spacer -->
+<div
+    class="fuse-vertical-navigation-item-wrapper"
+    [ngClass]="item.classes?.wrapper"></div>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/spacer/spacer.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/spacer/spacer.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..925bfc1cd0a748e9b64497fb0d57da08d81dfa54
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/components/spacer/spacer.component.ts
@@ -0,0 +1,61 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
+
+@Component({
+    selector       : 'fuse-vertical-navigation-spacer-item',
+    templateUrl    : './spacer.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FuseVerticalNavigationSpacerItemComponent implements OnInit, OnDestroy
+{
+    @Input() item: FuseNavigationItem;
+    @Input() name: string;
+
+    private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _fuseNavigationService: FuseNavigationService
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Get the parent navigation component
+        this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
+
+        // Subscribe to onRefreshed on the navigation component
+        this._fuseVerticalNavigationComponent.onRefreshed.pipe(
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/compact.scss b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/compact.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e91fbc6636d9c02364052552639b08e9ed6dd9d6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/compact.scss
@@ -0,0 +1,112 @@
+/* Variables */
+:root {
+    --fuse-vertical-navigation-compact-width: 112px;
+}
+
+fuse-vertical-navigation {
+
+    /* Compact appearance overrides */
+    &.fuse-vertical-navigation-appearance-compact {
+        width: var(--fuse-vertical-navigation-compact-width);
+        min-width: var(--fuse-vertical-navigation-compact-width);
+        max-width: var(--fuse-vertical-navigation-compact-width);
+
+        /* Left positioned */
+        &.fuse-vertical-navigation-position-left {
+
+            /* Side mode */
+            &.fuse-vertical-navigation-mode-side {
+                margin-left: calc(var(--fuse-vertical-navigation-compact-width) * -1);
+            }
+
+            /* Opened */
+            &.fuse-vertical-navigation-opened {
+                margin-left: 0;
+            }
+        }
+
+        /* Right positioned */
+        &.fuse-vertical-navigation-position-right {
+
+            /* Side mode */
+            &.fuse-vertical-navigation-mode-side {
+                margin-right: calc(var(--fuse-vertical-navigation-compact-width) * -1);
+            }
+
+            /* Opened */
+            &.fuse-vertical-navigation-opened {
+                margin-right: 0;
+            }
+
+            /* Aside wrapper */
+            .fuse-vertical-navigation-aside-wrapper {
+                left: auto;
+                right: var(--fuse-vertical-navigation-compact-width);
+            }
+        }
+
+        /* Wrapper */
+        .fuse-vertical-navigation-wrapper {
+
+            /* Content */
+            .fuse-vertical-navigation-content {
+
+                > fuse-vertical-navigation-aside-item,
+                > fuse-vertical-navigation-basic-item {
+
+                    .fuse-vertical-navigation-item-wrapper {
+                        margin: 4px 8px 0 8px;
+
+                        .fuse-vertical-navigation-item {
+                            flex-direction: column;
+                            justify-content: center;
+                            padding: 12px;
+                            border-radius: 6px;
+
+                            .fuse-vertical-navigation-item-icon {
+                                margin-right: 0;
+                            }
+
+                            .fuse-vertical-navigation-item-title-wrapper {
+                                margin-top: 8px;
+
+                                .fuse-vertical-navigation-item-title {
+                                    font-size: 12px;
+                                    font-weight: 500;
+                                    text-align: center;
+                                    line-height: 16px;
+                                }
+
+                                .fuse-vertical-navigation-item-subtitle {
+                                    display: none !important;
+                                }
+                            }
+
+                            .fuse-vertical-navigation-item-badge {
+                                position: absolute;
+                                top: 12px;
+                                left: 64px;
+                            }
+                        }
+                    }
+
+                    > fuse-vertical-navigation-collapsable-item {
+                        display: none
+                    }
+
+                    > fuse-vertical-navigation-group-item {
+
+                        > .fuse-vertical-navigation-item-wrapper {
+                            display: none
+                        }
+                    }
+                }
+            }
+        }
+
+        /* Aside wrapper */
+        .fuse-vertical-navigation-aside-wrapper {
+            left: var(--fuse-vertical-navigation-compact-width);
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/default.scss b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/default.scss
new file mode 100644
index 0000000000000000000000000000000000000000..aa0d579247a049cf636e87e1c43e7f6a307ac916
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/default.scss
@@ -0,0 +1,595 @@
+/* Variables */
+:root {
+    --fuse-vertical-navigation-width: 251px;
+}
+
+fuse-vertical-navigation {
+    position: sticky;
+    display: flex;
+    flex-direction: column;
+    flex: 1 0 auto;
+    top: 0;
+    width: var(--fuse-vertical-navigation-width);
+    min-width: var(--fuse-vertical-navigation-width);
+    max-width: var(--fuse-vertical-navigation-width);
+    
+    height: 100vh;
+    min-height: 100vh;
+    max-height: 100vh;
+    z-index: 200;
+
+    /* ----------------------------------------------------------------------------------------------------- */
+    /* @ Navigation Drawer
+    /* ----------------------------------------------------------------------------------------------------- */
+
+    /* Animations */
+    &.fuse-vertical-navigation-animations-enabled {
+        transition-duration: 400ms;
+        transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+        transition-property: visibility, margin-left, margin-right, transform, width, max-width, min-width;
+
+        /* Wrapper */
+        .fuse-vertical-navigation-wrapper {
+            transition-duration: 400ms;
+            transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+            transition-property: width, max-width, min-width;
+        }
+    }
+
+    /* Over mode */
+    &.fuse-vertical-navigation-mode-over {
+        position: fixed;
+        top: 0;
+        bottom: 0;
+    }
+
+    /* Left position */
+    &.fuse-vertical-navigation-position-left {
+
+        /* Side mode */
+        &.fuse-vertical-navigation-mode-side {
+            margin-left: calc(#{var(--fuse-vertical-navigation-width)} * -1);
+
+            &.fuse-vertical-navigation-opened {
+                margin-left: 0;
+            }
+        }
+
+        /* Over mode */
+        &.fuse-vertical-navigation-mode-over {
+            left: 0;
+            transform: translate3d(-100%, 0, 0);
+
+            &.fuse-vertical-navigation-opened {
+                transform: translate3d(0, 0, 0);
+            }
+        }
+
+        /* Wrapper */
+        .fuse-vertical-navigation-wrapper {
+            left: 0;
+        }
+    }
+
+    /* Right position */
+    &.fuse-vertical-navigation-position-right {
+
+        /* Side mode */
+        &.fuse-vertical-navigation-mode-side {
+            margin-right: calc(var(--fuse-vertical-navigation-width) * -1);
+
+            &.fuse-vertical-navigation-opened {
+                margin-right: 0;
+            }
+        }
+
+        /* Over mode */
+        &.fuse-vertical-navigation-mode-over {
+            right: 0;
+            transform: translate3d(100%, 0, 0);
+
+            &.fuse-vertical-navigation-opened {
+                transform: translate3d(0, 0, 0);
+            }
+        }
+
+        /* Wrapper */
+        .fuse-vertical-navigation-wrapper {
+            right: 0;
+        }
+    }
+
+    /* Inner mode */
+    &.fuse-vertical-navigation-inner {
+        position: relative;
+        width: auto;
+        min-width: 0;
+        max-width: none;
+        height: auto;
+        min-height: 0;
+        max-height: none;
+        box-shadow: none;
+
+        .fuse-vertical-navigation-wrapper {
+            position: relative;
+            overflow: visible;
+            height: auto;
+
+            .fuse-vertical-navigation-content {
+                overflow: visible !important;
+            }
+        }
+    }
+
+    /* Wrapper */
+    .fuse-vertical-navigation-wrapper {
+        position: absolute;
+        display: flex;
+        flex: 1 1 auto;
+        flex-direction: column;
+        top: 0;
+        bottom: 0;
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+        z-index: 10;
+        background: inherit;
+        box-shadow: inset -1px 0 0 var(--fuse-border);
+
+        /* Header */
+        .fuse-vertical-navigation-header {
+
+        }
+
+        /* Content */
+        .fuse-vertical-navigation-content {
+            flex: 1 1 auto;
+            overflow-x: hidden;
+            overflow-y: auto;
+            overscroll-behavior: contain;
+
+            /* Divider */
+            > fuse-vertical-navigation-divider-item {
+                margin: 24px 0;
+            }
+
+            /* Group */
+            > fuse-vertical-navigation-group-item {
+                margin-top: 24px;
+            }
+        }
+
+        /* Footer */
+        .fuse-vertical-navigation-footer {
+
+        }
+    }
+
+    /* Aside wrapper */
+    .fuse-vertical-navigation-aside-wrapper {
+        position: absolute;
+        display: flex;
+        flex: 1 1 auto;
+        flex-direction: column;
+        top: 0;
+        bottom: 0;
+        left: var(--fuse-vertical-navigation-width);
+        width: var(--fuse-vertical-navigation-width);
+        height: 100%;
+        z-index: 5;
+        overflow-x: hidden;
+        overflow-y: auto;
+        -webkit-overflow-scrolling: touch;
+        transition-duration: 400ms;
+        transition-property: left, right;
+        transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
+        background: inherit;
+
+        > fuse-vertical-navigation-aside-item {
+            padding: 24px 0;
+
+            /* First item of the aside */
+            > .fuse-vertical-navigation-item-wrapper {
+                display: none !important;
+            }
+        }
+    }
+
+    &.fuse-vertical-navigation-position-right {
+
+        .fuse-vertical-navigation-aside-wrapper {
+            left: auto;
+            right: var(--fuse-vertical-navigation-width);
+        }
+    }
+
+    /* ----------------------------------------------------------------------------------------------------- */
+    /* @ Navigation Items
+    /* ----------------------------------------------------------------------------------------------------- */
+
+    /* Navigation items common */
+    fuse-vertical-navigation-aside-item,
+    fuse-vertical-navigation-basic-item,
+    fuse-vertical-navigation-collapsable-item,
+    fuse-vertical-navigation-divider-item,
+    fuse-vertical-navigation-group-item,
+    fuse-vertical-navigation-spacer-item {
+        display: flex;
+        flex-direction: column;
+        flex: 1 0 auto;
+        user-select: none;
+
+        .fuse-vertical-navigation-item-wrapper {
+
+            .fuse-vertical-navigation-item {
+                position: relative;
+                display: flex;
+                align-items: center;
+                justify-content: flex-start;
+                padding: 20px 16px;
+                font-size: 13px;
+                font-weight: 500;
+                line-height: 20px;
+                text-decoration: none;
+                border-radius: 6px;
+
+                /* Disabled state */
+                &.fuse-vertical-navigation-item-disabled {
+                    cursor: default;
+                    opacity: 0.4;
+                }
+
+                .fuse-vertical-navigation-item-icon {
+                    margin-right: 16px;
+                }
+
+                .fuse-vertical-navigation-item-title-wrapper {
+
+                    .fuse-vertical-navigation-item-subtitle {
+                        font-size: 11px;
+                        line-height: 1.5;
+                    }
+                }
+
+                .fuse-vertical-navigation-item-badge {
+                    margin-left: auto;
+
+                    .fuse-vertical-navigation-item-badge-content {
+                        display: flex;
+                        align-items: center;
+                        justify-content: center;
+                        font-size: 10px;
+                        font-weight: 600;
+                        white-space: nowrap;
+                        height: 20px;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Aside, Basic, Collapsable, Group */
+    fuse-vertical-navigation-aside-item,
+    fuse-vertical-navigation-basic-item,
+    fuse-vertical-navigation-collapsable-item,
+    fuse-vertical-navigation-group-item {
+
+        > .fuse-vertical-navigation-item-wrapper {
+            margin: 0 12px;
+        }
+    }
+
+    /* Aside, Basic, Collapsable */
+    fuse-vertical-navigation-aside-item,
+    fuse-vertical-navigation-basic-item,
+    fuse-vertical-navigation-collapsable-item {
+        margin-bottom: 4px;
+        .fuse-vertical-navigation-item {
+            cursor: pointer;
+        
+        }
+    }
+
+    /* Aside */
+    fuse-vertical-navigation-aside-item {
+
+    }
+
+    /* Basic */
+    fuse-vertical-navigation-basic-item {
+
+    }
+
+    /* Collapsable */
+    fuse-vertical-navigation-collapsable-item {
+
+        > .fuse-vertical-navigation-item-wrapper {
+
+            .fuse-vertical-navigation-item {
+
+                .fuse-vertical-navigation-item-badge {
+
+                    + .fuse-vertical-navigation-item-arrow {
+                        margin-left: 8px;
+                    }
+                }
+
+                .fuse-vertical-navigation-item-arrow {
+                    height: 20px;
+                    line-height: 20px;
+                    margin-left: auto;
+                    transition: transform 300ms cubic-bezier(0.25, 0.8, 0.25, 1),
+                    color 375ms cubic-bezier(0.25, 0.8, 0.25, 1);
+                }
+            }
+        }
+
+        &.fuse-vertical-navigation-item-expanded {
+
+            > .fuse-vertical-navigation-item-wrapper {
+
+                .fuse-vertical-navigation-item {
+
+                    .fuse-vertical-navigation-item-arrow {
+                        transform: rotate(90deg);
+                    }
+                }
+            }
+        }
+
+        > .fuse-vertical-navigation-item-children {
+            margin-top: 6px;
+
+            > *:last-child {
+                padding-bottom: 6px;
+
+                > .fuse-vertical-navigation-item-children {
+
+                    > *:last-child {
+                        padding-bottom: 0;
+                    }
+                }
+            }
+
+            .fuse-vertical-navigation-item {
+                padding: 20px 16px;
+            }
+        }
+
+        /* 1st level */
+        .fuse-vertical-navigation-item-children {
+            overflow: hidden;
+
+            .fuse-vertical-navigation-item {
+                padding-left: 56px;
+            }
+
+            /* 2nd level */
+            .fuse-vertical-navigation-item-children {
+
+                .fuse-vertical-navigation-item {
+                    padding-left: 72px;
+                }
+
+                /* 3rd level */
+                .fuse-vertical-navigation-item-children {
+
+                    .fuse-vertical-navigation-item {
+                        padding-left: 88px;
+                    }
+
+                    /* 4th level */
+                    .fuse-vertical-navigation-item-children {
+
+                        .fuse-vertical-navigation-item {
+                            padding-left: 104px;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* Divider */
+    fuse-vertical-navigation-divider-item {
+        margin: 12px 0;
+
+        .fuse-vertical-navigation-item-wrapper {
+            height: 1px;
+            box-shadow: 0 1px 0 0;
+        }
+    }
+
+    /* Group */
+    fuse-vertical-navigation-group-item {
+
+        > .fuse-vertical-navigation-item-wrapper {
+
+            .fuse-vertical-navigation-item {
+
+                .fuse-vertical-navigation-item-badge,
+                .fuse-vertical-navigation-item-icon {
+                    display: none !important;
+                }
+
+                .fuse-vertical-navigation-item-title-wrapper {
+
+                    .fuse-vertical-navigation-item-title {
+                        font-size: 12px;
+                        font-weight: 600;
+                        letter-spacing: 0.05em;
+                        text-transform: uppercase;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Spacer */
+    fuse-vertical-navigation-spacer-item {
+        margin: 6px 0;
+    }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/* @ Overlay
+/* ----------------------------------------------------------------------------------------------------- */
+.fuse-vertical-navigation-overlay {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 170;
+    opacity: 0;
+    background-color: rgba(0, 0, 0, 0.6);
+
+    + .fuse-vertical-navigation-aside-overlay {
+        background-color: transparent;
+    }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/* @ Aside overlay
+/* ----------------------------------------------------------------------------------------------------- */
+.fuse-vertical-navigation-aside-overlay {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 169;
+    opacity: 0;
+    background-color: rgba(0, 0, 0, 0.3);
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/* @ Navigation Items Colors
+/* ----------------------------------------------------------------------------------------------------- */
+
+/* Navigation items common */
+fuse-vertical-navigation-aside-item,
+fuse-vertical-navigation-basic-item,
+fuse-vertical-navigation-collapsable-item,
+fuse-vertical-navigation-group-item {
+
+    .fuse-vertical-navigation-item-wrapper {
+
+        .fuse-vertical-navigation-item {
+            color: currentColor;
+
+            .fuse-vertical-navigation-item-icon {
+                @apply text-current opacity-60;
+            }
+
+            .fuse-vertical-navigation-item-title-wrapper {
+
+                .fuse-vertical-navigation-item-title {
+                    @apply text-current opacity-80;
+                }
+
+                .fuse-vertical-navigation-item-subtitle {
+                    @apply text-current opacity-50;
+                }
+            }
+        }
+    }
+}
+
+/* Aside, Basic, Collapsable */
+fuse-vertical-navigation-aside-item,
+fuse-vertical-navigation-basic-item,
+fuse-vertical-navigation-collapsable-item {
+
+    > .fuse-vertical-navigation-item-wrapper {
+
+        .fuse-vertical-navigation-item {
+
+            /* Active state */
+            &:not(.fuse-vertical-navigation-item-disabled) {
+
+                &.fuse-vertical-navigation-item-active,
+                &.fuse-vertical-navigation-item-active-forced {
+                    @apply bg-gray-800 bg-opacity-5 dark:bg-white dark:bg-opacity-12;
+
+                    .fuse-vertical-navigation-item-icon {
+                        @apply opacity-100;
+                    }
+
+                    .fuse-vertical-navigation-item-title {
+                        @apply opacity-100;
+                    }
+
+                    .fuse-vertical-navigation-item-subtitle {
+                        @apply opacity-100;
+                    }
+                }
+            }
+
+            /* Hover state */
+            &:not(.fuse-vertical-navigation-item-active-forced):not(.fuse-vertical-navigation-item-active):not(.fuse-vertical-navigation-item-disabled) {
+
+                &:hover {
+                    @apply bg-gray-800 bg-opacity-5 dark:bg-white dark:bg-opacity-12;
+
+                    .fuse-vertical-navigation-item-icon {
+                        @apply opacity-100;
+                    }
+
+                    .fuse-vertical-navigation-item-title,
+                    .fuse-vertical-navigation-item-arrow {
+                        @apply opacity-100;
+                    }
+
+                    .fuse-vertical-navigation-item-subtitle {
+                        @apply opacity-100;
+                    }
+                }
+            }
+        }
+    }
+}
+
+/* Collapsable */
+fuse-vertical-navigation-collapsable-item {
+
+    /* Expanded state */
+    &.fuse-vertical-navigation-item-expanded {
+
+        > .fuse-vertical-navigation-item-wrapper {
+
+            .fuse-vertical-navigation-item {
+
+                .fuse-vertical-navigation-item-icon {
+                    @apply opacity-100;
+                }
+
+                .fuse-vertical-navigation-item-title,
+                .fuse-vertical-navigation-item-arrow {
+                    @apply opacity-100;
+                }
+
+                .fuse-vertical-navigation-item-subtitle {
+                    @apply opacity-100;
+                }
+            }
+        }
+    }
+}
+
+/* Group */
+fuse-vertical-navigation-group-item {
+
+    > .fuse-vertical-navigation-item-wrapper {
+
+        .fuse-vertical-navigation-item {
+
+            .fuse-vertical-navigation-item-title-wrapper {
+
+                .fuse-vertical-navigation-item-title {
+                    @apply opacity-100 text-primary-600 dark:text-primary-400;
+                }
+            }
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/dense.scss b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/dense.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8bbebe249023bb7a25121277c6c36dd0f80dfd33
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/dense.scss
@@ -0,0 +1,194 @@
+/* Variables */
+:root {
+    --fuse-vertical-navigation-width: 280px;
+    --fuse-vertical-navigation-dense-width: 80px;
+}
+
+fuse-vertical-navigation {
+
+    /* Dense appearance overrides */
+    &.fuse-vertical-navigation-appearance-dense {
+
+        &:not(.fuse-vertical-navigation-mode-over) {
+            width: var(--fuse-vertical-navigation-dense-width);
+            min-width: var(--fuse-vertical-navigation-dense-width);
+            max-width: var(--fuse-vertical-navigation-dense-width);
+
+            /* Left positioned */
+            &.fuse-vertical-navigation-position-left {
+
+                /* Side mode */
+                &.fuse-vertical-navigation-mode-side {
+                    margin-left: calc(var(--fuse-vertical-navigation-dense-width) * -1);
+                }
+
+                /* Opened */
+                &.fuse-vertical-navigation-opened {
+                    margin-left: 0;
+                }
+            }
+
+            /* Right positioned */
+            &.fuse-vertical-navigation-position-right {
+
+                /* Side mode */
+                &.fuse-vertical-navigation-mode-side {
+                    margin-right: calc(var(--fuse-vertical-navigation-dense-width) * -1);
+                }
+
+                /* Opened */
+                &.fuse-vertical-navigation-opened {
+                    margin-right: 0;
+                }
+
+                /* Aside wrapper */
+                .fuse-vertical-navigation-aside-wrapper {
+                    left: auto;
+                    right: var(--fuse-vertical-navigation-dense-width);
+                }
+
+                &.fuse-vertical-navigation-hover {
+
+                    .fuse-vertical-navigation-aside-wrapper {
+                        left: auto;
+                        right: var(--fuse-vertical-navigation-width);
+                    }
+                }
+            }
+        }
+
+        /* Wrapper */
+        .fuse-vertical-navigation-wrapper {
+
+            /* Content */
+            .fuse-vertical-navigation-content {
+
+                fuse-vertical-navigation-aside-item,
+                fuse-vertical-navigation-basic-item,
+                fuse-vertical-navigation-collapsable-item,
+                fuse-vertical-navigation-group-item {
+
+                    .fuse-vertical-navigation-item-wrapper {
+
+                        .fuse-vertical-navigation-item {
+                            width: calc(var(--fuse-vertical-navigation-dense-width) - 24px);
+                            min-width: calc(var(--fuse-vertical-navigation-dense-width) - 24px);
+                            max-width: calc(var(--fuse-vertical-navigation-dense-width) - 24px);
+
+                            .fuse-vertical-navigation-item-arrow,
+                            .fuse-vertical-navigation-item-badge,
+                            .fuse-vertical-navigation-item-title-wrapper {
+                                transition: opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
+                            }
+                        }
+                    }
+                }
+
+                fuse-vertical-navigation-group-item {
+
+                    &:first-of-type {
+                        margin-top: 0;
+                    }
+                }
+            }
+        }
+
+        &:not(.fuse-vertical-navigation-hover):not(.fuse-vertical-navigation-mode-over) {
+
+            /* Wrapper */
+            .fuse-vertical-navigation-wrapper {
+
+                /* Content */
+                .fuse-vertical-navigation-content {
+
+                    .fuse-vertical-navigation-item-wrapper {
+
+                        .fuse-vertical-navigation-item {
+                            padding: 10px 16px;
+
+                            .fuse-vertical-navigation-item-arrow,
+                            .fuse-vertical-navigation-item-badge,
+                            .fuse-vertical-navigation-item-title-wrapper {
+                                white-space: nowrap;
+                                opacity: 0;
+                            }
+                        }
+                    }
+
+                    fuse-vertical-navigation-collapsable-item {
+
+                        .fuse-vertical-navigation-item-children {
+                            display: none;
+                        }
+                    }
+
+                    fuse-vertical-navigation-group-item {
+
+                        > .fuse-vertical-navigation-item-wrapper {
+
+                            .fuse-vertical-navigation-item {
+
+                                &:before {
+                                    content: '';
+                                    position: absolute;
+                                    top: 20px;
+                                    width: 23px;
+                                    border-top-width: 2px;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /* Aside wrapper */
+        .fuse-vertical-navigation-aside-wrapper {
+            left: var(--fuse-vertical-navigation-dense-width);
+        }
+
+        /* Hover */
+        &.fuse-vertical-navigation-hover {
+
+            .fuse-vertical-navigation-wrapper {
+                width: var(--fuse-vertical-navigation-width);
+
+                .fuse-vertical-navigation-content {
+
+                    .fuse-vertical-navigation-item-wrapper {
+
+                        .fuse-vertical-navigation-item {
+                            width: calc(var(--fuse-vertical-navigation-width) - 24px);
+                            min-width: calc(var(--fuse-vertical-navigation-width) - 24px);
+                            max-width: calc(var(--fuse-vertical-navigation-width) - 24px);
+
+                            .fuse-vertical-navigation-item-arrow,
+                            .fuse-vertical-navigation-item-badge,
+                            .fuse-vertical-navigation-item-title-wrapper {
+                                white-space: nowrap;
+                                animation: removeWhiteSpaceNoWrap 1ms linear 350ms;
+                                animation-fill-mode: forwards;
+                            }
+                        }
+                    }
+                }
+            }
+
+            .fuse-vertical-navigation-aside-wrapper {
+                left: var(--fuse-vertical-navigation-width);
+            }
+        }
+    }
+}
+
+@keyframes removeWhiteSpaceNoWrap {
+    0% {
+        white-space: nowrap
+    }
+    99% {
+        white-space: nowrap
+    }
+    100% {
+        white-space: normal;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/thin.scss b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/thin.scss
new file mode 100644
index 0000000000000000000000000000000000000000..997bf254aa1c88db457f39f3bfe4ca694e57b63e
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/styles/appearances/thin.scss
@@ -0,0 +1,99 @@
+/* Variables */
+:root {
+    --fuse-vertical-navigation-thin-width: 80px;
+}
+
+fuse-vertical-navigation {
+
+    /* Thin appearance overrides */
+    &.fuse-vertical-navigation-appearance-thin {
+        width: var(--fuse-vertical-navigation-thin-width);
+        min-width: var(--fuse-vertical-navigation-thin-width);
+        max-width: var(--fuse-vertical-navigation-thin-width);
+
+        /* Left positioned */
+        &.fuse-vertical-navigation-position-left {
+
+            &.fuse-vertical-navigation-mode-side {
+                margin-left: calc(var(--fuse-vertical-navigation-thin-width) * -1);
+            }
+
+            &.fuse-vertical-navigation-opened {
+                margin-left: 0;
+            }
+        }
+
+        /* Right positioned */
+        &.fuse-vertical-navigation-position-right {
+
+            &.fuse-vertical-navigation-mode-side {
+                margin-right: calc(var(--fuse-vertical-navigation-thin-width) * -1);
+            }
+
+            &.fuse-vertical-navigation-opened {
+                margin-right: 0;
+            }
+
+            .fuse-vertical-navigation-aside-wrapper {
+                left: auto;
+                right: var(--fuse-vertical-navigation-thin-width);
+            }
+        }
+
+        /* Wrapper */
+        .fuse-vertical-navigation-wrapper {
+
+            /* Content */
+            .fuse-vertical-navigation-content {
+
+                > fuse-vertical-navigation-aside-item,
+                > fuse-vertical-navigation-basic-item {
+                    flex-direction: column;
+                    justify-content: center;
+                    height: 64px;
+                    min-height: 64px;
+                    max-height: 64px;
+                    padding: 0 16px;
+
+                    .fuse-vertical-navigation-item-wrapper {
+                        display: flex;
+                        align-items: center;
+                        justify-content: center;
+
+                        .fuse-vertical-navigation-item {
+                            justify-content: center;
+                            padding: 12px;
+                            border-radius: 4px;
+
+                            .fuse-vertical-navigation-item-icon {
+                                margin: 0;
+                            }
+
+                            .fuse-vertical-navigation-item-arrow,
+                            .fuse-vertical-navigation-item-badge-content,
+                            .fuse-vertical-navigation-item-title-wrapper {
+                                display: none;
+                            }
+                        }
+                    }
+                }
+
+                > fuse-vertical-navigation-collapsable-item {
+                    display: none
+                }
+
+                > fuse-vertical-navigation-group-item {
+
+                    > .fuse-vertical-navigation-item-wrapper {
+                        display: none
+                    }
+                }
+            }
+        }
+
+        /* Aside wrapper */
+        .fuse-vertical-navigation-aside-wrapper {
+            left: var(--fuse-vertical-navigation-thin-width);
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.html b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ef0186e30004c66edb1c88323fb7e5214de43df4
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.html
@@ -0,0 +1,122 @@
+<div class="fuse-vertical-navigation-wrapper">
+
+    <!-- Header -->
+    <div class="fuse-vertical-navigation-header">
+        <ng-content select="[fuseVerticalNavigationHeader]"></ng-content>
+    </div>
+
+    <!-- Content -->
+    <div
+        class="fuse-vertical-navigation-content"
+        fuseScrollbar
+        [fuseScrollbarOptions]="{wheelPropagation: inner, suppressScrollX: true}"
+        #navigationContent>
+
+        <!-- Content header -->
+        <div class="fuse-vertical-navigation-content-header">
+            <ng-content select="[fuseVerticalNavigationContentHeader]"></ng-content>
+        </div>
+
+        <!-- Items -->
+        <ng-container *ngFor="let item of navigation; trackBy: trackByFn">
+
+            <!-- Skip the hidden items -->
+            <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+                <!-- Aside -->
+                <ng-container *ngIf="item.type === 'aside'">
+                    <fuse-vertical-navigation-aside-item
+                        [item]="item"
+                        [name]="name"
+                        [activeItemId]="activeAsideItemId"
+                        [autoCollapse]="autoCollapse"
+                        [skipChildren]="true"
+                        (click)="toggleAside(item)"></fuse-vertical-navigation-aside-item>
+                </ng-container>
+
+                <!-- Basic -->
+                <ng-container *ngIf="item.type === 'basic'">
+                    <fuse-vertical-navigation-basic-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-basic-item>
+                </ng-container>
+
+                <!-- Collapsable -->
+                <ng-container *ngIf="item.type === 'collapsable'">
+                    <fuse-vertical-navigation-collapsable-item
+                        [item]="item"
+                        [name]="name"
+                        [autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
+                </ng-container>
+
+                <!-- Divider -->
+                <ng-container *ngIf="item.type === 'divider'">
+                    <fuse-vertical-navigation-divider-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-divider-item>
+                </ng-container>
+
+                <!-- Group -->
+                <ng-container *ngIf="item.type === 'group'">
+                    <fuse-vertical-navigation-group-item
+                        [item]="item"
+                        [name]="name"
+                        [autoCollapse]="autoCollapse"></fuse-vertical-navigation-group-item>
+                </ng-container>
+
+                <!-- Spacer -->
+                <ng-container *ngIf="item.type === 'spacer'">
+                    <fuse-vertical-navigation-spacer-item
+                        [item]="item"
+                        [name]="name"></fuse-vertical-navigation-spacer-item>
+                </ng-container>
+
+            </ng-container>
+
+        </ng-container>
+
+        <!-- Content footer -->
+        <div class="fuse-vertical-navigation-content-footer">
+            <ng-content select="[fuseVerticalNavigationContentFooter]"></ng-content>
+        </div>
+
+    </div>
+
+    <!-- Footer -->
+    <div class="fuse-vertical-navigation-footer">
+        <ng-content select="[fuseVerticalNavigationFooter]"></ng-content>
+    </div>
+
+</div>
+
+<!-- Aside -->
+<ng-container *ngIf="activeAsideItemId">
+    <div
+        class="fuse-vertical-navigation-aside-wrapper"
+        fuseScrollbar
+        [fuseScrollbarOptions]="{wheelPropagation: false, suppressScrollX: true}"
+        [@fadeInLeft]="position === 'left'"
+        [@fadeInRight]="position === 'right'"
+        [@fadeOutLeft]="position === 'left'"
+        [@fadeOutRight]="position === 'right'">
+
+        <!-- Items -->
+        <ng-container *ngFor="let item of navigation; trackBy: trackByFn">
+
+            <!-- Skip the hidden items -->
+            <ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
+
+                <!-- Aside -->
+                <ng-container *ngIf="item.type === 'aside' && item.id === activeAsideItemId">
+                    <fuse-vertical-navigation-aside-item
+                        [item]="item"
+                        [name]="name"
+                        [autoCollapse]="autoCollapse"></fuse-vertical-navigation-aside-item>
+                </ng-container>
+
+            </ng-container>
+
+        </ng-container>
+
+    </div>
+</ng-container>
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.scss b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8a50cef99a369de87dffed83ae48bdd55ba5ca69
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.scss
@@ -0,0 +1,4 @@
+@import 'styles/appearances/default';
+@import 'styles/appearances/compact';
+@import 'styles/appearances/dense';
+@import 'styles/appearances/thin';
diff --git a/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.ts b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..33722098a91eb1f2cec98d6302795f0d382d23f6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/components/navigation/vertical/vertical.component.ts
@@ -0,0 +1,745 @@
+import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, QueryList, Renderer2, SimpleChanges, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
+import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
+import { NavigationEnd, Router } from '@angular/router';
+import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
+import { delay, filter, merge, ReplaySubject, Subject, Subscription, takeUntil } from 'rxjs';
+import { fuseAnimations } from '@fuse/animations';
+import { FuseNavigationItem, FuseVerticalNavigationAppearance, FuseVerticalNavigationMode, FuseVerticalNavigationPosition } from '@fuse/components/navigation/navigation.types';
+import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
+import { FuseScrollbarDirective } from '@fuse/directives/scrollbar/scrollbar.directive';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+
+@Component({
+    selector       : 'fuse-vertical-navigation',
+    templateUrl    : './vertical.component.html',
+    styleUrls      : ['./vertical.component.scss'],
+    animations     : fuseAnimations,
+    encapsulation  : ViewEncapsulation.None,
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    exportAs       : 'fuseVerticalNavigation'
+})
+export class FuseVerticalNavigationComponent implements OnChanges, OnInit, AfterViewInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_inner: BooleanInput;
+    static ngAcceptInputType_opened: BooleanInput;
+    static ngAcceptInputType_transparentOverlay: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() appearance: FuseVerticalNavigationAppearance = 'default';
+    @Input() autoCollapse: boolean = true;
+    @Input() inner: boolean = false;
+    @Input() mode: FuseVerticalNavigationMode = 'side';
+    @Input() name: string = this._fuseUtilsService.randomId();
+    @Input() navigation: FuseNavigationItem[];
+    @Input() opened: boolean = true;
+    @Input() position: FuseVerticalNavigationPosition = 'left';
+    @Input() transparentOverlay: boolean = false;
+    @Output() readonly appearanceChanged: EventEmitter<FuseVerticalNavigationAppearance> = new EventEmitter<FuseVerticalNavigationAppearance>();
+    @Output() readonly modeChanged: EventEmitter<FuseVerticalNavigationMode> = new EventEmitter<FuseVerticalNavigationMode>();
+    @Output() readonly openedChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
+    @Output() readonly positionChanged: EventEmitter<FuseVerticalNavigationPosition> = new EventEmitter<FuseVerticalNavigationPosition>();
+    @ViewChild('navigationContent') private _navigationContentEl: ElementRef;
+
+    activeAsideItemId: string | null = null;
+    onCollapsableItemCollapsed: ReplaySubject<FuseNavigationItem> = new ReplaySubject<FuseNavigationItem>(1);
+    onCollapsableItemExpanded: ReplaySubject<FuseNavigationItem> = new ReplaySubject<FuseNavigationItem>(1);
+    onRefreshed: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);
+    private _animationsEnabled: boolean = false;
+    private _asideOverlay: HTMLElement;
+    private readonly _handleAsideOverlayClick: any;
+    private readonly _handleOverlayClick: any;
+    private _hovered: boolean = false;
+    private _overlay: HTMLElement;
+    private _player: AnimationPlayer;
+    private _scrollStrategy: ScrollStrategy = this._scrollStrategyOptions.block();
+    private _fuseScrollbarDirectives!: QueryList<FuseScrollbarDirective>;
+    private _fuseScrollbarDirectivesSubscription: Subscription;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _animationBuilder: AnimationBuilder,
+        private _changeDetectorRef: ChangeDetectorRef,
+        private _elementRef: ElementRef,
+        private _renderer2: Renderer2,
+        private _router: Router,
+        private _scrollStrategyOptions: ScrollStrategyOptions,
+        private _fuseNavigationService: FuseNavigationService,
+        private _fuseUtilsService: FuseUtilsService
+    )
+    {
+        this._handleAsideOverlayClick = (): void => {
+            this.closeAside();
+        };
+        this._handleOverlayClick = (): void => {
+            this.close();
+        };
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Host binding for component classes
+     */
+    @HostBinding('class') get classList(): any
+    {
+        return {
+            'fuse-vertical-navigation-animations-enabled'             : this._animationsEnabled,
+            [`fuse-vertical-navigation-appearance-${this.appearance}`]: true,
+            'fuse-vertical-navigation-hover'                          : this._hovered,
+            'fuse-vertical-navigation-inner'                          : this.inner,
+            'fuse-vertical-navigation-mode-over'                      : this.mode === 'over',
+            'fuse-vertical-navigation-mode-side'                      : this.mode === 'side',
+            'fuse-vertical-navigation-opened'                         : this.opened,
+            'fuse-vertical-navigation-position-left'                  : this.position === 'left',
+            'fuse-vertical-navigation-position-right'                 : this.position === 'right'
+        };
+    }
+
+    /**
+     * Host binding for component inline styles
+     */
+    @HostBinding('style') get styleList(): any
+    {
+        return {
+            'visibility': this.opened ? 'visible' : 'hidden'
+        };
+    }
+
+    /**
+     * Setter for fuseScrollbarDirectives
+     */
+    @ViewChildren(FuseScrollbarDirective)
+    set fuseScrollbarDirectives(fuseScrollbarDirectives: QueryList<FuseScrollbarDirective>)
+    {
+        // Store the directives
+        this._fuseScrollbarDirectives = fuseScrollbarDirectives;
+
+        // Return if there are no directives
+        if ( fuseScrollbarDirectives.length === 0 )
+        {
+            return;
+        }
+
+        // Unsubscribe the previous subscriptions
+        if ( this._fuseScrollbarDirectivesSubscription )
+        {
+            this._fuseScrollbarDirectivesSubscription.unsubscribe();
+        }
+
+        // Update the scrollbars on collapsable items' collapse/expand
+        this._fuseScrollbarDirectivesSubscription =
+            merge(
+                this.onCollapsableItemCollapsed,
+                this.onCollapsableItemExpanded
+            )
+                .pipe(
+                    takeUntil(this._unsubscribeAll),
+                    delay(250)
+                )
+                .subscribe(() => {
+
+                    // Loop through the scrollbars and update them
+                    fuseScrollbarDirectives.forEach((fuseScrollbarDirective) => {
+                        fuseScrollbarDirective.update();
+                    });
+                });
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Decorated methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On mouseenter
+     *
+     * @private
+     */
+    @HostListener('mouseenter')
+    private _onMouseenter(): void
+    {
+        // Enable the animations
+        this._enableAnimations();
+
+        // Set the hovered
+        this._hovered = true;
+    }
+
+    /**
+     * On mouseleave
+     *
+     * @private
+     */
+    @HostListener('mouseleave')
+    private _onMouseleave(): void
+    {
+        // Enable the animations
+        this._enableAnimations();
+
+        // Set the hovered
+        this._hovered = false;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Appearance
+        if ( 'appearance' in changes )
+        {
+            // Execute the observable
+            this.appearanceChanged.next(changes.appearance.currentValue);
+        }
+
+        // Inner
+        if ( 'inner' in changes )
+        {
+            // Coerce the value to a boolean
+            this.inner = coerceBooleanProperty(changes.inner.currentValue);
+        }
+
+        // Mode
+        if ( 'mode' in changes )
+        {
+            // Get the previous and current values
+            const currentMode = changes.mode.currentValue;
+            const previousMode = changes.mode.previousValue;
+
+            // Disable the animations
+            this._disableAnimations();
+
+            // If the mode changes: 'over -> side'
+            if ( previousMode === 'over' && currentMode === 'side' )
+            {
+                // Hide the overlay
+                this._hideOverlay();
+            }
+
+            // If the mode changes: 'side -> over'
+            if ( previousMode === 'side' && currentMode === 'over' )
+            {
+                // Close the aside
+                this.closeAside();
+
+                // If the navigation is opened
+                if ( this.opened )
+                {
+                    // Show the overlay
+                    this._showOverlay();
+                }
+            }
+
+            // Execute the observable
+            this.modeChanged.next(currentMode);
+
+            // Enable the animations after a delay
+            // The delay must be bigger than the current transition-duration
+            // to make sure nothing will be animated while the mode changing
+            setTimeout(() => {
+                this._enableAnimations();
+            }, 500);
+        }
+
+        // Navigation
+        if ( 'navigation' in changes )
+        {
+            // Mark for check
+            this._changeDetectorRef.markForCheck();
+        }
+
+        // Opened
+        if ( 'opened' in changes )
+        {
+            // Coerce the value to a boolean
+            this.opened = coerceBooleanProperty(changes.opened.currentValue);
+
+            // Open/close the navigation
+            this._toggleOpened(this.opened);
+        }
+
+        // Position
+        if ( 'position' in changes )
+        {
+            // Execute the observable
+            this.positionChanged.next(changes.position.currentValue);
+        }
+
+        // Transparent overlay
+        if ( 'transparentOverlay' in changes )
+        {
+            // Coerce the value to a boolean
+            this.transparentOverlay = coerceBooleanProperty(changes.transparentOverlay.currentValue);
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Make sure the name input is not an empty string
+        if ( this.name === '' )
+        {
+            this.name = this._fuseUtilsService.randomId();
+        }
+
+        // Register the navigation component
+        this._fuseNavigationService.registerComponent(this.name, this);
+
+        // Subscribe to the 'NavigationEnd' event
+        this._router.events
+            .pipe(
+                filter(event => event instanceof NavigationEnd),
+                takeUntil(this._unsubscribeAll)
+            )
+            .subscribe(() => {
+
+                // If the mode is 'over' and the navigation is opened...
+                if ( this.mode === 'over' && this.opened )
+                {
+                    // Close the navigation
+                    this.close();
+                }
+
+                // If the mode is 'side' and the aside is active...
+                if ( this.mode === 'side' && this.activeAsideItemId )
+                {
+                    // Close the aside
+                    this.closeAside();
+                }
+            });
+    }
+
+    /**
+     * After view init
+     */
+    ngAfterViewInit(): void
+    {
+        setTimeout(() => {
+
+            // Return if 'navigation content' element does not exist
+            if ( !this._navigationContentEl )
+            {
+                return;
+            }
+
+            // If 'navigation content' element doesn't have
+            // perfect scrollbar activated on it...
+            if ( !this._navigationContentEl.nativeElement.classList.contains('ps') )
+            {
+                // Find the active item
+                const activeItem = this._navigationContentEl.nativeElement.querySelector('.fuse-vertical-navigation-item-active');
+
+                // If the active item exists, scroll it into view
+                if ( activeItem )
+                {
+                    activeItem.scrollIntoView();
+                }
+            }
+            // Otherwise
+            else
+            {
+                // Go through all the scrollbar directives
+                this._fuseScrollbarDirectives.forEach((fuseScrollbarDirective) => {
+
+                    // Skip if not enabled
+                    if ( !fuseScrollbarDirective.isEnabled() )
+                    {
+                        return;
+                    }
+
+                    // Scroll to the active element
+                    fuseScrollbarDirective.scrollToElement('.fuse-vertical-navigation-item-active', -120, true);
+                });
+            }
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Forcefully close the navigation and aside in case they are opened
+        this.close();
+        this.closeAside();
+
+        // Deregister the navigation component from the registry
+        this._fuseNavigationService.deregisterComponent(this.name);
+
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Refresh the component to apply the changes
+     */
+    refresh(): void
+    {
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+
+        // Execute the observable
+        this.onRefreshed.next(true);
+    }
+
+    /**
+     * Open the navigation
+     */
+    open(): void
+    {
+        // Return if the navigation is already open
+        if ( this.opened )
+        {
+            return;
+        }
+
+        // Set the opened
+        this._toggleOpened(true);
+    }
+
+    /**
+     * Close the navigation
+     */
+    close(): void
+    {
+        // Return if the navigation is already closed
+        if ( !this.opened )
+        {
+            return;
+        }
+
+        // Close the aside
+        this.closeAside();
+
+        // Set the opened
+        this._toggleOpened(false);
+    }
+
+    /**
+     * Toggle the navigation
+     */
+    toggle(): void
+    {
+        // Toggle
+        if ( this.opened )
+        {
+            this.close();
+        }
+        else
+        {
+            this.open();
+        }
+    }
+
+    /**
+     * Open the aside
+     *
+     * @param item
+     */
+    openAside(item: FuseNavigationItem): void
+    {
+        // Return if the item is disabled
+        if ( item.disabled || !item.id )
+        {
+            return;
+        }
+
+        // Open
+        this.activeAsideItemId = item.id;
+
+        // Show the aside overlay
+        this._showAsideOverlay();
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+    }
+
+    /**
+     * Close the aside
+     */
+    closeAside(): void
+    {
+        // Close
+        this.activeAsideItemId = null;
+
+        // Hide the aside overlay
+        this._hideAsideOverlay();
+
+        // Mark for check
+        this._changeDetectorRef.markForCheck();
+    }
+
+    /**
+     * Toggle the aside
+     *
+     * @param item
+     */
+    toggleAside(item: FuseNavigationItem): void
+    {
+        // Toggle
+        if ( this.activeAsideItemId === item.id )
+        {
+            this.closeAside();
+        }
+        else
+        {
+            this.openAside(item);
+        }
+    }
+
+    /**
+     * Track by function for ngFor loops
+     *
+     * @param index
+     * @param item
+     */
+    trackByFn(index: number, item: any): any
+    {
+        return item.id || index;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Enable the animations
+     *
+     * @private
+     */
+    private _enableAnimations(): void
+    {
+        // Return if the animations are already enabled
+        if ( this._animationsEnabled )
+        {
+            return;
+        }
+
+        // Enable the animations
+        this._animationsEnabled = true;
+    }
+
+    /**
+     * Disable the animations
+     *
+     * @private
+     */
+    private _disableAnimations(): void
+    {
+        // Return if the animations are already disabled
+        if ( !this._animationsEnabled )
+        {
+            return;
+        }
+
+        // Disable the animations
+        this._animationsEnabled = false;
+    }
+
+    /**
+     * Show the overlay
+     *
+     * @private
+     */
+    private _showOverlay(): void
+    {
+        // Return if there is already an overlay
+        if ( this._asideOverlay )
+        {
+            return;
+        }
+
+        // Create the overlay element
+        this._overlay = this._renderer2.createElement('div');
+
+        // Add a class to the overlay element
+        this._overlay.classList.add('fuse-vertical-navigation-overlay');
+
+        // Add a class depending on the transparentOverlay option
+        if ( this.transparentOverlay )
+        {
+            this._overlay.classList.add('fuse-vertical-navigation-overlay-transparent');
+        }
+
+        // Append the overlay to the parent of the navigation
+        this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._overlay);
+
+        // Enable block scroll strategy
+        this._scrollStrategy.enable();
+
+        // Create the enter animation and attach it to the player
+        this._player = this._animationBuilder.build([
+            animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
+        ]).create(this._overlay);
+
+        // Play the animation
+        this._player.play();
+
+        // Add an event listener to the overlay
+        this._overlay.addEventListener('click', this._handleOverlayClick);
+    }
+
+    /**
+     * Hide the overlay
+     *
+     * @private
+     */
+    private _hideOverlay(): void
+    {
+        if ( !this._overlay )
+        {
+            return;
+        }
+
+        // Create the leave animation and attach it to the player
+        this._player = this._animationBuilder.build([
+            animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 0}))
+        ]).create(this._overlay);
+
+        // Play the animation
+        this._player.play();
+
+        // Once the animation is done...
+        this._player.onDone(() => {
+
+            // If the overlay still exists...
+            if ( this._overlay )
+            {
+                // Remove the event listener
+                this._overlay.removeEventListener('click', this._handleOverlayClick);
+
+                // Remove the overlay
+                this._overlay.parentNode.removeChild(this._overlay);
+                this._overlay = null;
+            }
+
+            // Disable block scroll strategy
+            this._scrollStrategy.disable();
+        });
+    }
+
+    /**
+     * Show the aside overlay
+     *
+     * @private
+     */
+    private _showAsideOverlay(): void
+    {
+        // Return if there is already an overlay
+        if ( this._asideOverlay )
+        {
+            return;
+        }
+
+        // Create the aside overlay element
+        this._asideOverlay = this._renderer2.createElement('div');
+
+        // Add a class to the aside overlay element
+        this._asideOverlay.classList.add('fuse-vertical-navigation-aside-overlay');
+
+        // Append the aside overlay to the parent of the navigation
+        this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._asideOverlay);
+
+        // Create the enter animation and attach it to the player
+        this._player =
+            this._animationBuilder
+                .build([
+                    animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
+                ]).create(this._asideOverlay);
+
+        // Play the animation
+        this._player.play();
+
+        // Add an event listener to the aside overlay
+        this._asideOverlay.addEventListener('click', this._handleAsideOverlayClick);
+    }
+
+    /**
+     * Hide the aside overlay
+     *
+     * @private
+     */
+    private _hideAsideOverlay(): void
+    {
+        if ( !this._asideOverlay )
+        {
+            return;
+        }
+
+        // Create the leave animation and attach it to the player
+        this._player =
+            this._animationBuilder
+                .build([
+                    animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 0}))
+                ]).create(this._asideOverlay);
+
+        // Play the animation
+        this._player.play();
+
+        // Once the animation is done...
+        this._player.onDone(() => {
+
+            // If the aside overlay still exists...
+            if ( this._asideOverlay )
+            {
+                // Remove the event listener
+                this._asideOverlay.removeEventListener('click', this._handleAsideOverlayClick);
+
+                // Remove the aside overlay
+                this._asideOverlay.parentNode.removeChild(this._asideOverlay);
+                this._asideOverlay = null;
+            }
+        });
+    }
+
+    /**
+     * Open/close the navigation
+     *
+     * @param open
+     * @private
+     */
+    private _toggleOpened(open: boolean): void
+    {
+        // Set the opened
+        this.opened = open;
+
+        // Enable the animations
+        this._enableAnimations();
+
+        // If the navigation opened, and the mode
+        // is 'over', show the overlay
+        if ( this.mode === 'over' )
+        {
+            if ( this.opened )
+            {
+                this._showOverlay();
+            }
+            else
+            {
+                this._hideOverlay();
+            }
+        }
+
+        // Execute the observable
+        this.openedChanged.next(open);
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/index.ts b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f1f563bc30caece4ee6f3bacc7419b902aa40020
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/directives/scroll-reset/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/public-api.ts b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c394b43c4babdee7513b8604e78c23c62b696d29
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/directives/scroll-reset/scroll-reset.directive';
+export * from '@fuse/directives/scroll-reset/scroll-reset.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/scroll-reset.directive.ts b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/scroll-reset.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab8bae1ef0160f6b872b4a851fa6435a4f3c4c00
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/scroll-reset.directive.ts
@@ -0,0 +1,52 @@
+import { Directive, ElementRef, OnDestroy, OnInit } from '@angular/core';
+import { NavigationEnd, Router } from '@angular/router';
+import { filter, Subject, takeUntil } from 'rxjs';
+
+@Directive({
+    selector: '[fuseScrollReset]',
+    exportAs: 'fuseScrollReset'
+})
+export class FuseScrollResetDirective implements OnInit, OnDestroy
+{
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _elementRef: ElementRef,
+        private _router: Router
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Subscribe to NavigationEnd event
+        this._router.events.pipe(
+            filter(event => event instanceof NavigationEnd),
+            takeUntil(this._unsubscribeAll)
+        ).subscribe(() => {
+
+            // Reset the element's scroll position to the top
+            this._elementRef.nativeElement.scrollTop = 0;
+        });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/scroll-reset.module.ts b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/scroll-reset.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..48715be34602ae9777b47bc1821f7d2c34bee279
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scroll-reset/scroll-reset.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { FuseScrollResetDirective } from '@fuse/directives/scroll-reset/scroll-reset.directive';
+
+@NgModule({
+    declarations: [
+        FuseScrollResetDirective
+    ],
+    exports     : [
+        FuseScrollResetDirective
+    ]
+})
+export class FuseScrollResetModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/index.ts b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9aba5806f70d0c14ddff700b416b4a145896b183
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/directives/scrollbar/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/public-api.ts b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c74ff507417cfcf6ad24d116f33a1557791dc994
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/directives/scrollbar/scrollbar.directive';
+export * from '@fuse/directives/scrollbar/scrollbar.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.directive.ts b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03865f18a121bc6be4367d70466394fa347aa176
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.directive.ts
@@ -0,0 +1,466 @@
+import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
+import { Router } from '@angular/router';
+import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
+import { Platform } from '@angular/cdk/platform';
+import { debounceTime, fromEvent, Subject, takeUntil } from 'rxjs';
+import PerfectScrollbar from 'perfect-scrollbar';
+import { merge } from 'lodash-es';
+import { ScrollbarGeometry, ScrollbarPosition } from '@fuse/directives/scrollbar/scrollbar.types';
+
+/**
+ * Wrapper directive for the Perfect Scrollbar: https://github.com/mdbootstrap/perfect-scrollbar
+ */
+@Directive({
+    selector: '[fuseScrollbar]',
+    exportAs: 'fuseScrollbar'
+})
+export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
+{
+    /* eslint-disable @typescript-eslint/naming-convention */
+    static ngAcceptInputType_fuseScrollbar: BooleanInput;
+    /* eslint-enable @typescript-eslint/naming-convention */
+
+    @Input() fuseScrollbar: boolean = true;
+    @Input() fuseScrollbarOptions: PerfectScrollbar.Options;
+
+    private _animation: number;
+    private _options: PerfectScrollbar.Options;
+    private _ps: PerfectScrollbar;
+    private _unsubscribeAll: Subject<any> = new Subject<any>();
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _elementRef: ElementRef,
+        private _platform: Platform,
+        private _router: Router
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Getter for _elementRef
+     */
+    get elementRef(): ElementRef
+    {
+        return this._elementRef;
+    }
+
+    /**
+     * Getter for _ps
+     */
+    get ps(): PerfectScrollbar | null
+    {
+        return this._ps;
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On changes
+     *
+     * @param changes
+     */
+    ngOnChanges(changes: SimpleChanges): void
+    {
+        // Enabled
+        if ( 'fuseScrollbar' in changes )
+        {
+            // Interpret empty string as 'true'
+            this.fuseScrollbar = coerceBooleanProperty(changes.fuseScrollbar.currentValue);
+
+            // If enabled, init the directive
+            if ( this.fuseScrollbar )
+            {
+                this._init();
+            }
+            // Otherwise destroy it
+            else
+            {
+                this._destroy();
+            }
+        }
+
+        // Scrollbar options
+        if ( 'fuseScrollbarOptions' in changes )
+        {
+            // Merge the options
+            this._options = merge({}, this._options, changes.fuseScrollbarOptions.currentValue);
+
+            // Return if not initialized
+            if ( !this._ps )
+            {
+                return;
+            }
+
+            // Destroy and re-init the PerfectScrollbar to update its options
+            setTimeout(() => {
+                this._destroy();
+            });
+
+            setTimeout(() => {
+                this._init();
+            });
+        }
+    }
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+        // Subscribe to window resize event
+        fromEvent(window, 'resize')
+            .pipe(
+                takeUntil(this._unsubscribeAll),
+                debounceTime(150)
+            )
+            .subscribe(() => {
+
+                // Update the PerfectScrollbar
+                this.update();
+            });
+    }
+
+    /**
+     * On destroy
+     */
+    ngOnDestroy(): void
+    {
+        this._destroy();
+
+        // Unsubscribe from all subscriptions
+        this._unsubscribeAll.next(null);
+        this._unsubscribeAll.complete();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Is enabled
+     */
+    isEnabled(): boolean
+    {
+        return this.fuseScrollbar;
+    }
+
+    /**
+     * Update the scrollbar
+     */
+    update(): void
+    {
+        // Return if not initialized
+        if ( !this._ps )
+        {
+            return;
+        }
+
+        // Update the PerfectScrollbar
+        this._ps.update();
+    }
+
+    /**
+     * Destroy the scrollbar
+     */
+    destroy(): void
+    {
+        this.ngOnDestroy();
+    }
+
+    /**
+     * Returns the geometry of the scrollable element
+     *
+     * @param prefix
+     */
+    geometry(prefix: string = 'scroll'): ScrollbarGeometry
+    {
+        return new ScrollbarGeometry(
+            this._elementRef.nativeElement[prefix + 'Left'],
+            this._elementRef.nativeElement[prefix + 'Top'],
+            this._elementRef.nativeElement[prefix + 'Width'],
+            this._elementRef.nativeElement[prefix + 'Height']);
+    }
+
+    /**
+     * Returns the position of the scrollable element
+     *
+     * @param absolute
+     */
+    position(absolute: boolean = false): ScrollbarPosition
+    {
+        let scrollbarPosition;
+
+        if ( !absolute && this._ps )
+        {
+            scrollbarPosition = new ScrollbarPosition(
+                this._ps.reach.x || 0,
+                this._ps.reach.y || 0
+            );
+        }
+        else
+        {
+            scrollbarPosition = new ScrollbarPosition(
+                this._elementRef.nativeElement.scrollLeft,
+                this._elementRef.nativeElement.scrollTop
+            );
+        }
+
+        return scrollbarPosition;
+    }
+
+    /**
+     * Scroll to
+     *
+     * @param x
+     * @param y
+     * @param speed
+     */
+    scrollTo(x: number, y?: number, speed?: number): void
+    {
+        if ( y == null && speed == null )
+        {
+            this.animateScrolling('scrollTop', x, speed);
+        }
+        else
+        {
+            if ( x != null )
+            {
+                this.animateScrolling('scrollLeft', x, speed);
+            }
+
+            if ( y != null )
+            {
+                this.animateScrolling('scrollTop', y, speed);
+            }
+        }
+    }
+
+    /**
+     * Scroll to X
+     *
+     * @param x
+     * @param speed
+     */
+    scrollToX(x: number, speed?: number): void
+    {
+        this.animateScrolling('scrollLeft', x, speed);
+    }
+
+    /**
+     * Scroll to Y
+     *
+     * @param y
+     * @param speed
+     */
+    scrollToY(y: number, speed?: number): void
+    {
+        this.animateScrolling('scrollTop', y, speed);
+    }
+
+    /**
+     * Scroll to top
+     *
+     * @param offset
+     * @param speed
+     */
+    scrollToTop(offset: number = 0, speed?: number): void
+    {
+        this.animateScrolling('scrollTop', offset, speed);
+    }
+
+    /**
+     * Scroll to bottom
+     *
+     * @param offset
+     * @param speed
+     */
+    scrollToBottom(offset: number = 0, speed?: number): void
+    {
+        const top = this._elementRef.nativeElement.scrollHeight - this._elementRef.nativeElement.clientHeight;
+        this.animateScrolling('scrollTop', top - offset, speed);
+    }
+
+    /**
+     * Scroll to left
+     *
+     * @param offset
+     * @param speed
+     */
+    scrollToLeft(offset: number = 0, speed?: number): void
+    {
+        this.animateScrolling('scrollLeft', offset, speed);
+    }
+
+    /**
+     * Scroll to right
+     *
+     * @param offset
+     * @param speed
+     */
+    scrollToRight(offset: number = 0, speed?: number): void
+    {
+        const left = this._elementRef.nativeElement.scrollWidth - this._elementRef.nativeElement.clientWidth;
+        this.animateScrolling('scrollLeft', left - offset, speed);
+    }
+
+    /**
+     * Scroll to element
+     *
+     * @param qs
+     * @param offset
+     * @param ignoreVisible If true, scrollToElement won't happen if element is already inside the current viewport
+     * @param speed
+     */
+    scrollToElement(qs: string, offset: number = 0, ignoreVisible: boolean = false, speed?: number): void
+    {
+        const element = this._elementRef.nativeElement.querySelector(qs);
+
+        if ( !element )
+        {
+            return;
+        }
+
+        const elementPos = element.getBoundingClientRect();
+        const scrollerPos = this._elementRef.nativeElement.getBoundingClientRect();
+
+        if ( this._elementRef.nativeElement.classList.contains('ps--active-x') )
+        {
+            if ( ignoreVisible && elementPos.right <= (scrollerPos.right - Math.abs(offset)) )
+            {
+                return;
+            }
+
+            const currentPos = this._elementRef.nativeElement['scrollLeft'];
+            const position = elementPos.left - scrollerPos.left + currentPos;
+
+            this.animateScrolling('scrollLeft', position + offset, speed);
+        }
+
+        if ( this._elementRef.nativeElement.classList.contains('ps--active-y') )
+        {
+            if ( ignoreVisible && elementPos.bottom <= (scrollerPos.bottom - Math.abs(offset)) )
+            {
+                return;
+            }
+
+            const currentPos = this._elementRef.nativeElement['scrollTop'];
+            const position = elementPos.top - scrollerPos.top + currentPos;
+
+            this.animateScrolling('scrollTop', position + offset, speed);
+        }
+    }
+
+    /**
+     * Animate scrolling
+     *
+     * @param target
+     * @param value
+     * @param speed
+     */
+    animateScrolling(target: string, value: number, speed?: number): void
+    {
+        if ( this._animation )
+        {
+            window.cancelAnimationFrame(this._animation);
+            this._animation = null;
+        }
+
+        if ( !speed || typeof window === 'undefined' )
+        {
+            this._elementRef.nativeElement[target] = value;
+        }
+        else if ( value !== this._elementRef.nativeElement[target] )
+        {
+            let newValue = 0;
+            let scrollCount = 0;
+
+            let oldTimestamp = performance.now();
+            let oldValue = this._elementRef.nativeElement[target];
+
+            const cosParameter = (oldValue - value) / 2;
+
+            const step = (newTimestamp: number): void => {
+                scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp));
+                newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount));
+
+                // Only continue animation if scroll position has not changed
+                if ( this._elementRef.nativeElement[target] === oldValue )
+                {
+                    if ( scrollCount >= Math.PI )
+                    {
+                        this.animateScrolling(target, value, 0);
+                    }
+                    else
+                    {
+                        this._elementRef.nativeElement[target] = newValue;
+
+                        // On a zoomed out page the resulting offset may differ
+                        oldValue = this._elementRef.nativeElement[target];
+                        oldTimestamp = newTimestamp;
+
+                        this._animation = window.requestAnimationFrame(step);
+                    }
+                }
+            };
+
+            window.requestAnimationFrame(step);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Initialize
+     *
+     * @private
+     */
+    private _init(): void
+    {
+        // Return if already initialized
+        if ( this._ps )
+        {
+            return;
+        }
+
+        // Return if on mobile or not on browser
+        if ( this._platform.ANDROID || this._platform.IOS || !this._platform.isBrowser )
+        {
+            this.fuseScrollbar = false;
+            return;
+        }
+
+        // Initialize the PerfectScrollbar
+        this._ps = new PerfectScrollbar(this._elementRef.nativeElement, {...this._options});
+    }
+
+    /**
+     * Destroy
+     *
+     * @private
+     */
+    private _destroy(): void
+    {
+        // Return if not initialized
+        if ( !this._ps )
+        {
+            return;
+        }
+
+        // Destroy the PerfectScrollbar
+        this._ps.destroy();
+
+        // Clean up
+        this._ps = null;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.module.ts b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..628645b68322f53292d69a97ccca495aad107a44
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { FuseScrollbarDirective } from '@fuse/directives/scrollbar/scrollbar.directive';
+
+@NgModule({
+    declarations: [
+        FuseScrollbarDirective
+    ],
+    exports     : [
+        FuseScrollbarDirective
+    ]
+})
+export class FuseScrollbarModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.types.ts b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11694a95182d4a79ff4599d505ca3fbd603c5e5f
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/directives/scrollbar/scrollbar.types.ts
@@ -0,0 +1,28 @@
+export class ScrollbarGeometry
+{
+    public x: number;
+    public y: number;
+
+    public w: number;
+    public h: number;
+
+    constructor(x: number, y: number, w: number, h: number)
+    {
+        this.x = x;
+        this.y = y;
+        this.w = w;
+        this.h = h;
+    }
+}
+
+export class ScrollbarPosition
+{
+    public x: number | 'start' | 'end';
+    public y: number | 'start' | 'end';
+
+    constructor(x: number | 'start' | 'end', y: number | 'start' | 'end')
+    {
+        this.x = x;
+        this.y = y;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/fuse.module.ts b/transparency_dashboard_frontend/src/@fuse/fuse.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5886f7a461f43a646ee6f6e4d961e2dc0886bd5c
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/fuse.module.ts
@@ -0,0 +1,49 @@
+import { NgModule, Optional, SkipSelf } from '@angular/core';
+import { MATERIAL_SANITY_CHECKS } from '@angular/material/core';
+import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
+import { FuseConfirmationModule } from '@fuse/services/confirmation';
+import { FuseLoadingModule } from '@fuse/services/loading';
+import { FuseMediaWatcherModule } from '@fuse/services/media-watcher/media-watcher.module';
+import { FuseSplashScreenModule } from '@fuse/services/splash-screen/splash-screen.module';
+import { FuseUtilsModule } from '@fuse/services/utils/utils.module';
+
+@NgModule({
+    imports  : [
+        FuseConfirmationModule,
+        FuseLoadingModule,
+        FuseMediaWatcherModule,
+        FuseSplashScreenModule,
+        FuseUtilsModule
+    ],
+    providers: [
+        {
+            // Disable 'theme' sanity check
+            provide : MATERIAL_SANITY_CHECKS,
+            useValue: {
+                doctype: true,
+                theme  : false,
+                version: true
+            }
+        },
+        {
+            // Use the 'fill' appearance on Angular Material form fields by default
+            provide : MAT_FORM_FIELD_DEFAULT_OPTIONS,
+            useValue: {
+                appearance: 'fill'
+            }
+        }
+    ]
+})
+export class FuseModule
+{
+    /**
+     * Constructor
+     */
+    constructor(@Optional() @SkipSelf() parentModule?: FuseModule)
+    {
+        if ( parentModule )
+        {
+            throw new Error('FuseModule has already been loaded. Import this module in the AppModule only!');
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/index.ts b/transparency_dashboard_frontend/src/@fuse/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b172fb6feab6c5b2300dcf5977a6d1fc90d9c7bd
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/index.ts
@@ -0,0 +1 @@
+export * from './fuse.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/index.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..121e0f725edd307a7fafc633b38c99bb6a2a6e06
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/lib/mock-api/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.constants.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de7440fd5dd69916ccb74eb6ddf8cdd4121b2ea4
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.constants.ts
@@ -0,0 +1,3 @@
+import { InjectionToken } from '@angular/core';
+
+export const FUSE_MOCK_API_DEFAULT_DELAY = new InjectionToken<number>('FUSE_MOCK_API_DEFAULT_DELAY');
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.interceptor.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.interceptor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e94bdf1d235eeb8d10e169a412ffc7e25a901dad
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.interceptor.ts
@@ -0,0 +1,96 @@
+import { Inject, Injectable } from '@angular/core';
+import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
+import { delay, Observable, of, switchMap, throwError } from 'rxjs';
+import { FUSE_MOCK_API_DEFAULT_DELAY } from '@fuse/lib/mock-api/mock-api.constants';
+import { FuseMockApiService } from '@fuse/lib/mock-api/mock-api.service';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseMockApiInterceptor implements HttpInterceptor
+{
+    /**
+     * Constructor
+     */
+    constructor(
+        @Inject(FUSE_MOCK_API_DEFAULT_DELAY) private _defaultDelay: number,
+        private _fuseMockApiService: FuseMockApiService
+    )
+    {
+    }
+
+    /**
+     * Intercept
+     *
+     * @param request
+     * @param next
+     */
+    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
+    {
+        // Try to get the request handler
+        const {
+                  handler,
+                  urlParams
+              } = this._fuseMockApiService.findHandler(request.method.toUpperCase(), request.url);
+
+        // Pass through if the request handler does not exist
+        if ( !handler )
+        {
+            return next.handle(request);
+        }
+
+        // Set the intercepted request on the handler
+        handler.request = request;
+
+        // Set the url params on the handler
+        handler.urlParams = urlParams;
+
+        // Subscribe to the response function observable
+        return handler.response.pipe(
+            delay(handler.delay ?? this._defaultDelay ?? 0),
+            switchMap((response) => {
+
+                // If there is no response data,
+                // throw an error response
+                if ( !response )
+                {
+                    response = new HttpErrorResponse({
+                        error     : 'NOT FOUND',
+                        status    : 404,
+                        statusText: 'NOT FOUND'
+                    });
+
+                    return throwError(response);
+                }
+
+                // Parse the response data
+                const data = {
+                    status: response[0],
+                    body  : response[1]
+                };
+
+                // If the status code is in between 200 and 300,
+                // return a success response
+                if ( data.status >= 200 && data.status < 300 )
+                {
+                    response = new HttpResponse({
+                        body      : data.body,
+                        status    : data.status,
+                        statusText: 'OK'
+                    });
+
+                    return of(response);
+                }
+
+                // For other status codes,
+                // throw an error response
+                response = new HttpErrorResponse({
+                    error     : data.body.error,
+                    status    : data.status,
+                    statusText: 'ERROR'
+                });
+
+                return throwError(response);
+            }));
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.module.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff3b2c9380f9464b106f8e2eede8f2c692bdc9fd
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.module.ts
@@ -0,0 +1,56 @@
+import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
+import { HTTP_INTERCEPTORS } from '@angular/common/http';
+import { FUSE_MOCK_API_DEFAULT_DELAY } from '@fuse/lib/mock-api/mock-api.constants';
+import { FuseMockApiInterceptor } from '@fuse/lib/mock-api/mock-api.interceptor';
+import { KeycloakService } from 'keycloak-angular';
+
+@NgModule({
+    providers: [
+        {
+            provide : HTTP_INTERCEPTORS,
+            useClass: FuseMockApiInterceptor,
+            multi   : true
+        }
+    ]
+})
+export class FuseMockApiModule
+{
+    /**
+     * FuseMockApi module default configuration.
+     *
+     * @param mockApiServices - Array of services that register mock API handlers
+     * @param config - Configuration options
+     * @param config.delay - Default delay value in milliseconds to apply all responses
+     */
+    static forRoot(mockApiServices: any[], config?: { delay?: number }): ModuleWithProviders<FuseMockApiModule>
+    {
+        return {
+            ngModule : FuseMockApiModule,
+            providers: [
+                {
+                    provide   : APP_INITIALIZER,
+                    deps      : [...mockApiServices, KeycloakService],
+                    useFactory: (keycloak: KeycloakService) =>
+                    keycloak.init({
+                      config: {
+                        url: 'https://keycloak-security-dev.k8s.across-h2020.eu/auth',
+                        realm: 'across-dev',
+                        clientId: 'transparency-dashboard-frontend'
+                      },
+                      initOptions: {
+                        onLoad: 'login-required',
+                        pkceMethod: 'S256',
+                        enableLogging: true,
+                        checkLoginIframe: false
+                      },
+                    }),
+                    multi     : true
+                },
+                {
+                    provide : FUSE_MOCK_API_DEFAULT_DELAY,
+                    useValue: config?.delay ?? 0
+                }
+            ]
+        };
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.request-handler.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.request-handler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..668c7b0856ffd3a2b626fff580396a4f94062b99
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.request-handler.ts
@@ -0,0 +1,99 @@
+import { HttpRequest } from '@angular/common/http';
+import { Observable, of, take, throwError } from 'rxjs';
+import { FuseMockApiReplyCallback } from '@fuse/lib/mock-api/mock-api.types';
+
+export class FuseMockApiHandler
+{
+    request!: HttpRequest<any>;
+    urlParams!: { [key: string]: string };
+
+    // Private
+    private _reply: FuseMockApiReplyCallback = undefined;
+    private _replyCount = 0;
+    private _replied = 0;
+
+    /**
+     * Constructor
+     */
+    constructor(
+        public url: string,
+        public delay?: number
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Getter for response callback
+     */
+    get response(): Observable<any>
+    {
+        // If the execution limit has been reached, throw an error
+        if ( this._replyCount > 0 && this._replyCount <= this._replied )
+        {
+            return throwError('Execution limit has been reached!');
+        }
+
+        // If the response callback has not been set, throw an error
+        if ( !this._reply )
+        {
+            return throwError('Response callback function does not exist!');
+        }
+
+        // If the request has not been set, throw an error
+        if ( !this.request )
+        {
+            return throwError('Request does not exist!');
+        }
+
+        // Increase the replied count
+        this._replied++;
+
+        // Execute the reply callback
+        const replyResult = this._reply({
+            request  : this.request,
+            urlParams: this.urlParams
+        });
+
+        // If the result of the reply callback is an observable...
+        if ( replyResult instanceof Observable )
+        {
+            // Return the result as it is
+            return replyResult.pipe(take(1));
+        }
+
+        // Otherwise, return the result as an observable
+        return of(replyResult).pipe(take(1));
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Reply
+     *
+     * @param callback
+     */
+    reply(callback: FuseMockApiReplyCallback): void
+    {
+        // Store the reply
+        this._reply = callback;
+    }
+
+    /**
+     * Reply count
+     *
+     * @param count
+     */
+    replyCount(count: number): void
+    {
+        // Store the reply count
+        this._replyCount = count;
+    }
+}
+
+
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.service.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30a065247cb1940311fd99cee12fe52baa204276
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.service.ts
@@ -0,0 +1,202 @@
+import { Injectable } from '@angular/core';
+import { compact, fromPairs } from 'lodash-es';
+import { FuseMockApiHandler } from '@fuse/lib/mock-api/mock-api.request-handler';
+import { FuseMockApiMethods } from '@fuse/lib/mock-api/mock-api.types';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseMockApiService
+{
+    private _handlers: { [key: string]: Map<string, FuseMockApiHandler> } = {
+        'get'    : new Map<string, FuseMockApiHandler>(),
+        'post'   : new Map<string, FuseMockApiHandler>(),
+        'patch'  : new Map<string, FuseMockApiHandler>(),
+        'delete' : new Map<string, FuseMockApiHandler>(),
+        'put'    : new Map<string, FuseMockApiHandler>(),
+        'head'   : new Map<string, FuseMockApiHandler>(),
+        'jsonp'  : new Map<string, FuseMockApiHandler>(),
+        'options': new Map<string, FuseMockApiHandler>()
+    };
+
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Find the handler from the service
+     * with the given method and url
+     *
+     * @param method
+     * @param url
+     */
+    findHandler(method: string, url: string): { handler: FuseMockApiHandler | undefined; urlParams: { [key: string]: string } }
+    {
+        // Prepare the return object
+        const matchingHandler: { handler: FuseMockApiHandler | undefined; urlParams: { [key: string]: string } } = {
+            handler  : undefined,
+            urlParams: {}
+        };
+
+        // Split the url
+        const urlParts = url.split('/');
+
+        // Get all related request handlers
+        const handlers = this._handlers[method.toLowerCase()];
+
+        // Iterate through the handlers
+        handlers.forEach((handler, handlerUrl) => {
+
+            // Skip if there is already a matching handler
+            if ( matchingHandler.handler )
+            {
+                return;
+            }
+
+            // Split the handler url
+            const handlerUrlParts = handlerUrl.split('/');
+
+            // Skip if the lengths of the urls we are comparing are not the same
+            if ( urlParts.length !== handlerUrlParts.length )
+            {
+                return;
+            }
+
+            // Compare
+            const matches = handlerUrlParts.every((handlerUrlPart, index) => handlerUrlPart === urlParts[index] || handlerUrlPart.startsWith(':'));
+
+            // If there is a match...
+            if ( matches )
+            {
+                // Assign the matching handler
+                matchingHandler.handler = handler;
+
+                // Extract and assign the parameters
+                matchingHandler.urlParams = fromPairs(compact(handlerUrlParts.map((handlerUrlPart, index) =>
+                    handlerUrlPart.startsWith(':') ? [handlerUrlPart.substring(1), urlParts[index]] : undefined
+                )));
+            }
+        });
+
+        return matchingHandler;
+    }
+
+    /**
+     * Register GET request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onGet(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('get', url, delay);
+    }
+
+    /**
+     * Register POST request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onPost(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('post', url, delay);
+    }
+
+    /**
+     * Register PATCH request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onPatch(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('patch', url, delay);
+    }
+
+    /**
+     * Register DELETE request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onDelete(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('delete', url, delay);
+    }
+
+    /**
+     * Register PUT request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onPut(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('put', url, delay);
+    }
+
+    /**
+     * Register HEAD request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onHead(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('head', url, delay);
+    }
+
+    /**
+     * Register JSONP request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onJsonp(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('jsonp', url, delay);
+    }
+
+    /**
+     * Register OPTIONS request handler
+     *
+     * @param url - URL address of the mocked API endpoint
+     * @param delay - Delay of the response in milliseconds
+     */
+    onOptions(url: string, delay?: number): FuseMockApiHandler
+    {
+        return this._registerHandler('options', url, delay);
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Private methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Register and return a new instance of the handler
+     *
+     * @param method
+     * @param url
+     * @param delay
+     * @private
+     */
+    private _registerHandler(method: FuseMockApiMethods, url: string, delay?: number): FuseMockApiHandler
+    {
+        // Create a new instance of FuseMockApiRequestHandler
+        const fuseMockHttp = new FuseMockApiHandler(url, delay);
+
+        // Store the handler to access it from the interceptor
+        this._handlers[method].set(url, fuseMockHttp);
+
+        // Return the instance
+        return fuseMockHttp;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.types.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77236aa2d895c5e2a6f1ed55852175d3c73310b2
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.types.ts
@@ -0,0 +1,16 @@
+import { HttpRequest } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+export type FuseMockApiReplyCallback =
+    | ((data: { request: HttpRequest<any>; urlParams: { [key: string]: string } }) => ([number, string | any]) | Observable<any>)
+    | undefined;
+
+export type FuseMockApiMethods =
+    | 'get'
+    | 'post'
+    | 'patch'
+    | 'delete'
+    | 'put'
+    | 'head'
+    | 'jsonp'
+    | 'options';
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.utils.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..91af6a8f0f1b5d942ad6d05f2d9229c77d560af7
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/mock-api.utils.ts
@@ -0,0 +1,37 @@
+export class FuseMockApiUtils
+{
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Generate a globally unique id
+     */
+    static guid(): string
+    {
+        /* eslint-disable */
+
+        let d = new Date().getTime();
+
+        // Use high-precision timer if available
+        if ( typeof performance !== 'undefined' && typeof performance.now === 'function' )
+        {
+            d += performance.now();
+        }
+
+        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
+            const r = (d + Math.random() * 16) % 16 | 0;
+            d = Math.floor(d / 16);
+            return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
+        });
+
+        /* eslint-enable */
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/lib/mock-api/public-api.ts b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77e2345616335c7dd13c3a2c42db09a9e8cfff9e
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/lib/mock-api/public-api.ts
@@ -0,0 +1,5 @@
+export * from '@fuse/lib/mock-api/mock-api.constants';
+export * from '@fuse/lib/mock-api/mock-api.module';
+export * from '@fuse/lib/mock-api/mock-api.service';
+export * from '@fuse/lib/mock-api/mock-api.types';
+export * from '@fuse/lib/mock-api/mock-api.utils';
diff --git a/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/find-by-key.module.ts b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/find-by-key.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ead347784d5b0fdb5f1c7934e6d9b399169224ca
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/find-by-key.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { FuseFindByKeyPipe } from '@fuse/pipes/find-by-key/find-by-key.pipe';
+
+@NgModule({
+    declarations: [
+        FuseFindByKeyPipe
+    ],
+    exports     : [
+        FuseFindByKeyPipe
+    ]
+})
+export class FuseFindByKeyPipeModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/find-by-key.pipe.ts b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/find-by-key.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef56d41064257835718b95d7becbccb7ac903a99
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/find-by-key.pipe.ts
@@ -0,0 +1,37 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+/**
+ * Finds an object from given source using the given key - value pairs
+ */
+@Pipe({
+    name: 'fuseFindByKey',
+    pure: false
+})
+export class FuseFindByKeyPipe implements PipeTransform
+{
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    /**
+     * Transform
+     *
+     * @param value A string or an array of strings to find from source
+     * @param key Key of the object property to look for
+     * @param source Array of objects to find from
+     */
+    transform(value: string | string[], key: string, source: any[]): any
+    {
+        // If the given value is an array of strings...
+        if ( Array.isArray(value) )
+        {
+            return value.map(item => source.find(sourceItem => sourceItem[key] === item));
+        }
+
+        // If the value is a string...
+        return source.find(sourceItem => sourceItem[key] === value);
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/index.ts b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61efac020b9bfdc10045c32612fa3bba8ecfe31e
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/pipes/find-by-key/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/public-api.ts b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..178a2c7fe569b71afb2ad3de259b860e9519c231
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/pipes/find-by-key/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/pipes/find-by-key/find-by-key.pipe';
+export * from '@fuse/pipes/find-by-key/find-by-key.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/config/config.constants.ts b/transparency_dashboard_frontend/src/@fuse/services/config/config.constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef99d39c2edb2361618d40c4d59bbecdec507d5b
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/config/config.constants.ts
@@ -0,0 +1,3 @@
+import { InjectionToken } from '@angular/core';
+
+export const FUSE_APP_CONFIG = new InjectionToken<any>('FUSE_APP_CONFIG');
diff --git a/transparency_dashboard_frontend/src/@fuse/services/config/config.module.ts b/transparency_dashboard_frontend/src/@fuse/services/config/config.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4416a4ce9d02d8faa4e1b0efd3ef58e5838bdd72
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/config/config.module.ts
@@ -0,0 +1,32 @@
+import { ModuleWithProviders, NgModule } from '@angular/core';
+import { FuseConfigService } from '@fuse/services/config/config.service';
+import { FUSE_APP_CONFIG } from '@fuse/services/config/config.constants';
+
+@NgModule()
+export class FuseConfigModule
+{
+    /**
+     * Constructor
+     */
+    constructor(private _fuseConfigService: FuseConfigService)
+    {
+    }
+
+    /**
+     * forRoot method for setting user configuration
+     *
+     * @param config
+     */
+    static forRoot(config: any): ModuleWithProviders<FuseConfigModule>
+    {
+        return {
+            ngModule : FuseConfigModule,
+            providers: [
+                {
+                    provide : FUSE_APP_CONFIG,
+                    useValue: config
+                }
+            ]
+        };
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/config/config.service.ts b/transparency_dashboard_frontend/src/@fuse/services/config/config.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..33948a0f7c6abc92843616f557cf91f41f7da1d3
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/config/config.service.ts
@@ -0,0 +1,55 @@
+import { Inject, Injectable } from '@angular/core';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { merge } from 'lodash-es';
+import { FUSE_APP_CONFIG } from '@fuse/services/config/config.constants';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseConfigService
+{
+    private _config: BehaviorSubject<any>;
+
+    /**
+     * Constructor
+     */
+    constructor(@Inject(FUSE_APP_CONFIG) config: any)
+    {
+        // Private
+        this._config = new BehaviorSubject(config);
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Setter & getter for config
+     */
+    set config(value: any)
+    {
+        // Merge the new config over to the current config
+        const config = merge({}, this._config.getValue(), value);
+
+        // Execute the observable
+        this._config.next(config);
+    }
+
+    get config$(): Observable<any>
+    {
+        return this._config.asObservable();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Resets the config to the default
+     */
+    reset(): void
+    {
+        // Set the config
+        this._config.next(this.config);
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/config/index.ts b/transparency_dashboard_frontend/src/@fuse/services/config/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0563cafef4ee8a510d8c2ff42f933bc2ff2887f9
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/config/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/services/config/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/config/public-api.ts b/transparency_dashboard_frontend/src/@fuse/services/config/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36df7bdc4b5bbe2fe86e7ae5c24752b82c6d1c8c
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/config/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/services/config/config.module';
+export * from '@fuse/services/config/config.service';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.module.ts b/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ce1432963d97aae4305b10dbf57ac0ad333cf1aa
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.module.ts
@@ -0,0 +1,31 @@
+import { NgModule } from '@angular/core';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatIconModule } from '@angular/material/icon';
+import { FuseConfirmationService } from '@fuse/services/confirmation/confirmation.service';
+import { FuseConfirmationDialogComponent } from '@fuse/services/confirmation/dialog/dialog.component';
+import { CommonModule } from '@angular/common';
+
+@NgModule({
+    declarations: [
+        FuseConfirmationDialogComponent
+    ],
+    imports     : [
+        MatButtonModule,
+        MatDialogModule,
+        MatIconModule,
+        CommonModule
+    ],
+    providers   : [
+        FuseConfirmationService
+    ]
+})
+export class FuseConfirmationModule
+{
+    /**
+     * Constructor
+     */
+    constructor(private _fuseConfirmationService: FuseConfirmationService)
+    {
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.service.ts b/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b22ccae56189bb17f43759808ca957f9137cb86d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.service.ts
@@ -0,0 +1,58 @@
+import { Injectable } from '@angular/core';
+import { MatDialog, MatDialogRef } from '@angular/material/dialog';
+import { merge } from 'lodash-es';
+import { FuseConfirmationDialogComponent } from '@fuse/services/confirmation/dialog/dialog.component';
+import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
+
+@Injectable()
+export class FuseConfirmationService
+{
+    private _defaultConfig: FuseConfirmationConfig = {
+        title      : 'Confirm action',
+        message    : 'Are you sure you want to confirm this action?',
+        icon       : {
+            show : true,
+            name : 'heroicons_outline:exclamation',
+            color: 'warn'
+        },
+        actions    : {
+            confirm: {
+                show : true,
+                label: 'Confirm',
+                color: 'warn'
+            },
+            cancel : {
+                show : true,
+                label: 'Cancel'
+            }
+        },
+        dismissible: false
+    };
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _matDialog: MatDialog
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    open(config: FuseConfirmationConfig = {}): MatDialogRef<FuseConfirmationDialogComponent>
+    {
+        // Merge the user config with the default config
+        const userConfig = merge({}, this._defaultConfig, config);
+
+        // Open the dialog
+        return this._matDialog.open(FuseConfirmationDialogComponent, {
+            autoFocus   : false,
+            disableClose: !userConfig.dismissible,
+            data        : userConfig,
+            panelClass  : 'fuse-confirmation-dialog-panel'
+        });
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.types.ts b/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53bd2dc54b64a03409b9f260874f84c922add3f6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/confirmation.types.ts
@@ -0,0 +1,22 @@
+export interface FuseConfirmationConfig
+{
+    title?: string;
+    message?: string;
+    icon?: {
+        show?: boolean;
+        name?: string;
+        color?: 'primary' | 'accent' | 'warn' | 'basic' | 'info' | 'success' | 'warning' | 'error';
+    };
+    actions?: {
+        confirm?: {
+            show?: boolean;
+            label?: string;
+            color?: 'primary' | 'accent' | 'warn';
+        };
+        cancel?: {
+            show?: boolean;
+            label?: string;
+        };
+    };
+    dismissible?: boolean;
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/dialog/dialog.component.html b/transparency_dashboard_frontend/src/@fuse/services/confirmation/dialog/dialog.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..1d15451d8ec9492d668a10de9caad29a584460f1
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/dialog/dialog.component.html
@@ -0,0 +1,85 @@
+<div class="relative flex flex-col w-full h-full">
+
+    <!-- Dismiss button -->
+    <ng-container *ngIf="data.dismissible">
+        <div class="absolute top-0 right-0 pt-4 pr-4">
+            <button
+                mat-icon-button
+                [matDialogClose]="undefined">
+                <mat-icon
+                    class="text-secondary"
+                    [svgIcon]="'heroicons_outline:x'"></mat-icon>
+            </button>
+        </div>
+    </ng-container>
+
+    <!-- Content -->
+    <div class="flex flex-col sm:flex-row flex-auto items-center sm:items-start p-8 pb-6 sm:pb-8">
+
+        <!-- Icon -->
+        <ng-container *ngIf="data.icon.show">
+            <div
+                class="flex flex-0 items-center justify-center w-10 h-10 sm:mr-4 rounded-full"
+                [ngClass]="{'text-primary-600 bg-primary-100 dark:text-primary-50 dark:bg-primary-600': data.icon.color === 'primary',
+                            'text-accent-600 bg-accent-100 dark:text-accent-50 dark:bg-accent-600': data.icon.color === 'accent',
+                            'text-warn-600 bg-warn-100 dark:text-warn-50 dark:bg-warn-600': data.icon.color === 'warn',
+                            'text-gray-600 bg-gray-100 dark:text-gray-50 dark:bg-gray-600': data.icon.color === 'basic',
+                            'text-blue-600 bg-blue-100 dark:text-blue-50 dark:bg-blue-600': data.icon.color === 'info',
+                            'text-green-500 bg-green-100 dark:text-green-50 dark:bg-green-500': data.icon.color === 'success',
+                            'text-amber-500 bg-amber-100 dark:text-amber-50 dark:bg-amber-500': data.icon.color === 'warning',
+                            'text-red-600 bg-red-100 dark:text-red-50 dark:bg-red-600': data.icon.color === 'error'
+                            }">
+                <mat-icon
+                    class="text-current"
+                    [svgIcon]="data.icon.name"></mat-icon>
+            </div>
+        </ng-container>
+
+        <ng-container *ngIf="data.title || data.message">
+            <div class="flex flex-col items-center sm:items-start mt-4 sm:mt-0 sm:pr-8 space-y-1 text-center sm:text-left">
+
+                <!-- Title -->
+                <ng-container *ngIf="data.title">
+                    <div
+                        class="text-xl leading-6 font-medium"
+                        [innerHTML]="data.title"></div>
+                </ng-container>
+
+                <!-- Message -->
+                <ng-container *ngIf="data.message">
+                    <div
+                        class="text-secondary"
+                        [innerHTML]="data.message"></div>
+                </ng-container>
+            </div>
+        </ng-container>
+
+    </div>
+
+    <!-- Actions -->
+    <ng-container *ngIf="data.actions.confirm.show || data.actions.cancel.show">
+        <div class="flex items-center justify-center sm:justify-end px-6 py-4 space-x-3 bg-gray-50 dark:bg-black dark:bg-opacity-10">
+
+            <!-- Cancel -->
+            <ng-container *ngIf="data.actions.cancel.show">
+                <button
+                    mat-stroked-button
+                    [matDialogClose]="'cancelled'">
+                    {{data.actions.cancel.label}}
+                </button>
+            </ng-container>
+
+            <!-- Confirm -->
+            <ng-container *ngIf="data.actions.confirm.show">
+                <button
+                    mat-flat-button
+                    [color]="data.actions.confirm.color"
+                    [matDialogClose]="'confirmed'">
+                    {{data.actions.confirm.label}}
+                </button>
+            </ng-container>
+
+        </div>
+    </ng-container>
+
+</div>
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/dialog/dialog.component.ts b/transparency_dashboard_frontend/src/@fuse/services/confirmation/dialog/dialog.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4dd92d3ffab8e1b127446cd49ef70adb25b4110f
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/dialog/dialog.component.ts
@@ -0,0 +1,52 @@
+import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
+
+@Component({
+    selector     : 'fuse-confirmation-dialog',
+    templateUrl  : './dialog.component.html',
+    styles       : [
+        /* language=SCSS */
+        `
+            .fuse-confirmation-dialog-panel {
+                @screen md {
+                    @apply w-128;
+                }
+
+                .mat-dialog-container {
+                    padding: 0 !important;
+                }
+            }
+        `
+    ],
+    encapsulation: ViewEncapsulation.None
+})
+export class FuseConfirmationDialogComponent implements OnInit
+{
+    /**
+     * Constructor
+     */
+    constructor(
+        @Inject(MAT_DIALOG_DATA) public data: FuseConfirmationConfig,
+        public matDialogRef: MatDialogRef<FuseConfirmationDialogComponent>
+    )
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Lifecycle hooks
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On init
+     */
+    ngOnInit(): void
+    {
+
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/index.ts b/transparency_dashboard_frontend/src/@fuse/services/confirmation/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6f2fee1bd233a4f33dc3c88ee56aa8e50a752dc
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/services/confirmation/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/confirmation/public-api.ts b/transparency_dashboard_frontend/src/@fuse/services/confirmation/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..815db9fd5f150a99c517e33deff57843fd860354
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/confirmation/public-api.ts
@@ -0,0 +1,3 @@
+export * from '@fuse/services/confirmation/confirmation.module';
+export * from '@fuse/services/confirmation/confirmation.service';
+export * from '@fuse/services/confirmation/confirmation.types';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/loading/index.ts b/transparency_dashboard_frontend/src/@fuse/services/loading/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..deaac8da9d736a5b2be9b185a82816ba54e6035b
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/loading/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/services/loading/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/loading/loading.interceptor.ts b/transparency_dashboard_frontend/src/@fuse/services/loading/loading.interceptor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..886243f2db2c8afc99298bfecc0e2ae8e9121dd8
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/loading/loading.interceptor.ts
@@ -0,0 +1,48 @@
+import { Injectable } from '@angular/core';
+import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
+import { finalize, Observable } from 'rxjs';
+import { FuseLoadingService } from '@fuse/services/loading/loading.service';
+
+@Injectable()
+export class FuseLoadingInterceptor implements HttpInterceptor
+{
+    handleRequestsAutomatically: boolean;
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _fuseLoadingService: FuseLoadingService
+    )
+    {
+        // Subscribe to the auto
+        this._fuseLoadingService.auto$
+            .subscribe((value) => {
+                this.handleRequestsAutomatically = value;
+            });
+    }
+
+    /**
+     * Intercept
+     *
+     * @param req
+     * @param next
+     */
+    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
+    {
+        // If the Auto mode is turned off, do nothing
+        if ( !this.handleRequestsAutomatically )
+        {
+            return next.handle(req);
+        }
+
+        // Set the loading status to true
+        this._fuseLoadingService._setLoadingStatus(true, req.url);
+
+        return next.handle(req).pipe(
+            finalize(() => {
+                // Set the status to false if there are any errors or the request is completed
+                this._fuseLoadingService._setLoadingStatus(false, req.url);
+            }));
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/loading/loading.module.ts b/transparency_dashboard_frontend/src/@fuse/services/loading/loading.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..778ddfd305b73d982f8d576a24198278935b771a
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/loading/loading.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { HTTP_INTERCEPTORS } from '@angular/common/http';
+import { FuseLoadingInterceptor } from '@fuse/services/loading/loading.interceptor';
+
+@NgModule({
+    providers: [
+        {
+            provide : HTTP_INTERCEPTORS,
+            useClass: FuseLoadingInterceptor,
+            multi   : true
+        }
+    ]
+})
+export class FuseLoadingModule
+{
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/loading/loading.service.ts b/transparency_dashboard_frontend/src/@fuse/services/loading/loading.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..84049f224f12ba095f42dfd6f8c0815c40f4e32d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/loading/loading.service.ts
@@ -0,0 +1,146 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { BehaviorSubject, Observable } from 'rxjs';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseLoadingService
+{
+    private _auto$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
+    private _mode$: BehaviorSubject<'determinate' | 'indeterminate'> = new BehaviorSubject<'determinate' | 'indeterminate'>('indeterminate');
+    private _progress$: BehaviorSubject<number | null> = new BehaviorSubject<number | null>(0);
+    private _show$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+    private _urlMap: Map<string, boolean> = new Map<string, boolean>();
+
+    /**
+     * Constructor
+     */
+    constructor(private _httpClient: HttpClient)
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Getter for auto mode
+     */
+    get auto$(): Observable<boolean>
+    {
+        return this._auto$.asObservable();
+    }
+
+    /**
+     * Getter for mode
+     */
+    get mode$(): Observable<'determinate' | 'indeterminate'>
+    {
+        return this._mode$.asObservable();
+    }
+
+    /**
+     * Getter for progress
+     */
+    get progress$(): Observable<number>
+    {
+        return this._progress$.asObservable();
+    }
+
+    /**
+     * Getter for show
+     */
+    get show$(): Observable<boolean>
+    {
+        return this._show$.asObservable();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Show the loading bar
+     */
+    show(): void
+    {
+        this._show$.next(true);
+    }
+
+    /**
+     * Hide the loading bar
+     */
+    hide(): void
+    {
+        this._show$.next(false);
+    }
+
+    /**
+     * Set the auto mode
+     *
+     * @param value
+     */
+    setAutoMode(value: boolean): void
+    {
+        this._auto$.next(value);
+    }
+
+    /**
+     * Set the mode
+     *
+     * @param value
+     */
+    setMode(value: 'determinate' | 'indeterminate'): void
+    {
+        this._mode$.next(value);
+    }
+
+    /**
+     * Set the progress of the bar manually
+     *
+     * @param value
+     */
+    setProgress(value: number): void
+    {
+        if ( value < 0 || value > 100 )
+        {
+            console.error('Progress value must be between 0 and 100!');
+            return;
+        }
+
+        this._progress$.next(value);
+    }
+
+    /**
+     * Sets the loading status on the given url
+     *
+     * @param status
+     * @param url
+     */
+    _setLoadingStatus(status: boolean, url: string): void
+    {
+        // Return if the url was not provided
+        if ( !url )
+        {
+            console.error('The request URL must be provided!');
+            return;
+        }
+
+        if ( status === true )
+        {
+            this._urlMap.set(url, status);
+            this._show$.next(true);
+        }
+        else if ( status === false && this._urlMap.has(url) )
+        {
+            this._urlMap.delete(url);
+        }
+
+        // Only set the status to 'false' if all outgoing requests are completed
+        if ( this._urlMap.size === 0 )
+        {
+            this._show$.next(false);
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/loading/public-api.ts b/transparency_dashboard_frontend/src/@fuse/services/loading/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c52faedd695f98d313cf6f411e1685081751c570
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/loading/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/services/loading/loading.service';
+export * from '@fuse/services/loading/loading.module';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/media-watcher/index.ts b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b2dad0cbebe706f95cced17473dc8c9407c3e483
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/services/media-watcher/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/media-watcher/media-watcher.module.ts b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/media-watcher.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3e5133e0e05b8d92d9e4e6ebf7018570a43a9417
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/media-watcher.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { FuseMediaWatcherService } from '@fuse/services/media-watcher/media-watcher.service';
+
+@NgModule({
+    providers: [
+        FuseMediaWatcherService
+    ]
+})
+export class FuseMediaWatcherModule
+{
+    /**
+     * Constructor
+     */
+    constructor(private _fuseMediaWatcherService: FuseMediaWatcherService)
+    {
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/media-watcher/media-watcher.service.ts b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/media-watcher.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2066f065b800208360bcba63ba32f1d8781de4d6
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/media-watcher.service.ts
@@ -0,0 +1,79 @@
+import { Injectable } from '@angular/core';
+import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
+import { map, Observable, ReplaySubject, switchMap } from 'rxjs';
+import { fromPairs } from 'lodash-es';
+import { FuseConfigService } from '@fuse/services/config';
+
+@Injectable()
+export class FuseMediaWatcherService
+{
+    private _onMediaChange: ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }> = new ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }>(1);
+
+    /**
+     * Constructor
+     */
+    constructor(
+        private _breakpointObserver: BreakpointObserver,
+        private _fuseConfigService: FuseConfigService
+    )
+    {
+        this._fuseConfigService.config$.pipe(
+            map(config => fromPairs(Object.entries(config.screens).map(([alias, screen]) => ([alias, `(min-width: ${screen})`])))),
+            switchMap(screens => this._breakpointObserver.observe(Object.values(screens)).pipe(
+                map((state) => {
+
+                    // Prepare the observable values and set their defaults
+                    const matchingAliases: string[] = [];
+                    const matchingQueries: any = {};
+
+                    // Get the matching breakpoints and use them to fill the subject
+                    const matchingBreakpoints = Object.entries(state.breakpoints).filter(([query, matches]) => matches) ?? [];
+                    for ( const [query] of matchingBreakpoints )
+                    {
+                        // Find the alias of the matching query
+                        const matchingAlias = Object.entries(screens).find(([alias, q]) => q === query)[0];
+
+                        // Add the matching query to the observable values
+                        if ( matchingAlias )
+                        {
+                            matchingAliases.push(matchingAlias);
+                            matchingQueries[matchingAlias] = query;
+                        }
+                    }
+
+                    // Execute the observable
+                    this._onMediaChange.next({
+                        matchingAliases,
+                        matchingQueries
+                    });
+                })
+            ))
+        ).subscribe();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Getter for _onMediaChange
+     */
+    get onMediaChange$(): Observable<{ matchingAliases: string[]; matchingQueries: any }>
+    {
+        return this._onMediaChange.asObservable();
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * On media query change
+     *
+     * @param query
+     */
+    onMediaQueryChange$(query: string | string[]): Observable<BreakpointState>
+    {
+        return this._breakpointObserver.observe(query);
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/media-watcher/public-api.ts b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd5905d7355c3890085a4616b3640934edafc2e9
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/media-watcher/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/services/media-watcher/media-watcher.module';
+export * from '@fuse/services/media-watcher/media-watcher.service';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/splash-screen/index.ts b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..79ce6e491c8b5b2119aca88d45f3c5c8304e8d2a
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/services/splash-screen/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/splash-screen/public-api.ts b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab0ada99e2e4ac6245b87579c6829e6d4a360d43
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/services/splash-screen/splash-screen.module';
+export * from '@fuse/services/splash-screen/splash-screen.service';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/splash-screen/splash-screen.module.ts b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/splash-screen.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..913d595da5ceed0a5d6811aec18fa6d8039153cd
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/splash-screen.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { FuseSplashScreenService } from '@fuse/services/splash-screen/splash-screen.service';
+
+@NgModule({
+    providers: [
+        FuseSplashScreenService
+    ]
+})
+export class FuseSplashScreenModule
+{
+    /**
+     * Constructor
+     */
+    constructor(private _fuseSplashScreenService: FuseSplashScreenService)
+    {
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/splash-screen/splash-screen.service.ts b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/splash-screen.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5743f4c56e14626c691b4ea75a89c6f2a6203736
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/splash-screen/splash-screen.service.ts
@@ -0,0 +1,47 @@
+import { Inject, Injectable } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+import { NavigationEnd, Router } from '@angular/router';
+import { filter, take } from 'rxjs';
+
+@Injectable()
+export class FuseSplashScreenService
+{
+    /**
+     * Constructor
+     */
+    constructor(
+        @Inject(DOCUMENT) private _document: any,
+        private _router: Router
+    )
+    {
+        // Hide it on the first NavigationEnd event
+        this._router.events
+            .pipe(
+                filter(event => event instanceof NavigationEnd),
+                take(1)
+            )
+            .subscribe(() => {
+                this.hide();
+            });
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Show the splash screen
+     */
+    show(): void
+    {
+        this._document.body.classList.remove('fuse-splash-screen-hidden');
+    }
+
+    /**
+     * Hide the splash screen
+     */
+    hide(): void
+    {
+        this._document.body.classList.add('fuse-splash-screen-hidden');
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/utils/index.ts b/transparency_dashboard_frontend/src/@fuse/services/utils/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a507e7ffa073545faba203dc13f7d6b3fc8c7c5d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/utils/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/services/utils/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/utils/public-api.ts b/transparency_dashboard_frontend/src/@fuse/services/utils/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8d2a55bd65d4c4e43cc7f575a90950f5458379f7
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/utils/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/services/utils/utils.module';
+export * from '@fuse/services/utils/utils.service';
diff --git a/transparency_dashboard_frontend/src/@fuse/services/utils/utils.module.ts b/transparency_dashboard_frontend/src/@fuse/services/utils/utils.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9f974f339011f622bbebd5def8016221cf02355e
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/utils/utils.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { FuseUtilsService } from '@fuse/services/utils/utils.service';
+
+@NgModule({
+    providers: [
+        FuseUtilsService
+    ]
+})
+export class FuseUtilsModule
+{
+    /**
+     * Constructor
+     */
+    constructor(private _fuseUtilsService: FuseUtilsService)
+    {
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/services/utils/utils.service.ts b/transparency_dashboard_frontend/src/@fuse/services/utils/utils.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0c307ee655f3089bacb35b8faa17544dff29e312
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/services/utils/utils.service.ts
@@ -0,0 +1,67 @@
+import { Injectable } from '@angular/core';
+import { IsActiveMatchOptions } from '@angular/router';
+
+@Injectable({
+    providedIn: 'root'
+})
+export class FuseUtilsService
+{
+    /**
+     * Constructor
+     */
+    constructor()
+    {
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Accessors
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Get the equivalent "IsActiveMatchOptions" options for "exact = true".
+     */
+    get exactMatchOptions(): IsActiveMatchOptions
+    {
+        return {
+            paths       : 'exact',
+            fragment    : 'ignored',
+            matrixParams: 'ignored',
+            queryParams : 'exact'
+        };
+    }
+
+    /**
+     * Get the equivalent "IsActiveMatchOptions" options for "exact = false".
+     */
+    get subsetMatchOptions(): IsActiveMatchOptions
+    {
+        return {
+            paths       : 'subset',
+            fragment    : 'ignored',
+            matrixParams: 'ignored',
+            queryParams : 'subset'
+        };
+    }
+
+    // -----------------------------------------------------------------------------------------------------
+    // @ Public methods
+    // -----------------------------------------------------------------------------------------------------
+
+    /**
+     * Generates a random id
+     *
+     * @param length
+     */
+    randomId(length: number = 10): string
+    {
+        const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+        let name = '';
+
+        for ( let i = 0; i < 10; i++ )
+        {
+            name += chars.charAt(Math.floor(Math.random() * chars.length));
+        }
+
+        return name;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/components/example-viewer.scss b/transparency_dashboard_frontend/src/@fuse/styles/components/example-viewer.scss
new file mode 100644
index 0000000000000000000000000000000000000000..fe0fd30370f5eab54221aa824c1fa22570e1cdc2
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/components/example-viewer.scss
@@ -0,0 +1,47 @@
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Example viewer
+/* ----------------------------------------------------------------------------------------------------- */
+.example-viewer {
+    display: flex;
+    flex-direction: column;
+    margin: 32px 0;
+    overflow: hidden;
+    @apply rounded-2xl shadow bg-card;
+
+    .title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        height: 88px;
+        min-height: 88px;
+        max-height: 88px;
+        padding: 0 40px;
+
+        h6 {
+            font-weight: 700;
+        }
+
+        .controls {
+            display: flex;
+            align-items: center;
+
+            > * + * {
+                margin-left: 8px;
+            }
+        }
+    }
+
+    mat-tab-group {
+
+        .mat-tab-body-content {
+
+            .fuse-highlight {
+
+                pre {
+                    margin: 0;
+                    border-radius: 0;
+                }
+            }
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/components/input.scss b/transparency_dashboard_frontend/src/@fuse/styles/components/input.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1ae31b841e04d44400636cdb6dc25b31eec8fa9d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/components/input.scss
@@ -0,0 +1,41 @@
+input,
+textarea {
+    background: transparent;
+
+    /* Placeholder color */
+    &::placeholder {
+        @apply text-hint;
+    }
+
+    &::-moz-placeholder {
+        @apply text-hint;
+    }
+
+    &::-webkit-input-placeholder {
+        @apply text-hint;
+    }
+
+    &:-ms-input-placeholder {
+        @apply text-hint;
+    }
+
+    &:-webkit-autofill {
+        -webkit-transition: 'background-color 9999s ease-out';
+        -webkit-transition-delay: 9999s;
+    }
+
+    &:-webkit-autofill:hover {
+        -webkit-transition: 'background-color 9999s ease-out';
+        -webkit-transition-delay: 9999s;
+    }
+
+    &:-webkit-autofill:focus {
+        -webkit-transition: 'background-color 9999s ease-out';
+        -webkit-transition-delay: 9999s;
+    }
+
+    &:-webkit-autofill:active {
+        -webkit-transition: 'background-color 9999s ease-out';
+        -webkit-transition-delay: 9999s;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/main.scss b/transparency_dashboard_frontend/src/@fuse/styles/main.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e964bfe967b8d61e1a0723a7a37cfde4fbc72b50
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/main.scss
@@ -0,0 +1,9 @@
+/* 1. Components */
+@import 'components/example-viewer';
+@import 'components/input';
+
+/* 2. Overrides */
+@import 'overrides/angular-material';
+@import 'overrides/highlightjs';
+@import 'overrides/perfect-scrollbar';
+@import 'overrides/quill';
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/overrides/angular-material.scss b/transparency_dashboard_frontend/src/@fuse/styles/overrides/angular-material.scss
new file mode 100644
index 0000000000000000000000000000000000000000..7d2b6a80394a95ee27cbd2519d4dc83fb9d5d35d
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/overrides/angular-material.scss
@@ -0,0 +1,1383 @@
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Angular Material CDK helpers & overrides
+/* ----------------------------------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Overlay
+/* ----------------------------------------------------------------------------------------------------- */
+.fuse-backdrop-on-mobile {
+  @apply bg-black bg-opacity-60 sm:bg-transparent #{'!important'};
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Angular Material helpers & overrides
+/* ----------------------------------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Accordion
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-accordion {
+  .mat-expansion-panel {
+    margin-bottom: 24px;
+    border-radius: 8px !important;
+    transition: box-shadow 225ms cubic-bezier(0.4, 0, 0.2, 1);
+    @apply shadow #{'!important'};
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+
+    &.mat-expanded,
+    &:hover {
+      @apply shadow-lg #{'!important'};
+    }
+
+    &:not(.mat-expanded) {
+      .mat-expansion-panel-header {
+        &:not([aria-disabled="true"]) {
+          &.cdk-keyboard-focused,
+          &.cdk-program-focused,
+          &:hover {
+            background: transparent !important;
+          }
+        }
+      }
+    }
+
+    .mat-expansion-panel-header {
+      font-size: 14px;
+
+      &[aria-disabled="true"] {
+        .mat-expansion-panel-header-description {
+          margin-right: 28px;
+        }
+      }
+
+      .mat-expansion-indicator {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        width: 12px;
+        height: 12px;
+
+        /* Do not override the border color of the expansion panel indicator */
+        &:after {
+          border-color: currentColor !important;
+        }
+      }
+    }
+
+    .mat-expansion-panel-body {
+      line-height: 1.7;
+      @apply text-secondary #{'!important'};
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Buttons
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-button,
+.mat-fab,
+.mat-flat-button,
+.mat-icon-button,
+.mat-mini-fab,
+.mat-raised-button,
+.mat-stroked-button {
+  display: inline-flex !important;
+  align-items: center;
+  justify-content: center;
+  height: 40px;
+  min-height: 40px;
+  max-height: 40px;
+  line-height: 1 !important;
+
+  .mat-button-wrapper {
+    position: relative;
+    display: inline-flex !important;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    z-index: 2; /* Move mat-button-wrapper above the ripple and focus overlay */
+  }
+
+  .mat-button-focus-overlay,
+  .mat-button-ripple {
+    z-index: 1;
+  }
+
+  /* Large button */
+  &.fuse-mat-button-large {
+    height: 48px;
+    min-height: 48px;
+    max-height: 48px;
+  }
+
+  /* Lower the icon opacity on disabled buttons */
+  &.mat-button-disabled {
+    .mat-icon {
+      opacity: 0.38 !important;
+    }
+  }
+}
+
+.mat-fab {
+  max-height: 56px;
+}
+
+/* Rounded design */
+.mat-button,
+.mat-flat-button,
+.mat-raised-button,
+.mat-stroked-button {
+  padding: 0 20px !important;
+  border-radius: 9999px !important;
+}
+
+/* Target all buttons */
+.mat-button,
+.mat-fab,
+.mat-flat-button,
+.mat-icon-button,
+.mat-fab,
+.mat-mini-fab,
+.mat-raised-button,
+.mat-stroked-button {
+  /* mat-progress-spinner inside buttons */
+  .mat-progress-spinner {
+    &.mat-progress-spinner-indeterminate-animation[mode="indeterminate"] {
+      circle {
+        stroke: currentColor !important;
+        animation-duration: 6000ms;
+      }
+    }
+  }
+}
+
+/* Colored background buttons */
+.mat-flat-button,
+.mat-raised-button,
+.mat-fab,
+.mat-mini-fab {
+  .mat-icon {
+    color: currentColor !important;
+  }
+
+  /* Add hover and focus style on all buttons */
+  .mat-button-focus-overlay {
+    @apply bg-gray-400 bg-opacity-20 dark:bg-black dark:bg-opacity-5 #{'!important'};
+  }
+
+  /* On palette colored buttons, use a darker color */
+  &.mat-primary,
+  &.mat-accent,
+  &.mat-warn {
+    .mat-button-focus-overlay {
+      background-color: rgba(0, 0, 0, 0.1) !important;
+    }
+  }
+
+  &:hover,
+  &.cdk-keyboard-focused,
+  &.cdk-program-focused {
+    .mat-button-focus-overlay {
+      opacity: 1 !important;
+    }
+  }
+
+  @media (hover: none) {
+    &:hover {
+      .mat-button-focus-overlay {
+        opacity: 0 !important;
+      }
+    }
+  }
+
+  &.mat-button-disabled {
+    .mat-button-focus-overlay {
+      opacity: 0 !important;
+    }
+  }
+}
+
+/* Transparent background buttons */
+.mat-button,
+.mat-icon-button,
+.mat-stroked-button {
+  /* Apply primary color */
+  &.mat-primary:not(.mat-button-disabled) {
+    .mat-icon {
+      @apply text-primary #{'!important'};
+    }
+  }
+
+  /* Apply accent color */
+  &.mat-accent:not(.mat-button-disabled) {
+    .mat-icon {
+      @apply text-accent #{'!important'};
+    }
+  }
+
+  /* Apply warn color */
+  &.mat-warn:not(.mat-button-disabled) {
+    .mat-icon {
+      @apply text-warn #{'!important'};
+    }
+  }
+
+  /* Add hover and focus styles */
+  .mat-button-focus-overlay {
+    @apply bg-gray-400 bg-opacity-20 dark:bg-black dark:bg-opacity-5 #{'!important'};
+  }
+
+  /* On primary colored buttons, use the primary color as focus overlay */
+  &.mat-primary:not(.mat-button-disabled) {
+    .mat-button-focus-overlay {
+      @apply bg-primary #{'!important'};
+    }
+  }
+
+  /* On accent colored buttons, use the accent color as focus overlay */
+  &.mat-accent:not(.mat-button-disabled) {
+    .mat-button-focus-overlay {
+      @apply bg-accent #{'!important'};
+    }
+  }
+
+  /* On warn colored buttons, use the warn color as focus overlay */
+  &.mat-warn:not(.mat-button-disabled) {
+    .mat-button-focus-overlay {
+      @apply bg-warn #{'!important'};
+    }
+  }
+
+  &.mat-primary:not(.mat-button-disabled),
+  &.mat-accent:not(.mat-button-disabled),
+  &.mat-warn:not(.mat-button-disabled) {
+    &:hover,
+    &.cdk-keyboard-focused,
+    &.cdk-program-focused {
+      .mat-button-focus-overlay {
+        opacity: 0.1 !important;
+      }
+    }
+  }
+
+  &:hover,
+  &.cdk-keyboard-focused,
+  &.cdk-program-focused {
+    .mat-button-focus-overlay {
+      opacity: 1 !important;
+    }
+  }
+
+  @media (hover: none) {
+    &:hover {
+      .mat-button-focus-overlay {
+        opacity: 0 !important;
+      }
+    }
+  }
+
+  &.mat-button-disabled {
+    .mat-button-focus-overlay {
+      opacity: 0 !important;
+    }
+  }
+}
+
+/* Stroked buttons */
+.mat-stroked-button {
+  /* Border color */
+  &:not(.mat-button-disabled) {
+    @apply border-gray-300 dark:border-gray-500 #{'!important'};
+  }
+
+  &.mat-button-disabled {
+    @apply border-gray-200 dark:border-gray-600 #{'!important'};
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Button Toggle
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-button-toggle-group {
+  border: none !important;
+  @apply space-x-1;
+
+  &.mat-button-toggle-group-appearance-standard {
+    .mat-button-toggle + .mat-button-toggle {
+      background-clip: padding-box;
+    }
+  }
+
+  .mat-button-toggle {
+    border-radius: 9999px;
+    overflow: hidden;
+    border: none !important;
+    font-weight: 500;
+
+    &.mat-button-toggle-checked {
+      .mat-button-toggle-label-content {
+        @apply text-default #{'!important'};
+      }
+    }
+
+    .mat-button-toggle-label-content {
+      padding: 0 20px;
+      @apply text-secondary;
+    }
+
+    .mat-ripple {
+      border-radius: 9999px;
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Checkbox
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-checkbox {
+  display: inline-flex;
+
+  /* Allow multiline text */
+  .mat-checkbox-layout {
+    white-space: normal;
+
+    .mat-checkbox-inner-container {
+      display: inline-flex;
+      align-items: center;
+      margin: 0 8px 0 0;
+
+      /* Add a zero-width space character to trick the container */
+      /* into being the same height as a single line of the label */
+      &:after {
+        content: "\200b";
+      }
+    }
+
+    .mat-checkbox-label {
+      line-height: inherit;
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Chip
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-chip {
+  font-weight: 500 !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Dialog
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-dialog-container {
+  border-radius: 16px !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Drawer
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-drawer-backdrop.mat-drawer-shown {
+  background-color: rgba(0, 0, 0, 0.6) !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Form fields
+/* ----------------------------------------------------------------------------------------------------- */
+
+/* Fuse only uses 'fill' style form fields and therefore */
+/* only provides fixes and tweaks for that style */
+.mat-form-field.mat-form-field-appearance-fill {
+  /* Disabled */
+  &.mat-form-field-disabled {
+    opacity: 0.7 !important;
+  }
+
+  /* Invalid */
+  &.mat-form-field-invalid {
+    .mat-form-field-wrapper {
+      /* Border color */
+      .mat-form-field-flex {
+        @apply border-warn dark:border-warn #{'!important'};
+      }
+    }
+  }
+
+  /* Focused */
+  &.mat-focused {
+    .mat-form-field-wrapper {
+      /* Background color */
+      .mat-form-field-flex {
+        @apply bg-card dark:bg-card #{'!important'};
+      }
+    }
+  }
+
+  /* Focused and valid fields */
+  &.mat-focused:not(.mat-form-field-invalid) {
+    .mat-form-field-wrapper {
+      /* Border color */
+      .mat-form-field-flex {
+        @apply border-primary dark:border-primary #{'!important'};
+      }
+    }
+  }
+
+  /* Disable floating mat-label */
+  &.mat-form-field-has-label.mat-form-field-can-float.mat-form-field-should-float {
+    .mat-form-field-label-wrapper {
+      .mat-form-field-label {
+        width: 100% !important;
+        transform: none !important;
+      }
+    }
+  }
+
+  /* Remove the default arrow for native select */
+  &.mat-form-field-type-mat-native-select {
+    .mat-form-field-infix {
+      select {
+        top: auto;
+        margin-top: 0;
+        margin-bottom: 0;
+        padding-top: 0;
+        padding-right: 18px;
+        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%2364748B' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5H7z'/%3E%3C/svg%3E");
+        background-repeat: no-repeat;
+        background-position: right -7px center;
+        background-size: 24px;
+
+        .dark & {
+          background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%2397a6ba' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5H7z'/%3E%3C/svg%3E");
+        }
+      }
+
+      &:after {
+        display: none;
+      }
+    }
+  }
+
+  /* Adjustments for mat-label */
+  &.mat-form-field-has-label {
+    .mat-form-field-wrapper {
+      margin-top: 24px;
+    }
+  }
+
+  /* Default style tweaks and enhancements */
+  .mat-form-field-wrapper {
+    margin-bottom: 16px;
+    padding-bottom: 0;
+
+    .mat-form-field-flex {
+      position: relative;
+      display: flex;
+      align-items: stretch;
+      min-height: 48px;
+      border-radius: 6px;
+      padding: 0 16px;
+      border-width: 1px;
+      @apply shadow-sm bg-white border-gray-300 dark:bg-black dark:bg-opacity-5 dark:border-gray-500 #{'!important'};
+
+      .mat-form-field-prefix {
+        > .mat-icon {
+          margin-right: 12px;
+        }
+
+        > .mat-icon-button {
+          margin: 0 4px 0 -10px;
+        }
+
+        > .mat-select {
+          margin-right: 10px;
+        }
+
+        > .mat-datepicker-toggle {
+          margin-left: -8px;
+        }
+
+        > *:not(.mat-icon):not(.mat-icon-button):not(.mat-select):not(
+            .mat-datepicker-toggle
+          ) {
+          margin-right: 12px;
+        }
+      }
+
+      .mat-form-field-suffix {
+        > .mat-icon {
+          margin-left: 12px;
+        }
+
+        > .mat-icon-button {
+          margin: 0 -10px 0 4px;
+        }
+
+        > .mat-select {
+          margin-left: 10px;
+        }
+
+        > .mat-datepicker-toggle {
+          margin-right: -8px;
+        }
+      }
+
+      .mat-form-field-prefix,
+      .mat-form-field-suffix {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        @apply text-hint #{'!important'};
+
+        .mat-icon-button {
+          width: 40px;
+          min-width: 40px;
+          height: 40px;
+          min-height: 40px;
+        }
+
+        .mat-icon,
+        .mat-icon-button:not(.mat-button-disabled),
+        .mat-select-value {
+          @apply text-hint;
+        }
+
+        /* Remove the margins from the mat-icon if it's inside a button */
+        /* Force the icon size to 24 */
+        .mat-button,
+        .mat-raised-button,
+        .mat-icon-button,
+        .mat-stroked-button,
+        .mat-flat-button,
+        .mat-fab,
+        .mat-mini-fab {
+          .mat-icon {
+            margin: 0 !important;
+            @apply icon-size-6;
+          }
+        }
+
+        /* Datepicker default icon size */
+        .mat-datepicker-toggle-default-icon {
+          @apply icon-size-6;
+        }
+
+        /* Make mat-select usable as prefix and suffix */
+        .mat-select {
+          display: flex;
+          align-items: center;
+
+          &:focus {
+            .mat-select-trigger {
+              .mat-select-value {
+                @apply text-primary #{'!important'};
+              }
+
+              .mat-select-arrow-wrapper {
+                .mat-select-arrow {
+                  border-top-color: var(--fuse-primary) !important;
+                }
+              }
+            }
+          }
+
+          .mat-select-trigger {
+            display: flex;
+            align-items: center;
+
+            .mat-select-value {
+              display: flex;
+              max-width: none;
+
+              mat-select-trigger {
+                .mat-icon {
+                  margin: 0 !important;
+                }
+              }
+            }
+
+            .mat-select-arrow-wrapper {
+              display: flex;
+              align-items: center;
+              transform: none;
+              margin-left: 4px;
+
+              .mat-select-arrow {
+                min-height: 0;
+                @apply text-gray-500 dark:text-gray-400 #{'!important'};
+              }
+            }
+          }
+        }
+      }
+
+      .mat-form-field-infix {
+        position: static;
+        display: flex;
+        align-items: center;
+        width: 88px;
+        padding: 0;
+        border: 0;
+
+        .mat-input-element {
+          padding: 14px 0;
+          margin-top: 0;
+        }
+
+        /* Textarea */
+        textarea.mat-input-element {
+          display: flex;
+          align-self: stretch;
+          min-height: 36px;
+          height: auto;
+          margin: 14px 0;
+          padding: 0 6px 0 0;
+          transform: none;
+        }
+
+        /* Select */
+        .mat-select {
+          display: inline-flex;
+
+          .mat-select-trigger {
+            display: inline-flex;
+            align-items: center;
+            width: 100%;
+
+            .mat-select-value {
+              display: flex;
+              position: relative;
+              max-width: none;
+
+              .mat-select-value-text {
+                display: inline-flex;
+
+                > * {
+                  overflow: hidden;
+                  white-space: nowrap;
+                  text-overflow: ellipsis;
+                }
+              }
+            }
+          }
+
+          .mat-select-arrow-wrapper {
+            transform: translateY(0);
+
+            .mat-select-arrow {
+              margin: 0 0 0 8px;
+            }
+          }
+        }
+
+        /* Chips */
+        .mat-chip-list {
+          width: 100%;
+          margin: 0 -8px;
+
+          .mat-chip-input {
+            margin: 0 0 0 8px;
+          }
+        }
+
+        .mat-form-field-label-wrapper {
+          top: -25px;
+          height: auto;
+          padding-top: 0;
+          overflow: visible;
+          pointer-events: auto;
+
+          .mat-form-field-label {
+            position: relative;
+            top: 0;
+            margin-top: 0;
+            backface-visibility: hidden;
+            transition: none;
+            font-weight: 500;
+            @apply text-default #{'!important'};
+          }
+        }
+      }
+    }
+
+    /* Remove the underline */
+    .mat-form-field-underline {
+      display: none;
+    }
+
+    /* Subscript tweaks */
+    .mat-form-field-subscript-wrapper {
+      position: relative;
+      top: auto;
+      padding: 0;
+      margin-top: 0;
+      font-size: 12px;
+      font-weight: 500;
+      line-height: 1;
+
+      > div {
+        display: contents; /* Remove the div from flow to stop the subscript animation */
+      }
+
+      .mat-error,
+      .mat-hint {
+        display: block;
+        margin-top: 4px;
+      }
+
+      .mat-hint {
+        @apply text-hint #{'!important'};
+      }
+    }
+  }
+
+  /* Adds better alignment for textarea inputs */
+  &.fuse-mat-textarea {
+    .mat-form-field-wrapper {
+      .mat-form-field-flex {
+        .mat-form-field-prefix,
+        .mat-form-field-suffix {
+          align-items: flex-start;
+        }
+
+        .mat-form-field-prefix {
+          padding-top: 12px;
+        }
+
+        .mat-form-field-suffix {
+          padding-top: 12px;
+        }
+      }
+    }
+  }
+
+  /* Removes subscript space */
+  &.fuse-mat-no-subscript {
+    .mat-form-field-wrapper {
+      padding-bottom: 0;
+      margin-bottom: 0;
+
+      .mat-form-field-subscript-wrapper {
+        display: none !important;
+        height: 0 !important;
+      }
+    }
+  }
+
+  /* Rounded */
+  &.fuse-mat-rounded {
+    .mat-form-field-wrapper {
+      .mat-form-field-flex {
+        border-radius: 24px;
+      }
+    }
+
+    /* Emphasized affix */
+    &.fuse-mat-emphasized-affix {
+      .mat-form-field-wrapper {
+        .mat-form-field-flex {
+          .mat-form-field-prefix {
+            border-radius: 24px 0 0 24px;
+
+            > .mat-icon {
+              margin-right: 12px;
+            }
+
+            > .mat-icon-button {
+              margin-right: 2px;
+            }
+
+            > .mat-select {
+              margin-right: 8px;
+            }
+
+            > .mat-datepicker-toggle {
+              margin-right: 4px;
+            }
+
+            > *:not(.mat-icon):not(.mat-icon-button):not(.mat-select):not(
+                .mat-datepicker-toggle
+              ) {
+              margin-right: 12px;
+            }
+          }
+
+          .mat-form-field-suffix {
+            border-radius: 0 24px 24px 0;
+
+            > .mat-icon {
+              margin-left: 12px !important;
+            }
+
+            > .mat-icon-button {
+              margin-left: 2px !important;
+            }
+
+            > .mat-select {
+              margin-left: 12px !important;
+            }
+
+            > .mat-datepicker-toggle {
+              margin-left: 4px !important;
+            }
+
+            > *:not(.mat-icon):not(.mat-icon-button):not(.mat-select):not(
+                .mat-datepicker-toggle
+              ) {
+              margin-left: 12px !important;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /* Dense */
+  &.fuse-mat-dense {
+    .mat-form-field-wrapper {
+      .mat-form-field-flex {
+        min-height: 40px;
+
+        .mat-form-field-prefix,
+        .mat-form-field-suffix {
+          .mat-icon-button {
+            width: 32px;
+            min-width: 32px;
+            height: 32px;
+            min-height: 32px;
+          }
+        }
+
+        .mat-form-field-prefix {
+          > .mat-icon-button {
+            margin-left: -6px;
+            margin-right: 12px;
+          }
+        }
+
+        .mat-form-field-suffix {
+          > .mat-icon-button {
+            margin-left: 12px;
+            margin-right: -6px;
+          }
+        }
+
+        .mat-form-field-infix {
+          .mat-input-element {
+            padding: 11px 0;
+          }
+        }
+      }
+    }
+
+    /* Rounded */
+    &.fuse-mat-rounded {
+      .mat-form-field-wrapper {
+        .mat-form-field-flex {
+          border-radius: 20px;
+        }
+      }
+
+      /* Emphasized affix */
+      &.fuse-mat-emphasized-affix {
+        .mat-form-field-wrapper {
+          .mat-form-field-flex {
+            .mat-form-field-prefix {
+              border-radius: 20px 0 0 20px !important;
+            }
+
+            .mat-form-field-suffix {
+              border-radius: 0 20px 20px 0 !important;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /* Emphasized affix */
+  &.fuse-mat-emphasized-affix {
+    .mat-form-field-wrapper {
+      .mat-form-field-flex {
+        .mat-form-field-prefix {
+          margin: 0 16px 0 -16px;
+          padding-left: 16px;
+          border-radius: 6px 0 0 6px;
+          border-right-width: 1px;
+
+          > .mat-icon {
+            margin-right: 16px;
+          }
+
+          > .mat-icon-button {
+            margin: 0 6px 0 -10px;
+          }
+
+          > .mat-select {
+            margin-right: 12px;
+          }
+
+          > .mat-datepicker-toggle {
+            margin-right: 8px;
+          }
+
+          > *:not(.mat-icon):not(.mat-icon-button):not(.mat-select):not(
+              .mat-datepicker-toggle
+            ) {
+            margin-right: 16px;
+          }
+        }
+
+        .mat-form-field-suffix {
+          margin: 0 -16px 0 16px;
+          padding-right: 16px;
+          border-radius: 0 6px 6px 0;
+          border-left-width: 1px;
+
+          > .mat-icon {
+            margin-left: 16px;
+          }
+
+          > .mat-icon-button {
+            margin: 0 -10px 0 6px;
+          }
+
+          > .mat-select {
+            margin: 0 -4px 0 16px;
+          }
+
+          > .mat-datepicker-toggle {
+            margin-left: 8px;
+          }
+
+          > *:not(.mat-icon):not(.mat-icon-button):not(.mat-select):not(
+              .mat-datepicker-toggle
+            ) {
+            margin-left: 16px;
+          }
+        }
+
+        .mat-form-field-prefix,
+        .mat-form-field-suffix {
+          @apply bg-default border-gray-300 dark:border-gray-500 #{'!important'};
+        }
+      }
+    }
+  }
+
+  /* Bolder border width */
+  &.fuse-mat-bold {
+    .mat-form-field-wrapper {
+      .mat-form-field-flex {
+        border-width: 2px !important;
+      }
+    }
+  }
+}
+
+/* Fix the outline appearance */
+.mat-form-field.mat-form-field-appearance-outline {
+  .mat-form-field-wrapper {
+    .mat-form-field-flex {
+      .mat-form-field-outline {
+        @apply text-gray-300 dark:text-gray-500 #{'!important'};
+      }
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Datepicker
+/* ----------------------------------------------------------------------------------------------------- */
+/* Hover and active cell content background opacity */
+.mat-calendar-body-cell:not(.mat-calendar-body-disabled):hover,
+.cdk-keyboard-focused .mat-calendar-body-active,
+.cdk-program-focused .mat-calendar-body-active {
+  & > .mat-calendar-body-cell-content {
+    &:not(.mat-calendar-body-selected):not(
+        .mat-calendar-body-comparison-identical
+      ) {
+      @apply bg-primary bg-opacity-30 #{'!important'};
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Icon
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-icon {
+  display: inline-flex !important;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  min-width: 24px;
+  height: 24px;
+  min-height: 24px;
+  font-size: 24px;
+  line-height: 24px;
+  -webkit-appearance: none !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Inputs
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-input-element {
+  &::placeholder {
+    transition: none !important;
+    @apply text-hint #{'!important'};
+  }
+
+  &::-moz-placeholder {
+    transition: none !important;
+    @apply text-hint #{'!important'};
+  }
+
+  &::-webkit-input-placeholder {
+    transition: none !important;
+    @apply text-hint #{'!important'};
+  }
+
+  &:-ms-input-placeholder {
+    transition: none !important;
+    @apply text-hint #{'!important'};
+  }
+}
+
+/* Invalid */
+.mat-form-field-invalid {
+  .mat-input-element {
+    /* Placeholder color */
+    &::placeholder {
+      @apply text-warn #{'!important'};
+    }
+
+    &::-moz-placeholder {
+      @apply text-warn #{'!important'};
+    }
+
+    &::-webkit-input-placeholder {
+      @apply text-warn #{'!important'};
+    }
+
+    &:-ms-input-placeholder {
+      @apply text-warn #{'!important'};
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Menu
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-menu-panel {
+  min-width: 144px !important;
+
+  .mat-menu-content {
+    .mat-menu-item {
+      display: flex;
+      align-items: center;
+
+      &.mat-menu-item-submenu-trigger {
+        padding-right: 40px;
+      }
+
+      .mat-icon {
+        margin-right: 12px;
+      }
+    }
+
+    /* Divider within mat-menu */
+    mat-divider {
+      margin: 8px 0;
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Paginator
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-paginator {
+  .mat-paginator-container {
+    padding: 8px 16px;
+    justify-content: space-between;
+
+    @screen sm {
+      justify-content: normal;
+    }
+
+    /* Page size select */
+    .mat-paginator-page-size {
+      align-items: center;
+      min-height: 40px;
+      margin: 8px;
+
+      .mat-paginator-page-size-label {
+        display: none;
+        margin-right: 12px;
+
+        @screen sm {
+          display: block;
+        }
+      }
+
+      .mat-paginator-page-size-select {
+        margin: 0;
+
+        .mat-form-field-wrapper {
+          margin-bottom: 0;
+
+          .mat-form-field-flex {
+            min-height: 32px;
+            padding: 0 10px;
+          }
+        }
+      }
+    }
+
+    /* Range actions */
+    .mat-paginator-range-actions {
+      margin: 8px 0;
+
+      .mat-paginator-range-label {
+        margin-right: 16px;
+      }
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Select
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-select {
+  display: inline-flex;
+
+  .mat-select-placeholder {
+    transition: none !important;
+    @apply text-hint #{'!important'};
+  }
+
+  .mat-select-trigger {
+    display: inline-flex;
+    align-items: center;
+    width: 100%;
+    height: auto;
+
+    .mat-select-value {
+      display: flex;
+      position: relative;
+      max-width: none;
+
+      .mat-select-value-text {
+        display: inline-flex;
+
+        > * {
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+        }
+      }
+    }
+  }
+
+  .mat-select-arrow-wrapper {
+    transform: translateY(0);
+
+    .mat-select-arrow {
+      margin: 0 4px 0 2px;
+    }
+  }
+}
+
+/* Invalid */
+.mat-form-field-invalid {
+  .mat-select {
+    /* Placeholder color */
+    .mat-select-placeholder {
+      @apply text-warn #{'!important'};
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Slide Toggle
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-slide-toggle.mat-checked .mat-slide-toggle-bar {
+  background-color: rgba(var(--fuse-primary-rgb), 0.54) !important;
+}
+
+.mat-slide-toggle.mat-checked .mat-slide-toggle-thumb {
+  background-color: var(--fuse-primary) !important;
+}
+
+.mat-slide-toggle.mat-primary.mat-checked .mat-slide-toggle-bar {
+  background-color: rgba(var(--fuse-primary-500-rgb), 0.54) !important;
+}
+
+.mat-slide-toggle.mat-warn.mat-checked .mat-slide-toggle-bar {
+  background-color: rgba(var(--fuse-warn-500-rgb), 0.54) !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Stepper
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-step-icon {
+  /* Do not override the mat-icon color */
+  .mat-icon {
+    color: currentColor !important;
+  }
+}
+
+.mat-step-label,
+.mat-step-label-selected {
+  font-weight: 500 !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Tabs
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-tab-group {
+  /* No header */
+  &.fuse-mat-no-header {
+    .mat-tab-header {
+      height: 0 !important;
+      max-height: 0 !important;
+      border: none !important;
+      visibility: hidden !important;
+      opacity: 0 !important;
+    }
+  }
+
+  .mat-tab-header {
+    border-bottom: none !important;
+
+    .mat-tab-label-container {
+      padding: 0 24px;
+
+      .mat-tab-list {
+        .mat-tab-labels {
+          .mat-tab-label {
+            min-width: 0 !important;
+            height: 40px !important;
+            padding: 0 20px !important;
+            border-radius: 9999px !important;
+            @apply text-secondary;
+
+            &.mat-tab-label-active {
+              @apply bg-gray-700 bg-opacity-10 dark:bg-gray-50 dark:bg-opacity-10 #{'!important'};
+              @apply text-default #{'!important'};
+            }
+
+            + .mat-tab-label {
+              margin-left: 4px;
+            }
+
+            .mat-tab-label-content {
+              line-height: 20px;
+            }
+          }
+        }
+
+        .mat-ink-bar {
+          display: none !important;
+        }
+      }
+    }
+  }
+
+  .mat-tab-body-content {
+    padding: 24px;
+  }
+}
+
+.mat-tab-label {
+  opacity: 1 !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Textarea
+/* ----------------------------------------------------------------------------------------------------- */
+textarea.mat-input-element {
+  box-sizing: content-box !important;
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Toolbar
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-toolbar {
+  /* Apply primary contrast color */
+  &.mat-primary {
+    .mat-icon {
+      @apply text-on-primary #{'!important'};
+    }
+
+    .text-secondary {
+      @apply text-on-primary text-opacity-60 #{'!important'};
+    }
+
+    .text-hint {
+      @apply text-on-primary text-opacity-38 #{'!important'};
+    }
+
+    .text-disabled {
+      @apply text-on-primary text-opacity-38 #{'!important'};
+    }
+
+    .divider {
+      @apply text-on-primary text-opacity-12 #{'!important'};
+    }
+  }
+
+  /* Apply accent contrast color */
+  &.mat-accent {
+    .mat-icon {
+      @apply text-on-accent #{'!important'};
+    }
+
+    .text-secondary {
+      @apply text-on-accent text-opacity-60 #{'!important'};
+    }
+
+    .text-hint {
+      @apply text-on-accent text-opacity-38 #{'!important'};
+    }
+
+    .text-disabled {
+      @apply text-on-accent text-opacity-38 #{'!important'};
+    }
+
+    .divider {
+      @apply text-on-accent text-opacity-12 #{'!important'};
+    }
+  }
+
+  /* Apply warn contrast color */
+  &.mat-warn {
+    .mat-icon {
+      @apply text-on-warn #{'!important'};
+    }
+
+    .text-secondary {
+      @apply text-on-warn text-opacity-60 #{'!important'};
+    }
+
+    .text-hint {
+      @apply text-on-warn text-opacity-38 #{'!important'};
+    }
+
+    .text-disabled {
+      @apply text-on-warn text-opacity-38 #{'!important'};
+    }
+
+    .divider {
+      @apply text-on-warn text-opacity-12 #{'!important'};
+    }
+  }
+}
+
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Snackbar
+/* ----------------------------------------------------------------------------------------------------- */
+.mat-simple-snackbar-action {
+  @apply text-sky-600 #{'!important'};
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/overrides/highlightjs.scss b/transparency_dashboard_frontend/src/@fuse/styles/overrides/highlightjs.scss
new file mode 100644
index 0000000000000000000000000000000000000000..120ef830f24cdf1b0130f7b1facf9c63893cb951
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/overrides/highlightjs.scss
@@ -0,0 +1,82 @@
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Highlight.js overrides
+/* ----------------------------------------------------------------------------------------------------- */
+code[class*='language-'],
+pre[class*='language-'] {
+
+    .hljs-comment,
+    .hljs-quote {
+        color: #8B9FC1;
+        font-style: italic;
+    }
+
+    .hljs-doctag,
+    .hljs-keyword,
+    .hljs-formula {
+        color: #22D3EE;
+    }
+
+    .hljs-name {
+        color: #E879F9;
+    }
+
+    .hljs-tag {
+        color: #BAE6FD;
+    }
+
+    .hljs-section,
+    .hljs-selector-tag,
+    .hljs-deletion,
+    .hljs-subst {
+        color: #F87F71;
+    }
+
+    .hljs-literal {
+        color: #36BEFF;
+    }
+
+    .hljs-string,
+    .hljs-regexp,
+    .hljs-addition,
+    .hljs-attribute,
+    .hljs-meta-string {
+        color: #BEF264;
+    }
+
+    .hljs-built_in,
+    .hljs-class .hljs-title {
+        color: #FFD374;
+    }
+
+    .hljs-attr,
+    .hljs-variable,
+    .hljs-template-variable,
+    .hljs-type,
+    .hljs-selector-class,
+    .hljs-selector-attr,
+    .hljs-selector-pseudo,
+    .hljs-number {
+        color: #22D3EE;
+    }
+
+    .hljs-symbol,
+    .hljs-bullet,
+    .hljs-link,
+    .hljs-meta,
+    .hljs-selector-id,
+    .hljs-title {
+        color: #E879F9;
+    }
+
+    .hljs-emphasis {
+        font-style: italic;
+    }
+
+    .hljs-strong {
+        font-weight: 700;
+    }
+
+    .hljs-link {
+        text-decoration: underline;
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/overrides/perfect-scrollbar.scss b/transparency_dashboard_frontend/src/@fuse/styles/overrides/perfect-scrollbar.scss
new file mode 100644
index 0000000000000000000000000000000000000000..586b47dc90523c4185dc53c1b88bb8d0e7669233
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/overrides/perfect-scrollbar.scss
@@ -0,0 +1,69 @@
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Perfect scrollbar overrides
+/* ----------------------------------------------------------------------------------------------------- */
+.ps {
+    position: relative;
+
+    &:hover,
+    &.ps--focus,
+    &.ps--scrolling-x,
+    &.ps--scrolling-y {
+
+        > .ps__rail-x,
+        > .ps__rail-y {
+            opacity: 1;
+        }
+    }
+
+    > .ps__rail-x,
+    > .ps__rail-y {
+        z-index: 99999;
+    }
+
+    > .ps__rail-x {
+        height: 14px;
+        background: transparent !important;
+        transition: none !important;
+
+        &:hover,
+        &:focus,
+        &.ps--clicking {
+            opacity: 1;
+
+            .ps__thumb-x {
+                height: 10px;
+            }
+        }
+
+        .ps__thumb-x {
+            background: rgba(0, 0, 0, 0.5);
+            box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.15);
+            height: 6px;
+            transition: height 225ms cubic-bezier(0.25, 0.8, 0.25, 1);
+        }
+    }
+
+    > .ps__rail-y {
+        width: 14px;
+        background: transparent !important;
+        transition: none !important;
+        left: auto !important;
+
+        &:hover,
+        &:focus,
+        &.ps--clicking {
+            opacity: 1;
+
+            .ps__thumb-y {
+                width: 10px;
+            }
+        }
+
+        .ps__thumb-y {
+            background: rgba(0, 0, 0, 0.5);
+            box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.15);
+            width: 6px;
+            transition: width 225ms cubic-bezier(0.25, 0.8, 0.25, 1);
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/overrides/quill.scss b/transparency_dashboard_frontend/src/@fuse/styles/overrides/quill.scss
new file mode 100644
index 0000000000000000000000000000000000000000..61832818342fadeaf591e9e9801bb70116d875f4
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/overrides/quill.scss
@@ -0,0 +1,105 @@
+/* ----------------------------------------------------------------------------------------------------- */
+/*  @ Quill editor overrides
+/* ----------------------------------------------------------------------------------------------------- */
+.ql-toolbar {
+    border-radius: 6px 6px 0 0;
+    padding: 0 !important;
+    @apply bg-gray-100;
+    @apply border-gray-300 border-opacity-100 #{'!important'};
+
+    .dark & {
+        background-color: rgba(0, 0, 0, 0.05);
+        @apply border-gray-500 #{'!important'};
+    }
+
+    .ql-formats {
+        margin: 11px 8px !important;
+    }
+
+    .ql-picker {
+
+        &.ql-expanded {
+
+            .ql-picker-label {
+                @apply border-gray-300;
+
+                .dark & {
+                    @apply border-gray-500;
+                }
+            }
+
+            .ql-picker-options {
+                z-index: 10 !important;
+                @apply border-gray-300 bg-card;
+
+                .dark & {
+                    @apply border-gray-500;
+                }
+            }
+        }
+
+        .ql-picker-label {
+            @apply text-default;
+        }
+
+        .ql-picker-options {
+
+            .ql-picker-item {
+                @apply text-default;
+            }
+        }
+    }
+
+    .ql-stroke,
+    .ql-stroke-mitter {
+        stroke: var(--fuse-icon);
+    }
+
+    .ql-fill {
+        fill: var(--fuse-icon);
+    }
+
+    button:hover,
+    button:focus,
+    button.ql-active,
+    .ql-picker-label:hover,
+    .ql-picker-label.ql-active,
+    .ql-picker-item:hover,
+    .ql-picker-item.ql-selected {
+        @apply text-primary #{'!important'};
+
+        .ql-stroke,
+        .ql-stroke-mitter {
+            stroke: var(--fuse-primary) !important;
+        }
+
+        .ql-fill {
+            fill: var(--fuse-primary) !important;
+        }
+    }
+}
+
+.ql-container {
+    overflow: hidden;
+    border-radius: 0 0 6px 6px;
+    @apply border-gray-300 border-opacity-100 shadow-sm #{'!important'};
+
+    .dark & {
+        @apply border-gray-500 #{'!important'};
+    }
+
+    .ql-editor {
+        min-height: 160px;
+        max-height: 160px;
+        height: 160px;
+        @apply bg-card;
+
+        .dark & {
+            background-color: rgba(0, 0, 0, 0.05);
+        }
+
+        &.ql-blank::before {
+            @apply text-hint;
+        }
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/tailwind.scss b/transparency_dashboard_frontend/src/@fuse/styles/tailwind.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e4d823f94d47558eb0e41f2cd5154e8bb9c52286
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/tailwind.scss
@@ -0,0 +1,98 @@
+/* This injects Tailwind's base styles and any base styles registered by plugins. */
+@tailwind base;
+
+/* This injects additional styles into Tailwind's base styles layer. */
+@layer base {
+
+    * {
+        /* Text rendering */
+        text-rendering: optimizeLegibility;
+        -o-text-rendering: optimizeLegibility;
+        -ms-text-rendering: optimizeLegibility;
+        -moz-text-rendering: optimizeLegibility;
+        -webkit-text-rendering: optimizeLegibility;
+        -webkit-tap-highlight-color: transparent;
+
+        /* Remove the focus ring */
+        &:focus {
+            outline: none !important;
+        }
+    }
+
+    /* HTML and Body default styles */
+    html,
+    body {
+        display: flex;
+        flex-direction: column;
+        flex: 1 1 auto;
+        width: 100%;
+        min-height: 100%;
+        -webkit-font-smoothing: auto;
+        -moz-osx-font-smoothing: auto;
+    }
+
+    /* Font size */
+    html {
+        font-size: 16px;
+    }
+
+    body {
+        font-size: 0.875rem;
+    }
+
+    /* Stylistic alternates for Inter */
+    body {
+        font-feature-settings: 'salt';
+    }
+
+    /* Better spacing and border for horizontal rule */
+    hr {
+        margin: 32px 0;
+        border-bottom-width: 1px;
+    }
+
+    /* Make images and videos to take up all the available space */
+    img {
+        width: 100%;
+        vertical-align: top;
+    }
+
+    /* Fix: Disabled placeholder color is too faded on Safari */
+    input[disabled] {
+        opacity: 1;
+        -webkit-text-fill-color: currentColor;
+    }
+
+    body, .dark, .light {
+        @apply text-default bg-default #{'!important'};
+    }
+
+    *, *::before, *::after {
+        --tw-border-opacity: 1 !important;
+        border-color: rgba(var(--fuse-border-rgb), var(--tw-border-opacity));
+
+        .dark & {
+            --tw-border-opacity: 0.12 !important;
+        }
+    }
+
+    [disabled] * {
+        @apply text-disabled #{'!important'};
+    }
+
+    /* Print styles */
+    @media print {
+
+        /* Make the base font size smaller for print so everything is scaled nicely */
+        html {
+            font-size: 12px !important;
+        }
+
+        body, .dark, .light {
+            background: none !important;
+        }
+    }
+}
+
+/* This injects Tailwind's component classes and any component classes registered by plugins. */
+@tailwind components;
diff --git a/transparency_dashboard_frontend/src/@fuse/styles/themes.scss b/transparency_dashboard_frontend/src/@fuse/styles/themes.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8aaa96d50694c2362b95fb77498238057d42b244
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/styles/themes.scss
@@ -0,0 +1,167 @@
+@use '@angular/material' as mat;
+@use "sass:map";
+
+/* Include the core Angular Material styles */
+@include mat.core();
+
+/* Create a base theme without color.
+    This will globally set the density and typography for all future color themes. */
+@include mat.all-component-themes((
+    color: null,
+    density: -2,
+    typography: mat.define-typography-config(
+        $font-family: theme('fontFamily.sans'),
+        $title: mat.define-typography-level(1.25rem, 2rem, 600),
+        $body-2: mat.define-typography-level(0.875rem, 1.5rem, 600),
+        $button: mat.define-typography-level(0.875rem, 0.875rem, 500),
+        $input: mat.define-typography-level(0.875rem, 1.2857142857, 400) /* line-height: 20px */
+    )
+));
+
+/* Generate Primary, Accent and Warn palettes */
+$palettes: ();
+@each $name in (primary, accent, warn) {
+    $palettes: map.merge($palettes, (#{$name}: (
+        50: var(--fuse-#{$name}-50),
+        100: var(--fuse-#{$name}-100),
+        200: var(--fuse-#{$name}-200),
+        300: var(--fuse-#{$name}-300),
+        400: var(--fuse-#{$name}-400),
+        500: var(--fuse-#{$name}-500),
+        600: var(--fuse-#{$name}-600),
+        700: var(--fuse-#{$name}-700),
+        800: var(--fuse-#{$name}-800),
+        900: var(--fuse-#{$name}-900),
+        contrast: (
+            50: var(--fuse-on-#{$name}-50),
+            100: var(--fuse-on-#{$name}-100),
+            200: var(--fuse-on-#{$name}-200),
+            300: var(--fuse-on-#{$name}-300),
+            400: var(--fuse-on-#{$name}-400),
+            500: var(--fuse-on-#{$name}-500),
+            600: var(--fuse-on-#{$name}-600),
+            700: var(--fuse-on-#{$name}-700),
+            800: var(--fuse-on-#{$name}-800),
+            900: var(--fuse-on-#{$name}-900)
+        ),
+        default: var(--fuse-#{$name}),
+        lighter: var(--fuse-#{$name}-100),
+        darker: var(--fuse-#{$name}-700),
+        text: var(--fuse-#{$name}),
+        default-contrast: var(--fuse-on-#{$name}),
+        lighter-contrast: var(--fuse-on-#{$name}-100),
+        darker-contrast: var(--fuse-on-#{$name}-700)
+    )));
+}
+
+/* Generate Angular Material themes. Since we are using CSS Custom Properties,
+    we don't have to generate a separate Angular Material theme for each color
+    set. We can just create one light and one dark theme and then switch the
+    CSS Custom Properties to dynamically switch the colors. */
+body.light,
+body .light {
+    $base-light-theme: mat.define-light-theme((
+        color: ($palettes)
+    ));
+
+    $light-theme: (
+        color: (
+            primary: map.get(map.get($base-light-theme, color), primary),
+            accent: map.get(map.get($base-light-theme, color), accent),
+            warn: map.get(map.get($base-light-theme, color), warn),
+            is-dark: map.get(map.get($base-light-theme, color), is-dark),
+            foreground: (
+                base: #000000,
+                divider: #E2E8F0, /* slate.200 */
+                dividers: #E2E8F0, /* slate.200 */
+                disabled: #94A3B8, /* slate.400 */
+                disabled-button: #94A3B8, /* slate.400 */
+                disabled-text: #94A3B8, /* slate.400 */
+                elevation: #000000,
+                hint-text: #94A3B8, /* slate.400 */
+                secondary-text: #64748B, /* slate.500 */
+                icon: #64748B, /* slate.500 */
+                icons: #64748B, /* slate.500 */
+                mat-icon: #64748B, /* slate.500 */
+                text: #1E293B, /* slate.800 */
+                slider-min: #1E293B, /* slate.800 */
+                slider-off: #CBD5E1, /* slate.300 */
+                slider-off-active: #94A3B8 /* slate.400 */
+            ),
+            background: (
+                status-bar: #CBD5E1, /* slate.300 */
+                app-bar: #FFFFFF,
+                background: #F1F5F9, /* slate.100 */
+                hover: rgba(148, 163, 184, 0.12), /* slate.400 + opacity */
+                card: #FFFFFF,
+                dialog: #FFFFFF,
+                disabled-button: rgba(148, 163, 184, 0.38), /* slate.400 + opacity */
+                raised-button: #FFFFFF,
+                focused-button: #64748B, /* slate.500 */
+                selected-button: #E2E8F0, /* slate.200 */
+                selected-disabled-button: #E2E8F0, /* slate.200 */
+                disabled-button-toggle: #CBD5E1, /* slate.300 */
+                unselected-chip: #E2E8F0, /* slate.200 */
+                disabled-list-option: #CBD5E1, /* slate.300 */
+                tooltip: #1E293B /* slate.800 */
+            )
+        )
+    );
+
+    /* Use all-component-colors to only generate the colors */
+    @include mat.all-component-colors($light-theme);
+}
+
+body.dark,
+body .dark {
+    $base-dark-theme: mat.define-dark-theme((
+        color: ($palettes)
+    ));
+
+    $dark-theme: (
+        color: (
+            primary: map.get(map.get($base-dark-theme, color), primary),
+            accent: map.get(map.get($base-dark-theme, color), accent),
+            warn: map.get(map.get($base-dark-theme, color), warn),
+            is-dark: map.get(map.get($base-dark-theme, color), is-dark),
+            foreground: (
+                base: #FFFFFF,
+                divider: rgba(241, 245, 249, 0.12), /* slate.100 + opacity */
+                dividers: rgba(241, 245, 249, 0.12), /* slate.100 + opacity */
+                disabled: #475569, /* slate.600 */
+                disabled-button: #1E293B, /* slate.800 */
+                disabled-text: #475569, /* slate.600 */
+                elevation: #000000,
+                hint-text: #64748B, /* slate.500 */
+                secondary-text: #94A3B8, /* slate.400 */
+                icon: #F1F5F9, /* slate.100 */
+                icons: #F1F5F9, /* slate.100 */
+                mat-icon: #94A3B8, /* slate.400 */
+                text: #FFFFFF,
+                slider-min: #FFFFFF,
+                slider-off: #64748B, /* slate.500 */
+                slider-off-active: #94A3B8 /* slate.400 */
+            ),
+            background: (
+                status-bar: #0F172A, /* slate.900 */
+                app-bar: #0F172A, /* slate.900 */
+                background: #0F172A, /* slate.900 */
+                hover: rgba(255, 255, 255, 0.05),
+                card: #1E293B, /* slate.800 */
+                dialog: #1E293B, /* slate.800 */
+                disabled-button: rgba(15, 23, 42, 0.38), /* slate.900 + opacity */
+                raised-button: #0F172A, /* slate.900 */
+                focused-button: #E2E8F0, /* slate.200 */
+                selected-button: rgba(255, 255, 255, 0.05),
+                selected-disabled-button: #1E293B, /* slate.800 */
+                disabled-button-toggle: #0F172A, /* slate.900 */
+                unselected-chip: #475569, /* slate.600 */
+                disabled-list-option: #E2E8F0, /* slate.200 */
+                tooltip: #64748B /* slate.500 */
+            )
+        )
+    );
+
+    /* Use all-component-colors to only generate the colors */
+    @include mat.all-component-colors($dark-theme);
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/icon-size.js b/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/icon-size.js
new file mode 100644
index 0000000000000000000000000000000000000000..7933b5f1124d680658872bb46fcb2126c1045ffb
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/icon-size.js
@@ -0,0 +1,50 @@
+const plugin = require('tailwindcss/plugin');
+
+module.exports = plugin(
+    ({
+        matchUtilities,
+        theme
+    }) =>
+    {
+        matchUtilities(
+            {
+                'icon-size': (value) => ({
+                    width     : value,
+                    height    : value,
+                    minWidth  : value,
+                    minHeight : value,
+                    fontSize  : value,
+                    lineHeight: value,
+                    [`svg`]   : {
+                        width : value,
+                        height: value
+                    }
+                })
+            },
+            {
+                values: theme('iconSize')
+            });
+    },
+    {
+        theme: {
+            iconSize: {
+                3  : '0.75rem',
+                3.5: '0.875rem',
+                4  : '1rem',
+                4.5: '1.125rem',
+                5  : '1.25rem',
+                6  : '1.5rem',
+                7  : '1.75rem',
+                8  : '2rem',
+                10 : '2.5rem',
+                12 : '3rem',
+                14 : '3.5rem',
+                16 : '4rem',
+                18 : '4.5rem',
+                20 : '5rem',
+                22 : '5.5rem',
+                24 : '6rem'
+            }
+        }
+    }
+);
diff --git a/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/theming.js b/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/theming.js
new file mode 100644
index 0000000000000000000000000000000000000000..244b5acf20d78f8aff73e537c902cfd0a07c0444
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/theming.js
@@ -0,0 +1,232 @@
+const chroma = require('chroma-js');
+const _ = require('lodash');
+const path = require('path');
+const colors = require('tailwindcss/colors');
+const plugin = require('tailwindcss/plugin');
+const flattenColorPalette = require('tailwindcss/lib/util/flattenColorPalette').default;
+const generateContrasts = require(path.resolve(__dirname, ('../utils/generate-contrasts')));
+
+// -----------------------------------------------------------------------------------------------------
+// @ Utilities
+// -----------------------------------------------------------------------------------------------------
+
+/**
+ * Normalize the provided theme
+ *
+ * @param theme
+ */
+const normalizeTheme = (theme) =>
+{
+    return _.fromPairs(_.map(_.omitBy(theme, (palette, paletteName) => paletteName.startsWith('on') || _.isEmpty(palette)),
+        (palette, paletteName) => [
+            paletteName,
+            {
+                ...palette,
+                DEFAULT: palette['DEFAULT'] || palette[500]
+            }
+        ]
+    ));
+};
+
+/**
+ * Generates variable colors for the 'colors'
+ * configuration from the provided theme
+ *
+ * @param theme
+ */
+const generateVariableColors = (theme) =>
+{
+    // https://github.com/adamwathan/tailwind-css-variable-text-opacity-demo
+    const customPropertiesWithOpacity = (name) => ({
+        opacityVariable,
+        opacityValue
+    }) =>
+    {
+        if ( opacityValue )
+        {
+            return `rgba(var(--fuse-${name}-rgb), ${opacityValue})`;
+        }
+        if ( opacityVariable )
+        {
+            return `rgba(var(--fuse-${name}-rgb), var(${opacityVariable}, 1))`;
+        }
+        return `rgb(var(--fuse-${name}-rgb))`;
+    };
+
+    return _.fromPairs(_.flatten(_.map(_.keys(flattenColorPalette(normalizeTheme(theme))), (name) => [
+        [name, customPropertiesWithOpacity(name)],
+        [`on-${name}`, customPropertiesWithOpacity(`on-${name}`)]
+    ])));
+};
+
+/**
+ * Generate and return themes object with theme name and colors/
+ * This is useful for accessing themes from Angular (Typescript).
+ *
+ * @param themes
+ * @returns {unknown[]}
+ */
+function generateThemesObject(themes)
+{
+    const normalizedDefaultTheme = normalizeTheme(themes.default);
+    return _.map(_.cloneDeep(themes), (value, key) =>
+    {
+        const theme = normalizeTheme(value);
+        const primary = (theme && theme.primary && theme.primary.DEFAULT) ? theme.primary.DEFAULT : normalizedDefaultTheme.primary.DEFAULT;
+        const accent = (theme && theme.accent && theme.accent.DEFAULT) ? theme.accent.DEFAULT : normalizedDefaultTheme.accent.DEFAULT;
+        const warn = (theme && theme.warn && theme.warn.DEFAULT) ? theme.warn.DEFAULT : normalizedDefaultTheme.warn.DEFAULT;
+
+        return _.fromPairs([
+            [
+                key,
+                {
+                    primary,
+                    accent,
+                    warn
+                }
+            ]
+        ]);
+    });
+}
+
+// -----------------------------------------------------------------------------------------------------
+// @ FUSE TailwindCSS Main Plugin
+// -----------------------------------------------------------------------------------------------------
+const theming = plugin.withOptions((options) => ({
+        addComponents,
+        e,
+        theme
+    }) =>
+    {
+        // -----------------------------------------------------------------------------------------------------
+        // @ Map variable colors
+        // -----------------------------------------------------------------------------------------------------
+        const mapVariableColors = _.fromPairs(_.map(options.themes, (theme, themeName) => [
+            themeName === 'default' ? 'body, .theme-default' : `.theme-${e(themeName)}`,
+            _.fromPairs(_.flatten(_.map(flattenColorPalette(_.fromPairs(_.flatten(_.map(normalizeTheme(theme), (palette, paletteName) => [
+                    [
+                        e(paletteName),
+                        palette
+                    ],
+                    [
+                        `on-${e(paletteName)}`,
+                        _.fromPairs(_.map(generateContrasts(palette), (color, hue) => [hue, _.get(theme, [`on-${paletteName}`, hue]) || color]))
+                    ]
+                ])
+            ))), (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]])))
+        ]));
+
+        addComponents(mapVariableColors);
+
+        // -----------------------------------------------------------------------------------------------------
+        // @ Generate scheme based css custom properties and utility classes
+        // -----------------------------------------------------------------------------------------------------
+        const schemeCustomProps = _.map(['light', 'dark'], (colorScheme) =>
+        {
+            const isDark = colorScheme === 'dark';
+            const background = theme(`fuse.customProps.background.${colorScheme}`);
+            const foreground = theme(`fuse.customProps.foreground.${colorScheme}`);
+            const lightSchemeSelectors = 'body.light, .light, .dark .light';
+            const darkSchemeSelectors = 'body.dark, .dark, .light .dark';
+
+            return {
+                [(isDark ? darkSchemeSelectors : lightSchemeSelectors)]: {
+
+                    /**
+                     * If a custom property is not available, browsers will use
+                     * the fallback value. In this case, we want to use '--is-dark'
+                     * as the indicator of a dark theme so we can use it like this:
+                     * background-color: var(--is-dark, red);
+                     *
+                     * If we set '--is-dark' as "true" on dark themes, the above rule
+                     * won't work because of the said "fallback value" logic. Therefore,
+                     * we set the '--is-dark' to "false" on light themes and not set it
+                     * all on dark themes so that the fallback value can be used on
+                     * dark themes.
+                     *
+                     * On light themes, since '--is-dark' exists, the above rule will be
+                     * interpolated as:
+                     * "background-color: false"
+                     *
+                     * On dark themes, since '--is-dark' doesn't exist, the fallback value
+                     * will be used ('red' in this case) and the rule will be interpolated as:
+                     * "background-color: red"
+                     *
+                     * It's easier to understand and remember like this.
+                     */
+                    ...(!isDark ? {'--is-dark': 'false'} : {}),
+
+                    // Generate custom properties from customProps
+                    ..._.fromPairs(_.flatten(_.map(background, (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]]))),
+                    ..._.fromPairs(_.flatten(_.map(foreground, (value, key) => [[`--fuse-${e(key)}`, value], [`--fuse-${e(key)}-rgb`, chroma(value).rgb().join(',')]])))
+                }
+            };
+        });
+
+        const schemeUtilities = (() =>
+        {
+            // Generate general styles & utilities
+            return {};
+        })();
+
+        addComponents(schemeCustomProps);
+        addComponents(schemeUtilities);
+    },
+    (options) =>
+    {
+        return {
+            theme   : {
+                extend: {
+                    colors: generateVariableColors(options.themes.default)
+                },
+                fuse  : {
+                    customProps: {
+                        background: {
+                            light: {
+                                'bg-app-bar'   : '#FFFFFF',
+                                'bg-card'      : '#FFFFFF',
+                                'bg-default'   : colors.slate[100],
+                                'bg-dialog'    : '#FFFFFF',
+                                'bg-hover'     : chroma(colors.slate[400]).alpha(0.12).css(),
+                                'bg-status-bar': colors.slate[300]
+                            },
+                            dark : {
+                                'bg-app-bar'   : colors.slate[900],
+                                'bg-card'      : colors.slate[800],
+                                'bg-default'   : colors.slate[900],
+                                'bg-dialog'    : colors.slate[800],
+                                'bg-hover'     : 'rgba(255, 255, 255, 0.05)',
+                                'bg-status-bar': colors.slate[900]
+                            }
+                        },
+                        foreground: {
+                            light: {
+                                'text-default'  : colors.slate[800],
+                                'text-secondary': colors.slate[500],
+                                'text-hint'     : colors.slate[400],
+                                'text-disabled' : colors.slate[400],
+                                'border'        : colors.slate[200],
+                                'divider'       : colors.slate[200],
+                                'icon'          : colors.slate[500],
+                                'mat-icon'      : colors.slate[500]
+                            },
+                            dark : {
+                                'text-default'  : '#FFFFFF',
+                                'text-secondary': colors.slate[400],
+                                'text-hint'     : colors.slate[500],
+                                'text-disabled' : colors.slate[600],
+                                'border'        : chroma(colors.slate[100]).alpha(0.12).css(),
+                                'divider'       : chroma(colors.slate[100]).alpha(0.12).css(),
+                                'icon'          : colors.slate[400],
+                                'mat-icon'      : colors.slate[400]
+                            }
+                        }
+                    },
+                    themes     : generateThemesObject(options.themes)
+                }
+            }
+        };
+    }
+);
+
+module.exports = theming;
diff --git a/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/utilities.js b/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/utilities.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b1afb9dede3428e427b3ad3accae33de1860377
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/tailwind/plugins/utilities.js
@@ -0,0 +1,67 @@
+const plugin = require('tailwindcss/plugin');
+
+module.exports = plugin(({
+    addComponents
+}) =>
+{
+    /*
+    * Add base components. These are very important for everything to look
+    * correct. We are adding these to the 'components' layer because they must
+    * be defined before pretty much everything else.
+    */
+    addComponents(
+        {
+            '.mat-icon'       : {
+                '--tw-text-opacity': '1',
+                color              : 'rgba(var(--fuse-mat-icon-rgb), var(--tw-text-opacity))'
+            },
+            '.text-default'   : {
+                '--tw-text-opacity': '1 !important',
+                color              : 'rgba(var(--fuse-text-default-rgb), var(--tw-text-opacity)) !important'
+            },
+            '.text-secondary' : {
+                '--tw-text-opacity': '1 !important',
+                color              : 'rgba(var(--fuse-text-secondary-rgb), var(--tw-text-opacity)) !important'
+            },
+            '.text-hint'      : {
+                '--tw-text-opacity': '1 !important',
+                color              : 'rgba(var(--fuse-text-hint-rgb), var(--tw-text-opacity)) !important'
+            },
+            '.text-disabled'  : {
+                '--tw-text-opacity': '1 !important',
+                color              : 'rgba(var(--fuse-text-disabled-rgb), var(--tw-text-opacity)) !important'
+            },
+            '.divider'        : {
+                color: 'var(--fuse-divider) !important'
+            },
+            '.bg-card'        : {
+                '--tw-bg-opacity': '1 !important',
+                backgroundColor  : 'rgba(var(--fuse-bg-card-rgb), var(--tw-bg-opacity)) !important'
+            },
+            '.bg-default'     : {
+                '--tw-bg-opacity': '1 !important',
+                backgroundColor  : 'rgba(var(--fuse-bg-default-rgb), var(--tw-bg-opacity)) !important'
+            },
+            '.bg-dialog'      : {
+                '--tw-bg-opacity': '1 !important',
+                backgroundColor  : 'rgba(var(--fuse-bg-dialog-rgb), var(--tw-bg-opacity)) !important'
+            },
+            '.ring-bg-default': {
+                '--tw-ring-opacity': '1 !important',
+                '--tw-ring-color'  : 'rgba(var(--fuse-bg-default-rgb), var(--tw-ring-opacity)) !important'
+            },
+            '.ring-bg-card'   : {
+                '--tw-ring-opacity': '1 !important',
+                '--tw-ring-color'  : 'rgba(var(--fuse-bg-card-rgb), var(--tw-ring-opacity)) !important'
+            }
+        }
+    );
+
+    addComponents(
+        {
+            '.bg-hover': {
+                backgroundColor: 'var(--fuse-bg-hover) !important'
+            }
+        }
+    );
+});
diff --git a/transparency_dashboard_frontend/src/@fuse/tailwind/utils/generate-contrasts.js b/transparency_dashboard_frontend/src/@fuse/tailwind/utils/generate-contrasts.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd98c3c82815d29f86bfa8fca05c12530431459a
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/tailwind/utils/generate-contrasts.js
@@ -0,0 +1,31 @@
+const chroma = require('chroma-js');
+const _ = require('lodash');
+
+/**
+ * Generates contrasting counterparts of the given palette.
+ * The provided palette must be in the same format with
+ * default Tailwind color palettes.
+ *
+ * @param palette
+ * @private
+ */
+const generateContrasts = (palette) =>
+{
+    const lightColor = '#FFFFFF';
+    let darkColor = '#FFFFFF';
+
+    // Iterate through the palette to find the darkest color
+    _.forEach(palette, ((color) =>
+    {
+        darkColor = chroma.contrast(color, '#FFFFFF') > chroma.contrast(darkColor, '#FFFFFF') ? color : darkColor;
+    }));
+
+    // Generate the contrasting colors
+    return _.fromPairs(_.map(palette, ((color, hue) => [
+            hue,
+            chroma.contrast(color, darkColor) > chroma.contrast(color, lightColor) ? darkColor : lightColor
+        ]
+    )));
+};
+
+module.exports = generateContrasts;
diff --git a/transparency_dashboard_frontend/src/@fuse/tailwind/utils/generate-palette.js b/transparency_dashboard_frontend/src/@fuse/tailwind/utils/generate-palette.js
new file mode 100644
index 0000000000000000000000000000000000000000..e2a8c90c106ccd2382e8fec1e785586903f4cce3
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/tailwind/utils/generate-palette.js
@@ -0,0 +1,100 @@
+const chroma = require('chroma-js');
+const _ = require('lodash');
+
+/**
+ * Generates palettes from the provided configuration.
+ * Accepts a single color string or a Tailwind-like
+ * color object. If provided Tailwind-like color object,
+ * it must have a 500 hue level.
+ *
+ * @param config
+ */
+const generatePalette = (config) =>
+{
+    // Prepare an empty palette
+    const palette = {
+        50 : null,
+        100: null,
+        200: null,
+        300: null,
+        400: null,
+        500: null,
+        600: null,
+        700: null,
+        800: null,
+        900: null
+    };
+
+    // If a single color is provided,
+    // assign it to the 500
+    if ( _.isString(config) )
+    {
+        palette[500] = chroma.valid(config) ? config : null;
+    }
+
+    // If a partial palette is provided,
+    // assign the values
+    if ( _.isPlainObject(config) )
+    {
+        if ( !chroma.valid(config[500]) )
+        {
+            throw new Error('You must have a 500 hue in your palette configuration! Make sure the main color of your palette is marked as 500.');
+        }
+
+        // Remove everything that is not a hue/color entry
+        config = _.pick(config, Object.keys(palette));
+
+        // Merge the values
+        _.mergeWith(palette, config, (objValue, srcValue) => chroma.valid(srcValue) ? srcValue : null);
+    }
+
+    // Prepare the colors array
+    const colors = Object.values(palette).filter((color) => color);
+
+    // Generate a very dark and a very light versions of the
+    // default color to use them as the boundary colors rather
+    // than using pure white and pure black. This will stop
+    // in between colors' hue values to slipping into the grays.
+    colors.unshift(
+        chroma.scale(['white', palette[500]])
+            .domain([0, 1])
+            .mode("lrgb")
+            .colors(50)[1]
+    );
+    colors.push(
+        chroma.scale(['black', palette[500]])
+            .domain([0, 1])
+            .mode("lrgb")
+            .colors(10)[1]
+    );
+
+    // Prepare the domains array
+    const domain = [
+        0,
+        ...Object.entries(palette)
+            .filter(([key, value]) => value)
+            .map(([key]) => parseInt(key) / 1000),
+        1
+    ];
+
+    // Generate the color scale
+    const scale = chroma.scale(colors)
+        .domain(domain)
+        .mode('lrgb');
+
+    // Build and return the final palette
+    return {
+        50 : scale(0.05).hex(),
+        100: scale(0.1).hex(),
+        200: scale(0.2).hex(),
+        300: scale(0.3).hex(),
+        400: scale(0.4).hex(),
+        500: scale(0.5).hex(),
+        600: scale(0.6).hex(),
+        700: scale(0.7).hex(),
+        800: scale(0.8).hex(),
+        900: scale(0.9).hex()
+    };
+};
+
+module.exports = generatePalette;
diff --git a/transparency_dashboard_frontend/src/@fuse/validators/index.ts b/transparency_dashboard_frontend/src/@fuse/validators/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5e4748d357a80e8e14daae72d9e62c5179080047
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/validators/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/validators/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/validators/public-api.ts b/transparency_dashboard_frontend/src/@fuse/validators/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc16900a369fb1c83ca0f961d335f3d50d9791db
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/validators/public-api.ts
@@ -0,0 +1 @@
+export * from '@fuse/validators/validators';
diff --git a/transparency_dashboard_frontend/src/@fuse/validators/validators.ts b/transparency_dashboard_frontend/src/@fuse/validators/validators.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5fb1f60fad276dd096fab16c0582bd716ca8cc14
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/validators/validators.ts
@@ -0,0 +1,59 @@
+import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
+
+export class FuseValidators
+{
+    /**
+     * Check for empty (optional fields) values
+     *
+     * @param value
+     */
+    static isEmptyInputValue(value: any): boolean
+    {
+        return value == null || value.length === 0;
+    }
+
+    /**
+     * Must match validator
+     *
+     * @param controlPath A dot-delimited string values that define the path to the control.
+     * @param matchingControlPath A dot-delimited string values that define the path to the matching control.
+     */
+    static mustMatch(controlPath: string, matchingControlPath: string): ValidatorFn
+    {
+        return (formGroup: AbstractControl): ValidationErrors | null => {
+
+            // Get the control and matching control
+            const control = formGroup.get(controlPath);
+            const matchingControl = formGroup.get(matchingControlPath);
+
+            // Return if control or matching control doesn't exist
+            if ( !control || !matchingControl )
+            {
+                return null;
+            }
+
+            // Delete the mustMatch error to reset the error on the matching control
+            if ( matchingControl.hasError('mustMatch') )
+            {
+                delete matchingControl.errors.mustMatch;
+                matchingControl.updateValueAndValidity();
+            }
+
+            // Don't validate empty values on the matching control
+            // Don't validate if values are matching
+            if ( this.isEmptyInputValue(matchingControl.value) || control.value === matchingControl.value )
+            {
+                return null;
+            }
+
+            // Prepare the validation errors
+            const errors = {mustMatch: true};
+
+            // Set the validation error on the matching control
+            matchingControl.setErrors(errors);
+
+            // Return the errors
+            return errors;
+        };
+    }
+}
diff --git a/transparency_dashboard_frontend/src/@fuse/version/fuse-version.ts b/transparency_dashboard_frontend/src/@fuse/version/fuse-version.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a485a4a642b1e7ee9705b0d562bc2a943032b7e
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/version/fuse-version.ts
@@ -0,0 +1,3 @@
+import { Version } from '@fuse/version/version';
+
+export const FUSE_VERSION = new Version('14.2.0').full;
diff --git a/transparency_dashboard_frontend/src/@fuse/version/index.ts b/transparency_dashboard_frontend/src/@fuse/version/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aaf271872310c4770d1432bda6b89c5376a9a59b
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/version/index.ts
@@ -0,0 +1 @@
+export * from '@fuse/version/public-api';
diff --git a/transparency_dashboard_frontend/src/@fuse/version/public-api.ts b/transparency_dashboard_frontend/src/@fuse/version/public-api.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8645bbfcf0a456ccf7dae0966d8014708c401ebd
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/version/public-api.ts
@@ -0,0 +1,2 @@
+export * from '@fuse/version/fuse-version';
+export * from '@fuse/version/version';
diff --git a/transparency_dashboard_frontend/src/@fuse/version/version.ts b/transparency_dashboard_frontend/src/@fuse/version/version.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6e7a8d4b83269286bbcb855de48d459d5e00a003
--- /dev/null
+++ b/transparency_dashboard_frontend/src/@fuse/version/version.ts
@@ -0,0 +1,21 @@
+/**
+ * Derived from Angular's version class
+ */
+export class Version
+{
+    public readonly full: string;
+    public readonly major: string;
+    public readonly minor: string;
+    public readonly patch: string;
+
+    /**
+     * Constructor
+     */
+    constructor(public version: string)
+    {
+        this.full = version;
+        this.major = version.split('.')[0];
+        this.minor = version.split('.')[1];
+        this.patch = version.split('.').slice(2).join('.');
+    }
+}