import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component.ts';
import { UserViewComponent } from '../user/user-view.component.ts';
import { DriveViewComponent } from '../drive/drive-view.component.ts';
import { ErrorComponent } from './error.component.ts';


const routes: Routes = [
    {
        path: '',
        redirectTo: '/home',
        pathMatch: 'full'
    },
    {
        path: 'error',
        component: ErrorComponent
    },
    { path: 'home', component: HomeComponent },
    {
        path: 'user',
        component: UserViewComponent
    },
    {
        path: 'drive',
        component: DriveViewComponent
    }
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule
{
}
import { Component } from '@angular/core';

@Component({
    moduleId: __moduleName,
    selector: 'app',
    templateUrl: './../../html/app.html'
})
export class AppComponent
{
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { UtilityModule } from '../utility/utility.module.ts';
import { AppRoutingModule } from '../core/app-routing.module.ts';
import { NavbarModule } from '../navbar/navbar.module.ts';
import { UserModule } from '../user/user.module.ts';
import { DriveModule } from '../drive/drive.module.ts';


import { AppComponent } from './app.component.ts';
import { HomeComponent } from './home.component.ts';
import { ErrorComponent } from './error.component.ts';

@NgModule({
    imports: [
        BrowserModule,
        UtilityModule,
        AppRoutingModule,
        NavbarModule,
        UserModule,
        DriveModule
    ],
    declarations: [
        AppComponent,
        HomeComponent,
        ErrorComponent
    ],
    bootstrap: [AppComponent]
})
export class AppModule
{
}
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';


@Component({
    moduleId: __moduleName,
    selector: 'error',
    templateUrl: './../../html/error.html'
})
export class ErrorComponent implements OnInit
{
    constructor(private route: ActivatedRoute) {}

    public ngOnInit()
    {
        this.route.queryParams.subscribe(
            (params: Params) =>
            {
                this.message = params['message'];
                this.errorType = params['errorType'] || AppErrorType.Undefined;
            });
    }

    public errorType: AppErrorType;
    public message: Observable<string>;
}

export enum AppErrorType
{
    Undefined = 0,
    Unauthenticated = 1,
    Unauthorized = 2,
    ServerError = 3,
    NotFound = 4,
    LoginFailed = 5
}
import { Component } from '@angular/core';

@Component({
    moduleId: __moduleName,
    selector: 'app',
    templateUrl: './../../html/home.html'
})
export class HomeComponent
{
}
import { Component, Output, EventEmitter } from '@angular/core';
import { trigger, state, style, animate, transition, AnimationEvent, keyframes } from '@angular/animations';

import { Drive } from './drive.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';
import { IGridItemDetails } from '../grid/grid-item-details.ts';
import { GridFilterConstraint } from '../grid/grid-filter-constraint.ts';
import { FilterEvaluationMethod } from '../grid/grid-filter-constraint.ts';


@Component({
    moduleId: __moduleName,
    selector: 'item-details',
    templateUrl: './../../html/drive-details.html',
    animations: [
        trigger('detailsFadeInOut',
        [
            state('void', style({ opacity: 0 })),
            state('in', style({ opacity: 1 })),
            state('out', style({ opacity: 0 })),
            transition('*=>fadeIn',
                animate('300ms ease-in',
                    keyframes([
                        style({ opacity: 0, offset: 0 }),
                        style({ opacity: 1, offset: 1.0 })
                    ]))),
            transition('*=>fadeOut',
                animate('300ms ease-in-out',
                    keyframes([
                        style({ opacity: 1, offset: 0 }),
                        style({ opacity: 0, offset: 1.0 })
                    ])))
        ])
    ]
})

export class DriveDetailsComponent implements IGridItemDetails<Drive>
{
    public fadeIn(): void { this._fadeInOutStatus = 'fadeIn' }

    public fadeOut(): void { this._fadeInOutStatus = 'fadeOut' }

    public setFadeStatus(event: AnimationEvent): void
    {
        if (event.fromState === 'void' || this._fadeInOutStatus === 'fadeIn') this._fadeInOutStatus = 'in';
        else if (this._fadeInOutStatus === 'fadeOut') this._fadeInOutStatus = 'out';
    }

    public get fadeInOutStatus(): string { return this._fadeInOutStatus; }

    private _fadeInOutStatus: string;

    public item: Drive;
    public descriptionInput: boolean;
    public organizationInput: boolean;
    public ownerInput: boolean;

    public description: string;
    public organization: string;
    public owner: string;

    public saveDescription(): void
    {
        // todo: apply changes to entity
        this.descriptionInput = false;
    }

    public saveOwner(): void
    {
        // todo: apply changes to entity
        this.ownerInput = false;
    }

    public saveOrganization(): void
    {
        // todo: apply changes to entity
        this.organizationInput = false;
    }

    @Output()
    public onOpenModalEmitter = new EventEmitter<boolean>();

    public onMembersModalButton()
    {
        this.onOpenModalEmitter.emit(true);
    }
}
import { Component, EventEmitter, Output } from '@angular/core';
import { trigger, state, style, animate, transition } from '@angular/animations';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';

import { DriveService } from './drive.service.ts';
import { Drive } from './drive.ts';
import { IGridBody } from '../grid/grid-body.ts';

@Component({
    moduleId: __moduleName,
    selector: 'tbody [grid-body]',
    templateUrl: './../../html/drive-grid-body.html',
    animations: [
        trigger('detailsToggled',
        [
            state('up', style({ transform: 'scaleY(1)' })),
            state('down', style({ transform: 'scaleY(-1)' })),
            transition('up => down', animate('300ms')),
            transition('down => up', animate('300ms'))
        ])
    ]
})
// todo: pull base class to DRY
export class DriveGridBodyComponent implements IGridBody<Drive>
{
    public items: Drive[];
    public selectedItemId: string;

    public toggleIconState(index: number): string
    {
        return (this.selectedItemId === this.items[index].id) ? 'down' : 'up';
    }

    @Output()
    public onDetailsButtonEmitter = new EventEmitter<number>();

    public onDetailsButton(index: number)
    {
        if (index < 0) throw new RangeError('Index must not be smaller than zero.');
        if (index > this.items.length - 1)
            throw new RangeError(`No such grid index. Given: ${index}. Number of rows: ${this.items.length}.`);

        if (this.selectedItemId === this.items[index].id) this.selectedItemId = null;
        else this.selectedItemId = this.items[index].id;
        this.onDetailsButtonEmitter.emit(index);
    }
}
import { Component, Injectable, Output, EventEmitter } from '@angular/core';
import { IGridHeader } from '../grid/grid-header.ts';
import { ListOrder } from '../utility/list-order.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';

@Component({
    moduleId: __moduleName,
    selector: 'thead [grid-header]',
    templateUrl: './../../html/drive-grid-header.html'
})
@Injectable()
export class DriveGridHeaderComponent implements IGridHeader
{
    public readonly defaultColumn = 'Date Created';
    public activeColumn: string;
    public listOrder: ListOrder;
    public columns: { [columnName: string]: string; };

    constructor()
    {
        this.activeColumn = this.defaultColumn;
        this.listOrder = ListOrder.Descending;
        this.columns = {
            'ID': 'Id',
            'Name': 'Name',
            'Date Created': 'DateCreated',
            'Expiration Date': 'ExpirationDate',
            'Members': 'Memberships.Count',
            'Invitations': 'TeamdriveInvitations.Count',
            '': ''
        };
    }

    @Output()
    public onColumnHeaderToggledEmitter = new EventEmitter<string>();

    // todo: pull baseclass for this method
    public onColumnHeaderToggled(columnName: string): void { this.onColumnHeaderToggledEmitter.emit(columnName); }
}
import { Component, Inject, ViewChild } from '@angular/core';

import { Drive } from './drive.ts';
import { Grid } from '../grid/grid.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';
import { GridFilterConstraint, FilterEvaluationMethod } from '../grid/grid-filter-constraint.ts';

import { DriveService } from './drive.service.ts';
import { GridPagerService } from '../grid/grid-pager.service.ts';

import { DriveGridBodyComponent } from './drive-grid-body.component.ts';
import { DriveGridHeaderComponent } from './drive-grid-header.component.ts';
import { DriveDetailsComponent } from './drive-details.component.ts';
import { DriveFilterModalComponent } from './drive-filter-modal.component.ts';


@Component({
    moduleId: __moduleName,
    selector: '[drive-grid]',
    templateUrl: './../../html/grid.html'
})
export class DriveGridComponent extends Grid<Drive>
{
    @ViewChild(DriveGridBodyComponent)
    public body: DriveGridBodyComponent;

    @ViewChild(DriveGridHeaderComponent)
    public header: DriveGridHeaderComponent;

}
import { Component, ViewChild, Inject } from '@angular/core';

import { User } from '../user/user.ts';
import { GridView } from '../grid/grid-view.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';

import { UserGridComponent } from '../user/user-grid.component.ts';
import { UserDetailsComponent } from '../user/user-details.component.ts';
import { UserFilterModalComponent } from '../user/user-filter-modal.component.ts';

import { GridPagerService } from '../grid/grid-pager.service.ts';
import { UserService } from '../user/user.service.ts';


@Component({
    moduleId: __moduleName,
    selector: '[drive-members-modal]',
    templateUrl: './../../html/drive-members-modal.html'
})
export class DriveMembersModalComponent extends GridView<User>
{
    @ViewChild(UserGridComponent)
    public grid: UserGridComponent;

    public itemDetails: UserDetailsComponent;
    public filterModal: UserFilterModalComponent;

    public constructor(@Inject(GridPagerService) pagerService: GridPagerService,
                       @Inject(UserService) repositoryService: UserService)
    {
        super(pagerService, repositoryService);
    }

    protected loadItemDetails(item: User): void
    {
        // todo: implement        
    }
}
import { Component, Inject, ViewChild, AfterContentInit, ngAfterViewInit } from '@angular/core';
import { trigger, state, style, animate, transition, keyframes } from '@angular/animations';

import { Drive } from './drive.ts';
import { GridView } from '../grid/grid-view.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';
import { IGridFilterConstraint, GridFilterConstraint, FilterEvaluationMethod } from '../grid/grid-filter-constraint.ts';

import { DriveGridComponent } from './drive-grid.component.ts';
import { DriveDetailsComponent } from './drive-details.component.ts';
import { DriveFilterModalComponent } from './drive-filter-modal.component.ts';
import { DriveMembersModalComponent } from './drive-members-modal.component.ts';

import { GridPagerService } from '../grid/grid-pager.service.ts';
import { DriveService } from './drive.service.ts';

@Component({
    moduleId: __moduleName,
    selector: 'drive-view',
    templateUrl: './../../html/drive-view.html',
    animations: [
        trigger('detailsSlideUpDown',
        [
            state('void', style({ maxHeight: '0', overflow: 'hidden' })),
            state('in', style({ maxHeight: '*', overflow: 'hidden' })),
            state('out', style({ maxHeight: '0', overflow: 'hidden' })),
            transition('*=>fadeIn',
                animate('300ms ease-in',
                    keyframes([
                        style({ maxHeight: '0', overflow: 'hidden', offset: 0 }),
                        style({ maxHeight: '500px', overflow: 'hidden', offset: 1 })
                    ]))),
            transition('*=>fadeOut',
                animate('300ms ease-in-out',
                    keyframes([
                        style({ maxHeight: '500px', overflow: 'hidden', offset: 0 }),
                        style({ maxHeight: '0', overflow: 'hidden', offset: 1 })
                    ])))
        ])
    ]
})
export class DriveViewComponent extends GridView<Drive> implements AfterViewInit
{
    @ViewChild(DriveGridComponent)
    public grid: DriveGridComponent;

    @ViewChild(DriveDetailsComponent)
    public itemDetails: DriveDetailsComponent;

    @ViewChild(DriveMembersModalComponent)
    public driveMembersModal: DriveMembersModalComponent;
    
    public constructor(@Inject(GridPagerService) pagerService: GridPagerService,
                       @Inject(DriveService) repositoryService: DriveService)
    {
        super(pagerService, repositoryService);
    }

    public ngAfterContentInit(): void {
        const params = GridViewParameters.getCurrentViewParams(this);
        this.loadItems(params);
    }
    
     public ngAfterViewInit(): void {
        console.log(this.driveMembersModal);
     }

    protected loadItemDetails(item: Drive): void { this.itemDetails.owner = 'foo@bar.com' }

    public onDriveMembersButton(): void {
        const filter = new GridFilterConstraint();
        filter.propertyName = 'Id';
        filter.evaluationMethod = FilterEvaluationMethod.EQUAL;
        filter.propertyValue = this.grid.body.selectedItemId;

        const params = new GridViewParameters();
        params.activeColumn = 'Email';
        params.filterConstraints.push(filter);
        this.driveMembersModal.loadItems(params);
    }
}
import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { UtilityModule } from '../utility/utility.module.ts';
import { GridModule } from '../grid/grid.module.ts';

import { DriveService } from './drive.service.ts';
import { GridPagerService } from '../grid/grid-pager.service.ts';

import { DriveViewComponent } from './drive-view.component.ts';
import { DriveGridComponent } from './drive-grid.component.ts';
import { DriveMembersModalComponent } from './drive-members-modal.component.ts';
import { DriveGridHeaderComponent } from './drive-grid-header.component.ts';
import { DriveGridBodyComponent } from './drive-grid-body.component.ts';
import { DriveDetailsComponent } from './drive-details.component.ts';
import { UserModule } from '../user/user.module.ts';

// required for tooltips
import './../../node_modules/bootstrap/dist/js/bootstrap.min.js';
import * as $ from 'jquery';

@NgModule({
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        FormsModule,
        HttpModule,
        UtilityModule,
        GridModule,
        UserModule
    ],
    declarations: [
        DriveViewComponent,
        DriveGridComponent,
        DriveGridHeaderComponent,
        DriveGridBodyComponent,
        DriveDetailsComponent,
        DriveMembersModalComponent
    ],
    providers: [DriveService],
    bootstrap: [DriveViewComponent]
})
export class DriveModule
{
}
import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';


import { Drive } from './drive.ts';
import { GridRepositoryService } from '../grid/grid-repository.service.ts';

@Injectable()
export class DriveService extends GridRepositoryService<Drive>
{
    public webApiControllerName = 'drive';

    constructor(@Inject(Http) http: Http) { super(http); }
}
export class Drive
{
    public id: string;
    public name: string;
    public description: string;
    public dateCreated: string;
    public lastTimeModified: string;
    public expirationDate: string;
    public paymentSubscriptionId: string;
    public storageLocationId: string;
    public organizationId: string;
    public teamdriveInvitations: string[];
    public teamdriveAccessCodes: string[];
    public memberships: string[];

    public constructor(init?: Partial<Drive>) {
        Object.assign(this, init);
    }
}
import { EventEmitter } from '@angular/core';

export interface IGridBody<T>
{
    items: T[];
    selectedItemId: string;
    onDetailsButtonEmitter: EventEmitter<number>;
    onDetailsButton(index: number): void;
    toggleIconState(index: number): string;
}
export class GridFilterConstraint implements  IGridFilterConstraint
{
    public static getLabel(method: FilterEvaluationMethod): string
    {
        switch (method)
        {
            case FilterEvaluationMethod.LESS:
                return 'is less than';
            case FilterEvaluationMethod.EQUAL:
                return 'equals';
            case FilterEvaluationMethod.GREATER:
                return 'is greater than';
            case FilterEvaluationMethod.MATCH_CASE:
                return 'matches exactly';
            case FilterEvaluationMethod.IGNORE_CASE:
                return 'matches (ignoring case)';
            case FilterEvaluationMethod.CONTAINS:
                return 'contains';
            case FilterEvaluationMethod.STARTS_WITH:
                return 'starts with';
            case FilterEvaluationMethod.ENDS_WITH:
                return 'ends with';
            default:
                return 'unknown';
        }
    }

    public static getEnum(label: string): FilterEvaluationMethod
    {
        switch (label)
        {
            case 'is less than':
                return FilterEvaluationMethod.LESS;
            case 'equals':
                return FilterEvaluationMethod.EQUAL;
            case 'is greater than':
                return FilterEvaluationMethod.GREATER;
            case 'matches exactly':
                return FilterEvaluationMethod.MATCH_CASE;
            case 'matches (ignoring case)':
                return FilterEvaluationMethod.IGNORE_CASE;
            case 'contains':
                return FilterEvaluationMethod.CONTAINS;
            case 'starts with':
                return FilterEvaluationMethod.STARTS_WITH;
            case 'ends with':
                return FilterEvaluationMethod.ENDS_WITH;
            default:
                throw new Error(`unknown FilterEvaluationMethod label: ${label}`);
        }
    }

    public propertyName: string;
    public propertyValue: string;
    public evaluationMethod: FilterEvaluationMethod;
}

export enum FilterEvaluationMethod
{
    LESS,
    EQUAL,
    GREATER,
    IGNORE_CASE,
    MATCH_CASE,
    CONTAINS,
    STARTS_WITH,
    ENDS_WITH
}

export interface IGridFilterConstraint
{
    propertyName: string;
    propertyValue: string;
    evaluationMethod: FilterEvaluationMethod;
}
import { EventEmitter } from '@angular/core';

import { ListOrder } from '../utility/list-order.ts';
import { GridViewParameters } from './grid-view-parameters.ts';

export interface IGridHeader
{
    columns: { [columnName: string]: string };
    activeColumn: string;
    defaultColumn: string;
    listOrder: ListOrder;

    onColumnHeaderToggled(columnName: string): void;
    onColumnHeaderToggledEmitter : EventEmitter<string>;

}
import { EventEmitter } from '@angular/core';
import { AnimationEvent } from '@angular/animations';
import { GridViewParameters } from './grid-view-parameters.ts';


export interface IGridItemDetails<T>
{
    item: T;
    fadeInOutStatus: string;
    setFadeStatus(event: AnimationEvent): void;
    fadeIn(): void;
    fadeOut(): void;
    onOpenModalEmitter: EventEmitter<boolean>;
}
export class GridPagerButton
{
    public page: number;
    public text: string;
    public isActive: boolean;
    public isFirst: boolean;
    public isLast: boolean;

    public static createFirstPagerButton(): GridPagerButton
    {
        const button = new GridPagerButton();
        button.isFirst = true;
        button.text = 'First';
        button.page = 1;
        return button;
    }

    public static createLastPagerButton(pageNumber: number): GridPagerButton
    {
        const button = new GridPagerButton();
        button.isLast = true;
        button.text = `Last (${pageNumber})`;
        button.page = pageNumber;
        return button;
    }
}

export interface IGridPagerButton
{
    page: number;
    text: string;
    isActive: boolean;
    isFirst: boolean;
    isLast: boolean;
}
import { Component, EventEmitter, Output, Injectable } from '@angular/core';

import { IGridFilterConstraint } from './grid-filter-constraint.ts';
import { IGridPagerButton, GridPagerButton } from './grid-pager-button.ts';
import { GridViewParameters } from './grid-view-parameters.ts';


export interface IGridPagerService
{
    buttons: IGridPagerButton[];
    numPages: number;
    page: number;
    pageInputValue: number;
    refresh(page: number, numPages: number): void;
}

@Injectable()
export class GridPagerService implements IGridPagerService
{
    private _buttons: IGridPagerButton[];
    private _page: number;
    private _pageInputValue: number;
    private _numPages: number;

    constructor()
    {
        this._page = 1;
        this._pageInputValue = 1;
    }

    public get page(): number { return this._page; }

    public get buttons(): IGridPagerButton[] { return this._buttons; }

    public get pageInputValue(): number { return this._pageInputValue; }

    public get numPages(): number { return this._numPages; }

    private _pageRange = 2; // todo: configure

    public refresh(page: number, numPages: number): void
    {
        this._numPages = numPages;
        this._page = page;
        this._pageInputValue = page;
        this._buttons = [];
        const numButtons = 2 * this._pageRange + 1;

        let startPage = page - this._pageRange > 1 ? page - this._pageRange : 1;
        let endPage = page + this._pageRange < numPages ? page + this._pageRange : numPages;

        if (endPage - startPage < numButtons)
        // left or right shift required to have constant number of buttons in pager
        {
            if (endPage < numPages) endPage += 2 * this._pageRange - (endPage - startPage);
            else startPage -= 2 * this._pageRange - (endPage - startPage);
        }

        if (startPage < 1) startPage = 1;
        else if (startPage > 1) this._buttons = [GridPagerButton.createFirstPagerButton()];

        for (let i = startPage; i <= endPage; i++)
        {
            const button = new GridPagerButton();
            button.page = i;
            button.text = i.toString();
            if (i === page) button.isActive = true;
            this._buttons.push(button);
        }

        if (endPage < numPages) this._buttons.push(GridPagerButton.createLastPagerButton(numPages));
    }
}
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptionsArgs, RequestMethod } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';

