Several fixes
- added organizations - added industries - added logo in 2 colors for light and dark theme - improved authorization to allow multi tenancy
This commit is contained in:
156
src/app/pages/organizations/organizations.html
Normal file
156
src/app/pages/organizations/organizations.html
Normal file
@@ -0,0 +1,156 @@
|
||||
<div class="flex">
|
||||
<div class="flex-1 text-left">
|
||||
<div class="mb-5">
|
||||
<h1>Organisationen</h1>
|
||||
<p class="my-4 text-lg text-gray-500">Übersicht über Organisationen</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 text-right">
|
||||
|
||||
@if (['dev', 'admin', 'can-create-organizations'] | isRoleAllowed: 'any') {
|
||||
<p-button (click)="showDialog()" label="Neue Organisation" class="p-button-outlined ml-4">
|
||||
<span class="flex items-center gap-2">
|
||||
<span class="material-icons !text-base">add</span>
|
||||
</span>
|
||||
</p-button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p-dialog header="Neue Organisation"
|
||||
[modal]="true"
|
||||
[(visible)]="addNewOrganizationDialogVisible" [style]="{ width: '50rem' }">
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label for="organization_name" class="mb-2 font-semibold">Organisations Name</label>
|
||||
<input [(ngModel)]="newOrganizationName"
|
||||
[required]="true"
|
||||
pInputText
|
||||
id="organization_name"
|
||||
class="w-full"
|
||||
autocomplete="on" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label for="organization_owner" class="mb-2 font-semibold">Organisations Owner</label>
|
||||
<input [(ngModel)]="newOrganizationOwner"
|
||||
[required]="true"
|
||||
pInputText
|
||||
id="organization_owner"
|
||||
class="w-full"
|
||||
autocomplete="on" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-1 md:col-span-2 flex flex-col">
|
||||
<label for="organization_industry" class="mb-2 font-semibold">Organisations-Branche</label>
|
||||
<p-select [options]="organizationIndustryOptionsForNewOrganization"
|
||||
[(ngModel)]="newOrganizationIndustry"
|
||||
[required]="true"
|
||||
appendTo="body"
|
||||
optionLabel="name"
|
||||
id="organization_industry" class="w-full"></p-select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<p-button label="Cancel" severity="secondary" (click)="addNewOrganizationDialogVisible = false" />
|
||||
<p-button label="Save" (click)="createNewOrganization()" />
|
||||
</div>
|
||||
</p-dialog>
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="grid grid-cols-12 gap-8 mb-8">
|
||||
<div class="col-span-12 ">
|
||||
|
||||
<div class="card">
|
||||
<p-table #dt1
|
||||
[value]="organizations"
|
||||
dataKey="id"
|
||||
[rows]="10"
|
||||
[rowsPerPageOptions]="[10, 25, 50, 100]"
|
||||
[loading]="loading"
|
||||
[paginator]="true"
|
||||
[globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
|
||||
>
|
||||
<ng-template #caption>
|
||||
<div class="flex">
|
||||
<p-button label="Clear" [outlined]="true" icon="pi pi-filter-slash" (click)="clear(dt1)" />
|
||||
<p-iconfield iconPosition="left" class="ml-auto">
|
||||
<p-inputicon>
|
||||
<i class="pi pi-search"></i>
|
||||
</p-inputicon>
|
||||
<input pInputText type="text" [(ngModel)]="searchValue" (input)="filterGlobal(dt1, $event.target)" placeholder="Suche nach..." />
|
||||
</p-iconfield>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #header>
|
||||
<tr>
|
||||
<th pSortableColumn="name" style="width:50%;">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<!-- Linker Bereich: Text + SortIcon -->
|
||||
<div class="flex items-center gap-1">
|
||||
<span>Name</span>
|
||||
<p-sortIcon field="name"></p-sortIcon>
|
||||
</div>
|
||||
|
||||
<!-- Rechter Bereich: Filter -->
|
||||
<p-columnFilter type="text" field="name" display="menu"></p-columnFilter>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th pSortableColumn="industry.name" style="width:35%;">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<!-- Linker Bereich: Text + SortIcon -->
|
||||
<div class="flex items-center gap-1">
|
||||
<span>Branche</span>
|
||||
<p-sortIcon field="industry.name"></p-sortIcon>
|
||||
</div>
|
||||
|
||||
<!-- Rechter Bereich: Filter -->
|
||||
<p-columnFilter type="text" field="industry.name" display="menu"></p-columnFilter>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th pSortableColumn="owner" style="width:15%;">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<!-- Linker Bereich: Text + SortIcon -->
|
||||
<div class="flex items-center gap-1">
|
||||
<span>Owner</span>
|
||||
<p-sortIcon field="owner"></p-sortIcon>
|
||||
</div>
|
||||
|
||||
<!-- Rechter Bereich: Filter -->
|
||||
<p-columnFilter type="text" field="owner" display="menu"></p-columnFilter>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template #body let-organization>
|
||||
<tr>
|
||||
<td>
|
||||
{{ organization?.name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ organization?.industry?.name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ organization?.owner }}
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template #emptymessage>
|
||||
<tr>
|
||||
<td colspan="7">Keine Einträge gefunden.</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
0
src/app/pages/organizations/organizations.scss
Normal file
0
src/app/pages/organizations/organizations.scss
Normal file
23
src/app/pages/organizations/organizations.spec.ts
Normal file
23
src/app/pages/organizations/organizations.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Organizations } from './organizations';
|
||||
|
||||
describe('Organizations', () => {
|
||||
let component: Organizations;
|
||||
let fixture: ComponentFixture<Organizations>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [Organizations]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(Organizations);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
120
src/app/pages/organizations/organizations.ts
Normal file
120
src/app/pages/organizations/organizations.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Button } from 'primeng/button';
|
||||
import { Dialog } from 'primeng/dialog';
|
||||
import { IconField } from 'primeng/iconfield';
|
||||
import { InputIcon } from 'primeng/inputicon';
|
||||
import { InputText } from 'primeng/inputtext';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { Select } from 'primeng/select';
|
||||
import { ProjectStatus } from '@/pages/service/project-status.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { Table, TableModule } from 'primeng/table';
|
||||
import { Organization, OrganizationService } from '@/pages/service/organization.service';
|
||||
import { Industry, IndustryService } from '@/pages/service/industry.service';
|
||||
import { IsRoleAllowedPipe } from '@/pipes/is-role-allowed-pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-organizations',
|
||||
imports: [Button, Dialog, IconField, InputIcon, InputText, ReactiveFormsModule, Select, FormsModule, TableModule, IsRoleAllowedPipe],
|
||||
templateUrl: './organizations.html',
|
||||
styleUrl: './organizations.scss',
|
||||
providers: [MessageService]
|
||||
})
|
||||
export class Organizations {
|
||||
organizations: Organization[] = [];
|
||||
organizationIndustries: Industry[] = [];
|
||||
projectStatuses: ProjectStatus[] = [];
|
||||
addNewOrganizationDialogVisible = false;
|
||||
|
||||
statuses: any;
|
||||
measures: any;
|
||||
|
||||
// for new organization dialog
|
||||
organizationIndustryOptionsForNewOrganization: any = [];
|
||||
newOrganizationName: string | undefined;
|
||||
newOrganizationOwner: string | undefined;
|
||||
newOrganizationIndustry: Industry | undefined;
|
||||
protected loading: boolean = true;
|
||||
protected searchValue: string | undefined;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private organizationService: OrganizationService,
|
||||
private industryService: IndustryService,
|
||||
private messageService: MessageService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.statuses = [{ label: 'Alle', value: 'all' }, ...this.projectStatuses];
|
||||
|
||||
this.measures = [{ label: 'Alle', value: 'all' }, ...this.organizationIndustries];
|
||||
|
||||
this.organizationService.getOrganizations().subscribe((organizations) => {
|
||||
console.debug('Organizations', organizations);
|
||||
this.organizations = organizations;
|
||||
this.loading = false;
|
||||
});
|
||||
|
||||
this.industryService.getIndustries().subscribe((industries) => {
|
||||
console.debug('Industries', industries);
|
||||
this.organizationIndustries = industries;
|
||||
});
|
||||
}
|
||||
|
||||
navigateToDetails(id: string | undefined) {
|
||||
this.router.navigate(['/projects', id]);
|
||||
}
|
||||
|
||||
clear(table: Table) {
|
||||
table.clear();
|
||||
this.searchValue = '';
|
||||
}
|
||||
|
||||
filterGlobal(table: Table, eventTarget: EventTarget | null) {
|
||||
table.filterGlobal((eventTarget as HTMLInputElement).value, 'contains');
|
||||
}
|
||||
|
||||
showDialog() {
|
||||
this.industryService.getIndustries().subscribe((industries) => {
|
||||
this.organizationIndustryOptionsForNewOrganization = [];
|
||||
this.organizationIndustries.forEach((projectType) => {
|
||||
this.organizationIndustryOptionsForNewOrganization.push({
|
||||
id: projectType.id,
|
||||
name: projectType.name
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.organizationIndustryOptionsForNewOrganization = this.organizationIndustries;
|
||||
this.addNewOrganizationDialogVisible = true;
|
||||
}
|
||||
|
||||
createNewOrganization() {
|
||||
let newOrganization = this.organizationService.getOrganizationInstance(this.newOrganizationName, this.newOrganizationIndustry, this.newOrganizationOwner);
|
||||
|
||||
this.organizationService.create(newOrganization).subscribe({
|
||||
next: (arg) => {
|
||||
this.messageService.add({
|
||||
severity: 'success',
|
||||
summary: 'Erfolgreich',
|
||||
detail: 'Organisation erfolgreich angelegt',
|
||||
life: 3000
|
||||
});
|
||||
|
||||
this.organizations = [...this.organizations, newOrganization];
|
||||
|
||||
this.addNewOrganizationDialogVisible = false;
|
||||
},
|
||||
error: (err) => {
|
||||
this.messageService.add({
|
||||
severity: 'danger',
|
||||
summary: 'Fehler',
|
||||
detail: 'Beim Anlegen der Organisation ist ein Fehler aufgetreten.',
|
||||
life: 3000
|
||||
});
|
||||
console.log('Error while creating organization -- Error: ' + err + ' -- Organization: ', newOrganization);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user