diff --git a/package-lock.json b/package-lock.json
index 347cf0590622cf40ad377963bc60d3021a6e6da6..e00ab1ff2f806704e71a481d74e1ae4d11852c03 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
         "@angular/platform-browser-dynamic": "^19.0.0",
         "@angular/router": "^19.0.0",
         "@primeng/themes": "^19.0.6",
+        "ngx-skeleton-loader": "^10.0.0",
         "primeicons": "^7.0.0",
         "primeng": "^19.0.6",
         "rxjs": "~7.8.0",
@@ -9746,6 +9747,18 @@
       "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
       "dev": true
     },
+    "node_modules/ngx-skeleton-loader": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/ngx-skeleton-loader/-/ngx-skeleton-loader-10.0.0.tgz",
+      "integrity": "sha512-TYrWLrdRtzoZoPzurNDUJdAbdyplqgyDztCefEi+clHl5MSumwG4NrGxZC1OVxz7RitomhnF7wTM8T/j+tdwXw==",
+      "dependencies": {
+        "tslib": "^2.0.0"
+      },
+      "peerDependencies": {
+        "@angular/common": ">=18.0.0",
+        "@angular/core": ">=18.0.0"
+      }
+    },
     "node_modules/node-addon-api": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
diff --git a/package.json b/package.json
index 19bc81ac924236d214f85509e39d04f61c735a4a..aa94cd0823438fb7def27972152e47ab9500b9ac 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
     "@angular/platform-browser-dynamic": "^19.0.0",
     "@angular/router": "^19.0.0",
     "@primeng/themes": "^19.0.6",
+    "ngx-skeleton-loader": "^10.0.0",
     "primeicons": "^7.0.0",
     "primeng": "^19.0.6",
     "rxjs": "~7.8.0",
diff --git a/src/app/api/configuration.api.ts b/src/app/api/configuration.api.ts
index 691912a047e8efae3d3d49be524a18cbdd1b7f82..064fd8503d330dfd8f8e5a438860fbe2ba3e064c 100644
--- a/src/app/api/configuration.api.ts
+++ b/src/app/api/configuration.api.ts
@@ -1,8 +1,55 @@
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { map, Observable, tap } from 'rxjs';
+import {
+  Configuration,
+  ConfigurationDto,
+  configurationToDto,
+  dtoToConfiguration,
+} from '../models/configuration';
+import { ApiResponse } from '../models/api-response';
 
 export const CONFIGURATION_API_URL = '/api/v1/configuration';
 
 @Injectable({
   providedIn: 'root',
 })
-export class ConfigurationApi {}
+export class ConfigurationApi {
+  private readonly httpClient = inject(HttpClient);
+
+  fetch(): Observable<Configuration[]> {
+    return this.httpClient
+      .get<ApiResponse<ConfigurationDto[]>>(CONFIGURATION_API_URL)
+      .pipe(map(dto => dto.data.map(dtoToConfiguration)));
+  }
+
+  find(configName: string): Observable<Configuration> {
+    return this.httpClient
+      .get<
+        ApiResponse<ConfigurationDto>
+      >(`${CONFIGURATION_API_URL}/${configName}`)
+      .pipe(map(dto => dtoToConfiguration(dto.data)));
+  }
+
+  save(configuration: Configuration): Observable<Configuration> {
+    return this.httpClient
+      .put<
+        ApiResponse<ConfigurationDto>
+      >(CONFIGURATION_API_URL, configurationToDto(configuration))
+      .pipe(map(dto => dtoToConfiguration(dto.data)));
+  }
+
+  apply(configurationId: string): Observable<Configuration> {
+    return this.httpClient
+      .put<
+        ApiResponse<ConfigurationDto>
+      >(`${CONFIGURATION_API_URL}/${configurationId}/apply`, {})
+      .pipe(map(dto => dtoToConfiguration(dto.data)));
+  }
+
+  delete(configurationId: string): Observable<void> {
+    return this.httpClient.delete<void>(
+      `${CONFIGURATION_API_URL}/${configurationId}`
+    );
+  }
+}
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index c0b0c96217007634009491d119e61ef4421eb996..3112c3999012f8165db3d9682b94ec754415a45a 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -3,7 +3,7 @@
 
 .content {
   display: flex;
-  height: calc(100vh - vars.$headerHeight);
+  min-height: calc(100vh - vars.$headerHeight);
   background-color: map.get(vars.$grey, 30);
   margin-top: vars.$headerHeight;
 }
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
deleted file mode 100644
index 20a9c435dacb235ade1fe2ad42de43f2d5647ddd..0000000000000000000000000000000000000000
--- a/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { AppComponent } from './app.component';
-
-describe('AppComponent', () => {
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [AppComponent],
-    }).compileComponents();
-  });
-
-  it('should create the app', () => {
-    const fixture = TestBed.createComponent(AppComponent);
-    const app = fixture.componentInstance;
-    expect(app).toBeTruthy();
-  });
-
-  it(`should have the 'miars-frontend' title`, () => {
-    const fixture = TestBed.createComponent(AppComponent);
-    const app = fixture.componentInstance;
-    expect(app.title).toEqual('miars-frontend');
-  });
-
-  it('should render title', () => {
-    const fixture = TestBed.createComponent(AppComponent);
-    fixture.detectChanges();
-    const compiled = fixture.nativeElement as HTMLElement;
-    expect(compiled.querySelector('h1')?.textContent).toContain('Hello, miars-frontend');
-  });
-});
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
index 35ca21e32aebc0e2cdd09ae2a6a3a0c3f7bea681..40e49cbd7de244f64cdea9137b81fb9edfb9e497 100644
--- a/src/app/app.config.ts
+++ b/src/app/app.config.ts
@@ -3,13 +3,17 @@ import { provideRouter } from '@angular/router';
 import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
 import { providePrimeNG } from 'primeng/config';
 
+import { MockInterceptor } from './interceptor/mock-interceptor';
 import { routes } from './app.routes';
+import { provideHttpClient, withInterceptors } from '@angular/common/http';
 
 export const appConfig: ApplicationConfig = {
   providers: [
     provideZoneChangeDetection({ eventCoalescing: true }),
     provideRouter(routes),
     provideAnimationsAsync(),
-    providePrimeNG(), provideAnimationsAsync(),
+    provideHttpClient(withInterceptors([MockInterceptor])),
+    providePrimeNG(),
+    provideAnimationsAsync(),
   ],
 };
