Commit 7e90e657 authored by Skia's avatar Skia

Add ajax-select app and improve some templates

parent bfb2dc5f
Pipeline #127 failed with stage
in 2 minutes and 14 seconds
from ajax_select import register, LookupChannel
from core.views.site import search_user
from core.models import User
from counter.models import Product, Counter
@register('users')
class UsersLookup(LookupChannel):
model = User
def get_query(self, q, request):
return search_user(q)
def format_match(self, obj):
return obj.get_mini_item()
def format_item_display(self, item):
return item.get_display_name()
@register('counters')
class CountersLookup(LookupChannel):
model = Counter
def get_query(self, q, request):
return self.model.objects.filter(name__icontains=q)[:50]
def format_item_display(self, item):
return item.name
@register('products')
class ProductsLookup(LookupChannel):
model = Product
def get_query(self, q, request):
print(request.__dict__)
return (self.model.objects.filter(name__icontains=q) | self.model.objects.filter(code__icontains=q))[:50]
def format_item_display(self, item):
return item.name
......@@ -351,6 +351,20 @@ class User(AbstractBaseUser):
def can_be_edited_by(self, user):
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
def get_mini_item(self):
return """
<div class="mini_profile_link" >
<span>
<img src="%s" alt="%s" />
</span>
<em>%s</em>
</a>
""" % (
self.profile_pict.get_download_url() if self.profile_pict else "/static/core/img/na.gif",
_("Profile"),
self.get_display_name(),
)
class AnonymousUser(AuthAnonymousUser):
def __init__(self, request):
......
/* ----------------------------------------------------------------------------------------------------
Super Form Reset
----------------------------------------------------------------------------------------------------*/
form {
margin: 0px auto;
width: 60%;
}
input,
label,
select,
button,
textarea
{
margin: 1px;
border: none;
padding: 1px;
display: inline-block;
vertical-align: middle;
white-space: normal;
background: none;
line-height: 1;
/* Browsers have different default form fonts */
font-size: 13px;
font-family: Arial;
}
label {
min-width: 50%;
}
/* Remove the stupid outer glow in Webkit */
input:focus
{
outline: 0;
}
/* Box Sizing Reset
-----------------------------------------------*/
/* All of our custom controls should be what we expect them to be */
input,
textarea
{
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* These elements are usually rendered a certain way by the browser */
button,
input[type=reset],
input[type=button],
input[type=submit],
input[type=checkbox],
input[type=radio],
select
{
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* Text Inputs
-----------------------------------------------*/
input[type=date],
input[type=datetime],
input[type=datetime-local],
input[type=email],
input[type=month],
input[type=number],
input[type=password],
input[type=range],
input[type=search],
input[type=tel],
input[type=text],
input[type=time],
input[type=url],
input[type=week],
textarea
{
background-color: white;
border: 1px solid black;
padding: 2px;
border-radius: 2px;
}
/* Button Controls
-----------------------------------------------*/
input[type=checkbox],
input[type=radio]
{
width: 13px;
height: 13px;
}
/* File Uploads
-----------------------------------------------*/
input[type=file]
{
}
/* Search Input
-----------------------------------------------*/
/* Make webkit render the search input like a normal text field */
input[type=search]
{
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
}
/* Turn off the recent search for webkit. It adds about 15px padding on the left */
::-webkit-search-decoration
{
display: none;
}
/* Buttons
-----------------------------------------------*/
button,
input[type="reset"],
input[type="button"],
input[type="submit"]
{
/* Fix IE7 display bug */
overflow: visible;
width: auto;
cursor: pointer;
padding: 3px;
border: 1px solid black;
border-radius: 2px;
background-color: white;
}
button:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
input[type="submit"]:hover
{
box-shadow: 0 0 2px #000;
}
button:active,
input[type="reset"]:active,
input[type="button"]:active,
input[type="submit"]:active
{
box-shadow: inset 0 0 2px #000;
}
/* IE8 and FF freak out if this rule is within another selector */
::-webkit-file-upload-button
{
padding: 0;
border: 0;
background: none;
}
/* Textarea
-----------------------------------------------*/
textarea
{
/* Move the label to the top */
vertical-align: top;
/* Turn off scroll bars in IE unless needed */
overflow: auto;
}
/* Selects
-----------------------------------------------*/
select
{
}
select[multiple]
{
/* Move the label to the top */
vertical-align: top;
}
......@@ -160,22 +160,31 @@ tbody>tr:hover {
float: right;
padding: 10px;
}
#user_info_container {
}
#user_info {
float: right;
padding: 5px;
width: 40%;
margin: 0px auto;
background: lightgrey;
}
/*-----------------------------USER PROFILE----------------------------*/
#user_profile {
#user_profile_container {
width: 80%;
margin: 0px auto;
}
#user_profile {
width: 100%;
margin: 0px auto;
padding: 10px;
overflow: auto;
}
#user_profile h4 { border-bottom: 1px solid grey; max-width: 60%; }
#user_profile #pictures {
width: 30%;
max-width: 250px;
max-height: 300px;
float: right;
font-style: italic;
}
......@@ -238,7 +247,14 @@ footer{
text-align: center;
}
/*--------------------------------MODALE-------------------------------*/
/*---------------------------------FORMS-------------------------------*/
form {
margin: 0px auto;
width: 60%;
}
label {
display: block;
}
.choose_file_widget {
display: none;
}
......@@ -247,24 +263,10 @@ footer{
position: absolute;
width: 97%;
}
.form-wrapper {
display: inline-block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #DDD;
opacity: 0.8;
padding: 15px;
border-radius: 4px;
#user_edit * {
text-align: center;
}
.form-wrapper>form {
display: inline-block;
position: absolute;
padding: 15px;
border-radius: 4px;
left: 30%;
top: 40%;
background-color: white;
#user_edit img {
width: 100px;
}
......@@ -4,9 +4,9 @@
{% block head %}
<title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %}</title>
<link rel="stylesheet" href="{{ static('core/base.css') }}">
<link rel="stylesheet" href="{{ static('core/form.css') }}">
<link rel="stylesheet" href="{{ static('core/multiple-select.css') }}">
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}">
<link rel="stylesheet" href="{{ static('ajax_select/css/ajax_select.css') }}">
<link rel="stylesheet" href="{{ static('core/style.css') }}">
{% endblock %}
</head>
......@@ -70,6 +70,7 @@
<script src="{{ static('core/js/jquery-3.1.0.min.js') }}"></script>
<script src="{{ static('core/js/ui/jquery-ui.min.js') }}"></script>
<script src="{{ static('core/js/multiple-select.js') }}"></script>
<script src="{{ static('ajax_select/js/ajax_select.js') }}"></script>
<script src="{{ static('core/js/script.js') }}"></script>
<script>
$('.select_single').multipleSelect({
......
......@@ -14,3 +14,24 @@
<em>{{ user.get_display_name() }}</em>
</a>
{%- endmacro %}
{% macro user_mini_profile(user) %}
<div id="user_profile">
<div id="pictures">
{% if user.profile_pict %}
<img src="{{ user.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
{% endif %}
</div>
<p>{{ user.get_full_name() }}</p>
{% if user.nick_name %}
<p id="nickname">&laquo; {{ user.nick_name }} &raquo;</p>
{% endif %}
{% if user.date_of_birth %}
<p>{% trans %}Born: {% endtrans %}{{ user.date_of_birth|date("d/m/Y") }}</p>
{% endif %}
{% if user.promo %}
<p><img src="{{ static('core/img/promo_%02d.png' % user.promo) }}" alt="Promo {{ user.promo }}" class="promo_pict" />
{% trans %}Promo: {% endtrans %}{{ user.promo }}</p>
{% endif %}
</div>
{%- endmacro %}
......@@ -6,31 +6,33 @@
{% block infos %}
<div id="user_profile">
<div id="pictures">
{% if profile.profile_pict %}
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
<div id="user_profile_container">
<div id="user_profile">
<div id="pictures">
{% if profile.profile_pict %}
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
{% endif %}
<p><em>{{ profile.quote }}</em></p>
</div>
<h4>{{ profile.get_full_name() }}</h4>
{% if profile.nick_name %}
<p id="nickname">&laquo; {{ profile.nick_name }} &raquo;</p>
{% endif %}
{% if profile.date_of_birth %}
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
{% endif %}
{% if profile.department != "NA" %}
<p>{{ profile.department }}{{ profile.semester }}
{% endif %}
{% if profile.dpt_option %}
<br>{% trans %}Option: {% endtrans %}{{ profile.dpt_option }}
{% endif %}
</p>
{% if profile.promo %}
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
{% endif %}
<p><em>{{ profile.quote }}</em></p>
</div>
<h4>{{ profile.get_full_name() }}</h4>
{% if profile.nick_name %}
<p id="nickname">&laquo; {{ profile.nick_name }} &raquo;</p>
{% endif %}
{% if profile.date_of_birth %}
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
{% endif %}
{% if profile.department != "NA" %}
<p>{{ profile.department }}{{ profile.semester }}
{% endif %}
{% if profile.dpt_option %}
<br>{% trans %}Option: {% endtrans %}{{ profile.dpt_option }}
{% endif %}
</p>
{% if profile.promo %}
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
{% endif %}
</div>
{% if user.membership.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %}
......
......@@ -6,19 +6,19 @@
{% block infos %}
<h2>{% trans %}Edit user profile{% endtrans %}</h2>
<form action="" method="post" enctype="multipart/form-data">
<form action="" method="post" enctype="multipart/form-data" id="user_edit">
{% csrf_token %}
{% for field in form %}
<p>{{ field.errors }}<label for="{{ field.name }}">{{ field.label }}
{%- if field.name == "profile_pict" and form.instance.profile_pict -%}
<br>{% trans %}Current profile: {% endtrans %}
<img src="{{ form.instance.profile_pict.get_download_url() }}" title="{% trans %}Profile{% endtrans %}" width="50px" /><br>
<img src="{{ form.instance.profile_pict.get_download_url() }}" title="{% trans %}Profile{% endtrans %}" /><br>
{%- elif field.name == "avatar_pict" and form.instance.avatar_pict -%}
<br>{% trans %}Current avatar: {% endtrans %}
<img src="{{ form.instance.avatar_pict.get_download_url() }}" title="{% trans %}Avatar{% endtrans %}" width="50px" /><br>
<img src="{{ form.instance.avatar_pict.get_download_url() }}" title="{% trans %}Avatar{% endtrans %}" /><br>
{%- elif field.name == "scrub_pict" and form.instance.scrub_pict -%}
<br>{% trans %}Current scrub: {% endtrans %}
<img src="{{ form.instance.scrub_pict.get_download_url() }}" title="{% trans %}Scrub{% endtrans %}" width="50px" /><br>
<img src="{{ form.instance.scrub_pict.get_download_url() }}" title="{% trans %}Scrub{% endtrans %}" /><br>
{%- endif %}</label> {{ field }}</p>
{% endfor %}
<p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p>
......
<div id="user_profile">
<div id="pictures">
{% if profile.profile_pict %}
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" />
{% endif %}
</div>
<p>{{ profile.get_full_name() }}</p>
{% if profile.nick_name %}
<p id="nickname">&laquo; {{ profile.nick_name }} &raquo;</p>
{% endif %}
{% if profile.date_of_birth %}
<p>{% trans %}Born: {% endtrans %}{{ profile.date_of_birth|date("d/m/Y") }}</p>
{% endif %}
{% if profile.promo %}
<p><img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" class="promo_pict" />
{% trans %}Promo: {% endtrans %}{{ profile.promo }}</p>
{% endif %}
</div>
{% from "core/macros.jinja" import user_mini_profile %}
{{ user_mini_profile(profile) }}
......@@ -6,6 +6,7 @@ urlpatterns = [
url(r'^$', index, name='index'),
url(r'^search/$', search_view, name='search'),
url(r'^search_json/$', search_json, name='search_json'),
url(r'^search_user/$', search_user_json, name='search_user'),
# Login and co
url(r'^login/$', login, name='login'),
......
......@@ -52,6 +52,20 @@ class SelectFile(TextInput):
output += '<span name="' + name + '" class="choose_file_button">' + _("Choose file") + '</span>'
return output
class SelectUser(TextInput):
def render(self, name, value, attrs=None):
if attrs:
attrs['class'] = "select_user"
else:
attrs = {'class': "select_user"}
output = '%(content)s<div name="%(name)s" class="choose_user_widget" title="%(title)s"></div>' % {
'content': super(SelectUser, self).render(name, value, attrs),
'title': _("Choose user"),
'name': name,
}
output += '<span name="' + name + '" class="choose_user_button">' + _("Choose user") + '</span>'
return output
# Forms
class RegisteringForm(UserCreationForm):
......
......@@ -15,36 +15,57 @@ from club.models import Club
def index(request, context=None):
return render(request, "core/index.jinja")
def search(query, as_json=False):
result = {'users': None, 'clubs': None}
def search_user(query, as_json=False):
users = []
if query:
exact_nick = User.objects.filter(nick_name__iexact=query).all()
nicks = User.objects.filter(nick_name__icontains=query).exclude(id__in=exact_nick).all()
users = User.objects.filter(Q(first_name__icontains=query) |
Q(last_name__icontains=query)).exclude(id__in=exact_nick).exclude(id__in=nicks).all()
clubs = Club.objects.filter(name__icontains=query).all()
nicks = nicks[:5]
users = users[:5]
clubs = clubs[:5]
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
exact_nick = json.loads(serializers.serialize('json', exact_nick, fields=('nick_name', 'last_name', 'first_name', 'profile_pict')))
nicks = json.loads(serializers.serialize('json', nicks, fields=('nick_name', 'last_name', 'first_name', 'profile_pict')))
users = json.loads(serializers.serialize('json', users, fields=('nick_name', 'last_name', 'first_name', 'profile_pict')))
clubs = json.loads(serializers.serialize('json', clubs, fields=('name')))
else:
exact_nick = list(exact_nick)
nicks = list(nicks)
users = list(users)
users = exact_nick + nicks + users
return users
def search_club(query, as_json=False):
clubs = []
if query:
clubs = Club.objects.filter(name__icontains=query).all()
clubs = clubs[:5]
if as_json: # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
clubs = json.loads(serializers.serialize('json', clubs, fields=('name')))
else:
clubs = list(clubs)
result['users'] = exact_nick + nicks + users
result['clubs'] = clubs
return result
return clubs
@login_required
def search_view(request):
return render(request, "core/search.jinja", context={'result': search(request.GET.get('query', ''))})
result = {
'users': search_user(request.GET.get('query', '')),
'clubs': search_club(request.GET.get('query', '')),
}
return render(request, "core/search.jinja", context={'result': result})
@login_required
def search_user_json(request):
result = {
'users': search_user(request.GET.get('query', ''), True),
}
return JsonResponse(result)
@login_required
def search_json(request):
return JsonResponse(search(request.GET.get('query', ''), True))
result = {
'users': search_user(request.GET.get('query', ''), True),
'clubs': search_club(request.GET.get('query', ''), True),
}
return JsonResponse(result)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('counter', '0013_auto_20160818_1736'),
]
operations = [
migrations.AlterField(
model_name='refilling',
name='date',
field=models.DateTimeField(verbose_name='date'),
),
migrations.AlterField(
model_name='selling',
name='date',
field=models.DateTimeField(verbose_name='date'),
),
]
{% extends "core/base.jinja" %}
{% from "core/macros.jinja" import user_mini_profile %}
{% macro add_product(id, content) %}
<form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}" class="inline" style="display:inline">
......@@ -21,13 +23,12 @@
{% endblock %}
{% block content %}
<h3>{% trans %}Counter{% endtrans %}</h3>
<h4>{{ counter }}</h4>
<p><strong>{% trans %}Club: {% endtrans %}</strong> {{ counter.club }}</p>
<div>
<div id="user_info">
<h5>{% trans %}Customer{% endtrans %}</h5>
<p>{{ customer.user.get_display_name() }}, {{ customer.amount }}</p>
{{ user_mini_profile(customer.user) }}
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }}</p>
</div>
{% if counter.type == 'BAR' %}
<div>
......@@ -48,7 +49,7 @@
<form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
{% csrf_token %}
<input type="hidden" name="action" value="code">
<input type="input" name="code" value="" autofocus />
<input type="input" name="code" value="" autofocus id="code_field"/>
<input type="submit" value="{% trans %}Go{% endtrans %}" />
</form>
<p>{% trans %}Basket: {% endtrans %}</p>
......@@ -79,5 +80,45 @@
{% endblock %}
{% block script %}
{{ super() }}
<script>
$( function() {
var products = [
{% for p in counter.products.all() %}
{
value: "{{ p.code }}",
label: "{{ p.name }}",
tags: "{{ p.code }} {{ p.name }}",
},
{% endfor %}
];
var quantity = "";
var search = "";
var pattern = /^(\d+x)?(.*)/i;
$( "#code_field" ).autocomplete({
select: function (event, ui) {
event.preventDefault();
$("#code_field").val(quantity + ui.item.value);
},
focus: function (event, ui) {
event.preventDefault();
$("#code_field").val(quantity + ui.item.value);
},
source: function( request, response ) {
var res = pattern.exec(request.term);
quantity = res[1] || "";
search = res[2];
var matcher = new RegExp( $.ui.autocomplete.escapeRegex( search ), "i" );
response($.grep( products, function( value ) {
value = value.tags;
return matcher.test( value );
}));
},
});
});
</script>
{% endblock %}
{% extends "core/base.jinja" %}
{% block title %}
{% trans obj=object %}Edit {{ obj }}{% endtrans %}
{% endblock %}
{% block content %}
<h2>{% trans obj=object %}Edit {{ obj }}{% endtrans %}</h2>
<form action="" method="post">
{% csrf_token %}
<p>{{ form.sellers.errors }}<label for="{{ form.sellers.name }}">{{ form.sellers.label }}</label> {{ form.sellers }}</p>
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
<p>{{ form.products.errors }}<label for="{{ form.products.name }}">{{ form.products.label }}</label> {{ form.products }}</p>
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
{% endblock %}
......@@ -10,13 +10,19 @@
<h3>{% trans %}Counter admin list{% endtrans %}</h3>
<ul>
{% for c in counter_list %}
<li>
{% if c.type == "EBOUTIC" %}
<li><a href="{{ url('eboutic:main') }}">{{ c }}</a> -
<a href="{{ url('counter:admin',