From 3604e844a024a59d58a09afff68b9d04668abf5a Mon Sep 17 00:00:00 2001 From: Fabio Manganiello <fabio@manganiello.tech> Date: Sat, 5 Apr 2025 22:20:31 +0200 Subject: [PATCH] Fixed type errors --- frontend/src/components/Map.vue | 5 +- frontend/src/components/stats/MetricsForm.vue | 8 +-- frontend/src/mixins/Dates.vue | 7 +- frontend/src/models/LocationQuery.ts | 6 ++ frontend/src/styles/layout.scss | 2 +- frontend/src/views/Stats.vue | 71 +++++++++++++------ 6 files changed, 70 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/Map.vue b/frontend/src/components/Map.vue index 3e2e12e..166bd27 100644 --- a/frontend/src/components/Map.vue +++ b/frontend/src/components/Map.vue @@ -159,7 +159,7 @@ export default { data() { return { - countries: [] as string[], + countries: [] as Country[], devices: [] as UserDevice[], loading: false, map: null as Optional<Map>, @@ -307,10 +307,11 @@ export default { await this.processQueryChange(this.locationQuery, oldQuery) }, - async getCountries() { + async getCountries(): Promise<Country[]> { return ( await this.getStats( new StatsRequest({ + // @ts-ignore userId: this.$root.user.id, groupBy: ['country'], order: 'desc', diff --git a/frontend/src/components/stats/MetricsForm.vue b/frontend/src/components/stats/MetricsForm.vue index dc11a2e..4615521 100644 --- a/frontend/src/components/stats/MetricsForm.vue +++ b/frontend/src/components/stats/MetricsForm.vue @@ -2,13 +2,13 @@ <form class="metrics-form" @submit.prevent="$emit('submit', newMetrics)"> <div class="metrics"> <div v-for="enabled, metric in newMetrics" :key="metric" class="metric"> - <label :for="metric"> + <label :for="metric.toString()"> <input type="checkbox" - :id="metric" - v-model="newMetrics[metric]" + :id="metric.toString()" + v-model="newMetrics[metric.toString()]" /> - {{ displayName(metric) }} + {{ displayName(metric.toString()) }} </label> </div> </div> diff --git a/frontend/src/mixins/Dates.vue b/frontend/src/mixins/Dates.vue index abe609c..d8866c2 100644 --- a/frontend/src/mixins/Dates.vue +++ b/frontend/src/mixins/Dates.vue @@ -12,7 +12,12 @@ export default { 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) { dateStr = dateStr.slice(4) } diff --git a/frontend/src/models/LocationQuery.ts b/frontend/src/models/LocationQuery.ts index f7211e1..6026947 100644 --- a/frontend/src/models/LocationQuery.ts +++ b/frontend/src/models/LocationQuery.ts @@ -15,6 +15,8 @@ class LocationQuery { public country: Optional<string> = null; public locality: Optional<string> = null; public postalCode: Optional<string> = null; + public address: Optional<string> = null; + public description: Optional<string> = null; public order: string = 'desc'; constructor(data: { @@ -32,6 +34,8 @@ class LocationQuery { country?: Optional<string>; locality?: Optional<string>; postalCode?: Optional<string>; + address?: Optional<string>; + description?: Optional<string>; order?: Optional<string>; }) { this.limit = data.limit || this.limit; @@ -48,6 +52,8 @@ class LocationQuery { this.country = data.country || this.country; this.locality = data.locality || this.locality; this.postalCode = data.postalCode || this.postalCode; + this.address = data.address || this.address; + this.description = data.description || this.description; this.order = data.order || this.order; } } diff --git a/frontend/src/styles/layout.scss b/frontend/src/styles/layout.scss index 6c024fd..5923f4f 100644 --- a/frontend/src/styles/layout.scss +++ b/frontend/src/styles/layout.scss @@ -27,7 +27,7 @@ $screen-size-to-breakpoint: ( @each $screen-size, $breakpoint in $screen-size-to-breakpoint { .col-#{$screen-size}-#{$i} { @media (min-width: map.get($breakpoints, $breakpoint)) { - width: (100% / 12) * $i; + width: calc((100% / 12) * $i); } } } diff --git a/frontend/src/views/Stats.vue b/frontend/src/views/Stats.vue index ee7112b..86e251b 100644 --- a/frontend/src/views/Stats.vue +++ b/frontend/src/views/Stats.vue @@ -1,10 +1,16 @@ <template> <div class="stats view"> <div class="wrapper"> - <h1> - <font-awesome-icon icon="chart-line" /> - Statistics - </h1> + <div class="header"> + <h1> + <font-awesome-icon icon="chart-line" /> + Statistics + </h1> + + <small v-if="stats.length"> + Showing <b>{{ stats.length }}</b> records + </small> + </div> <div class="loading-container" v-if="loading"> <Loading /> @@ -23,7 +29,7 @@ </tr> </thead> <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"> {{ displayValue(attr, value) }} </td> @@ -61,10 +67,8 @@ </template> <script lang="ts"> -import { getCountryData, getEmojiFlag } from 'countries-list'; -import type { TCountryCode } from 'countries-list'; - import Api from '../mixins/Api.vue'; +import Country from '../models/Country'; import Dates from '../mixins/Dates.vue'; import FloatingButton from '../elements/FloatingButton.vue'; import Loading from '../elements/Loading.vue'; @@ -106,6 +110,7 @@ export default { computed: { query() { return new StatsRequest({ + // @ts-ignore userId: this.$root.user.id, groupBy: Object.entries(this.metrics) .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() { @@ -166,7 +177,14 @@ export default { }, onMetricsSubmit(newMetrics: Record<string, boolean>) { - this.metrics = newMetrics; + this.metrics = newMetrics as { + country: boolean, + locality: boolean, + address: boolean, + postalCode: boolean, + description: boolean, + }; + this.closeForm(); }, @@ -187,13 +205,12 @@ export default { return "<missing>"; } - const cc = countryCode.toUpperCase() as TCountryCode; - const country = getCountryData(cc); + const country = Country.fromCode(countryCode); if (!country) { return countryCode; } - return `${getEmojiFlag(cc)} ${country.name}`; + return `${country.flag} ${country.name}`; }, displayDate(date: Date | string | number | null | undefined) { @@ -209,14 +226,6 @@ export default { }, deep: true, }, - - showSelectForm(newValue: boolean) { - if (newValue) { - this.$nextTick(() => { - this.$refs.metricsForm?.focus(); - }); - } - }, }, 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 { tbody { tr {