Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve timeline usability (less clicks to apply time filters) #610

Merged
merged 9 commits into from
Oct 5, 2024
111 changes: 61 additions & 50 deletions src/components/InputTimeInterval.vue
Original file line number Diff line number Diff line change
@@ -1,55 +1,64 @@
<template lang="pug">
div
div
b-alert(v-if="mode == 'range' && invalidDaterange", variant="warning", show)
b-alert(v-if="invalidDaterange", variant="warning", show)
| The selected date range is invalid. The second date must be greater or equal to the first date.
b-alert(v-if="mode == 'range' && daterangeTooLong", variant="warning", show)
b-alert(v-if="daterangeTooLong", variant="warning", show)
| The selected date range is too long. The maximum is {{ maxDuration/(24*60*60) }} days.

div.d-flex.justify-content-between.align-items-end
ErikBjare marked this conversation as resolved.
Show resolved Hide resolved
table
tr
th.pr-2
label(for="mode") Interval mode:
td
select(id="mode", v-model="mode")
option(value='last_duration') Last duration
option(value='range') Date range
tr(v-if="mode == 'last_duration'")
th.pr-2
label(for="duration") Show last:
td
select(id="duration", v-model="duration", @change="valueChanged")
option(:value="15*60") 15min
option(:value="30*60") 30min
option(:value="60*60") 1h
option(:value="2*60*60") 2h
option(:value="4*60*60") 4h
option(:value="6*60*60") 6h
option(:value="12*60*60") 12h
option(:value="24*60*60") 24h
tr(v-if="mode == 'range'")
th.pr-2 Range:
td
input(type="date", v-model="start")
input(type="date", v-model="end")
button(
class="btn btn-outline-dark btn-sm",
type="button",
:disabled="mode == 'range' && (invalidDaterange || emptyDaterange || daterangeTooLong)",
@click="valueChanged"
) Update
table
tr
td.pr-2
label.col-form-label Show last:
td(colspan=2)
.btn-group(role="group")
input(type="radio", id="dur1", :value="15*60", v-model="duration", @change="applyLastDuration").sr-only
ErikBjare marked this conversation as resolved.
Show resolved Hide resolved
label(for="dur1").btn.btn-light.rounded-left &frac14;h
input(type="radio", id="dur2", :value="30*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur2").btn.btn-light &frac12;h
input(type="radio", id="dur3", :value="1*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur3").btn.btn-light 1h
input(type="radio", id="dur4", :value="2*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur4").btn.btn-light 2h
input(type="radio", id="dur5", :value="3*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur5").btn.btn-light 3h
input(type="radio", id="dur6", :value="4*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur6").btn.btn-light 4h
input(type="radio", id="dur7", :value="6*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur7").btn.btn-light 6h
input(type="radio", id="dur8", :value="12*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur8").btn.btn-light 12h
input(type="radio", id="dur9", :value="24*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur9").btn.btn-light 24h
input(type="radio", id="dur10", :value="48*60*60", v-model="duration", @change="applyLastDuration").sr-only
label(for="dur10").btn.btn-light 48h
ideadapt marked this conversation as resolved.
Show resolved Hide resolved

div(style="text-align:right" v-if="showUpdate && mode=='last_duration'")
b-button.px-2(@click="update()", variant="outline-dark", size="sm")
icon(name="sync")
span.d-none.d-md-inline
| Update
div.mt-1.small.text-muted(v-if="lastUpdate")
| Last update: #[time(:datetime="lastUpdate.format()") {{lastUpdate | friendlytime}}]
tr
td.pr-2
label.col-form-label Show from:
td
input.form-control.d-inline-block.p-1(type="date", v-model="start", style="height: auto; width: auto;")
label.col-form-label.pr-1.pl-2 to:
input.form-control.d-inline.p-1(type="date", v-model="end", style="height: auto; width: auto")
td.text-right
button(
class="btn btn-outline-dark btn-sm",
type="button",
:disabled="invalidDaterange || emptyDaterange || daterangeTooLong",
@click="applyRange"
) Apply

div.mt-1.small.text-muted(v-if="lastUpdate", v-bind:title="lastUpdate")
| Last update: #[time(:datetime="lastUpdate.format()") {{lastUpdate | friendlytime}}]
</template>

<style scoped lang="scss"></style>
<style scoped lang="scss">
.btn-group {
input[type='radio']:checked + label {
background-color: #aaa;
}
}
</style>

<script lang="ts">
import moment from 'moment';
Expand Down Expand Up @@ -109,7 +118,7 @@ export default {
const _lastUpdate = this.lastUpdate;
this.lastUpdate = null;
this.lastUpdate = _lastUpdate;
}, 1000);
}, 3000);
ideadapt marked this conversation as resolved.
Show resolved Hide resolved
ideadapt marked this conversation as resolved.
Show resolved Hide resolved
},
beforeDestroy() {
clearInterval(this.lastUpdateTimer);
Expand All @@ -124,12 +133,14 @@ export default {
this.$emit('input', this.value);
}
},
update() {
if (this.mode == 'last_duration') {
this.mode = ''; // remove cache on v-model, see explanation: https://github.com/ActivityWatch/aw-webui/pull/344/files#r892982094
this.mode = 'last_duration';
this.valueChanged();
}
applyRange() {
this.mode = 'range';
this.duration = 0;
this.valueChanged();
},
applyLastDuration() {
this.mode = 'last_duration';
this.valueChanged();
},
},
};
Expand Down
12 changes: 8 additions & 4 deletions src/views/Timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
div
h2 Timeline

input-timeinterval(v-model="daterange", :defaultDuration="timeintervalDefaultDuration", :maxDuration="maxDuration").mb-2
input-timeinterval(v-model="daterange", :defaultDuration="timeintervalDefaultDuration", :maxDuration="maxDuration").mb-3

// blocks
div.d-inline-block.border.rounded.p-2.mr-2
| Events shown: {{ num_events }}
details.d-inline-block.bg-light.small.border.rounded.mr-2.px-2
summary.p-2
b Filters
Expand All @@ -26,7 +24,11 @@ div
select(v-model="filter_client")
option(:value='null') All
option(v-for="client in clients", :value="client") {{ client }}
div(style="float: right; color: #999").d-inline-block.pt-3
div.d-inline-block.border.rounded.p-2.mr-2(v-if="num_events !== 0")
| Events shown: {{ num_events }}
b-alert.d-inline-block.p-2.mb-0(v-if="num_events === 0", variant="warning", show)
| No events match selected criteria. Timeline is not updated.
div.float-right.small.text-muted.pt-3
| Drag to pan and scroll to zoom

div(v-if="buckets !== null")
Expand Down Expand Up @@ -63,6 +65,8 @@ export default {
const settingsStore = useSettingsStore();
return Number(settingsStore.durationDefault);
},
// TODO this does not match the actual vis-timeline.chartData which is rendered in the timeline.
// chartData excludes short events.
ErikBjare marked this conversation as resolved.
Show resolved Hide resolved
num_events() {
return _.sumBy(this.buckets, 'events.length');
},
Expand Down
Loading