import { ListOrder } from '../utility/list-order.ts';
import { WebApiService } from '../utility/web-api.service.ts';
import { IGridResult, GridResult } from './grid-result.ts';
import { IGridFilterConstraint } from './grid-filter-constraint.ts';

@Injectable()
export abstract class GridRepositoryService<T> extends WebApiService implements IGridRepositoryService<T>
{
    constructor(protected http: Http) { super(http); }

    public abstract get webApiControllerName(): string;


    public list(page: number,
                activeColumnName: string,
                listOrder: ListOrder,
                filterConstraints: IGridFilterConstraint[]):
        Observable<IGridResult<T>>
    {
        const requestUrl = `${this.
            webApiControllerName}/list?page=${page}&column=${activeColumnName}&listOrder=${listOrder}`;
        const body = JSON.stringify(filterConstraints);
        return this.post(requestUrl, null, body).
            map(data => data.json() as IGridResult<T>).
            catch(error =>
            {
                console.log(error);
                var messages = [
                    `XHR-Status: ${error.status}`,
                    `XHR-ReadyState: ${error.readyState}`,
                    `XHR-StatusText: ${error.statusText}`
                ];
                var result = new GridResult((({ error: true, errorMessages: messages })) as any);
                return Observable.of(result);
            });
    }

    public get(id: string):
        Observable<IGridResult<T>>
    {
        const requestUrl = `${this.
            webApiControllerName}/get?id=${id}`;
        return this.post(requestUrl, null, null).
            map(data => data.json() as IGridResult<T>).
            catch(error =>
            {
                console.log(error);
                var messages = [
                    `XHR-Status: ${error.status}`,
                    `XHR-ReadyState: ${error.readyState}`,
                    `XHR-StatusText: ${error.statusText}`
                ];
                var result = new GridResult((({ error: true, errorMessages: messages })) as any);
                return Observable.of(result);
            });
    }
}


export interface IGridRepositoryService<T>
{
    webApiControllerName: string;

    list(page: number, activeColumnName: string, listOrder: ListOrder, filterConstraints: IGridFilterConstraint[]):
        Observable<IGridResult<T>>;

    get(id: string): Observable<IGridResult<T>>;
}
export class GridResult<T> implements IGridResult<T>
{
    public items: T[];
    public numPages: number;
    public error: boolean;
    public errorMessages: string[];

    public constructor(init?: Partial<GridResult<T>>) { Object.assign(this, init); }
}

export interface IGridResult<T>
{
    items: T[];
    numPages: number;
    error: boolean;
    errorMessages: string[];
}
import { IGridFilterConstraint } from './grid-filter-constraint.ts';
import { GridView } from './grid-view.ts';
import { ListOrder } from '../utility/list-order.ts';

export class GridViewParameters
{
    public page: number;
    public filterConstraints: IGridFilterConstraint[];
    public activeColumn: string;
    public listOrder: ListOrder;

    constructor(activeColumn: string = null)
    {
        this.activeColumn = activeColumn;
        this.page = 1;
        this.listOrder = ListOrder.Descending;
        this.filterConstraints = [];
    }

    public static getCurrentViewParams<T>(view: GridView<T>): GridViewParameters
    {
        const params = new GridViewParameters();
        params.page = view.pager.page;
        params.activeColumn = view.grid.header.activeColumn;
        params.listOrder = view.grid.header.listOrder;
        return params;
    }

    public syncMissingParams<T>(view: GridView<T>): void
    {
        if (!this.page) this.page = view.pager.page;
        if (!this.activeColumn) this.activeColumn = view.grid.header.activeColumn;
        if (!this.listOrder) this.listOrder = view.grid.header.listOrder;
    }
}

export interface IGridViewParameters
{
    page: number;
    filterConstraints: IGridFilterConstraint[];
    activeColumn: string;
    listOrder: ListOrder;
    syncMissingParams<T>(view: GridView<T>): void;
}
import { AfterViewInit } from '@angular/core';

import { Grid } from './grid.ts';
import { IGridFilterModal } from './grid-filter-modal.ts';
import { IGridItemDetails } from './grid-item-details.ts';
import { GridPagerButton } from './grid-pager-button.ts';
import { IGridViewParameters, GridViewParameters } from './grid-view-parameters.ts';
import { IGridResult } from './grid-result.ts';
import { IGridFilterConstraint } from './grid-filter-constraint.ts';
import { ListOrder } from '../utility/list-order.ts';

import { IGridPagerService } from './grid-pager.service.ts';
import { IGridRepositoryService } from './grid-repository.service.ts';


export abstract class GridView<TEntity> implements AfterViewInit
{
    public messages: string[];

    public messageTitle: string;

    public pager: IGridPagerService;

    public activefilterConstraints: IGridFilterConstraint[];

    public abstract grid: Grid<TEntity>;

    public abstract filterModal: IGridFilterModal;

    public abstract itemDetails: IGridItemDetails<TEntity>;

    protected constructor(pagerService: IGridPagerService, protected repositoryService: IGridRepositoryService<TEntity>)
    {
        this.pager = pagerService;
    }

    public ngAfterViewInit(): void
    {
        $('[data-toggle="tooltip"]').tooltip();
    }

    public onPageButton(page: number): void { this.loadPage(page); }

    public onFilterApplyButton(): void
    {
        if (!this.filterModal.isValid) return;
        const params = GridViewParameters.getCurrentViewParams(this);
        this.loadItems(params);
    }

    public onResetViewButton(): void { this.resetView(); }

    public onColumnHeaderToggled(columnName: string): void { this.toggleColumnSorting(columnName); }

    public onDetailsButton(rowIndex: number): void { this.toggleItemDetails(rowIndex); }

    protected abstract loadItemDetails(item: TEntity): void;

    public loadPage(page: number): void
    {
        const params = new GridViewParameters();
        params.filterConstraints = this.activefilterConstraints;
        params.page = page;
        params.listOrder = this.grid.header.listOrder;
        this.loadItems(params);
    }

