Bläddra i källkod

Merge branch 'ng-helpdesk' of github.com:pwm-project/pwm into ng-helpdesk

Joseph White 7 år sedan
förälder
incheckning
a8a5c83971

+ 3 - 3
client/package.json

@@ -19,9 +19,9 @@
     "license": "ISC",
     "dependencies": {},
     "devDependencies": {
-        "@microfocus/ias-icons": "1.0.0-alpha",
-        "@microfocus/ng-ias": "1.0.0-alpha1",
-        "@microfocus/ux-ias": "1.0.0-alpha",
+        "@microfocus/ias-icons": "^1.0.0-alpha",
+        "@microfocus/ux-ias": "^1.0.0-alpha.1",
+        "@microfocus/ng-ias": "^1.0.0-alpha.2",
         "@types/angular": "1.6.42",
         "@types/angular-mocks": "1.5.11",
         "@types/angular-translate": "2.15.1",

+ 80 - 74
client/src/helpdesk/helpdesk-detail.component.html

@@ -28,80 +28,86 @@
 
 <div class="help-desk-content">
     <div>
-        <ias-tabset>
-            <ias-tab id="Field_Profile" label="Profile">
-                <table class="details-table">
-                    <tbody>
-                    <tr ng-repeat="item in $ctrl.person.profileData">
-                        <td ng-bind="item.label"></td>
-                        <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
-                        <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
-                        <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
-                    </tr>
-                    </tbody>
-                </table>
-            </ias-tab>
-            <ias-tab id="Title_Status" label="Status">
-                <table class="details-table">
-                    <tbody>
-                    <tr ng-repeat="item in $ctrl.person.statusData">
-                        <td ng-bind="item.label"></td>
-                        <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
-                        <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
-                        <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
-                    </tr>
-                    </tbody>
-                </table>
-            </ias-tab>
-            <ias-tab ng-if="!!$ctrl.person.userHistory" id="Title_UserEventHistory" label="Password History">
-                <table class="details-table">
-                    <tbody>
-                    <tr ng-repeat="item in $ctrl.person.userHistory">
-                        <td ng-bind="item.timestamp | dateFilter"></td>
-                        <td ng-bind="item.label"></td>
-                    </tr>
-                    </tbody>
-                </table>
-            </ias-tab>
-            <ias-tab id="Title_PasswordPolicy" label="Password Policy">
-                <table class="details-table">
-                    <tbody>
-                    <tr>
-                        <td ng-bind="'Field_Policy' | translate"></td>
-                        <td ng-bind="$ctrl.person.passwordPolicyDN"></td>
-                    </tr>
-                    <tr>
-                        <td ng-bind="'Field_Profile' | translate"></td>
-                        <td ng-bind="$ctrl.person.passwordPolicyID"></td>
-                    </tr>
-                    <tr class="bottom-border">
-                        <td ng-bind="'Field_Display' | translate"></td>
-                        <td>
-                            <ul>
-                                <li ng-repeat="item in $ctrl.person.passwordRequirements" ng-bind="item"></li>
-                            </ul>
-                        </td>
-                    </tr>
-                    <tr ng-repeat="(key, item) in $ctrl.person.passwordPolicyRules">
-                        <td ng-bind="key"></td>
-                        <td ng-bind="item"></td>
-                    </tr>
-                    </tbody>
-                </table>
-            </ias-tab>
-            <ias-tab id="Title_SecurityResponses" label="Security Responses">
-                <table class="details-table">
-                    <tbody>
-                    <tr ng-repeat="item in $ctrl.person.helpdeskResponses">
-                        <td ng-bind="item.label"></td>
-                        <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
-                        <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
-                        <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
-                    </tr>
-                    </tbody>
-                </table>
-            </ias-tab>
-        </ias-tabset>
+        <div class="ias-tabset">
+            <div class="ias-tab" ias-toggle-active="ias-active" ias-toggle="profileTab">{{'Field_Profile' | translate}}</div>
+            <div class="ias-tab" ias-toggle-active="ias-active" ias-toggle="statusTab">{{'Title_Status' | translate}}</div>
+            <div class="ias-tab" ias-toggle-active="ias-active" ias-toggle="historyTab">{{'Title_UserEventHistory' | translate}}</div>
+            <div class="ias-tab" ias-toggle-active="ias-active" ias-toggle="passwordTab">{{'Title_PasswordPolicy' | translate}}</div>
+            <div class="ias-tab" ias-toggle-active="ias-active" ias-toggle="securityTab">{{'Title_SecurityResponses' | translate}}</div>
+        </div>
+
+        <ias-tab-pane toggle-group="detailsTabGroup" name="profileTab">
+            <table class="details-table">
+                <tbody>
+                <tr ng-repeat="item in $ctrl.person.profileData">
+                    <td ng-bind="item.label"></td>
+                    <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
+                    <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
+                    <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
+                </tr>
+                </tbody>
+            </table>
+        </ias-tab-pane>
+        <ias-tab-pane toggle-group="detailsTabGroup" name="statusTab">
+            <table class="details-table">
+                <tbody>
+                <tr ng-repeat="item in $ctrl.person.statusData">
+                    <td ng-bind="item.label"></td>
+                    <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
+                    <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
+                    <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
+                </tr>
+                </tbody>
+            </table>
+        </ias-tab-pane>
+        <ias-tab-pane toggle-group="detailsTabGroup" name="historyTab" ng-if="!!$ctrl.person.userHistory">
+            <table class="details-table">
+                <tbody>
+                <tr ng-repeat="item in $ctrl.person.userHistory">
+                    <td ng-bind="item.timestamp | dateFilter"></td>
+                    <td ng-bind="item.label"></td>
+                </tr>
+                </tbody>
+            </table>
+        </ias-tab-pane>
+        <ias-tab-pane toggle-group="detailsTabGroup" name="passwordTab">
+            <table class="details-table">
+                <tbody>
+                <tr>
+                    <td ng-bind="'Field_Policy' | translate"></td>
+                    <td ng-bind="$ctrl.person.passwordPolicyDN"></td>
+                </tr>
+                <tr>
+                    <td ng-bind="'Field_Profile' | translate"></td>
+                    <td ng-bind="$ctrl.person.passwordPolicyID"></td>
+                </tr>
+                <tr class="bottom-border">
+                    <td ng-bind="'Field_Display' | translate"></td>
+                    <td>
+                        <ul>
+                            <li ng-repeat="item in $ctrl.person.passwordRequirements" ng-bind="item"></li>
+                        </ul>
+                    </td>
+                </tr>
+                <tr ng-repeat="(key, item) in $ctrl.person.passwordPolicyRules">
+                    <td ng-bind="key"></td>
+                    <td ng-bind="item"></td>
+                </tr>
+                </tbody>
+            </table>
+        </ias-tab-pane>
+        <ias-tab-pane toggle-group="detailsTabGroup" name="securityTab">
+            <table class="details-table">
+                <tbody>
+                <tr ng-repeat="item in $ctrl.person.helpdeskResponses">
+                    <td ng-bind="item.label"></td>
+                    <td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
+                    <td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
+                    <td ng-bind="value" ng-if="item.type==='multiString'" ng-repeat="value in item.values"></td>
+                </tr>
+                </tbody>
+            </table>
+        </ias-tab-pane>
     </div>
 
     <div class="help-desk-buttons">

+ 1 - 0
client/src/i18n/translations_en.json

@@ -51,6 +51,7 @@
   "Title_Management": "Management",
   "Title_Organization": "Organization",
   "Title_OrgChart": "Organizational Chart",
+  "Title_PasswordPolicy": "Password Policy",
   "Title_PeopleSearch": "People Search",
   "Title_PeopleSearchCard": "People Search Cards",
   "Title_PeopleSearchTable": "People Search Table",

+ 7 - 13
client/src/peoplesearch/orgchart-search.component.html

@@ -22,19 +22,13 @@
 
 <div class="ias-header">
     <h2 id="page-content-title" translate="Title_Organization">Organization</h2>
-    <ias-search-box id="input" ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
-                    placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
-    </ias-search-box>
-    <!--<mf-auto-complete search-text="$ctrl.query"-->
-                      <!--on-search-text-change="$ctrl.onSearchTextChange(value)"-->
-                      <!--search="$ctrl.autoCompleteSearch(query)"-->
-                      <!--input-debounce="$ctrl.inputDebounce"-->
-                      <!--item-selected="$ctrl.onAutoCompleteItemSelected(person)"-->
-                      <!--item="person">-->
-        <!--<content-template>-->
-            <!--<span ng-bind="person._displayName"></span>-->
-        <!--</content-template>-->
-    <!--</mf-auto-complete>-->
+    <ias-autocomplete placeholder="{{'Placeholder_Search' | translate}}"
+                      ias-debounce="$ctrl.inputDebounce"
+                      ias-items="person in $ctrl.autoCompleteSearch($query)"
+                      ias-on-item-selected="$ctrl.onAutoCompleteItemSelected($item)"
+                      ias-search-text="$ctrl.query">
+        <template><span ng-bind="person._displayName" class="single-line"></span></template>
+    </ias-autocomplete>
 
     <span class="ias-fill"></span>
 

+ 6 - 16
client/src/peoplesearch/orgchart-search.component.ts

@@ -44,8 +44,7 @@ export default class OrgChartSearchComponent {
     query: string;
     searchTextLocalStorageKey: string;
 
-    static $inject = [ '$q',
-        '$scope',
+    static $inject = [
         '$state',
         '$stateParams',
         'ConfigService',
@@ -53,9 +52,7 @@ export default class OrgChartSearchComponent {
         'PeopleService',
         'PwmService'
     ];
-    constructor(private $q: IQService,
-                private $scope: IScope,
-                private $state: angular.ui.IStateService,
+    constructor(private $state: angular.ui.IStateService,
                 private $stateParams: angular.ui.IStateParamsService,
                 private configService: IPeopleSearchConfigService,
                 private localStorageService: LocalStorageService,
@@ -63,10 +60,6 @@ export default class OrgChartSearchComponent {
                 private pwmService: IPwmService) {
         this.searchTextLocalStorageKey = this.localStorageService.keys.SEARCH_TEXT;
         this.inputDebounce = this.pwmService.ajaxTypingWait;
-
-        $scope.$watch('$ctrl.query', () => {
-            this.onSearchTextChange();
-        });
     }
 
     $onInit(): void {
@@ -124,6 +117,7 @@ export default class OrgChartSearchComponent {
     }
 
     autoCompleteSearch(query: string): IPromise<IPerson[]> {
+        this.storeSearchText(query);
         return this.peopleService.autoComplete(query);
     }
 
@@ -132,14 +126,10 @@ export default class OrgChartSearchComponent {
     }
 
     onAutoCompleteItemSelected(person: IPerson): void {
+        this.storeSearchText(null);
         this.$state.go('orgchart.search', { personId: person.userKey, query: null });
     }
 
-    onSearchTextChange(): void {
-        // this.query = value;
-        this.storeSearchText();
-    }
-
     private fetchOrgChartData(personId): IPromise<IOrgChartData> {
         return this.peopleService.getOrgChartData(personId, true);
     }
@@ -157,7 +147,7 @@ export default class OrgChartSearchComponent {
         return param || this.localStorageService.getItem(this.searchTextLocalStorageKey);
     }
 
-    protected storeSearchText(): void {
-        this.localStorageService.setItem(this.searchTextLocalStorageKey, this.query || '');
+    protected storeSearchText(query): void {
+        this.localStorageService.setItem(this.searchTextLocalStorageKey, query || '');
     }
 }

+ 38 - 8
client/src/peoplesearch/orgchart.component.scss

@@ -27,6 +27,19 @@ $org-chart-text-color: #808080;
 
 $manager-connector-height: 16px;
 
+.reports {
+  background-color: #dae1e1;
+  border-radius: 2px;
+  color: #434c50;
+  font-size: 14px;
+  height: 25px;
+  line-height: 25px;
+  position: absolute;
+  right: 3px;
+  text-align: center;
+  top: 3px;
+  min-width: 35px;
+}
 
 .assistant,
 .manager {
@@ -50,6 +63,10 @@ $manager-connector-height: 16px;
       }
     }
 
+    > .reports {
+      right: 20px;
+    }
+
     > .ias-tile-content {
       background-color: white;
       display: block;
@@ -57,15 +74,15 @@ $manager-connector-height: 16px;
       text-align: center;
       width: 100%;
 
-      > .reports {
-        right: 20px;
-      }
-
       :nth-child(n + 3) {
         display: none;
       }
     }
   }
+
+  .reports {
+    right: 20px;
+  }
 }
 
 .self {
@@ -233,14 +250,14 @@ org-chart {
         .manager {
           &:last-child {
             > .ias-tile {
+              > .reports {
+                display: none;
+              }
+
               > .ias-tile-content {
                 > .avatar {
                   //background-image: url('../../images/icons/m_circle-horz-menu_thin.svg');
                 }
-
-                > .reports {
-                  display: none;
-                }
               }
             }
           }
@@ -316,6 +333,14 @@ org-chart {
 }
 
 [dir="rtl"] {
+  .assistant,
+  .manager {
+    .reports {
+      left: 20px;
+      right: auto;
+    }
+  }
+
   // (XS) Default display
   org-chart {
     > .org-chart-section {
@@ -333,6 +358,11 @@ org-chart {
           &[size="large"] {
             margin: 0 128px 0 0;
           }
+
+          .reports {
+            left: 3px;
+            right: initial;
+          }
         }
 
         .org-chart-connector {

+ 6 - 0
client/src/peoplesearch/peoplesearch.scss

@@ -100,3 +100,9 @@ body {
     width: 1px;
   }
 }
+
+.single-line {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}

+ 12 - 15
client/src/peoplesearch/person-card.component.html

@@ -31,26 +31,23 @@
          ng-attr-title="{{$ctrl.person.numDirectReports}} {{ 'Title_DirectReports' | translate }}"></div>
 
     <div class="ias-tile-content" ng-switch-when="small">
-        <h3 ng-bind="$ctrl.person.displayNames[0]"></h3>
-        <div ng-bind="$ctrl.person.displayNames[1]"></div>
+        <h3 class="single-line" ng-bind="$ctrl.person.displayNames[0]"
+            ng-attr-title="{{$ctrl.person.displayNames[0]}}"></h3>
+        <div class="single-line" ng-bind="$ctrl.person.displayNames[1]"
+             ng-attr-title="{{$ctrl.person.displayNames[1]}}"></div>
     </div>
 
     <div class="ias-tile-content" ng-class="{'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-when="large">
-        <h3 ng-bind="$ctrl.person.displayNames[0]"></h3>
-        <div ng-bind="$ctrl.person.displayNames[1]"></div>
-        <div ng-bind="$ctrl.person.displayNames[2]"></div>
-        <div ng-bind="$ctrl.person.displayNames[3]"></div>
-
-        <div ng-bind="$ctrl.person.displayNames[4]"></div>
-        <div ng-bind="$ctrl.person.displayNames[5]"></div>
-        <div ng-bind="$ctrl.person.displayNames[6]"></div>
-        <div ng-bind="$ctrl.person.displayNames[7]"></div>
+        <h3 class="single-line" ng-bind="$ctrl.person.displayNames[0]"
+            ng-attr-title="{{$ctrl.person.displayNames[0]}}"></h3>
+        <div class="single-line" ng-bind="displayName" ng-attr-title="{{displayName}}"
+            ng-repeat="displayName in $ctrl.person.displayNames.slice(1, 8)"></div>
     </div>
 
     <div class="ias-tile-content" ng-class="{'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-default>
-        <h3 ng-bind="$ctrl.person.displayNames[0]"></h3>
-        <div ng-bind="$ctrl.person.displayNames[1]"></div>
-        <div ng-bind="$ctrl.person.displayNames[2]"></div>
-        <div ng-bind="$ctrl.person.displayNames[3]"></div>
+        <h3 class="single-line" ng-bind="$ctrl.person.displayNames[0]"
+            ng-attr-title="{{$ctrl.person.displayNames[0]}}"></h3>
+        <div class="single-line" ng-bind="displayName" ng-attr-title="{{displayName}}"
+             ng-repeat="displayName in $ctrl.person.displayNames.slice(1, 4)"></div>
     </div>
 </div>

+ 0 - 270
client/src/peoplesearch/person-card.component.scss

@@ -1,270 +0,0 @@
-/*!
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-$text-color: #000000;
-$text-color-subtext: #808080;
-
-$person-card-bg-color: #eef2f2;
-$person-card-hover-bg-color: #f6f9f8;
-$person-card-border-color: #01a9e7;
-$person-card-height: 84px;
-$person-card-width: 272px;
-$person-card-avatar-size: 50px;
-$person-card-spacing: 10px;
-
-$person-card-large-avatar-size: 65px;
-
-person-card {
-  background: $person-card-bg-color;
-  border: 1px solid $person-card-bg-color;
-  border-radius: 3px;
-  box-sizing: border-box;
-  display: block;
-  height: $person-card-height;
-  padding: $person-card-spacing;
-  position: relative;
-  text-align: left;
-  vertical-align: top;
-  width: $person-card-width;
-
-  &[ng-click] {
-    cursor: pointer;
-
-    &:focus,
-    &:hover {
-      outline: none;
-    }
-
-    &:focus {
-      background-color: #fff6ce;
-      border-color: #ffd92d;
-    }
-
-    &:hover {
-      background-color: $person-card-hover-bg-color;
-      border-color: $person-card-border-color;
-    }
-  }
-
-  &[size="large"] {
-    background-color: #ffffff;
-    border: 3px solid #808080;
-    border-radius: 3px;
-    height: auto;
-    min-height: 96px;
-    width: 346px;
-    max-width: 100%;
-
-    > .person-card-content {
-      flex-flow: row nowrap;
-
-      > .avatar {
-        flex: 0 0 $person-card-large-avatar-size;
-        height: $person-card-large-avatar-size;
-        width: $person-card-large-avatar-size;
-        margin-bottom: 5px;
-      }
-    }
-  }
-
-  &[size="small"] {
-    border: none;
-    background-color: transparent;
-    height: 96px;
-    padding: 0;
-    width: 120px;
-
-    &[ng-click] {
-      &:focus,
-      &:hover {
-        background-color: transparent;
-      }
-
-      &:focus {
-        > .person-card-content {
-          > .avatar {
-            border-color: #ffd92d !important;
-          }
-        }
-      }
-
-      &:hover {
-        > .person-card-content {
-          > .avatar {
-            border-color: $person-card-border-color !important;
-          }
-        }
-      }
-    }
-
-    > .person-card-content {
-      display: block;
-      text-align: center;
-
-      > .avatar {
-        background-color: #808080;
-        border: 3px solid #808080;
-        border-radius: 100%;
-        height: $person-card-avatar-size;
-        margin: 0 auto;
-        width: $person-card-avatar-size;
-
-        > img {
-          border-radius: 100%;
-        }
-
-        &:hover {
-          border-color: $person-card-border-color;
-        }
-      }
-
-      > .details {
-        background-color: white;
-        margin-top: 8px;
-        width: 100%;
-
-        :first-child {
-          font-size: 13px;
-        }
-      }
-
-      > .reports {
-        right: 20px;
-      }
-    }
-  }
-
-  > .person-card-content {
-    display: flex;
-    flex-flow: row nowrap;
-    overflow: hidden;
-
-    > .avatar {
-      background: transparent url('../../images/user.png') no-repeat center center;
-      background-size: contain;
-      border-radius: 3px;
-      flex: 0 0 $person-card-avatar-size;
-      height: $person-card-avatar-size;
-      margin-right: $person-card-spacing;
-
-      img {
-        height: 100%;
-        width: 100%;
-      }
-    }
-
-    > .details {
-      flex: 1;
-      overflow: hidden;
-
-      &.direct-reports {
-        >:first-child {
-          margin-right: 28px;
-        }
-      }
-
-      > div {
-        line-height: 16px;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-
-      > :first-child {
-        color: $text-color;
-        font-size: 16px;
-        margin-bottom: 2px;
-      }
-
-      > :not(:first-child) {
-        color: $text-color-subtext;
-        font-size: 12px;
-      }
-
-      > .secondary-details {
-        padding-top: 8px;
-      }
-    }
-
-    > .reports {
-      background-color: #dae1e1;
-      border-radius: 2px;
-      color: #434c50;
-      font-size: 14px;
-      height: 25px;
-      line-height: 25px;
-      position: absolute;
-      right: 3px;
-      text-align: center;
-      top: 3px;
-      min-width: 35px;
-    }
-  }
-}
-
-[dir="rtl"] {
-  person-card {
-    text-align: right;
-
-    &[size="large"] {
-      > .person-card-content {
-        > .avatar {
-        }
-      }
-    }
-
-    &[size="small"] {
-      > .person-card-content {
-        > .avatar {
-          margin: 0 auto;
-        }
-
-        > .reports {
-          left: 20px;
-          right: auto;
-        }
-      }
-    }
-
-    > .person-card-content {
-      > .avatar {
-        margin-left: $person-card-spacing;
-        margin-right: 0;
-      }
-
-      > .details {
-        &.direct-reports {
-          >:first-child {
-            margin-left: 28px;
-            margin-right: 0;
-          }
-        }
-      }
-
-      > .reports {
-        left: 3px;
-        right: initial;
-      }
-    }
-  }
-}

+ 0 - 1
client/src/peoplesearch/person-card.component.ts

@@ -26,7 +26,6 @@ import { IPerson } from '../models/person.model';
 import { IPeopleService } from '../services/people.service';
 
 const templateUrl = require('peoplesearch/person-card.component.html');
-require('peoplesearch/person-card.component.scss');
 
 class PersonCardController {
     private details: any[]; // For large style cards