Responsive layout for better experience on mobile devices

This commit changes the layout of binder to provide a better experience on
mobile devices and for form validation errors. It's outcome might not be
perfect yet, but I believe it's a really nice step forward.

The changes consist of:
* moving the menu from the left side to the top, so it doesn't consume
  additional screen space when scrolling down
* changed the width of the content to depend on the screen size. For larger
  screens the forms won't consume the whole width, but validation errors will
  be shown beside the form field containing the error
* showing field independent error messages on top of each form
* highlighting of form fields which contain errors
This commit is contained in:
Daniel Roschka 2015-06-05 16:50:48 +02:00
parent cbbc5f08c0
commit f41133d733
6 changed files with 282 additions and 192 deletions

View File

@ -15,40 +15,51 @@
<script src="{% static "jquery-2.1.3.min.js" %}"></script>
<script src="{% static "bootstrap/js/bootstrap.min.js" %}"></script>
<div class="container-fluid">
<div class="container">
<div class="row">
<div class="col-md-2">
{% block navigation %}
<ul class="nav nav-pills nav-stacked">
<li role="presentation" class="active">Actions</li>
<li role="presentation"><a href="{% url "index" %}">Home</a></li>
<li role="presentation"><a href="{% url "server_list" %}">Server List</a></li>
</ul>
{% endblock navigation %}
</div>
<div class="col-md-10">
<div class="panel panel-default">
<div class="panel-heading">
{% block pageheader %}{% endblock pageheader %}
<div class="col-md-12">
<div class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar" aria-hidden="true"></span>
<span class="icon-bar" aria-hidden="true"></span>
<span class="icon-bar" aria-hidden="true"></span>
</button>
<a class="navbar-brand" href="{% url "index" %}">Binder</a>
</div>
<div class="collapse navbar-collapse" id="bs-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{% url "server_list" %}">Server List</a></li>
</ul>
</div>
</div>
</div>
</div>
{% block errors %}
{% if errors %}
<div class="alert alert-error">
Errors were encountered:
<br>
{{ errors }}
</div>
{% endif %}
{% endblock errors %}
<div class="panel-body">
{% block body %}
{% endblock body %}
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>{% block pageheader %}{% endblock pageheader %}</h2>
<br />
</div>
</div>
<div class="row">
<div class="col-md-12">
{% block errors %}
{% if errors %}
<div class="alert alert-error">
Errors were encountered:
<br />
{{ errors }}
</div>
{% endif %}
{% endblock errors %}
{% block body %}
{% endblock body %}
</div>
</div>
</div>
</body>

View File