    public loadItems(params: IGridViewParameters): void
    {
        this.clearPanelBody();
        params.syncMissingParams(this);
        const propertyName = this.grid.header.columns[params.activeColumn];

        this.repositoryService.list(params.page, propertyName, params.listOrder, params.filterConstraints).
            subscribe(result => this.refreshView(result, params));
    }

    protected toggleItemDetails(rowIndex: number): void
    {
        if (this.grid.body.selectedItemId)
        {
            const item = this.grid.body.items[rowIndex];
            this.itemDetails.item = item;
            this.loadItemDetails(item);
            this.itemDetails.fadeIn();
        }
        else this.itemDetails.fadeOut();
    }

    protected refreshView(result: IGridResult<TEntity>, params: IGridViewParameters)
    {
        if (result.error)
        {
            this.messageTitle = 'An error occurred, see details below:';
            this.messages = result.errorMessages;
        }
        else
        {
            this.grid.header.activeColumn = params.activeColumn;
            this.grid.header.listOrder = params.listOrder;
            this.grid.body.items = result.items;
            this.pager.refresh(params.page, result.numPages);
            this.activefilterConstraints = params.filterConstraints;
        }
    }

    protected resetView(): void
    {
        const params = new GridViewParameters(this.grid.header.defaultColumn);
        this.activefilterConstraints = null;
        this.grid.body.selectedItemId = null;
        this.itemDetails.item = null;

        this.clearPanelBody();
        this.loadItems(params);
    }

    protected clearPanelBody(): void
    {
        this.messageTitle = null;
        this.messages = null;
        this.grid.body.items = null;
    }

    private toggleColumnSorting(columnName: string): void
    {
        const params = GridViewParameters.getCurrentViewParams(this);
        params.activeColumn = columnName;

        const toggledListOrder = (this.grid.header.listOrder === ListOrder.Ascending)
            ? ListOrder.Descending
            : ListOrder.Ascending;

        params.listOrder = toggledListOrder;

        this.loadItems(params);
    }
}
import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { GridPagerService } from './grid-pager.service.ts';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule
    ],
    declarations: [],
    exports: [],
    providers: [GridPagerService],
    bootstrap: []
})

export class GridModule
{
}
import { Output, EventEmitter } from '@angular/core';

import { IGridBody } from './grid-body.ts';
import { IGridHeader } from './grid-header.ts';

import { GridViewParameters } from './grid-view-parameters.ts';
import { IGridFilterConstraint } from './grid-filter-constraint.ts';


export abstract class Grid<TEntity>
{
    public abstract header: IGridHeader;

    public abstract body: IGridBody<TEntity>;

    @Output()
    public onColumnHeaderToggledEmitter = new EventEmitter<string>();

    public onColumnHeaderToggled(columnName: string) { this.onColumnHeaderToggledEmitter.emit(columnName); }

    @Output()
    public onDetailsButtonEmitter = new EventEmitter<number>();

    public onDetailsButton(rowIndex: number) { this.onDetailsButtonEmitter.emit(rowIndex); }
}
import { Component, AfterViewInit, Injectable, Inject } from '@angular/core';
import { Router, NavigationExtras } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AppErrorType } from 'app/core/error.component.ts';

@Component({
    moduleId: __moduleName,
    selector: 'navbar',
    templateUrl: './../../html/navbar.html',
    styleUrls: ['./../../css/navbar.css']
})
@Injectable()
export class NavbarComponent implements AfterViewInit
{
    public ngAfterViewInit(): void { $('[data-toggle="tooltip"]').tooltip(); }

    public clientVersion: string;
    public apiVersion: string;
    public authService: IAuthService;
    public userName: string;
    public userPassword: string;
    private _loading: boolean;


    constructor(private router: Router)
    {
        this.clientVersion = '1.X.X';
        this.apiVersion = '2.X.X';
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from '../core/app-routing.module.ts';
import { FormsModule } from '@angular/forms';

import { NavbarComponent } from './navbar.component.ts';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        AppRoutingModule,
    ],
    declarations: [
        NavbarComponent
    ],
    exports: [
        NavbarComponent
    ],
    bootstrap: [NavbarComponent]
})
export class NavbarModule
{
}
import { Component, EventEmitter, Output } from '@angular/core';
import { trigger, state, style, animate, transition, AnimationEvent, keyframes } from '@angular/animations';

import { User } from './user.ts';
import { IGridItemDetails } from '../grid/grid-item-details.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';

@Component({
    moduleId: __moduleName,
    selector: 'item-details',
    templateUrl: './../../html/user-details.html',
    animations: [
        trigger('detailsFadeInOut',
        [
            state('void', style({ opacity: 0 })),
            state('in', style({ opacity: 1 })),
            state('out', style({ opacity: 0 })),
            transition('*=>fadeIn',
                animate('300ms ease-in',
                    keyframes([
                        style({ opacity: 0, offset: 0 }),
                        style({ opacity: 1, offset: 1.0 })
                    ]))),
            transition('*=>fadeOut',
                animate('300ms ease-in-out',
                    keyframes([
                        style({ opacity: 1, offset: 0 }),
                        style({ opacity: 0, offset: 1.0 })
                    ])))
        ])
    ]
})

export class UserDetailsComponent implements IGridItemDetails<User>
{
    public fadeIn(): void { this._fadeInOutStatus = 'fadeIn' }

    public fadeOut(): void { this._fadeInOutStatus = 'fadeOut' }

    public setFadeStatus(event: AnimationEvent): void
    {
        if (event.fromState === 'void' || this._fadeInOutStatus === 'fadeIn') this._fadeInOutStatus = 'in';
        else if (this._fadeInOutStatus === 'fadeOut') this._fadeInOutStatus = 'out';
    }

    public get fadeInOutStatus(): string { return this._fadeInOutStatus; }

    private _fadeInOutStatus: string;

    public item: User;

    // todo: adapt fields and methods below
    public commentInput: boolean;
    public organizationInput: boolean;
    public emailInput: boolean;

    public comment: string;
    public organization: string;
    public email: string;

    public saveComment(): void
    {
        // todo: apply changes to entity
        this.commentInput = false;
    }

    public saveEmail(): void
    {
        // todo: apply changes to entity
        this.emailInput = false;
    }

    public saveOrganization(): void
    {
        // todo: apply changes to entity
        this.organizationInput = false;
    }
    
    @Output()
    public onOpenModalEmitter = new EventEmitter<boolean>();
}
import { Component, EventEmitter, Output } from '@angular/core';
import { trigger, state, style, animate, transition } from '@angular/animations';
import { Observable } from 'rxjs/Observable';
import { Http } from '@angular/http';

import { UserService } from './user.service.ts';
import { User } from './user.ts';
import { IGridBody } from '../grid/grid-body.ts';

@Component({
    moduleId: __moduleName,
    selector: 'tbody [grid-body]',
    templateUrl: './../../html/user-grid-body.html',
    animations: [
        trigger('detailsToggled',
        [
            state('up', style({ transform: 'scaleY(1)' })),
            state('down', style({ transform: 'scaleY(-1)' })),
            transition('up => down', animate('300ms')),
            transition('down => up', animate('300ms'))
        ])
    ]
})
export class UserGridBodyComponent implements IGridBody<User>
{
    public items: User[];
    public selectedItemId: string;

    public toggleIconState(index: number): string
    {
        return (this.selectedItemId === this.items[index].id) ? 'down' : 'up';
    }

    @Output()
    public onDetailsButtonEmitter = new EventEmitter<number>();

    public onDetailsButton(index: number)
    {
        if (index < 0) throw new RangeError('Index must not be smaller than zero.');
        if (index > this.items.length - 1)
            throw new RangeError(`No such grid index. Given: ${index}. Number of rows: ${this.items.length}.`);

        if (this.selectedItemId === this.items[index].id) this.selectedItemId = null;
        else this.selectedItemId = this.items[index].id;
        this.onDetailsButtonEmitter.emit(index);
    }
}
import { Component, Output, EventEmitter } from '@angular/core';
import { IGridHeader } from '../grid/grid-header.ts';
import { ListOrder } from '../utility/list-order.ts';
import { GridViewParameters } from 'app/grid/grid-view-parameters.ts';

@Component({
    moduleId: __moduleName,
    selector: 'thead [grid-header]',
    templateUrl: './../../html/drive-grid-header.html'
})
export class UserGridHeaderComponent implements IGridHeader
{
    public readonly defaultColumn = 'Email';
    public activeColumn: string;
    public listOrder: ListOrder;
    public columns: { [columnName: string]: string; };

    constructor()
    {
        this.activeColumn = this.defaultColumn;
        this.listOrder = ListOrder.Descending;
        this.columns = {
            'Enabled': 'IsEnabled',
            'Email': 'Email',
            'Registration Date': 'RegistrationDate',
            'Date Approved': 'ApprovalDate',
            'Last Logon': 'LastLoginWithPassword',
            'Failed Logins': 'FailedLoginCount',
            'Organization ID': 'OrganizationId',
            '': ''
        };
    }

    @Output()
    public onColumnHeaderToggledEmitter = new EventEmitter<string>();

    public onColumnHeaderToggled(columnName: string): void { this.onColumnHeaderToggledEmitter.emit(columnName); }
}
import { Component, Inject, ViewChild } from '@angular/core';

import { User } from './user.ts';
import { Grid } from '../grid/grid.ts';

import { UserGridBodyComponent } from './user-grid-body.component.ts';
import { UserGridHeaderComponent } from './user-grid-header.component.ts';


@Component({
    moduleId: __moduleName,
    selector: '[user-grid]',
    templateUrl: './../../html/grid.html'
})
export class UserGridComponent extends Grid<User>
{
    @ViewChild(UserGridBodyComponent)
    public body: UserGridBodyComponent;

    @ViewChild(UserGridHeaderComponent)
    public header: UserGridHeaderComponent;
}
import { Component, Inject, ViewChild, AfterContentInit } from '@angular/core';
import { trigger, state, style, animate, transition, keyframes } from '@angular/animations';

import { User } from './user.ts';
import { GridView } from '../grid/grid-view.ts';
import { GridViewParameters } from '../grid/grid-view-parameters.ts';

import { UserGridComponent } from './user-grid.component.ts';
import { UserDetailsComponent } from './user-details.component.ts';

import { GridPagerService } from '../grid/grid-pager.service.ts';
import { UserService } from './user.service.ts';

@Component({
    moduleId: __moduleName,
    selector: 'user-view',
    templateUrl: './../../html/user-view.html',
    animations: [
        trigger('detailsSlideUpDown',
        [
            state('void', style({ maxHeight: '0', overflow: 'hidden' })),
            state('in', style({ maxHeight: '*', overflow: 'hidden' })),
            state('out', style({ maxHeight: '0', overflow: 'hidden' })),
            transition('*=>fadeIn',
                animate('300ms ease-in',
                    keyframes([
                        style({ maxHeight: '0', overflow: 'hidden', offset: 0 }),
                        style({ maxHeight: '500px', overflow: 'hidden', offset: 1 })
                    ]))),
            transition('*=>fadeOut',
                animate('300ms ease-in-out',
                    keyframes([
                        style({ maxHeight: '500px', overflow: 'hidden', offset: 0 }),
                        style({ maxHeight: '0', overflow: 'hidden', offset: 1 })
                    ])))
        ])
    ]
})
export class UserViewComponent extends GridView<User>
{
    @ViewChild(UserGridComponent)
    public grid: UserGridComponent;

    @ViewChild(UserDetailsComponent)
    public itemDetails: UserDetailsComponent;

    public constructor(@Inject(GridPagerService) pagerService: GridPagerService,
                       @Inject(UserService) repositoryService: UserService)
    {
        super(pagerService, repositoryService);
    }

    public ngAfterContentInit(): void
    {
        const params = GridViewParameters.getCurrentViewParams(this);
        this.loadItems(params);
    }

    protected loadItemDetails(item: User): void
    {
        // todo: implement        
    }
}
import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { UtilityModule } from '../utility/utility.module.ts';
import { GridModule } from '../grid/grid.module.ts';

import { UserService } from './user.service.ts';
import { GridPagerService } from '../grid/grid-pager.service.ts';

import { UserViewComponent } from './user-view.component.ts';
import { UserGridComponent } from './user-grid.component.ts';
import { UserGridHeaderComponent } from './user-grid-header.component.ts';
import { UserGridBodyComponent } from './user-grid-body.component.ts';
import { UserDetailsComponent } from './user-details.component.ts';

// required for tooltips
import './../../node_modules/bootstrap/dist/js/bootstrap.min.js';
import * as $ from 'jquery';

@NgModule({
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        FormsModule,
        HttpModule,
        UtilityModule,
        GridModule
    ],
    declarations: [
        UserViewComponent,
        UserGridComponent,
        UserGridHeaderComponent,
        UserGridBodyComponent,
        UserDetailsComponent
    ],
    providers: [UserService],
    bootstrap: [UserViewComponent]
})
export class UserModule
{
}
import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';


import { User } from './user.ts';
import { GridRepositoryService } from '../grid/grid-repository.service.ts';

@Injectable()
export class UserService extends GridRepositoryService<User>
{
    public webApiControllerName = 'user';

