simple role management
This commit is contained in:
@@ -2,8 +2,17 @@ rules_version = '2';
|
|||||||
service cloud.firestore {
|
service cloud.firestore {
|
||||||
match /databases/{database}/documents {
|
match /databases/{database}/documents {
|
||||||
match /users/{user} {
|
match /users/{user} {
|
||||||
allow read: if resource.id == request.auth.uid;
|
|
||||||
allow write: if resource.id == request.auth.uid;
|
function isUser(rsc) {
|
||||||
|
return rsc.id == request.auth.uid
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdmin() {
|
||||||
|
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin'
|
||||||
|
}
|
||||||
|
|
||||||
|
allow read: if isUser(resource) || isAdmin();
|
||||||
|
allow write: if isUser(resource) || isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
match /songs/{song} {
|
match /songs/{song} {
|
||||||
@@ -31,3 +40,4 @@ service cloud.firestore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,4 @@ export class AppComponent implements OnInit {
|
|||||||
setTimeout(() => AppComponent.hideLoader(), 800);
|
setTimeout(() => AppComponent.hideLoader(), 800);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
title = 'wgenerator';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {SongService} from '../../songs/services/song.service';
|
|||||||
import {ShowSong} from './show-song';
|
import {ShowSong} from './show-song';
|
||||||
import {Show} from './show';
|
import {Show} from './show';
|
||||||
import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component';
|
import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {User} from '../../../services/user';
|
import {User} from '../../../services/user/user';
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {Observable} from 'rxjs';
|
|||||||
import {ShowSong} from './show-song';
|
import {ShowSong} from './show-song';
|
||||||
import {SongDataService} from '../../songs/services/song-data.service';
|
import {SongDataService} from '../../songs/services/song-data.service';
|
||||||
import {take} from 'rxjs/operators';
|
import {take} from 'rxjs/operators';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import {Injectable} from '@angular/core';
|
|||||||
import {ShowDataService} from './show-data.service';
|
import {ShowDataService} from './show-data.service';
|
||||||
import {Show} from './show';
|
import {Show} from './show';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {map, switchMap} from 'rxjs/operators';
|
import {map, switchMap} from 'rxjs/operators';
|
||||||
import {User} from '../../../services/user';
|
import {User} from '../../../services/user/user';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
[showSong]="song"
|
[showSong]="song"
|
||||||
[showSongs]="showSongs"
|
[showSongs]="showSongs"
|
||||||
[showText]="showText"
|
[showText]="showText"
|
||||||
[song]="getSong(song.songId)"
|
|
||||||
[show]="show"
|
[show]="show"
|
||||||
|
[song]="getSong(song.songId)"
|
||||||
class="song-row"
|
class="song-row"
|
||||||
></app-song>
|
></app-song>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {Song} from '../services/song';
|
|||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {FileDataService} from '../services/file-data.service';
|
import {FileDataService} from '../services/file-data.service';
|
||||||
import {File} from '../services/file';
|
import {File} from '../services/file';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {User} from '../../../services/user';
|
import {User} from '../../../services/user/user';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-song',
|
selector: 'app-song',
|
||||||
|
|||||||
@@ -17,3 +17,6 @@
|
|||||||
<button mat-button routerLink="../logout">Abmelden</button>
|
<button mat-button routerLink="../logout">Abmelden</button>
|
||||||
</app-button-row>
|
</app-button-row>
|
||||||
</app-card>
|
</app-card>
|
||||||
|
|
||||||
|
|
||||||
|
<app-users *appRole="['admin']"></app-users>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {User} from '../../../services/user';
|
import {User} from '../../../services/user/user';
|
||||||
import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component';
|
import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {roles} from '../../../services/user/roles';
|
||||||
|
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'role'
|
name: 'role'
|
||||||
})
|
})
|
||||||
export class RolePipe implements PipeTransform {
|
export class RolePipe implements PipeTransform {
|
||||||
|
|
||||||
transform(role: 'admin'): string {
|
transform(role: roles): string {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
|
case 'none':
|
||||||
|
return 'keine Berechtigung';
|
||||||
case 'admin':
|
case 'admin':
|
||||||
return 'Administrator';
|
return 'Administrator';
|
||||||
|
case 'user':
|
||||||
|
return 'Benutzer';
|
||||||
|
case 'leader':
|
||||||
|
return 'Lobpreisleiter';
|
||||||
|
case 'presenter':
|
||||||
|
return 'Präsentator';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
src/app/modules/user/info/users/users.component.html
Normal file
18
src/app/modules/user/info/users/users.component.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<app-card heading="registrierte Benutzer">
|
||||||
|
|
||||||
|
<div *ngFor="let user of users$|async" class="users">
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input (change)="onNameChanged(user.id, $event)" [ngModel]="user.name" matInput>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>Rolle</mat-label>
|
||||||
|
<mat-select (ngModelChange)="onRoleChanged(user.id, $event)" [ngModel]="user.role">
|
||||||
|
<mat-option *ngFor="let role of ROLE_TYPES" [value]="role">{{role | role}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</app-card>
|
||||||
5
src/app/modules/user/info/users/users.component.less
Normal file
5
src/app/modules/user/info/users/users.component.less
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.users {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 20px;
|
||||||
|
}
|
||||||
25
src/app/modules/user/info/users/users.component.spec.ts
Normal file
25
src/app/modules/user/info/users/users.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {UsersComponent} from './users.component';
|
||||||
|
|
||||||
|
describe('UsersComponent', () => {
|
||||||
|
let component: UsersComponent;
|
||||||
|
let fixture: ComponentFixture<UsersComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [UsersComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UsersComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
31
src/app/modules/user/info/users/users.component.ts
Normal file
31
src/app/modules/user/info/users/users.component.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {UserService} from '../../../../services/user/user.service';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {User} from '../../../../services/user/user';
|
||||||
|
import {ROLE_TYPES} from '../../../../services/user/roles';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-users',
|
||||||
|
templateUrl: './users.component.html',
|
||||||
|
styleUrls: ['./users.component.less']
|
||||||
|
})
|
||||||
|
export class UsersComponent implements OnInit {
|
||||||
|
public users$: Observable<User[]>;
|
||||||
|
public ROLE_TYPES = ROLE_TYPES;
|
||||||
|
|
||||||
|
constructor(private userService: UserService) {
|
||||||
|
this.users$ = userService.list$();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onRoleChanged(id: string, role: any): Promise<void> {
|
||||||
|
await this.userService.update$(id, {role});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onNameChanged(id: string, name: any): Promise<void> {
|
||||||
|
await this.userService.update$(id, {name: name.target.value});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: 'app-login',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {AfterViewInit, Component} from '@angular/core';
|
import {AfterViewInit, Component} from '@angular/core';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-logout',
|
selector: 'app-logout',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {UserService} from '../../../services/user.service';
|
import {UserService} from '../../../services/user/user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-password',
|
selector: 'app-password',
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ import {RolePipe} from './info/role.pipe';
|
|||||||
import {MatSelectModule} from '@angular/material/select';
|
import {MatSelectModule} from '@angular/material/select';
|
||||||
import {PasswordComponent} from './password/password.component';
|
import {PasswordComponent} from './password/password.component';
|
||||||
import {PasswordSendComponent} from './password-send/password-send.component';
|
import {PasswordSendComponent} from './password-send/password-send.component';
|
||||||
|
import {UsersComponent} from './info/users/users.component';
|
||||||
|
import {RoleModule} from '../../services/user/role.module';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent],
|
declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent, UsersComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
UserRoutingModule,
|
UserRoutingModule,
|
||||||
@@ -30,6 +32,7 @@ import {PasswordSendComponent} from './password-send/password-send.component';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
RoleModule,
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
8
src/app/services/user/role.directive.spec.ts
Normal file
8
src/app/services/user/role.directive.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import {RoleDirective} from './role.directive';
|
||||||
|
|
||||||
|
describe('RoleDirective', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
const directive = new RoleDirective();
|
||||||
|
expect(directive).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
55
src/app/services/user/role.directive.ts
Normal file
55
src/app/services/user/role.directive.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import {Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
import {roles} from './roles';
|
||||||
|
import {UserService} from './user.service';
|
||||||
|
import {User} from './user';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[appRole]'
|
||||||
|
})
|
||||||
|
export class RoleDirective implements OnInit {
|
||||||
|
@Input() appRole: roles[] = [];
|
||||||
|
private currentUser: User;
|
||||||
|
private loggedIn: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private element: ElementRef,
|
||||||
|
private templateRef: TemplateRef<any>,
|
||||||
|
private viewContainer: ViewContainerRef,
|
||||||
|
private userService: UserService
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.userService.user$.subscribe(user => {
|
||||||
|
this.currentUser = user;
|
||||||
|
this.updateView();
|
||||||
|
});
|
||||||
|
this.userService.loggedIn$().subscribe(_ => {
|
||||||
|
this.loggedIn = !!_;
|
||||||
|
this.updateView();
|
||||||
|
});
|
||||||
|
this.updateView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateView() {
|
||||||
|
if (this.loggedIn && this.checkPermission()) {
|
||||||
|
this.viewContainer.createEmbeddedView(this.templateRef);
|
||||||
|
} else {
|
||||||
|
this.viewContainer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkPermission() {
|
||||||
|
if (this.currentUser && this.currentUser.role) {
|
||||||
|
if (this.currentUser.role === 'admin') return true;
|
||||||
|
for (const role of this.appRole) {
|
||||||
|
if (this.currentUser.role === role) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
13
src/app/services/user/role.module.ts
Normal file
13
src/app/services/user/role.module.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {RoleDirective} from './role.directive';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [RoleDirective],
|
||||||
|
exports: [RoleDirective],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RoleModule {
|
||||||
|
}
|
||||||
2
src/app/services/user/roles.ts
Normal file
2
src/app/services/user/roles.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export type roles = 'none' | 'admin' | 'user' | 'leader' | 'presenter';
|
||||||
|
export const ROLE_TYPES: roles[] = ['none', 'admin', 'user', 'leader', 'presenter'];
|
||||||
@@ -3,7 +3,7 @@ import {AngularFireAuth} from '@angular/fire/auth';
|
|||||||
import {BehaviorSubject, Observable} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {filter, switchMap} from 'rxjs/operators';
|
import {filter, switchMap} from 'rxjs/operators';
|
||||||
import {User} from './user';
|
import {User} from './user';
|
||||||
import {DbService} from './db.service';
|
import {DbService} from '../db.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -22,6 +22,10 @@ export class UserService {
|
|||||||
return this._user$.pipe(filter(_ => !!_));
|
return this._user$.pipe(filter(_ => !!_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public loggedIn$ = () => this.afAuth.authState;
|
||||||
|
|
||||||
|
public list$ = (): Observable<User[]> => this.db.col$('users');
|
||||||
|
|
||||||
public getUserbyId$(userId: string): Observable<User> {
|
public getUserbyId$(userId: string): Observable<User> {
|
||||||
return this.db.doc$<User>('users/' + userId);
|
return this.db.doc$<User>('users/' + userId);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {ChordMode} from '../widget-modules/components/song-text/song-text.component';
|
import {ChordMode} from '../../widget-modules/components/song-text/song-text.component';
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -6,7 +6,7 @@ import {FilterComponent} from './navigation/filter/filter.component';
|
|||||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
import {LinkComponent} from './navigation/link/link.component';
|
import {LinkComponent} from './navigation/link/link.component';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
|
import {RoleModule} from '../../../services/user/role.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -18,7 +18,8 @@ import {FormsModule} from '@angular/forms';
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
RoleModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
NavigationComponent
|
NavigationComponent
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<nav [class.hidden]="(windowScroll$|async)>60" class="head">
|
<nav [class.hidden]="(windowScroll$|async)>60" class="head">
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<app-link [icon]="faSongs" link="/songs" text="Lieder"></app-link>
|
<app-link *appRole="['user','presenter', 'leader']" [icon]="faSongs" link="/songs" text="Lieder"></app-link>
|
||||||
<app-link [icon]="faShows" link="/shows" text="Veranstaltungen"></app-link>
|
<app-link *appRole="['leader']" [icon]="faShows" link="/shows" text="Veranstaltungen"></app-link>
|
||||||
<app-link [icon]="faPresentation" link="/presentation" text="Präsentation"></app-link>
|
<app-link *appRole="['presenter']" [icon]="faPresentation" link="/presentation" text="Präsentation"></app-link>
|
||||||
<app-link [icon]="faUser" link="/user" text="Benutzer"></app-link>
|
<app-link [icon]="faUser" link="/user" text="Benutzer"></app-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ export class SongTextComponent implements OnInit {
|
|||||||
scrollTo(0, this.elRef.nativeElement.offsetTop - 20);
|
scrollTo(0, this.elRef.nativeElement.offsetTop - 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public checkDisabled(i: number) {
|
||||||
|
return this.index !== -1 && this.index !== i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private getNextChordMode(): ChordMode {
|
private getNextChordMode(): ChordMode {
|
||||||
switch (this._chordMode) {
|
switch (this._chordMode) {
|
||||||
case 'show':
|
case 'show':
|
||||||
@@ -93,9 +98,4 @@ export class SongTextComponent implements OnInit {
|
|||||||
return 'show';
|
return 'show';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkDisabled(i: number) {
|
|
||||||
return this.index !== -1 && this.index !== i;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ a {
|
|||||||
.content > *:not(router-outlet) {
|
.content > *:not(router-outlet) {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
Reference in New Issue
Block a user