Inline-edit user profile with HTMX

This commit is contained in:
Tobias Kunze 2025-03-20 08:28:09 +01:00
parent ecf4052819
commit 72bd7388d6
4 changed files with 107 additions and 14 deletions

View file

@ -1,3 +1,4 @@
from .organization import OrganizationCreateForm
from .profile import UserProfileForm
__all__ = ["OrganizationCreateForm"]
__all__ = ["OrganizationCreateForm", "UserProfileForm"]

View file

@ -0,0 +1,11 @@
from django import forms
from servala.core.models import User
from servala.frontend.forms.mixins import HtmxMixin
class UserProfileForm(HtmxMixin, forms.ModelForm):
class Meta:
model = User
fields = ("email", "company")

View file

@ -1,5 +1,6 @@
{% extends "frontend/base.html" %}
{% load i18n static %}
{% load partials %}
{% block html_title %}
{% block page_title %}
{% translate "Profile" %}
@ -10,6 +11,70 @@
<h4 class="card-title">{% translate "Account" %}</h4>
</div>
{% endblock %}
{% partialdef user-email %}
<tr>
<th>{% translate "E-mail" %}</th>
<td>{{ request.user.email }}</td>
<td>
<button class="btn btn-sm btn-primary"
hx-get="{% url 'frontend:profile' %}?fragment=user-email-edit"
hx-target="closest tr"
hx-swap="outerHTML">{% translate "Edit" %}</button>
</td>
</tr>
{% endpartialdef user-email %}
{% partialdef user-company %}
<tr>
<th>{% translate "Company" %}</th>
<td>{{ request.user.company|default_if_none:"" }}</td>
<td>
<button class="btn btn-sm btn-primary"
hx-get="{% url 'frontend:profile' %}?fragment=user-company-edit"
hx-target="closest tr"
hx-swap="outerHTML">{% translate "Edit" %}</button>
</td>
</tr>
{% endpartialdef user-company %}
{% partialdef user-email-edit %}
<tr>
<th>{% translate "E-mail" %}</th>
<td colspan="2">
<form hx-target="closest tr"
hx-swap="outerHTML"
hx-post="{{ request.url }}">
{{ form.email }}
<input type="hidden" name="hx-single-field" value="email">
<input type="hidden" name="fragment" value="user-email">
<button type="submit" class="btn btn-primary">{% translate "Save" %}</button>
<button type="button"
class="btn btn-secondary"
hx-get="{{ request.path }}?fragment=user-email"
hx-target="closest tr"
hx-swap="outerHTML">{% translate "Cancel" %}</button>
</form>
</td>
</tr>
{% endpartialdef %}
{% partialdef user-company-edit %}
<tr>
<th>{% translate "Company" %}</th>
<td colspan="2">
<form hx-target="closest tr"
hx-swap="outerHTML"
hx-post="{{ request.url }}">
{{ form.company }}
<input type="hidden" name="hx-single-field" value="company">
<input type="hidden" name="fragment" value="user-company">
<button type="submit" class="btn btn-primary">{% translate "Save" %}</button>
<button type="button"
class="btn btn-secondary"
hx-get="{{ request.path }}?fragment=user-company"
hx-target="closest tr"
hx-swap="outerHTML">{% translate "Cancel" %}</button>
</form>
</td>
</tr>
{% endpartialdef %}
{% block content %}
<section>
<div class="row match-height">
@ -23,22 +88,18 @@
<div class="table-responsive">
<table class="table table-lg">
<tbody>
<tr>
<th>{% translate "E-mail" %}</th>
<td>{{ request.user.email }}</td>
</tr>
{% partial user-email %}
<tr>
<th>{% translate "First name" %}</th>
<td>{{ request.user.first_name }}</td>
<td></td>
</tr>
<tr>
<th>{% translate "Last name" %}</th>
<td>{{ request.user.last_name }}</td>
<td></td>
</tr>
<tr>
<th>{% translate "Company" %}</th>
<td>{{ request.user.company }}</td>
</tr>
{% partial user-company %}
</tbody>
</table>
</div>

View file

@ -1,21 +1,41 @@
from django.conf import settings
from django.views.generic import TemplateView
from django.urls import reverse_lazy
from django.utils.functional import cached_property
from django.views.generic import TemplateView, UpdateView
from servala.core.models import User
from servala.frontend.forms.profile import UserProfileForm
from servala.frontend.views.mixins import HtmxMixin
class IndexView(TemplateView):
template_name = "frontend/index.html"
class ProfileView(TemplateView):
class ProfileView(HtmxMixin, UpdateView):
template_name = "frontend/profile.html"
form_class = UserProfileForm
success_url = reverse_lazy("frontend:profile")
fragments = ("user-email", "user-email-edit", "user-company", "user-company-edit")
model = User
def get_object(self):
return self.request.user
@cached_property
def object(self):
return self.get_object()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
keycloak_server_url = settings.SOCIALACCOUNT_PROVIDERS["openid_connect"][
"APPS"
][0]["settings"]["server_url"]
keycloak_settings = settings.SOCIALACCOUNT_PROVIDERS["openid_connect"]
keycloak_server_url = keycloak_settings["APPS"][0]["settings"]["server_url"]
account_url = keycloak_server_url.replace(
"/.well-known/openid-configuration", "/account"
)
context["account_href"] = account_url
return context
def form_valid(self, form):
form.save()
return super().form_valid(form)