@ -5,57 +5,81 @@
{% block body %}
<form class="form-horizontal" action="{% url "add_cname_result" %}" method="post">{% csrf_token %}
<legend>Create CNAME record</legend>
<div class="form-group">
<label for="id_dns_server" class="control-label col-md-2">DNS Server</label>
<div class="controls col-md-10">
<input type="text" id="id_dns_server" class="form-control" name="dns_server" value="{{dns_server.hostname}}" readonly />
</div>
</div>
<div class="form-group">
<label for="id_zone_name" class="control-label col-md-2">Zone</label>
<div class="controls col-md-10">
<input type="text" id="id_zone_name" class="form-control" name="zone_name" value="{{zone_name}}" readonly />
<div class="form-group{% if form_errors.dns_server %} has-error{% endif %}">
<label for="dns_server" class="col-sm-3 control-label">DNS Server:</label>
<div class="col-sm-5 col-md-4">
<input id="dns_server" name="dns_server" type="text" class="form-control" value="{{dns_server.hostname}}" readonly="readonly" />
</div>
</div>
<div class="form-group">
<label for="id_originating_record" class="control-label col-md-2">Originating Record</label>
<div class="controls col-md-10">
<input type="text" id="id_originating_record" class="form-control" name="originating_record" value="{{originating_record}}" readonly />
</div>
</div>
<div class="form-group">
<label for="id_cname" class="control-label col-md-2">CNAME</label>
<div class="controls col-md-10">
{% if form_errors.cname %}
<div class="alert alert-danger" role="alert">
Error in CNAME input: {{ form_errors.cname|stringformat:"s"|striptags }} Previous Value: {{ form_data.cname }}
{% if form_errors.dns_server %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.dns_server|stringformat:"s"|striptags }}
{% if form_data.dns_server %} Previous Value: {{ form_data.dns_server }}{% endif %}
</div>
{% endif %}
</div>
{% endif %}
</div>
<div class="form-group{% if form_errors.originating_record %} has-error{% endif %}">
<label for="originating_record" class="col-sm-3 control-label">Originating Record: </label>
<div class="col-sm-5 col-md-4">
<input type="text" id="originating_record" name="originating_record" class="form-control" value="{{originating_record}}" readonly="readonly"/>
</div>
{% if form_errors.originating_record %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.originating_record|stringformat:"s"|striptags }}
{% if form_data.originating_record %} Previous Value: {{ form_data.originating_record }}{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="form-group{% if form_errors.cname %} has-error{% endif %}">
<label for="cname" class="col-sm-3 control-label">CNAME: </label>
<div class="col-sm-5 col-md-4">
<div class="input-group">
<input type="text" id="id_originating_record" class="form-control" name="cname"/>
<div class="input-group-addon">.{{zone_name}}</div>
<input id="cname" name="cname" type="text" class="form-control" />
<span class="input-group-addon">.{{zone_name}}</span>
<input type="hidden" name="zone_name" value="{{zone_name}}"/>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">TTL</label>
<div class="controls col-md-10">
{% for ttl, description in ttl_choices %}
<label for="id_ttl_{{ttl}}" class="radio-inline">
<input type="radio" id="id_ttl_{{ ttl }}" name="ttl" value="{{ttl}}">{{ttl}} ({{description}})
</label>
{% endfor %}
{% if form_errors.cname %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
CNAME: {{ form_errors.cname|stringformat:"s"|striptags }}
{% if form_data.cnamr %} Previous Value: {{ form_data.cname }}{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="form-group">
<label for="id_key_name" class="control-label col-md-2">TSIG Key</label>
<div class="controls col-md-10">
<select id="id_key_name" class="form-control" name="key_name">
<div class="form-group{% if form_errors.ttl %} has-error{% endif %}">
<label for="ttl" class="col-sm-3 control-label">TTL: </label>
<div class="col-sm-5 col-md-4">
<select id="ttl" name="ttl" class="form-control">
{% for ttl, description in ttl_choices %}
<option value="{{ttl}}">
{{ttl}} ({{description}})
</option>
{% endfor %}
</select>
</div>
{% if form_errors.ttl %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.ttl|stringformat:"s"|striptags }}
{% if form_data.ttl %} Previous Value: {{ form_data.ttl }}{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="form-group{% if form_errors.key_name %} has-error{% endif %}">
<label for="key_name" class="col-sm-3 control-label">TSIG Key:</label>
<div class="col-sm-5 col-md-4">
<select id="key_name" name="key_name" class="form-control">
{% for key in tsig_keys %}
<option value="{{key.id}}"{% if key == dns_server.default_transfer_key %} selected="selected"{% endif %}>{{key}}</option>
{% empty %}
@ -63,9 +87,22 @@
{% endfor %}
</select>
</div>
{% if form_errors.key_name %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.key_name|stringformat:"s"|striptags }}
{% if form_data.key_name %} Previous Value: {{ form_data.key_name }}{% endif %}
</div>
</div>
{% endif %}
</div>
<button type="submit" class="btn btn-default">Save Changes</button>
<a href="{% url "zone_list" dns_server=dns_server zone_name=zone_name %}" class="btn btn-warning">Cancel</a>
</form>
<div class="form-group">
<div class="col-sm-3"></div>
<div class="col-sm-5 col-md-4">
<button type="submit" class="btn btn-default">Save Changes</button>
<a href="{% url "zone_list" dns_server=dns_server zone_name=zone_name %}" class="btn btn-warning">Cancel</a>
</div>
</div>
</form>
{% endblock body %}

View File

@ -5,98 +5,113 @@
{% block body %}
<form class="form-horizontal" action="{% url "add_record_result" %}" method="POST">{% csrf_token %}
<legend>Create Record</legend>
<div class="form-group">
<label for="id_dns_server" class="control-label col-md-2">DNS Server</label>
<div class="controls col-md-10">
<input type="text" id="id_dns_server" class="form-control" name="dns_server" value="{{dns_server.hostname}}" readonly />
<div class="form-group{% if form_errors.dns_server %} has-error{% endif %}">
<label for="dns_server" class="col-sm-3 control-label">DNS Server:</label>
<div class="col-sm-5 col-md-4">
<input id="dns_server" name="dns_server" type="text" class="form-control" value="{{dns_server.hostname}}" readonly="readonly" />
</div>
</div>
<div class="form-group">
<label for="id_zone_name" class="control-label col-md-2">Zone</label>
<div class="controls col-md-10">
<input type="text" id="id_zone_name" class="form-control" name="zone_name" value="{{zone_name}}" readonly>
</div>
</div>
<div class="form-group">
<label for="id_record_name" class="control-label col-md-2">Record Name</label>
<div class="controls col-md-10">
{% if form_errors.record_name %}
<div class="alert alert-danger" role="alert">
Record Name: {{ form_errors.record_name|stringformat:"s"|striptags }} Previous Value: {{ form_data.record_name }}
{% if form_errors.dns_server %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.dns_server|stringformat:"s"|striptags }}
{% if form_data.dns_server %}Previous Value: {{ form_data.dns_server }}{% endif %}
</div>
{% endif %}
</div>
{% endif %}
</div>
<div class="form-group{% if form_errors.record_name %} has-error{% endif %}">
<label for="record_name" class="col-sm-3 control-label">Record Name:</label>
<div class="col-sm-5 col-md-4">
<div class="input-group">
<input type="text" id="id_record_name" class="form-control" name="record_name"/>
<div class="input-group-addon">.{{zone_name}}</div>
<input id="record_name" name="record_name" type="text" class="form-control" />
<span class="input-group-addon">.{{zone_name}}</span>
<input type="hidden" name="zone_name" value="{{zone_name}}" />
</div>
</div>
{% if form_errors.record_name %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger form-control-static">
{{ form_errors.record_name|stringformat:"s"|striptags }}
{% if form_data.record_name %} Previous Value: {{ form_data.record_name }}{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="form-group">
<label class="control-label col-md-2">Record Type</label>
<div class="controls col-md-10">
<div class="form-group{% if form_errors.record_type %} has-error{% endif %}">
<label for="record_type" class="col-sm-3 control-label">Record Type:</label>
<div class="col-sm-5 col-md-4">
<select id="record_type" name="record_type" class="form-control">
{% if "in-addr.arpa" not in zone_name and "ip6.arpa" not in zone_name %}
{% for type, name in record_type_choices %}
<label for="id_record_type_{{ name }}" class="radio-inline">
<input type="radio" id="id_record_type_{{ name }}" name="record_type" value="{{name}}">{{name}}
</label>
{% endfor %}
{% for type, name in record_type_choices %}
<option value="{{name}}">{{name}}</option>
{% endfor %}
{% else %}
<label for="id_record_type" class="radio-inline">
<input type="radio" id="id_record_type" name="record_type" id="record_type_PTR" value="PTR" checked="checked">PTR
</label>
<option value="PTR">PTR</option>
{% endif %}
</select>
</div>
{% if form_errors.record_type %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.record_type|stringformat:"s"|striptags }}
{% if form_data.record_type %} Previous Value: {{ form_data.record_type }}{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="form-group">
<label for="id_record_data" class="control-label col-md-2">Record Data</label>
<div class="controls col-md-10">
{% if form_errors.record_data %}
<div class="alert alert-danger" role="alert">
Record Data: {{ form_errors.record_data|stringformat:"s"|striptags }} Previous Value: {{ form_data.record_data }}
</div>
{% endif %}
<div class="input-group">
<input type="text" id="id_record_data" class="form-control" name="record_data"/>
{% if "in-addr.arpa" not in zone_name and "ip6.arpa" not in zone_name %}
<div class="input-group-addon">IP Address</div>
{% else %}
<div class="input-group-addon">FQDN</div>
{% endif %}
<div class="form-group{% if form_errors.record_data %} has-error{% endif %}">
<label for="record_data" class="col-sm-3 control-label">Record Data:</label>
<div class="col-sm-5 col-md-4">
<input id="record_data" name="record_data" type="text" class="form-control" />
</div>
{% if form_errors.record_data %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.record_data|stringformat:"s"|striptags }}
{% if form_data.record_data %} Previous Value: {{ form_data.record_data }}{% endif %}
</div>
</div>
{% endif %}
</div>
<div class="form-group">
<label class="control-label col-md-2">TTL</label>
<div class="controls col-md-10">
{% for ttl, description in ttl_choices %}
<label for="id_ttl_{{ ttl }}" class="radio-inline">
<input type="radio" id="id_ttl_{{ ttl }}" name="ttl" value="{{ttl}}">{{ttl}} ({{description}})
</label>
{% endfor %}
<div class="form-group{% if form_errors.ttl %} has-error{% endif %}">
<label for="ttl" class="col-sm-3 control-label">TTL: </label>
<div class="col-sm-5 col-md-4">
<select id="ttl" name="ttl" class="form-control">
{% for ttl, description in ttl_choices %}
<option value="{{ttl}}">
{{ttl}} ({{description}})
</option>
{% endfor %}
</select>
</div>
{% if form_errors.ttl %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.ttl|stringformat:"s"|striptags }}
{% if form_data.ttl %} Previous Value: {{ form_data.ttl }}{% endif %}
</div>
</div>
{% endif %}
</div>
{% if "in-addr.arpa" not in zone_name and "ip.arpa" not in zone_name %}
<div class="form-group">
<label for="id_create_reverse" class="control-label col-md-2">Create PTR?</label>
<div class="checkbox controls col-md-10">
<label>
<input type="checkbox" id="id_create_reverse" name="create_reverse" id="create_reverse" value="True" aria-label="create ptr record?">Yes
<label>
<label for="create_reverse" class="col-sm-3 control-label checkbox">Create Reverse Record (PTR):</label>
<div class="col-sm-5 col-md-4">
<input type="checkbox" id="create_reverse" name="create_reverse" value="True" />
</div>
</div>
{% endif %}
<div class="form-group">
<label for="id_key_name" class="control-label col-md-2">TSIG Key</label>
<div class="controls col-md-10">
<select id="id_key_name" class="form-control" name="key_name">
<div class="form-group{% if form_errors.key_name %} has-error{% endif %}">
<label for="key_name" class="col-sm-3 control-label">TSIG Key:</label>
<div class="col-sm-5 col-md-4">
<select id="key_name" name="key_name" class="form-control">
{% for key in tsig_keys %}
<option value="{{key.id}}"{% if key == dns_server.default_transfer_key %} selected="selected"{% endif %}>{{key}}</option>
{% empty %}
@ -104,9 +119,22 @@
{% endfor %}
</select>
</div>
{% if form_errors.key_name %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.key_name|stringformat:"s"|striptags }}
{% if form_data.key_name %} Previous Value: {{ form_data.key_name }}{% endif %}
</div>
</div>
{% endif %}
</div>
<button type="submit" class="btn btn-default">Save Changes</button>
<a href="{% url "zone_list" dns_server=dns_server zone_name=zone_name %}" class="btn btn-warning">Cancel</a>
<div class="form-group">
<div class="col-sm-3"></div>
<div class="col-sm-5 col-md-4">
<button type="submit" class="btn btn-default">Save Changes</button>
<a href="{% url "zone_list" dns_server=dns_server zone_name=zone_name %}" class="btn btn-warning">Cancel</a>
</div>
</div>
</form>
{% endblock body %}

