@idea-ionic/uncommon 8.6.7 → 8.6.9
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/fesm2022/idea-ionic-uncommon.mjs +1763 -1394
- package/fesm2022/idea-ionic-uncommon.mjs.map +1 -1
- package/index.d.ts +97 -42
- package/package.json +1 -1
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import * as i1$1 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { EventEmitter, ViewChild, Input, Output, Component,
|
|
4
|
+
import { EventEmitter, ViewChild, Input, Output, Component, inject, Injectable } from '@angular/core';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
|
-
import { IonText, IonList, IonInput, IonItem, IonAccordion, IonAccordionGroup, IonButton, IonCardContent, IonCard, PopoverController, IonAvatar,
|
|
8
|
-
import { Countries, Suggestion,
|
|
9
|
-
import { IDEASelectComponent, IDEATranslatePipe, IDEAEnvironment, IDEAStorageService, IDEATranslationsService, IDEALocalizedDatePipe,
|
|
10
|
-
import
|
|
7
|
+
import { IonText, IonList, IonInput, IonItem, IonAccordion, IonAccordionGroup, Platform, IonButton, IonIcon, IonLabel, IonSpinner, IonCardContent, IonCard, PopoverController, IonAvatar, IonContent, IonGrid, IonRow, IonCol, AlertController, ModalController, IonFooter, IonButtons, IonTitle, IonToolbar, IonHeader, IonCardTitle, IonCardHeader, IonImg, IonTextarea, IonListHeader, IonFab, IonFabButton, IonBadge, IonSegmentButton, IonSegment } from '@ionic/angular/standalone';
|
|
8
|
+
import { Countries, Suggestion, ErrorReport, ClientInfo, Attachment, mdToHtml, COLORS, Contacts, ExternalCalendarSources, Sentiment, TimeInterval, PushNotificationsDevice, PushNotificationsPlatforms } from 'idea-toolbox';
|
|
9
|
+
import { IDEASelectComponent, IDEATranslatePipe, IDEAEnvironment, IDEAStorageService, IDEALoadingService, IDEAMessageService, IDEATranslationsService, IDEALocalizedDatePipe, IDEAActionSheetController } from '@idea-ionic/common';
|
|
10
|
+
import heic2any from 'heic2any';
|
|
11
|
+
import { Platform as Platform$1 } from '@ionic/angular';
|
|
12
|
+
import { Browser } from '@capacitor/browser';
|
|
13
|
+
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
|
|
11
14
|
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
|
12
15
|
import { Observable } from 'rxjs';
|
|
13
16
|
import { Network } from '@capacitor/network';
|
|
14
|
-
import {
|
|
17
|
+
import { ActivatedRoute } from '@angular/router';
|
|
15
18
|
import { PushNotifications } from '@capacitor/push-notifications';
|
|
16
19
|
|
|
17
20
|
class IDEAAddressComponent {
|
|
@@ -415,6 +418,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
415
418
|
args: ['accordion']
|
|
416
419
|
}] } });
|
|
417
420
|
|
|
421
|
+
class IDEAErrorReportingService {
|
|
422
|
+
constructor() {
|
|
423
|
+
this._env = inject(IDEAEnvironment);
|
|
424
|
+
this._http = inject(HttpClient);
|
|
425
|
+
this._platform = inject(Platform);
|
|
426
|
+
this.apiStage = this._env.idea.ideaApi?.stage || this._env.idea.ideaApi?.version;
|
|
427
|
+
this.apiUrl = 'https://'.concat([this._env.idea.ideaApi?.url, this.apiStage].filter(x => x).join('/'));
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Send the error report to the back-end.
|
|
431
|
+
*/
|
|
432
|
+
sendReport(error, forceSend) {
|
|
433
|
+
return new Promise(resolve => {
|
|
434
|
+
// skip in case we don't want to send the report or the report is useless (blank error)
|
|
435
|
+
if ((!this.shouldSend() && !forceSend) || !error?.name)
|
|
436
|
+
return resolve();
|
|
437
|
+
// prepare and send the report
|
|
438
|
+
const report = new ErrorReport({
|
|
439
|
+
version: this._env.idea.app.version,
|
|
440
|
+
stage: this.apiStage,
|
|
441
|
+
client: this.getClientInfo(),
|
|
442
|
+
type: error.name,
|
|
443
|
+
error: error.message,
|
|
444
|
+
stack: error.stack
|
|
445
|
+
});
|
|
446
|
+
this._http
|
|
447
|
+
.post(this.apiUrl.concat(`/projects/${this._env.idea.project}/errorReporting`), report)
|
|
448
|
+
.toPromise()
|
|
449
|
+
.catch(() => { }) // note: never throw an error when reporting an error
|
|
450
|
+
.finally(() => resolve());
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Whether we should send the reporting or we are in a scenario in which we should skip it.
|
|
455
|
+
*/
|
|
456
|
+
shouldSend() {
|
|
457
|
+
return this.apiStage === 'prod';
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Acquire the client's info.
|
|
461
|
+
*/
|
|
462
|
+
getClientInfo() {
|
|
463
|
+
return new ClientInfo({
|
|
464
|
+
timestamp: new Date().toISOString(),
|
|
465
|
+
platform: this._platform.platforms().join(' '),
|
|
466
|
+
screenWidth: this._platform.width(),
|
|
467
|
+
screenHeight: this._platform.height(),
|
|
468
|
+
isLandScape: this._platform.isLandscape(),
|
|
469
|
+
url: this._platform.url(),
|
|
470
|
+
referrer: document.referrer,
|
|
471
|
+
isOnline: navigator.onLine,
|
|
472
|
+
language: navigator.language,
|
|
473
|
+
userAgent: navigator.userAgent
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAErrorReportingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
477
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAErrorReportingService, providedIn: 'root' }); }
|
|
478
|
+
}
|
|
479
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAErrorReportingService, decorators: [{
|
|
480
|
+
type: Injectable,
|
|
481
|
+
args: [{ providedIn: 'root' }]
|
|
482
|
+
}], ctorParameters: () => [] });
|
|
483
|
+
|
|
418
484
|
/**
|
|
419
485
|
* @deprecated use a specific App service instead.
|
|
420
486
|
*/
|
|
@@ -463,1584 +529,1887 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
463
529
|
}], ctorParameters: () => [] });
|
|
464
530
|
|
|
465
531
|
/**
|
|
466
|
-
*
|
|
532
|
+
* Utility to monitor the network connection.
|
|
533
|
+
* Note: don't use it directly, if you're also using IDEAOfflineDataService.
|
|
467
534
|
*/
|
|
468
|
-
class
|
|
535
|
+
class IDEAOfflineService {
|
|
469
536
|
constructor() {
|
|
470
|
-
|
|
471
|
-
this.
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
537
|
+
// get an instant status
|
|
538
|
+
this._isOffline = !navigator.onLine;
|
|
539
|
+
if (Network) {
|
|
540
|
+
// set the basic listener for changes
|
|
541
|
+
this.listenerHandler = Network.addListener('networkStatusChange', status => (this._isOffline = !status.connected));
|
|
542
|
+
// create the observable to subscribe to the network changes; note: it won't run until subscribed
|
|
543
|
+
this.observable = new Observable(observer => {
|
|
544
|
+
// remove the basic listener add add one that supports a subscription
|
|
545
|
+
Network.removeAllListeners();
|
|
546
|
+
this.listenerHandler = Network.addListener('networkStatusChange', status => {
|
|
547
|
+
this._isOffline = !status.connected;
|
|
548
|
+
if (observer)
|
|
549
|
+
observer.next(status.connected);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
483
552
|
}
|
|
484
553
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
554
|
+
/**
|
|
555
|
+
* Subscribe to be notified when the connection status changes.
|
|
556
|
+
*/
|
|
557
|
+
subscribe(callback) {
|
|
558
|
+
return this.observable.subscribe(callback);
|
|
488
559
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
<p class="htmlContent" [innerHTML]="htmlContent"></p>
|
|
495
|
-
<p class="ion-text-right">
|
|
496
|
-
<ion-button color="primary" (click)="markAsRead()">
|
|
497
|
-
{{ 'COMMON.GOT_IT' | translate }}
|
|
498
|
-
</ion-button>
|
|
499
|
-
</p>
|
|
500
|
-
</ion-card-content>
|
|
501
|
-
</ion-card>
|
|
560
|
+
/**
|
|
561
|
+
* Quickly check the connection status.
|
|
562
|
+
*/
|
|
563
|
+
isOnline() {
|
|
564
|
+
return !this._isOffline;
|
|
502
565
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
selector: 'idea-announcement',
|
|
509
|
-
standalone: true,
|
|
510
|
-
imports: [CommonModule, IDEATranslatePipe, IonButton, IonCardContent, IonCard],
|
|
511
|
-
template: `
|
|
512
|
-
@if (htmlContent) {
|
|
513
|
-
<ion-card class="announcementCard" [color]="color">
|
|
514
|
-
<ion-card-content>
|
|
515
|
-
<p class="htmlContent" [innerHTML]="htmlContent"></p>
|
|
516
|
-
<p class="ion-text-right">
|
|
517
|
-
<ion-button color="primary" (click)="markAsRead()">
|
|
518
|
-
{{ 'COMMON.GOT_IT' | translate }}
|
|
519
|
-
</ion-button>
|
|
520
|
-
</p>
|
|
521
|
-
</ion-card-content>
|
|
522
|
-
</ion-card>
|
|
566
|
+
/**
|
|
567
|
+
* Quickly check the connection status.
|
|
568
|
+
*/
|
|
569
|
+
isOffline() {
|
|
570
|
+
return this._isOffline;
|
|
523
571
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
572
|
+
/**
|
|
573
|
+
* Quickly check for online connection.
|
|
574
|
+
*/
|
|
575
|
+
async check() {
|
|
576
|
+
if (!Network)
|
|
577
|
+
return false;
|
|
578
|
+
const status = await Network.getStatus();
|
|
579
|
+
this._isOffline = !status.connected;
|
|
580
|
+
return status.connected;
|
|
581
|
+
}
|
|
582
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAOfflineService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
583
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAOfflineService, providedIn: 'root' }); }
|
|
584
|
+
}
|
|
585
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAOfflineService, decorators: [{
|
|
586
|
+
type: Injectable,
|
|
587
|
+
args: [{ providedIn: 'root' }]
|
|
588
|
+
}], ctorParameters: () => [] });
|
|
529
589
|
|
|
530
590
|
/**
|
|
531
|
-
*
|
|
591
|
+
* To communicate with an AWS's API Gateway istance.
|
|
592
|
+
*
|
|
593
|
+
* Includes a rundimental cache on GET requests, until Android doesn't support SWs on local files.
|
|
594
|
+
*
|
|
595
|
+
* Note: requires an `AWSAPIAuthToken` variable to be set by IDEATinCanService service.
|
|
532
596
|
*/
|
|
533
|
-
class
|
|
597
|
+
class IDEAAWSAPIService {
|
|
534
598
|
constructor() {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
this.
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
this.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
this.iconSelect = new EventEmitter();
|
|
547
|
-
this._popover = inject(PopoverController);
|
|
548
|
-
}
|
|
549
|
-
async openPalette(event) {
|
|
550
|
-
if (this.disabled)
|
|
551
|
-
return;
|
|
552
|
-
const componentProps = { colors: this.colors, current: this.current };
|
|
553
|
-
const popover = await this._popover.create({ component: ColorsPaletteComponent, componentProps, event });
|
|
554
|
-
popover.onDidDismiss().then(res => {
|
|
555
|
-
if (res && res.data)
|
|
556
|
-
this.select.emit(res.data);
|
|
557
|
-
});
|
|
558
|
-
popover.present();
|
|
559
|
-
}
|
|
560
|
-
doIconSelect(event) {
|
|
561
|
-
if (event)
|
|
562
|
-
event.stopPropagation();
|
|
563
|
-
this.iconSelect.emit(event);
|
|
599
|
+
this._env = inject(IDEAEnvironment);
|
|
600
|
+
this._http = inject(HttpClient);
|
|
601
|
+
this._platform = inject(Platform);
|
|
602
|
+
this._tc = inject(IDEATinCanService);
|
|
603
|
+
this._storage = inject(IDEAStorageService);
|
|
604
|
+
this._errorReporting = inject(IDEAErrorReportingService);
|
|
605
|
+
this._offline = inject(IDEAOfflineService);
|
|
606
|
+
this.apiStage = this._env.idea.api?.stage || this._env.idea.api?.version;
|
|
607
|
+
this.apiUrlProject = 'https://'.concat([this._env.idea.api?.url, this.apiStage].filter(x => x).join('/'));
|
|
608
|
+
this.ideaApiStage = this._env.idea.ideaApi?.stage || this._env.idea.ideaApi?.version;
|
|
609
|
+
this.apiUrlIDEA = 'https://'.concat([this._env.idea.ideaApi?.url, this.ideaApiStage].filter(x => x).join('/'));
|
|
564
610
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
611
|
+
/**
|
|
612
|
+
* Execute an online API request.
|
|
613
|
+
* @param resource resource name (e.g. `users`)
|
|
614
|
+
* @param method enum HTTP methods
|
|
615
|
+
* @param options the request options
|
|
616
|
+
*/
|
|
617
|
+
request(resource, method, options) {
|
|
618
|
+
return new Promise((resolve, reject) => {
|
|
619
|
+
const opt = options || {};
|
|
620
|
+
// prepare the url
|
|
621
|
+
const url = this.prepareURL(resource, opt);
|
|
622
|
+
// prepare the headers and set the Authorization token
|
|
623
|
+
const headers = this.prepareHeaders(opt.headers);
|
|
624
|
+
// prepare the query params and add some info about the client
|
|
625
|
+
const params = this.prepareQueryParams(opt.params, true);
|
|
626
|
+
// execute the request
|
|
627
|
+
let req;
|
|
628
|
+
switch (method) {
|
|
629
|
+
case 'HEAD':
|
|
630
|
+
req = this._http.head(url, { headers, params });
|
|
631
|
+
break;
|
|
632
|
+
case 'POST':
|
|
633
|
+
req = this._http.post(url, opt.body, { headers, params });
|
|
634
|
+
break;
|
|
635
|
+
case 'PUT':
|
|
636
|
+
req = this._http.put(url, opt.body, { headers, params });
|
|
637
|
+
break;
|
|
638
|
+
case 'PATCH':
|
|
639
|
+
req = this._http.patch(url, opt.body, { headers, params });
|
|
640
|
+
break;
|
|
641
|
+
case 'DELETE':
|
|
642
|
+
req = this._http.delete(url, { headers, params });
|
|
643
|
+
break;
|
|
644
|
+
case 'GET':
|
|
645
|
+
default:
|
|
646
|
+
req = this._http.get(url, { headers, params });
|
|
647
|
+
}
|
|
648
|
+
// handle the request response
|
|
649
|
+
req.subscribe((res) => resolve(res), async (err) => {
|
|
650
|
+
// check if the request failed for network reasons (to trigger offline mode if needed)
|
|
651
|
+
await this._offline.check();
|
|
652
|
+
// (async) send a report, if wanted
|
|
653
|
+
if (opt.reportError)
|
|
654
|
+
this._errorReporting.sendReport(err);
|
|
655
|
+
// fix and return the error
|
|
656
|
+
this.fixErrMessageBeforeReject(err, reject);
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Prepare the URL to which execute the request.
|
|
662
|
+
*/
|
|
663
|
+
prepareURL(resource, opt) {
|
|
664
|
+
// decide whether to use IDEA's API or the project's API (or an alternative API)
|
|
665
|
+
let url = opt.idea ? this.apiUrlIDEA : opt.alternativeAPI || this.apiUrlProject;
|
|
666
|
+
// prepare a single-resource request (by id) or a normal one
|
|
667
|
+
url = `${url}/${resource}`;
|
|
668
|
+
if (opt.resourceId)
|
|
669
|
+
url = `${url}/${encodeURIComponent(opt.resourceId)}`;
|
|
670
|
+
return url;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Prepare the headers object to send with a request.
|
|
674
|
+
* Note: it always adds the Authorization token (`AWSAPIAuthToken`) and the API key (`AWSAPIKey`), if set.
|
|
675
|
+
*/
|
|
676
|
+
prepareHeaders(h) {
|
|
677
|
+
// preare the headers; note: HttpHeaders is immutable!
|
|
678
|
+
let headers = new HttpHeaders(h || null);
|
|
679
|
+
// set the Authorization token
|
|
680
|
+
if (!headers.get('Authorization') && this._tc.get('AWSAPIAuthToken'))
|
|
681
|
+
headers = headers.set('Authorization', this._tc.get('AWSAPIAuthToken'));
|
|
682
|
+
// set the API key
|
|
683
|
+
if (!headers.get('X-API-Key') && this._tc.get('AWSAPIKey'))
|
|
684
|
+
headers = headers.set('X-API-Key', this._tc.get('AWSAPIKey'));
|
|
685
|
+
return headers;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Prepare the queryParams object to send with a request; if requested, add some info about the client.
|
|
689
|
+
*/
|
|
690
|
+
prepareQueryParams(qp, addClientInfo) {
|
|
691
|
+
// prepare the query params; note: HttpParams is immutable!
|
|
692
|
+
let searchParams = new HttpParams();
|
|
693
|
+
if (qp && qp instanceof HttpParams)
|
|
694
|
+
searchParams = qp;
|
|
695
|
+
else if (qp)
|
|
696
|
+
for (const prop in qp)
|
|
697
|
+
if (qp[prop])
|
|
698
|
+
searchParams = searchParams.set(prop, qp[prop]);
|
|
699
|
+
// if requested, add app version and client platform to the info we send to the back-end
|
|
700
|
+
if (addClientInfo) {
|
|
701
|
+
searchParams = searchParams.set('_v', this._env.idea.app.version);
|
|
702
|
+
searchParams = searchParams.set('_p', this._platform.platforms().join(' '));
|
|
703
|
+
}
|
|
704
|
+
return searchParams;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Converts the error message to be readable.
|
|
708
|
+
*/
|
|
709
|
+
fixErrMessageBeforeReject(err, reject) {
|
|
710
|
+
let e;
|
|
711
|
+
try {
|
|
712
|
+
if (!err || !err.error)
|
|
713
|
+
e = {};
|
|
714
|
+
else if (typeof err.error === 'string')
|
|
715
|
+
e = JSON.parse(err.error);
|
|
716
|
+
else
|
|
717
|
+
e = err.error;
|
|
718
|
+
}
|
|
719
|
+
catch (_) {
|
|
720
|
+
e = {};
|
|
721
|
+
}
|
|
722
|
+
reject(new Error(e.message || 'Unknown error!'));
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Use the HttpClient to execute a raw request.
|
|
726
|
+
*/
|
|
727
|
+
rawRequest() {
|
|
728
|
+
return this._http;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* HEAD request.
|
|
732
|
+
* @param resource resource name (e.g. `users`)
|
|
733
|
+
* @param options the request options
|
|
734
|
+
*/
|
|
735
|
+
headResource(resource, options) {
|
|
736
|
+
return this.request(resource, 'HEAD', options);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* GET request. Include options to cache the requests (`options.useCache`), to work also offline.
|
|
740
|
+
* @param resource resource name (e.g. `users`)
|
|
741
|
+
* @param options the request options
|
|
742
|
+
*/
|
|
743
|
+
getResource(resource, options) {
|
|
744
|
+
return new Promise((resolve, reject) => {
|
|
745
|
+
const opt = options || {};
|
|
746
|
+
// if offline and with a cache mode set, force the request to the cache
|
|
747
|
+
if (!navigator.onLine && opt.useCache)
|
|
748
|
+
opt.useCache = CacheModes.CACHE_ONLY;
|
|
749
|
+
// execute the GET request online or through the cache, depending on the chosen mode
|
|
750
|
+
switch (opt.useCache) {
|
|
751
|
+
case CacheModes.CACHE_ONLY:
|
|
752
|
+
// return the result from the cache, without retrying online if a result wasn't found
|
|
753
|
+
this.getFromCache(resource, opt).then((res) => {
|
|
754
|
+
if (res)
|
|
755
|
+
resolve(res);
|
|
756
|
+
else
|
|
757
|
+
reject();
|
|
758
|
+
});
|
|
759
|
+
break;
|
|
760
|
+
case CacheModes.CACHE_FIRST:
|
|
761
|
+
// return the result from the cache
|
|
762
|
+
this.getFromCache(resource, opt).then((localRes) => {
|
|
763
|
+
// check whether the local resource is present (element or list)
|
|
764
|
+
if (localRes && (opt.resourceId || localRes.length)) {
|
|
765
|
+
resolve(localRes);
|
|
766
|
+
// asynchrounously execute the request online, to update the cache with latest data
|
|
767
|
+
this.request(resource, 'GET', opt)
|
|
768
|
+
.then((cloudRes) => {
|
|
769
|
+
// update the cache (if it fails, it's ok)
|
|
770
|
+
this.putInCache(resource, cloudRes, opt).catch(() => { });
|
|
771
|
+
})
|
|
772
|
+
.catch(() => { }); // we already returned the result, an error is acceptable
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
// if the result isn't found in the cache, return the result of the online request
|
|
776
|
+
this.request(resource, 'GET', opt)
|
|
777
|
+
.then((res) => {
|
|
778
|
+
resolve(res);
|
|
779
|
+
// update the cache (if it fails, it's ok)
|
|
780
|
+
this.putInCache(resource, res, opt).catch(() => { });
|
|
781
|
+
})
|
|
782
|
+
.catch((err) => reject(err)); // element not found
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
break;
|
|
786
|
+
case CacheModes.NETWORK_FIRST:
|
|
787
|
+
// return the result from an online request
|
|
788
|
+
this.request(resource, 'GET', opt)
|
|
789
|
+
.then((cloudRes) => {
|
|
790
|
+
resolve(cloudRes);
|
|
791
|
+
// asynchrounously get the same element from cache and decide whether to update or not
|
|
792
|
+
this.getFromCache(resource, opt).then(() => {
|
|
793
|
+
// update the cache (if it fails, it's ok)
|
|
794
|
+
this.putInCache(resource, cloudRes, opt).catch(() => { });
|
|
795
|
+
});
|
|
796
|
+
})
|
|
797
|
+
.catch(() => {
|
|
798
|
+
// if a request to the network fails, check in cache
|
|
799
|
+
this.getFromCache(resource, opt).then((res) => {
|
|
800
|
+
if (res)
|
|
801
|
+
resolve(res);
|
|
802
|
+
else
|
|
803
|
+
reject();
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
break;
|
|
807
|
+
default:
|
|
808
|
+
/* CacheModes.NO_CACHE */
|
|
809
|
+
// return the result from an online request, with no cache involved
|
|
810
|
+
this.request(resource, 'GET', opt)
|
|
811
|
+
.then((res) => resolve(res))
|
|
812
|
+
.catch((err) => reject(err));
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Observe a GET request. Include options to cache the requests (`options.useCache`), to work also offline.
|
|
818
|
+
* @param resource resource name (e.g. `users`)
|
|
819
|
+
* @param options the request options
|
|
820
|
+
*/
|
|
821
|
+
getResourceObserver(resource, options) {
|
|
822
|
+
return new Observable(observer => {
|
|
823
|
+
const opt = options || {};
|
|
824
|
+
// if offline and with a cache mode set, force the request to the cache
|
|
825
|
+
if (!navigator.onLine && opt.useCache)
|
|
826
|
+
opt.useCache = CacheModes.CACHE_ONLY;
|
|
827
|
+
// execute the GET request online or through the cache, depending on the chosen mode
|
|
828
|
+
switch (opt.useCache) {
|
|
829
|
+
case CacheModes.CACHE_ONLY:
|
|
830
|
+
// return the result from the cache, without retrying online if a result wasn't found
|
|
831
|
+
this.getFromCache(resource, opt).then((res) => {
|
|
832
|
+
if (res)
|
|
833
|
+
observer.next(res);
|
|
834
|
+
else
|
|
835
|
+
observer.error();
|
|
836
|
+
});
|
|
837
|
+
break;
|
|
838
|
+
case CacheModes.CACHE_FIRST:
|
|
839
|
+
// return the result from the cache
|
|
840
|
+
this.getFromCache(resource, opt).then((localRes) => {
|
|
841
|
+
if (localRes) {
|
|
842
|
+
observer.next(localRes);
|
|
843
|
+
// asynchrounously execute the request online, to update the cache with latest data
|
|
844
|
+
this.request(resource, 'GET', opt)
|
|
845
|
+
.then((cloudRes) => {
|
|
846
|
+
observer.next(cloudRes);
|
|
847
|
+
// update the cache (if it fails, it's ok)
|
|
848
|
+
this.putInCache(resource, cloudRes, opt).catch(() => { });
|
|
849
|
+
})
|
|
850
|
+
.catch(() => { }); // we already returned the result, an error is acceptable
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
// if the result isn't found in the cache, return the result of the online request
|
|
854
|
+
this.request(resource, 'GET', opt)
|
|
855
|
+
.then((res) => {
|
|
856
|
+
observer.next(res);
|
|
857
|
+
// update the cache (if it fails, it's ok)
|
|
858
|
+
this.putInCache(resource, res, opt).catch(() => { });
|
|
859
|
+
})
|
|
860
|
+
.catch((err) => observer.error(err)); // element not found
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
break;
|
|
864
|
+
case CacheModes.NETWORK_FIRST:
|
|
865
|
+
// return the result from an online request
|
|
866
|
+
this.request(resource, 'GET', opt)
|
|
867
|
+
.then((cloudRes) => {
|
|
868
|
+
observer.next(cloudRes);
|
|
869
|
+
// update the cache (if it fails, it's ok)
|
|
870
|
+
this.putInCache(resource, cloudRes, opt).catch(() => { });
|
|
871
|
+
})
|
|
872
|
+
.catch(() => {
|
|
873
|
+
// if a request to the network fails, check in cache
|
|
874
|
+
this.getFromCache(resource, opt).then((res) => {
|
|
875
|
+
if (res)
|
|
876
|
+
observer.next(res);
|
|
877
|
+
else
|
|
878
|
+
observer.error();
|
|
879
|
+
});
|
|
880
|
+
});
|
|
881
|
+
break;
|
|
882
|
+
default:
|
|
883
|
+
/* CacheModes.NO_CACHE */
|
|
884
|
+
// return the result from an online request, with no cache involved
|
|
885
|
+
this.request(resource, 'GET', opt)
|
|
886
|
+
.then((res) => observer.next(res))
|
|
887
|
+
.catch((err) => observer.error(err));
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Execute a GET request from the local storage.
|
|
893
|
+
* @param resource resource name (e.g. `users`)
|
|
894
|
+
* @param options the request options
|
|
895
|
+
*/
|
|
896
|
+
getFromCache(resource, options) {
|
|
897
|
+
return new Promise(resolve => {
|
|
898
|
+
const opt = options || {};
|
|
899
|
+
// prepare the url and the query params
|
|
900
|
+
const url = this.prepareURL(resource, opt);
|
|
901
|
+
const queryParams = this.prepareQueryParams(opt.params);
|
|
902
|
+
// get from storage, by the complete url
|
|
903
|
+
this._storage
|
|
904
|
+
.get(url.concat(queryParams.toString()))
|
|
905
|
+
.then((res) => (res ? resolve(res) : opt.resourceId ? resolve(null) : resolve([])));
|
|
906
|
+
});
|
|
658
907
|
}
|
|
659
|
-
|
|
660
|
-
|
|
908
|
+
/**
|
|
909
|
+
* Execute a PUT request in the local storage.
|
|
910
|
+
* @param resource resource name (e.g. `users`)
|
|
911
|
+
* @param data resource data
|
|
912
|
+
* @param options the request options
|
|
913
|
+
*/
|
|
914
|
+
putInCache(resource, data, options) {
|
|
915
|
+
return new Promise((resolve, reject) => {
|
|
916
|
+
const opt = options || {};
|
|
917
|
+
// prepare the url and the query params
|
|
918
|
+
const url = this.prepareURL(resource, opt);
|
|
919
|
+
const queryParams = this.prepareQueryParams(opt.params);
|
|
920
|
+
// put in the storage, by the complete url
|
|
921
|
+
this._storage
|
|
922
|
+
.set(url.concat(queryParams.toString()), data)
|
|
923
|
+
.then(() => resolve())
|
|
924
|
+
.catch((err) => reject(err));
|
|
925
|
+
});
|
|
661
926
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
927
|
+
/**
|
|
928
|
+
* Execute a DELETE request in the local storage.
|
|
929
|
+
* @param resource resource name (e.g. `users`)
|
|
930
|
+
* @param options the request options
|
|
931
|
+
*/
|
|
932
|
+
deleteFromCache(resource, options) {
|
|
933
|
+
return new Promise((resolve, reject) => {
|
|
934
|
+
const opt = options || {};
|
|
935
|
+
// prepare the url and the query params
|
|
936
|
+
const url = this.prepareURL(resource, opt);
|
|
937
|
+
const queryParams = this.prepareQueryParams(opt.params);
|
|
938
|
+
// delete from storage, by the complete url
|
|
939
|
+
this._storage
|
|
940
|
+
.remove(url.concat(queryParams.toString()))
|
|
941
|
+
.then(() => resolve())
|
|
942
|
+
.catch((err) => reject(err));
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* POST request.
|
|
947
|
+
* @param resource resource name (e.g. `users`)
|
|
948
|
+
* @param options the request options
|
|
949
|
+
*/
|
|
950
|
+
postResource(resource, options) {
|
|
951
|
+
return this.request(resource, 'POST', options);
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* PUT request.
|
|
955
|
+
* @param resource resource name (e.g. `users`)
|
|
956
|
+
* @param options the request options
|
|
957
|
+
*/
|
|
958
|
+
putResource(resource, options) {
|
|
959
|
+
return this.request(resource, 'PUT', options);
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* PATCH request.
|
|
963
|
+
* @param resource resource name (e.g. `users`)
|
|
964
|
+
* @param options the request options
|
|
965
|
+
*/
|
|
966
|
+
patchResource(resource, options) {
|
|
967
|
+
return this.request(resource, 'PATCH', options);
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* DELETE request.
|
|
971
|
+
* @param resource resource name (e.g. `users`)
|
|
972
|
+
* @param options the request options
|
|
973
|
+
*/
|
|
974
|
+
deleteResource(resource, options) {
|
|
975
|
+
return this.request(resource, 'DELETE', options);
|
|
976
|
+
}
|
|
977
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAAWSAPIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
978
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAAWSAPIService, providedIn: 'root' }); }
|
|
685
979
|
}
|
|
686
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type:
|
|
687
|
-
type:
|
|
688
|
-
args: [{
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
980
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAAWSAPIService, decorators: [{
|
|
981
|
+
type: Injectable,
|
|
982
|
+
args: [{ providedIn: 'root' }]
|
|
983
|
+
}], ctorParameters: () => [] });
|
|
984
|
+
/**
|
|
985
|
+
* The options of an API request.
|
|
986
|
+
*/
|
|
987
|
+
class APIRequestOption {
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Set of usable cache modes.
|
|
991
|
+
*/
|
|
992
|
+
var CacheModes;
|
|
993
|
+
(function (CacheModes) {
|
|
994
|
+
/**
|
|
995
|
+
* Return the result from an online request, with no cache involved.
|
|
996
|
+
*/
|
|
997
|
+
CacheModes[CacheModes["NO_CACHE"] = 0] = "NO_CACHE";
|
|
998
|
+
/**
|
|
999
|
+
* Return the result from an online request, storing the result in the cache, to update it with the latest data.
|
|
1000
|
+
*/
|
|
1001
|
+
CacheModes[CacheModes["NETWORK_FIRST"] = 1] = "NETWORK_FIRST";
|
|
1002
|
+
/**
|
|
1003
|
+
* Return the result from the cache, but also execute the request online, to update the cache with the latest data.
|
|
1004
|
+
*/
|
|
1005
|
+
CacheModes[CacheModes["CACHE_FIRST"] = 2] = "CACHE_FIRST";
|
|
1006
|
+
/**
|
|
1007
|
+
* Return the result from the cache, without retrying online if a result wasn't found.
|
|
1008
|
+
*/
|
|
1009
|
+
CacheModes[CacheModes["CACHE_ONLY"] = 3] = "CACHE_ONLY";
|
|
1010
|
+
})(CacheModes || (CacheModes = {}));
|
|
715
1011
|
|
|
716
|
-
class
|
|
1012
|
+
class IDEAOldAttachmentsComponent {
|
|
717
1013
|
constructor() {
|
|
718
|
-
this.
|
|
1014
|
+
this._platform = inject(Platform$1);
|
|
1015
|
+
this._loading = inject(IDEALoadingService);
|
|
1016
|
+
this._message = inject(IDEAMessageService);
|
|
1017
|
+
this._tc = inject(IDEATinCanService);
|
|
1018
|
+
this._api = inject(IDEAAWSAPIService);
|
|
1019
|
+
this._offline = inject(IDEAOfflineService);
|
|
719
1020
|
this._translate = inject(IDEATranslationsService);
|
|
720
1021
|
/**
|
|
721
|
-
* The
|
|
1022
|
+
* The team from which we want to load the resources. Default: try to guess current team.
|
|
722
1023
|
*/
|
|
723
|
-
this.
|
|
1024
|
+
this.team = null;
|
|
724
1025
|
/**
|
|
725
|
-
*
|
|
1026
|
+
* The path to the online API resource, as an array. Don't include the team. E.g. `['entities', entityId]`.
|
|
726
1027
|
*/
|
|
727
|
-
this.
|
|
1028
|
+
this.pathResource = [];
|
|
728
1029
|
/**
|
|
729
|
-
*
|
|
1030
|
+
* The array in which we want to add/remove attachments.
|
|
730
1031
|
*/
|
|
731
|
-
this.
|
|
1032
|
+
this.attachments = null;
|
|
1033
|
+
/**
|
|
1034
|
+
* Regulate the mode (view/edit).
|
|
1035
|
+
*/
|
|
1036
|
+
this.editMode = false;
|
|
1037
|
+
/**
|
|
1038
|
+
* Show errors as reported from the parent component.
|
|
1039
|
+
*/
|
|
1040
|
+
this.errors = new Set();
|
|
1041
|
+
/**
|
|
1042
|
+
* The lines attribute of the item.
|
|
1043
|
+
*/
|
|
1044
|
+
this.lines = 'none';
|
|
1045
|
+
/**
|
|
1046
|
+
* Stack of errors from the last upload.
|
|
1047
|
+
*/
|
|
1048
|
+
this.uploadErrors = [];
|
|
732
1049
|
}
|
|
733
|
-
|
|
734
|
-
if
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1050
|
+
ngOnInit() {
|
|
1051
|
+
// if the team isn't specified, try to guess it in the usual IDEA's paths
|
|
1052
|
+
this.team = this.team || this._tc.get('membership').teamId || this._tc.get('teamId');
|
|
1053
|
+
this.requestURL = `teams/${this.team}/`;
|
|
1054
|
+
if (this.pathResource && this.pathResource.length)
|
|
1055
|
+
this.requestURL = this.requestURL.concat(this.pathResource.filter(x => x).join('/'));
|
|
1056
|
+
}
|
|
1057
|
+
isCapacitor() {
|
|
1058
|
+
return this._platform.is('capacitor');
|
|
1059
|
+
}
|
|
1060
|
+
hasFieldAnError(field) {
|
|
1061
|
+
return this.errors.has(field);
|
|
1062
|
+
}
|
|
1063
|
+
browseFiles() {
|
|
1064
|
+
document.getElementById('attachmentPicker').click();
|
|
1065
|
+
}
|
|
1066
|
+
addAttachmentFromFile(ev) {
|
|
1067
|
+
this.uploadErrors = new Array();
|
|
1068
|
+
const files = ev.target ? ev.target.files : {};
|
|
1069
|
+
for (let i = 0; i < files.length; i++) {
|
|
1070
|
+
const file = files.item(i);
|
|
1071
|
+
const fullName = file.name.split('.');
|
|
1072
|
+
const format = fullName.pop();
|
|
1073
|
+
const name = fullName.join('.');
|
|
1074
|
+
this.addAttachment(name, format, file);
|
|
1075
|
+
}
|
|
738
1076
|
}
|
|
739
|
-
|
|
740
|
-
if (
|
|
1077
|
+
async takePictureAndAttach(ev) {
|
|
1078
|
+
if (ev)
|
|
1079
|
+
ev.stopPropagation();
|
|
1080
|
+
if (!this._platform.is('capacitor') || !Camera)
|
|
741
1081
|
return;
|
|
742
|
-
const
|
|
743
|
-
|
|
1082
|
+
const image = await Camera.getPhoto({
|
|
1083
|
+
quality: 90,
|
|
1084
|
+
allowEditing: false,
|
|
1085
|
+
source: CameraSource.Camera,
|
|
1086
|
+
resultType: CameraResultType.Base64
|
|
1087
|
+
});
|
|
1088
|
+
const filename = new Date().toISOString();
|
|
1089
|
+
const content = this.base64toBlob(image.base64String, 'image/jpeg');
|
|
1090
|
+
this.addAttachment(filename, image.format, content);
|
|
1091
|
+
}
|
|
1092
|
+
base64toBlob(base64str, type) {
|
|
1093
|
+
const binary = atob(base64str);
|
|
1094
|
+
const array = [];
|
|
1095
|
+
for (let i = 0; i < binary.length; i++)
|
|
1096
|
+
array.push(binary.charCodeAt(i));
|
|
1097
|
+
return new Blob([new Uint8Array(array)], { type });
|
|
1098
|
+
}
|
|
1099
|
+
async addAttachment(name, format, content) {
|
|
1100
|
+
if (format === FileFormatTypes.HEIC) {
|
|
1101
|
+
format = FileFormatTypes.JPEG;
|
|
1102
|
+
content = await heic2any({ blob: content, toType: 'image/jpeg' });
|
|
1103
|
+
}
|
|
1104
|
+
const attachment = new Attachment({ name, format });
|
|
1105
|
+
this.attachments.push(attachment);
|
|
1106
|
+
try {
|
|
1107
|
+
const signedURL = await this._api.patchResource(this.requestURL, {
|
|
1108
|
+
body: { action: 'ATTACHMENTS_PUT', attachmentId: attachment.attachmentId }
|
|
1109
|
+
});
|
|
1110
|
+
await this._api.rawRequest().put(signedURL.url, content).toPromise();
|
|
1111
|
+
attachment.attachmentId = signedURL.id;
|
|
1112
|
+
}
|
|
1113
|
+
catch (error) {
|
|
1114
|
+
this.uploadErrors.push(name);
|
|
1115
|
+
this.removeAttachment(attachment);
|
|
1116
|
+
this._message.error('IDEA_TEAMS.ATTACHMENTS.ERROR_UPLOADING_ATTACHMENT');
|
|
1117
|
+
}
|
|
744
1118
|
}
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
{ text: this._translate._('COMMON.CONFIRM'), handler: () => cb() }
|
|
750
|
-
];
|
|
751
|
-
const cssClass = 'selectableAlertMessage';
|
|
752
|
-
this._alert.create({ header, message, buttons, cssClass }).then(alert => alert.present());
|
|
1119
|
+
removeAttachment(attachment) {
|
|
1120
|
+
const index = this.attachments.indexOf(attachment);
|
|
1121
|
+
if (index !== -1)
|
|
1122
|
+
this.attachments.splice(index, 1);
|
|
753
1123
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
/>
|
|
768
|
-
</ion-item>
|
|
769
|
-
}
|
|
770
|
-
<ion-item [lines]="lines" [color]="color">
|
|
771
|
-
<ion-input
|
|
772
|
-
autocomplete="new"
|
|
773
|
-
labelPlacement="stacked"
|
|
774
|
-
[label]="'IDEA_UNCOMMON.CONTACTS.PHONE' | translate"
|
|
775
|
-
[disabled]="!editMode"
|
|
776
|
-
[placeholder]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
777
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
778
|
-
[(ngModel)]="contacts.phone"
|
|
779
|
-
/>
|
|
780
|
-
@if (!editMode) {
|
|
781
|
-
<ion-button
|
|
782
|
-
slot="end"
|
|
783
|
-
fill="clear"
|
|
784
|
-
color="dark"
|
|
785
|
-
class="marginTop"
|
|
786
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.CALL' | translate"
|
|
787
|
-
(click)="call()"
|
|
788
|
-
>
|
|
789
|
-
<ion-icon name="call" slot="icon-only" />
|
|
790
|
-
</ion-button>
|
|
1124
|
+
async openAttachment(attachment) {
|
|
1125
|
+
try {
|
|
1126
|
+
await this._loading.show();
|
|
1127
|
+
const { url } = await this._api.patchResource(this.requestURL, {
|
|
1128
|
+
body: { action: 'ATTACHMENTS_GET', attachmentId: attachment.attachmentId }
|
|
1129
|
+
});
|
|
1130
|
+
await Browser.open({ url });
|
|
1131
|
+
}
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
this._message.error('IDEA_TEAMS.ATTACHMENTS.ERROR_OPENING_ATTACHMENT');
|
|
1134
|
+
}
|
|
1135
|
+
finally {
|
|
1136
|
+
this._loading.hide();
|
|
791
1137
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1138
|
+
}
|
|
1139
|
+
getFormatIcon(format) {
|
|
1140
|
+
switch (format) {
|
|
1141
|
+
case FileFormatTypes.JPG:
|
|
1142
|
+
case FileFormatTypes.JPEG:
|
|
1143
|
+
case FileFormatTypes.PNG:
|
|
1144
|
+
return 'image';
|
|
1145
|
+
case FileFormatTypes.PDF:
|
|
1146
|
+
return 'document';
|
|
1147
|
+
default:
|
|
1148
|
+
return 'help';
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAOldAttachmentsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1152
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEAOldAttachmentsComponent, isStandalone: true, selector: "idea-old-attachments", inputs: { team: "team", pathResource: "pathResource", attachments: "attachments", editMode: "editMode", errors: "errors", lines: "lines" }, ngImport: i0, template: `
|
|
1153
|
+
@for (att of attachments; track att; let index = $index; let odd = $odd) {
|
|
1154
|
+
<ion-item
|
|
1155
|
+
class="attachments"
|
|
1156
|
+
[class.fieldHasError]="hasFieldAnError('attachments[' + index + '].attachmentId')"
|
|
1157
|
+
[class.odd]="odd"
|
|
1158
|
+
[lines]="lines"
|
|
1159
|
+
>
|
|
1160
|
+
@if (att.attachmentId && _offline.isOnline()) {
|
|
1161
|
+
<ion-button fill="clear" size="small" slot="start" (click)="openAttachment(att)">
|
|
1162
|
+
<ion-icon name="open-outline" slot="icon-only" size="small" />
|
|
813
1163
|
</ion-button>
|
|
814
1164
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAContactsComponent, decorators: [{
|
|
820
|
-
type: Component,
|
|
821
|
-
args: [{ selector: 'idea-contacts', standalone: true, imports: [CommonModule, FormsModule, IDEATranslatePipe, IonInput, IonIcon, IonButton, IonItem], template: `
|
|
822
|
-
<div class="contacts">
|
|
823
|
-
@if (showName) {
|
|
824
|
-
<ion-item [lines]="lines" [color]="color">
|
|
1165
|
+
@if (!editMode && att.attachmentId) {
|
|
1166
|
+
<ion-icon slot="start" color="medium" [name]="getFormatIcon(att.format)" [title]="att.format" />
|
|
1167
|
+
}
|
|
1168
|
+
@if (editMode && att.attachmentId) {
|
|
825
1169
|
<ion-input
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
[label]="'IDEA_UNCOMMON.CONTACTS.NAME' | translate"
|
|
829
|
-
[disabled]="!editMode"
|
|
830
|
-
[placeholder]="'IDEA_UNCOMMON.CONTACTS.NAME_HINT' | translate"
|
|
831
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.NAME_HINT' | translate"
|
|
832
|
-
[(ngModel)]="contacts.name"
|
|
1170
|
+
[(ngModel)]="att.name"
|
|
1171
|
+
(ionBlur)="$event.target.value = $event.target.value || _translate._('IDEA_TEAMS.ATTACHMENTS.NO_NAME')"
|
|
833
1172
|
/>
|
|
834
|
-
</ion-item>
|
|
835
|
-
}
|
|
836
|
-
<ion-item [lines]="lines" [color]="color">
|
|
837
|
-
<ion-input
|
|
838
|
-
autocomplete="new"
|
|
839
|
-
labelPlacement="stacked"
|
|
840
|
-
[label]="'IDEA_UNCOMMON.CONTACTS.PHONE' | translate"
|
|
841
|
-
[disabled]="!editMode"
|
|
842
|
-
[placeholder]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
843
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
844
|
-
[(ngModel)]="contacts.phone"
|
|
845
|
-
/>
|
|
846
|
-
@if (!editMode) {
|
|
847
|
-
<ion-button
|
|
848
|
-
slot="end"
|
|
849
|
-
fill="clear"
|
|
850
|
-
color="dark"
|
|
851
|
-
class="marginTop"
|
|
852
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.CALL' | translate"
|
|
853
|
-
(click)="call()"
|
|
854
|
-
>
|
|
855
|
-
<ion-icon name="call" slot="icon-only" />
|
|
856
|
-
</ion-button>
|
|
857
1173
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
[label]="'IDEA_UNCOMMON.CONTACTS.EMAIL' | translate"
|
|
864
|
-
[disabled]="!editMode"
|
|
865
|
-
[placeholder]="'IDEA_UNCOMMON.CONTACTS.EMAIL_HINT' | translate"
|
|
866
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.EMAIL_HINT' | translate"
|
|
867
|
-
[(ngModel)]="contacts.email"
|
|
868
|
-
/>
|
|
1174
|
+
@if (editMode && !att.attachmentId) {
|
|
1175
|
+
<ion-label class="loadingWarning">
|
|
1176
|
+
{{ 'IDEA_TEAMS.ATTACHMENTS.UPLOADING_ATTACHMENT_WARNING' | translate }}
|
|
1177
|
+
</ion-label>
|
|
1178
|
+
}
|
|
869
1179
|
@if (!editMode) {
|
|
1180
|
+
<ion-label>{{ att.name }}.{{ att.format }}</ion-label>
|
|
1181
|
+
}
|
|
1182
|
+
@if (!att.attachmentId) {
|
|
1183
|
+
<ion-spinner slot="end" [title]="'IDEA_TEAMS.ATTACHMENTS.UPLOADING' | translate" />
|
|
1184
|
+
}
|
|
1185
|
+
@if (editMode) {
|
|
870
1186
|
<ion-button
|
|
871
1187
|
slot="end"
|
|
1188
|
+
color="danger"
|
|
872
1189
|
fill="clear"
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
[title]="'IDEA_UNCOMMON.CONTACTS.SEND_EMAIL' | translate"
|
|
876
|
-
(click)="sendEmail()"
|
|
1190
|
+
[title]="'IDEA_TEAMS.ATTACHMENTS.REMOVE_ATTACHMENT' | translate"
|
|
1191
|
+
(click)="removeAttachment(att)"
|
|
877
1192
|
>
|
|
878
|
-
<ion-icon name="
|
|
1193
|
+
<ion-icon name="remove" slot="icon-only" />
|
|
879
1194
|
</ion-button>
|
|
880
1195
|
}
|
|
881
1196
|
</ion-item>
|
|
882
|
-
</div>
|
|
883
|
-
`, styles: [".marginTop{margin-top:14px}\n"] }]
|
|
884
|
-
}], propDecorators: { contacts: [{
|
|
885
|
-
type: Input
|
|
886
|
-
}], showName: [{
|
|
887
|
-
type: Input
|
|
888
|
-
}], editMode: [{
|
|
889
|
-
type: Input
|
|
890
|
-
}], lines: [{
|
|
891
|
-
type: Input
|
|
892
|
-
}], color: [{
|
|
893
|
-
type: Input
|
|
894
|
-
}] } });
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* A random sunday, used as reference to calculate the week days.
|
|
898
|
-
*/
|
|
899
|
-
const SUNDAY = 259200000;
|
|
900
|
-
class IDEACalendarPickerComponent {
|
|
901
|
-
constructor() {
|
|
902
|
-
this._modal = inject(ModalController);
|
|
903
|
-
this._alert = inject(AlertController);
|
|
904
|
-
this._translate = inject(IDEATranslationsService);
|
|
905
|
-
this.timePicker = false;
|
|
906
|
-
this.manualTimePicker = false;
|
|
907
|
-
this.hideClearButton = false;
|
|
908
1197
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
this.hour = this.selectedDate.getHours();
|
|
917
|
-
if (this.manualTimePicker)
|
|
918
|
-
this.minute = this.selectedDate.getMinutes();
|
|
919
|
-
else {
|
|
920
|
-
// round the minutes a multiple of 5
|
|
921
|
-
this.minute = Math.ceil(this.selectedDate.getMinutes() / 5) * 5;
|
|
922
|
-
this.selectedDate.setMinutes(this.minute);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
this.buildCalendarGrid(this.refDate);
|
|
926
|
-
this.hours = Array.from(Array(24).keys()).map(i => '0'.concat(i.toString()).slice(-2));
|
|
927
|
-
this.minutes = Array.from(Array(12).keys()).map(i => '0'.concat((i * 5).toString()).slice(-2));
|
|
928
|
-
// build the weekdays based on the current locale
|
|
929
|
-
this.weekDays = new Array();
|
|
930
|
-
const refDateForWeekDays = new Date(SUNDAY);
|
|
931
|
-
refDateForWeekDays.setDate(refDateForWeekDays.getDate() - this.getLocalisedDay(refDateForWeekDays));
|
|
932
|
-
for (let i = 0; i < 7; i++) {
|
|
933
|
-
this.weekDays.push(refDateForWeekDays.toLocaleDateString(this._translate.getCurrentLang(), { weekday: 'short' }));
|
|
934
|
-
refDateForWeekDays.setDate(refDateForWeekDays.getDate() + 1);
|
|
935
|
-
}
|
|
936
|
-
if (this.min)
|
|
937
|
-
this.dateMin = new Date(this.min);
|
|
938
|
-
if (this.max)
|
|
939
|
-
this.dateMax = new Date(this.max);
|
|
1198
|
+
<!----->
|
|
1199
|
+
@if (!editMode && !attachments?.length) {
|
|
1200
|
+
<ion-item lines="none" class="noAttachments">
|
|
1201
|
+
<ion-label>
|
|
1202
|
+
{{ 'IDEA_TEAMS.ATTACHMENTS.NO_ATTACHMENT' | translate }}
|
|
1203
|
+
</ion-label>
|
|
1204
|
+
</ion-item>
|
|
940
1205
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1206
|
+
<!----->
|
|
1207
|
+
@if (editMode) {
|
|
1208
|
+
<div>
|
|
1209
|
+
@for (err of uploadErrors; track err) {
|
|
1210
|
+
<ion-item class="attachments" [lines]="lines">
|
|
1211
|
+
<ion-icon name="alert-circle" slot="start" color="danger" />
|
|
1212
|
+
<ion-label color="danger">
|
|
1213
|
+
{{ err }}
|
|
1214
|
+
<p>{{ 'IDEA_TEAMS.ATTACHMENTS.ERROR_UPLOADING_ATTACHMENT' | translate }}</p>
|
|
1215
|
+
</ion-label>
|
|
1216
|
+
<ion-button
|
|
1217
|
+
slot="end"
|
|
1218
|
+
color="danger"
|
|
1219
|
+
fill="clear"
|
|
1220
|
+
[title]="'IDEA_TEAMS.ATTACHMENTS.HIDE_ERROR' | translate"
|
|
1221
|
+
(click)="uploadErrors.splice(uploadErrors.indexOf(err), 1)"
|
|
1222
|
+
>
|
|
1223
|
+
<ion-icon name="close" slot="icon-only" />
|
|
1224
|
+
</ion-button>
|
|
1225
|
+
</ion-item>
|
|
947
1226
|
}
|
|
948
|
-
|
|
1227
|
+
</div>
|
|
949
1228
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
if (this.getLocalisedDay(firstDateOfMonth) === j) {
|
|
964
|
-
// note: considers Sunday
|
|
965
|
-
haventFoundFirstDay = false;
|
|
966
|
-
this.calendarGrid[i][j] = new Date(firstDateOfMonth);
|
|
967
|
-
// now the I've found the first date of the month I can fill the calendar
|
|
968
|
-
// the dates fromt the previous month, until there's space in the grid
|
|
969
|
-
for (let y = this.getLocalisedDay(firstDateOfMonth); y >= 0; y--) {
|
|
970
|
-
const d = new Date(firstDateOfMonth);
|
|
971
|
-
d.setDate(firstDateOfMonth.getDate() - y);
|
|
972
|
-
this.calendarGrid[i][this.getLocalisedDay(firstDateOfMonth) - y] = d;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
else {
|
|
977
|
-
// fill the following dates until there's space in the grid
|
|
978
|
-
const d = new Date(firstDateOfMonth);
|
|
979
|
-
d.setDate(firstDateOfMonth.getDate() + index++);
|
|
980
|
-
this.calendarGrid[i][j] = d;
|
|
981
|
-
}
|
|
982
|
-
}
|
|
1229
|
+
<!----->
|
|
1230
|
+
@if (editMode) {
|
|
1231
|
+
<ion-item lines="none" class="selectable" (click)="browseFiles()">
|
|
1232
|
+
<ion-label color="medium">{{ 'IDEA_TEAMS.ATTACHMENTS.TAP_TO_ADD_ATTACHMENT' | translate }}</ion-label>
|
|
1233
|
+
<input
|
|
1234
|
+
id="attachmentPicker"
|
|
1235
|
+
type="file"
|
|
1236
|
+
multiple
|
|
1237
|
+
style="display: none"
|
|
1238
|
+
(change)="addAttachmentFromFile($event)"
|
|
1239
|
+
/>
|
|
1240
|
+
@if (editMode) {
|
|
1241
|
+
<ion-icon slot="end" name="caret-down" class="selectIcon" />
|
|
983
1242
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
}
|
|
989
|
-
setYear(year) {
|
|
990
|
-
if (!year)
|
|
991
|
-
return;
|
|
992
|
-
this.refDate.setFullYear(Number(year));
|
|
993
|
-
this.buildCalendarGrid(this.refDate);
|
|
994
|
-
}
|
|
995
|
-
addMonths(offset) {
|
|
996
|
-
this.refDate.setMonth(this.refDate.getMonth() + offset);
|
|
997
|
-
this.buildCalendarGrid(this.refDate);
|
|
998
|
-
}
|
|
999
|
-
async showMonths() {
|
|
1000
|
-
const buttons = [];
|
|
1001
|
-
const inputs = [];
|
|
1002
|
-
const month = new Date(0);
|
|
1003
|
-
for (let i = 1; i <= 12; i++) {
|
|
1004
|
-
inputs.push({
|
|
1005
|
-
type: 'radio',
|
|
1006
|
-
label: month.toLocaleDateString(this._translate.getCurrentLang(), { month: 'long' }),
|
|
1007
|
-
value: i.toString(),
|
|
1008
|
-
checked: i === this.refDate.getMonth() + 1
|
|
1009
|
-
});
|
|
1010
|
-
month.setMonth(month.getMonth() + 1);
|
|
1243
|
+
@if (editMode && isCapacitor()) {
|
|
1244
|
+
<ion-button slot="end" fill="clear" color="dark" (click)="takePictureAndAttach($event)">
|
|
1245
|
+
<ion-icon slot="icon-only" name="camera" />
|
|
1246
|
+
</ion-button>
|
|
1011
1247
|
}
|
|
1012
|
-
|
|
1013
|
-
buttons.push({
|
|
1014
|
-
text: this._translate._('COMMON.SELECT'),
|
|
1015
|
-
handler: (m) => {
|
|
1016
|
-
this.refDate.setMonth(parseInt(m, 10) - 1);
|
|
1017
|
-
this.buildCalendarGrid(this.refDate);
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
const alert = await this._alert.create({
|
|
1021
|
-
header: this._translate._('IDEA_UNCOMMON.CALENDAR.MONTH'),
|
|
1022
|
-
buttons,
|
|
1023
|
-
inputs
|
|
1024
|
-
});
|
|
1025
|
-
alert.present();
|
|
1026
|
-
}
|
|
1027
|
-
isDateSelectable(date) {
|
|
1028
|
-
if (this.dateMin && date < this.dateMin)
|
|
1029
|
-
return false;
|
|
1030
|
-
if (this.dateMax && date > this.dateMax)
|
|
1031
|
-
return false;
|
|
1032
|
-
return true;
|
|
1033
|
-
}
|
|
1034
|
-
selectDate(date) {
|
|
1035
|
-
this.selectedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), this.hour, this.minute);
|
|
1036
|
-
}
|
|
1037
|
-
selectHour(hour) {
|
|
1038
|
-
this.hour = Number(hour);
|
|
1039
|
-
this.selectedDate.setHours(this.hour);
|
|
1040
|
-
}
|
|
1041
|
-
selectMinute(minute) {
|
|
1042
|
-
this.minute = Number(minute);
|
|
1043
|
-
this.selectedDate.setMinutes(this.minute);
|
|
1044
|
-
}
|
|
1045
|
-
isSameDay(a, b) {
|
|
1046
|
-
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
1047
|
-
}
|
|
1048
|
-
isSameMonth(a, b) {
|
|
1049
|
-
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
1248
|
+
</ion-item>
|
|
1050
1249
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1250
|
+
`, isInline: true, styles: [".attachments ion-button[slot=start],.attachments ion-icon[slot=start]{margin-right:10px}.attachments .loadingWarning{font-style:italic;font-size:.8em;color:var(--ion-color-medium)}.noAttachments{font-style:italic}.selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.selectable{cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }] }); }
|
|
1251
|
+
}
|
|
1252
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAOldAttachmentsComponent, decorators: [{
|
|
1253
|
+
type: Component,
|
|
1254
|
+
args: [{ selector: 'idea-old-attachments', imports: [FormsModule, IDEATranslatePipe, IonItem, IonButton, IonIcon, IonInput, IonLabel, IonSpinner], template: `
|
|
1255
|
+
@for (att of attachments; track att; let index = $index; let odd = $odd) {
|
|
1256
|
+
<ion-item
|
|
1257
|
+
class="attachments"
|
|
1258
|
+
[class.fieldHasError]="hasFieldAnError('attachments[' + index + '].attachmentId')"
|
|
1259
|
+
[class.odd]="odd"
|
|
1260
|
+
[lines]="lines"
|
|
1261
|
+
>
|
|
1262
|
+
@if (att.attachmentId && _offline.isOnline()) {
|
|
1263
|
+
<ion-button fill="clear" size="small" slot="start" (click)="openAttachment(att)">
|
|
1264
|
+
<ion-icon name="open-outline" slot="icon-only" size="small" />
|
|
1265
|
+
</ion-button>
|
|
1266
|
+
}
|
|
1267
|
+
@if (!editMode && att.attachmentId) {
|
|
1268
|
+
<ion-icon slot="start" color="medium" [name]="getFormatIcon(att.format)" [title]="att.format" />
|
|
1269
|
+
}
|
|
1270
|
+
@if (editMode && att.attachmentId) {
|
|
1271
|
+
<ion-input
|
|
1272
|
+
[(ngModel)]="att.name"
|
|
1273
|
+
(ionBlur)="$event.target.value = $event.target.value || _translate._('IDEA_TEAMS.ATTACHMENTS.NO_NAME')"
|
|
1274
|
+
/>
|
|
1275
|
+
}
|
|
1276
|
+
@if (editMode && !att.attachmentId) {
|
|
1277
|
+
<ion-label class="loadingWarning">
|
|
1278
|
+
{{ 'IDEA_TEAMS.ATTACHMENTS.UPLOADING_ATTACHMENT_WARNING' | translate }}
|
|
1279
|
+
</ion-label>
|
|
1280
|
+
}
|
|
1281
|
+
@if (!editMode) {
|
|
1282
|
+
<ion-label>{{ att.name }}.{{ att.format }}</ion-label>
|
|
1283
|
+
}
|
|
1284
|
+
@if (!att.attachmentId) {
|
|
1285
|
+
<ion-spinner slot="end" [title]="'IDEA_TEAMS.ATTACHMENTS.UPLOADING' | translate" />
|
|
1286
|
+
}
|
|
1287
|
+
@if (editMode) {
|
|
1288
|
+
<ion-button
|
|
1289
|
+
slot="end"
|
|
1290
|
+
color="danger"
|
|
1291
|
+
fill="clear"
|
|
1292
|
+
[title]="'IDEA_TEAMS.ATTACHMENTS.REMOVE_ATTACHMENT' | translate"
|
|
1293
|
+
(click)="removeAttachment(att)"
|
|
1294
|
+
>
|
|
1295
|
+
<ion-icon name="remove" slot="icon-only" />
|
|
1296
|
+
</ion-button>
|
|
1297
|
+
}
|
|
1298
|
+
</ion-item>
|
|
1053
1299
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1300
|
+
<!----->
|
|
1301
|
+
@if (!editMode && !attachments?.length) {
|
|
1302
|
+
<ion-item lines="none" class="noAttachments">
|
|
1303
|
+
<ion-label>
|
|
1304
|
+
{{ 'IDEA_TEAMS.ATTACHMENTS.NO_ATTACHMENT' | translate }}
|
|
1305
|
+
</ion-label>
|
|
1306
|
+
</ion-item>
|
|
1056
1307
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1308
|
+
<!----->
|
|
1309
|
+
@if (editMode) {
|
|
1310
|
+
<div>
|
|
1311
|
+
@for (err of uploadErrors; track err) {
|
|
1312
|
+
<ion-item class="attachments" [lines]="lines">
|
|
1313
|
+
<ion-icon name="alert-circle" slot="start" color="danger" />
|
|
1314
|
+
<ion-label color="danger">
|
|
1315
|
+
{{ err }}
|
|
1316
|
+
<p>{{ 'IDEA_TEAMS.ATTACHMENTS.ERROR_UPLOADING_ATTACHMENT' | translate }}</p>
|
|
1317
|
+
</ion-label>
|
|
1318
|
+
<ion-button
|
|
1319
|
+
slot="end"
|
|
1320
|
+
color="danger"
|
|
1321
|
+
fill="clear"
|
|
1322
|
+
[title]="'IDEA_TEAMS.ATTACHMENTS.HIDE_ERROR' | translate"
|
|
1323
|
+
(click)="uploadErrors.splice(uploadErrors.indexOf(err), 1)"
|
|
1324
|
+
>
|
|
1325
|
+
<ion-icon name="close" slot="icon-only" />
|
|
1326
|
+
</ion-button>
|
|
1327
|
+
</ion-item>
|
|
1328
|
+
}
|
|
1329
|
+
</div>
|
|
1059
1330
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1331
|
+
<!----->
|
|
1332
|
+
@if (editMode) {
|
|
1333
|
+
<ion-item lines="none" class="selectable" (click)="browseFiles()">
|
|
1334
|
+
<ion-label color="medium">{{ 'IDEA_TEAMS.ATTACHMENTS.TAP_TO_ADD_ATTACHMENT' | translate }}</ion-label>
|
|
1335
|
+
<input
|
|
1336
|
+
id="attachmentPicker"
|
|
1337
|
+
type="file"
|
|
1338
|
+
multiple
|
|
1339
|
+
style="display: none"
|
|
1340
|
+
(change)="addAttachmentFromFile($event)"
|
|
1341
|
+
/>
|
|
1342
|
+
@if (editMode) {
|
|
1343
|
+
<ion-icon slot="end" name="caret-down" class="selectIcon" />
|
|
1344
|
+
}
|
|
1345
|
+
@if (editMode && isCapacitor()) {
|
|
1346
|
+
<ion-button slot="end" fill="clear" color="dark" (click)="takePictureAndAttach($event)">
|
|
1347
|
+
<ion-icon slot="icon-only" name="camera" />
|
|
1348
|
+
</ion-button>
|
|
1349
|
+
}
|
|
1350
|
+
</ion-item>
|
|
1062
1351
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
}
|
|
1066
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEACalendarPickerComponent, decorators: [{
|
|
1067
|
-
type: Component,
|
|
1068
|
-
args: [{ selector: 'idea-calendar-picker', standalone: true, imports: [
|
|
1069
|
-
CommonModule,
|
|
1070
|
-
FormsModule,
|
|
1071
|
-
IDEATranslatePipe,
|
|
1072
|
-
IDEALocalizedDatePipe,
|
|
1073
|
-
IonFooter,
|
|
1074
|
-
IonInput,
|
|
1075
|
-
IonCol,
|
|
1076
|
-
IonRow,
|
|
1077
|
-
IonContent,
|
|
1078
|
-
IonGrid,
|
|
1079
|
-
IonIcon,
|
|
1080
|
-
IonButton,
|
|
1081
|
-
IonButtons,
|
|
1082
|
-
IonTitle,
|
|
1083
|
-
IonToolbar,
|
|
1084
|
-
IonHeader
|
|
1085
|
-
], template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate.getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid; track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid[i][j], today) || isSameDay(calendarGrid[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid[i][j], refDate)\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid[i][j])\"\n (click)=\"selectDate(calendarGrid[i][j])\"\n >\n {{ calendarGrid[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"] }]
|
|
1086
|
-
}], propDecorators: { inputDate: [{
|
|
1352
|
+
`, styles: [".attachments ion-button[slot=start],.attachments ion-icon[slot=start]{margin-right:10px}.attachments .loadingWarning{font-style:italic;font-size:.8em;color:var(--ion-color-medium)}.noAttachments{font-style:italic}.selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.selectable{cursor:pointer}\n"] }]
|
|
1353
|
+
}], propDecorators: { team: [{
|
|
1087
1354
|
type: Input
|
|
1088
|
-
}],
|
|
1355
|
+
}], pathResource: [{
|
|
1089
1356
|
type: Input
|
|
1090
|
-
}],
|
|
1357
|
+
}], attachments: [{
|
|
1091
1358
|
type: Input
|
|
1092
|
-
}],
|
|
1359
|
+
}], editMode: [{
|
|
1093
1360
|
type: Input
|
|
1094
|
-
}],
|
|
1361
|
+
}], errors: [{
|
|
1095
1362
|
type: Input
|
|
1096
|
-
}],
|
|
1363
|
+
}], lines: [{
|
|
1097
1364
|
type: Input
|
|
1098
|
-
}]
|
|
1365
|
+
}] } });
|
|
1366
|
+
/**
|
|
1367
|
+
* The possibile file types (formats).
|
|
1368
|
+
*/
|
|
1369
|
+
var FileFormatTypes;
|
|
1370
|
+
(function (FileFormatTypes) {
|
|
1371
|
+
FileFormatTypes["JPG"] = "jpg";
|
|
1372
|
+
FileFormatTypes["JPEG"] = "jpeg";
|
|
1373
|
+
FileFormatTypes["PNG"] = "png";
|
|
1374
|
+
FileFormatTypes["PDF"] = "pdf";
|
|
1375
|
+
FileFormatTypes["HEIC"] = "heic";
|
|
1376
|
+
})(FileFormatTypes || (FileFormatTypes = {}));
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* Announcement card that can be set in any interface to show the alert for this project.
|
|
1380
|
+
*/
|
|
1381
|
+
class IDEAAnnouncementComponent {
|
|
1382
|
+
constructor() {
|
|
1383
|
+
this._env = inject(IDEAEnvironment);
|
|
1384
|
+
this._tc = inject(IDEATinCanService);
|
|
1385
|
+
this._storage = inject(IDEAStorageService);
|
|
1386
|
+
this.storageKey = (this._env.idea.project || 'app').concat('_LAST_ANNOUNCEMENT');
|
|
1387
|
+
this.color = 'dark';
|
|
1388
|
+
}
|
|
1389
|
+
async ngOnInit() {
|
|
1390
|
+
// standard IDEA: the announcement is read in the Init Guard
|
|
1391
|
+
this.announcement = this._tc.get('idea-announcement');
|
|
1392
|
+
if (this.announcement && this.announcement.content) {
|
|
1393
|
+
const lastRead = await this._storage.get(this.storageKey);
|
|
1394
|
+
if (!lastRead || this.announcement.content !== lastRead)
|
|
1395
|
+
this.htmlContent = mdToHtml(this.announcement.content);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
async markAsRead() {
|
|
1399
|
+
await this._storage.set(this.storageKey, this.announcement.content);
|
|
1400
|
+
this.htmlContent = null;
|
|
1401
|
+
}
|
|
1402
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAAnnouncementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1403
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEAAnnouncementComponent, isStandalone: true, selector: "idea-announcement", inputs: { color: "color" }, ngImport: i0, template: `
|
|
1404
|
+
@if (htmlContent) {
|
|
1405
|
+
<ion-card class="announcementCard" [color]="color">
|
|
1406
|
+
<ion-card-content>
|
|
1407
|
+
<p class="htmlContent" [innerHTML]="htmlContent"></p>
|
|
1408
|
+
<p class="ion-text-right">
|
|
1409
|
+
<ion-button color="primary" (click)="markAsRead()">
|
|
1410
|
+
{{ 'COMMON.GOT_IT' | translate }}
|
|
1411
|
+
</ion-button>
|
|
1412
|
+
</p>
|
|
1413
|
+
</ion-card-content>
|
|
1414
|
+
</ion-card>
|
|
1415
|
+
}
|
|
1416
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }] }); }
|
|
1417
|
+
}
|
|
1418
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAAnnouncementComponent, decorators: [{
|
|
1419
|
+
type: Component,
|
|
1420
|
+
args: [{
|
|
1421
|
+
selector: 'idea-announcement',
|
|
1422
|
+
standalone: true,
|
|
1423
|
+
imports: [CommonModule, IDEATranslatePipe, IonButton, IonCardContent, IonCard],
|
|
1424
|
+
template: `
|
|
1425
|
+
@if (htmlContent) {
|
|
1426
|
+
<ion-card class="announcementCard" [color]="color">
|
|
1427
|
+
<ion-card-content>
|
|
1428
|
+
<p class="htmlContent" [innerHTML]="htmlContent"></p>
|
|
1429
|
+
<p class="ion-text-right">
|
|
1430
|
+
<ion-button color="primary" (click)="markAsRead()">
|
|
1431
|
+
{{ 'COMMON.GOT_IT' | translate }}
|
|
1432
|
+
</ion-button>
|
|
1433
|
+
</p>
|
|
1434
|
+
</ion-card-content>
|
|
1435
|
+
</ion-card>
|
|
1436
|
+
}
|
|
1437
|
+
`
|
|
1438
|
+
}]
|
|
1439
|
+
}], ctorParameters: () => [], propDecorators: { color: [{
|
|
1099
1440
|
type: Input
|
|
1100
1441
|
}] } });
|
|
1101
1442
|
|
|
1102
|
-
|
|
1443
|
+
/**
|
|
1444
|
+
* Pick a color from a defined set.
|
|
1445
|
+
*/
|
|
1446
|
+
class IDEAColorPickerComponent {
|
|
1103
1447
|
constructor() {
|
|
1104
|
-
this._modal = inject(ModalController);
|
|
1105
|
-
this._translate = inject(IDEATranslationsService);
|
|
1106
|
-
/**
|
|
1107
|
-
* Whether to show the time picker (datetime) or not (date).
|
|
1108
|
-
*/
|
|
1109
|
-
this.timePicker = false;
|
|
1110
|
-
/**
|
|
1111
|
-
* Whether to show the MANUAL time picker (datetime) or not (date).
|
|
1112
|
-
*/
|
|
1113
|
-
this.manualTimePicker = false;
|
|
1114
|
-
/**
|
|
1115
|
-
* Whether to use the `epochISOString` format instead of `epochDateTime`.
|
|
1116
|
-
*/
|
|
1117
|
-
this.useISOFormat = false;
|
|
1118
1448
|
/**
|
|
1119
|
-
*
|
|
1449
|
+
* The pickable colors.
|
|
1120
1450
|
*/
|
|
1121
|
-
this.
|
|
1451
|
+
this.colors = COLORS;
|
|
1122
1452
|
/**
|
|
1123
|
-
*
|
|
1453
|
+
* On select event.
|
|
1124
1454
|
*/
|
|
1125
|
-
this.
|
|
1455
|
+
this.select = new EventEmitter();
|
|
1126
1456
|
/**
|
|
1127
|
-
*
|
|
1457
|
+
* Icon select.
|
|
1128
1458
|
*/
|
|
1129
|
-
this.hideClearButton = false;
|
|
1130
|
-
this.dateChange = new EventEmitter();
|
|
1131
1459
|
this.iconSelect = new EventEmitter();
|
|
1132
|
-
this.
|
|
1133
|
-
}
|
|
1134
|
-
ngOnInit() {
|
|
1135
|
-
this.langChangeSubscription = this._translate.onLangChange.subscribe(() => {
|
|
1136
|
-
this.valueToDisplay = this.getValueToDisplay(this.date);
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
ngOnDestroy() {
|
|
1140
|
-
if (this.langChangeSubscription)
|
|
1141
|
-
this.langChangeSubscription.unsubscribe();
|
|
1142
|
-
}
|
|
1143
|
-
ngOnChanges(changes) {
|
|
1144
|
-
if (changes.date || changes.timePicker)
|
|
1145
|
-
this.valueToDisplay = this.getValueToDisplay(this.date);
|
|
1460
|
+
this._popover = inject(PopoverController);
|
|
1146
1461
|
}
|
|
1147
|
-
async
|
|
1148
|
-
if (this.disabled
|
|
1462
|
+
async openPalette(event) {
|
|
1463
|
+
if (this.disabled)
|
|
1149
1464
|
return;
|
|
1150
|
-
this.
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
title: this.label,
|
|
1156
|
-
timePicker: this.timePicker,
|
|
1157
|
-
manualTimePicker: this.manualTimePicker,
|
|
1158
|
-
hideClearButton: this.hideClearButton,
|
|
1159
|
-
min: this.min,
|
|
1160
|
-
max: this.max
|
|
1161
|
-
}
|
|
1162
|
-
});
|
|
1163
|
-
modal.onDidDismiss().then(({ data }) => {
|
|
1164
|
-
if (data !== undefined && data !== null) {
|
|
1165
|
-
if (data === '')
|
|
1166
|
-
this.doSelect(null);
|
|
1167
|
-
else
|
|
1168
|
-
this.doSelect(data);
|
|
1169
|
-
}
|
|
1465
|
+
const componentProps = { colors: this.colors, current: this.current };
|
|
1466
|
+
const popover = await this._popover.create({ component: ColorsPaletteComponent, componentProps, event });
|
|
1467
|
+
popover.onDidDismiss().then(res => {
|
|
1468
|
+
if (res && res.data)
|
|
1469
|
+
this.select.emit(res.data);
|
|
1170
1470
|
});
|
|
1171
|
-
|
|
1172
|
-
this.isOpening = false;
|
|
1173
|
-
}
|
|
1174
|
-
getValueToDisplay(date) {
|
|
1175
|
-
return !date
|
|
1176
|
-
? ''
|
|
1177
|
-
: this._translate.formatDate(date, this.timePicker || this.manualTimePicker ? 'd MMM yyyy, HH:mm' : 'mediumDate');
|
|
1178
|
-
}
|
|
1179
|
-
doSelect(date) {
|
|
1180
|
-
this.dateChange.emit(date ? (this.useISOFormat ? date.toISOString() : date.valueOf()) : null);
|
|
1471
|
+
popover.present();
|
|
1181
1472
|
}
|
|
1182
1473
|
doIconSelect(event) {
|
|
1183
1474
|
if (event)
|
|
1184
1475
|
event.stopPropagation();
|
|
1185
|
-
this.iconSelect.emit();
|
|
1476
|
+
this.iconSelect.emit(event);
|
|
1186
1477
|
}
|
|
1187
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type:
|
|
1188
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type:
|
|
1478
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAColorPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1479
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEAColorPickerComponent, isStandalone: true, selector: "idea-color-picker", inputs: { colors: "colors", current: "current", label: "label", placeholder: "placeholder", icon: "icon", iconColor: "iconColor", disabled: "disabled", obligatory: "obligatory", lines: "lines", color: "color" }, outputs: { select: "select", iconSelect: "iconSelect" }, ngImport: i0, template: `
|
|
1189
1480
|
<ion-item
|
|
1190
|
-
class="
|
|
1481
|
+
class="colorPickerItem"
|
|
1191
1482
|
[color]="color"
|
|
1192
1483
|
[lines]="lines"
|
|
1193
|
-
[title]="placeholder || label || ''"
|
|
1194
1484
|
[button]="!disabled"
|
|
1195
|
-
[
|
|
1485
|
+
[title]="placeholder || ''"
|
|
1196
1486
|
[class.withLabel]="label"
|
|
1197
|
-
(click)="
|
|
1487
|
+
(click)="openPalette($event)"
|
|
1198
1488
|
>
|
|
1199
1489
|
@if (icon) {
|
|
1200
|
-
<ion-button
|
|
1201
|
-
|
|
1202
|
-
slot="start"
|
|
1203
|
-
[color]="iconColor"
|
|
1204
|
-
[class.marginTop]="label"
|
|
1205
|
-
(click)="doIconSelect($event)"
|
|
1206
|
-
>
|
|
1207
|
-
<ion-icon [icon]="icon" slot="icon-only" />
|
|
1490
|
+
<ion-button fill="clear" slot="start" [color]="iconColor" (click)="doIconSelect($event)">
|
|
1491
|
+
<ion-icon [name]="icon" slot="icon-only" />
|
|
1208
1492
|
</ion-button>
|
|
1209
1493
|
}
|
|
1210
1494
|
@if (label) {
|
|
1211
|
-
<ion-label
|
|
1495
|
+
<ion-label [class.selectable]="!disabled">
|
|
1212
1496
|
{{ label }}
|
|
1213
1497
|
@if (obligatory && !disabled) {
|
|
1214
1498
|
<ion-text class="obligatoryDot" />
|
|
1215
1499
|
}
|
|
1216
1500
|
</ion-label>
|
|
1217
|
-
}
|
|
1218
|
-
<ion-
|
|
1219
|
-
@if (!valueToDisplay && !disabled) {
|
|
1220
|
-
<ion-text class="placeholder" [class.selectable]="!disabled">
|
|
1221
|
-
{{ placeholder }}
|
|
1222
|
-
</ion-text>
|
|
1223
|
-
}
|
|
1224
|
-
{{ valueToDisplay }}
|
|
1225
|
-
</ion-label>
|
|
1501
|
+
}
|
|
1502
|
+
<ion-avatar slot="end" class="colorCircle" [style.background-color]="current" />
|
|
1226
1503
|
@if (!disabled) {
|
|
1227
|
-
<ion-icon slot="end"
|
|
1504
|
+
<ion-icon slot="end" name="caret-down" class="selectIcon" [class.selectable]="!disabled" />
|
|
1228
1505
|
}
|
|
1229
1506
|
</ion-item>
|
|
1230
|
-
`, isInline: true, styles: [".
|
|
1507
|
+
`, isInline: true, styles: [".colorPickerItem{min-height:48px;height:auto}.colorPickerItem .placeholder{color:var(--ion-color-medium)}.colorPickerItem .selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.selectItem.withLabel{min-height:58px;height:auto}.selectItem.withLabel .selectIcon{padding-top:25px}.selectItem.withLabel ion-button[slot=start]{margin-top:16px}.selectable{cursor:pointer}ion-avatar.colorCircle{width:20px;height:20px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonAvatar, selector: "ion-avatar" }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }] }); }
|
|
1231
1508
|
}
|
|
1232
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type:
|
|
1509
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAColorPickerComponent, decorators: [{
|
|
1233
1510
|
type: Component,
|
|
1234
|
-
args: [{ selector: 'idea-
|
|
1511
|
+
args: [{ selector: 'idea-color-picker', standalone: true, imports: [CommonModule, IonAvatar, IonText, IonLabel, IonIcon, IonButton, IonItem], template: `
|
|
1235
1512
|
<ion-item
|
|
1236
|
-
class="
|
|
1513
|
+
class="colorPickerItem"
|
|
1237
1514
|
[color]="color"
|
|
1238
1515
|
[lines]="lines"
|
|
1239
|
-
[title]="placeholder || label || ''"
|
|
1240
1516
|
[button]="!disabled"
|
|
1241
|
-
[
|
|
1517
|
+
[title]="placeholder || ''"
|
|
1242
1518
|
[class.withLabel]="label"
|
|
1243
|
-
(click)="
|
|
1519
|
+
(click)="openPalette($event)"
|
|
1244
1520
|
>
|
|
1245
1521
|
@if (icon) {
|
|
1246
|
-
<ion-button
|
|
1247
|
-
|
|
1248
|
-
slot="start"
|
|
1249
|
-
[color]="iconColor"
|
|
1250
|
-
[class.marginTop]="label"
|
|
1251
|
-
(click)="doIconSelect($event)"
|
|
1252
|
-
>
|
|
1253
|
-
<ion-icon [icon]="icon" slot="icon-only" />
|
|
1522
|
+
<ion-button fill="clear" slot="start" [color]="iconColor" (click)="doIconSelect($event)">
|
|
1523
|
+
<ion-icon [name]="icon" slot="icon-only" />
|
|
1254
1524
|
</ion-button>
|
|
1255
1525
|
}
|
|
1256
1526
|
@if (label) {
|
|
1257
|
-
<ion-label
|
|
1527
|
+
<ion-label [class.selectable]="!disabled">
|
|
1258
1528
|
{{ label }}
|
|
1259
1529
|
@if (obligatory && !disabled) {
|
|
1260
1530
|
<ion-text class="obligatoryDot" />
|
|
1261
1531
|
}
|
|
1262
1532
|
</ion-label>
|
|
1263
1533
|
}
|
|
1264
|
-
<ion-
|
|
1265
|
-
@if (!valueToDisplay && !disabled) {
|
|
1266
|
-
<ion-text class="placeholder" [class.selectable]="!disabled">
|
|
1267
|
-
{{ placeholder }}
|
|
1268
|
-
</ion-text>
|
|
1269
|
-
}
|
|
1270
|
-
{{ valueToDisplay }}
|
|
1271
|
-
</ion-label>
|
|
1534
|
+
<ion-avatar slot="end" class="colorCircle" [style.background-color]="current" />
|
|
1272
1535
|
@if (!disabled) {
|
|
1273
|
-
<ion-icon slot="end"
|
|
1536
|
+
<ion-icon slot="end" name="caret-down" class="selectIcon" [class.selectable]="!disabled" />
|
|
1274
1537
|
}
|
|
1275
1538
|
</ion-item>
|
|
1276
|
-
`, styles: [".
|
|
1277
|
-
}], propDecorators: {
|
|
1278
|
-
type: Input
|
|
1279
|
-
}], timePicker: [{
|
|
1280
|
-
type: Input
|
|
1281
|
-
}], manualTimePicker: [{
|
|
1539
|
+
`, styles: [".colorPickerItem{min-height:48px;height:auto}.colorPickerItem .placeholder{color:var(--ion-color-medium)}.colorPickerItem .selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.selectItem.withLabel{min-height:58px;height:auto}.selectItem.withLabel .selectIcon{padding-top:25px}.selectItem.withLabel ion-button[slot=start]{margin-top:16px}.selectable{cursor:pointer}ion-avatar.colorCircle{width:20px;height:20px}\n"] }]
|
|
1540
|
+
}], propDecorators: { colors: [{
|
|
1282
1541
|
type: Input
|
|
1283
|
-
}],
|
|
1542
|
+
}], current: [{
|
|
1284
1543
|
type: Input
|
|
1285
1544
|
}], label: [{
|
|
1286
1545
|
type: Input
|
|
1546
|
+
}], placeholder: [{
|
|
1547
|
+
type: Input
|
|
1287
1548
|
}], icon: [{
|
|
1288
1549
|
type: Input
|
|
1289
1550
|
}], iconColor: [{
|
|
1290
1551
|
type: Input
|
|
1291
|
-
}], lines: [{
|
|
1292
|
-
type: Input
|
|
1293
|
-
}], color: [{
|
|
1294
|
-
type: Input
|
|
1295
|
-
}], placeholder: [{
|
|
1296
|
-
type: Input
|
|
1297
1552
|
}], disabled: [{
|
|
1298
1553
|
type: Input
|
|
1299
1554
|
}], obligatory: [{
|
|
1300
1555
|
type: Input
|
|
1301
|
-
}],
|
|
1302
|
-
type: Input
|
|
1303
|
-
}], min: [{
|
|
1556
|
+
}], lines: [{
|
|
1304
1557
|
type: Input
|
|
1305
|
-
}],
|
|
1558
|
+
}], color: [{
|
|
1306
1559
|
type: Input
|
|
1307
|
-
}],
|
|
1560
|
+
}], select: [{
|
|
1308
1561
|
type: Output
|
|
1309
1562
|
}], iconSelect: [{
|
|
1310
1563
|
type: Output
|
|
1311
1564
|
}] } });
|
|
1565
|
+
/**
|
|
1566
|
+
* Component to open in a popover for displaying the colors available.
|
|
1567
|
+
*/
|
|
1568
|
+
class ColorsPaletteComponent {
|
|
1569
|
+
constructor() {
|
|
1570
|
+
this._popover = inject(PopoverController);
|
|
1571
|
+
}
|
|
1572
|
+
pick(color) {
|
|
1573
|
+
this._popover.dismiss(color);
|
|
1574
|
+
}
|
|
1575
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: ColorsPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1576
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: ColorsPaletteComponent, isStandalone: true, selector: "idea-colors-palette", inputs: { colors: "colors", current: "current" }, ngImport: i0, template: `
|
|
1577
|
+
<ion-content>
|
|
1578
|
+
<ion-grid>
|
|
1579
|
+
<ion-row>
|
|
1580
|
+
@for (c of colors; track c) {
|
|
1581
|
+
<ion-col [size]="2">
|
|
1582
|
+
<ion-avatar
|
|
1583
|
+
class="colorCircle tappable"
|
|
1584
|
+
[style.background-color]="c.hex"
|
|
1585
|
+
[title]="c.name || c.hex"
|
|
1586
|
+
(click)="pick(c.hex)"
|
|
1587
|
+
>
|
|
1588
|
+
@if (c.hex === current) {
|
|
1589
|
+
<ion-icon name="checkmark" />
|
|
1590
|
+
}
|
|
1591
|
+
</ion-avatar>
|
|
1592
|
+
</ion-col>
|
|
1593
|
+
}
|
|
1594
|
+
</ion-row>
|
|
1595
|
+
</ion-grid>
|
|
1596
|
+
</ion-content>
|
|
1597
|
+
`, isInline: true, styles: ["ion-content{--background: var(--ion-color-white)}ion-avatar.colorCircle{width:20px;height:20px}.tappable{cursor:pointer}ion-avatar.colorCircle ion-icon{color:#fff;font-size:1.2em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: IonRow, selector: "ion-row" }, { kind: "component", type: IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: IonAvatar, selector: "ion-avatar" }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
|
|
1598
|
+
}
|
|
1599
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: ColorsPaletteComponent, decorators: [{
|
|
1600
|
+
type: Component,
|
|
1601
|
+
args: [{ selector: 'idea-colors-palette', standalone: true, imports: [CommonModule, IonContent, IonGrid, IonRow, IonCol, IonAvatar, IonIcon], template: `
|
|
1602
|
+
<ion-content>
|
|
1603
|
+
<ion-grid>
|
|
1604
|
+
<ion-row>
|
|
1605
|
+
@for (c of colors; track c) {
|
|
1606
|
+
<ion-col [size]="2">
|
|
1607
|
+
<ion-avatar
|
|
1608
|
+
class="colorCircle tappable"
|
|
1609
|
+
[style.background-color]="c.hex"
|
|
1610
|
+
[title]="c.name || c.hex"
|
|
1611
|
+
(click)="pick(c.hex)"
|
|
1612
|
+
>
|
|
1613
|
+
@if (c.hex === current) {
|
|
1614
|
+
<ion-icon name="checkmark" />
|
|
1615
|
+
}
|
|
1616
|
+
</ion-avatar>
|
|
1617
|
+
</ion-col>
|
|
1618
|
+
}
|
|
1619
|
+
</ion-row>
|
|
1620
|
+
</ion-grid>
|
|
1621
|
+
</ion-content>
|
|
1622
|
+
`, styles: ["ion-content{--background: var(--ion-color-white)}ion-avatar.colorCircle{width:20px;height:20px}.tappable{cursor:pointer}ion-avatar.colorCircle ion-icon{color:#fff;font-size:1.2em}\n"] }]
|
|
1623
|
+
}], propDecorators: { colors: [{
|
|
1624
|
+
type: Input
|
|
1625
|
+
}], current: [{
|
|
1626
|
+
type: Input
|
|
1627
|
+
}] } });
|
|
1312
1628
|
|
|
1313
|
-
class
|
|
1629
|
+
class IDEAContactsComponent {
|
|
1314
1630
|
constructor() {
|
|
1631
|
+
this._alert = inject(AlertController);
|
|
1632
|
+
this._translate = inject(IDEATranslationsService);
|
|
1315
1633
|
/**
|
|
1316
|
-
*
|
|
1634
|
+
* The contacts to manage.
|
|
1317
1635
|
*/
|
|
1318
|
-
this.
|
|
1319
|
-
|
|
1636
|
+
this.contacts = new Contacts();
|
|
1637
|
+
/**
|
|
1638
|
+
* If true, show the field `name`.
|
|
1639
|
+
*/
|
|
1640
|
+
this.showName = false;
|
|
1641
|
+
/**
|
|
1642
|
+
* Whether the fields are editable or disabled.
|
|
1643
|
+
*/
|
|
1644
|
+
this.editMode = true;
|
|
1320
1645
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
refDate.setSeconds(refDate.getSeconds() + this.default);
|
|
1327
|
-
this.hours = refDate.getHours();
|
|
1328
|
-
this.minutes = refDate.getMinutes();
|
|
1329
|
-
this.seconds = this.hideSeconds ? 0 : refDate.getSeconds();
|
|
1330
|
-
}
|
|
1646
|
+
sendEmail() {
|
|
1647
|
+
if (!this.contacts.email)
|
|
1648
|
+
return;
|
|
1649
|
+
const url = `mailto:${this.contacts.email}`;
|
|
1650
|
+
this.preExternalAction(this.contacts.email, () => window.open(url, '_system'));
|
|
1331
1651
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
case 'hours':
|
|
1338
|
-
const hh = Number(this.hours) || 0;
|
|
1339
|
-
this.hours = hh > 23 ? 23 : hh < 0 ? 0 : hh;
|
|
1340
|
-
break;
|
|
1341
|
-
case 'minutes':
|
|
1342
|
-
const mm = Number(this.minutes) || 0;
|
|
1343
|
-
this.minutes = mm > 59 ? 59 : mm < 0 ? 0 : mm;
|
|
1344
|
-
break;
|
|
1345
|
-
case 'seconds':
|
|
1346
|
-
const ss = Number(this.seconds) || 0;
|
|
1347
|
-
if (this.hideSeconds)
|
|
1348
|
-
this.seconds = 0;
|
|
1349
|
-
else
|
|
1350
|
-
this.seconds = ss > 59 ? 59 : ss < 0 ? 0 : ss;
|
|
1351
|
-
break;
|
|
1352
|
-
}
|
|
1353
|
-
this.set.emit(this.hours * 3600 + this.minutes * 60 + this.seconds);
|
|
1652
|
+
call() {
|
|
1653
|
+
if (!this.contacts.phone)
|
|
1654
|
+
return;
|
|
1655
|
+
const url = `tel:${this.contacts.phone}`;
|
|
1656
|
+
this.preExternalAction(this.contacts.phone, () => window.open(url, '_system'));
|
|
1354
1657
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
<ion-label class="value">
|
|
1370
|
-
<ion-input
|
|
1371
|
-
type="number"
|
|
1372
|
-
min="0"
|
|
1373
|
-
max="23"
|
|
1374
|
-
inputmode="numeric"
|
|
1375
|
-
[disabled]="disabled"
|
|
1376
|
-
[(ngModel)]="hours"
|
|
1377
|
-
(ionChange)="setDuration('hours')"
|
|
1378
|
-
/>
|
|
1379
|
-
<ion-text>{{
|
|
1380
|
-
((shortLabels ? 'IDEA_UNCOMMON.DURATION.HH' : 'IDEA_UNCOMMON.DURATION.HOURS') | translate).toLowerCase()
|
|
1381
|
-
}}</ion-text>
|
|
1382
|
-
<ion-input
|
|
1383
|
-
type="number"
|
|
1384
|
-
min="0"
|
|
1385
|
-
max="59"
|
|
1386
|
-
inputmode="numeric"
|
|
1387
|
-
[disabled]="disabled"
|
|
1388
|
-
[(ngModel)]="minutes"
|
|
1389
|
-
(ionChange)="setDuration('minutes')"
|
|
1390
|
-
/>
|
|
1391
|
-
<ion-text>{{
|
|
1392
|
-
((shortLabels ? 'IDEA_UNCOMMON.DURATION.MM' : 'IDEA_UNCOMMON.DURATION.MINUTES') | translate).toLowerCase()
|
|
1393
|
-
}}</ion-text>
|
|
1394
|
-
@if (!hideSeconds) {
|
|
1658
|
+
preExternalAction(message, cb) {
|
|
1659
|
+
const header = this._translate._('COMMON.DO_YOU_WANT_TO_PROCEED');
|
|
1660
|
+
const buttons = [
|
|
1661
|
+
{ text: this._translate._('COMMON.CANCEL') },
|
|
1662
|
+
{ text: this._translate._('COMMON.CONFIRM'), handler: () => cb() }
|
|
1663
|
+
];
|
|
1664
|
+
const cssClass = 'selectableAlertMessage';
|
|
1665
|
+
this._alert.create({ header, message, buttons, cssClass }).then(alert => alert.present());
|
|
1666
|
+
}
|
|
1667
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAContactsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1668
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEAContactsComponent, isStandalone: true, selector: "idea-contacts", inputs: { contacts: "contacts", showName: "showName", editMode: "editMode", lines: "lines", color: "color" }, ngImport: i0, template: `
|
|
1669
|
+
<div class="contacts">
|
|
1670
|
+
@if (showName) {
|
|
1671
|
+
<ion-item [lines]="lines" [color]="color">
|
|
1395
1672
|
<ion-input
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
[
|
|
1401
|
-
[
|
|
1402
|
-
(
|
|
1673
|
+
autocomplete="new"
|
|
1674
|
+
labelPlacement="stacked"
|
|
1675
|
+
[label]="'IDEA_UNCOMMON.CONTACTS.NAME' | translate"
|
|
1676
|
+
[disabled]="!editMode"
|
|
1677
|
+
[placeholder]="'IDEA_UNCOMMON.CONTACTS.NAME_HINT' | translate"
|
|
1678
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.NAME_HINT' | translate"
|
|
1679
|
+
[(ngModel)]="contacts.name"
|
|
1403
1680
|
/>
|
|
1681
|
+
</ion-item>
|
|
1682
|
+
}
|
|
1683
|
+
<ion-item [lines]="lines" [color]="color">
|
|
1684
|
+
<ion-input
|
|
1685
|
+
autocomplete="new"
|
|
1686
|
+
labelPlacement="stacked"
|
|
1687
|
+
[label]="'IDEA_UNCOMMON.CONTACTS.PHONE' | translate"
|
|
1688
|
+
[disabled]="!editMode"
|
|
1689
|
+
[placeholder]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
1690
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
1691
|
+
[(ngModel)]="contacts.phone"
|
|
1692
|
+
/>
|
|
1693
|
+
@if (!editMode) {
|
|
1694
|
+
<ion-button
|
|
1695
|
+
slot="end"
|
|
1696
|
+
fill="clear"
|
|
1697
|
+
color="dark"
|
|
1698
|
+
class="marginTop"
|
|
1699
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.CALL' | translate"
|
|
1700
|
+
(click)="call()"
|
|
1701
|
+
>
|
|
1702
|
+
<ion-icon name="call" slot="icon-only" />
|
|
1703
|
+
</ion-button>
|
|
1404
1704
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1705
|
+
</ion-item>
|
|
1706
|
+
<ion-item [lines]="lines" [color]="color">
|
|
1707
|
+
<ion-input
|
|
1708
|
+
autocomplete="new"
|
|
1709
|
+
labelPlacement="stacked"
|
|
1710
|
+
[label]="'IDEA_UNCOMMON.CONTACTS.EMAIL' | translate"
|
|
1711
|
+
[disabled]="!editMode"
|
|
1712
|
+
[placeholder]="'IDEA_UNCOMMON.CONTACTS.EMAIL_HINT' | translate"
|
|
1713
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.EMAIL_HINT' | translate"
|
|
1714
|
+
[(ngModel)]="contacts.email"
|
|
1715
|
+
/>
|
|
1716
|
+
@if (!editMode) {
|
|
1717
|
+
<ion-button
|
|
1718
|
+
slot="end"
|
|
1719
|
+
fill="clear"
|
|
1720
|
+
color="dark"
|
|
1721
|
+
class="marginTop"
|
|
1722
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.SEND_EMAIL' | translate"
|
|
1723
|
+
(click)="sendEmail()"
|
|
1724
|
+
>
|
|
1725
|
+
<ion-icon name="mail" slot="icon-only" />
|
|
1726
|
+
</ion-button>
|
|
1411
1727
|
}
|
|
1412
|
-
</ion-
|
|
1413
|
-
</
|
|
1414
|
-
`, isInline: true, styles: [".
|
|
1728
|
+
</ion-item>
|
|
1729
|
+
</div>
|
|
1730
|
+
`, isInline: true, styles: [".marginTop{margin-top:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }] }); }
|
|
1415
1731
|
}
|
|
1416
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type:
|
|
1732
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAContactsComponent, decorators: [{
|
|
1417
1733
|
type: Component,
|
|
1418
|
-
args: [{ selector: 'idea-
|
|
1419
|
-
<
|
|
1420
|
-
@if (
|
|
1421
|
-
<ion-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1734
|
+
args: [{ selector: 'idea-contacts', standalone: true, imports: [CommonModule, FormsModule, IDEATranslatePipe, IonInput, IonIcon, IonButton, IonItem], template: `
|
|
1735
|
+
<div class="contacts">
|
|
1736
|
+
@if (showName) {
|
|
1737
|
+
<ion-item [lines]="lines" [color]="color">
|
|
1738
|
+
<ion-input
|
|
1739
|
+
autocomplete="new"
|
|
1740
|
+
labelPlacement="stacked"
|
|
1741
|
+
[label]="'IDEA_UNCOMMON.CONTACTS.NAME' | translate"
|
|
1742
|
+
[disabled]="!editMode"
|
|
1743
|
+
[placeholder]="'IDEA_UNCOMMON.CONTACTS.NAME_HINT' | translate"
|
|
1744
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.NAME_HINT' | translate"
|
|
1745
|
+
[(ngModel)]="contacts.name"
|
|
1746
|
+
/>
|
|
1747
|
+
</ion-item>
|
|
1430
1748
|
}
|
|
1431
|
-
<ion-
|
|
1749
|
+
<ion-item [lines]="lines" [color]="color">
|
|
1432
1750
|
<ion-input
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
[
|
|
1438
|
-
[
|
|
1439
|
-
(
|
|
1751
|
+
autocomplete="new"
|
|
1752
|
+
labelPlacement="stacked"
|
|
1753
|
+
[label]="'IDEA_UNCOMMON.CONTACTS.PHONE' | translate"
|
|
1754
|
+
[disabled]="!editMode"
|
|
1755
|
+
[placeholder]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
1756
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.PHONE_HINT' | translate"
|
|
1757
|
+
[(ngModel)]="contacts.phone"
|
|
1440
1758
|
/>
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1759
|
+
@if (!editMode) {
|
|
1760
|
+
<ion-button
|
|
1761
|
+
slot="end"
|
|
1762
|
+
fill="clear"
|
|
1763
|
+
color="dark"
|
|
1764
|
+
class="marginTop"
|
|
1765
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.CALL' | translate"
|
|
1766
|
+
(click)="call()"
|
|
1767
|
+
>
|
|
1768
|
+
<ion-icon name="call" slot="icon-only" />
|
|
1769
|
+
</ion-button>
|
|
1770
|
+
}
|
|
1771
|
+
</ion-item>
|
|
1772
|
+
<ion-item [lines]="lines" [color]="color">
|
|
1444
1773
|
<ion-input
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
[
|
|
1450
|
-
[
|
|
1451
|
-
(
|
|
1774
|
+
autocomplete="new"
|
|
1775
|
+
labelPlacement="stacked"
|
|
1776
|
+
[label]="'IDEA_UNCOMMON.CONTACTS.EMAIL' | translate"
|
|
1777
|
+
[disabled]="!editMode"
|
|
1778
|
+
[placeholder]="'IDEA_UNCOMMON.CONTACTS.EMAIL_HINT' | translate"
|
|
1779
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.EMAIL_HINT' | translate"
|
|
1780
|
+
[(ngModel)]="contacts.email"
|
|
1452
1781
|
/>
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
(ionChange)="setDuration('seconds')"
|
|
1465
|
-
/>
|
|
1466
|
-
}
|
|
1467
|
-
@if (!hideSeconds) {
|
|
1468
|
-
<ion-text>
|
|
1469
|
-
{{
|
|
1470
|
-
((shortLabels ? 'IDEA_UNCOMMON.DURATION.SS' : 'IDEA_UNCOMMON.DURATION.SECONDS') | translate).toLowerCase()
|
|
1471
|
-
}}
|
|
1472
|
-
</ion-text>
|
|
1782
|
+
@if (!editMode) {
|
|
1783
|
+
<ion-button
|
|
1784
|
+
slot="end"
|
|
1785
|
+
fill="clear"
|
|
1786
|
+
color="dark"
|
|
1787
|
+
class="marginTop"
|
|
1788
|
+
[title]="'IDEA_UNCOMMON.CONTACTS.SEND_EMAIL' | translate"
|
|
1789
|
+
(click)="sendEmail()"
|
|
1790
|
+
>
|
|
1791
|
+
<ion-icon name="mail" slot="icon-only" />
|
|
1792
|
+
</ion-button>
|
|
1473
1793
|
}
|
|
1474
|
-
</ion-
|
|
1475
|
-
</
|
|
1476
|
-
`, styles: [".
|
|
1477
|
-
}],
|
|
1478
|
-
type: Input
|
|
1479
|
-
}], label: [{
|
|
1480
|
-
type: Input
|
|
1481
|
-
}], icon: [{
|
|
1482
|
-
type: Input
|
|
1483
|
-
}], title: [{
|
|
1484
|
-
type: Input
|
|
1485
|
-
}], disabled: [{
|
|
1486
|
-
type: Input
|
|
1487
|
-
}], obligatory: [{
|
|
1488
|
-
type: Input
|
|
1489
|
-
}], lines: [{
|
|
1490
|
-
type: Input
|
|
1491
|
-
}], color: [{
|
|
1492
|
-
type: Input
|
|
1493
|
-
}], hideSeconds: [{
|
|
1794
|
+
</ion-item>
|
|
1795
|
+
</div>
|
|
1796
|
+
`, styles: [".marginTop{margin-top:14px}\n"] }]
|
|
1797
|
+
}], propDecorators: { contacts: [{
|
|
1494
1798
|
type: Input
|
|
1495
|
-
}],
|
|
1799
|
+
}], showName: [{
|
|
1496
1800
|
type: Input
|
|
1497
|
-
}],
|
|
1498
|
-
type:
|
|
1499
|
-
}]
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
this._http = inject(HttpClient);
|
|
1505
|
-
this._platform = inject(Platform);
|
|
1506
|
-
this.apiStage = this._env.idea.ideaApi?.stage || this._env.idea.ideaApi?.version;
|
|
1507
|
-
this.apiUrl = 'https://'.concat([this._env.idea.ideaApi?.url, this.apiStage].filter(x => x).join('/'));
|
|
1508
|
-
}
|
|
1509
|
-
/**
|
|
1510
|
-
* Send the error report to the back-end.
|
|
1511
|
-
*/
|
|
1512
|
-
sendReport(error, forceSend) {
|
|
1513
|
-
return new Promise(resolve => {
|
|
1514
|
-
// skip in case we don't want to send the report or the report is useless (blank error)
|
|
1515
|
-
if ((!this.shouldSend() && !forceSend) || !error?.name)
|
|
1516
|
-
return resolve();
|
|
1517
|
-
// prepare and send the report
|
|
1518
|
-
const report = new ErrorReport({
|
|
1519
|
-
version: this._env.idea.app.version,
|
|
1520
|
-
stage: this.apiStage,
|
|
1521
|
-
client: this.getClientInfo(),
|
|
1522
|
-
type: error.name,
|
|
1523
|
-
error: error.message,
|
|
1524
|
-
stack: error.stack
|
|
1525
|
-
});
|
|
1526
|
-
this._http
|
|
1527
|
-
.post(this.apiUrl.concat(`/projects/${this._env.idea.project}/errorReporting`), report)
|
|
1528
|
-
.toPromise()
|
|
1529
|
-
.catch(() => { }) // note: never throw an error when reporting an error
|
|
1530
|
-
.finally(() => resolve());
|
|
1531
|
-
});
|
|
1532
|
-
}
|
|
1533
|
-
/**
|
|
1534
|
-
* Whether we should send the reporting or we are in a scenario in which we should skip it.
|
|
1535
|
-
*/
|
|
1536
|
-
shouldSend() {
|
|
1537
|
-
return this.apiStage === 'prod';
|
|
1538
|
-
}
|
|
1539
|
-
/**
|
|
1540
|
-
* Acquire the client's info.
|
|
1541
|
-
*/
|
|
1542
|
-
getClientInfo() {
|
|
1543
|
-
return new ClientInfo({
|
|
1544
|
-
timestamp: new Date().toISOString(),
|
|
1545
|
-
platform: this._platform.platforms().join(' '),
|
|
1546
|
-
screenWidth: this._platform.width(),
|
|
1547
|
-
screenHeight: this._platform.height(),
|
|
1548
|
-
isLandScape: this._platform.isLandscape(),
|
|
1549
|
-
url: this._platform.url(),
|
|
1550
|
-
referrer: document.referrer,
|
|
1551
|
-
isOnline: navigator.onLine,
|
|
1552
|
-
language: navigator.language,
|
|
1553
|
-
userAgent: navigator.userAgent
|
|
1554
|
-
});
|
|
1555
|
-
}
|
|
1556
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAErrorReportingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1557
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAErrorReportingService, providedIn: 'root' }); }
|
|
1558
|
-
}
|
|
1559
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAErrorReportingService, decorators: [{
|
|
1560
|
-
type: Injectable,
|
|
1561
|
-
args: [{ providedIn: 'root' }]
|
|
1562
|
-
}], ctorParameters: () => [] });
|
|
1801
|
+
}], editMode: [{
|
|
1802
|
+
type: Input
|
|
1803
|
+
}], lines: [{
|
|
1804
|
+
type: Input
|
|
1805
|
+
}], color: [{
|
|
1806
|
+
type: Input
|
|
1807
|
+
}] } });
|
|
1563
1808
|
|
|
1564
1809
|
/**
|
|
1565
|
-
*
|
|
1566
|
-
* Note: don't use it directly, if you're also using IDEAOfflineDataService.
|
|
1810
|
+
* A random sunday, used as reference to calculate the week days.
|
|
1567
1811
|
*/
|
|
1568
|
-
|
|
1812
|
+
const SUNDAY = 259200000;
|
|
1813
|
+
class IDEACalendarPickerComponent {
|
|
1569
1814
|
constructor() {
|
|
1570
|
-
|
|
1571
|
-
this.
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1815
|
+
this._modal = inject(ModalController);
|
|
1816
|
+
this._alert = inject(AlertController);
|
|
1817
|
+
this._translate = inject(IDEATranslationsService);
|
|
1818
|
+
this.timePicker = false;
|
|
1819
|
+
this.manualTimePicker = false;
|
|
1820
|
+
this.hideClearButton = false;
|
|
1821
|
+
}
|
|
1822
|
+
ngOnInit() {
|
|
1823
|
+
this.today = new Date();
|
|
1824
|
+
this.refDate = this.inputDate ? new Date(this.inputDate) : new Date(this.today);
|
|
1825
|
+
this.selectedDate = new Date(this.refDate);
|
|
1826
|
+
this.hour = 12; // to endure timezones
|
|
1827
|
+
this.minute = 0;
|
|
1828
|
+
if (this.timePicker || this.manualTimePicker) {
|
|
1829
|
+
this.hour = this.selectedDate.getHours();
|
|
1830
|
+
if (this.manualTimePicker)
|
|
1831
|
+
this.minute = this.selectedDate.getMinutes();
|
|
1832
|
+
else {
|
|
1833
|
+
// round the minutes a multiple of 5
|
|
1834
|
+
this.minute = Math.ceil(this.selectedDate.getMinutes() / 5) * 5;
|
|
1835
|
+
this.selectedDate.setMinutes(this.minute);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
this.buildCalendarGrid(this.refDate);
|
|
1839
|
+
this.hours = Array.from(Array(24).keys()).map(i => '0'.concat(i.toString()).slice(-2));
|
|
1840
|
+
this.minutes = Array.from(Array(12).keys()).map(i => '0'.concat((i * 5).toString()).slice(-2));
|
|
1841
|
+
// build the weekdays based on the current locale
|
|
1842
|
+
this.weekDays = new Array();
|
|
1843
|
+
const refDateForWeekDays = new Date(SUNDAY);
|
|
1844
|
+
refDateForWeekDays.setDate(refDateForWeekDays.getDate() - this.getLocalisedDay(refDateForWeekDays));
|
|
1845
|
+
for (let i = 0; i < 7; i++) {
|
|
1846
|
+
this.weekDays.push(refDateForWeekDays.toLocaleDateString(this._translate.getCurrentLang(), { weekday: 'short' }));
|
|
1847
|
+
refDateForWeekDays.setDate(refDateForWeekDays.getDate() + 1);
|
|
1585
1848
|
}
|
|
1849
|
+
if (this.min)
|
|
1850
|
+
this.dateMin = new Date(this.min);
|
|
1851
|
+
if (this.max)
|
|
1852
|
+
this.dateMax = new Date(this.max);
|
|
1586
1853
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1854
|
+
getLocalisedDay(date) {
|
|
1855
|
+
let n = date.getDay();
|
|
1856
|
+
switch (this._translate.getCurrentLang()) {
|
|
1857
|
+
case 'it':
|
|
1858
|
+
n = --n < 0 ? 6 : n;
|
|
1859
|
+
break;
|
|
1860
|
+
}
|
|
1861
|
+
return n;
|
|
1592
1862
|
}
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1863
|
+
buildCalendarGrid(refDate) {
|
|
1864
|
+
// find the first day in the month: the important data here is the day of the week
|
|
1865
|
+
const firstDateOfMonth = new Date(refDate.getFullYear(), refDate.getMonth(), 1);
|
|
1866
|
+
// the following flag is used to divide the logic so I can fill the calendar
|
|
1867
|
+
// also with the dates from the previous month, until there's space in the grid
|
|
1868
|
+
let haventFoundFirstDay = true;
|
|
1869
|
+
// index used to build the dates of the month, starting from the first one
|
|
1870
|
+
let index = 1;
|
|
1871
|
+
this.calendarGrid = new Array();
|
|
1872
|
+
for (let i = 0; i < 6; i++) {
|
|
1873
|
+
this.calendarGrid[i] = new Array();
|
|
1874
|
+
for (let j = 0; j < 7; j++) {
|
|
1875
|
+
if (haventFoundFirstDay) {
|
|
1876
|
+
if (this.getLocalisedDay(firstDateOfMonth) === j) {
|
|
1877
|
+
// note: considers Sunday
|
|
1878
|
+
haventFoundFirstDay = false;
|
|
1879
|
+
this.calendarGrid[i][j] = new Date(firstDateOfMonth);
|
|
1880
|
+
// now the I've found the first date of the month I can fill the calendar
|
|
1881
|
+
// the dates fromt the previous month, until there's space in the grid
|
|
1882
|
+
for (let y = this.getLocalisedDay(firstDateOfMonth); y >= 0; y--) {
|
|
1883
|
+
const d = new Date(firstDateOfMonth);
|
|
1884
|
+
d.setDate(firstDateOfMonth.getDate() - y);
|
|
1885
|
+
this.calendarGrid[i][this.getLocalisedDay(firstDateOfMonth) - y] = d;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
else {
|
|
1890
|
+
// fill the following dates until there's space in the grid
|
|
1891
|
+
const d = new Date(firstDateOfMonth);
|
|
1892
|
+
d.setDate(firstDateOfMonth.getDate() + index++);
|
|
1893
|
+
this.calendarGrid[i][j] = d;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1598
1897
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
isOffline() {
|
|
1603
|
-
return this._isOffline;
|
|
1898
|
+
addYears(offset) {
|
|
1899
|
+
this.refDate.setFullYear(this.refDate.getFullYear() + offset);
|
|
1900
|
+
this.buildCalendarGrid(this.refDate);
|
|
1604
1901
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
return false;
|
|
1611
|
-
const status = await Network.getStatus();
|
|
1612
|
-
this._isOffline = !status.connected;
|
|
1613
|
-
return status.connected;
|
|
1902
|
+
setYear(year) {
|
|
1903
|
+
if (!year)
|
|
1904
|
+
return;
|
|
1905
|
+
this.refDate.setFullYear(Number(year));
|
|
1906
|
+
this.buildCalendarGrid(this.refDate);
|
|
1614
1907
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEAOfflineService, decorators: [{
|
|
1619
|
-
type: Injectable,
|
|
1620
|
-
args: [{ providedIn: 'root' }]
|
|
1621
|
-
}], ctorParameters: () => [] });
|
|
1622
|
-
|
|
1623
|
-
/**
|
|
1624
|
-
* To communicate with an AWS's API Gateway istance.
|
|
1625
|
-
*
|
|
1626
|
-
* Includes a rundimental cache on GET requests, until Android doesn't support SWs on local files.
|
|
1627
|
-
*
|
|
1628
|
-
* Note: requires an `AWSAPIAuthToken` variable to be set by IDEATinCanService service.
|
|
1629
|
-
*/
|
|
1630
|
-
class IDEAAWSAPIService {
|
|
1631
|
-
constructor() {
|
|
1632
|
-
this._env = inject(IDEAEnvironment);
|
|
1633
|
-
this._http = inject(HttpClient);
|
|
1634
|
-
this._platform = inject(Platform);
|
|
1635
|
-
this._tc = inject(IDEATinCanService);
|
|
1636
|
-
this._storage = inject(IDEAStorageService);
|
|
1637
|
-
this._errorReporting = inject(IDEAErrorReportingService);
|
|
1638
|
-
this._offline = inject(IDEAOfflineService);
|
|
1639
|
-
this.apiStage = this._env.idea.api?.stage || this._env.idea.api?.version;
|
|
1640
|
-
this.apiUrlProject = 'https://'.concat([this._env.idea.api?.url, this.apiStage].filter(x => x).join('/'));
|
|
1641
|
-
this.ideaApiStage = this._env.idea.ideaApi?.stage || this._env.idea.ideaApi?.version;
|
|
1642
|
-
this.apiUrlIDEA = 'https://'.concat([this._env.idea.ideaApi?.url, this.ideaApiStage].filter(x => x).join('/'));
|
|
1908
|
+
addMonths(offset) {
|
|
1909
|
+
this.refDate.setMonth(this.refDate.getMonth() + offset);
|
|
1910
|
+
this.buildCalendarGrid(this.refDate);
|
|
1643
1911
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
const url = this.prepareURL(resource, opt);
|
|
1655
|
-
// prepare the headers and set the Authorization token
|
|
1656
|
-
const headers = this.prepareHeaders(opt.headers);
|
|
1657
|
-
// prepare the query params and add some info about the client
|
|
1658
|
-
const params = this.prepareQueryParams(opt.params, true);
|
|
1659
|
-
// execute the request
|
|
1660
|
-
let req;
|
|
1661
|
-
switch (method) {
|
|
1662
|
-
case 'HEAD':
|
|
1663
|
-
req = this._http.head(url, { headers, params });
|
|
1664
|
-
break;
|
|
1665
|
-
case 'POST':
|
|
1666
|
-
req = this._http.post(url, opt.body, { headers, params });
|
|
1667
|
-
break;
|
|
1668
|
-
case 'PUT':
|
|
1669
|
-
req = this._http.put(url, opt.body, { headers, params });
|
|
1670
|
-
break;
|
|
1671
|
-
case 'PATCH':
|
|
1672
|
-
req = this._http.patch(url, opt.body, { headers, params });
|
|
1673
|
-
break;
|
|
1674
|
-
case 'DELETE':
|
|
1675
|
-
req = this._http.delete(url, { headers, params });
|
|
1676
|
-
break;
|
|
1677
|
-
case 'GET':
|
|
1678
|
-
default:
|
|
1679
|
-
req = this._http.get(url, { headers, params });
|
|
1680
|
-
}
|
|
1681
|
-
// handle the request response
|
|
1682
|
-
req.subscribe((res) => resolve(res), async (err) => {
|
|
1683
|
-
// check if the request failed for network reasons (to trigger offline mode if needed)
|
|
1684
|
-
await this._offline.check();
|
|
1685
|
-
// (async) send a report, if wanted
|
|
1686
|
-
if (opt.reportError)
|
|
1687
|
-
this._errorReporting.sendReport(err);
|
|
1688
|
-
// fix and return the error
|
|
1689
|
-
this.fixErrMessageBeforeReject(err, reject);
|
|
1912
|
+
async showMonths() {
|
|
1913
|
+
const buttons = [];
|
|
1914
|
+
const inputs = [];
|
|
1915
|
+
const month = new Date(0);
|
|
1916
|
+
for (let i = 1; i <= 12; i++) {
|
|
1917
|
+
inputs.push({
|
|
1918
|
+
type: 'radio',
|
|
1919
|
+
label: month.toLocaleDateString(this._translate.getCurrentLang(), { month: 'long' }),
|
|
1920
|
+
value: i.toString(),
|
|
1921
|
+
checked: i === this.refDate.getMonth() + 1
|
|
1690
1922
|
});
|
|
1923
|
+
month.setMonth(month.getMonth() + 1);
|
|
1924
|
+
}
|
|
1925
|
+
buttons.push({ text: this._translate._('COMMON.CANCEL'), role: 'cancel' });
|
|
1926
|
+
buttons.push({
|
|
1927
|
+
text: this._translate._('COMMON.SELECT'),
|
|
1928
|
+
handler: (m) => {
|
|
1929
|
+
this.refDate.setMonth(parseInt(m, 10) - 1);
|
|
1930
|
+
this.buildCalendarGrid(this.refDate);
|
|
1931
|
+
}
|
|
1932
|
+
});
|
|
1933
|
+
const alert = await this._alert.create({
|
|
1934
|
+
header: this._translate._('IDEA_UNCOMMON.CALENDAR.MONTH'),
|
|
1935
|
+
buttons,
|
|
1936
|
+
inputs
|
|
1691
1937
|
});
|
|
1938
|
+
alert.present();
|
|
1939
|
+
}
|
|
1940
|
+
isDateSelectable(date) {
|
|
1941
|
+
if (this.dateMin && date < this.dateMin)
|
|
1942
|
+
return false;
|
|
1943
|
+
if (this.dateMax && date > this.dateMax)
|
|
1944
|
+
return false;
|
|
1945
|
+
return true;
|
|
1946
|
+
}
|
|
1947
|
+
selectDate(date) {
|
|
1948
|
+
this.selectedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), this.hour, this.minute);
|
|
1949
|
+
}
|
|
1950
|
+
selectHour(hour) {
|
|
1951
|
+
this.hour = Number(hour);
|
|
1952
|
+
this.selectedDate.setHours(this.hour);
|
|
1953
|
+
}
|
|
1954
|
+
selectMinute(minute) {
|
|
1955
|
+
this.minute = Number(minute);
|
|
1956
|
+
this.selectedDate.setMinutes(this.minute);
|
|
1692
1957
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
*/
|
|
1696
|
-
prepareURL(resource, opt) {
|
|
1697
|
-
// decide whether to use IDEA's API or the project's API (or an alternative API)
|
|
1698
|
-
let url = opt.idea ? this.apiUrlIDEA : opt.alternativeAPI || this.apiUrlProject;
|
|
1699
|
-
// prepare a single-resource request (by id) or a normal one
|
|
1700
|
-
url = `${url}/${resource}`;
|
|
1701
|
-
if (opt.resourceId)
|
|
1702
|
-
url = `${url}/${encodeURIComponent(opt.resourceId)}`;
|
|
1703
|
-
return url;
|
|
1958
|
+
isSameDay(a, b) {
|
|
1959
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
1704
1960
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
* Note: it always adds the Authorization token (`AWSAPIAuthToken`) and the API key (`AWSAPIKey`), if set.
|
|
1708
|
-
*/
|
|
1709
|
-
prepareHeaders(h) {
|
|
1710
|
-
// preare the headers; note: HttpHeaders is immutable!
|
|
1711
|
-
let headers = new HttpHeaders(h || null);
|
|
1712
|
-
// set the Authorization token
|
|
1713
|
-
if (!headers.get('Authorization') && this._tc.get('AWSAPIAuthToken'))
|
|
1714
|
-
headers = headers.set('Authorization', this._tc.get('AWSAPIAuthToken'));
|
|
1715
|
-
// set the API key
|
|
1716
|
-
if (!headers.get('X-API-Key') && this._tc.get('AWSAPIKey'))
|
|
1717
|
-
headers = headers.set('X-API-Key', this._tc.get('AWSAPIKey'));
|
|
1718
|
-
return headers;
|
|
1961
|
+
isSameMonth(a, b) {
|
|
1962
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
1719
1963
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
*/
|
|
1723
|
-
prepareQueryParams(qp, addClientInfo) {
|
|
1724
|
-
// prepare the query params; note: HttpParams is immutable!
|
|
1725
|
-
let searchParams = new HttpParams();
|
|
1726
|
-
if (qp && qp instanceof HttpParams)
|
|
1727
|
-
searchParams = qp;
|
|
1728
|
-
else if (qp)
|
|
1729
|
-
for (const prop in qp)
|
|
1730
|
-
if (qp[prop])
|
|
1731
|
-
searchParams = searchParams.set(prop, qp[prop]);
|
|
1732
|
-
// if requested, add app version and client platform to the info we send to the back-end
|
|
1733
|
-
if (addClientInfo) {
|
|
1734
|
-
searchParams = searchParams.set('_v', this._env.idea.app.version);
|
|
1735
|
-
searchParams = searchParams.set('_p', this._platform.platforms().join(' '));
|
|
1736
|
-
}
|
|
1737
|
-
return searchParams;
|
|
1964
|
+
isSameHour(hour) {
|
|
1965
|
+
return this.selectedDate.getHours() === Number(hour);
|
|
1738
1966
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
*/
|
|
1742
|
-
fixErrMessageBeforeReject(err, reject) {
|
|
1743
|
-
let e;
|
|
1744
|
-
try {
|
|
1745
|
-
if (!err || !err.error)
|
|
1746
|
-
e = {};
|
|
1747
|
-
else if (typeof err.error === 'string')
|
|
1748
|
-
e = JSON.parse(err.error);
|
|
1749
|
-
else
|
|
1750
|
-
e = err.error;
|
|
1751
|
-
}
|
|
1752
|
-
catch (_) {
|
|
1753
|
-
e = {};
|
|
1754
|
-
}
|
|
1755
|
-
reject(new Error(e.message || 'Unknown error!'));
|
|
1967
|
+
isSameMinute(minute) {
|
|
1968
|
+
return this.selectedDate.getMinutes() === Number(minute);
|
|
1756
1969
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
*/
|
|
1760
|
-
rawRequest() {
|
|
1761
|
-
return this._http;
|
|
1970
|
+
save(reset) {
|
|
1971
|
+
this._modal.dismiss(reset ? '' : this.selectedDate);
|
|
1762
1972
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
* @param resource resource name (e.g. `users`)
|
|
1766
|
-
* @param options the request options
|
|
1767
|
-
*/
|
|
1768
|
-
headResource(resource, options) {
|
|
1769
|
-
return this.request(resource, 'HEAD', options);
|
|
1973
|
+
close() {
|
|
1974
|
+
this._modal.dismiss();
|
|
1770
1975
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1976
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEACalendarPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1977
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEACalendarPickerComponent, isStandalone: true, selector: "idea-calendar-picker", inputs: { inputDate: "inputDate", timePicker: "timePicker", manualTimePicker: "manualTimePicker", title: "title", hideClearButton: "hideClearButton", min: "min", max: "max" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate.getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid; track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid[i][j], today) || isSameDay(calendarGrid[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid[i][j], refDate)\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid[i][j])\"\n (click)=\"selectDate(calendarGrid[i][j])\"\n >\n {{ calendarGrid[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: IonRow, selector: "ion-row" }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }, { kind: "pipe", type: IDEALocalizedDatePipe, name: "dateLocale" }] }); }
|
|
1978
|
+
}
|
|
1979
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEACalendarPickerComponent, decorators: [{
|
|
1980
|
+
type: Component,
|
|
1981
|
+
args: [{ selector: 'idea-calendar-picker', standalone: true, imports: [
|
|
1982
|
+
CommonModule,
|
|
1983
|
+
FormsModule,
|
|
1984
|
+
IDEATranslatePipe,
|
|
1985
|
+
IDEALocalizedDatePipe,
|
|
1986
|
+
IonFooter,
|
|
1987
|
+
IonInput,
|
|
1988
|
+
IonCol,
|
|
1989
|
+
IonRow,
|
|
1990
|
+
IonContent,
|
|
1991
|
+
IonGrid,
|
|
1992
|
+
IonIcon,
|
|
1993
|
+
IonButton,
|
|
1994
|
+
IonButtons,
|
|
1995
|
+
IonTitle,
|
|
1996
|
+
IonToolbar,
|
|
1997
|
+
IonHeader
|
|
1998
|
+
], template: "<ion-header>\n <ion-toolbar color=\"ideaToolbar\">\n <ion-title>{{ title }}</ion-title>\n <ion-buttons slot=\"end\">\n @if (!hideClearButton) {\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.RESET' | translate\" (click)=\"save(true)\">\n <ion-icon slot=\"icon-only\" name=\"arrow-undo\" />\n </ion-button>\n }\n <ion-button [title]=\"'IDEA_UNCOMMON.CALENDAR.CLOSE_WITHOUT_SAVING' | translate\" (click)=\"close()\">\n <ion-icon slot=\"icon-only\" name=\"close\" />\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-grid fixed class=\"ion-no-padding\">\n <ion-row>\n <h1 class=\"headerLabel\">{{ 'IDEA_UNCOMMON.CALENDAR.SET_DATE' | translate }}</h1>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\">\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"5\">\n <ion-row class=\"ion-align-items-center ion-justify-content-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\">\n <ion-input\n type=\"text\"\n [placeholder]=\"'IDEA_UNCOMMON.CALENDAR.YEAR' | translate\"\n [value]=\"refDate.getFullYear()\"\n (ionFocus)=\"$event.target.value = ''\"\n (ionChange)=\"setYear($event?.target?.value)\"\n >\n </ion-input>\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addYears(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"7\">\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-text-right ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(-1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-back\" />\n </ion-button>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding tappable\" [size]=\"6\" (click)=\"showMonths()\">\n {{ refDate | dateLocale: 'MMMM' }}\n </ion-col>\n <ion-col class=\"ion-text-left ion-no-padding\" [size]=\"3\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"addMonths(+1)\">\n <ion-icon slot=\"icon-only\" name=\"chevron-forward\" />\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-col>\n </ion-row>\n <ion-row style=\"margin-bottom: 8px\" class=\"headerLabel\">\n @for (ddd of weekDays; track ddd) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n {{ ddd }}\n </ion-col>\n }\n </ion-row>\n @for (row of calendarGrid; track row; let i = $index) {\n <ion-row class=\"ion-align-items-center\">\n @for (col of row; track col; let j = $index) {\n <ion-col class=\"ion-text-center ion-no-padding\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"\n isSameDay(calendarGrid[i][j], today) || isSameDay(calendarGrid[i][j], selectedDate)\n ? 'primary'\n : isSameMonth(calendarGrid[i][j], refDate)\n ? 'dark'\n : 'medium'\n \"\n [fill]=\"\n isSameDay(calendarGrid[i][j], selectedDate)\n ? 'solid'\n : isSameDay(calendarGrid[i][j], today)\n ? 'outline'\n : 'clear'\n \"\n [disabled]=\"!isDateSelectable(calendarGrid[i][j])\"\n (click)=\"selectDate(calendarGrid[i][j])\"\n >\n {{ calendarGrid[i][j] | date: 'd' }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n }\n @if (timePicker || manualTimePicker) {\n <ion-row>\n <h1 class=\"headerLabel\">\n {{ 'IDEA_UNCOMMON.CALENDAR.SET_TIME' | translate }}\n </h1>\n </ion-row>\n }\n @if (timePicker) {\n <ion-row class=\"ion-align-items-center\">\n <ion-col class=\"ion-no-padding\" [size]=\"7\">\n <ion-row>\n @for (hour of hours; track hour) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"2\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameHour(hour) ? 'primary' : 'dark'\"\n [fill]=\"isSameHour(hour) ? 'solid' : 'clear'\"\n (click)=\"selectHour(hour)\"\n >\n {{ hour }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"1\"> : </ion-col>\n <ion-col class=\"ion-no-padding\" [size]=\"4\">\n <ion-row>\n @for (minute of minutes; track minute) {\n <ion-col class=\"ion-text-center ion-no-padding\" [size]=\"4\">\n <ion-button\n size=\"small\"\n shape=\"round\"\n [color]=\"isSameMinute(minute) ? 'primary' : 'dark'\"\n [fill]=\"isSameMinute(minute) ? 'solid' : 'clear'\"\n (click)=\"selectMinute(minute)\"\n >\n {{ minute }}\n </ion-button>\n </ion-col>\n }\n </ion-row>\n </ion-col>\n </ion-row>\n }\n @if (manualTimePicker) {\n <ion-row class=\"ion-align-items-center manualTimePicker\">\n <ion-col class=\"ion-text-center\" [size]=\"12\">\n <input type=\"number\" min=\"0\" max=\"23\" [value]=\"hour\" (change)=\"selectHour($event.target.value)\" />\n <span>:</span>\n <input type=\"number\" min=\"0\" max=\"59\" [value]=\"minute\" (change)=\"selectMinute($event.target.value)\" />\n </ion-col>\n </ion-row>\n }\n </ion-grid>\n</ion-content>\n<ion-footer>\n <ion-toolbar color=\"ideaToolbar\" class=\"tappable\" (click)=\"save()\">\n <ion-title class=\"ion-text-center\">\n <ion-icon name=\"checkmark\" />\n </ion-title>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-content{--background: var(--ion-color-white)}h1{margin:10px}.headerLabel{font-size:.7em;font-weight:700;text-transform:uppercase}ion-col,ion-button{font-size:.95em}ion-button{margin:3px 0}ion-footer ion-toolbar{--min-height: 40px}.tappable{cursor:pointer}.manualTimePicker{padding:20px}.manualTimePicker input[type=number],.manualTimePicker span{font-size:2.5em}.manualTimePicker input[type=number]{border:none;border-bottom:2px solid var(--ion-color-dark);text-align:center}.manualTimePicker span{padding-left:4px;padding-right:4px}\n"] }]
|
|
1999
|
+
}], propDecorators: { inputDate: [{
|
|
2000
|
+
type: Input
|
|
2001
|
+
}], timePicker: [{
|
|
2002
|
+
type: Input
|
|
2003
|
+
}], manualTimePicker: [{
|
|
2004
|
+
type: Input
|
|
2005
|
+
}], title: [{
|
|
2006
|
+
type: Input
|
|
2007
|
+
}], hideClearButton: [{
|
|
2008
|
+
type: Input
|
|
2009
|
+
}], min: [{
|
|
2010
|
+
type: Input
|
|
2011
|
+
}], max: [{
|
|
2012
|
+
type: Input
|
|
2013
|
+
}] } });
|
|
2014
|
+
|
|
2015
|
+
class IDEADateTimeComponent {
|
|
2016
|
+
constructor() {
|
|
2017
|
+
this._modal = inject(ModalController);
|
|
2018
|
+
this._translate = inject(IDEATranslationsService);
|
|
2019
|
+
/**
|
|
2020
|
+
* Whether to show the time picker (datetime) or not (date).
|
|
2021
|
+
*/
|
|
2022
|
+
this.timePicker = false;
|
|
2023
|
+
/**
|
|
2024
|
+
* Whether to show the MANUAL time picker (datetime) or not (date).
|
|
2025
|
+
*/
|
|
2026
|
+
this.manualTimePicker = false;
|
|
2027
|
+
/**
|
|
2028
|
+
* Whether to use the `epochISOString` format instead of `epochDateTime`.
|
|
2029
|
+
*/
|
|
2030
|
+
this.useISOFormat = false;
|
|
2031
|
+
/**
|
|
2032
|
+
* If true, the component is disabled.
|
|
2033
|
+
*/
|
|
2034
|
+
this.disabled = false;
|
|
2035
|
+
/**
|
|
2036
|
+
* If true, the obligatory dot is shown.
|
|
2037
|
+
*/
|
|
2038
|
+
this.obligatory = false;
|
|
2039
|
+
/**
|
|
2040
|
+
* If true, hidew the clear button in the header.
|
|
2041
|
+
*/
|
|
2042
|
+
this.hideClearButton = false;
|
|
2043
|
+
this.dateChange = new EventEmitter();
|
|
2044
|
+
this.iconSelect = new EventEmitter();
|
|
2045
|
+
this.isOpening = false;
|
|
2046
|
+
}
|
|
2047
|
+
ngOnInit() {
|
|
2048
|
+
this.langChangeSubscription = this._translate.onLangChange.subscribe(() => {
|
|
2049
|
+
this.valueToDisplay = this.getValueToDisplay(this.date);
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
ngOnDestroy() {
|
|
2053
|
+
if (this.langChangeSubscription)
|
|
2054
|
+
this.langChangeSubscription.unsubscribe();
|
|
2055
|
+
}
|
|
2056
|
+
ngOnChanges(changes) {
|
|
2057
|
+
if (changes.date || changes.timePicker)
|
|
2058
|
+
this.valueToDisplay = this.getValueToDisplay(this.date);
|
|
2059
|
+
}
|
|
2060
|
+
async openCalendarPicker() {
|
|
2061
|
+
if (this.disabled || this.isOpening)
|
|
2062
|
+
return;
|
|
2063
|
+
this.isOpening = true;
|
|
2064
|
+
const modal = await this._modal.create({
|
|
2065
|
+
component: IDEACalendarPickerComponent,
|
|
2066
|
+
componentProps: {
|
|
2067
|
+
inputDate: this.date,
|
|
2068
|
+
title: this.label,
|
|
2069
|
+
timePicker: this.timePicker,
|
|
2070
|
+
manualTimePicker: this.manualTimePicker,
|
|
2071
|
+
hideClearButton: this.hideClearButton,
|
|
2072
|
+
min: this.min,
|
|
2073
|
+
max: this.max
|
|
1846
2074
|
}
|
|
1847
2075
|
});
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
getResourceObserver(resource, options) {
|
|
1855
|
-
return new Observable(observer => {
|
|
1856
|
-
const opt = options || {};
|
|
1857
|
-
// if offline and with a cache mode set, force the request to the cache
|
|
1858
|
-
if (!navigator.onLine && opt.useCache)
|
|
1859
|
-
opt.useCache = CacheModes.CACHE_ONLY;
|
|
1860
|
-
// execute the GET request online or through the cache, depending on the chosen mode
|
|
1861
|
-
switch (opt.useCache) {
|
|
1862
|
-
case CacheModes.CACHE_ONLY:
|
|
1863
|
-
// return the result from the cache, without retrying online if a result wasn't found
|
|
1864
|
-
this.getFromCache(resource, opt).then((res) => {
|
|
1865
|
-
if (res)
|
|
1866
|
-
observer.next(res);
|
|
1867
|
-
else
|
|
1868
|
-
observer.error();
|
|
1869
|
-
});
|
|
1870
|
-
break;
|
|
1871
|
-
case CacheModes.CACHE_FIRST:
|
|
1872
|
-
// return the result from the cache
|
|
1873
|
-
this.getFromCache(resource, opt).then((localRes) => {
|
|
1874
|
-
if (localRes) {
|
|
1875
|
-
observer.next(localRes);
|
|
1876
|
-
// asynchrounously execute the request online, to update the cache with latest data
|
|
1877
|
-
this.request(resource, 'GET', opt)
|
|
1878
|
-
.then((cloudRes) => {
|
|
1879
|
-
observer.next(cloudRes);
|
|
1880
|
-
// update the cache (if it fails, it's ok)
|
|
1881
|
-
this.putInCache(resource, cloudRes, opt).catch(() => { });
|
|
1882
|
-
})
|
|
1883
|
-
.catch(() => { }); // we already returned the result, an error is acceptable
|
|
1884
|
-
}
|
|
1885
|
-
else {
|
|
1886
|
-
// if the result isn't found in the cache, return the result of the online request
|
|
1887
|
-
this.request(resource, 'GET', opt)
|
|
1888
|
-
.then((res) => {
|
|
1889
|
-
observer.next(res);
|
|
1890
|
-
// update the cache (if it fails, it's ok)
|
|
1891
|
-
this.putInCache(resource, res, opt).catch(() => { });
|
|
1892
|
-
})
|
|
1893
|
-
.catch((err) => observer.error(err)); // element not found
|
|
1894
|
-
}
|
|
1895
|
-
});
|
|
1896
|
-
break;
|
|
1897
|
-
case CacheModes.NETWORK_FIRST:
|
|
1898
|
-
// return the result from an online request
|
|
1899
|
-
this.request(resource, 'GET', opt)
|
|
1900
|
-
.then((cloudRes) => {
|
|
1901
|
-
observer.next(cloudRes);
|
|
1902
|
-
// update the cache (if it fails, it's ok)
|
|
1903
|
-
this.putInCache(resource, cloudRes, opt).catch(() => { });
|
|
1904
|
-
})
|
|
1905
|
-
.catch(() => {
|
|
1906
|
-
// if a request to the network fails, check in cache
|
|
1907
|
-
this.getFromCache(resource, opt).then((res) => {
|
|
1908
|
-
if (res)
|
|
1909
|
-
observer.next(res);
|
|
1910
|
-
else
|
|
1911
|
-
observer.error();
|
|
1912
|
-
});
|
|
1913
|
-
});
|
|
1914
|
-
break;
|
|
1915
|
-
default:
|
|
1916
|
-
/* CacheModes.NO_CACHE */
|
|
1917
|
-
// return the result from an online request, with no cache involved
|
|
1918
|
-
this.request(resource, 'GET', opt)
|
|
1919
|
-
.then((res) => observer.next(res))
|
|
1920
|
-
.catch((err) => observer.error(err));
|
|
2076
|
+
modal.onDidDismiss().then(({ data }) => {
|
|
2077
|
+
if (data !== undefined && data !== null) {
|
|
2078
|
+
if (data === '')
|
|
2079
|
+
this.doSelect(null);
|
|
2080
|
+
else
|
|
2081
|
+
this.doSelect(data);
|
|
1921
2082
|
}
|
|
1922
2083
|
});
|
|
2084
|
+
modal.present();
|
|
2085
|
+
this.isOpening = false;
|
|
1923
2086
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
*/
|
|
1929
|
-
getFromCache(resource, options) {
|
|
1930
|
-
return new Promise(resolve => {
|
|
1931
|
-
const opt = options || {};
|
|
1932
|
-
// prepare the url and the query params
|
|
1933
|
-
const url = this.prepareURL(resource, opt);
|
|
1934
|
-
const queryParams = this.prepareQueryParams(opt.params);
|
|
1935
|
-
// get from storage, by the complete url
|
|
1936
|
-
this._storage
|
|
1937
|
-
.get(url.concat(queryParams.toString()))
|
|
1938
|
-
.then((res) => (res ? resolve(res) : opt.resourceId ? resolve(null) : resolve([])));
|
|
1939
|
-
});
|
|
1940
|
-
}
|
|
1941
|
-
/**
|
|
1942
|
-
* Execute a PUT request in the local storage.
|
|
1943
|
-
* @param resource resource name (e.g. `users`)
|
|
1944
|
-
* @param data resource data
|
|
1945
|
-
* @param options the request options
|
|
1946
|
-
*/
|
|
1947
|
-
putInCache(resource, data, options) {
|
|
1948
|
-
return new Promise((resolve, reject) => {
|
|
1949
|
-
const opt = options || {};
|
|
1950
|
-
// prepare the url and the query params
|
|
1951
|
-
const url = this.prepareURL(resource, opt);
|
|
1952
|
-
const queryParams = this.prepareQueryParams(opt.params);
|
|
1953
|
-
// put in the storage, by the complete url
|
|
1954
|
-
this._storage
|
|
1955
|
-
.set(url.concat(queryParams.toString()), data)
|
|
1956
|
-
.then(() => resolve())
|
|
1957
|
-
.catch((err) => reject(err));
|
|
1958
|
-
});
|
|
2087
|
+
getValueToDisplay(date) {
|
|
2088
|
+
return !date
|
|
2089
|
+
? ''
|
|
2090
|
+
: this._translate.formatDate(date, this.timePicker || this.manualTimePicker ? 'd MMM yyyy, HH:mm' : 'mediumDate');
|
|
1959
2091
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
* @param resource resource name (e.g. `users`)
|
|
1963
|
-
* @param options the request options
|
|
1964
|
-
*/
|
|
1965
|
-
deleteFromCache(resource, options) {
|
|
1966
|
-
return new Promise((resolve, reject) => {
|
|
1967
|
-
const opt = options || {};
|
|
1968
|
-
// prepare the url and the query params
|
|
1969
|
-
const url = this.prepareURL(resource, opt);
|
|
1970
|
-
const queryParams = this.prepareQueryParams(opt.params);
|
|
1971
|
-
// delete from storage, by the complete url
|
|
1972
|
-
this._storage
|
|
1973
|
-
.remove(url.concat(queryParams.toString()))
|
|
1974
|
-
.then(() => resolve())
|
|
1975
|
-
.catch((err) => reject(err));
|
|
1976
|
-
});
|
|
2092
|
+
doSelect(date) {
|
|
2093
|
+
this.dateChange.emit(date ? (this.useISOFormat ? date.toISOString() : date.valueOf()) : null);
|
|
1977
2094
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
*/
|
|
1983
|
-
postResource(resource, options) {
|
|
1984
|
-
return this.request(resource, 'POST', options);
|
|
2095
|
+
doIconSelect(event) {
|
|
2096
|
+
if (event)
|
|
2097
|
+
event.stopPropagation();
|
|
2098
|
+
this.iconSelect.emit();
|
|
1985
2099
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2100
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEADateTimeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2101
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEADateTimeComponent, isStandalone: true, selector: "idea-date-time", inputs: { date: "date", timePicker: "timePicker", manualTimePicker: "manualTimePicker", useISOFormat: "useISOFormat", label: "label", icon: "icon", iconColor: "iconColor", lines: "lines", color: "color", placeholder: "placeholder", disabled: "disabled", obligatory: "obligatory", hideClearButton: "hideClearButton", min: "min", max: "max" }, outputs: { dateChange: "dateChange", iconSelect: "iconSelect" }, usesOnChanges: true, ngImport: i0, template: `
|
|
2102
|
+
<ion-item
|
|
2103
|
+
class="dateTimeItem"
|
|
2104
|
+
[color]="color"
|
|
2105
|
+
[lines]="lines"
|
|
2106
|
+
[title]="placeholder || label || ''"
|
|
2107
|
+
[button]="!disabled"
|
|
2108
|
+
[disabled]="isOpening"
|
|
2109
|
+
[class.withLabel]="label"
|
|
2110
|
+
(click)="openCalendarPicker()"
|
|
2111
|
+
>
|
|
2112
|
+
@if (icon) {
|
|
2113
|
+
<ion-button
|
|
2114
|
+
fill="clear"
|
|
2115
|
+
slot="start"
|
|
2116
|
+
[color]="iconColor"
|
|
2117
|
+
[class.marginTop]="label"
|
|
2118
|
+
(click)="doIconSelect($event)"
|
|
2119
|
+
>
|
|
2120
|
+
<ion-icon [icon]="icon" slot="icon-only" />
|
|
2121
|
+
</ion-button>
|
|
2122
|
+
}
|
|
2123
|
+
@if (label) {
|
|
2124
|
+
<ion-label position="stacked" [class.selectable]="!disabled">
|
|
2125
|
+
{{ label }}
|
|
2126
|
+
@if (obligatory && !disabled) {
|
|
2127
|
+
<ion-text class="obligatoryDot" />
|
|
2128
|
+
}
|
|
2129
|
+
</ion-label>
|
|
2130
|
+
}
|
|
2131
|
+
<ion-label class="value" [class.selectable]="!disabled">
|
|
2132
|
+
@if (!valueToDisplay && !disabled) {
|
|
2133
|
+
<ion-text class="placeholder" [class.selectable]="!disabled">
|
|
2134
|
+
{{ placeholder }}
|
|
2135
|
+
</ion-text>
|
|
2136
|
+
}
|
|
2137
|
+
{{ valueToDisplay }}
|
|
2138
|
+
</ion-label>
|
|
2139
|
+
@if (!disabled) {
|
|
2140
|
+
<ion-icon slot="end" icon="caret-down" class="selectIcon" [class.selectable]="!disabled" />
|
|
2141
|
+
}
|
|
2142
|
+
</ion-item>
|
|
2143
|
+
`, isInline: true, styles: [".dateTimeItem{min-height:48px;height:auto}.dateTimeItem .description{margin:10px 0;height:20px;line-height:20px;width:100%}.dateTimeItem .placeholder{color:var(--ion-color-medium)}.dateTimeItem .value{white-space:nowrap}.dateTimeItem .selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.dateTimeItem.withLabel{min-height:58px;height:auto}.dateTimeItem.withLabel .selectIcon{padding-top:25px}.dateTimeItem.withLabel ion-button[slot=start]{margin-top:16px}.selectable{cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }] }); }
|
|
2144
|
+
}
|
|
2145
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEADateTimeComponent, decorators: [{
|
|
2146
|
+
type: Component,
|
|
2147
|
+
args: [{ selector: 'idea-date-time', standalone: true, imports: [CommonModule, IonText, IonLabel, IonIcon, IonButton, IonItem], template: `
|
|
2148
|
+
<ion-item
|
|
2149
|
+
class="dateTimeItem"
|
|
2150
|
+
[color]="color"
|
|
2151
|
+
[lines]="lines"
|
|
2152
|
+
[title]="placeholder || label || ''"
|
|
2153
|
+
[button]="!disabled"
|
|
2154
|
+
[disabled]="isOpening"
|
|
2155
|
+
[class.withLabel]="label"
|
|
2156
|
+
(click)="openCalendarPicker()"
|
|
2157
|
+
>
|
|
2158
|
+
@if (icon) {
|
|
2159
|
+
<ion-button
|
|
2160
|
+
fill="clear"
|
|
2161
|
+
slot="start"
|
|
2162
|
+
[color]="iconColor"
|
|
2163
|
+
[class.marginTop]="label"
|
|
2164
|
+
(click)="doIconSelect($event)"
|
|
2165
|
+
>
|
|
2166
|
+
<ion-icon [icon]="icon" slot="icon-only" />
|
|
2167
|
+
</ion-button>
|
|
2168
|
+
}
|
|
2169
|
+
@if (label) {
|
|
2170
|
+
<ion-label position="stacked" [class.selectable]="!disabled">
|
|
2171
|
+
{{ label }}
|
|
2172
|
+
@if (obligatory && !disabled) {
|
|
2173
|
+
<ion-text class="obligatoryDot" />
|
|
2174
|
+
}
|
|
2175
|
+
</ion-label>
|
|
2176
|
+
}
|
|
2177
|
+
<ion-label class="value" [class.selectable]="!disabled">
|
|
2178
|
+
@if (!valueToDisplay && !disabled) {
|
|
2179
|
+
<ion-text class="placeholder" [class.selectable]="!disabled">
|
|
2180
|
+
{{ placeholder }}
|
|
2181
|
+
</ion-text>
|
|
2182
|
+
}
|
|
2183
|
+
{{ valueToDisplay }}
|
|
2184
|
+
</ion-label>
|
|
2185
|
+
@if (!disabled) {
|
|
2186
|
+
<ion-icon slot="end" icon="caret-down" class="selectIcon" [class.selectable]="!disabled" />
|
|
2187
|
+
}
|
|
2188
|
+
</ion-item>
|
|
2189
|
+
`, styles: [".dateTimeItem{min-height:48px;height:auto}.dateTimeItem .description{margin:10px 0;height:20px;line-height:20px;width:100%}.dateTimeItem .placeholder{color:var(--ion-color-medium)}.dateTimeItem .value{white-space:nowrap}.dateTimeItem .selectIcon{margin:0;padding-left:4px;font-size:.8em;color:var(--ion-color-medium)}.dateTimeItem.withLabel{min-height:58px;height:auto}.dateTimeItem.withLabel .selectIcon{padding-top:25px}.dateTimeItem.withLabel ion-button[slot=start]{margin-top:16px}.selectable{cursor:pointer}\n"] }]
|
|
2190
|
+
}], propDecorators: { date: [{
|
|
2191
|
+
type: Input
|
|
2192
|
+
}], timePicker: [{
|
|
2193
|
+
type: Input
|
|
2194
|
+
}], manualTimePicker: [{
|
|
2195
|
+
type: Input
|
|
2196
|
+
}], useISOFormat: [{
|
|
2197
|
+
type: Input
|
|
2198
|
+
}], label: [{
|
|
2199
|
+
type: Input
|
|
2200
|
+
}], icon: [{
|
|
2201
|
+
type: Input
|
|
2202
|
+
}], iconColor: [{
|
|
2203
|
+
type: Input
|
|
2204
|
+
}], lines: [{
|
|
2205
|
+
type: Input
|
|
2206
|
+
}], color: [{
|
|
2207
|
+
type: Input
|
|
2208
|
+
}], placeholder: [{
|
|
2209
|
+
type: Input
|
|
2210
|
+
}], disabled: [{
|
|
2211
|
+
type: Input
|
|
2212
|
+
}], obligatory: [{
|
|
2213
|
+
type: Input
|
|
2214
|
+
}], hideClearButton: [{
|
|
2215
|
+
type: Input
|
|
2216
|
+
}], min: [{
|
|
2217
|
+
type: Input
|
|
2218
|
+
}], max: [{
|
|
2219
|
+
type: Input
|
|
2220
|
+
}], dateChange: [{
|
|
2221
|
+
type: Output
|
|
2222
|
+
}], iconSelect: [{
|
|
2223
|
+
type: Output
|
|
2224
|
+
}] } });
|
|
2225
|
+
|
|
2226
|
+
class IDEADurationComponent {
|
|
2227
|
+
constructor() {
|
|
2228
|
+
/**
|
|
2229
|
+
* On change event. It emits a number of seconds representing the duration.
|
|
2230
|
+
*/
|
|
2231
|
+
this.set = new EventEmitter();
|
|
2232
|
+
this.default = this.hours = this.minutes = this.seconds = 0;
|
|
1993
2233
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2234
|
+
ngOnChanges(changes) {
|
|
2235
|
+
// assign a default value to the duration
|
|
2236
|
+
if (changes['default'] && changes['default'].isFirstChange()) {
|
|
2237
|
+
const refDate = new Date(0);
|
|
2238
|
+
refDate.setHours(0, 0, 0);
|
|
2239
|
+
refDate.setSeconds(refDate.getSeconds() + this.default);
|
|
2240
|
+
this.hours = refDate.getHours();
|
|
2241
|
+
this.minutes = refDate.getMinutes();
|
|
2242
|
+
this.seconds = this.hideSeconds ? 0 : refDate.getSeconds();
|
|
2243
|
+
}
|
|
2001
2244
|
}
|
|
2002
2245
|
/**
|
|
2003
|
-
*
|
|
2004
|
-
* @param resource resource name (e.g. `users`)
|
|
2005
|
-
* @param options the request options
|
|
2246
|
+
* When one of the parts changes, emit the new duration.
|
|
2006
2247
|
*/
|
|
2007
|
-
|
|
2008
|
-
|
|
2248
|
+
setDuration(type) {
|
|
2249
|
+
switch (type) {
|
|
2250
|
+
case 'hours':
|
|
2251
|
+
const hh = Number(this.hours) || 0;
|
|
2252
|
+
this.hours = hh > 23 ? 23 : hh < 0 ? 0 : hh;
|
|
2253
|
+
break;
|
|
2254
|
+
case 'minutes':
|
|
2255
|
+
const mm = Number(this.minutes) || 0;
|
|
2256
|
+
this.minutes = mm > 59 ? 59 : mm < 0 ? 0 : mm;
|
|
2257
|
+
break;
|
|
2258
|
+
case 'seconds':
|
|
2259
|
+
const ss = Number(this.seconds) || 0;
|
|
2260
|
+
if (this.hideSeconds)
|
|
2261
|
+
this.seconds = 0;
|
|
2262
|
+
else
|
|
2263
|
+
this.seconds = ss > 59 ? 59 : ss < 0 ? 0 : ss;
|
|
2264
|
+
break;
|
|
2265
|
+
}
|
|
2266
|
+
this.set.emit(this.hours * 3600 + this.minutes * 60 + this.seconds);
|
|
2009
2267
|
}
|
|
2010
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type:
|
|
2011
|
-
static { this.ɵ
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
class
|
|
2268
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEADurationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2269
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: IDEADurationComponent, isStandalone: true, selector: "idea-duration", inputs: { default: "default", label: "label", icon: "icon", title: "title", disabled: "disabled", obligatory: "obligatory", lines: "lines", color: "color", hideSeconds: "hideSeconds", shortLabels: "shortLabels" }, outputs: { set: "set" }, usesOnChanges: true, ngImport: i0, template: `
|
|
2270
|
+
<ion-item class="durationItem" [lines]="lines" [color]="color" [title]="title || label">
|
|
2271
|
+
@if (icon) {
|
|
2272
|
+
<ion-icon slot="start" [name]="icon" />
|
|
2273
|
+
}
|
|
2274
|
+
@if (!icon && label) {
|
|
2275
|
+
<ion-label position="stacked">
|
|
2276
|
+
{{ label }}
|
|
2277
|
+
@if (obligatory && !disabled) {
|
|
2278
|
+
<ion-text class="obligatoryDot" />
|
|
2279
|
+
}
|
|
2280
|
+
</ion-label>
|
|
2281
|
+
}
|
|
2282
|
+
<ion-label class="value">
|
|
2283
|
+
<ion-input
|
|
2284
|
+
type="number"
|
|
2285
|
+
min="0"
|
|
2286
|
+
max="23"
|
|
2287
|
+
inputmode="numeric"
|
|
2288
|
+
[disabled]="disabled"
|
|
2289
|
+
[(ngModel)]="hours"
|
|
2290
|
+
(ionChange)="setDuration('hours')"
|
|
2291
|
+
/>
|
|
2292
|
+
<ion-text>{{
|
|
2293
|
+
((shortLabels ? 'IDEA_UNCOMMON.DURATION.HH' : 'IDEA_UNCOMMON.DURATION.HOURS') | translate).toLowerCase()
|
|
2294
|
+
}}</ion-text>
|
|
2295
|
+
<ion-input
|
|
2296
|
+
type="number"
|
|
2297
|
+
min="0"
|
|
2298
|
+
max="59"
|
|
2299
|
+
inputmode="numeric"
|
|
2300
|
+
[disabled]="disabled"
|
|
2301
|
+
[(ngModel)]="minutes"
|
|
2302
|
+
(ionChange)="setDuration('minutes')"
|
|
2303
|
+
/>
|
|
2304
|
+
<ion-text>{{
|
|
2305
|
+
((shortLabels ? 'IDEA_UNCOMMON.DURATION.MM' : 'IDEA_UNCOMMON.DURATION.MINUTES') | translate).toLowerCase()
|
|
2306
|
+
}}</ion-text>
|
|
2307
|
+
@if (!hideSeconds) {
|
|
2308
|
+
<ion-input
|
|
2309
|
+
type="number"
|
|
2310
|
+
min="0"
|
|
2311
|
+
max="59"
|
|
2312
|
+
inputmode="numeric"
|
|
2313
|
+
[disabled]="disabled"
|
|
2314
|
+
[(ngModel)]="seconds"
|
|
2315
|
+
(ionChange)="setDuration('seconds')"
|
|
2316
|
+
/>
|
|
2317
|
+
}
|
|
2318
|
+
@if (!hideSeconds) {
|
|
2319
|
+
<ion-text>
|
|
2320
|
+
{{
|
|
2321
|
+
((shortLabels ? 'IDEA_UNCOMMON.DURATION.SS' : 'IDEA_UNCOMMON.DURATION.SECONDS') | translate).toLowerCase()
|
|
2322
|
+
}}
|
|
2323
|
+
</ion-text>
|
|
2324
|
+
}
|
|
2325
|
+
</ion-label>
|
|
2326
|
+
</ion-item>
|
|
2327
|
+
`, isInline: true, styles: [".durationItem .value{max-width:none;width:100%;pointer-events:all}.durationItem .value ion-input{display:inline-block;max-width:35px;text-align:right}.durationItem .value ion-text{margin-left:10px;margin-right:20px;font-size:.8em}\n"], dependencies: [{ kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: IDEATranslatePipe, name: "translate" }] }); }
|
|
2021
2328
|
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2329
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: IDEADurationComponent, decorators: [{
|
|
2330
|
+
type: Component,
|
|
2331
|
+
args: [{ selector: 'idea-duration', standalone: true, imports: [IonText, IonLabel, IonIcon, IonItem, CommonModule, FormsModule, IDEATranslatePipe], template: `
|
|
2332
|
+
<ion-item class="durationItem" [lines]="lines" [color]="color" [title]="title || label">
|
|
2333
|
+
@if (icon) {
|
|
2334
|
+
<ion-icon slot="start" [name]="icon" />
|
|
2335
|
+
}
|
|
2336
|
+
@if (!icon && label) {
|
|
2337
|
+
<ion-label position="stacked">
|
|
2338
|
+
{{ label }}
|
|
2339
|
+
@if (obligatory && !disabled) {
|
|
2340
|
+
<ion-text class="obligatoryDot" />
|
|
2341
|
+
}
|
|
2342
|
+
</ion-label>
|
|
2343
|
+
}
|
|
2344
|
+
<ion-label class="value">
|
|
2345
|
+
<ion-input
|
|
2346
|
+
type="number"
|
|
2347
|
+
min="0"
|
|
2348
|
+
max="23"
|
|
2349
|
+
inputmode="numeric"
|
|
2350
|
+
[disabled]="disabled"
|
|
2351
|
+
[(ngModel)]="hours"
|
|
2352
|
+
(ionChange)="setDuration('hours')"
|
|
2353
|
+
/>
|
|
2354
|
+
<ion-text>{{
|
|
2355
|
+
((shortLabels ? 'IDEA_UNCOMMON.DURATION.HH' : 'IDEA_UNCOMMON.DURATION.HOURS') | translate).toLowerCase()
|
|
2356
|
+
}}</ion-text>
|
|
2357
|
+
<ion-input
|
|
2358
|
+
type="number"
|
|
2359
|
+
min="0"
|
|
2360
|
+
max="59"
|
|
2361
|
+
inputmode="numeric"
|
|
2362
|
+
[disabled]="disabled"
|
|
2363
|
+
[(ngModel)]="minutes"
|
|
2364
|
+
(ionChange)="setDuration('minutes')"
|
|
2365
|
+
/>
|
|
2366
|
+
<ion-text>{{
|
|
2367
|
+
((shortLabels ? 'IDEA_UNCOMMON.DURATION.MM' : 'IDEA_UNCOMMON.DURATION.MINUTES') | translate).toLowerCase()
|
|
2368
|
+
}}</ion-text>
|
|
2369
|
+
@if (!hideSeconds) {
|
|
2370
|
+
<ion-input
|
|
2371
|
+
type="number"
|
|
2372
|
+
min="0"
|
|
2373
|
+
max="59"
|
|
2374
|
+
inputmode="numeric"
|
|
2375
|
+
[disabled]="disabled"
|
|
2376
|
+
[(ngModel)]="seconds"
|
|
2377
|
+
(ionChange)="setDuration('seconds')"
|
|
2378
|
+
/>
|
|
2379
|
+
}
|
|
2380
|
+
@if (!hideSeconds) {
|
|
2381
|
+
<ion-text>
|
|
2382
|
+
{{
|
|
2383
|
+
((shortLabels ? 'IDEA_UNCOMMON.DURATION.SS' : 'IDEA_UNCOMMON.DURATION.SECONDS') | translate).toLowerCase()
|
|
2384
|
+
}}
|
|
2385
|
+
</ion-text>
|
|
2386
|
+
}
|
|
2387
|
+
</ion-label>
|
|
2388
|
+
</ion-item>
|
|
2389
|
+
`, styles: [".durationItem .value{max-width:none;width:100%;pointer-events:all}.durationItem .value ion-input{display:inline-block;max-width:35px;text-align:right}.durationItem .value ion-text{margin-left:10px;margin-right:20px;font-size:.8em}\n"] }]
|
|
2390
|
+
}], ctorParameters: () => [], propDecorators: { default: [{
|
|
2391
|
+
type: Input
|
|
2392
|
+
}], label: [{
|
|
2393
|
+
type: Input
|
|
2394
|
+
}], icon: [{
|
|
2395
|
+
type: Input
|
|
2396
|
+
}], title: [{
|
|
2397
|
+
type: Input
|
|
2398
|
+
}], disabled: [{
|
|
2399
|
+
type: Input
|
|
2400
|
+
}], obligatory: [{
|
|
2401
|
+
type: Input
|
|
2402
|
+
}], lines: [{
|
|
2403
|
+
type: Input
|
|
2404
|
+
}], color: [{
|
|
2405
|
+
type: Input
|
|
2406
|
+
}], hideSeconds: [{
|
|
2407
|
+
type: Input
|
|
2408
|
+
}], shortLabels: [{
|
|
2409
|
+
type: Input
|
|
2410
|
+
}], set: [{
|
|
2411
|
+
type: Output
|
|
2412
|
+
}] } });
|
|
2044
2413
|
|
|
2045
2414
|
class IDEAEchoPage {
|
|
2046
2415
|
constructor() {
|
|
@@ -3901,5 +4270,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
3901
4270
|
* Generated bundle index. Do not edit.
|
|
3902
4271
|
*/
|
|
3903
4272
|
|
|
3904
|
-
export { APIRequestOption, CacheModes, CacheableResource, ColorsPaletteComponent, EchoRequests, IDEAAWSAPIService, IDEAAWSAPISocketService, IDEAAddressComponent, IDEAAnnouncementComponent, IDEACacheableResourceComponent, IDEACachedResourcesService, IDEACalendarPickerComponent, IDEAColorPickerComponent, IDEAContactsComponent, IDEADateTimeComponent, IDEADurationComponent, IDEAEchoPage, IDEAErrorReportingService, IDEAFromTimeToTimeComponent, IDEAMDEComponent, IDEAMDEToolbarComponent, IDEAOfflineDataService, IDEAOfflineIndicatorComponent, IDEAOfflineManagerComponent, IDEAOfflineService, IDEAPushNotificationsService, IDEASentimentComponent, IDEATimeIntervalComponent, IDEATinCanService, LAST_SYNC_KEY, NUM_ELEMENTS_WITHOUT_MANUAL_SYNC, NUM_ELEMENTS_WITH_MANUAL_SYNC, Periods, QUEUE_API_REQUESTS_KEY, SYNC_EXPIRATION_INTERVAL, ideaEchoRoutes };
|
|
4273
|
+
export { APIRequestOption, CacheModes, CacheableResource, ColorsPaletteComponent, EchoRequests, IDEAAWSAPIService, IDEAAWSAPISocketService, IDEAAddressComponent, IDEAAnnouncementComponent, IDEACacheableResourceComponent, IDEACachedResourcesService, IDEACalendarPickerComponent, IDEAColorPickerComponent, IDEAContactsComponent, IDEADateTimeComponent, IDEADurationComponent, IDEAEchoPage, IDEAErrorReportingService, IDEAFromTimeToTimeComponent, IDEAMDEComponent, IDEAMDEToolbarComponent, IDEAOfflineDataService, IDEAOfflineIndicatorComponent, IDEAOfflineManagerComponent, IDEAOfflineService, IDEAOldAttachmentsComponent, IDEAPushNotificationsService, IDEASentimentComponent, IDEATimeIntervalComponent, IDEATinCanService, LAST_SYNC_KEY, NUM_ELEMENTS_WITHOUT_MANUAL_SYNC, NUM_ELEMENTS_WITH_MANUAL_SYNC, Periods, QUEUE_API_REQUESTS_KEY, SYNC_EXPIRATION_INTERVAL, ideaEchoRoutes };
|
|
3905
4274
|
//# sourceMappingURL=idea-ionic-uncommon.mjs.map
|