diff --git a/frontend/src/components/filter/Form.vue b/frontend/src/components/filter/Form.vue index bd55fe9..fcff5a0 100644 --- a/frontend/src/components/filter/Form.vue +++ b/frontend/src/components/filter/Form.vue @@ -241,6 +241,26 @@ v-model="newFilter.description" :disabled="disabled" /> </div> + + <div class="container ids-container"> + <label for="ids"> + <p class="title"> + <font-awesome-icon icon="fas fa-tag" /> + Point IDs + </p> + + <p class="help"> + Comma-separated list of point IDs to filter by + </p> + </label> + + <input type="text" + id="ids" + name="ids" + placeholder="Filter by point IDs" + v-model="newFilter.ids" + :disabled="disabled" /> + </div> </main> <footer> @@ -516,6 +536,10 @@ $footer-height: 3.5em; width: 50%; } + .title { + text-align: center; + } + label { margin-bottom: 0.25em; @include until(tablet) { diff --git a/frontend/src/models/LocationQuery.ts b/frontend/src/models/LocationQuery.ts index 6026947..0e0f1e8 100644 --- a/frontend/src/models/LocationQuery.ts +++ b/frontend/src/models/LocationQuery.ts @@ -6,6 +6,7 @@ class LocationQuery { public deviceId: Optional<string> = null; public startDate: Optional<Date> = null; public endDate: Optional<Date> = null; + public ids: Optional<number[]> = null; public minId: Optional<number> = null; public maxId: Optional<number> = null; public minLatitude: Optional<number> = null; @@ -25,6 +26,7 @@ class LocationQuery { deviceId?: Optional<string>; startDate?: Optional<Date>; endDate?: Optional<Date>; + ids?: Optional<number[] | string>; minId?: Optional<number>; maxId?: Optional<number>; minLatitude?: Optional<number>; @@ -43,6 +45,7 @@ class LocationQuery { this.deviceId = data.deviceId || this.deviceId; this.startDate = data.startDate || this.startDate; this.endDate = data.endDate || this.endDate; + this.ids = data.ids ? (Array.isArray(data.ids) ? data.ids : data.ids.split(/\s*,\s*/).map(Number)) : this.ids; this.minId = data.minId || this.minId; this.maxId = data.maxId || this.maxId; this.minLatitude = data.minLatitude || this.minLatitude; diff --git a/src/requests/LocationRequest.ts b/src/requests/LocationRequest.ts index a1bb7c8..45f1081 100644 --- a/src/requests/LocationRequest.ts +++ b/src/requests/LocationRequest.ts @@ -13,6 +13,7 @@ class LocationRequest { offset: Optional<number> = null; startDate: Optional<Date> = null; endDate: Optional<Date> = null; + ids: Optional<number[]> = null; minId: Optional<number> = null; maxId: Optional<number> = null; minLatitude: Optional<number> = null; @@ -33,6 +34,7 @@ class LocationRequest { offset?: number; startDate?: Date; endDate?: Date; + ids?: number[] | string; minId?: number; maxId?: number; minLatitude?: number; @@ -64,6 +66,15 @@ class LocationRequest { this.description = req.description; this.orderBy = req.orderBy || this.orderBy; this.order = (req.order || this.order).toUpperCase() as Order; + + const ids = typeof req.ids === 'string' ? req.ids.split(/\s*,\s*/) : req.ids; + this.ids = (ids || []).map((id: any) => { + const numId = parseInt(id); + if (isNaN(numId)) { + throw new ValidationError(`Invalid value for ids: ${id}`); + } + return numId; + }); } private initNumber(key: string, req: any, parser: (s: string) => number = parseInt): void { @@ -89,6 +100,16 @@ class LocationRequest { let queryMap: any = {}; const where: any = {}; + if (this.ids?.length) { + queryMap.where = { + [db.locationTableColumns.id || 'id']: {[Op.in]: this.ids}, + }; + + queryMap.order = [[db.locationTableColumns.timestamp || 'timestamp', this.order.toUpperCase()]]; + // If we have ids, we don't need any other filters + return queryMap; + } + if (this.limit != null) { queryMap.limit = this.limit; }