From 28928a40cabb46e22b5ce71c3aa83ebb122960c5 Mon Sep 17 00:00:00 2001
From: Fabio Manganiello <fabio@manganiello.tech>
Date: Sat, 22 Feb 2025 17:23:38 +0100
Subject: [PATCH] Style improvements.

- Improved POI style (a bit bigger and with border)

- Improved styling of the close buttons.

- Added palette with accent/background colors.
---
 frontend/src/assets/base.css          | 43 ++++++++++++++++++++++
 frontend/src/components/Map.vue       | 51 +++++++++++++++++----------
 frontend/src/components/PointInfo.vue | 28 ++++++++++++++-
 3 files changed, 102 insertions(+), 20 deletions(-)

diff --git a/frontend/src/assets/base.css b/frontend/src/assets/base.css
index 8816868..86ae22f 100644
--- a/frontend/src/assets/base.css
+++ b/frontend/src/assets/base.css
@@ -19,6 +19,47 @@
   --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
   --vt-c-text-dark-1: var(--vt-c-white);
   --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
+
+  --vt-c-blue-fg-light: #3498db;
+  --vt-c-blue-fg-dark: #2980b9;
+  --vt-c-blue-bg-light: #5bc0de;
+  --vt-c-blue-bg-dark: #31b0d5;
+  --vt-c-green-fg-light: #2ecc71;
+  --vt-c-green-fg-dark: #27ae60;
+  --vt-c-green-bg-light: #5cb85c;
+  --vt-c-green-bg-dark: #449d44;
+  --vt-c-red-fg-light: #e74c3c;
+  --vt-c-red-fg-dark: #c0392b;
+  --vt-c-red-bg-light: #d9534f;
+  --vt-c-red-bg-dark: #c9302c;
+  --vt-c-yellow-fg-light: #f1c40f;
+  --vt-c-yellow-fg-dark: #f39c12;
+  --vt-c-yellow-bg-light: #f0ad4e;
+  --vt-c-yellow-bg-dark: #ec971f;
+  --vt-c-purple-fg-light: #9b59b6;
+  --vt-c-purple-fg-dark: #8e44ad;
+  --vt-c-purple-bg-light: #9d71d9;
+  --vt-c-purple-bg-dark: #8e44ad;
+  --vt-c-cyan-fg-light: #1abc9c;
+  --vt-c-cyan-fg-dark: #16a085;
+  --vt-c-cyan-bg-light: #5bc0de;
+  --vt-c-cyan-bg-dark: #31b0d5;
+  --vt-c-orange-fg-light: #e67e22;
+  --vt-c-orange-fg-dark: #d35400;
+  --vt-c-orange-bg-light: #f0ad4e;
+  --vt-c-orange-bg-dark: #ec971f;
+  --vt-c-pink-fg-light: #e91e63;
+  --vt-c-pink-fg-dark: #d81b60;
+  --vt-c-pink-bg-light: #d9534f;
+  --vt-c-pink-bg-dark: #c9302c;
+  --vt-c-teal-fg-light: #1abc9c;
+  --vt-c-teal-fg-dark: #16a085;
+  --vt-c-teal-bg-light: #5bc0de;
+  --vt-c-teal-bg-dark: #31b0d5;
+  --vt-c-lime-fg-light: #2ecc71;
+  --vt-c-lime-fg-dark: #27ae60;
+  --vt-c-lime-bg-light: #5cb85c;
+  --vt-c-lime-bg-dark: #449d44;
 }
 
 /* semantic color variables for this project */
@@ -32,6 +73,7 @@
 
   --color-heading: var(--vt-c-text-light-1);
   --color-text: var(--vt-c-text-light-1);
+  --color-accent: var(--vt-c-blue-fg-light);
 
   --section-gap: 160px;
 }
@@ -47,6 +89,7 @@
 
     --color-heading: var(--vt-c-text-dark-1);
     --color-text: var(--vt-c-text-dark-2);
+    --color-accent: var(--vt-c-blue-fg-dark);
   }
 }
 
diff --git a/frontend/src/components/Map.vue b/frontend/src/components/Map.vue
index 2370f6f..0e206ee 100644
--- a/frontend/src/components/Map.vue
+++ b/frontend/src/components/Map.vue
@@ -13,6 +13,7 @@
 import Feature from 'ol/Feature';
 import GPSPoint from '../models/GPSPoint';
 import Map from 'ol/Map';
