From 397de516d9519685f6d1811393018c1b7eacaece Mon Sep 17 00:00:00 2001 From: Sev Furneaux Date: Wed, 18 Dec 2024 12:32:32 +0000 Subject: [PATCH] react/kiez-search-profile: add initial dashboard --- .../react_kiezradar_search_profiles.py | 6 +- .../_search-profiles.scss | 96 +++++++ meinberlin/assets/scss/style_user_facing.scss | 1 + meinberlin/react/kiezradar/SearchProfiles.jsx | 252 ++++++++++++++++++ meinberlin/react/kiezradar/dummy.jsx | 7 - .../react_kiezradar_search_profiles_init.jsx | 22 +- 6 files changed, 364 insertions(+), 20 deletions(-) create mode 100644 meinberlin/assets/scss/components_user_facing/_search-profiles.scss create mode 100644 meinberlin/react/kiezradar/SearchProfiles.jsx delete mode 100644 meinberlin/react/kiezradar/dummy.jsx diff --git a/meinberlin/apps/kiezradar/templatetags/react_kiezradar_search_profiles.py b/meinberlin/apps/kiezradar/templatetags/react_kiezradar_search_profiles.py index 1969583264..c21f126a4c 100644 --- a/meinberlin/apps/kiezradar/templatetags/react_kiezradar_search_profiles.py +++ b/meinberlin/apps/kiezradar/templatetags/react_kiezradar_search_profiles.py @@ -1,6 +1,7 @@ import json from django import template +from django.urls import reverse from django.utils.html import format_html register = template.Library() @@ -8,7 +9,10 @@ @register.simple_tag() def react_kiezradar_search_profiles(): - attributes = {} + attributes = { + "apiUrl": reverse("searchprofiles-list"), + "planListUrl": reverse("meinberlin_plans:plan-list"), + } return format_html( '
+

{titleText}

+

{descriptionText}

+ + + ) +} + +function SearchProfileList ({ apiUrl, planListUrl }) { + const [searchProfiles, setSearchProfiles] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + const fetchSearchProfiles = async () => { + try { + setLoading(true) + setError(null) + + const response = await fetch(apiUrl) + + if (!response.ok) { + throw new Error(errorSearchProfilesText) + } + + const data = await response.json() + setSearchProfiles(data) + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } + } + fetchSearchProfiles() + }, []) + + if (loading) { + return + } + + if (error) { + return ( +
+ {errorText}: {error} +
+ ) + } + + return ( +
+ {searchProfiles.length === 0 + ? ( + <> +

{noSavedProfilesText}

+ + + {findProjectsText} + + + ) + : ( + <> +

+ {yourSavedProfilesText} ({searchProfiles.length}) +

+ {searchProfiles.map((profile) => ( + + setSearchProfiles((prevSearchProfiles) => + prevSearchProfiles.filter((profile) => profile.id !== id) + )} + /> + ))} + + )} +
+ ) +} + +function SearchProfile ({ apiUrl, planListUrl, profile: profile_, onDelete }) { + const [isEditing, setIsEditing] = useState(false) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + const [profile, setProfile] = useState(profile_) + + const handleDelete = async () => { + setLoading(true) + setError(null) + + try { + const response = await fetch(apiUrl + profile.id + '/', { + headers: { + 'X-CSRFToken': cookie.get('csrftoken') + }, + method: 'DELETE' + }) + + if (!response.ok) { + throw new Error(errorDeleteSearchProfilesText) + } + + onDelete(profile.id) + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } + } + + const handleSubmit = async (e) => { + e.preventDefault() + setLoading(true) + setError(null) + + try { + const response = await fetch(apiUrl + profile.id + '/', { + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-CSRFToken': cookie.get('csrftoken') + }, + method: 'PATCH', + body: JSON.stringify({ name: e.target.elements.name.value }) + }) + + if (!response.ok) { + throw new Error(errorUpdateSearchProfilesText) + } + + const data = await response.json() + setProfile(data) + } catch (err) { + setError(err.message) + } finally { + setIsEditing(false) + setLoading(false) + } + } + + return ( +
+
+

{profile.name}

+ {!isEditing && ( +
+ setIsEditing(true)} + onDelete={handleDelete} + loading={loading} + /> +
+ )} +
+ {error &&
{errorText + ': ' + error}
} + {isEditing && ( +
+
+ + +
+
+
+ +
+
+ +
+
+
+ )} +
+ ( -

Put content here

-) - -export default Dummy diff --git a/meinberlin/react/kiezradar/react_kiezradar_search_profiles_init.jsx b/meinberlin/react/kiezradar/react_kiezradar_search_profiles_init.jsx index c0cf76ca03..e95ed4dade 100644 --- a/meinberlin/react/kiezradar/react_kiezradar_search_profiles_init.jsx +++ b/meinberlin/react/kiezradar/react_kiezradar_search_profiles_init.jsx @@ -1,20 +1,18 @@ import React from 'react' import { createRoot } from 'react-dom/client' import { widget as ReactWidget } from 'adhocracy4' -import Dummy from './dummy' +import SearchProfiles from './SearchProfiles' function init () { - ReactWidget.initialise('mb', 'kiezradar-search-profiles', - function (el) { - // const props = JSON.parse(el.getAttribute('data-attributes')) - const root = createRoot(el) - root.render( - - - - ) - } - ) + ReactWidget.initialise('mb', 'kiezradar-search-profiles', function (el) { + const props = JSON.parse(el.getAttribute('data-attributes')) + const root = createRoot(el) + root.render( + + + + ) + }) } document.addEventListener('DOMContentLoaded', init, false)