From cff0585bd45462b4a4bc2e9cb796c20e892d17d3 Mon Sep 17 00:00:00 2001
From: Felipe Contreras
Date: Tue, 22 Nov 2022 21:26:27 -0300
Subject: [PATCH] =?UTF-8?q?A=C3=B1=C3=A1dir=20y=20Editar=20completos?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.rs | 5 +
migrations/20221121211314_swap_lat_long.sql | 1 +
src/routes.rs | 7 +-
static/index.html | 10 +-
ts-client/Makefile | 3 +
ts-client/client.ts | 141 +++++++++++++++++---
6 files changed, 146 insertions(+), 21 deletions(-)
create mode 100644 build.rs
create mode 100644 migrations/20221121211314_swap_lat_long.sql
create mode 100644 ts-client/Makefile
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..7609593
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,5 @@
+// generated by `sqlx migrate build-script`
+fn main() {
+ // trigger recompilation when a new migration is added
+ println!("cargo:rerun-if-changed=migrations");
+}
\ No newline at end of file
diff --git a/migrations/20221121211314_swap_lat_long.sql b/migrations/20221121211314_swap_lat_long.sql
new file mode 100644
index 0000000..ca86355
--- /dev/null
+++ b/migrations/20221121211314_swap_lat_long.sql
@@ -0,0 +1 @@
+UPDATE places SET longitude=latitude, latitude=longitude;
diff --git a/src/routes.rs b/src/routes.rs
index d278712..679540d 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -43,7 +43,7 @@ enum UpsertResponse {
NotFound(NotFound>),
}
-#[post("/places", format = "json", data = "")]
+#[put("/places", format = "json", data = "")]
async fn upsert_place(db: Connection, place: Json) -> Result {
if place.id.is_some() {
update_place(db, place).await
@@ -79,14 +79,15 @@ async fn insert_place(mut db: Connection, mut place: Json) -> Result<
async fn update_place(mut db: Connection, place: Json) -> Result {
let result = ::sqlx::query!(
- "UPDATE places SET (name, address, open_hours, icon, description, longitude, latitude) = (?, ?, ?, ?, ?, ?, ?)",
+ "UPDATE places SET (name, address, open_hours, icon, description, longitude, latitude) = (?, ?, ?, ?, ?, ?, ?) WHERE id = ?",
place.name,
place.address,
place.open_hours,
place.icon,
place.description,
place.longitude,
- place.latitude
+ place.latitude,
+ place.id,
)
.execute(&mut *db)
.await?;
diff --git a/static/index.html b/static/index.html
index d038ddf..2bf701f 100644
--- a/static/index.html
+++ b/static/index.html
@@ -52,13 +52,15 @@
}
#modal-form {
margin: 15vh auto;
- background-color: rgb(245, 245, 245);
+ color: #333;
+ background-color: white;
padding: 2em;
border: 1px solid rgb(25, 25, 25);
width: 80vw;
+ border-radius: 12px;
}
#close {
- color: rgb(80, 80, 80);
+ color: #333;
float: right;
font-size: 28px;
font-weight: bold;
@@ -76,6 +78,7 @@
display: table-row;
}
label {
+ font-weight: bold;
display: table-cell;
vertical-align: top;
padding-right: 2em;
@@ -137,6 +140,9 @@
+
+
+
diff --git a/ts-client/Makefile b/ts-client/Makefile
new file mode 100644
index 0000000..e33a104
--- /dev/null
+++ b/ts-client/Makefile
@@ -0,0 +1,3 @@
+all: client.ts
+ tsc
+ sed -i '1d' build/client.js
diff --git a/ts-client/client.ts b/ts-client/client.ts
index ca4f8fd..a118efc 100644
--- a/ts-client/client.ts
+++ b/ts-client/client.ts
@@ -1,5 +1,5 @@
import * as L from 'leaflet-contextmenu';
-import { Feature, FeatureCollection, GeoJSON } from 'geojson';
+import { Feature, FeatureCollection } from 'geojson';
interface PlaceModel {
id: number | null;
@@ -28,12 +28,12 @@ function toFeature(place: PlaceModel): Feature {
},
"geometry": {
"type": "Point",
- "coordinates": [place.latitude, place.longitude]
+ "coordinates": [place.longitude, place.latitude]
}
}
}
-function toLeafletPlaces(backendPlaces: Array): GeoJSON {
+function toLeafletPlaces(backendPlaces: Array): L.GeoJSON {
let result: FeatureCollection = {
type: "FeatureCollection",
features: new Array(),
@@ -45,9 +45,15 @@ function toLeafletPlaces(backendPlaces: Array): GeoJSON {
}
let placesLayer: L.GeoJSON;
-let places = new Map();
+let places = new Map();
-async function createPlace(): Promise {
+function toStr(latlng: L.LatLngLiteral) {
+ return latlng.lng + "|" + latlng.lat;
+}
+
+function getPlaceFromForm(): PlaceModel {
+ const idStr = (document.getElementById("id") as HTMLInputElement).value;
+ const id = idStr == "" ? null : parseInt(idStr);
const name =
(document.getElementById("name") as HTMLInputElement).value;
const address =
@@ -63,8 +69,8 @@ async function createPlace(): Promise {
const longitude = parseFloat(
(document.getElementById("long") as HTMLSelectElement).value);
- const newPlace: PlaceModel = {
- id: null,
+ return {
+ id: id,
name: name,
address: address,
open_hours: open_hours,
@@ -73,6 +79,37 @@ async function createPlace(): Promise {
latitude: latitude,
longitude: longitude,
};
+}
+
+function clearForm(): void {
+ /* Get the form elements*/
+ const idInput = (document.getElementById("id") as HTMLInputElement);
+ const latInput = (document.getElementById("lat") as HTMLInputElement);
+ const longInput = (document.getElementById("long") as HTMLInputElement);
+ const nameInput = (document.getElementById("name") as HTMLInputElement);
+ const addressInput = (document.getElementById("address") as HTMLInputElement);
+ const openHoursArea = (document.getElementById("open_hours") as HTMLTextAreaElement);
+ const descriptionArea = (document.getElementById("description") as HTMLTextAreaElement);
+
+ /* Clear them */
+ idInput.value = "";
+ latInput.value = "";
+ longInput.value = "";
+ nameInput.value = "";
+ addressInput.value = "";
+ openHoursArea.value = "";
+ descriptionArea.value = "";
+
+ /* Clear the callback */
+ document.getElementById("button").onclick = null;
+
+ /* Now you see it, now you don't*/
+ document.getElementById("modal").style.display = "none";
+}
+
+
+async function createPlace(): Promise {
+ const newPlace = getPlaceFromForm();
await fetch('places', {
method: 'PUT',
@@ -84,16 +121,46 @@ async function createPlace(): Promise {
.then((response) => response.json())
.then((place: PlaceModel) => {
places.set(
- { lat: place.latitude, lng: place.longitude },
+ toStr({ lat: place.latitude, lng: place.longitude }),
place
);
placesLayer.addData(toFeature(place));
+ clearForm();
+ alert("Lugar añadido exitosamente");
})
.catch((error) => alert(error));
}
+async function editPlace(): Promise {
+ const newPlace = getPlaceFromForm();
+
+ await fetch('places', {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(newPlace),
+ })
+ .then((response) => response.json())
+ .then((place: PlaceModel) => {
+ places.set(
+ toStr({ lat: place.latitude, lng: place.longitude }),
+ place
+ );
+ clearForm();
+ alert("Lugar guardado exitosamente. Para ver los cambios, recargue la página.");
+ })
+ .catch((error) => alert(error));
+}
+
+const enum Operation {
+ Create = "create",
+ Edit = "edit",
+}
+
interface MapEvent {
- latlng: L.latlng;
+ latlng: L.LatLng;
+ relatedTarget: L.Marker | L.Path | undefined;
}
async function setupMap(): Promise {
@@ -111,21 +178,62 @@ async function setupMap(): Promise {
}
}
- function openForm(title: string, lat: number, long: number): void {
+ function openForm(op: Operation, lat: number, long: number): void {
+ /* Fill the form for us */
const h1 = modal.getElementsByTagName("h1")[0];
- h1.innerText = title;
+ if (op == Operation.Create) {
+ clearForm()
+ h1.innerText = "Añadir lugar nuevo";
+ } else {
+ h1.innerText = "Editar lugar";
+ }
const latInput = (document.getElementById("lat") as HTMLInputElement);
- latInput.value = lat.toString();
const longInput = (document.getElementById("long") as HTMLInputElement);
+ latInput.value = lat.toString();
longInput.value = long.toString();
+ if (op == Operation.Edit) {
+ const place = places.get(toStr({ lat: lat, lng: long }));
+ console.log(toStr({ lat: lat, lng: long }));
+ /*Get the form elements*/
+ const idInput = (document.getElementById("id") as HTMLInputElement);
+ const nameInput = (document.getElementById("name") as HTMLInputElement);
+ const addressInput = (document.getElementById("address") as HTMLInputElement);
+ const openHoursArea = (document.getElementById("open_hours") as HTMLTextAreaElement);
+ const iconSelect = (document.getElementById("icon") as HTMLSelectElement);
+ const descriptionArea = (document.getElementById("description") as HTMLTextAreaElement);
+
+ /* And set them */
+ idInput.value = place.id.toString();
+ nameInput.value = place.name;
+ addressInput.value = place.address;
+ openHoursArea.value = place.open_hours;
+ iconSelect.value = place.icon;
+ descriptionArea.value = place.description;
+ }
+
+ /* Plug callbacks */
+ if (op == Operation.Create) {
+ document.getElementById("button").onclick = createPlace;
+ } else {
+ document.getElementById("button").onclick = editPlace;
+ }
+
+ /* Make it appear */
modal.style.display = "block";
}
function openCreateForm(e: MapEvent) {
const lat = e.latlng.lat;
const long = e.latlng.lng;
- openForm("Añadir lugar nuevo", lat, long);
+ openForm(Operation.Create, lat, long);
+ }
+
+ function openEditForm(e: MapEvent) {
+ const marker = (e.relatedTarget as L.Marker);
+ const lat = marker.getLatLng().lat;
+ const long = marker.getLatLng().lng;
+ openForm(Operation.Edit, lat, long);
}
/* Get places from backend */
@@ -133,13 +241,13 @@ async function setupMap(): Promise {
const leafletPlaces = toLeafletPlaces(backendPlaces);
for (const place of backendPlaces) {
places.set(
- { lat: place.latitude, lng: place.longitude },
+ toStr({ lat: place.latitude, lng: place.longitude }),
place
);
}
/* Set up the map*/
- const map = new L.Map('map', {
+ const map = new L.map('map', {
contextmenu: true,
contextmenuWidth: 140,
contextmenuItems: [
@@ -181,7 +289,8 @@ async function setupMap(): Promise {
contextmenu: true,
contextmenuInheritItems: false,
contextmenuItems: [{
- text: 'Marker item'
+ text: 'Editar',
+ callback: openEditForm,
}]
});
}