Compare commits
3
Commits
@@ -3,7 +3,6 @@ import type { Category, CreateReportRequest } from '~/types/api'
|
|||||||
import type { LeafletMouseEvent, Map as LeafletMap, Marker as LeafletMarker } from 'leaflet'
|
import type { LeafletMouseEvent, Map as LeafletMap, Marker as LeafletMarker } from 'leaflet'
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { BARI_BOUNDS, BARI_CENTER, isWithinBariBounds } from '~/utils/bari'
|
import { BARI_BOUNDS, BARI_CENTER, isWithinBariBounds } from '~/utils/bari'
|
||||||
import { reverseGeocode, searchAddresses } from '~/utils/geocoding'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
@@ -20,6 +19,14 @@ interface GeocodingSuggestion {
|
|||||||
longitude: number
|
longitude: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SearchAddressesResponse {
|
||||||
|
suggestions: GeocodingSuggestion[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReverseGeocodeResponse {
|
||||||
|
address: string
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
categories: () => []
|
categories: () => []
|
||||||
@@ -116,20 +123,18 @@ const fetchAddressSuggestions = async (query: string) => {
|
|||||||
addressSearchError.value = ''
|
addressSearchError.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (requestId !== addressSearchRequestId) {
|
const response = await $fetch<SearchAddressesResponse>('/api/geocoding/search', {
|
||||||
return
|
query: { q: query }
|
||||||
}
|
})
|
||||||
|
|
||||||
const suggestions = await searchAddresses(query)
|
|
||||||
|
|
||||||
if (requestId !== addressSearchRequestId) {
|
if (requestId !== addressSearchRequestId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addressSuggestions.value = suggestions
|
addressSuggestions.value = response.suggestions
|
||||||
showAddressSuggestions.value = suggestions.length > 0
|
showAddressSuggestions.value = response.suggestions.length > 0
|
||||||
|
|
||||||
if (!suggestions.length) {
|
if (!response.suggestions.length) {
|
||||||
addressSearchError.value = 'Nessun indirizzo trovato nel Comune di Bari.'
|
addressSearchError.value = 'Nessun indirizzo trovato nel Comune di Bari.'
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -152,11 +157,15 @@ const resolveAddressFromCoordinates = async (lat: number, lng: number, fallbackA
|
|||||||
addressSearchError.value = ''
|
addressSearchError.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const response = await $fetch<ReverseGeocodeResponse>('/api/geocoding/reverse', {
|
||||||
|
query: { lat, lng }
|
||||||
|
})
|
||||||
|
|
||||||
if (requestId !== reverseGeocodeRequestId) {
|
if (requestId !== reverseGeocodeRequestId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedAddress = (await reverseGeocode(lat, lng))?.trim() || fallbackAddress.trim()
|
const resolvedAddress = response.address?.trim() || fallbackAddress.trim()
|
||||||
|
|
||||||
if (resolvedAddress) {
|
if (resolvedAddress) {
|
||||||
setAddressValue(resolvedAddress)
|
setAddressValue(resolvedAddress)
|
||||||
@@ -532,7 +541,7 @@ watch([() => form.value.latitude, () => form.value.longitude], ([newLat, newLng]
|
|||||||
step="0.000001"
|
step="0.000001"
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
:class="{ 'is-invalid': errors.location }"
|
:class="{ 'is-invalid': errors.location }"
|
||||||
placeholder="Latitudine (es. 45.5)"
|
placeholder="Latitudine (Bari)"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -543,7 +552,7 @@ watch([() => form.value.latitude, () => form.value.longitude], ([newLat, newLng]
|
|||||||
step="0.000001"
|
step="0.000001"
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
:class="{ 'is-invalid': errors.location }"
|
:class="{ 'is-invalid': errors.location }"
|
||||||
placeholder="Longitudine (es. 10.2)"
|
placeholder="Longitudine (Bari)"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -685,3 +694,4 @@ textarea {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const banError = ref('')
|
|||||||
const roleError = ref('')
|
const roleError = ref('')
|
||||||
const pendingBanUser = ref<any | null>(null)
|
const pendingBanUser = ref<any | null>(null)
|
||||||
const isUpdatingBan = ref(false)
|
const isUpdatingBan = ref(false)
|
||||||
const parseRoles = (roles?: string) => roles ? roles.split(',').map((r) => r.trim().toUpperCase()).filter(Boolean) : []
|
const parseRoles = (roles?: string) => roles ? roles.split(',').map((r) => r.trim()).filter(Boolean) : []
|
||||||
const banConfirmMessage = computed(() =>
|
const banConfirmMessage = computed(() =>
|
||||||
pendingBanUser.value
|
pendingBanUser.value
|
||||||
? `Vuoi ${pendingBanUser.value.banned ? 'sbloccare' : 'bannare'} utente ${pendingBanUser.value.first_name} ${pendingBanUser.value.last_name}?`
|
? `Vuoi ${pendingBanUser.value.banned ? 'sbloccare' : 'bannare'} utente ${pendingBanUser.value.first_name} ${pendingBanUser.value.last_name}?`
|
||||||
|
|||||||
+23
-2
@@ -8,8 +8,6 @@ export default defineNuxtConfig({
|
|||||||
'@nuxtjs/sitemap',
|
'@nuxtjs/sitemap',
|
||||||
'@nuxtjs/robots'
|
'@nuxtjs/robots'
|
||||||
],
|
],
|
||||||
// TODO: riattivare prima di pubblicare il sito
|
|
||||||
ssr: false,
|
|
||||||
|
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
@@ -37,6 +35,29 @@ export default defineNuxtConfig({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
nitro: {
|
||||||
|
devProxy: {
|
||||||
|
'/api/v1': {
|
||||||
|
target: 'https://bk.visione.studentindipendenti.it/api/v1',
|
||||||
|
changeOrigin: true,
|
||||||
|
headers: {
|
||||||
|
host: 'bk.visione.studentindipendenti.it',
|
||||||
|
origin: 'https://visione.studentindipendenti.it',
|
||||||
|
referer: 'https://visione.studentindipendenti.it/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'/uploads': {
|
||||||
|
target: 'https://bk.visione.studentindipendenti.it/uploads',
|
||||||
|
changeOrigin: true,
|
||||||
|
headers: {
|
||||||
|
host: 'bk.visione.studentindipendenti.it',
|
||||||
|
origin: 'https://visione.studentindipendenti.it',
|
||||||
|
referer: 'https://visione.studentindipendenti.it/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
app: {
|
app: {
|
||||||
head: {
|
head: {
|
||||||
link: [
|
link: [
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { createError, getQuery } from 'h3'
|
||||||
|
import { reverseGeocode } from '../../utils/geocoding'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const query = getQuery(event)
|
||||||
|
const latitude = typeof query.lat === 'string' ? Number.parseFloat(query.lat) : Number.NaN
|
||||||
|
const longitude = typeof query.lng === 'string' ? Number.parseFloat(query.lng) : Number.NaN
|
||||||
|
|
||||||
|
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: 'Invalid coordinates'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = await reverseGeocode(latitude, longitude)
|
||||||
|
|
||||||
|
return { address }
|
||||||
|
})
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { createError, getQuery } from 'h3'
|
||||||
|
import { searchAddresses } from '../../utils/geocoding'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const query = getQuery(event)
|
||||||
|
const rawText = typeof query.q === 'string' ? query.q.trim() : ''
|
||||||
|
const rawLimit = typeof query.limit === 'string' ? Number.parseInt(query.limit, 10) : 5
|
||||||
|
const limit = Number.isFinite(rawLimit) ? Math.min(Math.max(rawLimit, 1), 8) : 5
|
||||||
|
|
||||||
|
if (rawText.length < 3) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: 'Query too short'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const suggestions = await searchAddresses(rawText, limit)
|
||||||
|
|
||||||
|
return { suggestions }
|
||||||
|
})
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import { BARI_BOUNDS, isWithinBariBounds } from '~/utils/bari'
|
|
||||||
|
|
||||||
interface NominatimAddress {
|
interface NominatimAddress {
|
||||||
road?: string
|
road?: string
|
||||||
house_number?: string
|
house_number?: string
|
||||||
@@ -29,8 +27,26 @@ export interface GeocodingSuggestion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org'
|
const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org'
|
||||||
|
const BARI_BOUNDS = {
|
||||||
|
north: 41.1696,
|
||||||
|
south: 41.0718,
|
||||||
|
east: 16.9521,
|
||||||
|
west: 16.7564
|
||||||
|
}
|
||||||
|
|
||||||
|
const NOMINATIM_HEADERS = {
|
||||||
|
'User-Agent': 'Visione Frontend/1.0 (https://visione.studentindipendenti.it)',
|
||||||
|
'Referer': 'https://visione.studentindipendenti.it/'
|
||||||
|
}
|
||||||
|
|
||||||
const bariViewbox = `${BARI_BOUNDS.west},${BARI_BOUNDS.north},${BARI_BOUNDS.east},${BARI_BOUNDS.south}`
|
const bariViewbox = `${BARI_BOUNDS.west},${BARI_BOUNDS.north},${BARI_BOUNDS.east},${BARI_BOUNDS.south}`
|
||||||
|
|
||||||
|
const isWithinBariBounds = (latitude: number, longitude: number) =>
|
||||||
|
latitude >= BARI_BOUNDS.south &&
|
||||||
|
latitude <= BARI_BOUNDS.north &&
|
||||||
|
longitude >= BARI_BOUNDS.west &&
|
||||||
|
longitude <= BARI_BOUNDS.east
|
||||||
|
|
||||||
const compactAddress = (displayName: string, address?: NominatimAddress) => {
|
const compactAddress = (displayName: string, address?: NominatimAddress) => {
|
||||||
if (!address) {
|
if (!address) {
|
||||||
return displayName
|
return displayName
|
||||||
@@ -75,7 +91,9 @@ export const searchAddresses = async (query: string, limit = 5) => {
|
|||||||
limit: String(limit)
|
limit: String(limit)
|
||||||
})
|
})
|
||||||
|
|
||||||
const results = await $fetch<NominatimResult[]>(`${NOMINATIM_BASE_URL}/search?${params.toString()}`)
|
const results = await $fetch<NominatimResult[]>(`${NOMINATIM_BASE_URL}/search?${params.toString()}`, {
|
||||||
|
headers: NOMINATIM_HEADERS
|
||||||
|
})
|
||||||
|
|
||||||
return results
|
return results
|
||||||
.map(toSuggestion)
|
.map(toSuggestion)
|
||||||
@@ -93,7 +111,9 @@ export const reverseGeocode = async (latitude: number, longitude: number) => {
|
|||||||
'accept-language': 'it'
|
'accept-language': 'it'
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await $fetch<NominatimResult | null>(`${NOMINATIM_BASE_URL}/reverse?${params.toString()}`)
|
const result = await $fetch<NominatimResult | null>(`${NOMINATIM_BASE_URL}/reverse?${params.toString()}`, {
|
||||||
|
headers: NOMINATIM_HEADERS
|
||||||
|
})
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return ''
|
return ''
|
||||||
Reference in New Issue
Block a user