@mis-wis/common 0.1.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/lib/components/create-non-shift-modal/create-non-shift-modal.component.mjs +237 -0
- package/esm2020/lib/constants/assets.constants.mjs +8 -0
- package/esm2020/lib/models/env.model.mjs +1 -1
- package/esm2020/lib/models/session.model.mjs +2 -0
- package/esm2020/lib/services/http.service.mjs +29 -9
- package/esm2020/lib/services/session.errors.mjs +45 -0
- package/esm2020/lib/services/session.service.mjs +198 -0
- package/esm2020/lib/wis-common.module.mjs +13 -8
- package/esm2020/public-api.mjs +5 -1
- package/fesm2015/mis-wis-common.mjs +530 -25
- package/fesm2015/mis-wis-common.mjs.map +1 -1
- package/fesm2020/mis-wis-common.mjs +522 -25
- package/fesm2020/mis-wis-common.mjs.map +1 -1
- package/lib/components/create-non-shift-modal/create-non-shift-modal.component.d.ts +56 -0
- package/lib/constants/assets.constants.d.ts +7 -0
- package/lib/models/env.model.d.ts +1 -0
- package/lib/models/session.model.d.ts +22 -0
- package/lib/services/http.service.d.ts +16 -5
- package/lib/services/session.errors.d.ts +31 -0
- package/lib/services/session.service.d.ts +79 -0
- package/lib/wis-common.module.d.ts +9 -4
- package/package.json +9 -3
- package/public-api.d.ts +4 -0
- package/src/assets/calendar.svg +7 -0
- package/src/assets/images/icons/Wis-icon.svg +4 -0
- package/src/assets/images/icons/brand.png +0 -0
- package/src/assets/images/icons/broascast.svg +3 -0
- package/src/assets/images/icons/bulk-upload.svg +3 -0
- package/src/assets/images/icons/desk-mgmt.svg +6 -0
- package/src/assets/images/icons/employees-sidebar.svg +3 -0
- package/src/assets/images/icons/help-section.svg +4 -0
- package/src/assets/images/icons/home.svg +3 -0
- package/src/assets/images/icons/logout.svg +4 -0
- package/src/assets/images/icons/manage-premise.svg +3 -0
- package/src/assets/images/icons/meeting-room.svg +7 -0
- package/src/assets/images/icons/mis-icon.svg +3 -0
- package/src/assets/images/icons/my-bookings.svg +5 -0
- package/src/assets/images/icons/profile.svg +3 -0
- package/src/assets/images/icons/reception.svg +14 -0
- package/src/assets/images/icons/report.svg +3 -0
- package/src/assets/images/icons/resource-group.svg +3 -0
- package/src/assets/images/icons/roles.svg +3 -0
- package/src/assets/images/icons/sanitize.svg +10 -0
- package/src/assets/images/icons/seat-mgmt.svg +3 -0
- package/src/assets/images/icons/settings-sidebar.svg +3 -0
- package/src/assets/images/icons/signout.svg +4 -0
- package/src/assets/images/icons/user-group.svg +6 -0
- package/src/assets/images/icons/user-permission.svg +5 -0
- package/src/assets/images/icons/utilisation.svg +3 -0
- package/src/assets/images/icons/vaccine_approval.svg +8 -0
- package/src/assets/images/icons/vaccine_upload.png +0 -0
- package/src/assets/images/icons/work-planner-sidebar.svg +3 -0
- package/src/assets/images/icons/workInSyncText.svg +14 -0
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, NgModule, Component, Inject } from '@angular/core';
|
|
3
|
-
import * as
|
|
3
|
+
import * as i5 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
|
-
import * as i1$
|
|
6
|
-
import { HttpErrorResponse } from '@angular/common/http';
|
|
7
|
-
import { tap } from 'rxjs/operators';
|
|
5
|
+
import * as i1$2 from '@angular/common/http';
|
|
6
|
+
import { HttpErrorResponse, HttpClientModule } from '@angular/common/http';
|
|
7
|
+
import { tap, finalize } from 'rxjs/operators';
|
|
8
8
|
import * as i1 from '@angular/router';
|
|
9
9
|
import * as i2 from 'mis-crystal-design-system/toast';
|
|
10
|
-
import * as i1$
|
|
10
|
+
import * as i1$1 from '@angular/cdk/overlay';
|
|
11
11
|
import { OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
|
|
12
12
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
13
13
|
import * as i2$1 from 'mis-crystal-design-system/loader';
|
|
14
14
|
import { LoaderModule } from 'mis-crystal-design-system/loader';
|
|
15
|
-
import { ReplaySubject } from 'rxjs';
|
|
15
|
+
import { map, forkJoin, ReplaySubject } from 'rxjs';
|
|
16
|
+
import * as i2$2 from '@angular/forms';
|
|
17
|
+
import { Validators, ReactiveFormsModule } from '@angular/forms';
|
|
18
|
+
import dayjs from 'dayjs';
|
|
19
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
20
|
+
import * as i1$3 from 'mis-crystal-design-system/modal';
|
|
21
|
+
import { MODAL_DATA_VAR } from 'mis-crystal-design-system/modal';
|
|
22
|
+
import * as i6 from 'mis-crystal-design-system/dropdown';
|
|
23
|
+
import { DropdownModule } from 'mis-crystal-design-system/dropdown';
|
|
24
|
+
import * as i7 from 'mis-crystal-design-system/datepicker_v2';
|
|
25
|
+
import { DatepickerModuleV2 } from 'mis-crystal-design-system/datepicker_v2';
|
|
26
|
+
import jwtDecode from 'jwt-decode';
|
|
16
27
|
|
|
17
28
|
class AuthService {
|
|
18
29
|
constructor() { }
|
|
@@ -151,7 +162,7 @@ class LoaderComponent {
|
|
|
151
162
|
ngOnInit() { }
|
|
152
163
|
}
|
|
153
164
|
LoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
154
|
-
LoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: LoaderComponent, selector: "mis-common-loader", ngImport: i0, template: "<div class=\"message-wrapper\" *ngIf=\"message; else noMessageLoader\">\n <span><mis-loader type=\"block\"></mis-loader></span>\n <p>{{ message }}</p>\n</div>\n<ng-template #noMessageLoader>\n <mis-loader></mis-loader>\n</ng-template>\n", styles: [".message-wrapper{max-width:343px;background-color:#fff;display:flex;flex-wrap:nowrap;align-items:center;padding:20px 16px;border-radius:6px}.message-wrapper span{position:relative;background-color:#fff;display:inline-block;width:40px;height:40px;margin-right:16px}.message-wrapper p{margin:0}\n"], dependencies: [{ kind: "directive", type:
|
|
165
|
+
LoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: LoaderComponent, selector: "mis-common-loader", ngImport: i0, template: "<div class=\"message-wrapper\" *ngIf=\"message; else noMessageLoader\">\n <span><mis-loader type=\"block\"></mis-loader></span>\n <p>{{ message }}</p>\n</div>\n<ng-template #noMessageLoader>\n <mis-loader></mis-loader>\n</ng-template>\n", styles: [".message-wrapper{max-width:343px;background-color:#fff;display:flex;flex-wrap:nowrap;align-items:center;padding:20px 16px;border-radius:6px}.message-wrapper span{position:relative;background-color:#fff;display:inline-block;width:40px;height:40px;margin-right:16px}.message-wrapper p{margin:0}\n"], dependencies: [{ kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.LoaderComponent, selector: "mis-loader", inputs: ["mobileView"] }] });
|
|
155
166
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoaderComponent, decorators: [{
|
|
156
167
|
type: Component,
|
|
157
168
|
args: [{ selector: "mis-common-loader", template: "<div class=\"message-wrapper\" *ngIf=\"message; else noMessageLoader\">\n <span><mis-loader type=\"block\"></mis-loader></span>\n <p>{{ message }}</p>\n</div>\n<ng-template #noMessageLoader>\n <mis-loader></mis-loader>\n</ng-template>\n", styles: [".message-wrapper{max-width:343px;background-color:#fff;display:flex;flex-wrap:nowrap;align-items:center;padding:20px 16px;border-radius:6px}.message-wrapper span{position:relative;background-color:#fff;display:inline-block;width:40px;height:40px;margin-right:16px}.message-wrapper p{margin:0}\n"] }]
|
|
@@ -203,14 +214,14 @@ class LoaderService {
|
|
|
203
214
|
return { positionStrategy, width, backdropClass, hasMessage };
|
|
204
215
|
}
|
|
205
216
|
}
|
|
206
|
-
LoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoaderService, deps: [{ token: i1$
|
|
217
|
+
LoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoaderService, deps: [{ token: i1$1.Overlay }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
207
218
|
LoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoaderService, providedIn: "root" });
|
|
208
219
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoaderService, decorators: [{
|
|
209
220
|
type: Injectable,
|
|
210
221
|
args: [{
|
|
211
222
|
providedIn: "root"
|
|
212
223
|
}]
|
|
213
|
-
}], ctorParameters: function () { return [{ type: i1$
|
|
224
|
+
}], ctorParameters: function () { return [{ type: i1$1.Overlay }]; } });
|
|
214
225
|
|
|
215
226
|
/** @format */
|
|
216
227
|
const CONSTANTS_LOGGER = {
|
|
@@ -268,14 +279,14 @@ class LoggerService {
|
|
|
268
279
|
this.logRequest({ ...data, componentName, serviceUrl }, "error");
|
|
269
280
|
}
|
|
270
281
|
}
|
|
271
|
-
LoggerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoggerService, deps: [{ token: i1$
|
|
282
|
+
LoggerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoggerService, deps: [{ token: i1$2.HttpClient }, { token: CONSTANTS_LOGGER.APP_NAME }, { token: CONSTANTS_LOGGER.ENV_VAR }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
272
283
|
LoggerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoggerService, providedIn: "root" });
|
|
273
284
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: LoggerService, decorators: [{
|
|
274
285
|
type: Injectable,
|
|
275
286
|
args: [{
|
|
276
287
|
providedIn: "root"
|
|
277
288
|
}]
|
|
278
|
-
}], ctorParameters: function () { return [{ type: i1$
|
|
289
|
+
}], ctorParameters: function () { return [{ type: i1$2.HttpClient }, { type: undefined, decorators: [{
|
|
279
290
|
type: Inject,
|
|
280
291
|
args: [CONSTANTS_LOGGER.APP_NAME]
|
|
281
292
|
}] }, { type: undefined, decorators: [{
|
|
@@ -328,23 +339,43 @@ const API_URLS = {
|
|
|
328
339
|
CONFIGURATIONS: "configurations"
|
|
329
340
|
};
|
|
330
341
|
class HttpService {
|
|
331
|
-
constructor(env,
|
|
342
|
+
constructor(env, http) {
|
|
332
343
|
this.env = env;
|
|
333
|
-
this.auth = auth;
|
|
334
344
|
this.http = http;
|
|
335
345
|
}
|
|
336
|
-
|
|
337
|
-
return this.http.get(`${this.env.
|
|
346
|
+
getProperties(Porperties) {
|
|
347
|
+
return this.http.get(`${this.env.ets}/ets/apis/properties/${Porperties.join(",")}`).pipe(map((res) => {
|
|
348
|
+
return res.reduce((acc, item) => {
|
|
349
|
+
acc[item.propertyName] = item.propertyValue === "true" ? true : item.propertyValue === "false" ? false : item.propertyValue;
|
|
350
|
+
return acc;
|
|
351
|
+
}, {});
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
getOffices(nonce) {
|
|
355
|
+
const body = `CSRF_NONCE=${nonce}&method=getOfficeList`;
|
|
356
|
+
return this.http.post(`${this.env.ets}/Locality_Servlet`, body, { headers: { "Content-Type": "application/x-www-form-urlencoded" } });
|
|
357
|
+
}
|
|
358
|
+
listTransportLocalities(nonce) {
|
|
359
|
+
const body = `CSRF_NONCE=${nonce}&method=listTransportLocalities`;
|
|
360
|
+
return this.http.post(`${this.env.ets}/Locality_Servlet`, body, { headers: { "Content-Type": "application/x-www-form-urlencoded" } });
|
|
361
|
+
}
|
|
362
|
+
createEmptyIrregularTripSheet(nonce, direction) {
|
|
363
|
+
const body = `CSRF_NONCE=${nonce}&method=createEmptyIrregularTripSheet&direction=${direction}`;
|
|
364
|
+
return this.http.post(`${this.env.ets}/Tripsheet_Edit_Servlet`, body, { headers: { "Content-Type": "application/x-www-form-urlencoded" } });
|
|
365
|
+
}
|
|
366
|
+
addEmployeeToTripSheet(nonce, params) {
|
|
367
|
+
const body = `CSRF_NONCE=${nonce}&method=addEmployeeToTripSheet&tripsheet=${encodeURIComponent(JSON.stringify(params.tripsheet))}&employee=${encodeURIComponent(JSON.stringify(params.employee))}&pickupLoc=${encodeURIComponent(JSON.stringify(params.pickupLoc))}&dropLoc=${encodeURIComponent(JSON.stringify(params.dropLoc))}&pickUpTime="${params.pickUpTime}"&dropTime="${params.dropTime}"&position=${params.position}${params.routeSource ? "&routeSource=" + params.routeSource : ""}${params.assetList ? "&assetList=" + encodeURIComponent(JSON.stringify(params.assetList)) : ""}`;
|
|
368
|
+
return this.http.post(`${this.env.ets}/Tripsheet_Edit_Servlet`, body, { headers: { "Content-Type": "application/x-www-form-urlencoded" } });
|
|
338
369
|
}
|
|
339
370
|
}
|
|
340
|
-
HttpService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: HttpService, deps: [{ token: CONSTANTS_WIS_COMMON }, { token:
|
|
371
|
+
HttpService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: HttpService, deps: [{ token: CONSTANTS_WIS_COMMON }, { token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
341
372
|
HttpService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: HttpService });
|
|
342
373
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: HttpService, decorators: [{
|
|
343
374
|
type: Injectable
|
|
344
375
|
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
|
|
345
376
|
type: Inject,
|
|
346
377
|
args: [CONSTANTS_WIS_COMMON]
|
|
347
|
-
}] }, { type:
|
|
378
|
+
}] }, { type: i1$2.HttpClient }]; } });
|
|
348
379
|
|
|
349
380
|
class SidenavComponent {
|
|
350
381
|
constructor() { }
|
|
@@ -357,24 +388,253 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
357
388
|
args: [{ selector: "wis-common-sidenav", template: "<div class=\"wis-sidenav-wrapper\">\n <div class=\"wis-sidenav-header\">\n <div class=\"wis-sidenav-header-logo\">\n <img src=\"assets/sidenav/assets/Wis-icon.svg\" alt=\"\" />\n </div>\n <div class=\"wis-sidenav-header-title\">\n <h1>WIS</h1>\n </div>\n </div>\n <div class=\"wis-sidenav-body\">\n <div class=\"wis-sidenav-body-item\">\n <div class=\"wis-sidenav-body-item-icon\">\n <i class=\"fas fa-home\"></i>\n </div>\n <div class=\"wis-sidenav-body-item-text\">\n <h3>Home</h3>\n </div>\n </div>\n <div class=\"wis-sidenav-body-item\">\n <div class=\"wis-sidenav-body-item-icon\">\n <i class=\"fas fa-user\"></i>\n </div>\n <div class=\"wis-sidenav-body-item-text\">\n <h3>Profile</h3>\n </div>\n </div>\n <div class=\"wis-sidenav-body-item\">\n <div class=\"wis-sidenav-body-item-icon\">\n <i class=\"fas fa-users\"></i>\n </div>\n <div class=\"wis-sidenav-body-item-text\">\n <h3>Users</h3>\n </div>\n </div>\n <div class=\"wis-sidenav-body-item\">\n <div class=\"wis-sidenav-body-item-icon\">\n <i class=\"fas fa-cog\"></i>\n </div>\n <div class=\"wis-sidenav-body-item-text\">\n <h3>Settings</h3>\n </div>\n </div>\n </div>\n</div>\n", styles: [".wis-sidenav-wrapper{width:72px;border-right:1px solid #e0e0e0}\n"] }]
|
|
358
389
|
}], ctorParameters: function () { return []; } });
|
|
359
390
|
|
|
391
|
+
/** @format */
|
|
392
|
+
const CONSTANTS_ASSETS_WIS_COMMON = {
|
|
393
|
+
IMAGES: {
|
|
394
|
+
CALENDAR_ICON: "assets/libraries/wis-common/calendar.svg"
|
|
395
|
+
},
|
|
396
|
+
ICONS: {}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
dayjs.extend(customParseFormat);
|
|
400
|
+
const PROPERTIES = ["NON_SHIFT_TRIPS_CATEGORIES", "USE_VALID_OFFICE_FOR_IRREGULAR_TRIPS"];
|
|
401
|
+
const DATE_FORMAT = "DD/MM/YYYY";
|
|
402
|
+
const PICKUP_TIME_FORMAT = "DD/MM/YYYY HH/mm/ss";
|
|
403
|
+
class CreateNonShiftModalComponent {
|
|
404
|
+
constructor(modalData, modalRef, fb, http, toast) {
|
|
405
|
+
this.modalData = modalData;
|
|
406
|
+
this.modalRef = modalRef;
|
|
407
|
+
this.fb = fb;
|
|
408
|
+
this.http = http;
|
|
409
|
+
this.toast = toast;
|
|
410
|
+
this.CONSTANTS_ASSETS_WIS_COMMON = CONSTANTS_ASSETS_WIS_COMMON;
|
|
411
|
+
this.dateConfig = {
|
|
412
|
+
minDate: dayjs().format(DATE_FORMAT),
|
|
413
|
+
format: DATE_FORMAT
|
|
414
|
+
};
|
|
415
|
+
this.offices = [];
|
|
416
|
+
this.locationDropdownData = [];
|
|
417
|
+
this.categoryDropdownData = [];
|
|
418
|
+
this.hoursDropdownData = [];
|
|
419
|
+
this.minutesDropdownData = [];
|
|
420
|
+
this.showOtherAddress = false;
|
|
421
|
+
this.loading = false;
|
|
422
|
+
this.isSubmitting = false;
|
|
423
|
+
this.TYPE_OFFICE = 6;
|
|
424
|
+
this.TYPE_TRANSPORT = 3;
|
|
425
|
+
this.TYPE_OTHER = 4;
|
|
426
|
+
this.properties = {};
|
|
427
|
+
}
|
|
428
|
+
ngOnInit() {
|
|
429
|
+
this.loading = true;
|
|
430
|
+
const $office = this.http.getOffices(this.modalData.nonce);
|
|
431
|
+
const $properties = this.http.getProperties(PROPERTIES);
|
|
432
|
+
forkJoin([$office, $properties])
|
|
433
|
+
.pipe(finalize(() => (this.loading = false)))
|
|
434
|
+
.subscribe({
|
|
435
|
+
next: ([offices, properties]) => {
|
|
436
|
+
this.properties = properties;
|
|
437
|
+
this.initializeForm();
|
|
438
|
+
this.initializeDropdownData(offices);
|
|
439
|
+
this.setupFormListeners();
|
|
440
|
+
},
|
|
441
|
+
error: error => {
|
|
442
|
+
console.error("Error loading form data:", error);
|
|
443
|
+
this.modalRef.close();
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
initializeForm() {
|
|
448
|
+
const currentTime = this.modalData?.date ?? dayjs().format(DATE_FORMAT);
|
|
449
|
+
this.createTripsheetForm = this.fb.group({
|
|
450
|
+
direction: [this.modalData?.shiftType === "logout" ? "OUT" : "IN", Validators.required],
|
|
451
|
+
location: [{ label: "", value: "" }],
|
|
452
|
+
otherAddress: "",
|
|
453
|
+
tripCategory: [{ label: "", value: "" }],
|
|
454
|
+
date: [currentTime, Validators.required],
|
|
455
|
+
hours: [{ label: "", value: "" }],
|
|
456
|
+
mins: [{ label: "", value: "" }]
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
initializeDropdownData(offices) {
|
|
460
|
+
this.locationDropdownData = offices.map((office) => ({
|
|
461
|
+
label: office.address,
|
|
462
|
+
value: { type: this.TYPE_OFFICE, office: office || null }
|
|
463
|
+
}));
|
|
464
|
+
this.createTripsheetForm.get("location")?.setValue(this.locationDropdownData[0]);
|
|
465
|
+
if (!this.properties?.["USE_VALID_OFFICE_FOR_IRREGULAR_TRIPS"]) {
|
|
466
|
+
this.locationDropdownData.push({ label: "Airport", value: { type: this.TYPE_TRANSPORT, office: null } }, { label: "Other", value: { type: this.TYPE_OTHER, office: null } });
|
|
467
|
+
}
|
|
468
|
+
// Initialize category dropdown
|
|
469
|
+
const categoriesString = this.properties?.["NON_SHIFT_TRIPS_CATEGORIES"];
|
|
470
|
+
if (categoriesString && typeof categoriesString === "string") {
|
|
471
|
+
const categories = categoriesString.split(",").filter(cat => cat && cat.trim());
|
|
472
|
+
if (categories.length > 0) {
|
|
473
|
+
this.categoryDropdownData = categories.map(category => ({
|
|
474
|
+
label: category.trim(),
|
|
475
|
+
value: category.trim()
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const currentHour = new Date().getHours();
|
|
480
|
+
this.hoursDropdownData = Array.from({ length: 24 }, (_, i) => ({
|
|
481
|
+
label: this.padZero(i),
|
|
482
|
+
value: this.padZero(i)
|
|
483
|
+
}));
|
|
484
|
+
this.createTripsheetForm.patchValue({ hours: this.hoursDropdownData[currentHour] });
|
|
485
|
+
const currentMinute = new Date().getMinutes();
|
|
486
|
+
this.minutesDropdownData = Array.from({ length: 60 }, (_, i) => ({
|
|
487
|
+
label: this.padZero(i),
|
|
488
|
+
value: this.padZero(i)
|
|
489
|
+
}));
|
|
490
|
+
this.createTripsheetForm.patchValue({ mins: this.minutesDropdownData[currentMinute] });
|
|
491
|
+
}
|
|
492
|
+
setupFormListeners() {
|
|
493
|
+
this.createTripsheetForm?.valueChanges.subscribe(value => {
|
|
494
|
+
const { location } = this.createTripsheetForm?.value;
|
|
495
|
+
if (this.showOtherAddress !== (location?.value?.type === this.TYPE_OTHER)) {
|
|
496
|
+
this.showOtherAddress = location?.value?.type === this.TYPE_OTHER;
|
|
497
|
+
if (this.showOtherAddress) {
|
|
498
|
+
this.createTripsheetForm.get("otherAddress")?.setValidators([Validators.required]);
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
this.createTripsheetForm.get("otherAddress")?.clearValidators();
|
|
502
|
+
}
|
|
503
|
+
this.createTripsheetForm.get("otherAddress")?.updateValueAndValidity();
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
padZero(num) {
|
|
508
|
+
return num.toString().padStart(2, "0");
|
|
509
|
+
}
|
|
510
|
+
onCancel() {
|
|
511
|
+
this.modalRef.close();
|
|
512
|
+
}
|
|
513
|
+
onSubmit() {
|
|
514
|
+
if (this.createTripsheetForm.invalid) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const formValue = this.createTripsheetForm.value;
|
|
518
|
+
const pickupTime = `${formValue.date} ${formValue.hours.value}/${formValue.mins.value}/00`;
|
|
519
|
+
const pickupTimeDate = dayjs(pickupTime, PICKUP_TIME_FORMAT).toDate();
|
|
520
|
+
if (this.isDateAndTimeFromPast(pickupTimeDate)) {
|
|
521
|
+
this.toast.displayMsg("Please select a future date and time.");
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (this.categoryDropdownData.length > 0 && (!formValue.tripCategory || !formValue.tripCategory.value)) {
|
|
525
|
+
this.toast.displayMsg("Please provide a trip category.");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (formValue.location?.value?.type === this.TYPE_OTHER) {
|
|
529
|
+
if (!formValue.otherAddress || formValue.otherAddress.trim() === "") {
|
|
530
|
+
this.toast.displayMsg("Please provide the address.");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (formValue.otherAddress.includes(",")) {
|
|
534
|
+
this.toast.displayMsg("Special character (,) not allowed in address.");
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
this.isSubmitting = true;
|
|
539
|
+
this.http.createEmptyIrregularTripSheet(this.modalData.nonce, formValue.direction).subscribe({
|
|
540
|
+
next: routeTripSheet => {
|
|
541
|
+
const pickupLoc = routeTripSheet.dir === "IN" ? this.modalData.employee.home : this.getLocation(formValue);
|
|
542
|
+
const dropLoc = routeTripSheet.dir === "OUT" ? this.modalData.employee.home : this.getLocation(formValue);
|
|
543
|
+
const pickupTimeFormatted = pickupTime;
|
|
544
|
+
const dropTimeFormatted = pickupTime;
|
|
545
|
+
const employee = { ...this.modalData.employee, employee_status: 1 };
|
|
546
|
+
const addEmployeeParams = {
|
|
547
|
+
tripsheet: routeTripSheet,
|
|
548
|
+
employee: employee,
|
|
549
|
+
pickupLoc: pickupLoc,
|
|
550
|
+
dropLoc: dropLoc,
|
|
551
|
+
pickUpTime: pickupTimeFormatted,
|
|
552
|
+
dropTime: dropTimeFormatted,
|
|
553
|
+
position: 0,
|
|
554
|
+
routeSource: this.modalData.routeSource,
|
|
555
|
+
assetList: this.modalData.assetList
|
|
556
|
+
};
|
|
557
|
+
this.http.addEmployeeToTripSheet(this.modalData.nonce, addEmployeeParams).subscribe({
|
|
558
|
+
next: updatedTripSheet => {
|
|
559
|
+
if (!updatedTripSheet.customMap) {
|
|
560
|
+
updatedTripSheet.customMap = {};
|
|
561
|
+
}
|
|
562
|
+
updatedTripSheet.customMap.tripCategory = formValue.tripCategory?.value || "";
|
|
563
|
+
this.modalRef.close({
|
|
564
|
+
success: true,
|
|
565
|
+
tripsheet: updatedTripSheet
|
|
566
|
+
});
|
|
567
|
+
},
|
|
568
|
+
error: error => {
|
|
569
|
+
console.error("Error adding employee to tripsheet:", error);
|
|
570
|
+
this.toast.displayMsg("Failed to add employee to tripsheet. Please try again.");
|
|
571
|
+
this.isSubmitting = false;
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
},
|
|
575
|
+
error: error => {
|
|
576
|
+
console.error("Error creating tripsheet:", error);
|
|
577
|
+
this.toast.displayMsg("Failed to create tripsheet. Please try again.");
|
|
578
|
+
this.isSubmitting = false;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
isDateAndTimeFromPast(date) {
|
|
583
|
+
const now = new Date();
|
|
584
|
+
return date < now;
|
|
585
|
+
}
|
|
586
|
+
getLocation(formValue) {
|
|
587
|
+
const location = formValue.location?.value;
|
|
588
|
+
if (!location) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
const locType = location.type;
|
|
592
|
+
switch (locType) {
|
|
593
|
+
case this.TYPE_OFFICE:
|
|
594
|
+
return location.office;
|
|
595
|
+
case this.TYPE_TRANSPORT:
|
|
596
|
+
return {
|
|
597
|
+
id: null,
|
|
598
|
+
type: this.TYPE_TRANSPORT,
|
|
599
|
+
address: "Airport",
|
|
600
|
+
geoCord: null
|
|
601
|
+
};
|
|
602
|
+
case this.TYPE_OTHER:
|
|
603
|
+
return {
|
|
604
|
+
...location.office,
|
|
605
|
+
address: formValue.otherAddress || ""
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
CreateNonShiftModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CreateNonShiftModalComponent, deps: [{ token: MODAL_DATA_VAR }, { token: i1$3.ModalRef }, { token: i2$2.FormBuilder }, { token: HttpService }, { token: i2.ToastService }], target: i0.ɵɵFactoryTarget.Component });
|
|
611
|
+
CreateNonShiftModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: CreateNonShiftModalComponent, selector: "app-create-non-shift-modal", ngImport: i0, template: "<div class=\"createTripsheet\">\n <h2 class=\"modal-header\">Create Non-Shift Tripsheet</h2>\n\n <div *ngIf=\"loading\" class=\"loading-container\">\n <div class=\"spinner\"></div>\n <p>Loading...</p>\n </div>\n\n <form\n *ngIf=\"!loading && createTripsheetForm\"\n [formGroup]=\"createTripsheetForm\"\n (ngSubmit)=\"onSubmit()\"\n class=\"createTripsheetForm\"\n id=\"createTripsheetForm\"\n >\n <div class=\"form-content\">\n <!-- Radio buttons row -->\n <div [ngClass]=\"{ 'radio-row': true, disabled: modalData?.shiftSelectionDisabled }\">\n <label class=\"radio-label\">\n <input class=\"modern-checkbox\" type=\"radio\" formControlName=\"direction\" value=\"OUT\" id=\"startLoc\" />\n <span>Start Location Fixed (Logout)</span>\n </label>\n <label class=\"radio-label\">\n <input class=\"modern-checkbox\" type=\"radio\" formControlName=\"direction\" value=\"IN\" id=\"endLoc\" />\n <span>End Location Fixed (Login)</span>\n </label>\n </div>\n\n <!-- Location field -->\n <div class=\"form-field\">\n <div class=\"label-cell\">\n <label class=\"locText\">Location</label>\n </div>\n <div class=\"input-cell\">\n <mis-dropdown\n [searchEnabled]=\"true\"\n [width]=\"'100%'\"\n [height]=\"'40px'\"\n [data]=\"locationDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('location')?.value\"\n [label]=\"'Select location...'\"\n (onChange)=\"createTripsheetForm.get('location')?.setValue($event)\"\n ></mis-dropdown>\n </div>\n </div>\n\n <!-- Other address field -->\n <div class=\"form-field otherAddress\" *ngIf=\"showOtherAddress\">\n <div class=\"label-cell\">\n <label>Pick/Drop point <sup>*</sup></label>\n </div>\n <div class=\"input-cell\">\n <input type=\"text\" formControlName=\"otherAddress\" class=\"modern-input\" />\n </div>\n </div>\n\n <!-- Trip category field -->\n <div class=\"form-field\" *ngIf=\"categoryDropdownData.length > 0\">\n <div class=\"label-cell\">\n <label>Trip Category</label>\n </div>\n <div class=\"input-cell\">\n <mis-dropdown\n [searchEnabled]=\"false\"\n [width]=\"'250px'\"\n [height]=\"'40px'\"\n [data]=\"categoryDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('tripCategory')?.value\"\n [label]=\"'Select category...'\"\n (onChange)=\"createTripsheetForm.get('tripCategory')?.setValue($event)\"\n ></mis-dropdown>\n </div>\n </div>\n\n <!-- Time field -->\n <div class=\"form-field\">\n <div class=\"label-cell\">\n <label class=\"fixedTimeText\">Time</label>\n </div>\n <div class=\"input-cell modern-date-container\">\n <div class=\"date-select\">\n <input\n misTzDp\n readonly\n [dpConfig]=\"dateConfig\"\n [formControl]=\"createTripsheetForm.get('date')\"\n style=\"height: 44px !important; font-size: 16px !important; cursor: pointer; vertical-align: middle !important\"\n />\n <img [src]=\"CONSTANTS_ASSETS_WIS_COMMON.IMAGES.CALENDAR_ICON\" alt=\"Calendar Icon\" class=\"date-icon data-picker-icon\" />\n </div>\n\n <mis-dropdown\n [searchEnabled]=\"false\"\n [width]=\"'5rem'\"\n [height]=\"'40px'\"\n [data]=\"hoursDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('hours')?.value\"\n [label]=\"'HH'\"\n (onChange)=\"createTripsheetForm.get('hours')?.setValue($event)\"\n ></mis-dropdown>\n\n <mis-dropdown\n [searchEnabled]=\"false\"\n [width]=\"'5rem'\"\n [height]=\"'40px'\"\n [data]=\"minutesDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('mins')?.value\"\n [label]=\"'MM'\"\n (onChange)=\"createTripsheetForm.get('mins')?.setValue($event)\"\n ></mis-dropdown>\n </div>\n </div>\n </div>\n\n <div class=\"form-actions\">\n <button mis-button type=\"outline\" (click)=\"onCancel()\" [disabled]=\"isSubmitting\">Cancel</button>\n <button mis-button type=\"primary\" [disabled]=\"isSubmitting || createTripsheetForm?.invalid\">\n {{ isSubmitting ? \"Creating...\" : \"Create\" }}\n </button>\n </div>\n </form>\n</div>\n", styles: [".createTripsheet{width:40rem;margin:auto;background:#fff;border-radius:.5rem;padding:1.25rem}.createTripsheet .modal-header{font-size:1.25rem;font-weight:600;color:#333;margin:0 0 1.25rem;padding-bottom:.9375rem;border-bottom:.0625rem solid #e0e0e0}.createTripsheet .loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3.75rem 1.25rem;min-height:12.5rem}.createTripsheet .loading-container .spinner{border:.1875rem solid #f3f3f3;border-top:.1875rem solid #007bff;border-radius:50%;width:2.5rem;height:2.5rem;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.createTripsheet .createTripsheetForm .form-content{display:flex;flex-direction:column}.createTripsheet .createTripsheetForm .form-field{display:flex;align-items:flex-start;gap:1.25rem;padding:.9375rem 0}.createTripsheet .createTripsheetForm .form-field .label-cell{flex:0 0 30%;padding-top:.5rem}.createTripsheet .createTripsheetForm .form-field .label-cell label{font-size:.875rem;font-weight:500;color:#333}.createTripsheet .createTripsheetForm .form-field .input-cell{flex:1;display:flex}.createTripsheet .createTripsheetForm .radio-row{display:flex;gap:1.25rem;padding:.9375rem 0}.createTripsheet .createTripsheetForm .radio-label{font-size:.875rem;display:flex;align-items:center;cursor:pointer;margin:0}.createTripsheet .createTripsheetForm .radio-label .modern-checkbox{margin-right:.625rem;vertical-align:sub;cursor:pointer;width:1rem;height:1rem;accent-color:#007bff}.createTripsheet .createTripsheetForm .radio-label span{-webkit-user-select:none;user-select:none;white-space:nowrap}.createTripsheet .createTripsheetForm .modern-input{width:100%;padding:.5rem .75rem;border:.0625rem solid #ddd;border-radius:.25rem;font-size:.875rem;transition:border-color .3s}.createTripsheet .createTripsheetForm .modern-input:focus{outline:none;border-color:#007bff;box-shadow:0 0 0 .2rem #007bff40}.createTripsheet .createTripsheetForm .modern-input:disabled{background-color:#f5f5f5;cursor:not-allowed}.createTripsheet .createTripsheetForm sup{color:#dc3545}.createTripsheet .createTripsheetForm ::ng-deep mis-dropdown{display:block;width:100%}.createTripsheet .createTripsheetForm .modern-date-container{position:inherit;float:inherit;display:flex;align-items:center;gap:.625rem}.createTripsheet .createTripsheetForm .ng-invalid{border:.0625rem solid #dc3545}.createTripsheet .createTripsheetForm .form-actions{text-align:right;margin:.625rem;border-top:.0625rem solid #e0e0e0;padding:.625rem;display:flex;justify-content:flex-end;gap:.625rem;margin-bottom:0}.createTripsheet .createTripsheetForm .form-actions button{padding:.625rem 1.5rem;font-size:.875rem;font-weight:500;border-radius:.375rem;border:none;cursor:pointer;transition:all .3s ease;min-width:6.25rem}.createTripsheet .createTripsheetForm .form-actions button[type=primary]{background-color:#0937b2;color:#fff;border:.0625rem solid #0937b2}.createTripsheet .createTripsheetForm .form-actions button[type=outline]{background-color:transparent;color:#6c757d;border:.0625rem solid #ddd}.createTripsheet .createTripsheetForm .form-actions button:disabled{opacity:.6;cursor:not-allowed;box-shadow:none}.date-select{height:2.5rem;border:.0625rem solid #e0e0e0;padding:0;border-radius:.375rem;display:flex;flex-wrap:nowrap;justify-content:space-between;align-items:center;position:relative;overflow:hidden;left:-.5%;flex-shrink:0}.date-select input{background-color:transparent;display:inline-block;padding:.25rem 0 .25rem .75rem;width:100%;border:none;outline:none;cursor:pointer;font-size:.875rem}.date-select img{position:relative;right:1rem}.radio-row.disabled{pointer-events:none;opacity:.5}\n"], dependencies: [{ kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i6.DropdownComponent, selector: "mis-dropdown", inputs: ["data", "height", "ariaLabel", "width", "label", "itemSizeForCdk", "showOnlyIcon", "higlightSelectedValue", "dropdownListHeight", "dropdownListWidth", "dropdownListPosition", "config", "searchEnabled", "selectedItem", "noDataMessage", "multiLine", "additionalInfoMessage", "scrollIntoView", "searchLabel", "customStyles", "activeItem"], outputs: ["onChange"] }, { kind: "directive", type: i2$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i7.TzDatepickerDirective, selector: "input[misTzDp]", inputs: ["dpConfig", "selectedDate", "datesDisabled", "dateMessages", "positionX", "positionY", "offsetX", "offsetY", "allowBookingOnDisabledDay", "disableBoxShadow", "disableOverLay", "openElement"], outputs: ["dateChange"] }] });
|
|
612
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CreateNonShiftModalComponent, decorators: [{
|
|
613
|
+
type: Component,
|
|
614
|
+
args: [{ selector: "app-create-non-shift-modal", template: "<div class=\"createTripsheet\">\n <h2 class=\"modal-header\">Create Non-Shift Tripsheet</h2>\n\n <div *ngIf=\"loading\" class=\"loading-container\">\n <div class=\"spinner\"></div>\n <p>Loading...</p>\n </div>\n\n <form\n *ngIf=\"!loading && createTripsheetForm\"\n [formGroup]=\"createTripsheetForm\"\n (ngSubmit)=\"onSubmit()\"\n class=\"createTripsheetForm\"\n id=\"createTripsheetForm\"\n >\n <div class=\"form-content\">\n <!-- Radio buttons row -->\n <div [ngClass]=\"{ 'radio-row': true, disabled: modalData?.shiftSelectionDisabled }\">\n <label class=\"radio-label\">\n <input class=\"modern-checkbox\" type=\"radio\" formControlName=\"direction\" value=\"OUT\" id=\"startLoc\" />\n <span>Start Location Fixed (Logout)</span>\n </label>\n <label class=\"radio-label\">\n <input class=\"modern-checkbox\" type=\"radio\" formControlName=\"direction\" value=\"IN\" id=\"endLoc\" />\n <span>End Location Fixed (Login)</span>\n </label>\n </div>\n\n <!-- Location field -->\n <div class=\"form-field\">\n <div class=\"label-cell\">\n <label class=\"locText\">Location</label>\n </div>\n <div class=\"input-cell\">\n <mis-dropdown\n [searchEnabled]=\"true\"\n [width]=\"'100%'\"\n [height]=\"'40px'\"\n [data]=\"locationDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('location')?.value\"\n [label]=\"'Select location...'\"\n (onChange)=\"createTripsheetForm.get('location')?.setValue($event)\"\n ></mis-dropdown>\n </div>\n </div>\n\n <!-- Other address field -->\n <div class=\"form-field otherAddress\" *ngIf=\"showOtherAddress\">\n <div class=\"label-cell\">\n <label>Pick/Drop point <sup>*</sup></label>\n </div>\n <div class=\"input-cell\">\n <input type=\"text\" formControlName=\"otherAddress\" class=\"modern-input\" />\n </div>\n </div>\n\n <!-- Trip category field -->\n <div class=\"form-field\" *ngIf=\"categoryDropdownData.length > 0\">\n <div class=\"label-cell\">\n <label>Trip Category</label>\n </div>\n <div class=\"input-cell\">\n <mis-dropdown\n [searchEnabled]=\"false\"\n [width]=\"'250px'\"\n [height]=\"'40px'\"\n [data]=\"categoryDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('tripCategory')?.value\"\n [label]=\"'Select category...'\"\n (onChange)=\"createTripsheetForm.get('tripCategory')?.setValue($event)\"\n ></mis-dropdown>\n </div>\n </div>\n\n <!-- Time field -->\n <div class=\"form-field\">\n <div class=\"label-cell\">\n <label class=\"fixedTimeText\">Time</label>\n </div>\n <div class=\"input-cell modern-date-container\">\n <div class=\"date-select\">\n <input\n misTzDp\n readonly\n [dpConfig]=\"dateConfig\"\n [formControl]=\"createTripsheetForm.get('date')\"\n style=\"height: 44px !important; font-size: 16px !important; cursor: pointer; vertical-align: middle !important\"\n />\n <img [src]=\"CONSTANTS_ASSETS_WIS_COMMON.IMAGES.CALENDAR_ICON\" alt=\"Calendar Icon\" class=\"date-icon data-picker-icon\" />\n </div>\n\n <mis-dropdown\n [searchEnabled]=\"false\"\n [width]=\"'5rem'\"\n [height]=\"'40px'\"\n [data]=\"hoursDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('hours')?.value\"\n [label]=\"'HH'\"\n (onChange)=\"createTripsheetForm.get('hours')?.setValue($event)\"\n ></mis-dropdown>\n\n <mis-dropdown\n [searchEnabled]=\"false\"\n [width]=\"'5rem'\"\n [height]=\"'40px'\"\n [data]=\"minutesDropdownData\"\n [selectedItem]=\"createTripsheetForm.get('mins')?.value\"\n [label]=\"'MM'\"\n (onChange)=\"createTripsheetForm.get('mins')?.setValue($event)\"\n ></mis-dropdown>\n </div>\n </div>\n </div>\n\n <div class=\"form-actions\">\n <button mis-button type=\"outline\" (click)=\"onCancel()\" [disabled]=\"isSubmitting\">Cancel</button>\n <button mis-button type=\"primary\" [disabled]=\"isSubmitting || createTripsheetForm?.invalid\">\n {{ isSubmitting ? \"Creating...\" : \"Create\" }}\n </button>\n </div>\n </form>\n</div>\n", styles: [".createTripsheet{width:40rem;margin:auto;background:#fff;border-radius:.5rem;padding:1.25rem}.createTripsheet .modal-header{font-size:1.25rem;font-weight:600;color:#333;margin:0 0 1.25rem;padding-bottom:.9375rem;border-bottom:.0625rem solid #e0e0e0}.createTripsheet .loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3.75rem 1.25rem;min-height:12.5rem}.createTripsheet .loading-container .spinner{border:.1875rem solid #f3f3f3;border-top:.1875rem solid #007bff;border-radius:50%;width:2.5rem;height:2.5rem;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.createTripsheet .createTripsheetForm .form-content{display:flex;flex-direction:column}.createTripsheet .createTripsheetForm .form-field{display:flex;align-items:flex-start;gap:1.25rem;padding:.9375rem 0}.createTripsheet .createTripsheetForm .form-field .label-cell{flex:0 0 30%;padding-top:.5rem}.createTripsheet .createTripsheetForm .form-field .label-cell label{font-size:.875rem;font-weight:500;color:#333}.createTripsheet .createTripsheetForm .form-field .input-cell{flex:1;display:flex}.createTripsheet .createTripsheetForm .radio-row{display:flex;gap:1.25rem;padding:.9375rem 0}.createTripsheet .createTripsheetForm .radio-label{font-size:.875rem;display:flex;align-items:center;cursor:pointer;margin:0}.createTripsheet .createTripsheetForm .radio-label .modern-checkbox{margin-right:.625rem;vertical-align:sub;cursor:pointer;width:1rem;height:1rem;accent-color:#007bff}.createTripsheet .createTripsheetForm .radio-label span{-webkit-user-select:none;user-select:none;white-space:nowrap}.createTripsheet .createTripsheetForm .modern-input{width:100%;padding:.5rem .75rem;border:.0625rem solid #ddd;border-radius:.25rem;font-size:.875rem;transition:border-color .3s}.createTripsheet .createTripsheetForm .modern-input:focus{outline:none;border-color:#007bff;box-shadow:0 0 0 .2rem #007bff40}.createTripsheet .createTripsheetForm .modern-input:disabled{background-color:#f5f5f5;cursor:not-allowed}.createTripsheet .createTripsheetForm sup{color:#dc3545}.createTripsheet .createTripsheetForm ::ng-deep mis-dropdown{display:block;width:100%}.createTripsheet .createTripsheetForm .modern-date-container{position:inherit;float:inherit;display:flex;align-items:center;gap:.625rem}.createTripsheet .createTripsheetForm .ng-invalid{border:.0625rem solid #dc3545}.createTripsheet .createTripsheetForm .form-actions{text-align:right;margin:.625rem;border-top:.0625rem solid #e0e0e0;padding:.625rem;display:flex;justify-content:flex-end;gap:.625rem;margin-bottom:0}.createTripsheet .createTripsheetForm .form-actions button{padding:.625rem 1.5rem;font-size:.875rem;font-weight:500;border-radius:.375rem;border:none;cursor:pointer;transition:all .3s ease;min-width:6.25rem}.createTripsheet .createTripsheetForm .form-actions button[type=primary]{background-color:#0937b2;color:#fff;border:.0625rem solid #0937b2}.createTripsheet .createTripsheetForm .form-actions button[type=outline]{background-color:transparent;color:#6c757d;border:.0625rem solid #ddd}.createTripsheet .createTripsheetForm .form-actions button:disabled{opacity:.6;cursor:not-allowed;box-shadow:none}.date-select{height:2.5rem;border:.0625rem solid #e0e0e0;padding:0;border-radius:.375rem;display:flex;flex-wrap:nowrap;justify-content:space-between;align-items:center;position:relative;overflow:hidden;left:-.5%;flex-shrink:0}.date-select input{background-color:transparent;display:inline-block;padding:.25rem 0 .25rem .75rem;width:100%;border:none;outline:none;cursor:pointer;font-size:.875rem}.date-select img{position:relative;right:1rem}.radio-row.disabled{pointer-events:none;opacity:.5}\n"] }]
|
|
615
|
+
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
|
|
616
|
+
type: Inject,
|
|
617
|
+
args: [MODAL_DATA_VAR]
|
|
618
|
+
}] }, { type: i1$3.ModalRef }, { type: i2$2.FormBuilder }, { type: HttpService }, { type: i2.ToastService }]; } });
|
|
619
|
+
|
|
360
620
|
class WisCommonModule {
|
|
361
621
|
static forRoot(env) {
|
|
362
622
|
return {
|
|
363
623
|
ngModule: WisCommonModule,
|
|
364
|
-
providers: [{ provide: CONSTANTS_WIS_COMMON, useValue: env }, ConfigurationService, LoaderService]
|
|
624
|
+
providers: [HttpService, { provide: CONSTANTS_WIS_COMMON, useValue: env }, ConfigurationService, LoaderService]
|
|
365
625
|
};
|
|
366
626
|
}
|
|
367
627
|
}
|
|
368
628
|
WisCommonModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: WisCommonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
369
|
-
WisCommonModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: WisCommonModule, declarations: [LoaderComponent, SidenavComponent], imports: [CommonModule, OverlayModule, LoaderModule], exports: [SidenavComponent] });
|
|
370
|
-
WisCommonModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: WisCommonModule,
|
|
629
|
+
WisCommonModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: WisCommonModule, declarations: [LoaderComponent, SidenavComponent, CreateNonShiftModalComponent], imports: [CommonModule, HttpClientModule, OverlayModule, LoaderModule, DropdownModule, ReactiveFormsModule, DatepickerModuleV2], exports: [SidenavComponent, CreateNonShiftModalComponent] });
|
|
630
|
+
WisCommonModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: WisCommonModule, imports: [CommonModule, HttpClientModule, OverlayModule, LoaderModule, DropdownModule, ReactiveFormsModule, DatepickerModuleV2] });
|
|
371
631
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: WisCommonModule, decorators: [{
|
|
372
632
|
type: NgModule,
|
|
373
633
|
args: [{
|
|
374
|
-
declarations: [LoaderComponent, SidenavComponent],
|
|
375
|
-
imports: [CommonModule, OverlayModule, LoaderModule],
|
|
376
|
-
providers: [
|
|
377
|
-
exports: [SidenavComponent]
|
|
634
|
+
declarations: [LoaderComponent, SidenavComponent, CreateNonShiftModalComponent],
|
|
635
|
+
imports: [CommonModule, HttpClientModule, OverlayModule, LoaderModule, DropdownModule, ReactiveFormsModule, DatepickerModuleV2,],
|
|
636
|
+
providers: [],
|
|
637
|
+
exports: [SidenavComponent, CreateNonShiftModalComponent]
|
|
378
638
|
}]
|
|
379
639
|
}] });
|
|
380
640
|
|
|
@@ -404,6 +664,243 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
404
664
|
}]
|
|
405
665
|
}], ctorParameters: function () { return []; } });
|
|
406
666
|
|
|
667
|
+
class SessionError extends Error {
|
|
668
|
+
}
|
|
669
|
+
class SessionTokenNotFoundError extends SessionError {
|
|
670
|
+
constructor() {
|
|
671
|
+
super('Session token was not found.');
|
|
672
|
+
this.name = 'SessionTokenNotFoundError';
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
class SessionTokenExpiredError extends SessionError {
|
|
676
|
+
constructor() {
|
|
677
|
+
super('Session token has expired.');
|
|
678
|
+
this.name = 'SessionTokenExpiredError';
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
class SessionTokenInvalidError extends SessionError {
|
|
682
|
+
constructor() {
|
|
683
|
+
super('Session token could not be decoded.');
|
|
684
|
+
this.name = 'SessionTokenInvalidError';
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
class SessionClaimNotFoundError extends SessionError {
|
|
688
|
+
constructor(claim) {
|
|
689
|
+
super(`Session claim "${claim}" was not found in the token.`);
|
|
690
|
+
this.claim = claim;
|
|
691
|
+
this.name = 'SessionClaimNotFoundError';
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
/** Renders failures as `claim (reason), claim (reason)` for error messages. */
|
|
695
|
+
function describeClaimFailures(failures) {
|
|
696
|
+
return failures.map(f => `${f.claim} (${f.reason})`).join(', ');
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Every claim `run`'s `next` failed to read, reported once after the block has finished.
|
|
700
|
+
* Extends `SessionClaimNotFoundError` — and reports the first failure as `claim` — so handlers
|
|
701
|
+
* written against the single-claim error keep working.
|
|
702
|
+
*/
|
|
703
|
+
class SessionClaimsMissingError extends SessionClaimNotFoundError {
|
|
704
|
+
constructor(failures) {
|
|
705
|
+
super(failures[0].claim);
|
|
706
|
+
this.failures = failures;
|
|
707
|
+
this.name = 'SessionClaimsMissingError';
|
|
708
|
+
this.message = `Session claims not found: ${describeClaimFailures(failures)}.`;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const DEFAULT_SESSION_TOKEN_PREFIX = 'sso-wis-token';
|
|
713
|
+
const DEFAULT_SESSION_TOKEN_FALLBACK_KEY = 'x-wis-token';
|
|
714
|
+
const DEFAULT_VET_TOKEN_PREFIX = 'x-vet-token';
|
|
715
|
+
const LAUNCH_SOURCE_VET = 'VET';
|
|
716
|
+
class SessionService {
|
|
717
|
+
constructor() {
|
|
718
|
+
this.config = {};
|
|
719
|
+
this.cachedToken = null;
|
|
720
|
+
this.cachedState = null;
|
|
721
|
+
this.collecting = null;
|
|
722
|
+
}
|
|
723
|
+
init(config) {
|
|
724
|
+
this.config = { ...config };
|
|
725
|
+
this.cachedToken = null;
|
|
726
|
+
this.cachedState = null;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Runs `handlers.next` and returns its result, or `handlers.error(error)` if it throws a `SessionError`.
|
|
730
|
+
* Centralizes the try/catch mechanics; the caller still supplies the actual handling logic.
|
|
731
|
+
*/
|
|
732
|
+
read(handlers) {
|
|
733
|
+
try {
|
|
734
|
+
return handlers.next(this);
|
|
735
|
+
}
|
|
736
|
+
catch (e) {
|
|
737
|
+
if (e instanceof SessionError) {
|
|
738
|
+
return handlers.error(e);
|
|
739
|
+
}
|
|
740
|
+
throw e;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Statement-style variant of `read` for blocks of assignments/side effects with no return value.
|
|
745
|
+
*
|
|
746
|
+
* `next` always runs to completion: a claim that is absent or blank yields `undefined` instead of
|
|
747
|
+
* throwing, and every such failure is reported once afterwards via `handlers.error`, as a
|
|
748
|
+
* `SessionClaimsMissingError` listing all of them. An unusable token (missing, expired,
|
|
749
|
+
* undecodable) still aborts `next` immediately with the matching `SessionError`.
|
|
750
|
+
*
|
|
751
|
+
* Collecting covers the synchronous body of `next` only — claim reads deferred into a subscription,
|
|
752
|
+
* timeout or promise run after it has returned and throw as usual.
|
|
753
|
+
*
|
|
754
|
+
* An error `next` throws for any other reason propagates untouched, except that any claims missing
|
|
755
|
+
* at the time are appended to its message and attached as `sessionClaimFailures` — a collected
|
|
756
|
+
* `undefined` reaching the block is the usual cause, and that is the part worth knowing.
|
|
757
|
+
*/
|
|
758
|
+
run(handlers) {
|
|
759
|
+
const failures = [];
|
|
760
|
+
const previous = this.collecting;
|
|
761
|
+
this.collecting = failures;
|
|
762
|
+
try {
|
|
763
|
+
handlers.next(this);
|
|
764
|
+
}
|
|
765
|
+
catch (e) {
|
|
766
|
+
if (e instanceof SessionError) {
|
|
767
|
+
handlers.error(e);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
if (failures.length && e instanceof Error) {
|
|
771
|
+
e.sessionClaimFailures = failures;
|
|
772
|
+
e.message = `${e.message} — session claims missing during run(): ${describeClaimFailures(failures)}`;
|
|
773
|
+
}
|
|
774
|
+
throw e;
|
|
775
|
+
}
|
|
776
|
+
finally {
|
|
777
|
+
this.collecting = previous;
|
|
778
|
+
}
|
|
779
|
+
if (failures.length) {
|
|
780
|
+
handlers.error(new SessionClaimsMissingError(failures));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
get isVET() {
|
|
784
|
+
return this.state.isVET;
|
|
785
|
+
}
|
|
786
|
+
get sub() { return this.getClaim('sub'); }
|
|
787
|
+
get empGuid() { return this.getClaim('employeeGUID'); }
|
|
788
|
+
get employeeId() { return this.getClaim('employeeId'); }
|
|
789
|
+
get internalSsoRole() { return this.getClaim('internal_sso_role'); }
|
|
790
|
+
get roles() { return this.getClaim('roles'); }
|
|
791
|
+
get sessionId() { return this.getClaim('sessionId'); }
|
|
792
|
+
get loggedInUser() { return this.getClaim('loggedInUser'); }
|
|
793
|
+
get exp() { return this.getClaim('exp'); }
|
|
794
|
+
get iat() { return this.getClaim('iat'); }
|
|
795
|
+
get businessUnitId() { return this.getClaim('businessUnitId'); }
|
|
796
|
+
get allowedBuids() { return this.getClaim('ALLOWED_BUIDS'); }
|
|
797
|
+
get vetUserEmailId() { return this.getClaim('vetUserEmailId'); }
|
|
798
|
+
get vetUserId() { return this.getClaim('vetUserId'); }
|
|
799
|
+
get source() { return this.getClaim('source'); }
|
|
800
|
+
get roleId() {
|
|
801
|
+
const raw = this.resolveRole(this.getClaims());
|
|
802
|
+
if (!raw) {
|
|
803
|
+
return this.missingClaim('roleId', 'absent');
|
|
804
|
+
}
|
|
805
|
+
return Number(raw);
|
|
806
|
+
}
|
|
807
|
+
/** First numeric role in `internal_sso_role` then `roles`, or `''` when the token carries none. */
|
|
808
|
+
resolveRole(claims) {
|
|
809
|
+
const candidates = [claims.internal_sso_role, ...(claims.roles ?? [])];
|
|
810
|
+
return candidates
|
|
811
|
+
.map(value => value?.trim())
|
|
812
|
+
.find(value => this.isDigitsOnly(value)) ?? '';
|
|
813
|
+
}
|
|
814
|
+
isDigitsOnly(value) {
|
|
815
|
+
return !!value && /^\d+$/.test(value);
|
|
816
|
+
}
|
|
817
|
+
getClaims() {
|
|
818
|
+
const state = this.state;
|
|
819
|
+
if (state.status === 'ok') {
|
|
820
|
+
return state.claims;
|
|
821
|
+
}
|
|
822
|
+
if (state.status === 'not-found') {
|
|
823
|
+
throw new SessionTokenNotFoundError();
|
|
824
|
+
}
|
|
825
|
+
if (state.status === 'expired') {
|
|
826
|
+
throw new SessionTokenExpiredError();
|
|
827
|
+
}
|
|
828
|
+
throw new SessionTokenInvalidError();
|
|
829
|
+
}
|
|
830
|
+
getClaim(key) {
|
|
831
|
+
const claims = this.getClaims();
|
|
832
|
+
const value = claims[key];
|
|
833
|
+
const isEmptyString = typeof value === 'string' && value.trim() === '';
|
|
834
|
+
if (value === undefined || value === null || isEmptyString) {
|
|
835
|
+
return this.missingClaim(key, isEmptyString ? 'blank' : 'absent');
|
|
836
|
+
}
|
|
837
|
+
return (typeof value === 'string' ? value.trim() : value);
|
|
838
|
+
}
|
|
839
|
+
/** Records the failure and yields `undefined` while `run` is collecting; throws otherwise. */
|
|
840
|
+
missingClaim(claim, reason) {
|
|
841
|
+
if (this.collecting) {
|
|
842
|
+
this.collecting.push({ claim, reason });
|
|
843
|
+
return undefined;
|
|
844
|
+
}
|
|
845
|
+
throw new SessionClaimNotFoundError(claim);
|
|
846
|
+
}
|
|
847
|
+
get state() {
|
|
848
|
+
const { token, isVET } = this.findToken();
|
|
849
|
+
if (!this.cachedState || token !== this.cachedToken) {
|
|
850
|
+
this.cachedToken = token;
|
|
851
|
+
this.cachedState = this.build(token, isVET);
|
|
852
|
+
}
|
|
853
|
+
return this.cachedState;
|
|
854
|
+
}
|
|
855
|
+
build(token, isVET) {
|
|
856
|
+
if (!token) {
|
|
857
|
+
return { status: 'not-found', isVET };
|
|
858
|
+
}
|
|
859
|
+
let claims;
|
|
860
|
+
try {
|
|
861
|
+
claims = jwtDecode(token);
|
|
862
|
+
}
|
|
863
|
+
catch {
|
|
864
|
+
return { status: 'invalid', isVET };
|
|
865
|
+
}
|
|
866
|
+
if (this.isExpired(claims)) {
|
|
867
|
+
return { status: 'expired', isVET };
|
|
868
|
+
}
|
|
869
|
+
return { status: 'ok', claims, isVET };
|
|
870
|
+
}
|
|
871
|
+
isExpired(claims) {
|
|
872
|
+
return typeof claims.exp === 'number' && claims.exp * 1000 <= Date.now();
|
|
873
|
+
}
|
|
874
|
+
isVETSource() {
|
|
875
|
+
return getQueryParam('source') === LAUNCH_SOURCE_VET;
|
|
876
|
+
}
|
|
877
|
+
resolveBuid() {
|
|
878
|
+
return getQueryParam('buid') || getCookie('buid') || null;
|
|
879
|
+
}
|
|
880
|
+
findToken() {
|
|
881
|
+
const buid = this.resolveBuid();
|
|
882
|
+
if (this.isVETSource()) {
|
|
883
|
+
const vetKey = this.config.vetTokenCookieKey ?? (buid ? `${DEFAULT_VET_TOKEN_PREFIX}-${buid}` : null);
|
|
884
|
+
const vetToken = (vetKey ? getCookie(vetKey) : '') || '';
|
|
885
|
+
if (vetToken) {
|
|
886
|
+
return { token: vetToken, isVET: true };
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
const sessionToken = this.config.sessionTokenCookieKey
|
|
890
|
+
? getCookie(this.config.sessionTokenCookieKey)
|
|
891
|
+
: (buid ? getCookie(`${DEFAULT_SESSION_TOKEN_PREFIX}-${buid}`) : '') || getCookie(DEFAULT_SESSION_TOKEN_FALLBACK_KEY);
|
|
892
|
+
return { token: sessionToken || '', isVET: false };
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
SessionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: SessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
896
|
+
SessionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: SessionService, providedIn: 'root' });
|
|
897
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: SessionService, decorators: [{
|
|
898
|
+
type: Injectable,
|
|
899
|
+
args: [{
|
|
900
|
+
providedIn: 'root'
|
|
901
|
+
}]
|
|
902
|
+
}] });
|
|
903
|
+
|
|
407
904
|
/*
|
|
408
905
|
* Public API Surface of wis-common
|
|
409
906
|
*/
|
|
@@ -412,5 +909,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
412
909
|
* Generated bundle index. Do not edit.
|
|
413
910
|
*/
|
|
414
911
|
|
|
415
|
-
export { AuthInterceptor, AuthModule, AuthService, ConfigurationService, LoaderService, LoggerModule, LoggerService, MfeCommunicationService, SidenavComponent, WisCommonModule, filterNavList, getCookie, getQueryParam, removeCookie, setCookie };
|
|
912
|
+
export { AuthInterceptor, AuthModule, AuthService, ConfigurationService, CreateNonShiftModalComponent, LoaderService, LoggerModule, LoggerService, MfeCommunicationService, SessionClaimNotFoundError, SessionClaimsMissingError, SessionError, SessionService, SessionTokenExpiredError, SessionTokenInvalidError, SessionTokenNotFoundError, SidenavComponent, WisCommonModule, describeClaimFailures, filterNavList, getCookie, getQueryParam, removeCookie, setCookie };
|
|
416
913
|
//# sourceMappingURL=mis-wis-common.mjs.map
|