Skip to content

Commit 9b39ca9

Browse files
committed
Add some validation. Only store known fields. Fix styling
1 parent 724cb51 commit 9b39ca9

File tree

9 files changed

+104
-43
lines changed

9 files changed

+104
-43
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { enumToEntries } from '@shared/helpers/enum.helper';
1+
import { recordToEntries } from '@shared/helpers/record.helper';
22

33
export enum ApplicationStatus {
44
'NONE' = 0,
@@ -8,4 +8,4 @@ export enum ApplicationStatus {
88
'OTHER' = 4,
99
}
1010

11-
export const ApplicationStatusEntries = enumToEntries(ApplicationStatus);
11+
export const ApplicationStatusEntries = recordToEntries(ApplicationStatus);

src/app/shared/components/forms/form-body-application/form-body-application.component.html

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<!-- TODO: Validate all validators! Make sure they make sense -->
21
<form (ngSubmit)="onSubmit()" #applicationForm="ngForm" class="os2-form p-3 mt-4">
32
<div *ngIf="errorMessages" class="error-messages p-3">
43
<ul class="mb-0">
@@ -35,13 +34,13 @@
3534
</mat-select>
3635
</div>
3736

38-
<!-- TODO: Fix styling -->
3937
<div class="form-group mt-3">
4038
<label class="form-label" for="metadata.startDate">{{'APPLICATION.METADATA-FIELD.START-DATE' | translate}}</label>
4139
<mat-form-field appearance="fill">
4240
<mat-label>{{'QUESTION.APPLICATION.START-DATE-PLACEHOLDER' | translate}}</mat-label>
4341
<input matInput [min]="today" [max]="serializedEndDate.value" [matDatepicker]="startDatePicker"
44-
[formControl]="serializedStartDate">
42+
[formControl]="serializedStartDate"
43+
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.startDate'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.startDate')}">
4544
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
4645
<mat-datepicker panelClass="datepicker-table-fix" #startDatePicker></mat-datepicker>
4746
</mat-form-field>
@@ -52,7 +51,8 @@
5251
<mat-form-field appearance="fill" [ngClass]="'datepicker-table-fix'">
5352
<mat-label>{{'QUESTION.APPLICATION.END-DATE-PLACEHOLDER' | translate}}</mat-label>
5453
<input matInput [min]="serializedStartDate.value" [matDatepicker]="endDatePicker"
55-
[formControl]="serializedEndDate">
54+
[formControl]="serializedEndDate"
55+
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.endDate'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.endDate')}">
5656
<mat-datepicker-toggle matSuffix [for]="endDatePicker"></mat-datepicker-toggle>
5757
<mat-datepicker panelClass="datepicker-table-fix" #endDatePicker></mat-datepicker>
5858
</mat-form-field>
@@ -61,15 +61,15 @@
6161
<div class="form-group mt-3">
6262
<label class="form-label" for="metadata.category">{{'APPLICATION.METADATA-FIELD.CATEGORY' | translate}}</label>
6363
<input type="text" class="form-control" id="category" name="category"
64-
[placeholder]="'QUESTION.APPLICATION.CATEGORY-PLACEHOLDER' | translate" maxlength="50" required
64+
[placeholder]="'QUESTION.APPLICATION.CATEGORY-PLACEHOLDER' | translate" maxlength="100"
6565
[(ngModel)]="metadata.category"
6666
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.category'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.category')}">
6767
</div>
6868

