admin firs step
This commit is contained in:
202
frontend/admin/components/ServiceMapTile.vue
Normal file
202
frontend/admin/components/ServiceMapTile.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<TileWrapper
|
||||
title="Geographical Map"
|
||||
subtitle="Service moderation map"
|
||||
icon="map"
|
||||
:loading="loading"
|
||||
>
|
||||
<div class="service-map-tile">
|
||||
<div class="mini-map">
|
||||
<div class="map-placeholder">
|
||||
<div class="map-grid">
|
||||
<div
|
||||
v-for="point in mapPoints"
|
||||
:key="point.id"
|
||||
class="map-point"
|
||||
:class="point.status"
|
||||
:style="{
|
||||
left: `${point.x}%`,
|
||||
top: `${point.y}%`
|
||||
}"
|
||||
:title="point.name"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-label">Pending in Scope</span>
|
||||
<span class="stat-value">{{ pendingCount }}</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Scope</span>
|
||||
<span class="stat-value scope">{{ scopeLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-actions">
|
||||
<button @click="navigateToMap" class="btn-primary">
|
||||
Open Full Map
|
||||
</button>
|
||||
<button @click="refresh" class="btn-secondary">
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</TileWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import TileWrapper from '~/components/TileWrapper.vue'
|
||||
import { useServiceMap } from '~/composables/useServiceMap'
|
||||
|
||||
const router = useRouter()
|
||||
const { pendingServices, scopeLabel } = useServiceMap()
|
||||
const loading = ref(false)
|
||||
|
||||
const pendingCount = computed(() => pendingServices.value.length)
|
||||
|
||||
// Generate random points for the mini map visualization
|
||||
const mapPoints = computed(() => {
|
||||
return pendingServices.value.slice(0, 8).map((service, index) => ({
|
||||
id: service.id,
|
||||
name: service.name,
|
||||
status: service.status,
|
||||
x: 10 + (index % 4) * 25 + Math.random() * 10,
|
||||
y: 10 + Math.floor(index / 4) * 30 + Math.random() * 10
|
||||
}))
|
||||
})
|
||||
|
||||
const navigateToMap = () => {
|
||||
router.push('/moderation-map')
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
loading.value = true
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.service-map-tile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mini-map {
|
||||
flex: 1;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.map-placeholder {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid #90caf9;
|
||||
}
|
||||
|
||||
.map-grid {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-point {
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.map-point.pending {
|
||||
background-color: #ffc107;
|
||||
}
|
||||
|
||||
.map-point.approved {
|
||||
background-color: #28a745;
|
||||
}
|
||||
|
||||
.tile-stats {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.stat-value.scope {
|
||||
font-size: 1rem;
|
||||
color: #4a90e2;
|
||||
background: #e3f2fd;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tile-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
flex: 2;
|
||||
background-color: #4a90e2;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #3a7bc8;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
flex: 1;
|
||||
background-color: #f8f9fa;
|
||||
color: #495057;
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user