parent
46e94c4a31
commit
d9e891a821
3 changed files with 101 additions and 15 deletions
|
@ -20,12 +20,24 @@
|
|||
<table class="table" v-if="stats.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="key" v-for="attr in Object.keys(stats[0].key)" :key="attr">
|
||||
{{ displayName(attr) }}
|
||||
<th class="key"
|
||||
@click="onColumnHeadClick(attr)"
|
||||
v-for="attr in Object.keys(stats[0].key)"
|
||||
:key="attr">
|
||||
<font-awesome-icon v-if="orderBy === attr"
|
||||
:icon="['fas', order === 'asc' ? 'sort-up' : 'sort-down']" />
|
||||
<font-awesome-icon icon="fas fa-sort" v-else />
|
||||
{{ displayName(attr) }}
|
||||
</th>
|
||||
<th :class="column.className"
|
||||
@click="onColumnHeadClick(columnName)"
|
||||
v-for="column, columnName in columns"
|
||||
:key="columnName">
|
||||
<font-awesome-icon v-if="orderBy === columnName"
|
||||
:icon="['fas', order === 'asc' ? 'sort-up' : 'sort-down']" />
|
||||
<font-awesome-icon icon="fas fa-sort" v-else />
|
||||
{{ column.displayName }}
|
||||
</th>
|
||||
<th class="count"># of records</th>
|
||||
<th class="date">First record</th>
|
||||
<th class="date">Last record</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -104,6 +116,20 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
columns: {
|
||||
count: {
|
||||
displayName: '# of records',
|
||||
className: 'count',
|
||||
},
|
||||
startDate: {
|
||||
displayName: 'First record',
|
||||
className: 'date',
|
||||
},
|
||||
endDate: {
|
||||
displayName: 'Last record',
|
||||
className: 'date',
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
metrics: {
|
||||
country: true,
|
||||
|
@ -112,6 +138,8 @@ export default {
|
|||
postalCode: false,
|
||||
description: false,
|
||||
},
|
||||
order: 'desc',
|
||||
orderBy: 'count',
|
||||
showSelectForm: false,
|
||||
stats: [] as LocationStats[],
|
||||
}
|
||||
|
@ -122,11 +150,34 @@ export default {
|
|||
return new StatsRequest({
|
||||
// @ts-ignore
|
||||
userId: this.$root.user.id,
|
||||
order: this.order,
|
||||
orderBy: this.orderBy,
|
||||
groupBy: Object.entries(this.metrics)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([metric]) => metric),
|
||||
})
|
||||
},
|
||||
|
||||
urlQuery() {
|
||||
return Object.entries(this.query)
|
||||
.map(([key, value]) => {
|
||||
if (key === 'userId') {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (key === 'groupBy') {
|
||||
return `${key}=${(value as string[]).sort().join(',')}`;
|
||||
}
|
||||
|
||||
if (key === 'order') {
|
||||
value = (value as string)?.toLowerCase();
|
||||
}
|
||||
|
||||
return `${key}=${encodeURIComponent(value as any)}`
|
||||
})
|
||||
.filter((param: string) => param.length)
|
||||
.join('&');
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -149,16 +200,14 @@ export default {
|
|||
return `/#${key}&order=${opts.ascending ? 'asc' : 'desc'}`
|
||||
},
|
||||
|
||||
setURLQuery() {
|
||||
const enabledMetrics = Object.entries(this.metrics)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([metric]) => metric);
|
||||
|
||||
async setURLQuery() {
|
||||
window.history.replaceState(
|
||||
window.history.state,
|
||||
'',
|
||||
`${window.location.pathname}#groupBy=${enabledMetrics.sort().join(',')}`,
|
||||
`${window.location.pathname}#${this.urlQuery}`,
|
||||
);
|
||||
|
||||
await this.refresh();
|
||||
},
|
||||
|
||||
setState() {
|
||||
|
@ -183,6 +232,9 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
this.orderBy = params.get('orderBy') || 'count';
|
||||
this.order = params.get('order') || 'desc';
|
||||
|
||||
this.metrics = metrics as {
|
||||
country: boolean,
|
||||
locality: boolean,
|
||||
|
@ -208,6 +260,15 @@ export default {
|
|||
this.closeForm();
|
||||
},
|
||||
|
||||
onColumnHeadClick(attr: string) {
|
||||
if (this.orderBy === attr) {
|
||||
this.order = this.order === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
this.orderBy = attr;
|
||||
this.order = attr === 'count' ? 'desc' : 'asc';
|
||||
}
|
||||
},
|
||||
|
||||
displayValue(key: string, value: any) {
|
||||
if (key === 'country') {
|
||||
return this.displayCountry(value);
|
||||
|
@ -242,16 +303,26 @@ export default {
|
|||
query: {
|
||||
handler() {
|
||||
this.setURLQuery();
|
||||
this.refresh();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
orderBy: {
|
||||
handler() {
|
||||
this.setURLQuery();
|
||||
},
|
||||
},
|
||||
|
||||
order: {
|
||||
handler() {
|
||||
this.setURLQuery();
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
async created() {
|
||||
this.setState();
|
||||
this.setURLQuery();
|
||||
await this.refresh();
|
||||
await this.setURLQuery();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -303,6 +374,18 @@ export default {
|
|||
}
|
||||
|
||||
table {
|
||||
thead {
|
||||
th {
|
||||
margin: 0 auto;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-accent);
|
||||
color: var(--color-background);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
td {
|
||||
|
|
|
@ -24,7 +24,7 @@ class Stats {
|
|||
.map((d) => d.dataValues.id)
|
||||
},
|
||||
group: groupBy,
|
||||
order: [[Sequelize.fn('COUNT', Sequelize.col($db.locationTableColumns.id)), req.order]],
|
||||
order: [[req.orderBy, req.order]],
|
||||
})
|
||||
).map(({dataValues: data}: any) =>
|
||||
new LocationStats({
|
||||
|
|
|
@ -4,11 +4,13 @@ type GroupBy = 'device' | 'country' | 'locality' | 'postalCode' | 'description';
|
|||
class StatsRequest {
|
||||
userId: number;
|
||||
groupBy: GroupBy[];
|
||||
orderBy: string = 'count';
|
||||
order: Order = 'DESC';
|
||||
|
||||
constructor(req: {
|
||||
userId: number;
|
||||
groupBy: string[] | string;
|
||||
orderBy?: string;
|
||||
order?: string;
|
||||
}) {
|
||||
this.userId = req.userId;
|
||||
|
@ -18,6 +20,7 @@ class StatsRequest {
|
|||
req.groupBy
|
||||
) as GroupBy[];
|
||||
|
||||
this.orderBy = req.orderBy || this.orderBy;
|
||||
this.order = (req.order || this.order).toUpperCase() as Order;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue