Introduce UserConfigForm for managing user preferences

This commit is contained in:
jeremystretch 2021-12-21 16:29:01 -05:00
parent 5e32c69e0e
commit 70f257b1ea
8 changed files with 94 additions and 96 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,6 @@
import { initConnectionToggle } from './connectionToggle'; import { initConnectionToggle } from './connectionToggle';
import { initDepthToggle } from './depthToggle'; import { initDepthToggle } from './depthToggle';
import { initMoveButtons } from './moveOptions'; import { initMoveButtons } from './moveOptions';
import { initPreferenceUpdate } from './preferences';
import { initReslug } from './reslug'; import { initReslug } from './reslug';
import { initSelectAll } from './selectAll'; import { initSelectAll } from './selectAll';
@ -11,7 +10,6 @@ export function initButtons(): void {
initConnectionToggle, initConnectionToggle,
initReslug, initReslug,
initSelectAll, initSelectAll,
initPreferenceUpdate,
initMoveButtons, initMoveButtons,
]) { ]) {
func(); func();

View File

@ -1,30 +0,0 @@
import { setColorMode } from '../colorMode';
import { getElement } from '../util';
/**
* Perform actions in the UI based on the value of user profile updates.
*
* @param event Form Submit
*/
function handlePreferenceSave(event: Event): void {
// Create a FormData instance to access the form values.
const form = event.currentTarget as HTMLFormElement;
const formData = new FormData(form);
// Update the UI color mode immediately when the user preference changes.
if (formData.get('ui.colormode') === 'dark') {
setColorMode('dark');
} else if (formData.get('ui.colormode') === 'light') {
setColorMode('light');
}
}
/**
* Initialize handlers for user profile updates.
*/
export function initPreferenceUpdate(): void {
const form = getElement<HTMLFormElement>('preferences-update');
if (form !== null) {
form.addEventListener('submit', handlePreferenceSave);
}
}

View File

@ -1,57 +1,27 @@
{% extends 'users/base.html' %} {% extends 'users/base.html' %}
{% load helpers %} {% load helpers %}
{% load form_helpers %}
{% block title %}User Preferences{% endblock %} {% block title %}User Preferences{% endblock %}
{% block content %} {% block content %}
<form method="post" action="" id="preferences-update"> <form method="post" action="" id="preferences-update">
{% csrf_token %} {% csrf_token %}
<div class="field-group mb-3">
<h5>Color Mode</h5> {% for group, fields in form.Meta.fieldsets %}
<p class="text-muted">Set preferred UI color mode</p> <div class="field-group my-5">
{% with color_mode=preferences|get_key:'ui.colormode'%} <div class="row mb-2">
<div class="form-check form-check-inline"> <h5 class="offset-sm-3">{{ group }}</h5>
<input class="form-check-input" type="radio" name="ui.colormode" id="color-mode-preference-dark" value="dark"{% if color_mode == 'dark'%} checked{% endif %}> </div>
<label class="form-check-label" for="color-mode-preference-dark">Dark</label> {% for name in fields %}
{% render_field form|getfield:name %}
{% endfor %}
</div> </div>
<div class="form-check form-check-inline"> {% endfor %}
<input class="form-check-input" type="radio" name="ui.colormode" id="color-mode-preference-light" value="light"{% if color_mode == 'light'%} checked{% endif %}>
<label class="form-check-label" for="color-mode-preference-light">Light</label> <div class="text-end my-3">
</div> <a class="btn btn-outline-secondary" href="{% url 'user:preferences' %}">Cancel</a>
{% endwith %} <button type="submit" name="_update" class="btn btn-primary">Save </button>
</div> </div>
<div class="row mb-3">
<div class="col">
<button type="submit" class="btn btn-primary" name="_update">Save</button>
</div>
</div>
{% if preferences %}
<div class="field-group mb-3">
<h5>Other Preferences</h5>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" class="toggle form-check-input" title="Toggle All"></th>
<th>Preference</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for key, value in preferences.items %}
<tr>
<td class="min-width"><input class="form-check-input" type="checkbox" name="pk" value="{{ key }}"></td>
<td><samp>{{ key }}</samp></td>
<td><samp>{{ value }}</samp></td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit" class="btn btn-danger" name="_delete">
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Clear Selected
</button>
</div>
{% else %}
<h3 class="text-muted text-center">No preferences found</h3>
{% endif %}
</form> </form>
{% endblock %} {% endblock %}

