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