Skip to content

Commit b6da279

Browse files
committed
Final Commit
1 parent 96c84f2 commit b6da279

File tree

14 files changed

+1311
-586
lines changed

14 files changed

+1311
-586
lines changed

package-lock.json

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"start-web": "npm run web-app:debug"
4848
},
4949
"devDependencies": {
50+
"@types/angular-ui-router": "^1.1.44",
5051
"npm-run-all": "4.1.5"
51-
},
52-
"dependencies": {}
53-
}
52+
}
53+
}

plugins/sftp/web/angular.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,5 +163,9 @@
163163
}
164164
}
165165
}
166-
}}
166+
}
167+
},
168+
"cli": {
169+
"analytics": false
170+
}
167171
}

service/src/routes/logins.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ module.exports = function(app, security) {
5454
if (req.query.deviceId) {
5555
filter.deviceId = req.query.deviceId;
5656
}
57+
if (req.query.deviceIds) {
58+
const ids = req.query.deviceIds.split(',').map(id => id.trim());
59+
filter.deviceId = { $in: ids };
60+
}
5761
if (req.query.startDate) {
5862
filter.startDate = moment(req.query.startDate).toDate();
5963
}
Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,126 @@
11
import { User } from "core-lib-src/user";
22

33
export type LoginPage = {
4-
logins: Login[],
5-
next: string,
6-
prev: string,
4+
logins: Login[];
5+
next: string;
6+
prev: string;
77
};
88

99
export type Login = {
10-
device: Device,
11-
id: string,
12-
timestamp: Date,
13-
user: User,
10+
device: Device;
11+
id: string;
12+
timestamp: Date;
13+
user: User;
14+
};
15+
16+
export type LoginSearchResults = {
17+
active: boolean;
18+
allPhones: string;
19+
displayName: string;
20+
enabled: boolean;
21+
id: string;
22+
username: string;
1423
};
1524

1625
export type Device = {
17-
appVersion: string,
18-
id: string,
19-
registered: boolean,
20-
uid: number | string,
21-
userAgent: string,
22-
userId: string,
23-
}
26+
appVersion: string;
27+
id: string;
28+
registered: boolean;
29+
uid: number | string;
30+
userAgent: string;
31+
userId: string;
32+
iconClass: string;
33+
};
34+
35+
export type LoginFilter = {
36+
user?: User | null;
37+
deviceIds?: string | null;
38+
startDate?: Date | null;
39+
endDate?: Date | null;
40+
};
41+
42+
export type Sort = {
43+
displayName: number;
44+
_id: number;
45+
}
46+
47+
export type UserFilter = {
48+
pageSize: number;
49+
pageIndex: number;
50+
sort: Sort;
51+
active?: boolean;
52+
enabled?: boolean;
53+
}
54+
55+
export type PageLinks = {
56+
next: string | null;
57+
prev: string | null;
58+
}
59+
60+
export type PageInfo = {
61+
totalCount: number;
62+
pageSize: number;
63+
pageIndex: number;
64+
items: User[];
65+
links: PageLinks;
66+
}
67+
68+
export type Category = {
69+
countFilter: Record<string, any>;
70+
userFilter: UserFilter;
71+
searchFilter: string;
72+
userCount: number;
73+
pageInfo: PageInfo;
74+
}
75+
76+
export type UsersResponse = {
77+
all: Category;
78+
active: Category;
79+
inactive: Category;
80+
disabled: Category;
81+
}
82+
export type DeviceUser = {
83+
displayName: string;
84+
id: string;
85+
};
86+
87+
export type DeviceSort = {
88+
userAgent: number;
89+
_id: number;
90+
};
91+
92+
export type DeviceFilter = {
93+
limit: number;
94+
sort: DeviceSort;
95+
registered?: boolean;
96+
};
97+
98+
export type DevicePageLinks = {
99+
base: string;
100+
context: string;
101+
next: string | null;
102+
prev: string | null;
103+
self: string;
104+
};
105+
106+
export type DevicePageInfo = {
107+
links: DevicePageLinks;
108+
limit: number;
109+
size: number;
110+
start: number;
111+
devices: Device[];
112+
};
113+
114+
export type DeviceCategory = {
115+
countFilter: Record<string, any>;
116+
deviceFilter: DeviceFilter;
117+
searchFilter: string;
118+
deviceCount: number;
119+
pageInfo: DevicePageInfo;
120+
};
121+
122+
export type DevicesResponse = {
123+
all: DeviceCategory;
124+
registered: DeviceCategory;
125+
unregistered: DeviceCategory;
126+
};
Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
1-
<!-- Dashboard Breadcrumbs -->
21
<div class="admin-nav-gap">
32
<mat-toolbar class="breadcrumbs-toolbar" color="transparent">
4-
<span><mat-icon>dashboard</mat-icon>&nbsp;Dashboard</span>
3+
<span>
4+
<mat-icon>dashboard</mat-icon>&nbsp;Dashboard
5+
</span>
56
</mat-toolbar>
67
</div>
7-
8-
<!-- Main Admin Content -->
98
<div class="admin-content p-4">
10-
<!-- Top Summary Card -->
119
<mat-card class="mb-4 top-card">
1210
<div fxLayout="row" fxLayoutGap="24px" fxLayoutAlign="start stretch">
1311
<div class="top-row">
1412
<!-- Inactive Users -->
1513
<div class="inactive-users">
1614
<mat-card>
1715
<mat-card-title>
18-
<span class="title-with-badge">
19-
Inactive Users
20-
<span matBadge="{{ count() }}" matBadgeColor="accent" matBadgeOverlap="false" class="inline-badge"></span>
16+
<span class="title-with-badge"> Inactive Users <span matBadge="{{ count() }}" matBadgeColor="accent" matBadgeOverlap="false" class="inline-badge"></span>
2117
</span>
2218
</mat-card-title>
2319
<div class="search-wrapper">
@@ -36,24 +32,19 @@
3632
</button>
3733
</mat-list-item>
3834
</mat-list>
39-
4035
<div class="pagination-controls">
4136
<button mat-button (click)="previous()" [disabled]="!hasPrevious()">← Previous User</button>
4237
<button mat-button (click)="next()" [disabled]="!hasNext()">Next User →</button>
4338
</div>
4439
</mat-card>
4540
</div>
46-
4741
<!-- Unregistered Devices -->
4842
<div class="unregistered-devices">
4943
<mat-card>
5044
<mat-card-title>
51-
<span class="title-with-badge">
52-
Unregistered Devices
53-
<span matBadge="{{ deviceCount() }}" matBadgeColor="accent" matBadgeOverlap="false" class="inline-badge"></span>
45+
<span class="title-with-badge"> Unregistered Devices <span matBadge="{{ deviceCount() }}" matBadgeColor="accent" matBadgeOverlap="false" class="inline-badge"></span>
5446
</span>
5547
</mat-card-title>
56-
5748
<div class="search-wrapper">
5849
<mat-form-field class="w-100 mb-3">
5950
<mat-label>Search</mat-label>
@@ -63,14 +54,15 @@
6354
<mat-list>
6455
<mat-list-item *ngFor="let d of unregisteredDevices" (click)="gotoDevice(d)" class="clickable">
6556
<mat-icon matListIcon [ngClass]="iconClass(d)"></mat-icon>
66-
<div matLine *ngIf="d.user">{{ d.user.displayName }}</div>
67-
<div matLine class="text-muted" *ngIf="d.user">({{ d.uid }})</div>
57+
<div class="mat-line-horizontal">
58+
<span *ngIf="d.user">{{ d.user.displayName }}</span>
59+
<span class="text-muted">({{ d.uid }})</span>
60+
</div>
6861
<button mat-mini-button color="primary" *ngIf="hasPermission('UPDATE_DEVICE')" (click)="registerDevice($event, d)">
6962
<mat-icon>check</mat-icon> Register
7063
</button>
7164
</mat-list-item>
7265
</mat-list>
73-
7466
<div class="pagination-controls">
7567
<button mat-button (click)="previousDevice()" [disabled]="!hasPreviousDevice()">← Previous Device</button>
7668
<button mat-button (click)="nextDevice()" [disabled]="!hasNextDevice()">Next Device →</button>
@@ -80,62 +72,54 @@
8072
</div>
8173
</div>
8274
</mat-card>
83-
8475
<!-- Logins Section -->
8576
<mat-card class="mt-5 logins">
8677
<div class="logins-header">
8778
<mat-card-title>Logins</mat-card-title>
8879
</div>
89-
90-
<!-- Filter Toggle Button above the filter bar -->
9180
<div class="filter-toggle-wrapper">
9281
<button mat-stroked-button color="primary" (click)="toggleFilters = !toggleFilters" [attr.aria-expanded]="toggleFilters">
9382
<mat-icon>{{ toggleFilters ? 'close' : 'filter_list' }}</mat-icon>
9483
{{ toggleFilters ? 'Hide Filters' : 'Show Filters' }}
9584
</button>
9685
</div>
97-
9886
<!-- Filter Bar -->
9987
<div class="filter-bar" [ngClass]="{ 'expanded': toggleFilters }">
10088
<mat-form-field>
10189
<mat-label>Filter on User</mat-label>
102-
<input
103-
type="text"
104-
matInput
105-
[matAutocomplete]="userAuto"
106-
[(ngModel)]="user"
107-
(ngModelChange)="onUserInputChange($event)"
108-
/>
90+
<input type="text" matInput [matAutocomplete]="userAuto" [(ngModel)]="user" (ngModelChange)="onUserInputChange($event)" />
91+
<button mat-icon-button matSuffix *ngIf="user" aria-label="Clear" (click)="clearUserFilter()">
92+
<mat-icon>close</mat-icon>
93+
</button>
10994
<mat-autocomplete #userAuto="matAutocomplete" (optionSelected)="filterLogins()" [displayWith]="displayUser">
11095
<mat-option *ngFor="let u of loginSearchResults" [value]="u">
11196
{{ u.displayName }}
11297
</mat-option>
11398
</mat-autocomplete>
11499
</mat-form-field>
115-
116100
<mat-form-field>
117101
<mat-label>Filter on Device</mat-label>
118102
<mat-select [(ngModel)]="device" (selectionChange)="filterLogins()" multiple>
119103
<mat-option *ngFor="let d of loginDeviceSearchResults" [value]="d">
120-
{{ d.userAgent }}
104+
{{ d.uid }} - {{ d.user?.displayName || 'Unknown User' }}
121105
</mat-option>
122106
</mat-select>
107+
<button mat-icon-button matSuffix *ngIf="device?.length" aria-label="Clear" (click)="clearDeviceFilter()">
108+
<mat-icon>close</mat-icon>
109+
</button>
123110
</mat-form-field>
124-
125111
<mat-form-field>
126112
<mat-label>Start Date</mat-label>
127113
<input matInput [matDatepicker]="startPicker" [(ngModel)]="login.startDate" (dateChange)="dateFilterChanged()" />
128114
<mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle>
129115
<mat-datepicker #startPicker></mat-datepicker>
130116
</mat-form-field>
131-
132117
<mat-form-field>
133118
<mat-label>End Date</mat-label>
134119
<input matInput [matDatepicker]="endPicker" [(ngModel)]="login.endDate" (dateChange)="dateFilterChanged()" />
135120
<mat-datepicker-toggle matSuffix [for]="endPicker"></mat-datepicker-toggle>
136121
<mat-datepicker #endPicker></mat-datepicker>
137122
</mat-form-field>
138-
139123
<div class="results-select">
140124
<mat-form-field>
141125
<mat-label>Results</mat-label>
@@ -145,8 +129,6 @@
145129
</mat-form-field>
146130
</div>
147131
</div>
148-
149-
<!-- Table Wrapper -->
150132
<div class="table-wrapper">
151133
<table mat-table [dataSource]="loginPage?.logins" class="mat-elevation-z1 full-width-table">
152134
<!-- Icon Column -->
@@ -156,41 +138,36 @@
156138
<mat-icon [ngClass]="iconClass(login.device)"></mat-icon>
157139
</td>
158140
</ng-container>
159-
160141
<!-- User Column -->
161142
<ng-container matColumnDef="user">
162143
<th mat-header-cell *matHeaderCellDef>User</th>
163144
<td mat-cell *matCellDef="let login">
164145
<a (click)="gotoUser(login.user)">{{ login.user.displayName }}</a>
165146
</td>
166147
</ng-container>
167-
168148
<!-- Device Column -->
169149
<ng-container matColumnDef="device">
170150
<th mat-header-cell *matHeaderCellDef>Device</th>
171151
<td mat-cell *matCellDef="let login">
172152
<a (click)="gotoDevice(login.device)">{{ login.device.uid }}</a>
173153
</td>
174154
</ng-container>
175-
176155
<!-- Timestamp Column -->
177156
<ng-container matColumnDef="timestamp">
178157
<th mat-header-cell *matHeaderCellDef>Timestamp</th>
179158
<td mat-cell *matCellDef="let login">
180159
{{ login.timestamp | date:'short' }}
181160
</td>
182161
</ng-container>
183-
184162
<!-- Table Rows -->
185163
<tr mat-header-row *matHeaderRowDef="['icon', 'user', 'device', 'timestamp']"></tr>
186164
<tr mat-row *matRowDef="let row; columns: ['icon', 'user', 'device', 'timestamp']"></tr>
187165
</table>
188166
</div>
189-
190167
<!-- Pagination -->
191168
<div class="d-flex justify-content-end mt-3 pagination-controls">
192169
<button mat-button (click)="pageLogin(loginPage.prev)" [disabled]="!showPrevious">← Previous</button>
193170
<button mat-button (click)="pageLogin(loginPage.next)" [disabled]="!showNext">Next →</button>
194171
</div>
195172
</mat-card>
196-
</div>
173+
</div>

0 commit comments

Comments
 (0)