View File

@ -2,7 +2,9 @@ from django import forms
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
from utilities.forms import BootstrapMixin, DateTimePicker from utilities.forms import BootstrapMixin, DateTimePicker
from .models import Token from utilities.paginator import EnhancedPaginator
from utilities.utils import flatten_dict
from .models import Token, UserConfig
class LoginForm(BootstrapMixin, AuthenticationForm): class LoginForm(BootstrapMixin, AuthenticationForm):
@ -13,6 +15,68 @@ class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
pass pass
def get_page_lengths():
return [
(v, str(v)) for v in EnhancedPaginator.default_page_lengths
]
class UserConfigForm(BootstrapMixin, forms.ModelForm):
pagination__per_page = forms.TypedChoiceField(
label='Page length',
coerce=lambda val: int(val),
choices=get_page_lengths,
required=False
)
ui__colormode = forms.ChoiceField(
label='Color mode',
choices=(
('light', 'Light'),
('dark', 'Dark'),
),
required=False
)
extras__configcontext__format = forms.ChoiceField(
label='ConfigContext format',
choices=(
('json', 'JSON'),
('yaml', 'YAML'),
),
required=False
)
class Meta:
model = UserConfig
fields = ()
fieldsets = (
('User Interface', (
'pagination__per_page',
'ui__colormode',
)),
('Miscellaneous', (
'extras__configcontext__format',
)),
)
def __init__(self, *args, instance=None, **kwargs):
# Get initial data from UserConfig instance
initial_data = flatten_dict(instance.data, separator='__')
kwargs['initial'] = initial_data
super().__init__(*args, instance=instance, **kwargs)
def save(self, *args, **kwargs):
# Set UserConfig data
for field_name, value in self.cleaned_data.items():
pref_name = field_name.replace('__', '.')
print(f'{pref_name}: {value}')
self.instance.set(pref_name, value, commit=False)
return super().save(*args, **kwargs)
class TokenForm(BootstrapMixin, forms.ModelForm): class TokenForm(BootstrapMixin, forms.ModelForm):
key = forms.CharField( key = forms.CharField(
required=False, required=False,

View File

@ -19,7 +19,7 @@ from extras.models import ObjectChange
from extras.tables import ObjectChangeTable from extras.tables import ObjectChangeTable
from netbox.config import get_config from netbox.config import get_config
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from .forms import LoginForm, PasswordChangeForm, TokenForm from .forms import LoginForm, PasswordChangeForm, TokenForm, UserConfigForm
from .models import Token from .models import Token
@ -137,32 +137,28 @@ class UserConfigView(LoginRequiredMixin, View):
template_name = 'users/preferences.html' template_name = 'users/preferences.html'
def get(self, request): def get(self, request):
userconfig = request.user.config
form = UserConfigForm(instance=userconfig)
return render(request, self.template_name, { return render(request, self.template_name, {
'preferences': request.user.config.all(), 'form': form,
'active_tab': 'preferences', 'active_tab': 'preferences',
}) })
def post(self, request): def post(self, request):
userconfig = request.user.config userconfig = request.user.config
data = userconfig.all() form = UserConfigForm(request.POST, instance=userconfig)
# Delete selected preferences if form.is_valid():
if "_delete" in request.POST: form.save()
for key in request.POST.getlist('pk'):
if key in data:
userconfig.clear(key)
# Update specific values
elif "_update" in request.POST:
for key in request.POST:
if not key.startswith('_') and not key.startswith('csrf'):
for value in request.POST.getlist(key):
userconfig.set(key, value)
userconfig.save() messages.success(request, "Your preferences have been updated.")
messages.success(request, "Your preferences have been updated.") return redirect('user:preferences')
return redirect('user:preferences') return render(request, self.template_name, {
'form': form,
'active_tab': 'preferences',
})
class ChangePasswordView(LoginRequiredMixin, View): class ChangePasswordView(LoginRequiredMixin, View):

View File

@ -282,7 +282,7 @@ def flatten_dict(d, prefix='', separator='.'):
for k, v in d.items(): for k, v in d.items():
key = separator.join([prefix, k]) if prefix else k key = separator.join([prefix, k]) if prefix else k
if type(v) is dict: if type(v) is dict:
ret.update(flatten_dict(v, prefix=key)) ret.update(flatten_dict(v, prefix=key, separator=separator))
else: else:
ret[key] = v ret[key] = v
return ret return ret