diff --git a/src/app/components/button/button.component.html b/src/app/components/button/button.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..566160711f0f5c60abde951cf4ac0b7c759a5ebc
--- /dev/null
+++ b/src/app/components/button/button.component.html
@@ -0,0 +1,5 @@
+<button
+  [ngClass]="{ 'button-secondary': secondary() !== null }"
+  mat-flat-button>
+  <ng-content />
+</button>
diff --git a/src/app/components/button/button.component.scss b/src/app/components/button/button.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..40311a029f887efb3cf789fa1e716fddf4c70428
--- /dev/null
+++ b/src/app/components/button/button.component.scss
@@ -0,0 +1,19 @@
+@use '../../../vars';
+@use 'sass:map';
+
+:host {
+  button {
+    text-transform: uppercase;
+    font-size: map.get(vars.$text, 'md');
+    padding: map.get(vars.$spacing, 'xl') map.get(vars.$spacing, 'xxxl');
+  }
+
+  * {
+    background-color: vars.$textPrimary;
+  }
+
+  .button-secondary {
+    color: vars.$textPrimary;
+    background-color: map.get(vars.$grey, 20);
+  }
+}
diff --git a/src/app/components/button/button.component.ts b/src/app/components/button/button.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc2d400904cb38c0b53075e7e8d254c8c2b20f6b
--- /dev/null
+++ b/src/app/components/button/button.component.ts
@@ -0,0 +1,13 @@
+import { Component, input } from '@angular/core';
+import { MatButton } from '@angular/material/button';
+import { NgClass } from '@angular/common';
+
+@Component({
+  selector: 'app-button',
+  imports: [MatButton, NgClass],
+  templateUrl: './button.component.html',
+  styleUrl: './button.component.scss',
+})
+export class ButtonComponent {
+  secondary = input<string | null>(null);
+}
diff --git a/src/app/components/configuration-form/configuration-form.builder.ts b/src/app/components/configuration-form/configuration-form.builder.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b785bf79cdb43c33990cfcdfa0b306bea5394dfb
--- /dev/null
+++ b/src/app/components/configuration-form/configuration-form.builder.ts
@@ -0,0 +1,87 @@
+import {
+  FormBuilder,
+  FormControl,
+  FormGroup,
+  Validators,
+} from '@angular/forms';
+import { inject, Injectable } from '@angular/core';
+import { Configuration } from '../../models/configuration';
+
+export const SizeRanges: [number, number][] = [
+  [0, 63],
+  [64, 127],
+  [128, 255],
+  [256, 511],
+  [512, 1023],
+  [1024, 1518],
+  [1519, Infinity],
+];
+
+export const Protocols: string[] = [
+  'ARP',
+  'IPv4',
+  'IPv6',
+  'ICMP',
+  'TCP',
+  'UDP',
+];
+
+export type ConfigurationForm = FormGroup<{
+  id: FormControl<string | undefined>;
+  name: FormControl<string>;
+  source_mac: FormControl<string[]>;
+  dest_mac: FormControl<string[]>;
+  frame_ranges: FormControl<[number, number][]>;
+  protocols: FormControl<string[]>;
+
+  source_mac_control: FormControl<string>;
+  dest_mac_control: FormControl<string>;
+}>;
+
+@Injectable({
+  providedIn: 'root',
+})
+export class ConfigurationFormBuilder {
+  formBuilder = inject(FormBuilder);
+
+  create(configuration?: Configuration): ConfigurationForm {
+    const fb = this.formBuilder.nonNullable;
+
+    return fb.group({
+      id: fb.control<string | undefined>(configuration?.id),
+      name: fb.control<string>(configuration?.name || '', {
+        validators: [Validators.required],
+      }),
+      source_mac: fb.control<string[]>(configuration?.mac_source || []),
+      dest_mac: fb.control<string[]>(configuration?.mac_destination || []),
+      frame_ranges: fb.control<[number, number][]>(
+        configuration?.frame_ranges || []
+      ),
+      protocols: fb.control<string[]>(configuration?.protocols || []),
+
+      source_mac_control: fb.control<string>('', {
+        validators: [
+          Validators.pattern(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/),
+        ],
+      }),
+      dest_mac_control: fb.control<string>('', {
+        validators: [
+          Validators.pattern(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/),
+        ],
+      }),
+    });
+  }
+
+  toValue(form: ConfigurationForm): Configuration {
+    const data = form.value;
+    return <Configuration>{
+      id: data.id,
+      name: data.name,
+      is_applied: false,
+      mac_source: data.source_mac || [],
+      mac_destination: data.dest_mac || [],
+      frame_ranges: data.frame_ranges || [],
+      protocols: data.protocols || [],
+    };
+  }
+}
diff --git a/src/app/components/configuration-form/configuration-form.component.html b/src/app/components/configuration-form/configuration-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4a1e25ba495884c5565a46a7d2d05f4c875f1a1c
--- /dev/null
+++ b/src/app/components/configuration-form/configuration-form.component.html
@@ -0,0 +1,114 @@
+<app-configuration-template>
+  <div class="configuration-form__header">
+    <mat-form-field>
+      <mat-label>Configuration name</mat-label>
+      <input matInput [formControl]="form.controls.name" />
+      @if (form.controls.name.hasError('required')) {
+        <mat-error>Name is required!</mat-error>
+      }
+    </mat-form-field>
+    <div class="configuration-form__header-actions">
+      <app-button secondary (click)="cancel.emit()">Cancel</app-button>
+      <app-button (click)="onSubmit()">Save</app-button>
+    </div>
+  </div>
+
+  <div mac-source class="configuration__mac">
+    <div class="configuration__mac-title">
+      <mat-form-field>
+        <mat-label>Source</mat-label>
+        <input
+          [formControl]="form.controls.source_mac_control"
+          matInput
+          #sourceInput
+          (keydown.enter)="addSourceMac(sourceInput.value)" />
+        <mat-hint>Press Enter to add</mat-hint>
+        @if (form.controls.source_mac_control.hasError('pattern')) {
+          <mat-error>Mac address is incorrect!</mat-error>
+        }
+      </mat-form-field>
+    </div>
+    <div class="configuration__mac-content">
+      @for (mac of form.controls.source_mac.value; track $index) {
+        <app-pill [removable]="true" (click)="removeSourceMac($index)">{{
+          mac
+        }}</app-pill>
+      } @empty {
+        <app-pill>All addresses</app-pill>
+      }
+    </div>
+  </div>
+  <div mac-destination class="configuration__mac">
+    <div class="configuration__mac-title">
+      <mat-form-field>
+        <mat-label>Destination</mat-label>
+        <input
+          [formControl]="form.controls.dest_mac_control"
+          matInput
+          #destInput
+          (keydown.enter)="addDestMac(destInput.value)" />
+        <mat-hint>Press Enter to add</mat-hint>
+        @if (form.controls.dest_mac_control.hasError('pattern')) {
+          <mat-error>Mac address is incorrect!</mat-error>
+        }
+      </mat-form-field>
+    </div>
+    <div class="configuration__mac-content">
+      @for (mac of form.controls.dest_mac.value; track $index) {
+        <app-pill [removable]="true" (click)="removeDestMac($index)">{{
+          mac
+        }}</app-pill>
+      } @empty {
+        <app-pill>All addresses</app-pill>
+      }
+    </div>
+  </div>
+  <div frames>
+    @if (sizeRangeOptions().length) {
+      <mat-form-field>
+        <mat-label>Add size range</mat-label>
+        <mat-select
+          (selectionChange)="
+            addFrameRange($event.value); selectRange.value = undefined
+          "
+          #selectRange>
+          @for (range of sizeRangeOptions(); track $index) {
+            <mat-option [value]="range"
+              >{{ range[0] }} — {{ range[1] }}</mat-option
+            >
+          }
+        </mat-select>
+      </mat-form-field>
+    }
+    @for (range of form.controls.frame_ranges.value; track $index) {
+      <app-pill [removable]="true" (click)="removeFrameRange($index)"
+        >{{ range[0] }} — {{ range[1] }}</app-pill
+      >
+    } @empty {
+      <app-pill>All sizes</app-pill>
+    }
+  </div>
+  <div protocols>
+    @if (protocolOptions().length) {
+      <mat-form-field>
+        <mat-label>Add protocol</mat-label>
+        <mat-select
+          (selectionChange)="
+            addProtocol($event.value); selectProtocol.value = undefined
+          "
+          #selectProtocol>
+          @for (protocol of protocolOptions(); track $index) {
+            <mat-option [value]="protocol">{{ protocol }}</mat-option>
+          }
+        </mat-select>
+      </mat-form-field>
+    }
+    @for (protocol of form.controls.protocols.value; track $index) {
+      <app-pill [removable]="true" (click)="removeProtocol($index)">{{
+        protocol
+      }}</app-pill>
+    } @empty {
+      <app-pill>None</app-pill>
+    }
+  </div>
+</app-configuration-template>
diff --git a/src/app/components/configuration-form/configuration-form.component.scss b/src/app/components/configuration-form/configuration-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5e6b0cfaabdeb4ef4feea82e9427c9ccabebd075
--- /dev/null
+++ b/src/app/components/configuration-form/configuration-form.component.scss
@@ -0,0 +1,31 @@
+@use '../../../vars';
+@use 'sass:map';
+
+:host {
+  .configuration {
+    &-form {
+      &__header {
+        display: flex;
+        justify-content: space-between;
+
+        &-actions {
+          display: flex;
+          gap: map.get(vars.$spacing, 'md');
+        }
+      }
+    }
+    &__mac {
+      display: flex;
+      justify-content: space-between;
+
+      &-title {
+        color: map.get(vars.$grey, 100);
+        font-size: map.get(vars.$text, 'lg');
+      }
+
+      &-content {
+        width: calc(100% - 200px);
+      }
+    }
+  }
+}
diff --git a/src/app/components/configuration-form/configuration-form.component.ts b/src/app/components/configuration-form/configuration-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0996c777d0ac25d824cd26344fc76e5d9722e5e8
--- /dev/null
+++ b/src/app/components/configuration-form/configuration-form.component.ts
@@ -0,0 +1,128 @@
+import {
+  Component,
+  inject,
+  input,
+  OnInit,
+  output,
+  signal,
+} from '@angular/core';
+import { ConfigurationTemplateComponent } from '../configuration-template/configuration-template.component';
+import { Configuration } from '../../models/configuration';
+import {
+  ConfigurationForm,
+  ConfigurationFormBuilder,
+  Protocols,
+  SizeRanges,
+} from './configuration-form.builder';
+import {
+  MatError,
+  MatFormField,
+  MatHint,
+  MatLabel,
+} from '@angular/material/form-field';
+import { MatInput } from '@angular/material/input';
+import { MatOption, MatSelect } from '@angular/material/select';
+import { ReactiveFormsModule } from '@angular/forms';
+import { PillComponent } from '../pill/pill.component';
+import { ButtonComponent } from '../button/button.component';
+
+@Component({
+  selector: 'app-configuration-form',
+  imports: [
+    ConfigurationTemplateComponent,
+    MatFormField,
+    MatInput,
+    MatLabel,
+    MatSelect,
+    MatOption,
+    ReactiveFormsModule,
+    PillComponent,
+    MatError,
+    MatHint,
+    ButtonComponent,
+  ],
+  templateUrl: './configuration-form.component.html',
+  styleUrl: './configuration-form.component.scss',
+})
+export class ConfigurationFormComponent implements OnInit {
+  configurationFormBuilder = inject(ConfigurationFormBuilder);
+  configuration = input<Configuration>();
+  form!: ConfigurationForm;
+
+  submit = output<Configuration>();
+  cancel = output<void>();
+
+  sizeRangeOptions = signal<[number, number][]>(SizeRanges);
+  protocolOptions = signal<string[]>(Protocols);
+
+  ngOnInit() {
+    this.form = this.configurationFormBuilder.create(this.configuration());
+    this.updateSizeRangeOptions();
+    this.updateProtocolOptions();
+  }
+
+  onSubmit() {
+    if (this.form.valid) {
+      this.submit.emit(this.configurationFormBuilder.toValue(this.form));
+    } else {
+      this.form.markAsDirty();
+    }
+  }
+
+  addSourceMac(sourceMac: string) {
+    if (!this.form.controls.source_mac_control.errors) {
+      this.form.controls.source_mac.value?.push(sourceMac);
+    }
+  }
+
+  removeSourceMac(index: number) {
+    this.form.controls.source_mac.value?.splice(index, 1);
+  }
+
+  addDestMac(destMac: string) {
+    if (!this.form.controls.dest_mac_control.errors) {
+      this.form.controls.dest_mac.value?.push(destMac);
+    }
+  }
+
+  removeDestMac(index: number) {
+    this.form.controls.dest_mac.value?.splice(index, 1);
+  }
+
+  addFrameRange(frameRange: [number, number]) {
+    this.form.controls.frame_ranges.value?.push(frameRange);
+    this.updateSizeRangeOptions();
+  }
+
+  removeFrameRange(index: number) {
+    this.form.controls.frame_ranges.value?.splice(index, 1);
+    this.updateSizeRangeOptions();
+  }
+
+  addProtocol(protocol: string) {
+    this.form.controls.protocols.value?.push(protocol);
+    this.updateProtocolOptions();
+  }
+
+  removeProtocol(index: number) {
+    this.form.controls.protocols.value?.splice(index, 1);
+    this.updateProtocolOptions();
+  }
+
+  updateSizeRangeOptions() {
+    const chosenRanges = this.form.controls.frame_ranges.value ?? [];
+    const sizeRanges = SizeRanges.filter(
+      range =>
+        !chosenRanges.some(cr => cr[0] === range[0] && cr[1] === range[1])
+    );
+    this.sizeRangeOptions.set(sizeRanges);
+  }
+
+  updateProtocolOptions() {
+    const chosenProtocols = this.form.controls.protocols.value ?? [];
+    const protocols = Protocols.filter(
+      protocol => !chosenProtocols.includes(protocol)
+    );
+    this.protocolOptions.set(protocols);
+  }
+}
diff --git a/src/app/components/configuration-template/configuration-template.component.html b/src/app/components/configuration-template/configuration-template.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..98213f63c0c5a229ff79d1dbdaafa6dac5fb35cf
--- /dev/null
+++ b/src/app/components/configuration-template/configuration-template.component.html
@@ -0,0 +1,25 @@
+<div class="configuration-header">
+  <ng-content />
+</div>
+<div class="configuration-section">
+  <div class="configuration-section__title">MAC address</div>
+  <div class="configuration-section__card">
+    <ng-content select="[mac-source]" />
+    <mat-divider />
+    <ng-content select="[mac-destination]" />
+  </div>
+</div>
+
+<div class="configuration-section">
+  <div class="configuration-section__title">Frame sizes</div>
+  <div class="configuration-section__card">
+    <ng-content select="[frames]" />
+  </div>
+</div>
+
+<div class="configuration-section">
+  <div class="configuration-section__title">Protocols</div>
+  <div class="configuration-section__card">
+    <ng-content select="[protocols]" />
+  </div>
+</div>
diff --git a/src/app/components/configuration-template/configuration-template.component.scss b/src/app/components/configuration-template/configuration-template.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8566472fecdaa1a4313fe03cf1a77f5fb642a631
--- /dev/null
+++ b/src/app/components/configuration-template/configuration-template.component.scss
@@ -0,0 +1,31 @@
+@use '../../../vars';
+@use 'sass:map';
+
+:host {
+  display: flex;
+  flex-direction: column;
+  gap: map.get(vars.$spacing, 'lg');
+
+  .configuration {
+    &-section {
+      display: flex;
+      flex-direction: column;
+      gap: map.get(vars.$spacing, 'xs');
+
+      &__title {
+        font-weight: bold;
+        color: map.get(vars.$grey, 100);
+        font-size: map.get(vars.$text, 'lg');
+      }
+
+      &__card {
+        display: flex;
+        flex-direction: column;
+        gap: map.get(vars.$spacing, 'md');
+        padding: map.get(vars.$spacing, 'lg');
+        border-radius: map.get(vars.$radius, 'sm');
+        background-color: map.get(vars.$grey, 10);
+      }
+    }
+  }
+}
diff --git a/src/app/components/configuration-template/configuration-template.component.ts b/src/app/components/configuration-template/configuration-template.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d6f45a2ae79cb75f4cc59ddaea46e445c3975ff5
--- /dev/null
+++ b/src/app/components/configuration-template/configuration-template.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+import { MatDivider } from '@angular/material/divider';
+
+@Component({
+  selector: 'app-configuration-template',
+  imports: [MatDivider],
+  templateUrl: './configuration-template.component.html',
+  styleUrl: './configuration-template.component.scss',
+})
+export class ConfigurationTemplateComponent {}
diff --git a/src/app/components/configuration/configuration.component.html b/src/app/components/configuration/configuration.component.html
index 8434b8c17cf0b0eb651f46dd6fd926415b7c24b9..6c6e9ab6e887126be45eac4dd694739630150ba8 100644
--- a/src/app/components/configuration/configuration.component.html
+++ b/src/app/components/configuration/configuration.component.html
@@ -1,6 +1,130 @@
 <app-page-wrapper>
