Fixed type errors

This commit is contained in:
Fabio Manganiello 2025-04-05 22:20:31 +02:00
parent ca666c9ef8
commit 3604e844a0
Signed by: blacklight
GPG key ID: D90FBA7F76362774
6 changed files with 70 additions and 29 deletions
frontend/src

View file

@ -159,7 +159,7 @@ export default {
data() { data() {
return { return {
countries: [] as string[], countries: [] as Country[],
devices: [] as UserDevice[], devices: [] as UserDevice[],
loading: false, loading: false,
map: null as Optional<Map>, map: null as Optional<Map>,
@ -307,10 +307,11 @@ export default {
await this.processQueryChange(this.locationQuery, oldQuery) await this.processQueryChange(this.locationQuery, oldQuery)
}, },
async getCountries() { async getCountries(): Promise<Country[]> {
return ( return (
await this.getStats( await this.getStats(
new StatsRequest({ new StatsRequest({
// @ts-ignore
userId: this.$root.user.id, userId: this.$root.user.id,
groupBy: ['country'], groupBy: ['country'],
order: 'desc', order: 'desc',

View file

@ -2,13 +2,13 @@
<form class="metrics-form" @submit.prevent="$emit('submit', newMetrics)"> <form class="metrics-form" @submit.prevent="$emit('submit', newMetrics)">
<div class="metrics"> <div class="metrics">
<div v-for="enabled, metric in newMetrics" :key="metric" class="metric"> <div v-for="enabled, metric in newMetrics" :key="metric" class="metric">
<label :for="metric"> <label :for="metric.toString()">
<input <input
type="checkbox" type="checkbox"
:id="metric" :id="metric.toString()"
v-model="newMetrics[metric]" v-model="newMetrics[metric.toString()]"
/>&nbsp; />&nbsp;
{{ displayName(metric) }} {{ displayName(metric.toString()) }}
</label> </label>
</div> </div>
</div> </div>

View file

@ -12,7 +12,12 @@ export default {
return '-' return '-'
} }
let dateStr = this.normalizeDate(date).toString().replace(/GMT.*/, '').trim() as string date = this.normalizeDate(date)
if (!date) {
return '-'
}
let dateStr = date.toString().replace(/GMT.*/, '').trim() as string
if (!opts.dayOfWeek) { if (!opts.dayOfWeek) {
dateStr = dateStr.slice(4) dateStr = dateStr.slice(4)
} }

View file

@ -15,6 +15,8 @@ class LocationQuery {
public country: Optional<string> = null; public country: Optional<string> = null;
public locality: Optional<string> = null; public locality: Optional<string> = null;
public postalCode: Optional<string> = null; public postalCode: Optional<string> = null;
public address: Optional<string> = null;
public description: Optional<string> = null;
public order: string = 'desc'; public order: string = 'desc';
constructor(data: { constructor(data: {
@ -32,6 +34,8 @@ class LocationQuery {
country?: Optional<string>; country?: Optional<string>;
locality?: Optional<string>; locality?: Optional<string>;
postalCode?: Optional<string>; postalCode?: Optional<string>;
address?: Optional<string>;
description?: Optional<string>;
order?: Optional<string>; order?: Optional<string>;
}) { }) {
this.limit = data.limit || this.limit; this.limit = data.limit || this.limit;
@ -48,6 +52,8 @@ class LocationQuery {
this.country = data.country || this.country; this.country = data.country || this.country;
this.locality = data.locality || this.locality; this.locality = data.locality || this.locality;
this.postalCode = data.postalCode || this.postalCode; this.postalCode = data.postalCode || this.postalCode;
this.address = data.address || this.address;
this.description = data.description || this.description;
this.order = data.order || this.order; this.order = data.order || this.order;
} }
} }

View file

@ -27,7 +27,7 @@ $screen-size-to-breakpoint: (
@each $screen-size, $breakpoint in $screen-size-to-breakpoint { @each $screen-size, $breakpoint in $screen-size-to-breakpoint {
.col-#{$screen-size}-#{$i} { .col-#{$screen-size}-#{$i} {
@media (min-width: map.get($breakpoints, $breakpoint)) { @media (min-width: map.get($breakpoints, $breakpoint)) {
width: (100% / 12) * $i; width: calc((100% / 12) * $i);
} }
} }
} }

View file

@ -1,10 +1,16 @@
<template> <template>
<div class="stats view"> <div class="stats view">
<div class="wrapper"> <div class="wrapper">
<h1> <div class="header">
<font-awesome-icon icon="chart-line" />&nbsp; <h1>
Statistics <font-awesome-icon icon="chart-line" />&nbsp;
</h1> Statistics
</h1>
<small v-if="stats.length">
Showing <b>{{ stats.length }}</b> records
</small>
</div>
<div class="loading-container" v-if="loading"> <div class="loading-container" v-if="loading">
<Loading /> <Loading />
@ -23,7 +29,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="stat in stats" :key="stat.key"> <tr v-for="stat, i in stats" :key="i">
<td class="key" v-for="value, attr in stat.key" :key="attr"> <td class="key" v-for="value, attr in stat.key" :key="attr">
{{ displayValue(attr, value) }} {{ displayValue(attr, value) }}
</td> </td>
@ -61,10 +67,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { getCountryData, getEmojiFlag } from 'countries-list';
import type { TCountryCode } from 'countries-list';
import Api from '../mixins/Api.vue'; import Api from '../mixins/Api.vue';
import Country from '../models/Country';
import Dates from '../mixins/Dates.vue'; import Dates from '../mixins/Dates.vue';
import FloatingButton from '../elements/FloatingButton.vue'; import FloatingButton from '../elements/FloatingButton.vue';
import Loading from '../elements/Loading.vue'; import Loading from '../elements/Loading.vue';
@ -106,6 +110,7 @@ export default {
computed: { computed: {
query() { query() {
return new StatsRequest({ return new StatsRequest({
// @ts-ignore
userId: this.$root.user.id, userId: this.$root.user.id,
groupBy: Object.entries(this.metrics) groupBy: Object.entries(this.metrics)
.filter(([_, enabled]) => enabled) .filter(([_, enabled]) => enabled)
@ -158,7 +163,13 @@ export default {
} }
} }
this.metrics = metrics; this.metrics = metrics as {
country: boolean,
locality: boolean,
address: boolean,
postalCode: boolean,
description: boolean,
};
}, },
closeForm() { closeForm() {
@ -166,7 +177,14 @@ export default {
}, },
onMetricsSubmit(newMetrics: Record<string, boolean>) { onMetricsSubmit(newMetrics: Record<string, boolean>) {
this.metrics = newMetrics; this.metrics = newMetrics as {
country: boolean,
locality: boolean,
address: boolean,
postalCode: boolean,
description: boolean,
};
this.closeForm(); this.closeForm();
}, },
@ -187,13 +205,12 @@ export default {
return "<missing>"; return "<missing>";
} }
const cc = countryCode.toUpperCase() as TCountryCode; const country = Country.fromCode(countryCode);
const country = getCountryData(cc);
if (!country) { if (!country) {
return countryCode; return countryCode;
} }
return `${getEmojiFlag(cc)} ${country.name}`; return `${country.flag} ${country.name}`;
}, },
displayDate(date: Date | string | number | null | undefined) { displayDate(date: Date | string | number | null | undefined) {
@ -209,14 +226,6 @@ export default {
}, },
deep: true, deep: true,
}, },
showSelectForm(newValue: boolean) {
if (newValue) {
this.$nextTick(() => {
this.$refs.metricsForm?.focus();
});
}
},
}, },
async created() { async created() {
@ -253,6 +262,26 @@ export default {
} }
} }
.header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
small {
margin: -1em 0 1em 0;
opacity: 0.6;
}
}
.list {
.no-data {
font-size: 1.2rem;
margin: 1rem;
text-align: center;
}
}
table { table {
tbody { tbody {
tr { tr {