diff --git a/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.html b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.html
new file mode 100644
index 0000000..834abcf
--- /dev/null
+++ b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
Videoconference rooms in one click
+
+
+
+
+
+
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.scss b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.scss
new file mode 100644
index 0000000..830a0ab
--- /dev/null
+++ b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.scss
@@ -0,0 +1,255 @@
+$loginBorderRadius: 0.35rem;
+$loginColor: #e6e6e6;
+$loginError: #770000;
+$loginInputBackgroundColor: #434a52;
+$loginInputHoverBackgroundColor: #3c4249;
+$loginLabelBackgroundColor: #363b41;
+$loginSubmitBackgroundColor: #0087a9;
+$loginSubmitDisabledBackgroundColor: #264b55;
+
+$loginSubmitColor: #eee;
+$loginSubmitDisabledColor: #bdbdbd;
+
+$loginSubmitHoverBackgroundColor: #006a85;
+$iconFill: #606468;
+$formGap: 0.375rem;
+
+.section1 {
+ background:
+ linear-gradient(rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.2)),
+ var(--background-image-url) top center no-repeat;
+ background-size: cover;
+ height: 100%;
+ text-align: center;
+ position: relative;
+ color: $loginColor;
+}
+
+#header,
+.footer {
+ background-color: transparent;
+ color: $loginColor;
+}
+
+.loginError {
+ font-size: 14px;
+ color: $loginError;
+ text-shadow: 0.2px 0px #ffffff;
+ text-align: left;
+ font-weight: 600;
+}
+
+.loginError mat-icon {
+ vertical-align: bottom;
+ padding-right: 4px;
+}
+
+.footer {
+ position: absolute;
+ bottom: 0;
+ font-size: 9px;
+ height: auto;
+}
+
+.footer a {
+ color: $loginColor;
+}
+
+#meet-version,
+#logout-content {
+ right: 15px;
+ position: absolute;
+ font-size: 13px;
+}
+
+h4 {
+ font-size: 25px;
+ font-weight: 300;
+ color: #ffffff;
+ position: relative;
+ padding-bottom: 5px;
+}
+
+/* Extra small devices (phones, 600px and down) */
+@media only screen and (max-width: 600px) {
+ #header_img,
+ .ovVersion,
+ .footer {
+ display: none;
+ }
+}
+
+.openvidu-slogan-container {
+ margin-top: 80px;
+}
+
+#form-img {
+ max-width: 290px;
+ margin-right: 10px;
+ margin-top: 10px;
+ padding: 15px;
+}
+
+/* Extra large devices (large laptops and desktops, 1200px and up) */
+@media only screen and (min-width: 1200px) {
+ .section1 {
+ background-attachment: fixed;
+ }
+}
+
+.grid {
+ inline-size: 90%;
+ margin-inline: auto;
+ max-inline-size: 26rem;
+}
+
+.hidden {
+ border: 0;
+ clip: rect(0 0 0 0);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+}
+
+.icons {
+ display: none;
+}
+
+.icon {
+ block-size: 1em;
+ display: inline-block;
+ fill: $iconFill;
+ inline-size: 1em;
+ vertical-align: middle;
+}
+
+input {
+ background-image: none;
+ border: 0;
+ color: inherit;
+ font: inherit;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ transition: background-color 0.3s;
+}
+
+input[type='submit'] {
+ cursor: pointer;
+}
+
+.form {
+ display: grid;
+ gap: $formGap;
+}
+
+.form input[type='password'],
+.form input[type='text'],
+.form button[type='submit'] {
+ inline-size: 100%;
+}
+
+.form-field {
+ display: flex;
+}
+
+.form-input {
+ flex: 1;
+}
+
+.login {
+ color: $loginColor;
+}
+
+.login label.error {
+ background-color: $loginError;
+}
+.login label,
+.login input[type='text'],
+.login input[type='password'] {
+ border-radius: $loginBorderRadius;
+ padding: 0.85rem;
+}
+#room-name-input {
+ border-radius: 0;
+}
+
+.login button[type='submit'] {
+ border-radius: $loginBorderRadius;
+ padding: 0.4rem;
+ cursor: pointer;
+}
+
+.login button:disabled[type='submit'] {
+ cursor: auto;
+ color: $loginSubmitDisabledColor !important;
+ background-color: $loginSubmitDisabledBackgroundColor !important;
+}
+
+.login label {
+ background-color: $loginLabelBackgroundColor;
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ padding-inline: 1.25rem;
+}
+
+.login input[type='password'],
+.login input[type='text'] {
+ background-color: $loginInputBackgroundColor;
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ font-size: 16px;
+}
+
+.login input[type='password']:focus,
+.login input[type='password']:hover,
+.login input[type='text']:focus,
+.login input[type='text']:hover {
+ background-color: $loginInputHoverBackgroundColor;
+}
+
+.login button[type='submit'] {
+ background-color: $loginSubmitBackgroundColor;
+ color: $loginSubmitColor;
+ font-weight: 500;
+ text-transform: uppercase;
+}
+
+.login button[type='submit']:focus,
+.login button[type='submit']:hover {
+ background-color: $loginSubmitHoverBackgroundColor;
+}
+#clear-room-name-btn {
+ height: auto;
+ background-color: $loginInputBackgroundColor;
+ border-radius: 0;
+ color: $loginColor;
+ mat-icon {
+ vertical-align: middle;
+ }
+}
+#room-name-generator-btn {
+ height: auto;
+ background-color: $loginInputBackgroundColor;
+ border-radius: $loginBorderRadius;
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ color: $loginColor;
+ mat-icon {
+ vertical-align: middle;
+ }
+ &:hover {
+ background-color: $loginInputHoverBackgroundColor;
+ }
+}
+
+p {
+ margin-block: 1.5rem;
+}
+
+.text--center {
+ text-align: center;
+}
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.spec.ts b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.spec.ts
new file mode 100644
index 0000000..e95c3a1
--- /dev/null
+++ b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+ let component: LoginComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [LoginComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(LoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.ts
new file mode 100644
index 0000000..e9f325f
--- /dev/null
+++ b/frontend/projects/shared-meet-components/src/lib/pages/login/login.component.ts
@@ -0,0 +1,58 @@
+import { NgClass } from '@angular/common';
+import { Component } from '@angular/core';
+import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
+import { MatButton } from '@angular/material/button';
+import { MatIcon } from '@angular/material/icon';
+import { MatToolbar } from '@angular/material/toolbar';
+import { MatTooltip } from '@angular/material/tooltip';
+import { Router } from '@angular/router';
+import { ContextService, AuthService } from '../../services/index';
+import { HttpErrorResponse } from '@angular/common/http';
+
+@Component({
+ selector: 'app-login',
+ standalone: true,
+ imports: [MatToolbar, MatTooltip, MatIcon, FormsModule, ReactiveFormsModule, NgClass, MatButton],
+ templateUrl: './login.component.html',
+ styleUrl: './login.component.scss'
+})
+export class LoginComponent {
+ version = '';
+ openviduLogoUrl = '';
+ backgroundImageUrl = '';
+
+ loginForm = new FormGroup({
+ username: new FormControl('', [Validators.required, Validators.minLength(4)]),
+ password: new FormControl('', [Validators.required, Validators.minLength(4)])
+ });
+ loginErrorMessage: string | undefined;
+
+ constructor(
+ private router: Router,
+ private authService: AuthService,
+ private contextService: ContextService
+ ) {}
+
+ async ngOnInit() {
+ this.version = this.contextService.getVersion();
+ this.openviduLogoUrl = this.contextService.getOpenViduLogoUrl();
+ this.backgroundImageUrl = this.contextService.getBackgroundImageUrl();
+ }
+
+ async login() {
+ this.loginErrorMessage = undefined;
+ const { username, password } = this.loginForm.value;
+
+ try {
+ // TODO: Replace with user login
+ await this.authService.adminLogin(username!, password!);
+ this.router.navigate(['']);
+ } catch (error) {
+ if ((error as HttpErrorResponse).status === 429) {
+ this.loginErrorMessage = 'Too many login attempts. Please try again later';
+ } else {
+ this.loginErrorMessage = 'Invalid username or password';
+ }
+ }
+ }
+}
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/room-creator/room-creator.component.html b/frontend/projects/shared-meet-components/src/lib/pages/room-creator/room-creator.component.html
index 68bd98e..62f6ae1 100644
--- a/frontend/projects/shared-meet-components/src/lib/pages/room-creator/room-creator.component.html
+++ b/frontend/projects/shared-meet-components/src/lib/pages/room-creator/room-creator.component.html
@@ -1,6 +1,6 @@