Added in panel support - details panels

Change-Id: I2803edd6fe12cb0d97a2d3c45a692ea701786dd2
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
new file mode 100644
index 0000000..79fa310
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
+import { DialogService } from '../../../fw/layer/dialog.service';
+import { FnService } from '../../../fw/util/fn.service';
+import { IconService } from '../../../fw/svg/icon.service';
+import { KeyService } from '../../../fw/util/key.service';
+import { LionService } from '../../../fw/util/lion.service';
+import { LoadingService } from '../../../fw/layer/loading.service';
+import { LogService } from '../../../log.service';
+import { TableBaseImpl, TableResponse, TableFilter, SortParams, SortDir } from '../../../fw/widget/table.base';
+import { UrlFnService } from '../../../fw/remote/urlfn.service';
+import { WebSocketService } from '../../../fw/remote/websocket.service';
+import { TableFilterPipe } from '../../../fw/widget/tablefilter.pipe';
+
+const INSTALLED = 'INSTALLED';
+const ACTIVE = 'ACTIVE';
+const appMgmtReq = 'appManagementRequest';
+const topPdg = 60;
+const panelWidth = 540;
+const pName = 'application-details-panel';
+const detailsReq = 'appDetailsRequest';
+const detailsResp = 'appDetailsResponse';
+const fileUploadUrl = 'applications/upload';
+const activateOption = '?activate=true';
+
+/** Prefix to access the REST service for applications */
+export const APPURLPREFIX = '../../ui/rs/applications/'; // TODO: This is a hack to work off GUIv1 URL
+/** Suffix to access the icon of the application - gives back an image */
+export const ICONURLSUFFIX = '/icon';
+
+const downloadSuffix = '/download';
+const dialogId = 'app-dialog';
+const dialogOpts = {
+    edge: 'right',
+    width: 400,
+};
+const strongWarning = {
+    'org.onosproject.drivers': true,
+};
+const propOrder = ['id', 'state', 'category', 'version', 'origin', 'role'];
+
+/**
+ * Model of the data returned through the Web Socket about apps.
+ */
+interface AppTableResponse extends TableResponse {
+    apps: App[];
+}
+
+/**
+ * Model of the data returned through Web Socket for a single App
+ */
+export interface App {
+    category: string;
+    desc: string;
+    features: string[];
+    icon: string;
+    id: string;
+    origin: string;
+    permissions: string[];
+    readme: string;
+    required_apps: string[];
+    role: string;
+    state: string;
+    title: string;
+    url: string;
+    version: string;
+    _iconid_state: string;
+}
+
+/**
+ * Model of the Control Button
+ */
+interface CtrlBtnState {
+    installed: boolean;
+    selection: string;
+    active: boolean;
+}
+
+/**
+ * ONOS GUI -- Apps View Component
+ */
+@Component({
+  selector: 'onos-apps',
+  templateUrl: './apps.component.html',
+  styleUrls: [
+    './apps.component.css', './apps.theme.css',
+    '../../../fw/widget/table.css', '../../../fw/widget/table.theme.css'
+    ]
+})
+export class AppsComponent extends TableBaseImpl implements OnInit, OnDestroy {
+
+    // deferred localization strings
+    lionFn; // Function
+    warnDeactivate: string;
+    warnOwnRisk: string;
+    ctrlBtnState: CtrlBtnState;
+    detailsPanel: any;
+    appFile: any;
+    activateImmediately = '';
+
+    uploadTip: string;
+    activateTip: string;
+    deactivateTip: string;
+    uninstallTip: string;
+    downloadTip: string;
+
+    constructor(
+        protected fs: FnService,
+        private ds: DialogService,
+        private is: IconService,
+        private ks: KeyService,
+        private lion: LionService,
+        protected ls: LoadingService,
+        protected log: LogService,
+        private ufs: UrlFnService,
+        protected wss: WebSocketService,
+        @Inject('Window') private window: Window,
+    ) {
+        super(fs, null, log, wss, 'app');
+        this.responseCallback = this.appResponseCb;
+        // pre-populate sort so active apps are at the top of the list
+        this.sortParams = {
+            firstCol: 'state',
+            firstDir: SortDir.desc,
+            secondCol: 'title',
+            secondDir: SortDir.asc,
+        };
+        // We want doLion() to be called only after the Lion
+        // service is populated (from the WebSocket)
+        // If lion is not ready we make do with a dummy function
+        // As soon a lion gets loaded this function will be replaced with
+        // the real thing
+        if (this.lion.ubercache.length === 0) {
+            this.lionFn = this.dummyLion;
+            this.lion.loadCbs.set('apps', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+
+        this.ctrlBtnState = <CtrlBtnState>{
+            installed: false,
+            active: false
+        };
+    }
+
+    /**
+     * Initialize querying the WebSocket for App table details
+     */
+    ngOnInit() {
+        this.init();
+    }
+
+    /**
+     * Stop sending queries to WebSocket
+     */
+    ngOnDestroy() {
+        this.lion.loadCbs.delete('apps');
+        this.destroy();
+        this.log.debug('AppComponent destroyed');
+    }
+
+    /**
+     * The callback called when App data returns from WSS
+     */
+    appResponseCb(data: AppTableResponse) {
+        this.log.debug('App response received for ', data.apps.length, 'apps');
+    }
+
+    refreshCtrls() {
+        let row;
+        let rowIdx;
+        if (this.ctrlBtnState.selection) {
+            rowIdx = this.fs.find(this.selId, this.tableData);
+            row = rowIdx >= 0 ? this.tableData[rowIdx] : null;
+
+            this.ctrlBtnState.installed = row && row.state === INSTALLED;
+            this.ctrlBtnState.active = row && row.state === ACTIVE;
+        } else {
+            this.ctrlBtnState.installed = false;
+            this.ctrlBtnState.active = false;
+        }
+    }
+
+    createConfirmationText(action, itemId) {
+//        let content = this.ds.createDiv();
+//        content.append('p').text(this.lionFn(action) + ' ' + itemId);
+//        if (strongWarning[itemId]) {
+//            content.append('p').html(
+//                this.fs.sanitize(this.warnDeactivate) +
+//                '<br>' +
+//                this.fs.sanitize(this.warnOwnRisk)
+//            ).classed('strong', true);
+//        }
+//        return content;
+    }
+
+    confirmAction(action): void {
+        const itemId = this.selId;
+        const spar = this.sortParams;
+
+        function dOk() {
+            this.log.debug('Initiating', action, 'of', itemId);
+            this.wss.sendEvent(appMgmtReq, {
+                action: action,
+                name: itemId,
+                sortCol: spar.firstCol,
+                sortDir: spar.firstDir,
+            });
+            if (action === 'uninstall') {
+                this.detailsPanel.hide();
+            } else {
+                this.wss.sendEvent(detailsReq, { id: itemId });
+            }
+        }
+
+        function dCancel() {
+            this.log.debug('Canceling', action, 'of', itemId);
+        }
+
+//        this.ds.openDialog(dialogId, dialogOpts)
+//            .setTitle(this.lionFn('dlg_confirm_action'))
+//            .addContent(this.createConfirmationText(action, itemId))
+//            .addOk(dOk)
+//            .addCancel(dCancel)
+//            .bindKeys();
+    }
+
+    appAction(action) {
+        if (this.ctrlBtnState.selection) {
+            this.confirmAction(action);
+        }
+    }
+
+    downloadApp() {
+        if (this.ctrlBtnState.selection) {
+            (<any>this.window).location = APPURLPREFIX + this.selId + ICONURLSUFFIX;
+        }
+    }
+
+    /**
+     * Read the LION bundle for App and set up the lionFn
+     */
+    doLion() {
+        this.lionFn = this.lion.bundle('core.view.App');
+
+        this.warnDeactivate = this.lionFn('dlg_warn_deactivate');
+        this.warnOwnRisk = this.lionFn('dlg_warn_own_risk');
+
+        this.uploadTip = this.lionFn('tt_ctl_upload');
+        this.activateTip = this.lionFn('tt_ctl_activate');
+        this.deactivateTip = this.lionFn('tt_ctl_deactivate');
+        this.uninstallTip = this.lionFn('tt_ctl_uninstall');
+        this.downloadTip = this.lionFn('tt_ctl_download');
+    }
+
+    appDropped() {
+        this.activateImmediately = activateOption;
+//        $scope.$emit('FileChanged'); // TODO: Implement this
+        this.appFile = null;
+    }
+}