Finished Login / Register system
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
@@ -99,6 +100,7 @@
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
|
||||
66
Frontend/package-lock.json
generated
66
Frontend/package-lock.json
generated
@@ -9,10 +9,12 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@angular/animations": "~13.1.0",
|
||||
"@angular/cdk": "^13.3.9",
|
||||
"@angular/common": "~13.1.0",
|
||||
"@angular/compiler": "~13.1.0",
|
||||
"@angular/core": "~13.1.0",
|
||||
"@angular/forms": "~13.1.0",
|
||||
"@angular/material": "^13.3.9",
|
||||
"@angular/platform-browser": "~13.1.0",
|
||||
"@angular/platform-browser-dynamic": "~13.1.0",
|
||||
"@angular/router": "~13.1.0",
|
||||
@@ -347,6 +349,28 @@
|
||||
"@angular/core": "13.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cdk": {
|
||||
"version": "13.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.3.9.tgz",
|
||||
"integrity": "sha512-XCuCbeuxWFyo3EYrgEYx7eHzwl76vaWcxtWXl00ka8d+WAOtMQ6Tf1D98ybYT5uwF9889fFpXAPw98mVnlo3MA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"parse5": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^13.0.0 || ^14.0.0-0",
|
||||
"@angular/core": "^13.0.0 || ^14.0.0-0",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cdk/node_modules/parse5": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@angular/cli": {
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.1.4.tgz",
|
||||
@@ -472,6 +496,23 @@
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/material": {
|
||||
"version": "13.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-13.3.9.tgz",
|
||||
"integrity": "sha512-FU8lcMgo+AL8ckd27B4V097ZPoIZNRHiCe3wpgkImT1qC0YwcyXZVn0MqQTTFSdC9a/aI8wPm3AbTClJEVw5Vw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "^13.0.0 || ^14.0.0-0",
|
||||
"@angular/cdk": "13.3.9",
|
||||
"@angular/common": "^13.0.0 || ^14.0.0-0",
|
||||
"@angular/core": "^13.0.0 || ^14.0.0-0",
|
||||
"@angular/forms": "^13.0.0 || ^14.0.0-0",
|
||||
"@angular/platform-browser": "^13.0.0 || ^14.0.0-0",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser": {
|
||||
"version": "13.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.1.3.tgz",
|
||||
@@ -11779,6 +11820,23 @@
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@angular/cdk": {
|
||||
"version": "13.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.3.9.tgz",
|
||||
"integrity": "sha512-XCuCbeuxWFyo3EYrgEYx7eHzwl76vaWcxtWXl00ka8d+WAOtMQ6Tf1D98ybYT5uwF9889fFpXAPw98mVnlo3MA==",
|
||||
"requires": {
|
||||
"parse5": "^5.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"parse5": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular/cli": {
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.1.4.tgz",
|
||||
@@ -11857,6 +11915,14 @@
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "13.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-13.3.9.tgz",
|
||||
"integrity": "sha512-FU8lcMgo+AL8ckd27B4V097ZPoIZNRHiCe3wpgkImT1qC0YwcyXZVn0MqQTTFSdC9a/aI8wPm3AbTClJEVw5Vw==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"version": "13.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.1.3.tgz",
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~13.1.0",
|
||||
"@angular/cdk": "^13.3.9",
|
||||
"@angular/common": "~13.1.0",
|
||||
"@angular/compiler": "~13.1.0",
|
||||
"@angular/core": "~13.1.0",
|
||||
"@angular/forms": "~13.1.0",
|
||||
"@angular/material": "^13.3.9",
|
||||
"@angular/platform-browser": "~13.1.0",
|
||||
"@angular/platform-browser-dynamic": "~13.1.0",
|
||||
"@angular/router": "~13.1.0",
|
||||
@@ -36,4 +38,4 @@
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "~4.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {LoginComponent} from "./sites/login/login.component";
|
||||
import {RegisterComponent} from "./sites/register/register.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{path: "login", component: LoginComponent}
|
||||
{path: "login", component: LoginComponent},
|
||||
{path: "register", component: RegisterComponent}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -22,6 +22,7 @@ export class AppComponent implements OnInit {
|
||||
|
||||
if (await this.backend.requestToken()) this.loaded = true;
|
||||
else await this.router.navigate(["login"]);
|
||||
this.loaded = true;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,30 @@ import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import {HttpClientModule} from "@angular/common/http";
|
||||
import { LoginComponent } from './sites/login/login.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import {MatFormFieldModule} from "@angular/material/form-field";
|
||||
import {MatInputModule} from "@angular/material/input";
|
||||
import {MatButtonModule} from "@angular/material/button";
|
||||
import {MatDividerModule} from "@angular/material/divider";
|
||||
import { RegisterComponent } from './sites/register/register.component';
|
||||
import {ReactiveFormsModule} from "@angular/forms";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
LoginComponent
|
||||
LoginComponent,
|
||||
RegisterComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatDividerModule,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface User extends UserEditor {
|
||||
id: string;
|
||||
created: Date;
|
||||
created: string;
|
||||
}
|
||||
|
||||
export interface UserLogin {
|
||||
|
||||
@@ -74,7 +74,7 @@ export class BackendService {
|
||||
}
|
||||
}
|
||||
|
||||
return {content: undefined, success: false, code: error.status, message: error.error.title};
|
||||
return {content: undefined, success: false, code: error.status, message: error.error};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ export class UserApi {
|
||||
return response.success;
|
||||
}
|
||||
|
||||
public async getUserPermissions(id: string, includeGroupPermissions: boolean = true): Promise<string[]> {
|
||||
const response = await this.backend.sendRequest<string[]>(RequestTypes.GET, "users/" + id + "/permissions" + (includeGroupPermissions ? "/raw" : ""), undefined, {authorized: true});
|
||||
public async getUserPermissions(id: string, excludeGroupPermissions: boolean = false): Promise<string[]> {
|
||||
const response = await this.backend.sendRequest<string[]>(RequestTypes.GET, "users/" + id + "/permissions" + (excludeGroupPermissions ? "/raw" : ""), undefined, {authorized: true});
|
||||
if (!response.success) return [];
|
||||
return response.content;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export class UserApi {
|
||||
return response.success;
|
||||
}
|
||||
|
||||
public async login(login: UserLogin): Promise<{success: boolean, errorMessage: string}> {
|
||||
public async login(login: UserLogin): Promise<{success: boolean, errorMessage: string, errorCode: number}> {
|
||||
const response = await this.backend.sendRequest<AccessToken>(RequestTypes.PUT, "users/login", login, {withCredentials: true});
|
||||
|
||||
if (response.success) {
|
||||
@@ -56,10 +56,10 @@ export class UserApi {
|
||||
await this.getAuthorizedUser();
|
||||
}
|
||||
|
||||
return {success: response.success, errorMessage: response.message};
|
||||
return {success: response.success, errorMessage: response.message, errorCode: response.code};
|
||||
}
|
||||
|
||||
public async register(register: UserEditor): Promise<{success: boolean, errorMessage: string}> {
|
||||
public async register(register: UserEditor): Promise<{success: boolean, errorMessage: string, errorCode: number}> {
|
||||
const response = await this.backend.sendRequest<AccessToken>(RequestTypes.POST, "users/register", register, {withCredentials: true});
|
||||
|
||||
if (response.success) {
|
||||
@@ -67,7 +67,7 @@ export class UserApi {
|
||||
await this.getAuthorizedUser();
|
||||
}
|
||||
|
||||
return {success: response.success, errorMessage: response.message};
|
||||
return {success: response.success, errorMessage: response.message, errorCode: response.code};
|
||||
}
|
||||
|
||||
public async logout(id: string): Promise<boolean> {
|
||||
|
||||
@@ -1 +1,24 @@
|
||||
<p>login works!</p>
|
||||
<section class="login">
|
||||
<form class="glass" (submit)="$event.preventDefault(); login(form, username.value, password.value)" #form>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Benutzername oder E-Mail</mat-label>
|
||||
<input matInput type="text" required #username>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Passwort</mat-label>
|
||||
<input matInput type="password" required #password>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-button type="submit" [disabled]="disableLogin">Einloggen</button>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div>
|
||||
<span>Du besitzt keinen Account? </span>
|
||||
<a routerLink="/register">Registrieren</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<div class="error" #error></div>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@use "src/styles";
|
||||
|
||||
.login {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-image: url(styles.$background);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
form {
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
flex-direction: column;
|
||||
|
||||
button[type="submit"] {
|
||||
width: max-content;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
position: absolute;
|
||||
bottom: 120px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
background-color: #F00;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
opacity: 0;
|
||||
|
||||
transition: 500ms;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,45 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||
import {UserApi} from "../../services/users.service";
|
||||
import {UserLogin} from "../../entitys/user";
|
||||
import {Router} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
export class LoginComponent {
|
||||
@ViewChild('error') error: ElementRef;
|
||||
public disableLogin: boolean = false;
|
||||
|
||||
constructor() { }
|
||||
constructor(private users: UserApi, private router: Router) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
public async login(form: HTMLFormElement, username: string, password: string) {
|
||||
if (!form.reportValidity()) return;
|
||||
|
||||
const login: UserLogin = {usernameOrEmail: username, password: password};
|
||||
const response = await this.users.login(login);
|
||||
|
||||
if (!response.success) {
|
||||
this.showError(response.errorMessage)
|
||||
return;
|
||||
}
|
||||
|
||||
await this.router.navigate([""]);
|
||||
}
|
||||
|
||||
public showError(error: string) {
|
||||
this.disableLogin = true;
|
||||
this.error.nativeElement.innerText = error;
|
||||
|
||||
this.error.nativeElement.style.opacity = "1";
|
||||
this.error.nativeElement.style.bottom = "150px";
|
||||
|
||||
setTimeout(() => {
|
||||
this.error.nativeElement.style.opacity = "0";
|
||||
this.error.nativeElement.style.bottom = "120px";
|
||||
this.disableLogin = false;
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
Frontend/src/app/sites/register/register.component.html
Normal file
57
Frontend/src/app/sites/register/register.component.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<section class="login">
|
||||
<form class="glass" (submit)="$event.preventDefault(); register(form, {
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
firstName: firstname.value,
|
||||
lastName: lastname.value,
|
||||
email: email.value
|
||||
}, pwRepeat.value)" #form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Vorname</mat-label>
|
||||
<input matInput type="text" required #firstname>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
<td>
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Nachname</mat-label>
|
||||
<input matInput type="text" required #lastname>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Benutzername</mat-label>
|
||||
<input matInput type="text" required #username>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>E-Mail</mat-label>
|
||||
<input matInput type="email" required #email>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Passwort</mat-label>
|
||||
<input matInput type="password" required #password>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Passwort wiederholen</mat-label>
|
||||
<input matInput type="password" required #pwRepeat>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-button [disabled]="disableRegister" type="submit">Registrieren</button>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div>
|
||||
<span>Du hast bereits einen Account? </span>
|
||||
<a routerLink="/login">Einloggen</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<div class="error" #error></div>
|
||||
42
Frontend/src/app/sites/register/register.component.scss
Normal file
42
Frontend/src/app/sites/register/register.component.scss
Normal file
@@ -0,0 +1,42 @@
|
||||
@use "src/styles";
|
||||
|
||||
.login {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-image: url(styles.$background);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
form {
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
flex-direction: column;
|
||||
|
||||
button[type="submit"] {
|
||||
width: max-content;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
position: absolute;
|
||||
bottom: 120px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
background-color: #F00;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
opacity: 0;
|
||||
|
||||
transition: 500ms;
|
||||
}
|
||||
64
Frontend/src/app/sites/register/register.component.ts
Normal file
64
Frontend/src/app/sites/register/register.component.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||
import {UserApi} from "../../services/users.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {UserEditor} from "../../entitys/user";
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
templateUrl: './register.component.html',
|
||||
styleUrls: ['./register.component.scss']
|
||||
})
|
||||
export class RegisterComponent {
|
||||
@ViewChild('error') error: ElementRef;
|
||||
public disableRegister: boolean = false;
|
||||
|
||||
constructor(private users: UserApi, private router: Router) { }
|
||||
|
||||
public async register(form: HTMLFormElement, register: UserEditor, pwRepeat: string) {
|
||||
if (!form.reportValidity()) return;
|
||||
|
||||
if (register.password !== pwRepeat) {
|
||||
this.showError("Passwörter stimmen nicht überein!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!register.email.includes("@") || !register.email.includes(".")) {
|
||||
this.showError("Bitte gebe eine gültige E-Mail Adresse ein!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (register.username.includes("@")) {
|
||||
this.showError("Benutzername enthätlt ungültige Zeichen!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (register.password.length < 8) {
|
||||
this.showError("Dein Passwort muss mindestens 8 Zeichen lang sein!");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await this.users.register(register);
|
||||
|
||||
if (!response.success) {
|
||||
this.showError(response.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.router.navigate([""]);
|
||||
}
|
||||
|
||||
public showError(error: string) {
|
||||
this.disableRegister = true;
|
||||
this.error.nativeElement.innerText = error;
|
||||
|
||||
this.error.nativeElement.style.opacity = "1";
|
||||
this.error.nativeElement.style.bottom = "150px";
|
||||
|
||||
setTimeout(() => {
|
||||
this.error.nativeElement.style.opacity = "0";
|
||||
this.error.nativeElement.style.bottom = "120px";
|
||||
this.disableRegister = false;
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Frontend/src/assets/background.png
Normal file
BIN
Frontend/src/assets/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
4
Frontend/src/colors.scss
Normal file
4
Frontend/src/colors.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
$dark: #0D1321;
|
||||
$medium: #1D2D44;
|
||||
$light: #3E5C76;
|
||||
$text: #FFFFFF;
|
||||
@@ -6,8 +6,11 @@
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<body class="mat-typography">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1 +1,31 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@use "colors";
|
||||
|
||||
html, body { height: 100%; overflow: hidden; }
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
|
||||
$background: "/assets/background.png";
|
||||
|
||||
* {
|
||||
color: colors.$text;
|
||||
}
|
||||
|
||||
.glass {
|
||||
background: rgba(colors.$light, 0.15);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@mixin mat-select-theme() {
|
||||
.mat-form-field-appearance-fill .mat-form-field-underline::before {
|
||||
background-color: colors.$text;
|
||||
}
|
||||
|
||||
.mat-divider {
|
||||
border-top-color: colors.$text;
|
||||
}
|
||||
}
|
||||
|
||||
@include mat-select-theme();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user