+import LineString from 'ol/geom/LineString';
 import OSM from 'ol/source/OSM';
 import Overlay from 'ol/Overlay';
 import Point from 'ol/geom/Point';
@@ -21,9 +22,9 @@ import VectorLayer from 'ol/layer/Vector';
 import VectorSource from 'ol/source/Vector';
 import View from 'ol/View';
 import TileLayer from 'ol/layer/Tile';
-import { Circle, Fill, Style } from 'ol/style';
+import { Circle, Fill, Style, Stroke } from 'ol/style';
 import { useGeographic } from 'ol/proj';
-import { type Nullable } from '../models/Types';
+import type { Nullable } from '../models/Types';
 
 // @ts-ignore
 const baseURL = __API_PATH__
@@ -40,6 +41,7 @@ export default {
       loading: false,
       map: null as Nullable<Map>,
       popup: null as Nullable<Overlay>,
+      routeFeatures: [] as Feature[],
       selectedPoint: null as Nullable<GPSPoint>,
     }
   },
@@ -60,32 +62,41 @@ export default {
       }
     },
 
+    osmLayer() {
+      return new TileLayer({
+        source: new OSM(),
+      })
+    },
+
+    pointsLayer(points: Point[]) {
+      const pointFeatures = points.map((point: Point) => new Feature(point))
+      return new VectorLayer({
+        source: new VectorSource({
+          features: pointFeatures,
+        }),
+        style: new Style({
+          image: new Circle({
+            radius: 6,
+            fill: new Fill({ color: 'red' }),
+            stroke: new Stroke({ color: 'black', width: 1 }),
+          }),
+          zIndex: Infinity,  // Ensure that points are always displayed above other layers
+        }),
+      })
+    },
+
     createMap(gpsPoints: GPSPoint[]) {
       const points = gpsPoints.map((gps: GPSPoint) => {
         const point = new Point([gps.longitude, gps.latitude])
         return point
-      })
+      });
 
-      const pointFeatures = points.map((point: Point) => new Feature(point))
       const view = new View(this.getCenterAndZoom())
       const map = new Map({
         target: 'map',
         layers: [
-          new TileLayer({
-            source: new OSM(),
-          }),
-
-          new VectorLayer({
-            source: new VectorSource({
-              features: pointFeatures,
-            }),
-            style: new Style({
-              image: new Circle({
-                radius: 5,
-                fill: new Fill({ color: 'red' }),
-              }),
-            }),
-          }),
+          this.osmLayer(),
+          this.pointsLayer(points),
         ],
         view: view
       })
@@ -130,6 +141,8 @@ export default {
             this.selectedPoint = point
             // @ts-expect-error
             this.$refs.popup.setPosition(event.coordinate)
+            // Center the map on the selected point
+            map.getView().setCenter(event.coordinate)
           }
         } else {
           this.selectedPoint = null
diff --git a/frontend/src/components/PointInfo.vue b/frontend/src/components/PointInfo.vue
index bc30196..1c4e604 100644
--- a/frontend/src/components/PointInfo.vue
+++ b/frontend/src/components/PointInfo.vue
@@ -1,7 +1,9 @@
 <template>
   <div class="popup" :class="{ hidden: !point }" ref="popup">
     <div class="popup-content" v-if="point">
-      <button @click="$emit('close')">Close</button>
+      <div class="header">
+        <button @click="$emit('close')" title="Close">✕</button>
+      </div>
       <div class="point-info">
         <h2 class="address" v-if="point.address">{{ point.address }}</h2>
         <h2 class="latlng" v-else>{{ point.latitude }}, {{ point.longitude }}</h2>
@@ -90,6 +92,30 @@ export default {
   border-radius: 1em;
   box-shadow: 2px 2px 2px 2px var(--color-border);
 
+  .popup-content {
+    display: flex;
+    flex-direction: column;
+    gap: 1.5em;
+  }
+
+  .header {
+    position: absolute;
+    top: 0.5em;
+    right: 0.5em;
+
+    button {
+      background: none;
+      border: none;
+      color: var(--color-heading);
+      font-size: 1.2em;
+      cursor: pointer;
+
+      &:hover {
+        color: var(--color-accent);
+      }
+    }
+  }
+
   &.hidden {
     padding: 0;
     border-radius: 0;