-  <div header>Analyzer configuration</div>
+  <div title>Analyzer configuration</div>
 
-  Configuration content goes here
-  <p-skeleton height="10rem" width="100%" />
+  <div actions class="actions">
+    @switch (pageMode()) {
+      @case (ConfigurationPageMode.READ) {
+        <app-button (click)="pageMode.set(ConfigurationPageMode.CREATE)"
+          >New profile</app-button
+        >
+      }
+    }
+  </div>
+
+  <ng-template #configurationSelect>
+    <mat-form-field>
+      <mat-label>Configuration</mat-label>
+      <mat-select
+        [value]="chosenConfiguration()"
+        (valueChange)="onChangeConfiguration($event)">
+        @for (config of configurationOptions(); track $index) {
+          <mat-option [value]="config.id">{{ config.name }}</mat-option>
+        }
+      </mat-select>
+    </mat-form-field>
+  </ng-template>
+
+  @if (isLoading()) {
+    <app-configuration-template>
+      <div class="configuration__header">
+        <ng-container *ngTemplateOutlet="configurationSelect" />
+        <div class="configuration__header-actions">
+          <ngx-skeleton-loader count="3" />
+        </div>
+      </div>
+      <div mac-source class="configuration__mac">
+        <div class="configuration__mac-title">Source:</div>
+        <div class="configuration__mac-content">
+          <ngx-skeleton-loader count="7" />
+        </div>
+      </div>
+      <div mac-destination class="configuration__mac">
+        <div class="configuration__mac-title">Destination:</div>
+        <div class="configuration__mac-content">
+          <ngx-skeleton-loader count="7" />
+        </div>
+      </div>
+      <div frames>
+        <ngx-skeleton-loader count="10" />
+      </div>
+      <div protocols>
+        <ngx-skeleton-loader count="10" />
+      </div>
+    </app-configuration-template>
+  } @else {
+    @switch (pageMode()) {
+      @case (ConfigurationPageMode.CREATE) {
+        <app-configuration-form
+          (cancel)="pageMode.set(ConfigurationPageMode.READ)"
+          (submit)="onSubmitConfiguration($event)" />
+      }
+      @case (ConfigurationPageMode.EDIT) {
+        <app-configuration-form
+          [configuration]="configuration()!"
+          (cancel)="pageMode.set(ConfigurationPageMode.READ)"
+          (submit)="onSubmitConfiguration($event)" />
+      }
+      @default {
+        <app-configuration-template>
+          <div class="configuration__header">
+            <ng-container *ngTemplateOutlet="configurationSelect" />
+
+            <div class="configuration__header-actions">
+              <button mat-fab extended (click)="onDeleteConfiguration()">
+                <mat-icon>delete</mat-icon>
+                Delete
+              </button>
+              @if (!configuration()?.is_applied) {
+                <button mat-fab extended (click)="onApplyConfiguration()">
+                  <mat-icon>save</mat-icon>
+                  Apply
+                </button>
+              }
+              <button
+                mat-fab
+                extended
+                (click)="pageMode.set(ConfigurationPageMode.EDIT)">
+                <mat-icon>edit_square</mat-icon>
+                Edit
+              </button>
+            </div>
+          </div>
+          <div mac-source class="configuration__mac">
+            <div class="configuration__mac-title">Source:</div>
+            <div class="configuration__mac-content">
+              @for (mac of configuration()?.mac_source; track $index) {
+                <app-pill>{{ mac }}</app-pill>
+              } @empty {
+                <app-pill>All addresses</app-pill>
+              }
+            </div>
+          </div>
+          <div mac-destination class="configuration__mac">
+            <div class="configuration__mac-title">Destination:</div>
+            <div class="configuration__mac-content">
+              @for (mac of configuration()?.mac_destination; track $index) {
+                <app-pill>{{ mac }}</app-pill>
+              } @empty {
+                <app-pill>All addresses</app-pill>
+              }
+            </div>
+          </div>
+          <div frames>
+            @for (frame of configuration()?.frame_ranges; track $index) {
+              <app-pill>{{ frame[0] }} — {{ frame[1] }}</app-pill>
+            } @empty {
+              <app-pill>All sizes</app-pill>
+            }
+          </div>
+          <div protocols>
+            @for (protocol of configuration()?.protocols; track $index) {
+              <app-pill>{{ protocol }}</app-pill>
+            } @empty {
+              <app-pill>None</app-pill>
+            }
+          </div>
+        </app-configuration-template>
+      }
+    }
+  }
 </app-page-wrapper>