    constructor(@Inject(Http) http: Http) { super(http); }
}
export class User
{
    public about: string;
    public approvalDate: string;
    public comment: string;
    public croppedProfileImageId: string;
    public displayName: string;
    public email: string;
    public facebookId: string;
    public failedLoginCound: number;
    public googleId: string;
    public id: string;
    public isEnabled: boolean;
    public isApproved: boolean;
    public lastLoginWithPassword: string;
    public organizationId: string;
    public organizationRoleId: string;
    public registrationDate: string;
    public registrationKey: string;
    public twoLetterIsoLanguage: string;

    public constructor(init?: Partial<User>) {
        Object.assign(this, init);
    }
}
import { PipeTransform, Pipe } from '@angular/core';

@Pipe({ name: 'dict' })
export class DictionaryPipe implements PipeTransform
{
    public transform(value: any, args: string[]): any
    {
        const keys: any[] = [];
        for (let key in value) keys.push({ key: key, value: value[key] });
        return keys;
    }
}
export class Environment {

}

export enum ListOrder {
    None,
    Ascending,
    Descending
}
import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NavbarModule } from '../navbar/navbar.module.ts';

import { DictionaryPipe } from './dictionary.pipe.ts';

@
NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        BrowserAnimationsModule,
        NavbarModule
    ],
    declarations: [
        DictionaryPipe
    ],
    exports: [
        DictionaryPipe
    ]
})
export class UtilityModule
{
}
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptionsArgs, RequestMethod, Response, ResponseOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';


@Injectable()
export class WebApiService
{
    protected constructor(protected http: Http) {}

    protected baseUrl = 'https://local.dev:5000'; // todo: make base url configurable