6969
<div class="form-group mt-3">
7070
<label class="form-label" for="metadata.owner">{{'APPLICATION.METADATA-FIELD.OWNER' | translate}}</label>
7171
<input type="text" class="form-control" id="owner" name="owner"
72-
[placeholder]="'QUESTION.APPLICATION.OWNER-PLACEHOLDER' | translate" maxlength="50" required
72+
[placeholder]="'QUESTION.APPLICATION.OWNER-PLACEHOLDER' | translate" maxlength="100"
7373
[(ngModel)]="metadata.owner"
7474
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.owner'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.owner')}">
7575
</div>
@@ -78,32 +78,31 @@
7878
<label class="form-label" for="metadata.contactPerson">{{'APPLICATION.METADATA-FIELD.CONTACT-PERSON' |
7979
translate}}</label>
8080
<input type="text" class="form-control" id="contactPerson" name="contactPerson"
81-
[placeholder]="'QUESTION.APPLICATION.CONTACT-PERSON-PLACEHOLDER' | translate" maxlength="50" required
81+
[placeholder]="'QUESTION.APPLICATION.CONTACT-PERSON-PLACEHOLDER' | translate" maxlength="100"
8282
[(ngModel)]="metadata.contactPerson"
8383
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.contactPerson'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.contactPerson')}">
8484
</div>
8585

86-
<!-- TODO: Validate email. God, not this shit... -->
8786
<div class="form-group mt-3">
88-
<label class="form-label" for="metadata.contactEmail">{{'APPLICATION.METADATA-FIELD.CONTACT-EMAIL' | translate}}</label>
89-
<input type="text" class="form-control" id="contactEmail" name="contactEmail"
90-
[placeholder]="'QUESTION.APPLICATION.CONTACT-EMAIL-PLACEHOLDER' | translate" maxlength="50" required
87+
<label class="form-label" for="metadata.contactEmail">{{'APPLICATION.METADATA-FIELD.CONTACT-EMAIL' |
88+
translate}}</label>
89+
<input type="email" class="form-control" id="contactEmail" name="contactEmail"
90+
[placeholder]="'QUESTION.APPLICATION.CONTACT-EMAIL-PLACEHOLDER' | translate" maxlength="50" [email]="true"
9191
[(ngModel)]="metadata.contactEmail"
9292
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.contactEmail'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.contactEmail')}">
9393
</div>
9494

95-
<!-- TODO: Validate phone. -->
9695
<div class="form-group mt-3">
97-
<label class="form-label" for="metadata.contactPhone">{{'APPLICATION.METADATA-FIELD.CONTACT-PHONE' | translate}}</label>
98-
<input type="text" class="form-control" id="contactPhone" name="contactPhone"
99-
[placeholder]="'QUESTION.APPLICATION.CONTACT-PHONE-PLACEHOLDER' | translate" maxlength="50" required
96+
<label class="form-label" for="metadata.contactPhone">{{'APPLICATION.METADATA-FIELD.CONTACT-PHONE' |
97+
translate}}</label>
98+
<input type="text" class="form-control" id="contactPhone" name="contactPhone" [formControl]="phoneCtrl"
99+
[placeholder]="'QUESTION.APPLICATION.CONTACT-PHONE-PLACEHOLDER' | translate" maxlength="12"
100100
[(ngModel)]="metadata.contactPhone"
101-
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.contactPhone'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.contactPhone')}">
101+
[ngClass]="{'is-invalid' : metadata.contactPhone && phoneCtrl.invalid, 'is-valid' : !phoneCtrl.invalid}">
102102
</div>
103103

104104
<div class="form-group mt-3">
105-
<mat-checkbox id="metadata.personalData" name="metadata.personalData"
106-
[(ngModel)]="metadata.personalData">
105+
<mat-checkbox id="metadata.personalData" name="metadata.personalData" [(ngModel)]="metadata.personalData">
107106
<mat-label for="metadata.personalData" class="form-check-label text--bold margin-top-25px"
108107
style="white-space: initial">{{'QUESTION.APPLICATION.PERSONAL-DATA-PLACEHOLDER' | translate}}</mat-label>
109108
</mat-checkbox>
@@ -122,6 +121,7 @@
122121
translate}}</label>
123122
<mat-select class="form-control" name="metadata.controlledProperty" multiple="true"
124123
[(ngModel)]="metadata.controlledProperty"
124+
[placeholder]="'QUESTION.APPLICATION.CONTROLLED-PROPERTY-PLACEHOLDER' | translate"
125125
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.controlledProperty'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.controlledProperty')}">
126126
<mat-option *ngFor="let ctrlProperty of controlledProperties" [value]="ctrlProperty">
127127
{{ctrlProperty}}
@@ -130,9 +130,10 @@
130130
</div>
131131

