Inline-edit user profile with HTMX
This commit is contained in:
parent
ecf4052819
commit
72bd7388d6
4 changed files with 107 additions and 14 deletions
|
@ -1,3 +1,4 @@
|
|||
from .organization import OrganizationCreateForm
|
||||
from .profile import UserProfileForm
|
||||
|
||||
__all__ = ["OrganizationCreateForm"]
|
||||
__all__ = ["OrganizationCreateForm", "UserProfileForm"]
|
||||
|
|
11
src/servala/frontend/forms/profile.py
Normal file
11
src/servala/frontend/forms/profile.py
Normal 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")
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue