diff --git a/frontend/src/components/Map.vue b/frontend/src/components/Map.vue index 48637cb..79dc7c7 100644 --- a/frontend/src/components/Map.vue +++ b/frontend/src/components/Map.vue @@ -44,13 +44,8 @@ <FilterForm :value="locationQuery" :devices="devices" :disabled="loading" - :has-next-page="hasNextPage" - :has-prev-page="hasPrevPage" :resolution="resolutionMeters" @refresh="locationQuery = $event" - @reset-page="locationQuery.minId = locationQuery.maxId = null" - @next-page="fetchNextPage" - @prev-page="fetchPrevPage" @set-resolution="setResolution" /> </div> <FilterButton @input="showControls = !showControls" @@ -70,8 +65,12 @@ <div class="timeline"> <Timeline :loading="loading" + :locationQuery="locationQuery" :points="gpsPoints" :show-metrics="showMetrics" + @next-page="fetchNextPage" + @prev-page="fetchPrevPage" + @reset-page="resetPage" @point-hover="onTimelinePointHover" @show-metrics="setShowMetrics" /> </div> @@ -283,6 +282,12 @@ export default { this.locationQuery = prevPageQuery }, + async resetPage() { + const oldQuery = { ...this.locationQuery } + this.locationQuery.minId = this.locationQuery.maxId = null + await this.processQueryChange(this.locationQuery, oldQuery) + }, + createMap(): Map { this.pointsLayer = this.createPointsLayer(Object.values(this.mappedPoints) as Point[]) this.routesLayer = this.createRoutesLayer(Object.values(this.mappedPoints) as Point[]) @@ -431,6 +436,8 @@ export default { ...this.locationQuery, startDate: null, endDate: null, + minId: null, + maxId: null, minLongitude: startLon, minLatitude: startLat, maxLongitude: endLon, @@ -444,6 +451,8 @@ export default { } else { this.locationQuery = { ...this.locationQuery, + minId: null, + maxId: null, minLongitude: null, minLatitude: null, maxLongitude: null, @@ -451,18 +460,8 @@ export default { } } }, - }, - - watch: { - locationQuery: { - async handler(newQuery: LocationQuery, oldQuery: LocationQuery) { - if (!this.isQueryChanged({ - newValue: newQuery, - oldValue: oldQuery, - })) { - return - } + async processQueryChange(newQuery: LocationQuery, oldQuery: LocationQuery) { // If startDate/endDate have changed, reset minId/maxId if (newQuery.startDate !== oldQuery.startDate || newQuery.endDate !== oldQuery.endDate) { newQuery.minId = null @@ -507,6 +506,20 @@ export default { this.refreshMap() } }, + }, + + watch: { + locationQuery: { + async handler(newQuery: LocationQuery, oldQuery: LocationQuery) { + if (!this.isQueryChanged({ + newValue: newQuery, + oldValue: oldQuery, + })) { + return + } + + await this.processQueryChange(newQuery, oldQuery) + }, deep: true, }, diff --git a/frontend/src/components/Timeline.vue b/frontend/src/components/Timeline.vue index cf568d3..7cbb9fe 100644 --- a/frontend/src/components/Timeline.vue +++ b/frontend/src/components/Timeline.vue @@ -23,9 +23,31 @@ </button> </div> + <div class="page-button-container"> + <button @click="$emit('prev-page')" + title="Previous results"> + <font-awesome-icon icon="chevron-left" /> + </button> + </div> + <div class="timeline"> <Line :data="graphData" :options="graphOptions" /> </div> + + <div class="page-button-container"> + <button @click="$emit('next-page')" + title="Next results"> + <font-awesome-icon icon="chevron-right" /> + </button> + </div> + + <div class="page-button-container" + v-if="locationQuery?.minId || locationQuery?.maxId"> + <button @click="$emit('reset-page')" + title="Reset pagination"> + <font-awesome-icon icon="fas fa-undo" /> + </button> + </div> </div> </div> </template> @@ -47,6 +69,7 @@ import 'chartjs-adapter-date-fns'; import Geo from '../mixins/Geo.vue'; import GPSPoint from '../models/GPSPoint'; +import LocationQuery from '../models/LocationQuery'; import TimelineMetricsConfiguration from '../models/TimelineMetricsConfiguration'; ChartJS.register( @@ -60,7 +83,13 @@ ChartJS.register( ); export default { - emits: ['point-hover', 'show-metrics'], + emits: [ + 'next-page', + 'point-hover', + 'prev-page', + 'reset-page', + 'show-metrics', + ], mixins: [Geo], components: { Line, @@ -71,6 +100,9 @@ export default { type: Boolean, default: false, }, + locationQuery: { + type: LocationQuery, + }, points: { type: Array as () => GPSPoint[], default: () => [], @@ -332,4 +364,26 @@ $options-width: 5em; } } } + +.page-button-container { + width: 3em; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + + button { + width: 100%; + height: 100%; + font-size: 1em; + background-color: var(--color-background); + border: 0; + margin-left: 0.5em; + cursor: pointer; + + &:hover { + color: var(--color-hover); + } + } +} </style> diff --git a/frontend/src/components/filter/Form.vue b/frontend/src/components/filter/Form.vue index 671805c..61ddcba 100644 --- a/frontend/src/components/filter/Form.vue +++ b/frontend/src/components/filter/Form.vue @@ -86,24 +86,6 @@ </div> <div class="pagination-container"> - <div class="page-button-container"> - <button type="button" - :disabled="disabled" - v-if="value?.minId || value?.maxId" - @click.stop="$emit('reset-page')"> - <font-awesome-icon icon="fas fa-undo" /> - </button> - </div> - - <div class="page-button-container"> - <button type="button" - @click="$emit('prev-page')" - title="Previous Results" - :disabled="disabled || !hasPrevPage"> - <font-awesome-icon icon="fas fa-chevron-left" /> - </button> - </div> - <div class="limit-container"> <label for="limit">Max Results</label> <input type="number" @@ -115,15 +97,6 @@ :disabled="disabled" min="1" /> </div> - - <div class="page-button-container"> - <button type="button" - @click="$emit('next-page')" - title="Next Results" - :disabled="disabled || !hasNextPage"> - <font-awesome-icon icon="fas fa-chevron-right" /> - </button> - </div> </div> <div class="resolution-container"> @@ -176,9 +149,6 @@ import UserDevice from '../../models/UserDevice' export default { mixins: [LocationQueryMixin], emit: [ - 'next-page', - 'prev-page', - 'reset-page', 'refresh', 'set-resolution', ], @@ -193,14 +163,6 @@ export default { type: Boolean, default: false, }, - hasPrevPage: { - type: Boolean, - default: true, - }, - hasNextPage: { - type: Boolean, - default: true, - }, resolution: { type: Number, required: true, @@ -432,11 +394,6 @@ export default { align-items: center; } - .page-button-container { - display: flex; - justify-content: center; - } - label { margin-bottom: 0.25em; }