diff --git a/src/app/components/configuration/configuration.component.scss b/src/app/components/configuration/configuration.component.scss
index 2995308e841ca96f9de6435ee45baad2167aa6e6..efe7e855928bbd6baf4cb9c9a24f72f527e26c09 100644
--- a/src/app/components/configuration/configuration.component.scss
+++ b/src/app/components/configuration/configuration.component.scss
@@ -1,4 +1,64 @@
+@use '../../../vars';
+@use 'sass:map';
+
 :host {
   height: 100%;
   width: 100%;
+
+  .actions {
+    display: flex;
+    gap: map.get(vars.$spacing, 'md');
+  }
+
+  p-select {
+    border-radius: map.get(vars.$radius, 'xs');
+    background-color: map.get(vars.$grey, 30);
+    padding: map.get(vars.$spacing, 'md') map.get(vars.$spacing, 'xl');
+  }
+
+  ngx-skeleton-loader {
+    display: flex;
+    flex-direction: row;
+    gap: map.get(vars.$spacing, 'md');
+
+    ::ng-deep * {
+      height: 50px;
+    }
+  }
+
+  .configuration {
+    &__header {
+      display: flex;
+      justify-content: space-between;
+
+      &-actions {
+        display: flex;
+        gap: map.get(vars.$spacing, 'xs');
+
+        & > button {
+          box-shadow: none;
+        }
+      }
+    }
+
+    &__mac {
+      display: flex;
+      justify-content: space-between;
+
+      &-title {
+        color: map.get(vars.$grey, 100);
+        font-size: map.get(vars.$text, 'lg');
+      }
+
+      &-content {
+        width: calc(100% - 200px);
+      }
+    }
+  }
+
+  // Configuration select
+  ::ng-deep .mdc-text-field--filled:not(.mdc-text-field--disabled) {
+    background-color: map.get(vars.$grey, 10);
+    border-radius: map.get(vars.$radius, 'xs');
+  }
 }