    public post(requestUrl: string, headers: Headers, body: any): Observable<Response>
    {
        const url = `${this.baseUrl}/${requestUrl}`;
        if (!headers)
        {
            headers = new Headers({
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            });
        }
        else
        {
            if (!headers.get('Content-Type')) headers.append('Content-Type', 'application/json');
            if (!headers.get('Accept')) headers.append('Accept', 'application/json');
        }


        // return this.http.post(url, body, { headers: headers, withCredentials: true });
        let json : string;
        if(this.webApiControllerName == "user"){
          json = JSON.stringify({"error":false,"errorMessages":null,"items":[{"email":"Zuri.Guster@none.com","about":"5W+W9]1:=t>","approvalDate":"2017-05-21T06:24:05.642+02:00","comment":"sa#`jRT7Gz(&>","croppedProfileImageId":"fe02dcee-4738-0cc4-3b8b-b31c67b12004","displayName":"))wv_,?L,W^,Z","facebookId":"_tE<`(PI]C+!7zg","failedLoginCount":-7559895158973810688,"googleId":"!p<F~ln","id":"6b31d960-052b-58ce-1e25-38de589f7d95","isApproved":false,"isEnabled":true,"lastLoginWithPassword":"2021-02-23T04:42:17.517+01:00","organizationId":"e60afffe-04cc-6fce-f5ed-4f33327f58cb","organizationRoleId":"0697c572-dfb9-2b59-69a2-8c4536d3d619","registrationDate":"2016-09-26T22:58:01.113+02:00","registrationKey":"@jeFe?RBjb;","saltedPasswordHash":"WJb(~-t!b(","twoLetterIsoLanguage":"J485avy#"},{"email":"Zuri.Abbot@none.com","about":"^b?,BAG231Rn)Fx","approvalDate":"2022-05-05T23:25:57.052+02:00","comment":"Zro`Fn8b~/FWX","croppedProfileImageId":"7a539b58-5f39-a277-509a-2dabf7ac1b1c","displayName":"]yK:IcxNW5","facebookId":"uFjY(Wfi@^","failedLoginCount":3968299979464013824,"googleId":"UT%haS/ie","id":"cd2b1264-7ddc-df6f-430d-8ddc141a0087","isApproved":false,"isEnabled":true,"lastLoginWithPassword":"2019-01-02T03:01:52.719+01:00","organizationId":"32d54b2e-4fa4-8e4b-1e4f-b4176417264a","organizationRoleId":"71d6634c-e1e8-4770-2f6d-0dbe192cdbe9","registrationDate":"2022-06-04T11:03:05.66+02:00","registrationKey":"RTu46&1vL]X&OH","saltedPasswordHash":"H`jiL7BGz0H5QP","twoLetterIsoLanguage":"R<bJcr,[l#~NKLk"},{"email":"Zain.Freeman@none.com","about":"tqL!MV?u/:","approvalDate":"2014-01-10T15:31:45.276+01:00","comment":"i$]PgF/W!Ufax","croppedProfileImageId":"5bbd6a9c-6338-c23f-4f52-281b07609944","displayName":"lgt/ZlIx&=n#","facebookId":"W@QjnJ)jl","failedLoginCount":-4107109375460426752,"googleId":"-Y(%Q@l0","id":"20934546-f296-4948-aaa6-2062abbd7e1e","isApproved":true,"isEnabled":true,"lastLoginWithPassword":"2021-07-17T01:13:37.555+02:00","organizationId":"79bfa79e-2556-528d-9feb-e6243cfcfc14","organizationRoleId":"dbab21bc-a8b6-91ab-9383-4a38042346e5","registrationDate":"2019-09-26T17:51:46.398+02:00","registrationKey":"NcanG4","saltedPasswordHash":"sBbh7L[","twoLetterIsoLanguage":"uN06>z"},{"email":"Zain.Connoly@none.com","about":"alG/7^Q$oW=MC","approvalDate":"2012-09-08T05:51:22.216+02:00","comment":"F$1]j&t`","croppedProfileImageId":"882f9c1a-2c0f-fad0-d297-056e851681e4","displayName":"p*4B4:D99","facebookId":"Yw~c`wdTM","failedLoginCount":7250333844782233600,"googleId":")#9=.@","id":"08f38e62-5218-2790-39ba-9fd6931a4a39","isApproved":true,"isEnabled":false,"lastLoginWithPassword":"2017-10-28T06:55:22.076+02:00","organizationId":"450dcb10-25c1-4e4b-6c91-43c0e02c44c3","organizationRoleId":"3627f404-9bec-511d-f2e8-9eb1169df4ac","registrationDate":"2014-10-08T16:48:45.674+02:00","registrationKey":"UDqIn^zA~~08","saltedPasswordHash":"xG3`[\\Znt@tC","twoLetterIsoLanguage":"Gt%Tyz2"},{"email":"Yazmin.Phillips@none.com","about":"y_t>>JBu","approvalDate":"2013-02-11T16:59:13.455+01:00","comment":"\\-~e#P","croppedProfileImageId":"64305b3a-a15d-4a33-f307-6d7ac084c4cc","displayName":"?Y4E9/a2~afY","facebookId":":;Bn@x!/:_-HJ:","failedLoginCount":-6749403961911240704,"googleId":"Pt10m/+q+^%;t","id":"14e6535e-44eb-6a23-0fc1-51dc4d7f34ca","isApproved":false,"isEnabled":true,"lastLoginWithPassword":"2019-02-02T08:40:31.164+01:00","organizationId":"b70926fe-f7b1-499d-ea2a-2e7d88d5f800","organizationRoleId":"d540d290-e8a8-6077-f4c9-81f517bf1ff2","registrationDate":"2015-02-15T22:44:07.64+01:00","registrationKey":"tUYn:b;gButt","saltedPasswordHash":"\\Z3#vlm^3jqdJJU","twoLetterIsoLanguage":"&vOJEidb#"},{"email":"Yazmin.Devey@none.com","about":"GR]1NWS","approvalDate":"2021-09-13T13:18:38.859+02:00","comment":"nTk(8`5FR:","croppedProfileImageId":"4e782192-5b81-baf4-f524-1c257fead1f6","displayName":"MADp=@UOk?$H","facebookId":"iFt=@8b`JZV4","failedLoginCount":238530466412420192,"googleId":"zdZ;sN&Tpr#4U","id":"07ab6496-da92-35b1-500d-4cbc739e42e7","isApproved":false,"isEnabled":true,"lastLoginWithPassword":"2016-11-23T07:47:24.724+01:00","organizationId":"0a900222-b804-ccaa-8b68-fb09291236ef","organizationRoleId":"2c21fe58-ea02-544f-d50b-bcfb0b7c3ad7","registrationDate":"2019-03-04T15:43:59.677+01:00","registrationKey":"Zu<d7*7`Xa","saltedPasswordHash":"?H5C5","twoLetterIsoLanguage":"YQ9gZhjByt3R]"},{"email":"Xavier.Guster@none.com","about":"#*&+5*T(%j+`:E","approvalDate":"2022-03-05T07:25:49.661+01:00","comment":"J[Cb)UI","croppedProfileImageId":"fc18f7e4-f12a-700e-11e2-0a7c453c59fb","displayName":"XMg@oRm","facebookId":"cVf)/$RVm<#N/i*","failedLoginCount":4706969578023225344,"googleId":"3&w)9","id":"e9abfdbc-4b58-6517-1b7b-52222786afdd","isApproved":false,"isEnabled":false,"lastLoginWithPassword":"2018-04-03T08:18:59.525+02:00","organizationId":"4efa2762-e03d-d95d-40a7-d2f3543c0d76","organizationRoleId":"a5f7e1c8-18be-8239-75dc-5e6533d672d4","registrationDate":"2020-06-17T15:43:42.151+02:00","registrationKey":"asa)r^b%4J/)&U1","saltedPasswordHash":"$y^dEV+C","twoLetterIsoLanguage":"Fbo[a)p&25[s"},{"email":"Xander.Guster@none.com","about":"s;(F3xx2g*U-xkC","approvalDate":"2016-01-12T10:36:04.695+01:00","comment":"`O0&v8Y,wi>lee6","croppedProfileImageId":"1ed4af66-c34e-b41c-45f2-78ae091c8304","displayName":"4R)l!^s","facebookId":"\\mY7(=pf,:","failedLoginCount":-167815264369927744,"googleId":"Y?SLdisyLQwtS","id":"ef674824-61c4-6edb-f649-5fd7ced0ace4","isApproved":true,"isEnabled":false,"lastLoginWithPassword":"2019-08-04T11:54:49.563+02:00","organizationId":"fe451044-5109-dc4b-d509-007312b151b4","organizationRoleId":"a964e088-a8c5-014c-f56b-a94f714f4d19","registrationDate":"2021-10-31T10:20:44.313+01:00","registrationKey":"8;,3%k1","saltedPasswordHash":"/Is`X4steXH;`CK","twoLetterIsoLanguage":"Wi!y<Wj0gFW4Uu"},{"email":"William.Freeman@none.com","about":"/vcQaiM2NqWg","approvalDate":"2020-08-04T10:32:12.497+02:00","comment":"\\T?/s8","croppedProfileImageId":"79a8d28e-f4ed-ebda-d896-3bcc3ed48ae1","displayName":"hEOM4#>Yb[","facebookId":"[beJvg","failedLoginCount":-44109013502749136,"googleId":"l`.=Cq/&","id":"98e256ce-7e14-1488-339b-4dfd2a6467de","isApproved":true,"isEnabled":false,"lastLoginWithPassword":"2018-11-08T14:53:32.778+01:00","organizationId":"a92a7d84-539b-d318-c587-3b8742ddb189","organizationRoleId":"99f5e684-1c79-1e95-7485-1c74d33746d2","registrationDate":"2013-02-25T20:43:52.551+01:00","registrationKey":"u!fu\\7`3gRAXjW5","saltedPasswordHash":"vdChG!/YKY<k=Hf","twoLetterIsoLanguage":"~0=tY9(0lB-bsz"},{"email":"Wes.Slater@none.com","about":"b5B!8~T89#g","approvalDate":"2017-05-04T13:50:50.5+02:00","comment":"X9Rz!","croppedProfileImageId":"ba28776e-0a82-eb04-fbb3-5bf7afe86b47","displayName":"/bIag","facebookId":">hB&[zf","failedLoginCount":-8672023996729028608,"googleId":"6,py#i_IDc~~$>","id":"58ed4fba-64e8-b2d8-ead5-0dcbbf7bbc7c","isApproved":true,"isEnabled":false,"lastLoginWithPassword":"2019-01-12T17:57:31.448+01:00","organizationId":"e379e080-54a3-9f97-d96b-1fb9bcf2b30b","organizationRoleId":"cceb17ac-1cbd-0036-ad0a-010c5850b8c1","registrationDate":"2021-05-10T20:35:48.173+02:00","registrationKey":"@0%_IM*U!","saltedPasswordHash":"S>KiV2xBm9Y","twoLetterIsoLanguage":"4S&mM"}],"numPages":11,"page":1});
        }
        else{
          json = JSON.stringify({"error":false,"errorMessages":null,"items":[{"dateCreated":"2022-05-26T21:26:44.431+02:00","description":"Y0Lsi9","expirationDate":"2012-10-20T10:08:49.849+02:00","id":"0d1a47f0-bc83-f85c-7207-0b68969e3dd3","lastTimeModified":"2013-11-22T07:10:30.816+01:00","memberships":["1a1b333c-a3a3-527b-933f-34977fa16cbb","7c75b376-4c27-7b48-d0dc-0092e24014eb","557068e6-c46a-f2e1-4759-f53c5df3f983","eb983b96-0109-39ff-9125-423267876a4f","3be64e74-f60d-bbc3-8f97-b99da9efe2fd"],"name":"awD7WD","paymentSubscriptionId":"4313de3e-447f-792a-c0f7-6079964ada50","storageLocationId":"b117b774-d8c2-8c12-8106-87fb8bc95e82","storageType":190,"teamdriveAccessCodes":["09ff99c6-a0b9-ed73-d2ea-03834a57c645","26af9c86-d62d-7a2f-fc5a-15006ec2593f","0a99228e-720f-abcd-4705-a36c0f42bf84","2f41c7d0-b7b6-57a3-72a5-aa4756055811","46b89120-5ade-2fe9-0efe-30b35d13fbef","ca569dfc-9620-7b64-27a3-fddedd488384"],"teamdriveInvitations":["a6ec4e2e-c278-a60a-e76e-55276b53e5f7","2b57a4cc-50ee-1833-f6a4-3b5be4ac883e","ed604a04-c181-f7b5-7e07-497627108cc1","b9a4ddaa-88fc-4bdb-5848-4508ea8f9ce2","9d32d830-500c-d6ee-8a06-3aa1eb9ed717","680ad552-d517-e487-efef-4dddf32bd56b","6985c962-9749-8f36-aa4b-f215dcc0f8a1","09d4143e-244f-a7a8-a77b-423c8523ece8"]},{"dateCreated":"2022-05-02T06:11:59.441+02:00","description":"<`6@4s(o?,ZK","expirationDate":"2017-01-24T03:12:17.051+01:00","id":"e7a3b234-9eaf-ead0-0202-13d5f328fa61","lastTimeModified":"2017-08-29T11:57:33.936+02:00","memberships":["f15730ec-59c0-8501-3938-569bdb753f0d","e89f9bc8-7e78-9a0c-ad83-a132791224de","106f160c-78ac-52ad-54de-26b02146937d","1639f6bc-9107-c3dd-f2dd-f28a1a98715e","dd36156a-1a22-83e7-7927-6f86c8e771b3","120ba280-4951-0c54-e6e2-77742b54f878","6a8d193e-683e-0905-e990-c23eeef78a6d","b1fba412-9b76-388a-d064-fd409a7dab0c","621ad7e2-7efb-7a8b-e6f6-1013a46d1c64"],"name":"I;m=YOv7p","paymentSubscriptionId":"28ae7a12-a43f-9755-70ad-111275e9b97b","storageLocationId":"cd080056-5e69-0624-896f-a9cb2dda531d","storageType":80,"teamdriveAccessCodes":["3791f780-88f4-b591-f3c0-60d6f2b29239","dabc0cb4-e334-760b-48fd-012e8262e896","512bff80-1584-e6f4-cc35-f55c1d250b52","4a4f3636-394b-2593-5c1e-b388e18819ca","ccb81edc-af4c-8cbc-3383-2fd92499ed63","a0b51a60-37c8-a00c-0df3-2be096956c2d"],"teamdriveInvitations":["98e5d620-3cbf-2300-a1e1-79f9805f7c84","48cc9310-37a7-4078-0171-2c42017064e5","9896ccc2-edc9-bc57-11bf-5d00f1835e9e","27c1b328-f3d6-f745-608c-f6a99f6d0323","b244bf64-5740-d576-b32a-6e69526abb22","b2953cfc-a428-3536-e5d0-b99c7f6df826","aa2d245e-0fe6-4ca1-0911-9f4a56d0f6e1"]},{"dateCreated":"2022-04-22T20:11:40.276+02:00","description":"+EcVNX8i^]R","expirationDate":"2018-02-12T10:32:55.763+01:00","id":"442eefe4-9147-5478-3c37-3ef9fd36b3e7","lastTimeModified":"2017-06-24T10:49:39.444+02:00","memberships":["306ebc7e-8021-d441-3b3c-181d44fee774","0f4b31f8-529a-884f-1bdb-bb0c0f6e1076","af0b5664-4127-51dc-8efa-c8fc6188bbd5","a73dce20-8db2-5c25-282b-d20fac4b44a8","dfeb928c-2d69-e584-b5de-2279a6d7111f"],"name":"Ty2H(c:BgL-G","paymentSubscriptionId":"fa9c7e32-bf3b-b376-9039-fec0c3320660","storageLocationId":"2473299a-a978-44ad-e9d9-e6e0e2c387d3","storageType":138,"teamdriveAccessCodes":["41cf1822-2504-f1df-5805-a54827f339bb","1fbff262-6f26-e8b9-6dd7-4f5b85ca31c4","045e8d6a-d948-6a19-6bb6-7ed474232533","1e1584a8-4f92-149b-0d68-bd8bbc39306e","88e269a8-ca47-6b84-fd73-0a742b4ebf63","7cc505a6-20a6-1896-ee98-eec9517e1695","eb934d9c-5110-9927-58c9-e69cf90df694"],"teamdriveInvitations":["50670a2c-9fd1-00d9-b70a-cafef6d673b9","d2cd9dce-f3f4-f56c-7925-9cf2e7b058f2","deb492c4-1a74-6565-1a3f-8d8e54b26be7","8b983142-c82f-4f2f-9737-be867f1ebd77","9680129a-c659-650a-d46d-765c1a0fe23b","5d5c21f0-c11c-e664-52ef-8a716882c04e","eab74d78-685c-2d3d-2a8d-653361303934"]},{"dateCreated":"2022-03-30T15:57:12.92+02:00","description":"<lUIRhI4r","expirationDate":"2020-05-18T17:11:04.706+02:00","id":"11215b7e-c7bc-0b33-a8d8-b367162f546f","lastTimeModified":"2018-09-06T10:00:58.148+02:00","memberships":["9f797c34-9446-aa06-6f27-529902360db6","6a3c9a5a-6a83-976f-2cd3-36be2a3ae147","3f451414-211c-1e23-7140-632adda5eeaa","e772d52a-341d-1d48-1ff1-879ce4c7d9b9","53c2f02a-3efe-6e5e-1c3a-41f7954464a3","578ffc6e-5501-88ea-51b4-1235d65767c7","bf031f00-8310-6351-0fb3-43368c55196c"],"name":"SY@\\]10^G:","paymentSubscriptionId":"42fa88ba-dc8c-c845-95a0-d18aaef08251","storageLocationId":"8bdecfdc-7ffb-eb4c-b92b-8cb45eb70e89","storageType":233,"teamdriveAccessCodes":["bb912986-729b-b1a9-ed0c-ed71571d773f","86c3fb2a-470a-1e23-a264-01a628e93341","5f29d236-18af-69b5-fc5a-30b0f137b7f1","f8c36f02-dafe-2e24-209b-a10211f86e07","ab374ad0-32f7-e4dc-6e11-f8b56761fe9a","7a163a7e-1371-98f9-840f-9340a62a3f87","9477a63e-4e47-4324-7444-640526d83ad2","576863ea-6d78-36bd-18eb-1fb73ccf56f1","5d3b863e-60e8-1ab2-8617-916ecf5143c6"],"teamdriveInvitations":["9e8089b0-9694-2c0f-9cb3-50623a2660a6","bc0940d6-2b81-4e58-f6a6-21eded6f30a1","f10c4dde-e23a-5a51-bd4d-72b708e78e8f","49d0a78c-983c-81ca-bf82-03ea6d1ccbfb","b9c7df64-5ea9-5bc0-c899-c6ae635557f2"]},{"dateCreated":"2022-03-09T11:40:35.39+01:00","description":"<0!2sGJ0>rW9\\Wl","expirationDate":"2012-10-05T21:16:42.033+02:00","id":"c5837588-f95c-b32b-9bc2-e08da3cc8748","lastTimeModified":"2017-10-22T02:10:03.223+02:00","memberships":["c69567a2-555c-fcb7-b150-369465561359","4001547e-9c85-76e7-c342-9b8b758ed5a6","4ec37f10-b81c-5c81-2666-575c1df8622c","2a3bc880-85a4-2a64-da4b-d2a0daa16de5","590d30ca-5c3b-2667-69c9-6130fbd1f8e3","1fd3d5c4-0699-449a-5529-cf3c083919f6","13d14736-7249-f141-9866-4fb8d4b23338","68b8f2f6-5f0f-0045-00fc-8dfaebfc8f32"],"name":"i\\z6FTBA","paymentSubscriptionId":"3489123e-cc3d-615c-48b3-7830f9e043fc","storageLocationId":"461c93e0-afd4-e778-9de4-5429d2ae0d4d","storageType":28,"teamdriveAccessCodes":["e16a3e3c-2c0d-8b6f-75ce-580f04159a03","9c7aa5b6-fdb2-b7da-4b83-5d000bb892eb","9666c7a0-7e57-85ea-b0a1-7ffded8ae5ae","f99f9d5e-66ff-880f-ecba-2a8734f73014","e3a82418-0165-e7d6-9cdc-4e510d4262d4","f51fc924-95a6-0d18-ee1e-b68e0626101c","eccd523c-0413-50db-c395-a2d4828499bb"],"teamdriveInvitations":["9aaffc38-8485-c9b7-04a1-ae9c7da34c24","71e042a8-f268-e088-ca0c-cfa4f10904c5","14982986-e3c2-4c69-19b7-ae37bedc3a39","13c00afc-7267-6b40-54b2-ec568891789f","df52a878-defa-d1af-b5d6-c7b2dfa6b17d","d3fa624a-6fed-e5f3-b788-f5ed45e46ee9","388a3cae-dea8-6e20-5fb7-1db69b7f714d"]},{"dateCreated":"2022-02-20T11:15:04.232+01:00","description":"!uG^>)TYn8cg[6","expirationDate":"2017-01-16T08:55:17.558+01:00","id":"7327638a-5721-8e1a-afeb-d3184b6174fa","lastTimeModified":"2021-10-20T19:07:58.387+02:00","memberships":["8ed90050-6d09-6189-773f-404d6b21a700","26a60394-3628-1fe5-eef2-53d3e77e901f","ab882b64-ffac-25be-0b08-e83d489a5132","eb10630e-4f8d-08b0-99dd-20e5a60950ae","8d1def6e-89e9-b613-b328-570ea0126f53","6effb1c0-c180-61dc-5134-37632ed80cae","f3e266dc-4b17-d056-e659-74b202d786cd","fc77852c-728e-9bd4-5454-c0e439f93ec2","9723415a-e08d-472e-388b-ebad42da76a1"],"name":"zk0~9A3^zSCP4K","paymentSubscriptionId":"df5359c6-e303-a33e-489e-117030b3ee9f","storageLocationId":"20e99b1c-e96b-c6fb-5494-ab57599405e9","storageType":112,"teamdriveAccessCodes":["e7565d96-a129-0d85-2e2b-c9408148d40d","cb24d3fc-9f74-9276-d690-6c64b6f2a0dc","af83a6e0-2645-bb7a-0120-486a59aff471","23a5f748-614d-fe73-0441-05e8ae1dd613","b9c932b8-8952-f61b-197f-c8289f337ddd","09f3a090-f1a5-e73e-7229-a9f616ee2418","59862316-7bcf-3129-d88c-2a5fcc448205"],"teamdriveInvitations":["6c7c1b9e-3228-0bb8-06c8-a1310fc1f345","575a30ec-0cce-1cfa-cdda-3eb706e7eea1","2fccf966-7a4c-e8b4-a53c-fc4065385a04","5616bd0c-7ac3-413b-1ea3-2e74bc145565","365c99c0-d9ce-cc77-6f5c-848e8b7c1dab","cad27e74-025b-916a-1d22-6534ce5cbae9","5327b5fa-b6b6-a236-8cbc-9988912bd94b","ca5ad554-43f0-0ee5-d9cc-a0bad5abdde6"]},{"dateCreated":"2022-01-18T18:45:56.466+01:00","description":"bnoZbpT-KG","expirationDate":"2016-11-26T10:55:35.312+01:00","id":"1b93a468-5173-c3f3-7162-1d1742b05583","lastTimeModified":"2017-03-29T06:31:39.401+02:00","memberships":["b2145c9c-c6d4-3c74-027e-b17f3a385aae","6d672e02-512f-0863-730f-a87257b660e3","1dfd87b0-b868-3eb2-8b13-e467431ecff9","a06fd2e6-2fc9-8b75-3ccd-b8042c36e2f5","14ba72c4-0451-11c7-15a3-3207e12bc274","cde6009a-a8d7-840b-c3f2-9d9ad2f33bde","732f4c84-b0bf-d898-e7d2-dab953892a00","27895022-a3ad-3a61-79fd-40343b3ca336","2b7ea0ba-61e3-e29c-b80a-c4659162edb9"],"name":"xg=Mg,[","paymentSubscriptionId":"d835d35a-38ed-4a98-cc80-df88789f599d","storageLocationId":"b8ca5c7a-ff79-3b30-3b84-760519d0f4ec","storageType":247,"teamdriveAccessCodes":["1211fc0e-6d0a-6e55-a818-c083d40f14cb","fe091e66-e0e5-94d6-60d7-804bc437c435","bb039512-fa26-54e9-dac5-b6455a32e940","8364f8e8-baf9-1443-71df-5fded5c8a8ea","6f351318-3e2e-a45a-e0a3-4150a827c002","dc14b66e-b206-742d-543d-f9cc8eb3e0e0","bd9a37ca-5d80-d9dc-4c66-9feae561fa8b","cff8a8b2-8af2-16ba-37e3-12040a88c17f"],"teamdriveInvitations":["dee5aff4-623b-fdd8-8b22-e3083af4098d","8094d8f8-46da-0801-56da-afbd415e064f","e20eff38-e93a-3d99-0516-b98aa8571e3d","dc77b3b2-faf6-eace-6814-c70158c096c5","fd04e820-9625-f08f-dbbb-295aca802079","8f5a555e-802c-149d-4e1d-cd4dae4bb06e","42a031e6-6a62-0d0a-6b72-9af43f0545b7"]},{"dateCreated":"2021-12-25T00:45:53.9+01:00","description":"!Lq2)$s","expirationDate":"2019-04-12T09:46:21.877+02:00","id":"bbe8aa06-d5d6-88d2-0e5c-5b951795b709","lastTimeModified":"2012-10-12T06:25:39.374+02:00","memberships":["f9213012-a39e-b073-3b55-087212ec3f99","9c91d1c2-2d16-8a8a-5cd8-17d416a9a0cd","2169a190-912a-c28a-fb92-e94ca31109ea","eb9367f6-0923-155d-caae-c900119bcd95","ef111920-7180-2fc4-d039-3d7de2f15da6","2b0de080-8235-1f49-7859-7487c5482e8f"],"name":"~r:%j$*A@9vD","paymentSubscriptionId":"510a79b8-cc37-c1b6-af0e-252e168d43dd","storageLocationId":"93283784-ac77-360a-708a-88d584675ebf","storageType":237,"teamdriveAccessCodes":["f1c0252c-3033-1641-d6dc-c3a02bf57c28","8d4e9ac4-b380-d7db-b623-fe7822fca2b3","611e75a4-3215-9a72-890f-2f7a23428fdd","71627a92-bede-de6a-0f56-39c8a2b29b2f","0dd9097c-e37f-d7d6-b01e-6167e49cf0ac","3d36ffda-cf14-e42b-3c52-b371b0d13997"],"teamdriveInvitations":["421eb10e-18fc-d8ea-13a8-3f595aef1831","243c6f70-b6f2-b275-5e11-18bb5df1ecc3","81a86c32-0f55-2b7b-19e5-143001c8f475","34285020-95b8-981c-454d-bea44395bb24","9d3f8214-bfee-863b-9f55-609716dba4c1","7dc021f8-9753-c994-e78e-594429ed4e3c","ae1005fa-82ca-1cbd-c6ca-c9fcb7ad5607","5c8b9bd8-7216-6b8b-9245-becf69b218d0"]},{"dateCreated":"2021-11-19T00:09:28.463+01:00","description":"*\\:3g9JXU@`>*9","expirationDate":"2021-06-17T02:58:14.654+02:00","id":"211703ea-49f6-80b0-c13c-88e590c3aa33","lastTimeModified":"2022-05-23T18:30:48.941+02:00","memberships":["41e32096-ecc5-4384-840a-007ed7a3e5d5","e90f8f16-9113-6964-a1b2-e70b47026f9f","747adaca-4d4c-6149-b35b-2e9280402c01","f5e06fb4-9aea-e64f-c0bc-3f15800d7c63","aabfa86a-d8bc-ab9e-1f44-ddda9feafa4e","a1eb8eb2-784b-f637-2356-a44f4522a4a9","e8095298-9b33-ce78-bbf0-2aca32816222","10bed056-a28c-888c-083b-e9b3a4a04005"],"name":"7lRA2l/QeN!4","paymentSubscriptionId":"106ac964-5117-8884-ebb0-d3b878eb7e67","storageLocationId":"53ab248a-9b30-4dfd-6864-907d0468e1f3","storageType":212,"teamdriveAccessCodes":["272a0b84-90ed-e1ae-c150-99c752a097f7","eb02c75e-6219-c830-f3cb-868bba790676","fecef824-9b86-06ff-cb36-dbb8970e1c7b","3a97f866-c514-1fea-f1f6-2851b614b103","55ba1bd6-7e14-02e4-c889-38e155385b47","b011eae6-921f-4628-ba84-63ea980989db"],"teamdriveInvitations":["2781e5d0-031c-d43d-d88f-6368c1f0c4fa","45cccec0-88e9-c91b-6d52-80b4b7e21ff2","15038f74-8dca-acbf-3ba2-edca7ba77979","0eb0ad04-db62-c604-b45e-51f1cf9855ea","96a8ebc2-005c-326d-bbf5-981753d0f74f","345e2468-ee17-4673-2c54-bf7af5741c81","cbf278ae-7a39-edb8-a69c-2263c413859c"]},{"dateCreated":"2021-10-12T06:43:39.438+02:00","description":"#lFRb%o99eI","expirationDate":"2016-05-12T11:05:47.029+02:00","id":"d439eb9e-c0b5-c137-d69b-fc4f84826d93","lastTimeModified":"2016-03-30T21:26:15.845+02:00","memberships":["ad936882-406f-0b34-ac07-2f3290adbd25","00fc5acc-69ba-c071-bd7c-143ee8a3f44f","93d5490e-0097-0bb9-bbe8-11690899da48","e38f9a56-a483-cd80-6b51-f6297dd12602","9becc71c-d6ca-3263-3d90-076e2faabbf1","5d9c5318-ac99-0a9c-a14a-462127a42449","b7ff42c4-862a-1bee-ef11-c146be262329","9181db06-64aa-34ee-89ab-7f61996a2f8b","f15d97d2-46e7-20e6-61af-abe25caa81dd"],"name":"(G)C6G13","paymentSubscriptionId":"9667c1e6-cec7-3429-1aa5-6cc09aa0c40a","storageLocationId":"b998f0fa-1a4f-2baf-94d4-ceaf1299dac8","storageType":120,"teamdriveAccessCodes":["b3711f32-12ee-1621-dd89-de6bec8143f4","22954392-12be-05ce-ba43-ea48bae47624","376cb11e-f31b-176a-4ccf-46994d11cb55","8c792f36-67a6-9302-56e2-10c0591c37d5","35c8a54e-7237-c247-c9f3-cdae920e9230","8ef9545a-db80-2305-c53d-0e2552343229","cc4fb2aa-8644-9e26-28eb-0837f98b59ec","609b926e-bd52-a531-8906-51ca9e7dbdc2"],"teamdriveInvitations":["d8adc202-b781-3396-1cd1-b134c803a30c","a5e89888-3c03-2bcf-cbe0-a55a1307d5a1","7ae63f64-65b1-216f-b308-d28794b36a09","2a4aecdc-c578-70f5-b8eb-65475790e8bb","b0c14184-ff48-4e0b-da85-e9f723a9bb85","36c67bd0-3c9b-cde3-f91e-a960416defa1"]}],"numPages":11,"page":1});
        }
        var options = new ResponseOptions({
            body: json,
            status: 200
        });
        const response = new Response(options);
        return Observable.of(response)
    }
}
var angularVersion;
if(window.AngularVersionForThisPlunker === 'latest'){
  angularVersion = ''; //picks up latest
}
else {
  angularVersion = '@' + window.AngularVersionForThisPlunker;
}

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    '@angular/core': 'npm:@angular/core'+ angularVersion + '/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common' + angularVersion + '/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler' + angularVersion  + '/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic' + angularVersion + '/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http' + angularVersion + '/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router' + angularVersion +'/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms' + angularVersion + '/bundles/forms.umd.js',
    '@angular/animations': 'npm:@angular/animations' + angularVersion + '/bundles/animations.umd.js',
    '@angular/platform-browser/animations': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser-animations.umd.js',
    '@angular/animations/browser': 'npm:@angular/animations' + angularVersion + '/bundles/animations-browser.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core' + angularVersion + '/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common' + angularVersion + '/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler' + angularVersion + '/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic' + angularVersion + '/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http' + angularVersion + '/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router' + angularVersion + '/bundles/router-testing.umd.js',
    'tslib': 'npm:tslib@1.6.1',
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.2.1/lib/typescript.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
.spinner {
    background-position: center center;
    background-repeat: no-repeat;
    border-radius: 50%; /* Rounds out the halo */
    opacity: .9; /* Some subtle opacity to help blend with variable background colors color: #808080 */
}