132132
<div class="form-group mt-3">
133-
<label class="form-label" for="metadata.deviceType">{{'APPLICATION.METADATA-FIELD.DEVICE-TYPE' | translate}}</label>
134-
<mat-select class="form-control" name="metadata.deviceType" multiple="true"
135-
[(ngModel)]="metadata.deviceType"
133+
<label class="form-label" for="metadata.deviceType">{{'APPLICATION.METADATA-FIELD.DEVICE-TYPE' |
134+
translate}}</label>
135+
<mat-select class="form-control" name="metadata.deviceType" multiple="true" [(ngModel)]="metadata.deviceType"
136+
[placeholder]="'QUESTION.APPLICATION.DEVICE-TYPE-PLACEHOLDER' | translate"
136137
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('metadata.deviceType'), 'is-valid' : formFailedSubmit && !errorFields.includes('metadata.deviceType')}">
137138
<mat-option *ngFor="let deviceType of deviceTypes" [value]="deviceType.value">
138139
{{deviceType.label}}

src/app/shared/components/forms/form-body-application/form-body-application.component.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import { MatDatepicker } from '@angular/material/datepicker';
1313
import { isJSDocThisTag } from 'typescript';
1414
import { SupportedUnit } from '@app/device-model/supported-unit.model';
1515
import { ControlledPropperty } from '@app/device-model/Enums/controlled-propperty.enum';
16-
import { enumToEntries } from '@shared/helpers/enum.helper';
16+
import { recordToEntries } from '@shared/helpers/record.helper';
1717
import { DeviceType } from '@shared/enums/device-type';
18+
import { isPhoneNumberValid } from '@shared/validators/phone-number.validator';
1819

1920