View File

@ -3,51 +3,65 @@
{% block pageheader %}Delete record(s) in {{ zone_name }}{% endblock pageheader %}
{% block body %}
<form action="{% url "delete_record_result" %}" method="POST">
{% csrf_token %}
<table class="sortable table table-hover">
<tr>
<td>DNS Server</td>
<td>{{dns_server.hostname}}</td>
</tr>
<tr>
<td>Zone</td>
<td>{{zone_name}}</td>
</tr>
<tr>
<td>Records</td>
<td>{% for current_rr in rr_list %}
{{ current_rr }}<br>
{% endfor %}
</td>
</tr>
<tr>
<td><label for="id_key_name">TSIG Key</label></td>
<td>
<div class="form-group">
<div class="controls">
<select id="id_key_name" class="form-control" name="key_name">
{% for key in tsig_keys %}
<form class="form-horizontal" action="{% url "delete_record_result" %}" method="POST">{% csrf_token %}
<legend>Delete Record</legend>
<div class="row">
<div class="col-sm-3"></div>
<p class="col-sm-5 col-md-4 bg-danger" style="padding: 1em; border-radius: 4px;">Do you really want to delete the following records?</p>
</div>
<div class="form-group">
<label for="dns_server" class="col-sm-3 control-label">Server:</label>
<input type="hidden" id="dns_server" name="dns_server" value="{{ dns_server.hostname }}" />
<div class="col-sm-5 col-md-4">
<p class="form-control-static">{{ dns_server.hostname }}</p>
</div>
</div>
<div class="form-group">
<label for="zone_name" class="col-sm-3 control-label">Zone:</label>
<input type="hidden" id="zone_name" name="zone_name" value="{{ zone_name }}" />
<div class="col-sm-5 col-md-4">
<p class="form-control-static">{{ zone_name }}</p>
</div>
</div>
<div class="form-group">
<label for="rr_list" class="col-sm-3 control-label">Records:</label>
<input type="hidden" id="rr_list" name="rr_list" value="{{ rr_list }}" />
<div class="col-sm-5 col-md-4">
<p class="form-control-static">{% for current_rr in rr_list %}{{ current_rr }}<br /> {% endfor %}</p>
</div>
</div>
<div class="form-group{% if form_errors.key_name %} has-error{% endif %}">
<label for="key_name" class="col-sm-3 control-label">TSIG Key:</label>
<div class="col-sm-5 col-md-4">
<select id="key_name" name="key_name" class="form-control">
{% for key in tsig_keys %}
<option value="{{key.id}}"{% if key == dns_server.default_transfer_key %} selected="selected"{% endif %}>{{key}}</option>
{% empty %}
{% empty %}
<option selected="selected" value=""/>
{% endfor %}
</select>
{% endfor %}
</select>
</div>
{% if form_errors.key_name %}
<div class="col-sm-4 col-md-5">
<div class="alert alert-danger">
{{ form_errors.key_name|stringformat:"s"|striptags }}
{% if form_data.key_name %} Previous Value: {{ form_data.key_name }}{% endif %}
</div>
</div>
</td>
</tr>
<tr>
<td>
<button type="submit" class="btn btn-default">Yes, really delete.</button>
<a href="{% url "zone_list" dns_server=dns_server zone_name=zone_name %}" class="btn btn-warning">Cancel</a>
</td>
<td></td>
</tr>
</table>
{% endif %}
</div>
<input type="text" class="form-control hidden" name="dns_server" value="{{ dns_server.hostname }}"/>
<input type="text" class="form-control hidden" name="zone_name" value="{{ zone_name }}"/>
<input type="text" class="form-control hidden" name="rr_list" value="{{ rr_list }}"/>
<div class="form-group">
<div class="col-sm-3"></div>
<div class="col-sm-5 col-md-4">
<button type="submit" class="btn btn-default">Yes, I really want to delete them.</button>
<a href="{% url "zone_list" dns_server=dns_server zone_name=zone_name %}" class="btn btn-warning">Cancel</a>
</div>
</div>
</form>
{% endblock body %}