.btn-nohover:hover {
    background-color: transparent;
    border: 1px solid #ccc;
}

.btn-nohighlight:focus {
    background-color: white;
    border: 1px solid #ccc;
    outline: none;
}

.btn-nohighlight:active:focus {
    background-color: white;
    border: 1px solid #ccc;
    outline: none;
}

.btn-textbox {
    background-color: white;
    border-radius: 0;
}


.btn.btn-hover:active {
    background-color: transparent;
    border: 0;
    box-shadow: none;
}

.btn.btn-hover:focus {
    background-color: transparent;
    color: transparent;
}

.btn-hover:hover {
    background-color: transparent;
    visibility: hidden;
}

tr.show-btn-onhover:hover .btn-hover { visibility: visible; }

/*tr.show-btn-onhover:hover { cursor: pointer }*/

.progress-bar > p { font-weight: 800; }

.panel-heading-sm {
    padding-bottom: 5px;
    padding-top: 0;
}

.container-fluid { padding: 30px; }

.voffset01 { margin-top: 0.3em; }

.voffset02 { margin-top: 0.5em; }

.voffset03 { margin-top: 1em; }

.voffset04 { margin-top: 2em; }

.voffset05 { margin-top: 4em; }

.voffset06 { margin-top: 8em; }

.voffset07 { margin-top: 16em; }

.voffset08 { margin-top: 32em; }

.vboffset01 { margin-bottom: 0.3em; }

.vboffset02 { margin-bottom: 0.5em; }

.vboffset03 { margin-bottom: 1em; }

.vboffset04 { margin-bottom: 2em; }

.vboffset05 { margin-bottom: 4em; }

.vboffset06 { margin-bottom: 8em; }

.vboffset07 { margin-bottom: 16em; }

.vboffset08 { margin-bottom: 32em; }

.no-text-select {
    -moz-user-select: none;
    -ms-user-select: none;
    -webkit-user-select: none;
    user-select: none;
}

.p-error-message {
    background-color: #ffff97;
    font-family: Courier;
    font-size: 1.5em;
    text-align: center;
}

.tooltip-inner {
    background-color: #8ca568;
    border-radius: 3px;
    color: white;
}

