feat: add Map.tags and allow to edit from client

cf #2283
This commit is contained in:
Yohan Boniface 2025-02-26 19:32:16 +01:00
parent e2f154f62e
commit 39f38a9cdf
9 changed files with 90 additions and 5 deletions

View file

@ -4,7 +4,6 @@ from django.contrib.auth import get_user_model
from django.contrib.gis.geos import Point
from django.forms.utils import ErrorList
from django.template.defaultfilters import slugify
from django.utils.translation import gettext_lazy as _
from .models import DataLayer, Map, Team
@ -92,7 +91,7 @@ class MapSettingsForm(forms.ModelForm):
return self.cleaned_data["center"]
class Meta:
fields = ("settings", "name", "center", "slug")
fields = ("settings", "name", "center", "slug", "tags")
model = Map

View file

@ -0,0 +1,23 @@
# Generated by Django 5.1.6 on 2025-02-26 16:18
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("umap", "0026_datalayer_modified_at_datalayer_share_status"),
]
operations = [
migrations.AddField(
model_name="map",
name="tags",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=200),
blank=True,
default=list,
size=None,
),
),
]

View file

@ -4,6 +4,7 @@ import uuid
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.gis.db import models
from django.contrib.postgres.fields import ArrayField
from django.core.files.base import File
from django.core.files.storage import storages
from django.core.signing import Signer
@ -236,6 +237,7 @@ class Map(NamedModel):
settings = models.JSONField(
blank=True, null=True, verbose_name=_("settings"), default=dict
)
tags = ArrayField(models.CharField(max_length=200), blank=True, default=list)
objects = models.Manager()
public = PublicManager()
@ -420,7 +422,8 @@ class Map(NamedModel):
return {
"iconUrl": {
"default": "%sumap/img/marker.svg" % settings.STATIC_URL,
}
},
"tags": {"choices": settings.UMAP_TAGS},
}

View file

@ -6,6 +6,7 @@ from email.utils import parseaddr
import environ
from django.conf.locale import LANG_INFO
from django.utils.translation import gettext_lazy as _
import umap as project_module
@ -290,6 +291,19 @@ UMAP_HOME_FEED = "latest"
UMAP_IMPORTERS = {}
UMAP_HOST_INFOS = {}
UMAP_LABEL_KEYS = ["name", "title"]
UMAP_TAGS = (
("art", _("Art and Culture")),
("bike", _("Bike")),
("environment", _("Environment")),
("education", _("Education")),
("food", _("Food and Agriculture")),
("history", _("History")),
("public", _("Public sector")),
("sport", _("Sport and Leisure")),
("travel", _("Travel")),
("trekking", _("Trekking")),
("tourism", _("Tourism")),
)
UMAP_READONLY = env("UMAP_READONLY", default=False)
UMAP_GZIP = True

View file

@ -138,6 +138,8 @@ export class MutatingForm extends Form {
} else if (properties.type === Number) {
if (properties.step) properties.handler = 'Range'
else properties.handler = 'IntInput'
} else if (properties.type === Array) {
properties.handler = 'CheckBoxes'
} else if (properties.choices) {
const text_length = properties.choices.reduce(
(acc, [_, label]) => acc + label.length,

View file

@ -324,6 +324,24 @@ Fields.CheckBox = class extends BaseElement {
}
}
Fields.CheckBoxes = class extends BaseElement {
build() {
const initial = this.get() || []
for (const [value, label] of this.properties.choices) {
const tpl = `<label><input type=checkbox value="${value}" name="${this.name}" data-ref=input />${label}</label>`
const [root, { input }] = Utils.loadTemplateWithRefs(tpl)
this.container.appendChild(root)
input.checked = initial.includes(value)
input.addEventListener('change', () => this.sync())
}
super.build()
}
value() {
return Array.from(this.root.querySelectorAll('input:checked')).map((el) => el.value)
}
}
Fields.Select = class extends BaseElement {
getTemplate() {
return `<select name="${this.name}" data-ref=select></select>`
@ -1296,12 +1314,13 @@ Fields.ManageEditors = class extends BaseElement {
placeholder: translate("Type editor's username"),
}
this.autocomplete = new AjaxAutocompleteMultiple(this.container, options)
this._values = this.toHTML()
if (this._values)
this._values = this.toHTML() || []
if (this._values) {
for (let i = 0; i < this._values.length; i++)
this.autocomplete.displaySelected({
item: { value: this._values[i].id, label: this._values[i].name },
})
}
}
value() {

View file

@ -516,6 +516,9 @@ export const SCHEMA = {
helpEntries: ['sync'],
default: false,
},
tags: {
type: Array,
},
team: {
type: Object,
},

View file

@ -755,6 +755,12 @@ export default class Umap {
const form = builder.build()
container.appendChild(form)
const tags = DomUtil.createFieldset(container, translate('Tags'))
const tagsFields = ['properties.tags']
const tagsBuilder = new MutatingForm(this, tagsFields, {
umap: this,
})
tags.appendChild(tagsBuilder.build())
const credits = DomUtil.createFieldset(container, translate('Credits'))
const creditsFields = [
'properties.licence',
@ -1168,6 +1174,7 @@ export default class Umap {
const formData = new FormData()
formData.append('name', this.properties.name)
formData.append('center', JSON.stringify(this.geometry()))
formData.append('tags', this.properties.tags || [])
formData.append('settings', JSON.stringify(geojson))
const uri = this.urls.get('map_save', { map_id: this.id })
const [data, _, error] = await this.server.post(uri, {}, formData)

View file

@ -226,3 +226,18 @@ def test_hover_tooltip_setting_should_be_persistent(live_server, map, page):
- text: always never on hover
""")
expect(page.locator(".umap-field-showLabel input[value=null]")).to_be_checked()
def test_can_edit_map_tags(live_server, map, page):
map.settings["properties"]["tags"] = ["art"]
map.edit_status = Map.ANONYMOUS
map.save()
page.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
page.get_by_role("button", name="Edit map name and caption").click()
page.get_by_text("Tags").click()
expect(page.get_by_label("Art and Culture")).to_be_checked()
page.get_by_label("Bike").check()
with page.expect_response(re.compile("./update/settings/.*")):
page.get_by_role("button", name="Save").click()
saved = Map.objects.get(pk=map.pk)
assert saved.tags == ["art", "bike"]