inventory-items #42
@ -61,7 +61,7 @@ import {
|
|||||||
ProductStateEditorComponent
|
ProductStateEditorComponent
|
||||||
} from "./editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component";
|
} from "./editor/gamesystem-editor/state-editor/product-state-editor/product-state-editor.component";
|
||||||
import {MatTooltip} from "@angular/material/tooltip";
|
import {MatTooltip} from "@angular/material/tooltip";
|
||||||
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card";
|
import {MatCard, MatCardActions, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card";
|
||||||
import {
|
import {
|
||||||
ScriptaccountActionEditorComponent
|
ScriptaccountActionEditorComponent
|
||||||
} from "./editor/gamesystem-editor/transition-editor/scriptaccount-action-editor/scriptaccount-action-editor.component";
|
} from "./editor/gamesystem-editor/transition-editor/scriptaccount-action-editor/scriptaccount-action-editor.component";
|
||||||
@ -181,6 +181,7 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
|
|||||||
MatExpansionPanelTitle,
|
MatExpansionPanelTitle,
|
||||||
MatCardTitle,
|
MatCardTitle,
|
||||||
MatExpansionPanelHeader,
|
MatExpansionPanelHeader,
|
||||||
|
MatCardActions,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>Item Qualtities</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-accordion>
|
||||||
|
<mat-expansion-panel *ngFor="let quality of item!.possible_qualities">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>{{quality.quality_step}}</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<app-item-property-editor #perQualityPropertyEditors [item]="item" [quality]="quality"></app-item-property-editor>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</mat-accordion>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
<mat-card>
|
<mat-card>
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>Item Characteristics</mat-card-title>
|
<mat-card-title>Item Characteristics</mat-card-title>
|
||||||
@ -6,3 +22,53 @@
|
|||||||
<app-item-property-editor [item]="item"></app-item-property-editor>
|
<app-item-property-editor [item]="item"></app-item-property-editor>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>Item Quality Characteristics</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<table mat-table [dataSource]="datasource" class="mat-elevation-z8">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Name</th>
|
||||||
|
<td mat-cell *matCellDef="let itemProperty">
|
||||||
|
<span *ngIf="itemProperty !== editedProperty">{{itemProperty.propertyName}}</span>
|
||||||
|
<mat-form-field appearance="fill" *ngIf="itemProperty === editedProperty">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input matInput [(ngModel)]="itemProperty.propertyName">
|
||||||
|
</mat-form-field>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="description">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Description</th>
|
||||||
|
<td mat-cell *matCellDef="let itemProperty">
|
||||||
|
<span *ngIf="itemProperty !== editedProperty">{{itemProperty.propertyDescription}}</span>
|
||||||
|
<mat-form-field appearance="fill" *ngIf="itemProperty === editedProperty">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input matInput [(ngModel)]="itemProperty.propertyDescription">
|
||||||
|
</mat-form-field>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="edit">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let itemProperty">
|
||||||
|
<button *ngIf="editedProperty == undefined" mat-icon-button (click)="editPerQualityProperty(itemProperty)"><mat-icon>edit</mat-icon></button>
|
||||||
|
<button *ngIf="editedProperty !== undefined" mat-icon-button (click)="finishEditing()"><mat-icon>done</mat-icon></button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
<button mat-icon-button (click)="addQualityProperty()"><mat-icon>add</mat-icon></button>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let itemProperty">
|
||||||
|
<button mat-icon-button color="warn" (click)="deletePerQualityProperty(itemProperty)"><mat-icon>delete</mat-icon></button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-edit, .mat-column-delete {
|
||||||
|
width: 32px;
|
||||||
|
}
|
@ -1,17 +1,58 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, ElementRef, Input, OnInit, ViewChildren} from '@angular/core';
|
||||||
import {Character} from "../../project/game-model/characters/Character";
|
import {Character} from "../../project/game-model/characters/Character";
|
||||||
import {GameModel} from "../../project/game-model/GameModel";
|
import {GameModel} from "../../project/game-model/GameModel";
|
||||||
import {Item} from "../../project/game-model/inventory/Item";
|
import {Item} from "../../project/game-model/inventory/Item";
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
||||||
|
import {ItemPropertyDescription} from "../../project/game-model/inventory/ItemPropertyDescription";
|
||||||
|
import {ItemPropertyEditorComponent} from "./item-property-editor/item-property-editor.component";
|
||||||
|
import {MatTableDataSource} from "@angular/material/table";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-inventory-item-editor',
|
selector: 'app-inventory-item-editor',
|
||||||
templateUrl: './inventory-item-editor.component.html',
|
templateUrl: './inventory-item-editor.component.html',
|
||||||
styleUrl: './inventory-item-editor.component.scss'
|
styleUrl: './inventory-item-editor.component.scss'
|
||||||
})
|
})
|
||||||
export class InventoryItemEditorComponent {
|
export class InventoryItemEditorComponent implements OnInit{
|
||||||
|
|
||||||
@Input() item: Item | undefined;
|
@Input() item: Item | undefined;
|
||||||
@Input() gameModel: GameModel | undefined;
|
@Input() gameModel: GameModel | undefined;
|
||||||
|
@ViewChildren('perQualityPropertyEditors') perQualityPropertyEditors: ItemPropertyEditorComponent[] = [];
|
||||||
|
|
||||||
|
displayedColumns: string[] = ['name', 'description', 'edit', 'delete']
|
||||||
|
editedProperty: ItemPropertyDescription | undefined
|
||||||
|
|
||||||
|
datasource: MatTableDataSource<ItemPropertyDescription> = new MatTableDataSource<ItemPropertyDescription>()
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.datasource.data = this.item!.perQualityProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePerQualityProperty(itemProperty: ItemPropertyDescription) {
|
||||||
|
this.item!.removePerQualityProperty(itemProperty);
|
||||||
|
this.datasource.data = this.item!.perQualityProperties
|
||||||
|
this.perQualityPropertyEditors.forEach(component => component.updatedDisplayedData())
|
||||||
|
}
|
||||||
|
|
||||||
|
editPerQualityProperty(itemProperty: ItemPropertyDescription) {
|
||||||
|
this.editedProperty = itemProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
finishEditing() {
|
||||||
|
if(this.editedProperty!.propertyName.length > 0) {
|
||||||
|
this.item!.editPerQualityProperty(this.editedProperty!);
|
||||||
|
|
||||||
|
this.editedProperty = undefined;
|
||||||
|
this.perQualityPropertyEditors.forEach(component => component.updatedDisplayedData())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addQualityProperty() {
|
||||||
|
const qualityDescription = new ItemPropertyDescription("New Property", "")
|
||||||
|
this.item!.addPerQualityProperty(qualityDescription);
|
||||||
|
this.datasource.data = this.item!.perQualityProperties
|
||||||
|
this.editedProperty = qualityDescription;
|
||||||
|
|
||||||
|
this.perQualityPropertyEditors.forEach(component => component.updatedDisplayedData())
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<ng-container matColumnDef="name">
|
<ng-container matColumnDef="name">
|
||||||
<th mat-header-cell *matHeaderCellDef>Name</th>
|
<th mat-header-cell *matHeaderCellDef>Name</th>
|
||||||
<td mat-cell *matCellDef="let itemProperty">
|
<td mat-cell *matCellDef="let itemProperty">
|
||||||
<span *ngIf="editedItemProperty !== itemProperty"> {{itemProperty.propertyName}}</span>
|
<span *ngIf="editedItemProperty !== itemProperty || quality != undefined"> {{itemProperty.propertyName}}</span>
|
||||||
<mat-form-field appearance="fill" *ngIf="editedItemProperty === itemProperty">
|
<mat-form-field appearance="fill" *ngIf="editedItemProperty === itemProperty && quality == undefined">
|
||||||
<mat-label>Name</mat-label>
|
<mat-label>Name</mat-label>
|
||||||
<input matInput [(ngModel)]="itemProperty.propertyName">
|
<input matInput [(ngModel)]="itemProperty.propertyName">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -14,8 +14,8 @@
|
|||||||
<ng-container matColumnDef="expandedDetail">
|
<ng-container matColumnDef="expandedDetail">
|
||||||
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
|
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
|
||||||
<div class="example-element-detail" [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
|
<div class="example-element-detail" [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
|
||||||
<p *ngIf="editedItemProperty !== element">{{element.propertyDescription}}</p>
|
<p *ngIf="editedItemProperty !== element || quality != undefined">{{element.propertyDescription}}</p>
|
||||||
<mat-form-field appearance="fill" class="long-form" *ngIf="editedItemProperty === element">
|
<mat-form-field appearance="fill" class="long-form" *ngIf="editedItemProperty === element && quality == undefined">
|
||||||
<mat-label>Description</mat-label>
|
<mat-label>Description</mat-label>
|
||||||
<textarea matInput [(ngModel)]="element.propertyDescription" rows="3"></textarea>
|
<textarea matInput [(ngModel)]="element.propertyDescription" rows="3"></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -46,13 +46,13 @@
|
|||||||
<ng-container matColumnDef="delete">
|
<ng-container matColumnDef="delete">
|
||||||
<th mat-header-cell *matHeaderCellDef></th>
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
<td mat-cell *matCellDef="let itemProperty">
|
<td mat-cell *matCellDef="let itemProperty">
|
||||||
<button mat-icon-button color="warn" (click)="deleteItemProperty(itemProperty)"><mat-icon>delete</mat-icon></button>
|
<button *ngIf="quality == undefined" mat-icon-button color="warn" (click)="deleteItemProperty(itemProperty)"><mat-icon>delete</mat-icon></button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="expand">
|
<ng-container matColumnDef="expand">
|
||||||
<th mat-header-cell *matHeaderCellDef aria-label="row actions">
|
<th mat-header-cell *matHeaderCellDef aria-label="row actions">
|
||||||
<button mat-icon-button [disabled]="editedItemProperty !== undefined" (click)="addItemProperty()"><mat-icon>add</mat-icon></button>
|
<button *ngIf="quality == undefined" mat-icon-button [disabled]="editedItemProperty !== undefined" (click)="addItemProperty()"><mat-icon>add</mat-icon></button>
|
||||||
</th>
|
</th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button mat-icon-button aria-label="expand row" (click)="(expandedElement = expandedElement === element ? null : element); $event.stopPropagation()">
|
<button mat-icon-button aria-label="expand row" (click)="(expandedElement = expandedElement === element ? null : element); $event.stopPropagation()">
|
||||||
|
@ -4,6 +4,7 @@ import {MatTableDataSource} from "@angular/material/table";
|
|||||||
import {ItemProperty} from "../../../project/game-model/inventory/ItemProperty";
|
import {ItemProperty} from "../../../project/game-model/inventory/ItemProperty";
|
||||||
import {animate, state, style, transition, trigger} from "@angular/animations";
|
import {animate, state, style, transition, trigger} from "@angular/animations";
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {ItemQuality} from "../../../project/game-model/inventory/ItemQuality";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-item-property-editor',
|
selector: 'app-item-property-editor',
|
||||||
@ -20,6 +21,7 @@ import {MatSnackBar} from "@angular/material/snack-bar";
|
|||||||
export class ItemPropertyEditorComponent implements OnInit{
|
export class ItemPropertyEditorComponent implements OnInit{
|
||||||
|
|
||||||
@Input() item: Item | undefined
|
@Input() item: Item | undefined
|
||||||
|
@Input() quality: ItemQuality | undefined
|
||||||
editedItemProperty: ItemProperty | undefined
|
editedItemProperty: ItemProperty | undefined
|
||||||
expandedElement: ItemProperty | undefined
|
expandedElement: ItemProperty | undefined
|
||||||
|
|
||||||
@ -32,12 +34,19 @@ export class ItemPropertyEditorComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.datasource.data = this.item!.itemProperties
|
this.updatedDisplayedData()
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteItemProperty(itemProperty: ItemProperty) {
|
deleteItemProperty(itemProperty: ItemProperty) {
|
||||||
this.item!.itemProperties = this.item!.itemProperties.filter(property => property.propertyName !== itemProperty.propertyName)
|
if(this.quality == undefined) {
|
||||||
this.datasource.data = this.item!.itemProperties
|
this.item!.itemProperties = this.item!.itemProperties.filter(property => property.propertyName !== itemProperty.propertyName)
|
||||||
|
this.datasource.data = this.item!.itemProperties
|
||||||
|
} else {
|
||||||
|
this.quality.quality_propertes = this.quality.quality_propertes.filter(property => property.propertyName !== itemProperty.propertyName)
|
||||||
|
this.datasource.data = this.quality.quality_propertes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editItemProperty(itemProperty: ItemProperty) {
|
editItemProperty(itemProperty: ItemProperty) {
|
||||||
@ -53,9 +62,21 @@ export class ItemPropertyEditorComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
addItemProperty() {
|
addItemProperty() {
|
||||||
const itemProperty = new ItemProperty("New Property", "", 0);
|
if(this.quality == undefined) {
|
||||||
this.item!.itemProperties.push(itemProperty);
|
const itemProperty = new ItemProperty("New Property", "", 0);
|
||||||
this.datasource.data = this.item!.itemProperties;
|
this.item!.itemProperties.push(itemProperty);
|
||||||
this.editedItemProperty = itemProperty;
|
this.datasource.data = this.item!.itemProperties;
|
||||||
|
this.editedItemProperty = itemProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedDisplayedData() {
|
||||||
|
if(this.quality != undefined) {
|
||||||
|
this.datasource.data = this.quality!.quality_propertes
|
||||||
|
this.displayedColumns = ['name', 'property', 'edit']
|
||||||
|
this.columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
|
||||||
|
} else {
|
||||||
|
this.datasource.data = this.item!.itemProperties
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,43 @@ import {ItemProperty} from "./ItemProperty";
|
|||||||
import {ItemQuality} from "./ItemQuality";
|
import {ItemQuality} from "./ItemQuality";
|
||||||
import {ModelComponent} from "../ModelComponent";
|
import {ModelComponent} from "../ModelComponent";
|
||||||
import {ModelComponentType} from "../ModelComponentType";
|
import {ModelComponentType} from "../ModelComponentType";
|
||||||
|
import {ItemPropertyDescription} from "./ItemPropertyDescription";
|
||||||
|
|
||||||
export class Item extends ModelComponent {
|
export class Item extends ModelComponent {
|
||||||
|
|
||||||
possible_qualities: ItemQuality[] = []
|
possible_qualities: ItemQuality[] = []
|
||||||
itemProperties: ItemProperty[] = []
|
itemProperties: ItemProperty[] = []
|
||||||
|
|
||||||
|
perQualityProperties: ItemPropertyDescription[] = []
|
||||||
|
|
||||||
constructor(componentName: string, componentDescription: string, type: ModelComponentType) {
|
constructor(componentName: string, componentDescription: string, type: ModelComponentType) {
|
||||||
super(componentName, componentDescription, type);
|
super(componentName, componentDescription, type);
|
||||||
this.itemProperties.push(new ItemProperty("Weight", "Some Weights", 10))
|
this.itemProperties.push(new ItemProperty("Weight", "Some Weights", 10))
|
||||||
|
this.possible_qualities.push(new ItemQuality(0.25))
|
||||||
|
this.possible_qualities.push(new ItemQuality(0.50))
|
||||||
|
this.possible_qualities.push(new ItemQuality(0.75))
|
||||||
|
this.possible_qualities.push(new ItemQuality(1.00))
|
||||||
|
|
||||||
|
this.addPerQualityProperty(new ItemPropertyDescription("Price", "Price to buy item"))
|
||||||
|
}
|
||||||
|
|
||||||
|
removePerQualityProperty(qualityProperty: ItemPropertyDescription) {
|
||||||
|
this.perQualityProperties = this.perQualityProperties.filter(perQualityProperty => perQualityProperty !== qualityProperty);
|
||||||
|
this.possible_qualities.forEach(quality => {
|
||||||
|
quality.removeQualityProperty(qualityProperty.propertyName);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addPerQualityProperty(qualityProperty: ItemPropertyDescription) {
|
||||||
|
if(this.perQualityProperties.find(property => property.propertyName === qualityProperty.propertyName) == undefined) {
|
||||||
|
this.perQualityProperties.push(qualityProperty);
|
||||||
|
this.possible_qualities.forEach(quality => quality.addQualityProperty(qualityProperty))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editPerQualityProperty(qualityProperty: ItemPropertyDescription) {
|
||||||
|
this.possible_qualities.forEach(quality => {
|
||||||
|
quality.editQualityProperty(qualityProperty, this.perQualityProperties.map(property => property.propertyName))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
export class ItemPropertyDescription {
|
||||||
|
propertyName: string;
|
||||||
|
propertyDescription: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(propertyName: string, propertyDescription: string) {
|
||||||
|
this.propertyName = propertyName;
|
||||||
|
this.propertyDescription = propertyDescription;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,30 @@
|
|||||||
|
import {ItemProperty} from "./ItemProperty";
|
||||||
|
import {ItemPropertyDescription} from "./ItemPropertyDescription";
|
||||||
|
|
||||||
export class ItemQuality {
|
export class ItemQuality {
|
||||||
quality_step: number
|
quality_step: number
|
||||||
quality_propertes: ItemQuality[] = []
|
quality_propertes: ItemProperty[] = []
|
||||||
|
|
||||||
|
|
||||||
constructor(quality_step: number) {
|
constructor(quality_step: number) {
|
||||||
this.quality_step = quality_step;
|
this.quality_step = quality_step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeQualityProperty(propertyName: string) {
|
||||||
|
this.quality_propertes = this.quality_propertes.filter(qualityProperty => qualityProperty.propertyName !== propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
addQualityProperty(qualityProperty: ItemPropertyDescription) {
|
||||||
|
this.quality_propertes.push(new ItemProperty(qualityProperty.propertyName, qualityProperty.propertyDescription, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
editQualityProperty(qualityProperty: ItemPropertyDescription, knownProperties: string[]) {
|
||||||
|
const affectedProperty = this.quality_propertes.find(property => !knownProperties.includes(property.propertyName) )
|
||||||
|
if(affectedProperty != undefined) {
|
||||||
|
affectedProperty!.propertyName = qualityProperty.propertyName;
|
||||||
|
affectedProperty!.propertyDescription = qualityProperty.propertyDescription;
|
||||||
|
} else {
|
||||||
|
console.log("Property was not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user