diff --git a/src/app/components/configuration/configuration.component.spec.ts b/src/app/components/configuration/configuration.component.spec.ts
deleted file mode 100644
index a719df815f26937c9c5088f6b488339846c5277a..0000000000000000000000000000000000000000
--- a/src/app/components/configuration/configuration.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ConfigurationComponent } from './configuration.component';
-
-describe('ConfigurationComponent', () => {
-  let component: ConfigurationComponent;
-  let fixture: ComponentFixture<ConfigurationComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [ConfigurationComponent]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(ConfigurationComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/components/configuration/configuration.component.ts b/src/app/components/configuration/configuration.component.ts
index 6cde6f1c642627c033bb2948d159e3975cde5268..11789edec83a31d97f37799fe86ffc9ff419990a 100644
--- a/src/app/components/configuration/configuration.component.ts
+++ b/src/app/components/configuration/configuration.component.ts
@@ -1,11 +1,132 @@
-import { Component } from '@angular/core';
+import {
+  afterNextRender,
+  Component,
+  effect,
+  inject,
+  signal,
+} from '@angular/core';
 import { PageWrapperComponent } from '../page-wrapper/page-wrapper.component';
-import { Skeleton } from 'primeng/skeleton';
+import { ConfigurationTemplateComponent } from '../configuration-template/configuration-template.component';
+import { ConfigurationFormComponent } from '../configuration-form/configuration-form.component';
+import { ButtonComponent } from '../button/button.component';
+import { ConfigurationApi } from '../../api/configuration.api';
+import { map, Observable, tap } from 'rxjs';
+import { PillComponent } from '../pill/pill.component';
+import { SkeletonModule } from 'primeng/skeleton';
+import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
+import { FormsModule } from '@angular/forms';
+import { MatFormField, MatLabel } from '@angular/material/form-field';
+import { MatOption, MatSelect } from '@angular/material/select';
+import { NgTemplateOutlet } from '@angular/common';
+import { Configuration } from '../../models/configuration';
+import { MatFabButton } from '@angular/material/button';
+import { MatIcon } from '@angular/material/icon';
+
+enum ConfigurationPageMode {
+  READ,
+  EDIT,
+  CREATE,
+}
 
 @Component({
   selector: 'app-configuration',
-  imports: [PageWrapperComponent, Skeleton],
+  imports: [
+    PageWrapperComponent,
+    ConfigurationTemplateComponent,
+    ConfigurationFormComponent,
+    ButtonComponent,
+    PillComponent,
+    SkeletonModule,
+    NgxSkeletonLoaderModule,
+    FormsModule,
+    MatFormField,
+    MatSelect,
+    MatOption,
+    MatLabel,
+    NgTemplateOutlet,
+    MatIcon,
+    MatFabButton,
+  ],
   templateUrl: './configuration.component.html',
   styleUrl: './configuration.component.scss',
 })
-export class ConfigurationComponent {}
+export class ConfigurationComponent {
+  configurationApi = inject(ConfigurationApi);
+
+  pageMode = signal<ConfigurationPageMode>(ConfigurationPageMode.READ);
+  configurationOptions = signal<Partial<Configuration>[]>([]);
+  chosenConfiguration = signal<string | undefined>(undefined);
+  isLoading = signal<boolean>(true);
+  configuration = signal<Configuration | undefined>(undefined);
+
+  constructor() {
+    effect(() => {
+      this.isLoading.set(true);
+      const chosenConfiguration = this.chosenConfiguration();
+      if (chosenConfiguration) {
+        this.configurationApi.find(chosenConfiguration).subscribe(config => {
+          this.isLoading.set(false);
+          this.configuration.set(config);
+        });
+      }
+    });
+
+    afterNextRender(() => {
+      this.fetchConfigurationOptions().subscribe(configurations => {
+        const appliedConfiguration = configurations.find(c => c.is_applied);
+        if (appliedConfiguration) {
+          this.chosenConfiguration.set(appliedConfiguration.id);
+        }
+        this.configurationOptions.set(configurations);
+      });
+    });
+  }
+
+  onDeleteConfiguration() {
+    this.configurationApi.delete(this.chosenConfiguration()!).subscribe(() => {
+      this.fetchConfigurationOptions().subscribe(configurations => {
+        const appliedConfiguration = configurations.find(c => c.is_applied);
+        if (appliedConfiguration) {
+          this.chosenConfiguration.set(appliedConfiguration.id);
+        }
+        this.configurationOptions.set(configurations);
+      });
+    });
+  }
+
+  onApplyConfiguration() {
+    this.configurationApi.apply(this.chosenConfiguration()!).subscribe(() => {
+      this.fetchConfigurationOptions().subscribe(configurations =>
+        this.configurationOptions.set(configurations)
+      );
+    });
+  }
+
+  onChangeConfiguration(configId: string) {
+    this.chosenConfiguration.set(configId);
+  }
+
+  onSubmitConfiguration(configuration: Configuration) {
+    console.log('Submitting configuration', configuration);
+    this.configurationApi.save(configuration);
+    this.pageMode.set(ConfigurationPageMode.READ);
+  }
+
+  private fetchConfigurationOptions(): Observable<Partial<Configuration>[]> {
+    this.isLoading.set(true);
+    return this.configurationApi.fetch().pipe(
+      map(configurations =>
+        configurations.map(configuration => {
+          return {
+            id: configuration.id,
+            is_applied: configuration.is_applied,
+            name: configuration.name + (configuration.is_applied ? '*' : ''),
+          };
+        })
+      ),
+      tap(() => this.isLoading.set(false))
+    );
+  }
+
+  protected readonly ConfigurationPageMode = ConfigurationPageMode;
+}
diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html
index cb0f2015d2c2aaeed5adaaed87a58cd8936d3ac8..d8bafe617dbe0d21b31c96bb7e223559926dbdee 100644
--- a/src/app/components/dashboard/dashboard.component.html
+++ b/src/app/components/dashboard/dashboard.component.html
@@ -1,5 +1,6 @@
 <app-page-wrapper>
-  <div header>Monitoring and Analysis</div>
+  <div title>Monitoring and Analysis</div>
+  <div actions>actions</div>
 
   Dashboard content goes here
   <p-skeleton height="10rem" width="100%" />
diff --git a/src/app/components/dashboard/dashboard.component.spec.ts b/src/app/components/dashboard/dashboard.component.spec.ts
deleted file mode 100644
index 30e39a2a4ce50c5ba05a66e706c22216e8278472..0000000000000000000000000000000000000000
--- a/src/app/components/dashboard/dashboard.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { DashboardComponent } from './dashboard.component';
-
-describe('DashboardComponent', () => {
-  let component: DashboardComponent;
-  let fixture: ComponentFixture<DashboardComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [DashboardComponent]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(DashboardComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/components/header/header.component.scss b/src/app/components/header/header.component.scss
index c5a236d8118f89f8ce05b4308c1ef74320704183..194ccfaaff95f29d52a4a4f751d863e6ea45a759 100644
--- a/src/app/components/header/header.component.scss
+++ b/src/app/components/header/header.component.scss
@@ -45,7 +45,7 @@
       }
     }
 
-    & > * {
+    * {
       color: vars.$textPrimary;
     }
   }
diff --git a/src/app/components/header/header.component.spec.ts b/src/app/components/header/header.component.spec.ts
deleted file mode 100644
index 204ed6e4b7fd0c0aa28ef4fd67a8097ac22272cd..0000000000000000000000000000000000000000
--- a/src/app/components/header/header.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { HeaderComponent } from './header.component';
-
-describe('HeaderComponent', () => {
-  let component: HeaderComponent;
-  let fixture: ComponentFixture<HeaderComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [HeaderComponent]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(HeaderComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/components/not-found/not-found.component.spec.ts b/src/app/components/not-found/not-found.component.spec.ts
deleted file mode 100644
index 5b65d9eadd6d55f6868f3a788f6606dad682ab28..0000000000000000000000000000000000000000
--- a/src/app/components/not-found/not-found.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { NotFoundComponent } from './not-found.component';
-
-describe('NotFoundComponent', () => {
-  let component: NotFoundComponent;
-  let fixture: ComponentFixture<NotFoundComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [NotFoundComponent]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(NotFoundComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/components/page-wrapper/page-wrapper.component.html b/src/app/components/page-wrapper/page-wrapper.component.html
index 60cb6e288d65ee39b7e4e70d39666827f3ec1ca7..fae4904e34fac12ee085461926655beb2b7366aa 100644
--- a/src/app/components/page-wrapper/page-wrapper.component.html
+++ b/src/app/components/page-wrapper/page-wrapper.component.html
@@ -1,5 +1,8 @@
 <div class="page-header">
-  <ng-content select="[header]"></ng-content>
+  <div class="page-header__title">
+    <ng-content select="[title]"></ng-content>
+  </div>
+  <ng-content select="[actions]"></ng-content>
 </div>
 <mat-divider />
 <div class="page-body">
diff --git a/src/app/components/page-wrapper/page-wrapper.component.scss b/src/app/components/page-wrapper/page-wrapper.component.scss
index 1a8364713177fdbf9d329e860999f5b009ca4a94..ab5c237d1ca95efeca1f74759bf130e2079d4f03 100644
--- a/src/app/components/page-wrapper/page-wrapper.component.scss
+++ b/src/app/components/page-wrapper/page-wrapper.component.scss
@@ -5,21 +5,26 @@
   display: block;
   min-height: calc(100% - 2 * map.get(vars.$spacing, 'lg'));
 
-  margin: map.get(vars.$spacing, 'lg');
+  margin: map.get(vars.$spacing, 'xl');
   background-color: map.get(vars.$grey, 0);
   border-radius: map.get(vars.$radius, 'xs');
 
   .page {
     &-header {
-      padding: map.get(vars.$spacing, 'lg') map.get(vars.$spacing, 'lg')
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: map.get(vars.$spacing, 'lg') map.get(vars.$spacing, 'xxl')
         map.get(vars.$spacing, 'xs');
-      font-family: Monoton, cursive;
-      font-size: map.get(vars.$text, 'xxl');
+
+      &__title {
+        font-family: Monoton, cursive;
+        font-size: map.get(vars.$text, 'xxl');
+      }
     }
 
     &-body {
-      padding: map.get(vars.$spacing, 'xl') map.get(vars.$spacing, 'lg')
-        map.get(vars.$spacing, 'lg');
+      padding: map.get(vars.$spacing, 'xxl');
     }
   }
 }
diff --git a/src/app/components/page-wrapper/page-wrapper.component.spec.ts b/src/app/components/page-wrapper/page-wrapper.component.spec.ts
deleted file mode 100644
index 73525cc5b2ec289ef51c9f3d96b9ac8995d23f3b..0000000000000000000000000000000000000000
--- a/src/app/components/page-wrapper/page-wrapper.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { PageWrapperComponent } from './page-wrapper.component';
-
-describe('PageWrapperComponent', () => {
-  let component: PageWrapperComponent;
-  let fixture: ComponentFixture<PageWrapperComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [PageWrapperComponent]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(PageWrapperComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/components/pill/pill.component.html b/src/app/components/pill/pill.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0548d4578c1da8e55ffe8bfe375fd681e0befc5c
--- /dev/null
+++ b/src/app/components/pill/pill.component.html
@@ -0,0 +1,6 @@
+<div class="pill">
+  <ng-content />
+  @if (removable()) {
+    <mat-icon (click)="clickRemove.emit()">close</mat-icon>
+  }
+</div>
diff --git a/src/app/components/pill/pill.component.scss b/src/app/components/pill/pill.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..ac096b3fa2e8ce3c40f9075d432610c825631338
--- /dev/null
+++ b/src/app/components/pill/pill.component.scss
@@ -0,0 +1,17 @@
+@use '../../../vars';
+@use 'sass:map';
+
+.pill {
+  display: inline-flex;
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
+  gap: map.get(vars.$spacing, 'sm');
+  border-radius: map.get(vars.$radius, 'md');
+  border: 1px solid map.get(vars.$grey, 50);
+  background-color: map.get(vars.$grey, 0);
+  color: map.get(vars.$grey, 100);
+  padding: map.get(vars.$spacing, 'xs') map.get(vars.$spacing, 'lg');
+  margin: map.get(vars.$spacing, 'xs');
+  line-height: map.get(vars.$text, 'xl');
+}
diff --git a/src/app/components/pill/pill.component.ts b/src/app/components/pill/pill.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f43c8ace3b9566369df724bce387c1871662b6be
--- /dev/null
+++ b/src/app/components/pill/pill.component.ts
@@ -0,0 +1,13 @@
+import { Component, input, output } from '@angular/core';
+import { MatIcon } from '@angular/material/icon';
+
+@Component({
+  selector: 'app-pill',
+  imports: [MatIcon],
+  templateUrl: './pill.component.html',
+  styleUrl: './pill.component.scss',
+})
+export class PillComponent {
+  removable = input<boolean | null>(null);
+  clickRemove = output<void>();
+}
diff --git a/src/app/components/sidenav/sidenav.component.spec.ts b/src/app/components/sidenav/sidenav.component.spec.ts
deleted file mode 100644
index 25f799c1d0518fa961fcfb038eb94618951ca913..0000000000000000000000000000000000000000
--- a/src/app/components/sidenav/sidenav.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { SidenavComponent } from './sidenav.component';
-
-describe('SidenavComponent', () => {
-  let component: SidenavComponent;
-  let fixture: ComponentFixture<SidenavComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [SidenavComponent]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(SidenavComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/components/sidenav/sidenav.component.ts b/src/app/components/sidenav/sidenav.component.ts
index 2fd9c08614d71573be1f94baf716b6d68402d51b..a630fe2959e7f2106957b7664ff0dc3063d3151d 100644
--- a/src/app/components/sidenav/sidenav.component.ts
+++ b/src/app/components/sidenav/sidenav.component.ts
@@ -1,5 +1,11 @@
-import { afterNextRender, Component, inject, signal } from '@angular/core';
-import { MatSidenavModule } from '@angular/material/sidenav';
+import {
+  afterNextRender,
+  Component,
+  inject,
+  signal,
+  viewChild,
+} from '@angular/core';
+import { MatDrawer, MatSidenavModule } from '@angular/material/sidenav';
 import { MatButtonModule } from '@angular/material/button';
 import { NavigationEnd, Router, RouterLink } from '@angular/router';
 import { Button } from 'primeng/button';
@@ -23,6 +29,8 @@ export class SidenavComponent {
   router = inject(Router);
   currentPage = signal<string>('');
 
+  drawer = viewChild<MatDrawer>('drawer');
+
   constructor() {
     afterNextRender(() => {
       this.router.events.subscribe(event => {
@@ -30,6 +38,7 @@ export class SidenavComponent {
           this.currentPage.set(event.url);
         }
       });
+      this.drawer()?.open();
     });
   }
 }
diff --git a/src/app/interceptor/interceptor.ts b/src/app/interceptor/interceptor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2dfdedf6c4fbf82c4cc97a526eb7533b346baef7
--- /dev/null
+++ b/src/app/interceptor/interceptor.ts
@@ -0,0 +1,18 @@
+import * as configurationList from './mocks/configurations.json';
+import * as configuration1 from './mocks/configuration-1.json';
+import * as configuration2 from './mocks/configuration-2.json';
+
+export const urls = [
+  {
+    url: '/api/v1/configuration/1abc',
+    json: configuration1,
+  },
+  {
+    url: '/api/v1/configuration/2def',
+    json: configuration2,
+  },
+  {
+    url: '/api/v1/configuration',
+    json: configurationList,
+  },
+];
diff --git a/src/app/interceptor/mock-interceptor.ts b/src/app/interceptor/mock-interceptor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1ae561c1d7b35f3c2ae8fb39c14f7865087fc90
--- /dev/null
+++ b/src/app/interceptor/mock-interceptor.ts
@@ -0,0 +1,17 @@
+import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
+import { delay, of } from 'rxjs';
+import { urls } from './interceptor';
+
+export const MockInterceptor: HttpInterceptorFn = (req, next) => {
+  const { url, method } = req;
+
+  for (const element of urls) {
+    if (url.includes(element.url)) {
+      console.log('Loaded from json for url: ' + url, element.json);
+      return of(
+        new HttpResponse({ status: 200, body: (element.json as any).default })
+      ).pipe(delay(300));
+    }
+  }
+  return next(req);
+};
diff --git a/src/app/interceptor/mocks/configuration-1.json b/src/app/interceptor/mocks/configuration-1.json
new file mode 100644
index 0000000000000000000000000000000000000000..6e971313aa0358f2068b1a5eebec1e90d8cb8ba7
--- /dev/null
+++ b/src/app/interceptor/mocks/configuration-1.json
@@ -0,0 +1,24 @@
+{
+  "data": {
+    "id": "1abc",
+    "name": "Custom",
+    "is_applied": false,
+    "mac_source": [
+      "01:23:45:67:89:00",
+      "01:23:45:67:89:11",
+      "01:23:45:67:89:22",
+      "01:23:45:67:89:33",
+      "01:23:45:67:89:44",
+      "01:23:45:67:89:55",
+      "01:23:45:67:89:66",
+      "01:23:45:67:89:77"
+    ],
+    "mac_destination": ["fe:dc:ba:98:76:54"],
+    "frame_ranges": [
+      [0, 63],
+      [128, 255],
+      [1024, 1518]
+    ],
+    "protocols": ["IPv4", "IP6", "UDP"]
+  }
+}
diff --git a/src/app/interceptor/mocks/configuration-2.json b/src/app/interceptor/mocks/configuration-2.json
new file mode 100644
index 0000000000000000000000000000000000000000..d18ca9470f7c6dc9b350038ae2edee502e66b3f7
--- /dev/null
+++ b/src/app/interceptor/mocks/configuration-2.json
@@ -0,0 +1,11 @@
+{
+  "data": {
+    "id": "2def",
+    "name": "Everything",
+    "is_applied": true,
+    "mac_source": [],
+    "mac_destination": [],
+    "frame_ranges": [],
+    "protocols": []
+  }
+}
diff --git a/src/app/interceptor/mocks/configurations.json b/src/app/interceptor/mocks/configurations.json
new file mode 100644
index 0000000000000000000000000000000000000000..b1ed282448df746db14186a2c1aae8cfe1f9a6dc
--- /dev/null
+++ b/src/app/interceptor/mocks/configurations.json
@@ -0,0 +1,35 @@
+{
+  "data": [
+    {
+      "id": "1abc",
+      "name": "Custom",
+      "is_applied": false,
+      "mac_source": [
+        "01:23:45:67:89:00",
+        "01:23:45:67:89:11",
+        "01:23:45:67:89:22",
+        "01:23:45:67:89:33",
+        "01:23:45:67:89:44",
+        "01:23:45:67:89:55",
+        "01:23:45:67:89:66",
+        "01:23:45:67:89:77"
+      ],
+      "mac_destination": ["fe:dc:ba:98:76:54"],
+      "frame_ranges": [
+        [0, 63],
+        [128, 255],
+        [1024, 1518]
+      ],
+      "protocols": ["IPv4", "IP6", "UDP"]
+    },
+    {
+      "id": "2def",
+      "name": "Everything",
+      "is_applied": true,
+      "mac_source": [],
+      "mac_destination": [],
+      "frame_ranges": [],
+      "protocols": []
+    }
+  ]
+}
diff --git a/src/app/models/.gitkeep b/src/app/models/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/models/api-response.ts b/src/app/models/api-response.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3db5cbb43eef40a5bca74d47231a8d4c7f73676c
--- /dev/null
+++ b/src/app/models/api-response.ts
@@ -0,0 +1,3 @@
+export interface ApiResponse<T> {
+  data: T;
+}
diff --git a/src/app/models/configuration.ts b/src/app/models/configuration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8424ba4169009534cb22bf058708fd0f8a4a592e
--- /dev/null
+++ b/src/app/models/configuration.ts
@@ -0,0 +1,21 @@
+export type Configuration = ConfigurationDto;
+
+export interface ConfigurationDto {
+  id: string;
+  name: string;
+  is_applied: boolean;
+  mac_source: string[];
+  mac_destination: string[];
+  frame_ranges: [number, number][];
+  protocols: string[];
+}
+
+export function dtoToConfiguration(dto: ConfigurationDto): Configuration {
+  return dto;
+}
+
+export function configurationToDto(
+  configuration: Configuration
+): ConfigurationDto {
+  return configuration;
+}
diff --git a/src/vars.scss b/src/vars.scss
index 4cdf01f51db35fe3ea0c2cba5cdaf3c28c598b9a..9a4c50ca8dd75a3fe532f2efb03700e666a1613f 100644
--- a/src/vars.scss
+++ b/src/vars.scss
@@ -9,6 +9,7 @@ $grey: (
   20: #f3eaea,
   30: #dddddd,
   50: #c5c5c5,
+  100: #686868,
   200: #181818,
   500: #000000,
 );