.tooltip.bottom .tooltip-arrow { border-bottom-color: #8ca568; }

.tooltip.top .tooltip-arrow { border-top-color: #8ca568; }

.tooltip.in { opacity: 1; }

.glyphicon.glyphicon-large { font-size: 14pt; }

.row.unauthorized {
    align-items: center;
    display: flex;
    min-height: 90%;
    min-height: 90vh;
}

.row.unauthorized > div {
    border: #d9534f solid 4px;
    margin-left: 30%;
    margin-right: 30%;
    padding: 50px;
    text-align: center;
}

.jumbotron {
    margin-left: 3em;
    margin-right: 3em;
    margin-top: 4em;
}
.grid tbody {
    -moz-animation-delay: 100ms;
    -moz-animation-duration: 300ms;
    -o-animation-delay: 100ms;
    -o-animation-duration: 300ms;
    -webkit-animation-delay: 100ms;
    -webkit-animation-duration: 300ms;
    animation-delay: 100ms;
    animation-duration: 300ms;
}

.grid tbody tr td {
    margin: .3em .1em .3em .1em;
    padding: .3em .3em .3em .3em;
}

table { border-collapse: initial; }

tbody tr.tr-selected td {
    border-bottom: .25em;
    border-bottom-color: #8ca568;
    border-bottom-style: solid;
    border-left: 0;
    border-right: 0;
    border-top: .25em;
    border-top-color: #8ca568;
    border-top-style: solid;
}

tbody tr.tr-selected td:first-of-type {
    border-left: .25em;
    border-left-color: #8ca568;
    border-left-style: solid;
}

tbody tr.tr-selected td:last-of-type {
    border-right: .25em;
    border-right-color: #8ca568;
    border-right-style: solid;
}

tbody tr.tr-selected:hover { border-width: .25em .5em .25em .5em; }

.vertical-align {
    align-items: center;
    display: flex;
}

.table-hover tbody tr:hover td:last-child { visibility: visible; }


a.th-anchor {
    -moz-user-select: none;
    -ms-user-select: none;
    -webkit-user-select: none;
    color: black;
    font-size: 11pt;
    text-decoration: none;
    user-select: none;
}

a.th-anchor :hover { color: black; }

a.th-anchor :focus { color: black; }

td.btn.btn-hover { width: 0; }

th:hover:not(:last-of-type) {
    background: #f2f3f5;
    border-radius: .3em;
    cursor: pointer;
}

item-details .btn-default {
  width: 12em;
}

item-details td:nth-of-type(1) {
  font-weight: bold;
  padding-right: 1em;
}

item-details td { height: 2em; }

.btn-hover {
    border: 0;
    text-align: left;
    visibility: hidden;
}

.row.row-separator {
    border-bottom: 0;
    border-color: #2d3959;
    border-left: 0;
    border-right: 0;
    border-style: solid;
    margin-top: 0em;
    padding-top: 1em;
}

.btn.btn-delete {
    background-color: white;
    border-color: gray;
    color: black
}

.btn.btn-delete:hover {
    background-color: #c9302c;
    border-color: #2d3959;
    color: white;
}

.btn.btn-row-details {
    background-color: transparent;
    border: 0;
    border-color: transparent;
    font-size: 12pt;
    line-height: 0;
    margin: 0;
    padding: 0;
    vertical-align: inherit;
}

.btn.btn-row-details:hover {
    background-color: transparent;
    border: 0;
    border-color: transparent;
    margin: 0;
    padding: 0;
}

.btn.btn-row-details:active {
    border-color: transparent;
    box-shadow: none
}

.btn.btn-row-details:focus {
    border-color: transparent;
    outline: none
}

.btn.btn-save-detail { width: 2.8em; }

.panel-heading .btn-group .btn.btn-default:last-of-type {
    border-bottom-right-radius: 4px;
    /*    border-radius: 4px !important;*/
    border-top-right-radius: 4px;
}

input:disabled[type=checkbox] {
    background-color: White;
    border: solid 1px black;
    color: black;
}

.img-rounded {
    border: solid #2d3959 4px;
    max-width: 100%
}

.btn.btn-user-toggle:hover {
    background-color: orange;
    color: black;
}
.navbar {
    background-color: #2d3959;
    border-bottom-color: white;
    border-bottom-style: solid;
    border-bottom-width: 3px;
}

.navbar-nav li {
    display: table-cell;
    float: none;
}

.navbar-inverse .navbar-nav > .open > a:focus {
    background-color: #8ca568;
    border-color: #2d3959;
}

.navbar-inverse .navbar-nav.navbar-right > .open > a:focus {
    background-color: #626b83;
    border-color: #2d3959;
}

ul.nav.navbar-nav.navbar-right li:hover { background-color: #2d3959; }

ul.nav.navbar-nav.navbar-right > li:not(.dropdown) a:hover { color: lightgray; }

.navbar-inverse .navbar-nav > li > a { color: white; }

ul.nav.navbar-nav li:hover { background-color: #626b83; }

.navbar .container-fullwidth {
    margin-left: 3em;
    margin-right: 3em;
}

.navbar-brand { padding-left: 0; }


.open > .dropdown-menu { display: grid; }

.navbar .form-group {
    display: inline-flex;
    margin-bottom: 0;
}

.navbar .form-group input { margin-right: 1em; }

.navbar li .btn { display: block }
@-webkit-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@-webkit-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@-moz-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@-ms-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@-moz-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@-webkit-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@-o-keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

@keyframes spinloader {
    0% {
        -moz-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        -o-transform: rotate(0deg);
        -webkit-transform: rotate(0deg);
        transform: rotate(0deg);
    }

    50% {
        -moz-transform: rotate(180deg);
        -ms-transform: rotate(180deg);
        -o-transform: rotate(180deg);
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }

    100% {
        -moz-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        -o-transform: rotate(360deg);
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
    }
}

.spinloader {
    height: 50%;
    width: 50%;
}

.spinloader > div {
    -moz-animation: spinloader 1s linear infinite;
    -ms-animation: spinloader 1s linear infinite;
    -o-animation: spinloader 1s linear infinite;
    -webkit-animation: spinloader 1s linear infinite;
    animation: spinloader 1s linear infinite;
    border: 20px solid #2d3959;
    border-bottom: 20px solid #2d3959;
    border-radius: 100px;
    border-right: 20px solid #2d3959;
    border-top: 20px solid rgba(0, 0, 0, 0);
    display: block;
    height: 120px;
    left: 50%;
    position: relative;
    width: 120px;
}

.spinloader > div:after {
    -moz-transform: translate(-15px, 0) rotate(45deg);
    -ms-transform: translate(-15px, 0) rotate(45deg);
    -o-transform: translate(-15px, 0) rotate(45deg);
    -webkit-transform: translate(-15px, 0) rotate(45deg);
    border-color: transparent transparent #2d3959 transparent;
    border-style: solid;
    border-width: 0 25px 25px 25px;
    content: " ";
    display: block;
    height: 0px;
    transform: translate(-17px, -10px) rotate(45deg);
    width: 0px;
}

.spinloader {
    margin-bottom: 2em;
}
.btn-success {
    background-color: #8ca568;
    border-color: #8ca568;
}

.jumbotron {
    background-color: #2d3959;
    color: white;
}

.panel-theme {
    border-color: #2d3959;
    border-radius: 5px;
    border-style: solid;
    border-width: 3px;
}

.panel-theme .panel-heading {
    background-color: #2d3959;
    border-color: #2d3959;
    border-radius: 0;
    color: white;
}

.background-color-active { background-color: #8ca568; }

.panel-theme .panel-body {
    padding-bottom: 0;
}
<navbar></navbar>
<div class="container-fluid">
    <div class="row">
        <div class="col-lg-12">
            <div id="Alert"></div>
        </div>
    </div>
    <router-outlet></router-outlet>
</div>
<div *ngIf="item" style="margin-bottom: 2em">
    <div class="row voffset04 row-separator">
        <div class="col-lg-2 col-lg-offset-10"
             [@detailsFadeInOut]="fadeInOutStatus">
            <div class="btn btn-delete btn-xs pull-right">
                <span class="glyphicon glyphicon-trash"></span>
            </div>
        </div>
    </div>
    <div class="row"
         [@detailsFadeInOut]="fadeInOutStatus"
         (@detailsFadeInOut.done)="setFadeStatus($event)">
        <div class="col-lg-4 col-lg-offset-2">
            <table align="right">
                <tr>
                    <td>Drive ID: </td>
                    <td>{{item?.id || 'None'}}</td>
                </tr>
                <tr class="show-btn-onhover">
                    <td>Owner: </td>
                    <td *ngIf="!ownerInput">
                        <a>{{item?.owner || 'None'}}</a>
                    </td>
                    <td *ngIf="!ownerInput"
                        class="btn btn-hover btn-default"
                        (click)="ownerInput=true">
                        <span class="glyphicon glyphicon-pencil"></span>
                    </td>
                    <td *ngIf="ownerInput">
                    <div class="input-group">
                        <input type="text" class="form-control" required
                               data-toggle="tooltip"
                               data-placement="bottom"
                               title="Enter a valid user ID or E-mail address."
                               [(ngModel)]="owner"
                               name="owner" #name="ngModel"
                               placeholder="{{item?.owner}}">
                        <span class="input-group-btn">
                            <button class="btn btn-save-detail btn-default"
                                    type="button"
                                    (click)="saveOwner()">
                                <span class="glyphicon glyphicon-floppy-disk"></span>
                            </button>
                        </span>
                    </div>
                </tr>
                <tr class="show-btn-onhover">
                    <td>Description: </td>
                    <td *ngIf="!descriptionInput">{{item?.description || 'None'}}</td>
                    <td *ngIf="!descriptionInput"
                        class="btn btn-hover btn-default"
                        (click)="descriptionInput=true">
                        <span class="glyphicon glyphicon-pencil"></span>
                    </td>
                    <td *ngIf="descriptionInput">
                    <div class="input-group">
                        <input type="text" class="form-control"
                               data-toggle="tooltip"
                               data-placement="bottom"
                               [(ngModel)]="description"
                               name="description" #name="ngModel"
                               title="Enter some text"
                               placeholder="{{item?.description}}">
                        <span class="input-group-btn">
                            <button class="btn btn-save-detail btn-default"
                                    type="button"
                                    (click)="saveDescription()">
                                <span class="glyphicon glyphicon-floppy-disk"></span>
                            </button>
                        </span>
                    </div>
                </tr>
                <tr class="show-btn-onhover">
                    <td>Organization: </td>
                    <td *ngIf="!organizationInput">{{item?.organizationId || 'None'}}</td>
                    <td *ngIf="!organizationInput"
                        class="btn btn-hover btn-default"
                        (click)="organizationInput=true">
                        <span class="glyphicon glyphicon-pencil"></span>
                    </td>
                    <td *ngIf="organizationInput">
                    <div class="input-group">
                        <input type="text" class="form-control"
                               data-toggle="tooltip"
                               data-placement="bottom"
                               title="Enter a valid organization ID"
                               placeholder="{{item?.organizationId}}">
                        <span class="input-group-btn">
                            <button class="btn btn-save-detail btn-default"
                                    type="button"
                                    (click)="saveOrganization()">
                                <span class="glyphicon glyphicon-floppy-disk"></span>
                            </button>
                        </span>
                    </div>
                </tr>
                <tr>
                    <td>Storage Location: </td>
                    <td>{{item?.storageLocationId || 'None'}}</td>
                </tr>
            </table>
        </div>
        <div class="col-lg-2 pull-left">
            <div class="row">
                <div class="col-lg-12">
                    <div class="btn btn-default" 
                         data-toggle="modal" 
                         data-target="#subscription-modal">
                        <span class="glyphicon glyphicon-credit-card glyphicon-large pull-left"></span> Payment
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-lg-12">
                    <div class="btn btn-default" 
                         data-toggle="modal"
                         data-target="#drive-members-modal"
                         (click)="onMembersModalButton()">
                        <span class="glyphicon glyphicon-user glyphicon-large pull-left"></span> Members
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-lg-12">
                    <div class="btn btn-default">
                        <span class="glyphicon glyphicon-envelope glyphicon-large pull-left"></span> Invitations
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-lg-12">
                    <div class="btn btn-default">
                        <span class="glyphicon glyphicon-qrcode glyphicon-large pull-left"></span> Access Codes
                    </div>
                </div>
            </div>
        </div>
        <div class="col-lg-2 pull-left">
            <div class="row">
                <div class="col-lg-12">
                    <div class="btn btn-default" disabled data-toggle="modal" data-target="#subscription-modal">
                        <span class="glyphicon glyphicon glyphicon-save glyphicon-large pull-left"></span> Backups
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<tr *ngFor="let drive of items; let i = index"
    [ngClass]="{'tr-selected' : selectedItemId==drive.id}">
    <td nowrap>
        <span>{{drive.id}}</span>
    </td>
    <td nowrap>
        <span>{{drive.name}}</span>
    </td>
    <td nowrap>
        <span>{{drive.dateCreated}}</span>
    </td>
    <td nowrap>
        <span>{{drive.expirationDate}}</span>
    </td>
    <td nowrap class="text-center">
        <span class="badge">{{drive.memberships?.length}}</span>
    </td>
    <td nowrap class="text-center">
        <span class="badge ">{{drive.teamdriveInvitations?.length}}</span>
    </td>
    <td nowrap>
        <div class="pull-right">
            <button (click)="onDetailsButton(i); false"
                    [@detailsToggled]="toggleIconState(i)"
                    class="btn btn-row-details btn-xs btn-default glyphicon glyphicon-collapse-down"
                    type="button">
            </button>
        </div>
    </td>
</tr>
<tr>
    <th *ngFor="let entry of columns | dict" (click)="onColumnHeaderToggled(entry.key)">
        <a class="th-anchor">
            <span *ngIf="entry.key===activeColumn" [ngClass]="(listOrder==1)?'glyphicon glyphicon-sort-by-attributes':'glyphicon glyphicon-sort-by-attributes-alt'"></span>
            {{entry.key}}
        </a>
    </th>
</tr>
<div class="modal fade" id="drive-members-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">Members of 'NAME'</h4>
            </div>
            <div class="modal-body">
                <div class="row">
                    <div class="col-lg-12"
                         user-grid
                         (onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
                         (onDetailsButtonEmitter)="onDetailsButton($event)">
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">
                        <span class="glyphicon glyphicon-chevron-left"></span> Back
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row">
    <div class="col-lg-12"
         id="drive-members-modal"
         drive-members-modal>
        <!-- (onMembersModalButtonEmitter)="loadMembersModal()">-->
    </div>
</div>
<div class="row voffset06">
    <div class="col-lg-12">
        <div class="panel panel-theme">
            <div class="panel-heading">
                <div class="row">
                    <div class="col-lg-2 pull-right">
                        <div class="btn-group pull-right">
                            <button class="btn btn-default" type="button"
                                    *ngIf="pager.buttons"
                                    (click)="resetView()"
                                    data-toggle="tooltip"
                                    data-placement="bottom"
                                    title="Reset View">
                                <span class="glyphicon glyphicon-repeat"></span>
                            </button>
                            <button class="btn btn-default" type="button"
                                    (click)="onPageButton(pager.page)"
                                    data-toggle="tooltip"
                                    data-placement="bottom"
                                    title="Refresh Results">
                                <span class="glyphicon glyphicon-refresh"></span>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            <div class="panel-body">
                <div class="row text-center animated"
                     [ngClass]="{'fadeIn': messages, 'fadeOut': !messages}"
                     *ngIf="messages">
                    <div class="col-lg-12">
                        <h2 style="margin-bottom: 1.5em">{{messageTitle}}</h2>
                        <p *ngFor="let message of messages" class="p-error-message">{{message}}</p>
                    </div>
                </div>
                <div class="row text-center voffset04"
                     *ngIf="!messages && !grid.body?.items">
                    <div class="col-lg-12 voffset05 vboffset05">
                        <div class="spinloader">
                            <div style="margin: auto;"></div>
                        </div>
                        <p style="color: #2d3959; font-size: 1.5em; font-weight: bolder;">Loading...</p>
                    </div>
                </div>
                <div class="row" [hidden]="!grid.body?.items">
                    <div class="col-lg-12"
                         drive-grid
                         (onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
                         (onDetailsButtonEmitter)="onDetailsButton($event)">
                    </div>
                </div>
                <div class="row"
                     id="row-grid-item-details">
                    <div class="col-lg-12"
                         [@detailsSlideUpDown]="itemDetails.fadeInOutStatus">
                        <item-details (onOpenModalEmitter)="onDriveMembersButton()"></item-details>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<!--<div class="row voffset04" style="display: inline-block; float: none; vertical-align: middle;">-->
<div *ngIf="errorType==0" class="row unauthorized voffset04">
  <div class="col-lg-12">
    <h1>
      <span class="glyphicon glyphicon-warning-sign"></span> Unknown Error!
    </h1>
    <p style="font-size: 13pt">Some unexpected error occurred.</p>
  </div>
</div>
<div *ngIf="errorType==1"class="row unauthorized voffset04">
    <div class="col-lg-12">
        <h1>
            <span class="glyphicon glyphicon-warning-sign"></span> Not Authenticated!
        </h1>
        <p style="font-size: 13pt">Please log in to continue. Use the navigation bar's login-button.</p>
    </div>
</div>
<div *ngIf="errorType==2" class="row unauthorized voffset04">
    <div class="col-lg-12">
        <h1>
            <span class="glyphicon glyphicon-warning-sign"></span> Unauthorized!
        </h1>
        <p style="font-size: 13pt">You do not have sufficient rights to perform this action.</p>
    </div>
</div>
<div *ngIf="errorType==3" class="row unauthorized voffset04">
    <div class="col-lg-12">
        <h1>
            <span class="glyphicon glyphicon-warning-sign"></span> Internal Sever Error!
        </h1>
        <p style="font-size: 13pt">Message: {{message}}.</p>
    </div>
</div>
<div *ngIf="errorType==4" class="row unauthorized voffset04">
  <div class="col-lg-12">
    <h1>
      <span class="glyphicon glyphicon-warning-sign"></span> The requested resource was not found!
    </h1>
    <p style="font-size: 13pt">Nothing to see here.</p>
  </div>
</div>
<div class="table-responsive">
  <table class="grid table table-hover">
    <thead grid-header (onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"></thead>
    <tbody grid-body (onDetailsButtonEmitter)="onDetailsButton($event)"
           [ngClass]="{'fadeIn': body?.items, 'fadeOut': !body?.items}"
           class="animated"></tbody>
  </table>
</div>

<div class="row voffset04">
  <div class="col-lg-12">
    <div class="jumbotron">
      <h1>Awesome Webapp</h1>
      <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea
        takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores
        et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
      <p>
        <a class="btn btn-success btn-lg" href="#" role="button">Request Account</a>
      </p>
    </div>
  </div>
</div>
<router-outlet></router-outlet>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fullwidth">
        <div class="navbar-header">
            <a class="navbar-brand" href="">WebApp</a>
        </div>
        <div id="navbar" class="navbar-collapse">
            <ul class="nav navbar-nav">
                <li [routerLinkActive]="['background-color-active']">
                    <a routerLink="/home" class="glyphicon glyphicon-home"></a>
                </li>
                <li [routerLinkActive]="['background-color-active']">
                    <a routerLink="/user">Users</a>
                </li>
                <li [routerLinkActive]="['background-color-active']">
                    <a routerLink="/drive">Drives</a>
                </li>
            </ul>
        </div><!--/.nav-collapse -->
    </div>
</nav>
<div *ngIf="item" style="margin-bottom: 2em">
    <div class="row voffset04 row-separator">
        <div class="col-lg-2 col-lg-offset-10"
             [@detailsFadeInOut]="fadeInOutStatus">
            <div class="btn btn-delete btn-xs pull-right">
                <span class="glyphicon glyphicon-trash"></span>
            </div>
        </div>
    </div>
    <div class="row"
         [@detailsFadeInOut]="fadeInOutStatus"
         (@detailsFadeInOut.done)="setFadeStatus($event)">
        <div class="col-lg-2 col-lg-offset-1">
            <div class="row">
                <div class="col-lg-12">
                    <img src="https://maxcdn.icons8.com/Share/icon/Users//user1600.png" class="img-rounded">
                </div>
            </div>
            <div class="row voffset01">
                <div class="col-lg-12">
                    <div class="btn btn-xs btn-user-toggle"
                         [ngClass]="item.isApproved? 'btn-success' : 'btn-danger'">
                        {{item.isApproved? 'Approved' : 'Not Approved'}}
                    </div>
                  <div class="btn btn-xs btn-user-toggle"
                       [ngClass]="item.isEnabled? 'btn-success' : 'btn-danger'">
                    {{item.isEnabled? 'Enabled' : 'Disabled'}}
                  </div>
                </div>
            </div>
        </div>
        <div class="col-lg-4">
            <table>
                <tr>
                    <td>User ID: </td>
                    <td>{{item?.id || 'None'}}</td>
                </tr>
                <tr class="show-btn-onhover">
                    <td>Email: </td>
                    <td *ngIf="!emailInput">
                        {{item?.email || 'None'}}
                    </td>
                    <td *ngIf="!emailInput"
                        class="btn btn-hover btn-default"
                        (click)="emailInput=true">
                        <span class="glyphicon glyphicon-pencil"></span>
                    </td>
                    <td *ngIf="emailInput">
                    <div class="input-group">
                        <input type="text" class="form-control" required
                               data-toggle="tooltip"
                               data-placement="bottom"
                               title="Enter a valid E-mail address."
                               [(ngModel)]="email"
                               name="email" #name="ngModel"
                               placeholder="{{item?.email}}">
                        <span class="input-group-btn">
                            <button class="btn btn-save-detail btn-default"
                                    type="button"
                                    (click)="saveEmail()">
                                <span class="glyphicon glyphicon-floppy-disk"></span>
                            </button>
                        </span>
                    </div>
                </tr>
                <tr>
                    <td>Display Name: </td>
                    <td>{{item?.displayName || 'None'}}</td>
                </tr>
                <tr class="show-btn-onhover">
                    <td>Comment: </td>
                    <td *ngIf="!commentInput">{{item?.comment || 'None'}}</td>
                    <td *ngIf="!commentInput"
                        class="btn btn-hover btn-default"
                        (click)="commentInput=true">
                        <span class="glyphicon glyphicon-pencil"></span>
                    </td>
                    <td *ngIf="commentInput">
                    <div class="input-group">
                        <input type="text" class="form-control"
                               data-toggle="tooltip"
                               data-placement="bottom"
                               [(ngModel)]="comment"
                               name="comment" #name="ngModel"
                               title="Enter some text"
                               placeholder="{{item?.comment}}">
                        <span class="input-group-btn">
                            <button class="btn btn-save-detail btn-default"
                                    type="button"
                                    (click)="saveComment()">
                                <span class="glyphicon glyphicon-floppy-disk"></span>
                            </button>
                        </span>
                    </div>
                </tr>
                <tr>
                    <td>About: </td>
                    <td>{{item?.about || 'None'}}</td>
                </tr>
                <tr>
                    <td>Language: </td>
                    <td>{{item?.twoLetterIsoLanguage || 'None'}}</td>
                </tr>
                <tr class="show-btn-onhover">
                    <td>Organization: </td>
                    <td *ngIf="!organizationInput">{{item?.organizationId || 'None'}}</td>
                    <td *ngIf="!organizationInput"
                        class="btn btn-hover btn-default"
                        (click)="organizationInput=true">
                        <span class="glyphicon glyphicon-pencil"></span>
                    </td>
                    <td *ngIf="organizationInput">
                    <div class="input-group">
                        <input type="text" class="form-control"
                               data-toggle="tooltip"
                               data-placement="bottom"
                               title="Enter a valid organization ID"
                               placeholder="{{item?.organizationId}}">
                        <span class="input-group-btn">
                            <button class="btn btn-save-detail btn-default"
                                    type="button"
                                    (click)="saveOrganization()">
                                <span class="glyphicon glyphicon-floppy-disk"></span>
                            </button>
                        </span>
                    </div>
                </tr>
            </table>
        </div>
        <div class="col-lg-4">
            <table>
                <tr>
                    <td>Facebook ID: </td>
                    <td>{{item?.facebookId || 'None'}}</td>
                </tr>
                <tr>
                    <td>Google ID: </td>
                    <td>{{item?.googleId || 'None'}}</td>
                </tr>
                <tr>
                    <td>Registration Date: </td>
                    <td>{{item?.registrationDate || 'None'}}</td>
                </tr>
                <tr>
                    <td>Approval Date: </td>
                    <td>{{item?.ApprovalDate || 'None'}}</td>
                </tr>
                <tr>
                    <td>Last Login: </td>
                    <td>{{item?.lastLoginWithPassword || 'None'}}</td>
                </tr>
                <tr>
                    <td></td>
                    <td></td>
                </tr>
            </table>
        </div>
    </div>
    <div class="row voffset02">
        <div class="col-lg-12 col-lg-offset-3">
            <div class="btn btn-default" data-toggle="modal" data-target="#subscription-modal">
                <span class="glyphicon glyphicon-tags glyphicon-large"></span> Profile
            </div>
            <div class="btn btn-default" data-toggle="modal" data-target="#subscription-modal">
                <span class="glyphicon glyphicon glyphicon-wrench glyphicon-large"></span> Settings
            </div>
            <div class="btn btn-default">
                <span class="glyphicon glyphicon glyphicon-hdd glyphicon-large"></span> Drives
            </div>
            <div class="btn btn-default">
                <span class="glyphicon glyphicon-lock glyphicon-large"></span> Reset Password
            </div>
        </div>
    </div>
</div>
<tr *ngFor="let user of items; let i = index"
    [ngClass]="{'tr-selected' : selectedItemId==user.id}">
    <td nowrap class="text-center">
        <span class="glyphicon" [ngClass]="user.isEnabled? 'glyphicon-ok': 'glyphicon-remove'">
        </span>
    </td>
    <td nowrap>
        <span>{{user.email}}</span>
    </td>
    <td nowrap>
        <span>{{user.registrationDate}}</span>
    </td>
    <td nowrap>
        <span>{{user.approvalDate}}</span>
    </td>
    <td nowrap>
        <span>{{user.lastLoginWithPassword}}</span>
    </td>
    <td nowrap class="text-center">
        <span class="badge">{{user.failedLoginCount}}</span>
    </td>
    <td nowrap>
        <span>{{user.organizationId}}</span>
    </td>
    <td nowrap>
        <div class="pull-right">
            <button (click)="onDetailsButton(i); false"
                    [@detailsToggled]="toggleIconState(i)"
                    class="btn btn-row-details btn-xs btn-default glyphicon glyphicon-collapse-down"
                    type="button">
            </button>
        </div>
    </td>
</tr>
<tr>
    <th *ngFor="let entry of columns | dict" (click)="onColumnHeaderToggled(entry.key)">
        <a class="th-anchor">
            <span *ngIf="entry.key===activeColumn" [ngClass]="(listOrder==1)?'glyphicon glyphicon-sort-by-attributes':'glyphicon glyphicon-sort-by-attributes-alt'"></span>
            {{entry.key}}
        </a>
    </th>
</tr>
<div class="row voffset06">
    <div class="col-lg-12">
        <div class="panel panel-theme">
            <div class="panel-heading">
                <div class="row">
                    <div class="col-lg-2 pull-right">
                        <div class="btn-group pull-right">
                            <button class="btn btn-default" type="button"
                                    *ngIf="pager.buttons"
                                    (click)="onResetViewButton()"
                                    data-toggle="tooltip"
                                    data-placement="bottom"
                                    title="Reset View">
                                <span class="glyphicon glyphicon-repeat"></span>
                            </button>
                            <button class="btn btn-default" type="button"
                                    (click)="onPageButton(pager.page)"
                                    data-toggle="tooltip"
                                    data-placement="bottom"
                                    title="Refresh Results">
                                <span class="glyphicon glyphicon-refresh"></span>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            <div class="panel-body">
                <div class="row text-center animated"
                     [ngClass]="{'fadeIn': messages, 'fadeOut': !messages}"
                     *ngIf="messages">
                    <div class="col-lg-12">
                        <h2 style="margin-bottom: 1.5em">{{messageTitle}}</h2>
                        <p *ngFor="let message of messages" class="p-error-message">{{message}}</p>
                    </div>
                </div>
                <div class="row text-center voffset04"
                     *ngIf="!messages && !grid.body?.items">
                    <div class="col-lg-12 voffset05 vboffset05">
                        <div class="spinloader">
                            <div style="margin: auto;"></div>
                        </div>
                        <p style="color: #2d3959; font-size: 1.5em; font-weight: bolder;">Loading...</p>
                    </div>
                </div>
                <div class="row" [hidden]="!grid.body?.items">
                    <div class="col-lg-12" 
                         user-grid
                         (onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
                         (onDetailsButtonEmitter)="onDetailsButton($event)"></div>
                </div>
                <div class="row"
                     id="row-grid-item-details">
                    <div class="col-lg-12"
                         [@detailsSlideUpDown]="itemDetails.fadeInOutStatus">
                        <item-details></item-details>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<!DOCTYPE html>
<html>

<head>
  <title>ViewChild Plunker</title>
  <base href="." />
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="icon" type="image/png" href="img/favicon.png" />
  <script type="text/javascript" charset="utf-8">
    window.window.AngularVersionForThisPlunker = 'latest'
  </script>

  <script data-require="jquery@3.1.1" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  <link data-require="bootstrap@3.3.7" data-semver="3.3.7" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
  <link data-require="animate.css@3.2.0" data-semver="3.2.0" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.0/animate.min.css" />

  <link href="css/theme.css" rel="stylesheet" />
  <link href="./css/custom.css" rel="stylesheet" />
  <link href="./css/spinloader.css" rel="stylesheet" />
  <link href="./css/grid.css" rel="stylesheet" />

  <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
  <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
  <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
  <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
  <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
  <script src="config.js"></script>

  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <script>
           System.import('main.js').catch(function(err) { console.error(err); });

  </script>
</head>

<body>
  <app></app>
</body>

</html>
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app/core/app.module.ts';

platformBrowserDynamic().bootstrapModule(AppModule)