Skip to content

Commit

Permalink
react/kiezradar: add kiezradars on projects overview
Browse files Browse the repository at this point in the history
  • Loading branch information
sevfurneaux committed Feb 19, 2025
1 parent b22e763 commit 5460fac
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 4.2.16 on 2025-02-17 16:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("meinberlin_kiezradar", "0003_searchprofile_plans_only"),
]

operations = [
migrations.RemoveField(
model_name="searchprofile",
name="kiezradar",
),
migrations.AddField(
model_name="searchprofile",
name="kiezradars",
field=models.ManyToManyField(
blank=True,
related_name="search_profiles",
to="meinberlin_kiezradar.kiezradar",
),
),
]
12 changes: 5 additions & 7 deletions meinberlin/apps/kiezradar/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,18 @@ class SearchProfile(UserGeneratedContentModel):
disabled = models.BooleanField(default=False)
notification = models.BooleanField(default=False)
plans_only = models.BooleanField(default=False)
kiezradar = models.OneToOneField(
KiezRadar,
models.SET_NULL,
related_name="search_profile",
blank=True,
null=True,
)
query = models.ForeignKey(
KiezradarQuery,
models.SET_NULL,
related_name="search_profiles",
blank=True,
null=True,
)
kiezradars = models.ManyToManyField(
KiezRadar,
related_name="search_profiles",
blank=True,
)
status = models.ManyToManyField(
ProjectStatus,
related_name="search_profiles",
Expand Down
20 changes: 14 additions & 6 deletions meinberlin/apps/kiezradar/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def validate(self, data):
class SearchProfileSerializer(serializers.ModelSerializer):
"""Serializer for the SearchProfile model."""

kiezradar = (
kiezradars = (
serializers.PrimaryKeyRelatedField(
queryset=KiezRadar.objects.all(), required=False
),
Expand Down Expand Up @@ -82,16 +82,18 @@ class Meta:
"districts",
"project_types",
"topics",
"kiezradar",
"kiezradars",
]

read_only_fields = ["id", "creator", "number"]

def validate_kiezradar(self, instance):
def validate_kiezradars(self, objs):
user = self.context["request"].user
if not user.has_perm("meinberlin_kiezradar.change_kiezradar", instance):
raise serializers.ValidationError("Permission denied")
return instance

for obj in objs:
if not user.has_perm("meinberlin_kiezradar.change_kiezradar", obj):
raise serializers.ValidationError("Permission denied")
return objs

def create(self, validated_data):
# Pop one-to-many fields from validated_data
Expand Down Expand Up @@ -120,6 +122,12 @@ def update(self, instance, validated_data):

def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.kiezradars:
representation["kiezradars"] = KiezRadarSerializer(
instance.kiezradars, many=True
).data
else:
representation["kiezradars"] = None
representation["organisations"] = [
{"id": organisation.id, "name": organisation.name}
for organisation in instance.organisations.all()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
data-use_vector_map="{{ use_vector_map }}"
data-baseurl="{{ baseurl }}"
data-bounds="{{ bounds }}"
data-kiezradars="{{ kiezradars }}"
data-search-profile="{{ search_profile|default:"" }}"
data-search-profiles-url="{{ search_profiles_url }}"
data-search-profiles-count="{{ search_profiles_count }}"
Expand Down
14 changes: 14 additions & 0 deletions meinberlin/apps/plans/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
from meinberlin.apps.contrib.enums import TopicEnum
from meinberlin.apps.contrib.views import CanonicalURLDetailView
from meinberlin.apps.dashboard.mixins import DashboardProjectListGroupMixin
from meinberlin.apps.kiezradar.models import KiezRadar
from meinberlin.apps.kiezradar.models import ProjectStatus
from meinberlin.apps.kiezradar.models import ProjectType
from meinberlin.apps.kiezradar.models import SearchProfile
from meinberlin.apps.kiezradar.serializers import KiezRadarSerializer
from meinberlin.apps.kiezradar.serializers import SearchProfileSerializer
from meinberlin.apps.maps.models import MapPreset
from meinberlin.apps.organisations.models import Organisation
Expand Down Expand Up @@ -133,6 +135,17 @@ def get_project_status(self):
]
return json.dumps(statuses)

def get_kiezradars(self):
if not self.request.user.is_authenticated:
return json.dumps([])

kiezradars = KiezRadar.objects.filter(creator=self.request.user)
return (
JSONRenderer()
.render(KiezRadarSerializer(kiezradars, many=True).data)
.decode("utf-8")
)

def get_search_profile(self):
if (
self.request.GET.get("search-profile", None)
Expand Down Expand Up @@ -174,6 +187,7 @@ def get_context_data(self, **kwargs):
if hasattr(settings, "A4_OPENMAPTILES_TOKEN"):
omt_token = settings.A4_OPENMAPTILES_TOKEN

context["kiezradars"] = self.get_kiezradars()
context["search_profile"] = self.get_search_profile()
context["districts"] = self.get_districts()
context["organisations"] = self.get_organisations()
Expand Down
31 changes: 31 additions & 0 deletions meinberlin/react/contrib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,34 @@ export const arraysEqual = (a, b) => {
}
return true
}

/*
* converts an object to URLSearchParams
*
* This function takes an object and converts it into a URLSearchParams
* instance, which can be used for query string formatting in URLs.
* It handles arrays by appending each value to the parameter, and ignores
* any null or undefined values.
*
* Example:
* For the input:
* { search: 'apple', category: 'fruit', tags: ['red', 'green'], page: null }
* The output will be:
* "search=apple&category=fruit&tags=red&tags=green"
*
* @param {Object} params - The object to convert into URLSearchParams
* @returns {URLSearchParams} - The resulting URLSearchParams instance
*/
export const toSearchParams = (params) => {
return Object.entries(params).reduce((acc, [key, value]) => {
if (value == null || value === '') return acc

if (Array.isArray(value)) {
value.forEach(val => (val != null && val !== '') && acc.append(key, val))
} else {
acc.set(key, value)
}

return acc
}, new URLSearchParams())
}
2 changes: 1 addition & 1 deletion meinberlin/react/kiezradar/KiezradarList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default function KiezradarList ({
</header>
<footer className="kiezradar-list__footer">
<a
href={planListUrl + '?kiezradar=' + kiezradar.id}
href={planListUrl + '?kiezradars=' + kiezradar.name}
className="button button--light kiezradar-list__view-projects"
>
{viewProjectsText}
Expand Down
4 changes: 3 additions & 1 deletion meinberlin/react/kiezradar/SearchProfile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,14 @@ export default function SearchProfile ({ apiUrl, planListUrl, profile: profile_,
profile.topics,
profile.project_types,
profile.status.map((status) => ({ name: statusNames[status.name] })),
profile.organisations
profile.organisations,
profile.kiezradars
]
.map((filter) => filter.map(({ name }) => name))

const selection = [
[profile.query_text],
[profile.kiezradar ? profile.kiezradar.name : null],
...filters,
[profile.plans_only ? plansText : null]
]
Expand Down
23 changes: 17 additions & 6 deletions meinberlin/react/kiezradar/use-create-search-profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function useCreateSearchProfile ({
topicChoices,
participationChoices,
projectStatus,
kiezradars,
searchProfilesCount,
onSearchProfileCreate
}) {
Expand All @@ -42,15 +43,17 @@ export function useCreateSearchProfile ({
organisations,
topicChoices,
participationChoices,
projectStatus
projectStatus,
kiezradars
})

const {
districtIds,
organisationIds,
topicIds,
participationIds,
projectStatusIds
projectStatusIds,
kiezradarIds
} = getFilteredIds(results)

const params = {
Expand All @@ -59,6 +62,7 @@ export function useCreateSearchProfile ({
topics: topicIds,
project_types: participationIds,
status: projectStatusIds,
kiezradars: kiezradarIds,
plans_only: appliedFilters.plansOnly,
notification: true
}
Expand Down Expand Up @@ -88,7 +92,8 @@ function getFilteredResults ({
organisations,
topicChoices,
participationChoices,
projectStatus
projectStatus,
kiezradars
}) {
return {
filteredDistricts: districts.filter(district =>
Expand All @@ -105,6 +110,9 @@ function getFilteredResults ({
),
filteredProjectStatus: projectStatus.filter(status =>
appliedFilters.projectState.includes(STATUS_MAPPING[status.name])
),
filteredKiezradars: kiezradars.filter(kiezradar =>
appliedFilters.kiezradars.includes(kiezradar.name)
)
}
}
Expand All @@ -115,22 +123,25 @@ function getFilteredIds (results) {
results.filteredOrganisations,
results.filteredTopics,
results.filteredParticipationChoices,
results.filteredProjectStatus
results.filteredProjectStatus,
results.filteredKiezradars
]

const [
districtIds,
organisationIds,
topicIds,
participationIds,
projectStatusIds
projectStatusIds,
kiezradarIds
] = filters.map(items => items.map(item => item.id))

return {
districtIds,
organisationIds,
topicIds,
participationIds,
projectStatusIds
projectStatusIds,
kiezradarIds
}
}
5 changes: 3 additions & 2 deletions meinberlin/react/plans/SaveSearchProfile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const savingText = django.gettext('Saving')
export default function SaveSearchProfile ({
isAuthenticated,
searchProfile,
searchProfilesCount,
...props
}) {
if (!isAuthenticated) {
Expand Down Expand Up @@ -44,7 +43,7 @@ export default function SaveSearchProfile ({

return (
<div className="save-search-profile">
<CreateSearchProfileButton {...props} searchProfilesCount={searchProfilesCount} />
<CreateSearchProfileButton {...props} />
</div>
)
}
Expand All @@ -55,6 +54,7 @@ function CreateSearchProfileButton ({
topicChoices,
participationChoices,
projectStatus,
kiezradars,
searchProfilesApiUrl,
appliedFilters,
searchProfilesCount,
Expand All @@ -68,6 +68,7 @@ function CreateSearchProfileButton ({
topicChoices,
participationChoices,
projectStatus,
kiezradars,
searchProfilesCount,
onSearchProfileCreate
})
Expand Down
2 changes: 2 additions & 0 deletions meinberlin/react/plans/react_plans_map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function init () {
const attribution = el.getAttribute('data-attribution')
const baseUrl = el.getAttribute('data-baseurl')
const bounds = JSON.parse(el.getAttribute('data-bounds'))
const kiezradars = el.getAttribute('data-kiezradars') && JSON.parse(el.getAttribute('data-kiezradars'))
const searchProfile = el.getAttribute('data-search-profile') && JSON.parse(el.getAttribute('data-search-profile'))
const selectedDistrict = el.getAttribute('data-selected-district')
const selectedTopic = el.getAttribute('data-selected-topic')
Expand Down Expand Up @@ -46,6 +47,7 @@ function init () {
districts={districts}
topicChoices={topicChoices}
participationChoices={participationChoices}
kiezradars={kiezradars}
searchProfile={searchProfile}
searchProfilesApiUrl={searchProfilesApiUrl}
searchProfilesCount={searchProfilesCount}
Expand Down
Loading

0 comments on commit 5460fac

Please sign in to comment.