Add optional URL field (#14)

Co-authored-by: Felipe Contreras Salinas <felipe@bstr.cl>
Reviewed-on: #14
This commit is contained in:
Felipe 2023-03-28 02:01:09 -03:00
parent aabebfd1c4
commit 2279026a81
Signed by: Ludwig
GPG key ID: 441A26F83D31FAFF
6 changed files with 44 additions and 15 deletions

2
Cargo.lock generated
View file

@ -654,7 +654,7 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "huellas"
version = "0.1.3"
version = "0.1.4"
dependencies = [
"rocket",
"rocket_db_pools",

View file

@ -0,0 +1 @@
ALTER TABLE places ADD url VARCHAR DEFAULT null;

View file

@ -11,4 +11,5 @@ pub struct Place {
pub description: String,
pub longitude: f64,
pub latitude: f64,
pub url: Option<String>,
}

View file

@ -16,7 +16,7 @@ struct Db(rocket_db_pools::sqlx::SqlitePool);
#[get("/places")]
async fn get_places(mut db: Connection<Db>) -> Result<Json<Vec<Place>>> {
let places = rocket_db_pools::sqlx::query!(
"SELECT id, name, address, open_hours, icon, description," +
"SELECT id, name, address, open_hours, icon, description, url," +
r#"longitude as "longitude: f64", latitude as "latitude: f64" FROM places WHERE active = TRUE"#
)
.fetch(&mut *db)
@ -29,6 +29,7 @@ async fn get_places(mut db: Connection<Db>) -> Result<Json<Vec<Place>>> {
description: p.description,
latitude: p.latitude,
longitude: p.longitude,
url: p.url,
})
.try_collect::<Vec<_>>()
.await?;
@ -59,8 +60,8 @@ struct Id {
async fn insert_place(mut db: Connection<Db>, mut place: Json<Place>) -> Result<UpsertResponse> {
let i = ::sqlx::query_as!(
Id,
"INSERT INTO places (name, address, open_hours, icon, description, longitude, latitude)\
VALUES (?, ?, ?, ?, ?, ?, ?)\
"INSERT INTO places (name, address, open_hours, icon, description, longitude, latitude, url)\
VALUES (?, ?, ?, ?, ?, ?, ?, ?)\
RETURNING id",
place.name,
place.address,
@ -68,7 +69,8 @@ async fn insert_place(mut db: Connection<Db>, mut place: Json<Place>) -> Result<
place.icon,
place.description,
place.longitude,
place.latitude
place.latitude,
place.url
)
.fetch_one(&mut *db)
.await?;
@ -79,7 +81,7 @@ async fn insert_place(mut db: Connection<Db>, mut place: Json<Place>) -> Result<
async fn update_place(mut db: Connection<Db>, place: Json<Place>) -> Result<UpsertResponse> {
let result = ::sqlx::query!(
"UPDATE places SET (name, address, open_hours, icon, description, longitude, latitude) = (?, ?, ?, ?, ?, ?, ?) WHERE id = ?",
"UPDATE places SET (name, address, open_hours, icon, description, longitude, latitude, url) = (?, ?, ?, ?, ?, ?, ?, ?) WHERE id = ?",
place.name,
place.address,
place.open_hours,
@ -87,6 +89,7 @@ async fn update_place(mut db: Connection<Db>, place: Json<Place>) -> Result<Upse
place.description,
place.longitude,
place.latitude,
place.url,
place.id,
)
.execute(&mut *db)
@ -105,7 +108,7 @@ async fn delete_place(mut db: Connection<Db>, id: i64) -> Result<Option<()>> {
.execute(&mut *db)
.await?;
Ok((result.rows_affected() == 1).then(|| ()))
Ok((result.rows_affected() == 1).then_some(()))
}
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {

View file

@ -136,6 +136,10 @@
<option value="shop">Tienda</option>
</select>
</p>
<p>
<label for="url"> URL:</label>
<input type="text" id="url" name="url" size="30">
</p>
<p>
<label for="description"> Descripción:</label>
<textarea id="description" name="description"

View file

@ -10,6 +10,7 @@ interface PlaceModel {
description: string;
latitude: number;
longitude: number;
url: string | null;
}
async function loadPlaces(): Promise<Array<PlaceModel>> {
@ -24,7 +25,8 @@ function toFeature(place: PlaceModel): Feature {
"address": place.address,
"open_hours": place.open_hours,
"icon": place.icon,
"description": place.description
"description": place.description,
"url": place.url,
},
"geometry": {
"type": "Point",
@ -68,6 +70,8 @@ function getPlaceFromForm(): PlaceModel {
(document.getElementById("lat") as HTMLInputElement).value);
const longitude = parseFloat(
(document.getElementById("long") as HTMLSelectElement).value);
const url =
(document.getElementById("url") as HTMLInputElement).value;
return {
id: id,
@ -78,6 +82,7 @@ function getPlaceFromForm(): PlaceModel {
description: description,
latitude: latitude,
longitude: longitude,
url: url,
};
}
@ -88,6 +93,7 @@ function clearForm(): void {
const longInput = (document.getElementById("long") as HTMLInputElement);
const nameInput = (document.getElementById("name") as HTMLInputElement);
const addressInput = (document.getElementById("address") as HTMLInputElement);
const urlInput = (document.getElementById("url") as HTMLInputElement);
const openHoursArea = (document.getElementById("open_hours") as HTMLTextAreaElement);
const descriptionArea = (document.getElementById("description") as HTMLTextAreaElement);
@ -97,6 +103,7 @@ function clearForm(): void {
longInput.value = "";
nameInput.value = "";
addressInput.value = "";
urlInput.value = "";
openHoursArea.value = "";
descriptionArea.value = "";
@ -168,6 +175,14 @@ async function getAddressReverse(lat: number, long: number): Promise<string> {
return `${address.road} ${address.house_number}, ${address.city}`;
}
function toLink(url: string): string {
let content = url;
const m = url.match("https://instagram.com/(.*)");
if (m) {
content = `@${m[1]}`;
}
return `<a href="${url}" target="_blank">${content}</a>`
}
async function setupMap(): Promise<void> {
/* Create/Edit form */
const modal = document.getElementById("modal");
@ -204,6 +219,7 @@ async function setupMap(): Promise<void> {
/*Get the form elements*/
const idInput = (document.getElementById("id") as HTMLInputElement);
const nameInput = (document.getElementById("name") as HTMLInputElement);
const urlInput = (document.getElementById("url") as HTMLInputElement);
const openHoursArea = (document.getElementById("open_hours") as HTMLTextAreaElement);
const iconSelect = (document.getElementById("icon") as HTMLSelectElement);
const descriptionArea = (document.getElementById("description") as HTMLTextAreaElement);
@ -212,6 +228,7 @@ async function setupMap(): Promise<void> {
idInput.value = place.id.toString();
nameInput.value = place.name;
addressInput.value = place.address;
urlInput.value = place.url;
openHoursArea.value = place.open_hours;
iconSelect.value = place.icon;
descriptionArea.value = place.description;
@ -287,19 +304,22 @@ async function setupMap(): Promise<void> {
function onEachFeature(feature: Feature, layer: L.Layer) {
if (feature.properties) {
let popupStr = "<h3>" + feature.properties.name + "</h3>";
let popupStr = `<h3>${feature.properties.name}</h3>`;
popupStr += "<ul>"
if (feature.properties.address)
popupStr += "<li><b>Dirección:</b> " + feature.properties.address + "</li>";
popupStr += `<li><b>Dirección:</b>${feature.properties.address}</li>`;
if (feature.properties.open_hours)
popupStr += "<li><b>Horario:</b> " + feature.properties.open_hours + "</li>";
popupStr += `<li><b>Horario:</b>${feature.properties.open_hours}</li>`;
if (feature.properties.description)
popupStr += "<li>" + feature.properties.description + "</li>";
popupStr += `<li>${feature.properties.description}</li>`;
if (feature.properties.url)
popupStr += `<li>${toLink(feature.properties.url)}</li>`;
const lnglat = (feature.geometry as Point).coordinates;
popupStr += "<a href=\"https://www.google.com/maps/dir//" +
lnglat[1] + "," + lnglat[0] + "/@" + lnglat[1] + "," + lnglat[0] +
",15z\" target=\"_blank\">GMaps</a>"
const lng = lnglat[0];
const lat = lnglat[0];
popupStr += `<a href="https://www.google.com/maps/dir//` +
`${lat},${lng}/@${lat},${lng},15z" target="_blank">GMaps</a>`
popupStr += "</ul>";
layer.bindPopup(popupStr);