Skip to content

Commit

Permalink
load current org/user + stubs for event visualization + org selector #…
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Dec 15, 2022
1 parent e8f4fbf commit 3e47c6f
Show file tree
Hide file tree
Showing 21 changed files with 191 additions and 14 deletions.
4 changes: 4 additions & 0 deletions frontend-admin/src/app/app-routing.module.ts
Expand Up @@ -3,6 +3,10 @@ import {PreloadAllModules, RouterModule, Routes} from '@angular/router';
import { MissingOrgComponent } from './missing-org/missing-org.component';

const routes: Routes = [
{
path: 'organization/:organizationId/event/:eventId',
loadChildren: () => import('./event/event.module').then(m => m.EventModule)
},
{
path: 'organization/:organizationId',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
Expand Down
9 changes: 8 additions & 1 deletion frontend-admin/src/app/app.component.html
Expand Up @@ -2,8 +2,15 @@
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
<span class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6">
<img src="assets/alfio-logo.svg" height="48">
<span>
<button class="btn btn-light" (click)="openOrgSelector()">
<svg-icon key="organization" size="lg"></svg-icon>
{{(currentOrganization$ | async)?.name}}
<svg-icon key="arrowdropdown"></svg-icon>
</button>
</span>
</span>
<span style="color: white;">org id {{organizationId$ | async}}</span>
<span style="color: white;">user: {{(currentUser$ | async)?.username}}</span>
<!-- <input class="form-control form-control-dark w-100 rounded-0 border-0" type="text" placeholder="Search"
aria-label="Search">
<div class="navbar-nav">
Expand Down
23 changes: 22 additions & 1 deletion frontend-admin/src/app/app.component.ts
@@ -1,9 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { ActivationEnd, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateService } from '@ngx-translate/core';
import { filter, map, Observable, tap } from 'rxjs';
import { filter, map, Observable, of, tap, zip } from 'rxjs';
import { Organization } from './model/organization';
import { UserInfo } from './model/user';
import { OrgSelectorComponent } from './org-selector/org-selector.component';
import { OrganizationService } from './shared/organization.service';
import { UserService } from './shared/user.service';

@Component({
selector: 'app-root',
Expand All @@ -14,11 +18,15 @@ export class AppComponent implements OnInit {

public organizations$?: Observable<Organization[]>;
public organizationId$?: Observable<string | null>;
public currentUser$?: Observable<UserInfo>;
public currentOrganization$?: Observable<Organization | undefined>;

constructor(
private readonly translateService: TranslateService,
private readonly organizationService: OrganizationService,
private readonly userService: UserService,
private readonly router: Router,
private readonly modalService: NgbModal,
) {
}

Expand All @@ -30,5 +38,18 @@ export class AppComponent implements OnInit {
const ae = a as ActivationEnd;
return ae.snapshot.paramMap.get('organizationId');
}));
this.currentUser$ = this.userService.getCurrent();
this.currentOrganization$ = zip(this.organizationId$, this.organizations$)
.pipe(map(([id, orgs]) => orgs.find(o => id !== null && o.id === Number.parseInt(id))));
}

public openOrgSelector(): void {
const modalRef = this.modalService.open(OrgSelectorComponent);
const selector: OrgSelectorComponent = modalRef.componentInstance
selector.organizations$ = this.organizations$;
selector.organizationId$ = this.organizationId$;
modalRef.result.then((res: Organization) => {
this.router.navigate(['/organization', res.id]).then(r => {if (r) {this.currentOrganization$ = of(res)}});
});
}
}
2 changes: 2 additions & 0 deletions frontend-admin/src/app/app.module.ts
Expand Up @@ -15,6 +15,7 @@ import {MissingOrgComponent} from './missing-org/missing-org.component';
import { ICON_CONFIG } from './shared/icons';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {CustomLoader} from './shared/i18n.service';
import { OrgSelectorComponent } from './org-selector/org-selector.component';

export function RedirectToLoginIfNeeded(userService: UserService, router: Router): () => Promise<boolean> {
return async () => {
Expand All @@ -35,6 +36,7 @@ export function HttpLoaderFactory(http: HttpClient) {
declarations: [
AppComponent,
MissingOrgComponent,
OrgSelectorComponent,
],
imports: [
BrowserModule,
Expand Down
8 changes: 4 additions & 4 deletions frontend-admin/src/app/dashboard/dashboard.component.html
@@ -1,19 +1,19 @@
<h1 translate="admin.dashboard.my-events"></h1>
<app-filter-button text="Active" [checked]="activeFilter" (toggleFilter)="toggleActiveFilter($event)"></app-filter-button>
<app-filter-button text="Inactive" [checked]="inactiveFilter" (toggleFilter)="toggleInactiveFilter($event)"></app-filter-button>
<app-filter-button text="Past" [checked]="inactiveFilter" (toggleFilter)="toggleInactiveFilter($event)"></app-filter-button>
<ng-container *ngIf="activeFilter">
<h2>Active</h2>
<ul>
<li *ngFor="let ev of activeEvents$ | async">
{{ev.displayName}} {{ev.formattedBegin}} {{ev.formattedEnd}}
<a [routerLink]="['./event', ev.id]">{{ev.displayName}} {{ev.formattedBegin}} {{ev.formattedEnd}}</a>
</li>
</ul>
</ng-container>
<ng-container *ngIf="inactiveFilter">
<h2>Expired</h2>
<h2>Past</h2>
<ul>
<li *ngFor="let ev of expiredEvents$ | async">
{{ev.displayName}} {{ev.formattedBegin}} {{ev.formattedEnd}}
<a [routerLink]="['./event', ev.id]">{{ev.displayName}} {{ev.formattedBegin}} {{ev.formattedEnd}}</a>
</li>
</ul>
</ng-container>
20 changes: 14 additions & 6 deletions frontend-admin/src/app/dashboard/dashboard.component.ts
@@ -1,6 +1,6 @@
import {Component, OnInit} from "@angular/core";
import {ActivatedRoute} from "@angular/router";
import { Observable, of } from "rxjs";
import { map, mergeMap, Observable, of } from "rxjs";
import { EventInfo } from "../model/event";
import { EventService } from "../shared/event.service";

Expand All @@ -9,24 +9,32 @@ import { EventService } from "../shared/event.service";
})
export class DashboardComponent implements OnInit {

public readonly organizationId: string;
public organizationId$: Observable<string | null> = of();
public activeEvents$: Observable<EventInfo[]> = of();
public expiredEvents$: Observable<EventInfo[]> = of();
public activeFilter: boolean = true;
public inactiveFilter: boolean = false;

constructor(route: ActivatedRoute, private readonly eventService: EventService) {
this.organizationId = route.snapshot.paramMap.get('organizationId') as string;
this.organizationId$ = route.paramMap.pipe(map((pm) => pm.get('organizationId')))
}

public ngOnInit(): void {
this.activeEvents$ = this.eventService.getActiveEvents(this.organizationId); // default
this.activeEvents$ = this.loadActiveEvents(); // default
}

private loadActiveEvents() {
return this.organizationId$.pipe(mergeMap(orgId => orgId != null ? this.eventService.getActiveEvents(orgId) : []));
}

private loadInactiveEvents() {
return this.organizationId$.pipe(mergeMap(orgId => orgId != null ? this.eventService.getExpiredEvents(orgId) : []));
}

public toggleActiveFilter(toggle: boolean): void {
this.activeFilter = toggle;
if (toggle) {
this.activeEvents$ = this.eventService.getActiveEvents(this.organizationId);
this.activeEvents$ = this.loadActiveEvents();
} else {
this.activeEvents$ = of();
}
Expand All @@ -35,7 +43,7 @@ export class DashboardComponent implements OnInit {
public toggleInactiveFilter(toggle: boolean): void {
this.inactiveFilter = toggle;
if (toggle) {
this.expiredEvents$ = this.eventService.getExpiredEvents(this.organizationId);
this.expiredEvents$ = this.loadInactiveEvents();
} else {
this.expiredEvents$ = of();
}
Expand Down
4 changes: 2 additions & 2 deletions frontend-admin/src/app/dashboard/dashboard.module.ts
Expand Up @@ -13,7 +13,7 @@ import {ICON_CONFIG} from "../shared/icons";
import {SubscriptionsComponent} from './subscriptions/subscriptions.component';
import {OrganizationInfoComponent} from './organization-info/organization-info.component';
import {GroupsComponent} from './groups/groups.component';
import {FilterButtonComponent} from "../shared/filter-button/filter-button.component";
import {FilterButtonComponent} from '../shared/filter-button/filter-button.component';

@NgModule({
imports: [
Expand All @@ -23,7 +23,7 @@ import {FilterButtonComponent} from "../shared/filter-button/filter-button.compo
RouterModule.forChild([
{ path: '', component: DashboardComponent},
{ path: '', component: DashboardMenuComponent, outlet: 'sidebar-content'},
{ path: 'configuration', component: OrganizationConfigurationComponent },
{ path: 'configuration', component: OrganizationConfigurationComponent},
{ path: 'subscriptions', component: SubscriptionsComponent },
{ path: 'organization-info', component: OrganizationInfoComponent },
{ path: 'groups', component: GroupsComponent },
Expand Down
@@ -0,0 +1 @@
<p>event-dashboard works!</p>
Empty file.
@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-event-dashboard',
templateUrl: './event-dashboard.component.html',
styleUrls: ['./event-dashboard.component.scss']
})
export class EventDashboardComponent implements OnInit {

constructor() { }

ngOnInit(): void {
}

}
@@ -0,0 +1 @@
<p>event-menu works!</p>
Empty file.
15 changes: 15 additions & 0 deletions frontend-admin/src/app/event/event-menu/event-menu.component.ts
@@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-event-menu',
templateUrl: './event-menu.component.html',
styleUrls: ['./event-menu.component.scss']
})
export class EventMenuComponent implements OnInit {

constructor() { }

ngOnInit(): void {
}

}
32 changes: 32 additions & 0 deletions frontend-admin/src/app/event/event.module.ts
@@ -0,0 +1,32 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { provideSvgIconsConfig, SvgIconComponent } from '@ngneat/svg-icon';
import { RouterModule } from '@angular/router';
import { OrganizationService } from '../shared/organization.service';
import { EventService } from '../shared/event.service';
import { ICON_CONFIG } from '../shared/icons';
import { EventDashboardComponent } from './event-dashboard/event-dashboard.component';
import { EventMenuComponent } from './event-menu/event-menu.component';

@NgModule({
declarations: [
EventMenuComponent,
EventDashboardComponent
],
imports: [
TranslateModule.forChild(),
CommonModule,
SvgIconComponent,
RouterModule.forChild([
{path: '', component: EventDashboardComponent},
{path: '', component: EventMenuComponent, outlet: 'sidebar-content'}
])
],
providers: [
OrganizationService,
EventService,
provideSvgIconsConfig(ICON_CONFIG)
]
})
export class EventModule { }
25 changes: 25 additions & 0 deletions frontend-admin/src/app/model/user.ts
@@ -0,0 +1,25 @@
export interface UserInfo {
id: number;
type: UserType;
username: string;
description: string;
firstName: string;
lastName: string;
emailAddress: string;
role: Role;
}

export enum UserType {
INTERNAL = 'INTERNAL',
DEMO = 'DEMO',
API_KEY = 'API_KEY'
}

export enum Role {
ADMIN = 'ADMIN',
OWNER = 'OWNER',
SUPERVISOR = 'SUPERVISOR',
OPERATOR = 'OPERATOR',
SPONSOR = 'SPONSOR',
API_CONSUMER = 'API_CONSUMER'
}
14 changes: 14 additions & 0 deletions frontend-admin/src/app/org-selector/org-selector.component.html
@@ -0,0 +1,14 @@
<div class="modal-header">
<h4 class="modal-title">Select organization</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="activeModal.dismiss('Cross click')"></button>
</div>
<div class="modal-body">
<ul>
<li *ngFor="let org of organizations$ | async">
<button class="btn btn-link" (click)="selectOrganization(org)">{{org.name}}</button>
</li>
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
Empty file.
25 changes: 25 additions & 0 deletions frontend-admin/src/app/org-selector/org-selector.component.ts
@@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Observable } from 'rxjs';
import { Organization } from '../model/organization';

@Component({
selector: 'app-org-selector',
templateUrl: './org-selector.component.html',
styleUrls: ['./org-selector.component.scss']
})
export class OrgSelectorComponent implements OnInit {

public organizations$?: Observable<Organization[]>;
public organizationId$?: Observable<string | null>;

constructor(public activeModal: NgbActiveModal) { }

ngOnInit(): void {
}

public selectOrganization(org: Organization): void {
this.activeModal.close(org);
}

}
2 changes: 2 additions & 0 deletions frontend-admin/src/app/shared/icons.ts
@@ -1,4 +1,5 @@
import { addIcon } from '../svg/add';
import { arrowdropdownIcon } from '../svg/arrowdropdown';
import {checkIcon} from '../svg/check';
import {homeIcon} from '../svg/home';
import { organizationIcon } from '../svg/organization';
Expand All @@ -11,6 +12,7 @@ const ICONS = [
addIcon,
organizationIcon,
settingsIcon,
arrowdropdownIcon,
];
export const ICON_CONFIG = {
sizes: {
Expand Down
4 changes: 4 additions & 0 deletions frontend-admin/src/app/shared/user.service.ts
@@ -1,6 +1,7 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {catchError, map, Observable, of} from "rxjs";
import {UserInfo} from "../model/user";

@Injectable()
export class UserService {
Expand All @@ -22,4 +23,7 @@ export class UserService {
)
}

getCurrent(): Observable<UserInfo> {
return this.httpClient.get<UserInfo>('/admin/api/users/current');
}
}
1 change: 1 addition & 0 deletions frontend-admin/src/assets/svg/arrowdropdown.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3e47c6f

Please sign in to comment.