View File

@ -11,7 +11,7 @@
<input type="hidden" name="dns_server" value="{{ dns_server.hostname }}">
<input type="hidden" name="zone_name" value="{{ zone_name }}">
<table class="table table-condensed table-hover sortable">
<table class="sortable table table-condensed table-hover">
<tr>
<th>Select</th>
<th>Name</th>
@ -19,12 +19,11 @@
<th>Class</th>
<th>Type</th>
<th>Data</th>
<th></th>
<th>Actions</th>
</tr>
{% for current_record in zone_array %}
<tr>
<td><input type="checkbox" id="rr_{{current_record.rr_name}}" name="rr_list" value="{{ current_record.rr_name }}.{{ zone_name }}" /></td>
<td><input type="checkbox" name="rr_list" value="{{ current_record.rr_name }}.{{ zone_name }}" /></td>
<td>{{ current_record.rr_name }}</td>
<td>{{ current_record.rr_ttl }}</td>
<td>{{ current_record.rr_class }}</td>
@ -33,7 +32,9 @@
<td>
<div class="btn-toolbar" style="margin: 0;">
<div class="btn-group">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">Record Actions <span class="caret"></span></button>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Record Actions <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">Edit Record (Coming Soon)</a></li>
{% if current_record.rr_type == "A" %}
@ -45,7 +46,6 @@
</td>
</tr>
{% endfor %}
</table>
<button class="btn btn-danger" type="submit">Delete Selected</button>

View File

@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block pageheader %}{% endblock pageheader %}
{% block pageheader %}Home{% endblock pageheader %}
{% block body %}