Skip to content

Commit

Permalink
react/kiez-search-profile: add search profile alerts in project overview
Browse files Browse the repository at this point in the history
  • Loading branch information
sevfurneaux committed Feb 11, 2025
1 parent 91080e7 commit 0021a76
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 6 deletions.
3 changes: 3 additions & 0 deletions changelog/_8895.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Added

- Added search profile alerts to plan list page.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
data-baseurl="{{ baseurl }}"
data-bounds="{{ bounds }}"
data-search-profile="{{ search_profile|default:"" }}"
data-search-profiles-api-url="{{ search_profiles_api_url }}"
data-search-profiles-url="{{ search_profiles_url }}"
data-search-profiles-count="{{ search_profiles_count }}"
data-is-authenticated="{{ is_authenticated }}"
Expand Down
3 changes: 2 additions & 1 deletion meinberlin/apps/plans/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ def get_context_data(self, **kwargs):
context["district"] = self.request.GET.get("district", -1)
context["topic"] = self.request.GET.get("topic", -1)
context["participation_choices"] = self.get_participation_choices()
context["search_profiles_url"] = reverse("searchprofiles-list")
context["search_profiles_api_url"] = reverse("searchprofiles-list")
context["search_profiles_url"] = reverse("search_profiles")
context["search_profiles_count"] = self.get_search_profiles_count()
context["is_authenticated"] = json.dumps(self.request.user.is_authenticated)
context["project_status"] = self.get_project_status()
Expand Down
5 changes: 5 additions & 0 deletions meinberlin/assets/scss/components_user_facing/_alert.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
background-color: $message-light-blue;
}

.alert a {
color: inherit;
text-decoration: underline;
}