2021
export class User {
@@ -29,6 +30,21 @@ interface DropdownOption {
2930
value: string | number;
3031
}
3132

33+
const knownMetadataFields = [
34+
'status',
35+
'startDate',
36+
'endDate',
37+
'category',
38+
'owner',
39+
'contactPerson',
40+
'contactEmail',
41+
'contactPhone',
42+
'personalData',
43+
'hardware',
44+
'controlledProperty',
45+
'deviceType',
46+
];
47+
3248
@Component({
3349
selector: 'app-form-body-application',
3450
templateUrl: './form-body-application.component.html',
@@ -50,6 +66,7 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
5066
statuses: DropdownOption[] = [];
5167
serializedStartDate = new FormControl();
5268
serializedEndDate = new FormControl();
69+
phoneCtrl: FormControl;
5370
controlledProperties = Object.values(ControlledPropperty);
5471
deviceTypes: DropdownOption[] = [];
5572
today = new Date();
@@ -63,6 +80,7 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
6380
private sharedVariableService: SharedVariableService
6481
) {
6582
this.fillDefaultMetadata();
83+
this.phoneCtrl = new FormControl(this.metadata.contactPhone, [isPhoneNumberValid()]);
6684
}
6785

6886
ngOnInit(): void {
@@ -75,7 +93,7 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
7593
const statusTranslationPrefix = 'APPLICATION.STATUS.';
7694
const statusTranslationKeys = ApplicationStatusEntries.map((x) => `${statusTranslationPrefix}${x.key}`);
7795
const deviceTypeTranslationPrefix = 'IOT-DEVICE-TYPES.';
78-
const deviceTypeEntries = enumToEntries(DeviceType);
96+
const deviceTypeEntries = recordToEntries(DeviceType);
7997
const deviceTypeTranslationKeys = deviceTypeEntries.map((x) => `${deviceTypeTranslationPrefix}${x.key}`);
8098
this.translate
8199
.get([...statusTranslationKeys, ...deviceTypeTranslationKeys, deviceTypeTranslationPrefix + 'OTHER'])
@@ -129,7 +147,19 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
129147
this.application.organizationId = this.sharedVariableService.getSelectedOrganisationId();
130148
this.metadata.startDate = this.serializedStartDate.value?.toISOString();
131149
this.metadata.endDate = this.serializedStartDate.value?.toISOString();
132-
this.application.metadata = JSON.stringify(this.metadata);
150+
151+
// Any unknown properties should not be serialized
152+
const knownMetadata = Object.keys(this.metadata).reduce(
153+
(res: Record<string, unknown>, key) => {
154+
if (knownMetadataFields.includes(key)) {
155+
res[key] = this.metadata[key];
156+
}
157+
158+
return res;
159+
},
160+
{}
161+
);
162+
this.application.metadata = JSON.stringify(knownMetadata);
133163

134164
if (this.id) {
135165
this.updateApplication(this.id);

src/app/shared/components/metadata-details/metadata-details.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class MetadataDetailsComponent implements OnInit {
2929
} else if (typeof value === 'number') {
3030
return value;
3131
} else if (typeof value === 'string') {
32-
if (moment(value).isValid()) {
32+
if (moment(value, moment.ISO_8601, true).isValid()) {
3333
return this.datePipe.transform(value);
3434
}
3535
}

src/app/shared/helpers/enum.helper.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const recordToEntries = <T extends Record<string, unknown>>(
2+
record: T
3+
) => {
4+
return Object.keys(record)
5+
.filter((entry) => isNaN(Number(entry)))
6+
.map((key: keyof typeof record) => ({
7+
key,
8+
value: record[key],
9+
}));
10+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
2+
3+
const phoneNumberRegex = /[- +()0-9]{6,}/;
4+
5+
export const isPhoneNumberValid = (): ValidatorFn => {
6+
return (control: AbstractControl): ValidationErrors | null => {
7+
const isValid = phoneNumberRegex.test(control.value);
8+
return !isValid ? { invalidPhoneNumber: { value: control.value } } : null;
9+
};
10+
};

src/assets/i18n/da.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@
139139
"CONTACT-PHONE": "Kontakttelefon",
140140
"PERSONAL-DATA": "Persondata",
141141
"HARDWARE": "Hardware",
142-
"HARDWARE-PLACEHOLDER": "Angiv hvad der bruges af sensorer/hardware, samt model",
143142
"CONTROLLED-PROPERTY": "Data",
144143
"DEVICE-TYPE": "Forbindelsesteknologi"
145144
}
@@ -629,6 +628,7 @@
629628
"CONTACT-EMAIL-PLACEHOLDER": "Angiv mailadresse på ansvarlig medarbejder",
630629
"CONTACT-PHONE-PLACEHOLDER": "Angiv telefonnummer på ansvarlig medarbejder",
631630
"PERSONAL-DATA-PLACEHOLDER": "Markér, hvis data i applikationen indeholder personoplysninger. Berører ikke funktionalitet i OS2iot",
631+
"HARDWARE-PLACEHOLDER": "Angiv hvad der bruges af sensorer/hardware, samt model",
632632
"CONTROLLED-PROPERTY-PLACEHOLDER": "Angiv hvilke datatyper der opsamles",
633633
"DEVICE-TYPE-PLACEHOLDER": "Angiv hvilke forbindelsesteknologier, der benyttes"
634634
}
Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
1-
.datepicker-table-fix {
2-
// TODO: Hack. Should be applied to mat-datepicker-content but doesn't work
3-
mat-datepicker-content {
4-
margin: 1000px; // Temp to see the difference
5-
padding-bottom: 10px;
1+
mat-calendar.datepicker-table-fix {
2+
margin-bottom: 25px;
3+
4+
table {
5+
box-shadow: unset;
6+
-webkit-box-shadow: unset;
7+
-moz-box-shadow: unset;
8+
9+
thead {
10+
background-color: unset;
11+
}
12+
13+
tbody {
14+
tr {
15+
border-top: unset;
16+
17+
&:hover {
18+
background-color: unset;
19+
}
20+
21+
td.mat-calendar-body-active div {
22+
border-bottom-width: 1px !important;
23+
}
24+
}
25+
}
626
}
727
}

0 commit comments

Comments
 (0)