Commit a8639a58 authored by Tomáš Biloš's avatar Tomáš Biloš
Browse files

Merge branch 'feat/visuals' into 'main'

feat: Agent, country visuals

See merge request !83
parents 8d5922ea 7175d65a
<div *ngIf="state === requestState.ERROR">
Error occured: {{ errorMessage }}
</div>
<div *ngIf="agent && state === requestState.SUCCESS" class="px-4">
<div class="flex">
<span class="text-4xl text-black mb-4">{{ agent.name }}</span>
<button
mat-icon-button
routerLink="../edit/{{ agent.id }}"
class="justify-end"
>
<mat-icon>edit</mat-icon>
</button>
</div>
<mat-toolbar *ngIf="agent" color="primary" class="flex flex-row">
<button mat-icon-button routerLink="/auth/agents" aria-label="Go back">
<mat-icon>arrow_back</mat-icon>
</button>
<br />
<span class="text-md">Training</span>
<p class="mb-4 border-b-[1xp] border-gray-200 text-lg">
{{ agent.training }}
</p>
<span>Agent detail</span>
<table mat-table [dataSource]="agent.codeNames" class="pb-4 w-full">
<ng-container matColumnDef="codename">
<th mat-header-cell *matHeaderCellDef class="text-lg text-black">
Codenames
</th>
<td mat-cell *matCellDef="let codename" class="text-gray-700">
{{ codename.codeName }}
</td>
</ng-container>
<span class="flex-1"></span>
<tr mat-header-row *matHeaderRowDef="codenameColumns"></tr>
<tr mat-row *matRowDef="let row; columns: codenameColumns"></tr>
</table>
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Example icon-button with a menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Delete</button>
</mat-menu>
</mat-toolbar>
<table
mat-table
[dataSource]="agent.skills"
class="pb-6 text-gray-700 w-full"
>
<ng-container matColumnDef="skill">
<th mat-header-cell *matHeaderCellDef class="text-lg text-black">
Skills
</th>
<td mat-cell *matCellDef="let skill">{{ skill.name }}</td>
</ng-container>
<div *ngIf="state === requestState.ERROR">
Error occured: {{ errorMessage }}
</div>
<tr mat-header-row *matHeaderRowDef="skillColumns"></tr>
<tr mat-row *matRowDef="let row; columns: skillColumns"></tr>
</table>
<div *ngIf="agent && state === requestState.SUCCESS" class="main-container">
<div class="pl-6 text-lg text-black">Assignments</div>
<table
mat-table
[dataSource]="agent.agentAssignments"
class="pb-6 w-full text-gray-700"
>
<ng-container matColumnDef="missionName">
<th mat-header-cell *matHeaderCellDef class="text-black">Mission</th>
<td mat-cell *matCellDef="let assignment">
{{ assignment.mission.name }}
</td>
</ng-container>
<div class="px-4">
<h1>{{ agent.name }}</h1>
<ng-container matColumnDef="start">
<th mat-header-cell *matHeaderCellDef class="text-black">Start</th>
<td mat-cell *matCellDef="let assignment">{{ assignment.start }}</td>
</ng-container>
<h4 class="!m-0">Training</h4>
<p>{{ agent.training }}</p>
</div>
<tr mat-header-row *matHeaderRowDef="assignmentColumns"></tr>
<tr
mat-row
*matRowDef="let row; columns: assignmentColumns"
routerLink="/auth/assignment/{{ row.id }}"
class="hover:bg-gray-200 hover:cursor-pointer"
></tr>
</table>
<mat-list>
<div mat-subheader>Codenames</div>
<mat-list-item *ngFor="let codeName of agent.codeNames">
<mat-icon mat-list-icon>badge</mat-icon>
<h2 mat-line> {{codeName.codeName}} </h2>
</mat-list-item>
<mat-divider></mat-divider>
<div mat-subheader>Skills</div>
<mat-list-item *ngFor="let skill of agent.skills">
<mat-icon mat-list-icon>plumbing</mat-icon>
<h2 mat-line> {{skill.name}} </h2>
</mat-list-item>
<mat-divider></mat-divider>
<div mat-subheader>Assignments</div>
<mat-list-item *ngFor="let assignment of agent.agentAssignments">
<mat-icon mat-list-icon>assignment</mat-icon>
<div mat-line>{{ assignment.mission.name }} </div>
<div mat-line>{{ assignment.start }} ({{ assignment.durationInDays }} days)</div>
</mat-list-item>
</mat-list>
<button color="accent" mat-fab routerLink="../edit/{{ agent.id }}" class="!fixed right-8 bottom-20"
aria-label="Edit country details">
<mat-icon>edit</mat-icon>
</button>
</div>
<div *ngIf="state === requestState.ERROR">
Error occured: {{ errorMessage }}
</div>
<div *ngIf="state === requestState.SUCCESS">
<mat-form-field appearance="fill">
<mat-label>State</mat-label>
<mat-select #filterCountry>
<mat-option *ngFor="let country of countries" [value]="country">{{
country.name
}}</mat-option>
</mat-select>
</mat-form-field>
<div *ngIf="state === requestState.SUCCESS" class="main-container">
<div class="flex flex-col px-4">
<button mat-raised-button routerLink="../agent/create">Create agent</button>
<mat-form-field>
<mat-form-field class="example-chip-list" appearance="fill">
<mat-label>Filter by skills</mat-label>
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip *ngFor="let skill of filterSkills" (removed)="remove(skill)">
{{ skill.name }}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input
placeholder="Skill ..."
#fruitInput
[formControl]="fruitCtrl"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="add($event)"
/>
</mat-chip-list>
<mat-autocomplete
#auto="matAutocomplete"
(optionSelected)="selected($event)"
>
<mat-option *ngFor="let skill of filteredSkills | async" [value]="skill">
{{ skill.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-label>Filter by state</mat-label>
<mat-select #filterCountry (valueChange)="filter(filterCountry.value)">
<mat-option>Any</mat-option>
<mat-option *ngFor="let country of countries" [value]="country">
{{ country.name }}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-raised-button (click)="filter(filterCountry.value)">
Filter
</button>
<mat-form-field>
<mat-label>Filter by skills</mat-label>
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip *ngFor="let skill of filterSkills" (removed)="remove(skill); filter(filterCountry.value)">
{{ skill.name }}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input placeholder="Skill…" #fruitInput [formControl]="fruitCtrl" [matAutocomplete]="auto"
[matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="add($event)" />
</mat-chip-list>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngFor="let skill of filteredSkills | async" [value]="skill">
{{ skill.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
<mat-divider></mat-divider>
<table mat-table [dataSource]="agents" class="w-full">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let agent">
{{ agent.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="agentColumns"></tr>
<tr
mat-row
class="hover:bg-gray-200"
routerLink="../agent/{{ row.id }}"
*matRowDef="let row; columns: agentColumns"
></tr>
</table>
<mat-action-list>
<mat-list-item *ngFor="let agent of agents" [routerLink]="['/auth/agent/', agent.id]">
<h2 mat-line> {{agent.name}} </h2>
<p mat-line>
<span *ngFor="let codeName of agent.codeNames; let isLast = last" class="italic">
{{codeName.codeName}}{{isLast ? '' : ', '}}
</span>
</p>
<mat-divider></mat-divider>
</mat-list-item>
</mat-action-list>
</div>
<button mat-fab color="accent" class="!fixed right-8 bottom-20" routerLink="../agent/create"
aria-label="Create new agent">
<mat-icon>add</mat-icon>
</button>
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { HttpClient } from '@angular/common/http';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
......@@ -26,7 +26,9 @@ interface Country {
selector: 'agent-list',
templateUrl: 'agent-list.component.html',
})
export class AgentListComponent implements OnInit {
export class AgentListComponent implements OnInit, AfterViewInit {
@ViewChild("filterCountry", {read: ElementRef}) filterCountryElem!: ElementRef;
// Big spaghetti, I don't know what I am doing.
requestState = RequestState;
state: RequestState = this.requestState.PENDING;
......@@ -44,6 +46,7 @@ export class AgentListComponent implements OnInit {
filterSkills: Skill[] = [];
allSkills: Skill[] = [];
constructor(private httpClient: HttpClient, private router: Router) {
this.filteredSkills = this.fruitCtrl.valueChanges.pipe(
startWith(null),
......@@ -53,13 +56,19 @@ export class AgentListComponent implements OnInit {
);
}
ngAfterViewInit() {
this.fruitCtrl.valueChanges.subscribe(() => {
this.filter(this.filterCountryElem.nativeElement.value)
});
}
@ViewChild('fruitInput') fruitInput: ElementRef<HTMLInputElement> | undefined;
add(event: MatChipInputEvent): void {
const value = (event.value || '').trim();
const toAdd = this.allSkills.filter((skill) => skill.name === value)[0];
// Add our fruit
// Add our skills
if (toAdd) {
this.filterSkills.push(toAdd);
}
......@@ -68,10 +77,13 @@ export class AgentListComponent implements OnInit {
event.chipInput!.clear();
this.fruitCtrl.setValue(null);
console.log(this.filterCountryElem.nativeElement.value)
console.log("was here")
this.filter(this.filterCountryElem.nativeElement.value)
}
remove(fruit: Skill): void {
const index = this.filterSkills.indexOf(fruit);
remove(skill: Skill): void {
const index = this.filterSkills.indexOf(skill);
if (index >= 0) {
this.filterSkills.splice(index, 1);
......@@ -134,6 +146,10 @@ export class AgentListComponent implements OnInit {
throw err;
}
this.state = this.requestState.SUCCESS;
// this.chipList.nativeElement.valueChanges.subscribe(() => {
// this.filter(this.filterCountryElem.nativeElement.value)
// });
}
async getAgents(query: string) {
......
<div *ngIf="agent">
<form [formGroup]="edit">
<mat-toolbar *ngIf="agent" color="primary" class="flex flex-row">
<button mat-icon-button routerLink="/auth/agents" aria-label="Cancel editing">
<mat-icon>close</mat-icon>
</button>
<span>Edit agent</span>
<span class="flex-1"></span>
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Delete</button>
</mat-menu>
</mat-toolbar>
<div *ngIf="agent" class="main-container">
<form [formGroup]="edit" class="flex flex-col px-4">
<mat-form-field>
<mat-label>Agent name</mat-label>
<input matInput formControlName="name" />
......@@ -7,81 +24,72 @@
</mat-form-field>
<mat-form-field>
<mat-label>Training</mat-label>
<textarea matInput formControlName="training"></textarea>
<mat-label>Role</mat-label>
<mat-select [(value)]="selectedRole">
<mat-option *ngFor="let role of roles" [value]="role">
{{ role }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput formControlName="password" />
<input matInput formControlName="password" type="password" />
<mat-error>Must be at least 6 characters long</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Training</mat-label>
<textarea matInput formControlName="training"></textarea>
</mat-form-field>
</form>
<mat-form-field>
<mat-label>New skill</mat-label>
<input matInput #newSkill />
</mat-form-field>
<button mat-icon-button (click)="addSkill(newSkill.value)">
<mat-icon>add</mat-icon>
</button>
<mat-divider></mat-divider>
<mat-form-field>
<mat-label>New codename</mat-label>
<input matInput #newCodename />
</mat-form-field>
<button mat-icon-button (click)="addCodename(newCodename.value)">
<mat-icon>add</mat-icon>
</button>
<mat-list>
<div mat-subheader>Codenames</div>
<mat-list-item *ngFor="let codeName of agent.codeNames">
<mat-icon mat-list-icon>badge</mat-icon>
<h2 mat-line> {{codeName.codeName}} </h2>
</mat-list-item>
<mat-form-field appearance="fill">
<mat-label>Favorite food</mat-label>
<mat-select [(value)]="selectedRole">
<mat-option *ngFor="let role of roles" [value]="role">
{{ role }}
</mat-option>
</mat-select>
</mat-form-field>
<table mat-table [dataSource]="agent.codeNames" class="pb-4 w-full">
<ng-container matColumnDef="codename">
<th mat-header-cell *matHeaderCellDef class="text-lg text-black">
Codenames
</th>
<td mat-cell *matCellDef="let codename" class="text-gray-700">
{{ codename.codeName }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="codenameColumns"></tr>
<tr mat-row *matRowDef="let row; columns: codenameColumns"></tr>
</table>
<table
mat-table
[dataSource]="agent.skills"
class="pb-6 text-gray-700 w-full"
>
<ng-container matColumnDef="skill">
<th mat-header-cell *matHeaderCellDef class="text-lg text-black">
Skills
</th>
<td mat-cell *matCellDef="let skill">{{ skill.name }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="skillColumns"></tr>
<tr mat-row *matRowDef="let row; columns: skillColumns"></tr>
</table>
<button class="ml-4" mat-raised-button routerLink="auth/agents">
Cancel
</button>
<button
class="mr-4"
mat-raised-button
disabled="{{ !edit.valid }}"
(click)="saveAgent()"
>
Save
<mat-list-item>
<mat-icon mat-list-icon></mat-icon>
<mat-form-field mat-line>
<mat-label>New codename</mat-label>
<input matInput #newCodename />
</mat-form-field>
<button mat-icon-button (click)="addCodename(newCodename)">
<mat-icon>add</mat-icon>
</button>
</mat-list-item>
<mat-divider></mat-divider>
<div mat-subheader>Skills</div>
<mat-list-item *ngFor="let skill of agent.skills">
<mat-icon mat-list-icon>plumbing</mat-icon>
<h2 mat-line> {{skill.name}} </h2>
</mat-list-item>
<mat-list-item>
<mat-icon mat-list-icon></mat-icon>
<mat-form-field mat-line>
<mat-label>Add skill</mat-label>
<input matInput #newSkill />
</mat-form-field>
<button mat-icon-button (click)="addSkill(newSkill)">
<mat-icon>add</mat-icon>
</button>
</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<button color="accent" mat-fab class="!fixed right-8 bottom-20" aria-label="Save agent details"
disabled="{{ !edit.valid }}" (click)="saveAgent()">
<mat-icon>done</mat-icon>
</button>
</div>
......@@ -70,7 +70,9 @@ export class EditAgentComponent implements OnInit {
}
}
addCodename(newCodename: string) {
addCodename(input: HTMLInputElement) {
const newCodename = input.value
if (!newCodename || newCodename.length === 0) {
return;
}
......@@ -79,9 +81,13 @@ export class EditAgentComponent implements OnInit {
}
this.agent!.codeNames.push({ codeName: newCodename });
this.agent!.codeNames = [...this.agent!.codeNames];
input.value = ""
}
addSkill(newSkill: string) {
addSkill(input: HTMLInputElement) {
const newSkill = input.value
if (!newSkill || newSkill.length === 0) {
return;
}
......@@ -90,6 +96,8 @@ export class EditAgentComponent implements OnInit {
}
this.agent!.skills.push({ name: newSkill });
this.agent!.skills = [...this.agent!.skills];
input.value = ""
}
async saveAgent() {
......
......@@ -10,6 +10,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatTableModule } from '@angular/material/table';
......@@ -68,6 +69,7 @@ import {AssignmentListComponent} from "./assignment/assignment-list.component";
MatIconModule,
MatInputModule,
MatListModule,
MatMenuModule,
MatSelectModule,
MatSidenavModule,
MatTableModule,
......
......@@ -38,7 +38,7 @@
</mat-action-list>
</mat-drawer>
<mat-toolbar color="primary" class="sticky inset-x-0 top-0 w-full bg-black h-20 text-white shadow-lg flex">
<mat-toolbar color="primary" class="sticky z-50 inset-x-0 top-0 shadow-lg flex">
<div class="example-sidenav-content">
<button type="button" mat-icon-button (click)="drawer.toggle()">
<mat-icon>menu</mat-icon>
......
<mat-toolbar color="primary" class="flex flex-row">
<button mat-icon-button routerLink="/auth/countries" aria-label="Cancel editing">
<mat-icon>arrow_back</mat-icon>
</button>
<span>Country detail</span>
<span class="flex-1"></span>
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Example icon-button with a menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Delete</button>
</mat-menu>
</mat-toolbar>
<div *ngIf="state === requestState.ERROR">
Error occured: {{ errorMessage }}
</div>
<div *ngIf="country && state === requestState.SUCCESS" class="flex flex-col max-w-xl m-auto p-4">
<div *ngIf="country && state === requestState.SUCCESS" class="main-container">
<div *ngIf="!editing">
<h1>{{ country.name}}</h1>
......@@ -77,7 +96,7 @@
</button>
<button color="accent" *ngIf="editing" mat-fab (click)="setEditing(false)" class="!fixed right-8 bottom-20"
aria-label="Example icon button with a delete icon">
aria-label="Edit country details">
<mat-icon>done</mat-icon>
</button>
</div>
<div *ngIf="state === requestState.ERROR">
Error occured: {{ errorMessage }}
</div>
<div *ngIf="state === requestState.SUCCESS" class="max-w-xl m-auto">
<div *ngIf="state === requestState.SUCCESS" class="main-container">
<mat-action-list>
<mat-list-item *ngFor="let country of countries" [routerLink]="['/auth/country/', country.id]">
<p matLine class="!text-xs !tracking-widest capitalize"> {{country.code}} </p>
<p matLine class="!text-xs !tracking-widest uppercase"> {{country.code}} </p>
<p matLine> {{country.name}} </p>
<mat-divider></mat-divider>
</mat-list-item>
......
<div *ngIf="state === requestState.ERROR">
Error occured: {{ errorMessage }}
</div>
<div *ngIf="state === requestState.SUCCESS">
<table mat-table [dataSource]="missions" class="w-full">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let mission">
{{ mission.name }}
</td>
</ng-container>