.alert__content {
padding: 1.125rem 2rem 1.125rem 1.125rem;
margin: 0 auto;
Expand Down
5 changes: 4 additions & 1 deletion meinberlin/react/kiezradar/use-create-search-profile.jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ describe('useCreateSearchProfile', () => {
search: ''
}

const mockOnSearchProfileCreate = jest.fn()

const { result } = renderHook(() =>
useCreateSearchProfile({
searchProfilesApiUrl,
Expand All @@ -166,7 +168,7 @@ describe('useCreateSearchProfile', () => {
participationChoices,
projectStatus,
searchProfilesCount: 10,
onSearchProfileCreate: () => {}
onSearchProfileCreate: mockOnSearchProfileCreate
})
)

Expand All @@ -175,5 +177,6 @@ describe('useCreateSearchProfile', () => {
})

expect(result.current.limitExceeded).toBe(true)
expect(mockOnSearchProfileCreate).toHaveBeenCalledWith(null, true)
})
})
1 change: 1 addition & 0 deletions meinberlin/react/kiezradar/use-create-search-profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function useCreateSearchProfile ({
const createSearchProfile = async () => {
if (searchProfilesCount === 10) {
setLimitExceeded(true)
onSearchProfileCreate(null, true)
return
}

Expand Down
4 changes: 3 additions & 1 deletion meinberlin/react/plans/react_plans_map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ function init () {
const omtToken = el.getAttribute('data-omt-token')
const useVectorMap = el.getAttribute('data-use_vector_map')
const participationChoices = JSON.parse(el.getAttribute('data-participation-choices'))
const searchProfilesApiUrl = el.getAttribute('data-search-profiles-url')
const searchProfilesApiUrl = el.getAttribute('data-search-profiles-api-url')
const searchProfilesUrl = el.getAttribute('data-search-profiles-url')
const searchProfilesCount = JSON.parse(el.getAttribute('data-search-profiles-count'))
const isAuthenticated = JSON.parse(el.getAttribute('data-is-authenticated'))
const projectStatus = JSON.parse(el.getAttribute('data-project-status'))
Expand All @@ -48,6 +49,7 @@ function init () {
participationChoices={participationChoices}
searchProfile={searchProfile}
searchProfilesApiUrl={searchProfilesApiUrl}
searchProfilesUrl={searchProfilesUrl}
searchProfilesCount={searchProfilesCount}
isAuthenticated={isAuthenticated}
projectStatus={projectStatus}
Expand Down
45 changes: 43 additions & 2 deletions meinberlin/react/projects/ProjectsControlBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,21 @@ const translated = {
plans: django.gettext('Plans'),
nav: django.gettext('Search, filter and sort the ideas list'),
searchFor: django.gettext('Search for Proposals'),
button: django.gettext('Show projects')
button: django.gettext('Show projects'),
searchProfileCreatedTitle: django.gettext('Search profile created successfully'),
searchProfileCreatedText: (url) =>
django.interpolate(
django.gettext('You will be informed about new projects that meet the selected filters. You can manage your search profiles in the User Settings under <a href="%(url)s">Search Profiles</a>.'),
{ url },
true
),
searchProfileLimitExceededTitle: django.gettext('Search profile cannot be saved'),
searchProfileLimitExceededText: (url) =>
django.interpolate(
django.gettext('You have saved the maximum number of 10 search profiles. To save a new one, delete an existing profile in the User Settings under <a href="%(url)s">Search Profiles</a>.'),
{ url },
true
)
}

const statusNames = {
Expand Down Expand Up @@ -75,9 +89,12 @@ export const ProjectsControlBar = ({
appliedFilters,
onFiltered,
onResetClick,
onAlert,
onError,
hasContainer,
searchProfile: initialSearchProfile,
searchProfilesApiUrl,
searchProfilesUrl,
searchProfilesCount: initialSearchProfilesCount,
isAuthenticated,
projectStatus
Expand All @@ -98,9 +115,33 @@ export const ProjectsControlBar = ({
window.history.replaceState({}, '', window.location.pathname)
}

const createSearchProfile = (searchProfile) => {
const createSearchProfile = (searchProfile, limitExceeded) => {
if (limitExceeded) {
onError({
title: translated.searchProfileLimitExceededTitle,
message: (
<div
dangerouslySetInnerHTML={{
__html: translated.searchProfileLimitExceededText(searchProfilesUrl)
}}
/>
)
})
return
}

setSearchProfile(searchProfile)
setSearchProfilesCount(searchProfilesCount + 1)
onAlert({
title: translated.searchProfileCreatedTitle,
message: (
<div
dangerouslySetInnerHTML={{
__html: translated.searchProfileCreatedText(searchProfilesUrl)
}}
/>
)
})
window.history.replaceState({}, '', window.location.pathname + '?search-profile=' + searchProfile.id
)
}
Expand Down
28 changes: 27 additions & 1 deletion meinberlin/react/projects/ProjectsListMapBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ProjectsList from '../projects/ProjectsList'
import { ToggleSwitch } from '../contrib/ToggleSwitch'
import { IconSwitch } from '../contrib/IconSwitch'
import { classNames } from 'adhocracy4'
import { alert as Alert, classNames } from 'adhocracy4'
import sortProjects from './sort-projects'
import ProjectsMap from './ProjectsMap'
import Spinner from '../contrib/Spinner'
Expand Down Expand Up @@ -39,6 +39,7 @@ const ProjectsListMapBox = ({
extprojectApiUrl,
privateprojectApiUrl,
searchProfilesApiUrl,
searchProfilesUrl,
attribution,
bounds,
baseUrl,
Expand All @@ -60,6 +61,8 @@ const ProjectsListMapBox = ({
const [items, setItems] = useState([])
const fetchCache = useRef({})
const [appliedFilters, setAppliedFilters] = useState(getDefaultState(searchProfile))
const [alert, setAlert] = useState(null)
const [error, setError] = useState(null)

const fetchItems = useCallback(async () => {
setLoading(true)
Expand Down Expand Up @@ -113,6 +116,26 @@ const ProjectsListMapBox = ({

return (
<div>
{(error || alert) && (
<div className="container">
{error && (
<Alert
type="danger"
title={error.title}
message={error.message}
onClick={() => setError(null)}
/>
)}
{alert && (
<Alert
type="success"
title={alert.title}
message={alert.message}
onClick={() => setAlert(null)}
/>
)}
</div>
)}
<ProjectsControlBar
participationChoices={participationChoices}
organisations={organisations}
Expand All @@ -128,8 +151,11 @@ const ProjectsListMapBox = ({
setAppliedFilters(getDefaultState(searchProfile))
setProjectState(['active', 'future'])
}}
onAlert={setAlert}
onError={setError}
searchProfile={searchProfile}
searchProfilesApiUrl={searchProfilesApiUrl}
searchProfilesUrl={searchProfilesUrl}
searchProfilesCount={searchProfilesCount}
isAuthenticated={isAuthenticated}
projectStatus={projectStatus}
Expand Down

0 comments on commit 0021a76

Please sign in to comment.