Files
service-finder/frontend/admin/components/map/ServiceMap.vue
2026-03-23 21:43:40 +00:00

189 lines
4.2 KiB
Vue

<template>
<div class="service-map-container">
<div class="scope-indicator">
<span class="badge">Current Scope: {{ scopeLabel }}</span>
</div>
<div class="map-wrapper">
<l-map
ref="map"
:zoom="zoom"
:center="center"
@ready="onMapReady"
style="height: 600px; width: 100%;"
>
<l-tile-layer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
/>
<l-marker
v-for="service in services"
:key="service.id"
:lat-lng="[service.lat, service.lng]"
@click="openPopup(service)"
>
<l-icon
:icon-url="getMarkerIcon(service.status)"
:icon-size="[32, 32]"
:icon-anchor="[16, 32]"
/>
<l-popup v-if="selectedService?.id === service.id">
<div class="popup-content">
<h3>{{ service.name }}</h3>
<p><strong>Status:</strong> <span :class="service.status">{{ service.status }}</span></p>
<p><strong>Address:</strong> {{ service.address }}</p>
<p><strong>Distance:</strong> {{ service.distance }} km</p>
<button @click="approveService(service)" class="btn-approve">Approve</button>
</div>
</l-popup>
</l-marker>
</l-map>
</div>
<div class="legend">
<div class="legend-item">
<img src="/marker-pending.png" alt="Pending" class="legend-icon" />
<span>Pending</span>
</div>
<div class="legend-item">
<img src="/marker-approved.png" alt="Approved" class="legend-icon" />
<span>Approved</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { LMap, LTileLayer, LMarker, LPopup, LIcon } from '@vue-leaflet/vue-leaflet'
import 'leaflet/dist/leaflet.css'
import type { Service } from '~/composables/useServiceMap'
const props = defineProps<{
services?: Service[]
scopeLabel?: string
}>()
const map = ref<any>(null)
const zoom = ref(11)
const center = ref<[number, number]>([47.6333, 19.1333]) // Budapest area
const selectedService = ref<Service | null>(null)
const services = ref<Service[]>(props.services || [])
const getMarkerIcon = (status: string) => {
return status === 'approved' ? '/marker-approved.png' : '/marker-pending.png'
}
const openPopup = (service: Service) => {
selectedService.value = service
}
const approveService = (service: Service) => {
console.log('Approving service:', service)
// TODO: Implement API call
service.status = 'approved'
selectedService.value = null
}
const onMapReady = () => {
console.log('Map is ready')
}
onMounted(() => {
// If no services provided, use mock data
if (services.value.length === 0) {
// Mock data will be loaded via composable
}
})
</script>
<style scoped>
.service-map-container {
position: relative;
width: 100%;
height: 100%;
}
.scope-indicator {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
}
.badge {
background-color: #4a90e2;
color: white;
padding: 8px 12px;
border-radius: 20px;
font-weight: bold;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.map-wrapper {
border-radius: 8px;
overflow: hidden;
border: 1px solid #ddd;
}
.legend {
position: absolute;
bottom: 20px;
left: 20px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
z-index: 1000;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.legend-icon {
width: 20px;
height: 20px;
margin-right: 8px;
}
.popup-content {
min-width: 200px;
}
.popup-content h3 {
margin-top: 0;
color: #333;
}
.popup-content p {
margin: 5px 0;
}
.btn-approve {
background-color: #28a745;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
margin-top: 10px;
width: 100%;
}
.btn-approve:hover {
background-color: #218838;
}
.pending {
color: #ffc107;
font-weight: bold;
}
.approved {
color: #28a745;
font-weight: bold;
}
</style>