Compare commits
131 commits
Qwann/base
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
368285c92d | ||
|
177ecdc541 | ||
|
2a0113412a | ||
|
b43422dc50 | ||
|
cd771c5ba5 | ||
|
51251c0a8f | ||
|
c4e70840ad | ||
|
81eb6bebac | ||
|
9e7937d7d9 | ||
|
40a9ce6531 | ||
|
6cc04e3792 | ||
|
ad02830521 | ||
|
aa06036968 | ||
|
3c848fe47c | ||
|
a0d158ca77 | ||
|
ab4338aa84 | ||
|
a2a14cb84c | ||
|
ff5c0845bc | ||
|
a029987159 | ||
|
72617946eb | ||
|
7a1e6c0be3 | ||
|
7217bd1862 | ||
|
362c7400eb | ||
|
a192765b03 | ||
|
49e147e620 | ||
|
5e9423c858 | ||
|
eb8f82f6c4 | ||
|
41b640a1ea | ||
|
b2dabd1dc4 | ||
|
504044b1cf | ||
|
bd84b46476 | ||
|
c30f588bed | ||
|
74eaf82575 | ||
|
12534ff2da | ||
|
2699573913 | ||
|
7a7254f3da | ||
|
5da4c7050c | ||
|
555fa8fb7c | ||
|
1865054c29 | ||
|
3ebb89c1c6 | ||
|
350836aeb2 | ||
|
d08a39307f | ||
|
d3e1943021 | ||
|
511c3096f7 | ||
|
b1c0dd857d | ||
|
abdd893309 | ||
|
1042b9f9e4 | ||
|
94a629371c | ||
|
a32881a24b | ||
|
7ebee733c5 | ||
|
601ba4d116 | ||
|
6d81735a55 | ||
|
634c4ad4ff | ||
|
727bd10aeb | ||
|
3275de7c9c | ||
|
dd47cc3b07 | ||
|
62330956ec | ||
|
c8717fb06b | ||
|
3c9486a858 | ||
|
906db7069b | ||
|
fc0f861c2c | ||
|
b45d7722a7 | ||
|
7c7adab658 | ||
|
7ebc34d5aa | ||
|
c723f53909 | ||
|
5d39b1018b | ||
|
0ff2f40832 | ||
|
e6d79df735 | ||
|
7690c1a8ab | ||
|
7064e89059 | ||
|
20fad42d6d | ||
|
e8c1ecadcc | ||
|
fb1308e70c | ||
|
617edf8691 | ||
|
fd4567b531 | ||
|
df0d5b4351 | ||
|
44c003ea7d | ||
|
573df9e3f7 | ||
|
57e3feb414 | ||
|
8dee36f928 | ||
|
6e993c0e27 | ||
|
14728c8513 | ||
|
ec8e289ff8 | ||
|
174e62b316 | ||
|
b063d18cf0 | ||
|
ae0fac4b86 | ||
|
5b2207bd59 | ||
|
70d1d403f6 | ||
|
c8f4ab1a80 | ||
|
4c1a1da64f | ||
|
a5cb99c0c3 | ||
|
f361932590 | ||
|
c8f5df3a13 | ||
|
788a2077e2 | ||
|
41883cb2dd | ||
|
4bfb11bdd3 | ||
|
707ef2292f | ||
|
e5ac994380 | ||
|
747d3419b2 | ||
|
7ab4dcede0 | ||
|
ecdcc0e949 | ||
|
b07cddaf3b | ||
|
7215da1d81 | ||
|
31cde9ca26 | ||
|
e863823457 | ||
|
e6c0a0697e | ||
|
0c32b05a48 | ||
|
6b2ea6eae4 | ||
|
556e59706f | ||
|
f2c65b11e1 | ||
|
0483610e69 | ||
|
0be9e5eb3a | ||
|
123d524eab | ||
|
328f3d7852 | ||
|
3aab76613a | ||
|
10e4393bda | ||
|
94b2529ab6 | ||
|
306ca2bcc8 | ||
|
ce9ee193d8 | ||
|
392e5cf144 | ||
|
c894d35932 | ||
|
cbd5a7db0b | ||
|
e1b4dc1651 | ||
|
0b1641a002 | ||
|
64a979a4ac | ||
|
25df34e57e | ||
|
910d8fe9c0 | ||
|
e2d5e726cd | ||
|
e499281a1d | ||
|
6ece994f1c | ||
|
0093956696 |
186 changed files with 9738 additions and 8965 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,7 +1,8 @@
|
|||
.vagrant/
|
||||
__pycache__
|
||||
venv
|
||||
evenementiel/settings.py
|
||||
poulpe/settings.py
|
||||
.*.swp
|
||||
*.pyc
|
||||
*.sqlite3
|
||||
*.scssc
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'api.apps.APIConfig'
|
7
api/apps.py
Normal file
7
api/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class APIConfig(AppConfig):
|
||||
name = 'api'
|
||||
verbose_name = _("API")
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'communication.apps.CommunicationConfig'
|
|
@ -1,4 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class CommunicationConfig(AppConfig):
|
||||
name = 'communication'
|
||||
verbose_name = _("Communication")
|
||||
|
|
|
@ -20,7 +20,10 @@ class Subscription(models.Model):
|
|||
|
||||
|
||||
class UserSubscription(Subscription):
|
||||
user = models.ForeignKey(User)
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
is_unsub = models.BooleanField(
|
||||
_("désinscription"),
|
||||
default=False
|
||||
|
@ -32,7 +35,10 @@ class UserSubscription(Subscription):
|
|||
|
||||
|
||||
class GroupSubscription(Subscription):
|
||||
group = models.ForeignKey(Group)
|
||||
group = models.ForeignKey(
|
||||
Group,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("souscription en groupe")
|
||||
|
|
1
equipment/__init__.py
Normal file
1
equipment/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
default_app_config = 'equipment.apps.EquipmentConfig'
|
133
equipment/admin.py
Normal file
133
equipment/admin.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
from django.contrib import admin
|
||||
from django import forms
|
||||
|
||||
from .models import Equipment, EquipmentDefault, EquipmentRevision, EquipmentCategory, EquipmentLost, EquipmentAttributeValue, EquipmentAttribute, EquipmentStorage
|
||||
from .fields import IdField, IdWidget
|
||||
|
||||
|
||||
class IdForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'min_value' in kwargs:
|
||||
kwargs.pop('min_value')
|
||||
if 'num_choices' in kwargs:
|
||||
num_choices = kwargs.pop('num_choices')
|
||||
else:
|
||||
num_choices = None
|
||||
super(IdForm, self).__init__(*args, **kwargs)
|
||||
|
||||
for field in self.instance._meta.fields:
|
||||
if isinstance(field, IdField):
|
||||
if num_choices is None:
|
||||
choices = []
|
||||
else:
|
||||
choices = [(k, str(k)) for k in range(1, num_choices+1)]
|
||||
self.fields[field.name].choices = choices
|
||||
self.fields[field.name].widget = IdWidget(choices=self.fields[field.name].choices)
|
||||
|
||||
|
||||
class IdFormset(forms.models.BaseInlineFormSet):
|
||||
def get_form_kwargs(self, index):
|
||||
kwargs = super().get_form_kwargs(index)
|
||||
if self.instance:
|
||||
kwargs["num_choices"] = self.instance.stock
|
||||
return kwargs
|
||||
|
||||
|
||||
class EquipmentRevisionExtraInline(admin.TabularInline):
|
||||
model = EquipmentRevision
|
||||
extra = 0
|
||||
form = IdForm
|
||||
formset = IdFormset
|
||||
classes = ['collapse']
|
||||
|
||||
|
||||
class EquipmentDefaultExtraInline(admin.TabularInline):
|
||||
model = EquipmentDefault
|
||||
extra = 0
|
||||
form = IdForm
|
||||
formset = IdFormset
|
||||
classes = ['collapse']
|
||||
|
||||
|
||||
class EquipmentLostExtraInline(admin.TabularInline):
|
||||
model = EquipmentLost
|
||||
extra = 0
|
||||
form = IdForm
|
||||
formset = IdFormset
|
||||
classes = ['collapse']
|
||||
|
||||
|
||||
class EquipmentAttributeValueInline(admin.TabularInline):
|
||||
model = EquipmentAttributeValue
|
||||
extra = 0
|
||||
|
||||
|
||||
class CategoryAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ['full_name_p']
|
||||
list_display = ['name', 'parent', "full_name_p"]
|
||||
ordering = ['name', 'parent']
|
||||
search_fields = ['name',]
|
||||
autocomplete_fields = ['parent', ]
|
||||
|
||||
class StorageAdmin(admin.ModelAdmin):
|
||||
list_display = ['name']
|
||||
ordering = ['name']
|
||||
search_fields = ['name']
|
||||
|
||||
|
||||
class EquipmentAttributeAdmin(admin.ModelAdmin):
|
||||
list_display = ['name']
|
||||
ordering = ['name']
|
||||
search_fields = ['name',]
|
||||
|
||||
|
||||
class EquipmentAdmin(admin.ModelAdmin):
|
||||
save_as_continue = True
|
||||
save_on_top = True
|
||||
autocomplete_fields = ['category', 'storage', ]
|
||||
readonly_fields = ['full_category_p',
|
||||
'added_at',
|
||||
'modified_at',
|
||||
'stock_aviable_p',
|
||||
'ids_aviable_p',
|
||||
'stock_lost_p',
|
||||
'ids_lost_p',
|
||||
]
|
||||
list_display = ['name', 'stock', 'owner', 'category', 'storage', 'modified_at']
|
||||
fieldsets = (
|
||||
('Général', {
|
||||
'fields': ('name', 'owner', 'stock', )
|
||||
}),
|
||||
('Info stock',
|
||||
{
|
||||
'fields': (
|
||||
'stock_aviable_p',
|
||||
'ids_aviable_p',
|
||||
'stock_lost_p',
|
||||
'ids_lost_p',
|
||||
),
|
||||
}),
|
||||
('Attributs', {
|
||||
'fields': ('category', 'full_category_p', 'storage'),
|
||||
}),
|
||||
('Description', {
|
||||
'fields': ('description', 'added_at', 'modified_at',),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
ordering = ['name', 'owner', 'category']
|
||||
inlines = [EquipmentAttributeValueInline,
|
||||
EquipmentDefaultExtraInline,
|
||||
EquipmentLostExtraInline,
|
||||
EquipmentRevisionExtraInline]
|
||||
search_fields = ['name', 'description',]
|
||||
list_filter = ['owner', 'category', 'storage', ]
|
||||
|
||||
|
||||
admin.site.register(Equipment, EquipmentAdmin)
|
||||
admin.site.register(EquipmentCategory, CategoryAdmin)
|
||||
admin.site.register(EquipmentStorage, StorageAdmin)
|
||||
admin.site.register(EquipmentAttribute, EquipmentAttributeAdmin)
|
|
@ -1,5 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class EquipmentConfig(AppConfig):
|
||||
name = 'equipment'
|
||||
verbose_name = _("Équipement")
|
||||
|
|
77
equipment/fields.py
Normal file
77
equipment/fields.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
from django.db import models
|
||||
from django import forms
|
||||
|
||||
|
||||
#class IdWidget(AdminMultipleChoiceFieldWidget):
|
||||
class IdWidget(forms.widgets.CheckboxSelectMultiple):
|
||||
template_name = 'equipment/widgets/checkbox_select.html'
|
||||
option_template_name = 'equipment/widgets/checkbox_option.html'
|
||||
def __init__(self, *args, **kwargs):
|
||||
# nb_items = kwargs.pop('nb_items')
|
||||
# kwargs['choices'] = list(range(1, nb_items+1))
|
||||
super(IdWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'all': ('css/idwidget.css',)
|
||||
}
|
||||
|
||||
class IdFormField(forms.MultipleChoiceField):
|
||||
#widget = IdWidget
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'min_value' in kwargs:
|
||||
kwargs.pop('min_value')
|
||||
if 'max_value' in kwargs:
|
||||
kwargs.pop('max_value')
|
||||
super(IdFormField, self).__init__(*args, **kwargs)
|
||||
|
||||
class IdField(models.BigIntegerField):
|
||||
def parse_integer(self, n):
|
||||
res = []
|
||||
k = 1
|
||||
while(n > 0):
|
||||
if n & 1:
|
||||
res.append(k)
|
||||
n >>= 1
|
||||
k += 1
|
||||
return res
|
||||
|
||||
def from_db_value(self, value, expression, connection):
|
||||
if value is None:
|
||||
return value
|
||||
return self.parse_integer(value)
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
return self.parse_integer(value)
|
||||
|
||||
def get_prep_value(self, value):
|
||||
res = 0
|
||||
for b in value:
|
||||
res |= 1 << (int(b)-1)
|
||||
return res
|
||||
|
||||
def __init__(self, separator=",", *args, **kwargs):
|
||||
self.separator = separator
|
||||
super(IdField, self).__init__(*args, **kwargs)
|
||||
self.validators = [] # TODO : validateurs pertinents
|
||||
|
||||
def deconstruct(self):
|
||||
name, path, args, kwargs = super(IdField, self).deconstruct()
|
||||
# Only include kwarg if it's not the default
|
||||
if self.separator != ",":
|
||||
kwargs['separator'] = self.separator
|
||||
return name, path, args, kwargs
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
# This is a fairly standard way to set up some defaults
|
||||
# while letting the caller override them.
|
||||
defaults = {'form_class': IdFormField}
|
||||
defaults.update(kwargs)
|
||||
return super(IdField, self).formfield(**defaults)
|
|
@ -1,9 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-12 12:47
|
||||
# Generated by Django 1.11.11 on 2018-08-06 17:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import equipment.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -32,7 +34,7 @@ class Migration(migrations.Migration):
|
|||
name='EquipmentAttribution',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.PositiveSmallIntegerField(verbose_name='quantité attribuée')),
|
||||
('amount', models.BigIntegerField(verbose_name='quantité attribuée')),
|
||||
('remarks', models.TextField(verbose_name="remarques concernant l'attribution")),
|
||||
('activity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='event.Activity')),
|
||||
('equipment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='equipment.Equipment')),
|
||||
|
@ -42,12 +44,45 @@ class Migration(migrations.Migration):
|
|||
'verbose_name_plural': 'attributions de matériel',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentCategory',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='nom')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'catégories',
|
||||
'verbose_name_plural': 'catégories',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentOwner',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='nom')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'propriétaire de matériel',
|
||||
'verbose_name_plural': 'propriétaires de matériel',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentPole',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='nom')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'pôle',
|
||||
'verbose_name_plural': 'pôle',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentRemark',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('remark', models.TextField(verbose_name='remarque sur le matériel')),
|
||||
('amount', models.PositiveSmallIntegerField(verbose_name='quantité concernée')),
|
||||
('ids', equipment.fields.IdField()),
|
||||
('is_broken', models.BooleanField()),
|
||||
('is_lost', models.BooleanField()),
|
||||
('equipment', models.ForeignKey(help_text='Matériel concerné par la remarque', on_delete=django.db.models.deletion.CASCADE, related_name='remarks', to='equipment.Equipment')),
|
||||
|
@ -57,14 +92,43 @@ class Migration(migrations.Migration):
|
|||
'verbose_name_plural': 'remarques sur le matériel',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentRevision',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField(default=datetime.date.today, verbose_name='date')),
|
||||
('remark', models.TextField(verbose_name='remarque sur la révision')),
|
||||
('ids', equipment.fields.IdField()),
|
||||
('equipment', models.ForeignKey(help_text='Matériel concerné par les révisions', on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='equipment.Equipment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'révision de matériel',
|
||||
'verbose_name_plural': 'révisions de matériel',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='activities',
|
||||
field=models.ManyToManyField(related_name='equipment', through='equipment.EquipmentAttribution', to='event.Activity'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentCategory'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='event',
|
||||
field=models.ForeignKey(blank=True, help_text="Si spécifié, l'instance du modèle est spécifique à l'évènement en question.", null=True, on_delete=django.db.models.deletion.CASCADE, to='event.Event', verbose_name='évènement'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='owner',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentOwner'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='pole',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentPole'),
|
||||
),
|
||||
]
|
||||
|
|
49
equipment/migrations/0002_auto_20180807_1658.py
Normal file
49
equipment/migrations/0002_auto_20180807_1658.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-07 16:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import equipment.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EquipmentDefault',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('remark', models.TextField(verbose_name='remarque sur le défaut')),
|
||||
('ids', equipment.fields.IdField()),
|
||||
('is_unusable', models.BooleanField(verbose_name='inutilisable')),
|
||||
('send_repare', models.BooleanField(verbose_name='à envoyer réparareur')),
|
||||
('equipment', models.ForeignKey(help_text='Matériel concerné par le defaut', on_delete=django.db.models.deletion.CASCADE, related_name='remarks', to='equipment.Equipment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'defaut matériel',
|
||||
'verbose_name_plural': 'défauts sur le matériel',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentLost',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('lost_at', models.DateField(default=datetime.date.today, verbose_name='perdu le')),
|
||||
('ids', equipment.fields.IdField()),
|
||||
('equipment', models.ForeignKey(help_text='Matériel concerné par la perte', on_delete=django.db.models.deletion.CASCADE, related_name='losts', to='equipment.Equipment')),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='equipmentremark',
|
||||
name='equipment',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EquipmentRemark',
|
||||
),
|
||||
]
|
22
equipment/migrations/0003_auto_20180807_1843.py
Normal file
22
equipment/migrations/0003_auto_20180807_1843.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-07 18:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0002_auto_20180807_1658'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='equipment',
|
||||
name='owner',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EquipmentOwner',
|
||||
),
|
||||
]
|
22
equipment/migrations/0004_equipment_owner.py
Normal file
22
equipment/migrations/0004_equipment_owner.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-07 18:44
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
('equipment', '0003_auto_20180807_1843'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='owner',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.Group'),
|
||||
),
|
||||
]
|
28
equipment/migrations/0005_auto_20180808_1013.py
Normal file
28
equipment/migrations/0005_auto_20180808_1013.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-08 10:13
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0004_equipment_owner'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='equipment',
|
||||
name='pole',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipmentcategory',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentCategory'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='EquipmentPole',
|
||||
),
|
||||
]
|
21
equipment/migrations/0006_auto_20180808_1354.py
Normal file
21
equipment/migrations/0006_auto_20180808_1354.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-08 13:54
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0005_auto_20180808_1013'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipmentcategory',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='merci de ne pas faire de référence cyclique', null=True, on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentCategory'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-08 14:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0006_auto_20180808_1354'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EquipmentAttribute',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, unique=True, verbose_name='nom')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'attribut',
|
||||
'verbose_name_plural': 'attributs',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentAttributeValue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.CharField(max_length=200, verbose_name='valeur')),
|
||||
('attribute', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentAttribute')),
|
||||
('equipment', models.ForeignKey(help_text='Matériel concerné par le defaut', on_delete=django.db.models.deletion.CASCADE, related_name='attributes', to='equipment.Equipment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'attribut de matériel',
|
||||
'verbose_name_plural': 'attributs de matériel',
|
||||
},
|
||||
),
|
||||
]
|
21
equipment/migrations/0008_auto_20180808_1454.py
Normal file
21
equipment/migrations/0008_auto_20180808_1454.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-08 14:54
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0007_equipmentattribute_equipmentattributevalue'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipment',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='equipment.EquipmentCategory'),
|
||||
),
|
||||
]
|
28
equipment/migrations/0009_auto_20180809_1200.py
Normal file
28
equipment/migrations/0009_auto_20180809_1200.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-09 12:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
from django.utils.timezone import utc
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0008_auto_20180808_1454'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='added_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2018, 8, 9, 12, 0, 50, 140250, tzinfo=utc), verbose_name='ajouté le'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='modified_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='dernière modification'),
|
||||
),
|
||||
]
|
20
equipment/migrations/0010_auto_20180809_1211.py
Normal file
20
equipment/migrations/0010_auto_20180809_1211.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-09 12:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0009_auto_20180809_1200'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipment',
|
||||
name='stock',
|
||||
field=models.PositiveSmallIntegerField(verbose_name='quantité totale'),
|
||||
),
|
||||
]
|
20
equipment/migrations/0011_auto_20180809_1405.py
Normal file
20
equipment/migrations/0011_auto_20180809_1405.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-09 14:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0010_auto_20180809_1211'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipment',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, verbose_name='description'),
|
||||
),
|
||||
]
|
61
equipment/migrations/0012_auto_20180820_1124.py
Normal file
61
equipment/migrations/0012_auto_20180820_1124.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-20 11:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0011_auto_20180809_1405'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipment',
|
||||
name='category',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='equipment.EquipmentCategory', verbose_name='catégorie'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipment',
|
||||
name='owner',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.Group', verbose_name='propriétaire'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentattributevalue',
|
||||
name='attribute',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='equipment.EquipmentAttribute', verbose_name='attribut'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentattributevalue',
|
||||
name='equipment',
|
||||
field=models.ForeignKey(help_text='Matériel concerné par le defaut', on_delete=django.db.models.deletion.CASCADE, related_name='attributes', to='equipment.Equipment', verbose_name='matériel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentattribution',
|
||||
name='equipment',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='equipment.Equipment', verbose_name='matériel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentcategory',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='merci de ne pas faire de référence cyclique', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='equipment.EquipmentCategory', verbose_name='parent'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentdefault',
|
||||
name='equipment',
|
||||
field=models.ForeignKey(help_text='Matériel concerné par le defaut', on_delete=django.db.models.deletion.CASCADE, related_name='remarks', to='equipment.Equipment', verbose_name='matériel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentlost',
|
||||
name='equipment',
|
||||
field=models.ForeignKey(help_text='Matériel concerné par la perte', on_delete=django.db.models.deletion.CASCADE, related_name='losts', to='equipment.Equipment', verbose_name='matériel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentrevision',
|
||||
name='equipment',
|
||||
field=models.ForeignKey(help_text='Matériel concerné par les révisions', on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='equipment.Equipment', verbose_name='matériel'),
|
||||
),
|
||||
]
|
20
equipment/migrations/0013_auto_20180826_1949.py
Normal file
20
equipment/migrations/0013_auto_20180826_1949.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-26 17:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0012_auto_20180820_1124'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipmentattribution',
|
||||
name='remarks',
|
||||
field=models.TextField(blank=True, verbose_name="remarques concernant l'attribution"),
|
||||
),
|
||||
]
|
26
equipment/migrations/0014_auto_20180827_0005.py
Normal file
26
equipment/migrations/0014_auto_20180827_0005.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-26 22:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0013_auto_20180826_1949'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='equipment',
|
||||
name='owner',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='auth.Group', verbose_name='propriétaire'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='equipmentcategory',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='merci de ne pas faire de référence cyclique', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='equipment.EquipmentCategory', verbose_name='parent'),
|
||||
),
|
||||
]
|
26
equipment/migrations/0015_auto_20190318_1245.py
Normal file
26
equipment/migrations/0015_auto_20190318_1245.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 2.1.5 on 2019-03-18 11:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0014_auto_20180827_0005'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EquipmentStorage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='nom')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='equipment',
|
||||
name='storage',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='equipment.EquipmentStorage', verbose_name='stockage'),
|
||||
),
|
||||
]
|
17
equipment/migrations/0016_auto_20190318_1247.py
Normal file
17
equipment/migrations/0016_auto_20190318_1247.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 2.1.5 on 2019-03-18 11:47
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('equipment', '0015_auto_20190318_1245'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='equipmentstorage',
|
||||
options={'verbose_name': 'stockage', 'verbose_name_plural': 'stockages'},
|
||||
),
|
||||
]
|
|
@ -1,7 +1,98 @@
|
|||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import Group
|
||||
from event.models import Activity, EventSpecificMixin
|
||||
from django.db.models import Q
|
||||
|
||||
from .fields import IdField
|
||||
|
||||
from datetime import date
|
||||
|
||||
|
||||
class EquipmentStorage(models.Model):
|
||||
name = models.CharField(
|
||||
_("nom"),
|
||||
max_length=200,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("stockage")
|
||||
verbose_name_plural = _("stockages")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class EquipmentCategory(models.Model):
|
||||
name = models.CharField(
|
||||
_("nom"),
|
||||
max_length=200,
|
||||
)
|
||||
parent = models.ForeignKey(
|
||||
'self',
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="children",
|
||||
help_text=_("merci de ne pas faire de référence cyclique"),
|
||||
verbose_name=_("parent"),
|
||||
)
|
||||
|
||||
def has_parent(self, cat):
|
||||
current = self
|
||||
for k in range(100):
|
||||
if current is None:
|
||||
return False
|
||||
if current == cat:
|
||||
return True
|
||||
current = current.parent
|
||||
|
||||
def full_name(self):
|
||||
current = self
|
||||
res = ""
|
||||
for k in range(100):
|
||||
res = "/{current}{old}".format(
|
||||
current=current.name,
|
||||
old=res)
|
||||
if current.parent is None:
|
||||
break
|
||||
current = current.parent
|
||||
return res
|
||||
|
||||
full_name.short_description = _("Chemin complet")
|
||||
full_name_p = property(full_name)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("catégories")
|
||||
verbose_name_plural = _("catégories")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
done = False
|
||||
current = self
|
||||
while not done:
|
||||
if current.parent == self:
|
||||
self.parent = None
|
||||
done = True
|
||||
elif current.parent is None:
|
||||
done = True
|
||||
current = current.parent
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class EquipmentQuerySet(models.QuerySet):
|
||||
def in_category(self, cat):
|
||||
filtre = Q(id__lt=0)
|
||||
childs_id = [c.id for c in EquipmentCategory.objects.all()
|
||||
if c.has_parent(cat)]
|
||||
for pk in childs_id:
|
||||
filtre |= Q(category__id=pk)
|
||||
return self.filter(filtre)
|
||||
|
||||
|
||||
class Equipment(EventSpecificMixin, models.Model):
|
||||
|
@ -9,13 +100,98 @@ class Equipment(EventSpecificMixin, models.Model):
|
|||
_("nom du matériel"),
|
||||
max_length=200,
|
||||
)
|
||||
stock = models.PositiveSmallIntegerField(_("quantité disponible"))
|
||||
description = models.TextField(_("description"))
|
||||
stock = models.PositiveSmallIntegerField(_("quantité totale"))
|
||||
description = models.TextField(
|
||||
_("description"),
|
||||
blank=True,
|
||||
)
|
||||
activities = models.ManyToManyField(
|
||||
Activity,
|
||||
related_name="equipment",
|
||||
through="EquipmentAttribution",
|
||||
)
|
||||
owner = models.ForeignKey(
|
||||
Group,
|
||||
verbose_name=_("propriétaire"),
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
category = models.ForeignKey(
|
||||
EquipmentCategory,
|
||||
verbose_name=_("catégorie"),
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
storage = models.ForeignKey(
|
||||
EquipmentStorage,
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None,
|
||||
verbose_name=_("stockage"),
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
added_at = models.DateTimeField(
|
||||
_("ajouté le"),
|
||||
auto_now_add=True,
|
||||
)
|
||||
modified_at = models.DateTimeField(
|
||||
_("dernière modification"),
|
||||
auto_now=True,
|
||||
)
|
||||
|
||||
objects = EquipmentQuerySet.as_manager()
|
||||
|
||||
def is_in_category(self, cat):
|
||||
current = self.category
|
||||
for k in range(100):
|
||||
if current is None:
|
||||
return False
|
||||
if current == cat:
|
||||
return True
|
||||
current = current.parent
|
||||
|
||||
def ids_aviable(self):
|
||||
if self.stock is None:
|
||||
return []
|
||||
res = list(map(lambda x: x+1, range(self.stock)))
|
||||
for lost in self.losts.all():
|
||||
res = [x
|
||||
for x in res
|
||||
if x not in lost.ids]
|
||||
# TODO cassé
|
||||
# TODO utilisés
|
||||
return res
|
||||
|
||||
def ids_lost(self):
|
||||
res = []
|
||||
for lost in self.losts.all():
|
||||
res = res + [x
|
||||
for x in lost.ids
|
||||
if x not in res]
|
||||
return res
|
||||
|
||||
def stock_aviable(self):
|
||||
aviable = self.ids_aviable()
|
||||
return len(aviable)
|
||||
|
||||
def stock_lost(self):
|
||||
return len(self.ids_lost())
|
||||
|
||||
def full_category(self):
|
||||
return self.category.full_name()
|
||||
|
||||
full_category.short_description = _("Chemin complet")
|
||||
ids_aviable.short_description = _("disponibles")
|
||||
ids_lost.short_description = _("perdus")
|
||||
stock_aviable.short_description = _("quantité disponible")
|
||||
stock_lost.short_description = _("quantité perdue")
|
||||
|
||||
full_category_p = property(full_category)
|
||||
ids_aviable_p = property(ids_aviable)
|
||||
ids_lost_p = property(ids_lost)
|
||||
stock_aviable_p = property(stock_aviable)
|
||||
stock_lost_p = property(stock_lost)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("matériel")
|
||||
|
@ -25,11 +201,63 @@ class Equipment(EventSpecificMixin, models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
class EquipmentAttribute(models.Model):
|
||||
name = models.CharField(
|
||||
_("nom"),
|
||||
max_length=200,
|
||||
unique=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("attribut")
|
||||
verbose_name_plural = _("attributs")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class EquipmentAttributeValue(models.Model):
|
||||
equipment = models.ForeignKey(
|
||||
Equipment,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="attributes",
|
||||
verbose_name=_("matériel"),
|
||||
help_text=_("Matériel concerné par le defaut"),
|
||||
)
|
||||
attribute = models.ForeignKey(
|
||||
EquipmentAttribute,
|
||||
verbose_name=_("attribut"),
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
value = models.CharField(
|
||||
_("valeur"),
|
||||
max_length=200,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("attribut de matériel")
|
||||
verbose_name_plural = _("attributs de matériel")
|
||||
|
||||
def __str__(self):
|
||||
return "{attr}={value}".format(attr=self.attribute.name,
|
||||
value=self.value)
|
||||
|
||||
|
||||
class EquipmentAttribution(models.Model):
|
||||
equipment = models.ForeignKey(Equipment)
|
||||
activity = models.ForeignKey(Activity)
|
||||
amount = models.PositiveSmallIntegerField(_("quantité attribuée"))
|
||||
remarks = models.TextField(_("remarques concernant l'attribution"))
|
||||
equipment = models.ForeignKey(
|
||||
Equipment,
|
||||
verbose_name=_("matériel"),
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
activity = models.ForeignKey(
|
||||
Activity,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
amount = models.BigIntegerField(_("quantité attribuée"))
|
||||
remarks = models.TextField(
|
||||
_("remarques concernant l'attribution"),
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("attribution de matériel")
|
||||
|
@ -37,30 +265,72 @@ class EquipmentAttribution(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return "%s (%d) -> %s" % (self.equipment.name,
|
||||
self.amout,
|
||||
self.amount,
|
||||
self.activity.get_herited('title'))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.equipment.event and self.equipment.event != self.activity.event:
|
||||
if (self.equipment.event
|
||||
and self.equipment.event != self.activity.event):
|
||||
raise ValidationError
|
||||
|
||||
super(EquipmentAttribution, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class EquipmentRemark(models.Model):
|
||||
remark = models.TextField(_("remarque sur le matériel"))
|
||||
class EquipmentDefault(models.Model):
|
||||
remark = models.TextField(_("remarque sur le défaut"))
|
||||
equipment = models.ForeignKey(
|
||||
Equipment,
|
||||
verbose_name=_("matériel"),
|
||||
on_delete=models.CASCADE,
|
||||
related_name="remarks",
|
||||
help_text=_("Matériel concerné par la remarque"),
|
||||
help_text=_("Matériel concerné par le defaut"),
|
||||
)
|
||||
amount = models.PositiveSmallIntegerField(_("quantité concernée"))
|
||||
is_broken = models.BooleanField()
|
||||
is_lost = models.BooleanField()
|
||||
ids = IdField()
|
||||
is_unusable = models.BooleanField(_("inutilisable"))
|
||||
send_repare = models.BooleanField(_("à envoyer réparareur"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("remarque sur matériel")
|
||||
verbose_name_plural = _("remarques sur le matériel")
|
||||
verbose_name = _("defaut matériel")
|
||||
verbose_name_plural = _("défauts sur le matériel")
|
||||
|
||||
def __str__(self):
|
||||
return "%s : %s" % (self.equipment.name,
|
||||
self.remark)
|
||||
|
||||
|
||||
class EquipmentLost(models.Model):
|
||||
lost_at = models.DateField(
|
||||
_("perdu le"),
|
||||
default=date.today,
|
||||
)
|
||||
equipment = models.ForeignKey(
|
||||
Equipment,
|
||||
verbose_name=_("matériel"),
|
||||
on_delete=models.CASCADE,
|
||||
related_name="losts",
|
||||
help_text=_("Matériel concerné par la perte"),
|
||||
)
|
||||
ids = IdField()
|
||||
|
||||
|
||||
class EquipmentRevision(models.Model):
|
||||
date = models.DateField(
|
||||
_("date"),
|
||||
default=date.today,
|
||||
)
|
||||
equipment = models.ForeignKey(
|
||||
Equipment,
|
||||
verbose_name=_("matériel"),
|
||||
on_delete=models.CASCADE,
|
||||
related_name="revisions",
|
||||
help_text=_("Matériel concerné par les révisions"),
|
||||
)
|
||||
remark = models.TextField(_("remarque sur la révision"))
|
||||
ids = IdField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("révision de matériel")
|
||||
verbose_name_plural = _("révisions de matériel")
|
||||
|
||||
def __str__(self):
|
||||
return "%s : %s" % (self.equipment.name,
|
||||
|
|
12
equipment/static/css/idwidget.css
Normal file
12
equipment/static/css/idwidget.css
Normal file
|
@ -0,0 +1,12 @@
|
|||
.nice_select input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nice_select input[type="checkbox"]:checked + label {
|
||||
background: red;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.nice_select ul {
|
||||
display: inline-block;
|
||||
}
|
109
equipment/tables.py
Normal file
109
equipment/tables.py
Normal file
|
@ -0,0 +1,109 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db.models import Q
|
||||
from django.http.request import QueryDict
|
||||
|
||||
import django_filters
|
||||
from django_filters.widgets import LinkWidget
|
||||
from django_tables2.utils import A
|
||||
import django_tables2 as tables
|
||||
|
||||
from .models import Equipment, EquipmentCategory
|
||||
|
||||
|
||||
class EquipmentFilter(django_filters.FilterSet):
|
||||
owner = django_filters.ModelChoiceFilter(
|
||||
field_name='owner',
|
||||
queryset=Group.objects.all(),
|
||||
widget=LinkWidget(),
|
||||
)
|
||||
category = django_filters.ModelChoiceFilter(
|
||||
field_name='category',
|
||||
queryset=EquipmentCategory.objects.all(),
|
||||
widget=LinkWidget(),
|
||||
method='filter_category',
|
||||
)
|
||||
|
||||
def filter_category(self, queryset, name, value):
|
||||
return queryset.in_category(value)
|
||||
|
||||
def get_categories(self, qs):
|
||||
"""
|
||||
rend les catégories qui servent à filtrer les Equipments de qs
|
||||
ie les catégories des equipments et tous leurs parents
|
||||
"""
|
||||
filtre = Q(id__lt=0)
|
||||
for eq in qs:
|
||||
current = eq.category
|
||||
for k in range(100):
|
||||
if current is None:
|
||||
break
|
||||
filtre |= Q(id=current.id)
|
||||
current = current.parent
|
||||
return EquipmentCategory.objects.filter(filtre)
|
||||
|
||||
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
|
||||
# On que les requêtes vides rendent quelque chose
|
||||
if data is None:
|
||||
data = QueryDict('category&owner')
|
||||
super().__init__(data=data, queryset=queryset,
|
||||
request=request, prefix=prefix)
|
||||
if self.queryset is not None:
|
||||
for filter_ in self.filters.values():
|
||||
if filter_.queryset.model == Group:
|
||||
own_ids = [eq.owner.id for eq in self.queryset
|
||||
if eq.owner is not None]
|
||||
filtre = Q(id__lt=0)
|
||||
for own_id in own_ids:
|
||||
filtre |= Q(id=own_id)
|
||||
filter_.queryset = Group.objects.filter(filtre)
|
||||
if filter_.queryset.model == EquipmentCategory:
|
||||
filter_.queryset = self.get_categories(self.queryset)
|
||||
|
||||
class Meta:
|
||||
model = Equipment
|
||||
fields = ['category', 'owner']
|
||||
|
||||
|
||||
class AbstractEquipmentTable(tables.Table):
|
||||
stock_aviable_p = tables.Column(
|
||||
accessor=A('stock_aviable_p'),
|
||||
orderable=False, # TODO le rendre ordorable
|
||||
verbose_name=_("Quantité disponible"),
|
||||
)
|
||||
full_category_p = tables.Column(
|
||||
accessor=A('full_category_p'),
|
||||
order_by=('category'),
|
||||
verbose_name=_("Catégorie"),
|
||||
)
|
||||
name = tables.LinkColumn(
|
||||
'equipment:detail',
|
||||
args=[A('pk')],
|
||||
verbose_name=_("Matériel"),
|
||||
)
|
||||
admin = tables.LinkColumn(
|
||||
'admin:equipment_equipment_change',
|
||||
attrs={
|
||||
'a': {'class': 'glyphicon glyphicon-cog'}
|
||||
},
|
||||
text="",
|
||||
orderable=False,
|
||||
args=[A('pk')],
|
||||
verbose_name=_(""),
|
||||
)
|
||||
|
||||
def before_render(self, request):
|
||||
if (request.user.is_staff and
|
||||
request.user.has_perm('equipment_change_equipment')):
|
||||
self.columns.show('admin')
|
||||
else:
|
||||
self.columns.hide('admin')
|
||||
|
||||
|
||||
class EquipmentTable(AbstractEquipmentTable):
|
||||
class Meta:
|
||||
model = Equipment
|
||||
template_name = 'equipment/tables/bootstrap-responsive.html'
|
||||
fields = ['name', 'stock', 'owner', ]
|
||||
sequence = ['admin', 'name', 'stock', 'stock_aviable_p',
|
||||
'full_category_p', 'owner', ]
|
13
equipment/templates/equipment/detail.html
Normal file
13
equipment/templates/equipment/detail.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "shared/base.html" %}
|
||||
{% load i18n staticfiles %}
|
||||
|
||||
{% block title %}{% trans "Matériel" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ equipment.name }}<a class="pull-right glyphicon glyphicon-cog" href="{% url 'admin:equipment_equipment_change' equipment.id %}"></a></h2>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block aside %}
|
||||
Coucou :)
|
||||
{% endblock %}
|
50
equipment/templates/equipment/home.html
Normal file
50
equipment/templates/equipment/home.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
{% extends "shared/base.html" %}
|
||||
{% load i18n staticfiles %}
|
||||
|
||||
{% block title %}{% trans "Matériel" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="equipment">{% trans "Inventaire" %}</h1>
|
||||
<div class="module-list">
|
||||
<a href="{% url 'equipment:list' %}" class="module equipment">
|
||||
<span class="glyphicon glyphicon-list-alt"></span>
|
||||
{% trans "Tout le matériel" %}
|
||||
</a>
|
||||
<a href="#TODO" class="module equipment">
|
||||
<span class="glyphicon glyphicon-list-alt"></span>
|
||||
{% trans "Disponible" %}
|
||||
</a>
|
||||
</div>
|
||||
<h2 class="staff">{% trans "Liste par Propriétaire" %}</h2>
|
||||
<div class="module-list">
|
||||
{% for owner in owners %}
|
||||
<a href="{% url 'equipment:list_by_owner' owner.id %}" class="module staff">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
{{ owner.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<h2 class="equipment">{% trans "Liste par Catégorie" %}</h2>
|
||||
<div class="tree">
|
||||
<ul>
|
||||
{% for node in root_cat %}
|
||||
{% include "equipment/tree_cat.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block aside %}
|
||||
<div class="heading">
|
||||
{{ nb_type }} <span class="sub">{% trans "référence" %}{{ nb_type|pluralize}}</span>
|
||||
</div>
|
||||
<div class="heading separator">
|
||||
{{ stock }} <span class="sub">{% trans "item" %}{{ stock|pluralize}}</span>
|
||||
</div>
|
||||
<div class="heading inverted small">
|
||||
{{ categories.count }} <span class="sub">{% trans "catégorie" %}{{ categories.count|pluralize}}</span>
|
||||
</div>
|
||||
<div class="heading inverted small ">
|
||||
{{ owners.count }} <span class="sub">{% trans "propriéaire" %}{{ owners.count|pluralize}}</span>
|
||||
</div>
|
||||
{% endblock %}
|
30
equipment/templates/equipment/list.html
Normal file
30
equipment/templates/equipment/list.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% extends "shared/base.html" %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n staticfiles %}
|
||||
|
||||
{% block title %}{% trans "Matériel" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="equipment">Inventaire</h1>
|
||||
{% if subtitle %}
|
||||
<h2 class="equipment">{{ subtitle }}</h2>
|
||||
{% endif %}
|
||||
{% render_table table %}
|
||||
{% endblock %}
|
||||
|
||||
{% block aside %}
|
||||
<div class="heading">
|
||||
{{ nb_type }} <span class="sub">{% trans "référence" %}{{ nb_type|pluralize}}</span>
|
||||
</div>
|
||||
<div class="heading separator">
|
||||
{{ stock }} <span class="sub">{% trans "item" %}{{ stock|pluralize}}</span>
|
||||
</div>
|
||||
{% if filter %}
|
||||
<div class="text inverted">
|
||||
<form id="filter_form" action="" method="get" class="form form-inline">
|
||||
{% bootstrap_form filter.form layout='horizontal' size='lg' %}
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
{% extends 'equipment/tables/bootstrap.html' %}
|
||||
|
||||
{% block table-wrapper %}
|
||||
<div class="table-container table-responsive">
|
||||
{% block table %}
|
||||
{{ block.super }}
|
||||
{% endblock table %}
|
||||
|
||||
{% if table.page and table.paginator.num_pages > 1 %}
|
||||
{% block pagination %}
|
||||
{{ block.super }}
|
||||
{% endblock pagination %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock table-wrapper %}
|
||||
|
103
equipment/templates/equipment/tables/bootstrap.html
Normal file
103
equipment/templates/equipment/tables/bootstrap.html
Normal file
|
@ -0,0 +1,103 @@
|
|||
{% load django_tables2 %}
|
||||
{% load i18n %}
|
||||
{% block table-wrapper %}
|
||||
<div class="table-container">
|
||||
{% block table %}
|
||||
<table {% render_attrs table.attrs class="table table-striped" %}>
|
||||
{% block table.thead %}
|
||||
{% if table.show_header %}
|
||||
<thead>
|
||||
<tr>
|
||||
{% for column in table.columns %}
|
||||
<th {{ column.attrs.th.as_html }}>
|
||||
{% if column.orderable %}
|
||||
<a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
|
||||
{% else %}
|
||||
{{ column.header }}
|
||||
{% endif %}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
{% endif %}
|
||||
{% endblock table.thead %}
|
||||
{% block table.tbody %}
|
||||
<tbody>
|
||||
{% for row in table.paginated_rows %}
|
||||
{% block table.tbody.row %}
|
||||
<tr {{ row.attrs.as_html }}>
|
||||
{% for column, cell in row.items %}
|
||||
<td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endblock table.tbody.row %}
|
||||
{% empty %}
|
||||
{% if table.empty_text %}
|
||||
{% block table.tbody.empty_text %}
|
||||
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
|
||||
{% endblock table.tbody.empty_text %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endblock table.tbody %}
|
||||
{% block table.tfoot %}
|
||||
{% if table.has_footer %}
|
||||
<tfoot>
|
||||
<tr>
|
||||
{% for column in table.columns %}
|
||||
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
{% endblock table.tfoot %}
|
||||
</table>
|
||||
{% endblock table %}
|
||||
|
||||
{% if table.page and table.paginator.num_pages > 1 %}
|
||||
{% block pagination %}
|
||||
<nav aria-label="Table navigation">
|
||||
<ul class="pagination">
|
||||
{% if table.page.has_previous %}
|
||||
{% block pagination.previous %}
|
||||
<li class="previous">
|
||||
<a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
|
||||
<span aria-hidden="true">«</span>
|
||||
{% trans 'previous' %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock pagination.previous %}
|
||||
{% endif %}
|
||||
{% if table.page.has_previous or table.page.has_next %}
|
||||
{% block pagination.range %}
|
||||
{% for p in table.page|table_page_range:table.paginator %}
|
||||
<li {% if p == table.page.number %}class="active"{% endif %}>
|
||||
{% if p == '...' %}
|
||||
<a href="#">{{ p }}</a>
|
||||
{% else %}
|
||||
<a href="{% querystring table.prefixed_page_field=p %}">
|
||||
{{ p }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endblock pagination.range %}
|
||||
{% endif %}
|
||||
|
||||
{% if table.page.has_next %}
|
||||
{% block pagination.next %}
|
||||
<li class="next">
|
||||
<a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
|
||||
{% trans 'next' %}
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endblock pagination.next %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endblock pagination %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock table-wrapper %}
|
||||
|
11
equipment/templates/equipment/tree_cat.html
Normal file
11
equipment/templates/equipment/tree_cat.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<li> <a href="{% url "equipment:list_by_category" node.id %}"><span class="category_node">{{node.name}}</span></a>
|
||||
{%if node.children %}
|
||||
<ul>
|
||||
{%for ch in node.children.all %}
|
||||
{%with node=ch template_name="equipment/tree_cat.html" %}
|
||||
{%include template_name%}
|
||||
{%endwith%}
|
||||
{%endfor%}
|
||||
</ul>
|
||||
{%endif%}
|
||||
</li>
|
|
@ -0,0 +1 @@
|
|||
{% include "django/forms/widgets/input.html" %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{{ widget.label }}</label>
|
|
@ -0,0 +1,5 @@
|
|||
{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }} nice_select"{% endif %} style="display: inline-block">{% for group, options, index in widget.optgroups %}{% if group %}
|
||||
<li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %}
|
||||
<li style="list-style-type: none;">{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %}
|
||||
</ul></li>{% endif %}{% endfor %}
|
||||
</ul>{% endwith %}
|
11
equipment/urls.py
Normal file
11
equipment/urls.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from django.conf.urls import url
|
||||
from .views import EquipmentList, EquipmentView, EquipmentListByCategory, EquipmentListByOwner, EquipmentHome
|
||||
|
||||
app_name = 'equipment'
|
||||
urlpatterns = [
|
||||
url(r'^$', EquipmentHome.as_view(), name='home'),
|
||||
url(r'^all/$', EquipmentList.as_view(), name='list'),
|
||||
url(r'^(?P<pk>[0-9]+)/$', EquipmentView.as_view(), name='detail'),
|
||||
url(r'^c/(?P<pk>[0-9]+)/$', EquipmentListByCategory.as_view(), name='list_by_category'),
|
||||
url(r'^o/(?P<pk>[0-9]+)/$', EquipmentListByOwner.as_view(), name='list_by_owner'),
|
||||
]
|
97
equipment/views.py
Normal file
97
equipment/views.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
from .models import Equipment, EquipmentCategory
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db.models import Sum
|
||||
from django.views.generic import DetailView, ListView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
from django_filters.views import FilterView
|
||||
from django_tables2.views import SingleTableMixin
|
||||
|
||||
from .tables import EquipmentTable, EquipmentFilter
|
||||
|
||||
|
||||
class EquipmentHome(LoginRequiredMixin, ListView):
|
||||
template_name = 'equipment/home.html'
|
||||
context_object_name = 'categories'
|
||||
model = EquipmentCategory
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
# TODO remplacer par les vrais owners
|
||||
context['owners'] = Group.objects.all()
|
||||
categories = (EquipmentCategory.objects.order_by('name')
|
||||
.prefetch_related('children'))
|
||||
context['root_cat'] = categories.filter(parent=None)
|
||||
queryset = Equipment.objects.all()
|
||||
context['stock'] = queryset.aggregate(Sum('stock'))['stock__sum']
|
||||
context['nb_type'] = queryset.count()
|
||||
return context
|
||||
|
||||
|
||||
class EquipmentListAbstract(LoginRequiredMixin, SingleTableMixin,FilterView):
|
||||
table_class = EquipmentTable
|
||||
filterset_class = EquipmentFilter
|
||||
template_name = 'equipment/list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['stock'] = self.queryset.aggregate(Sum('stock'))['stock__sum']
|
||||
context['nb_type'] = self.queryset.count()
|
||||
return context
|
||||
|
||||
|
||||
class EquipmentList(EquipmentListAbstract):
|
||||
def get_queryset(self):
|
||||
self.queryset = Equipment.objects.all()
|
||||
return self.queryset
|
||||
|
||||
|
||||
class EquipmentListByCategory(EquipmentListAbstract):
|
||||
def get_category(self):
|
||||
try:
|
||||
pk = self.kwargs.get('pk')
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
"View %s must be called with an object "
|
||||
"pk in the URLconf." % self.__class__.__name__
|
||||
)
|
||||
return EquipmentCategory.objects.get(id=pk)
|
||||
|
||||
def get_queryset(self):
|
||||
cat = self.get_category()
|
||||
self.queryset = Equipment.objects.all().in_category(cat)
|
||||
return self.queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
cat = self.get_category()
|
||||
context['subtitle'] = "Dans {cat}".format(cat=cat.full_name())
|
||||
return context
|
||||
|
||||
|
||||
class EquipmentListByOwner(EquipmentListAbstract):
|
||||
def get_owner(self):
|
||||
try:
|
||||
pk = self.kwargs.get('pk')
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
"View %s must be called with an object "
|
||||
"pk in the URLconf." % self.__class__.__name__
|
||||
)
|
||||
return Group.objects.get(id=pk)
|
||||
|
||||
def get_queryset(self):
|
||||
owner = self.get_owner()
|
||||
self.queryset = Equipment.objects.filter(owner=owner)
|
||||
return self.queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
owner = self.get_owner()
|
||||
context['subtitle'] = "Matériel de {owner}".format(owner=owner)
|
||||
return context
|
||||
|
||||
|
||||
class EquipmentView(LoginRequiredMixin, DetailView):
|
||||
model = Equipment
|
||||
template_name = 'equipment/detail.html'
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'event.apps.EventConfig'
|
|
@ -1,3 +1,97 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
from .models import Event, Place, ActivityTag, Activity, ActivityTemplate # TODO add me
|
||||
from equipment.models import EquipmentAttribution
|
||||
|
||||
|
||||
class EquipmentAttributionExtraInline(admin.TabularInline):
|
||||
autocomplete_fields = ['equipment', ]
|
||||
model = EquipmentAttribution
|
||||
extra = 0
|
||||
classes = ['collapse']
|
||||
|
||||
|
||||
class EventAdmin(admin.ModelAdmin):
|
||||
list_display = ['title', 'slug', 'beginning_date', 'ending_date']
|
||||
readonly_fields = ['created_by', 'created_at', ]
|
||||
ordering = ['title', 'beginning_date', 'ending_date', ]
|
||||
search_fields = ['title', 'decription', ]
|
||||
list_filter = ['beginning_date', 'ending_date', ]
|
||||
date_hierarchy = 'beginning_date'
|
||||
|
||||
|
||||
class PlaceAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'event', ]
|
||||
ordering = ['name', 'event', ]
|
||||
search_fields = ['name', ]
|
||||
list_filter = ['event', ]
|
||||
|
||||
|
||||
class ActivityTagAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'event', 'is_public', ]
|
||||
ordering = ['name', 'event', 'is_public', ]
|
||||
search_fields = ['name', ]
|
||||
list_filter = ['event', 'is_public', ]
|
||||
|
||||
|
||||
class ActivityTemplateAdmin(admin.ModelAdmin):
|
||||
save_as_continue = True
|
||||
save_on_top = True
|
||||
list_display = ['name', 'title', 'event', 'is_public', ]
|
||||
ordering = ['name', 'title', 'event', 'has_perm', ]
|
||||
search_fields = ['name', 'title', 'description', 'remark', ]
|
||||
list_filter = ['event', 'is_public', 'has_perm', 'tags', ]
|
||||
filter_horizontal = ['tags', 'places', ]
|
||||
fieldsets = (
|
||||
('Identifiant', {
|
||||
'fields': ('name', ),
|
||||
}),
|
||||
('Général', {
|
||||
'fields': ('event', 'title', 'is_public', 'places', ),
|
||||
'description': "Tous ces champs sont héritables (Sauf Évènement)",
|
||||
}),
|
||||
('Permanences', {
|
||||
'fields': ('has_perm', ('min_perm', 'max_perm', ), ),
|
||||
'classes': ('collapse',),
|
||||
'description': "Tous ces champs sont héritables",
|
||||
}),
|
||||
('Descriptions', {
|
||||
'fields': ('description', 'tags', 'remarks', ),
|
||||
'classes': ('collapse',),
|
||||
'description': "Tous ces champs sont héritables",
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
class ActivityAdmin(admin.ModelAdmin):
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_display = ['title', 'event', 'is_public', 'parent', ]
|
||||
ordering = ['title', 'event', 'has_perm', 'parent', ]
|
||||
search_fields = ['title', 'description', 'remark', ]
|
||||
list_filter = ['event', 'is_public', 'has_perm', 'tags', ]
|
||||
filter_horizontal = ['tags', 'places', 'staff', ]
|
||||
inlines = [EquipmentAttributionExtraInline, ]
|
||||
fieldsets = (
|
||||
('Général', {
|
||||
'fields': ('event', 'parent', 'title', 'is_public', 'beginning', 'end', 'places', ),
|
||||
'description': "Tous ces champs sont héritables (sauf parent et Évènement)",
|
||||
}),
|
||||
('Permanences', {
|
||||
'fields': ('has_perm', ('min_perm', 'max_perm', ), 'staff', ),
|
||||
'classes': ('wide',),
|
||||
'description': "Tous ces champs sont héritables (sauf les gens en perm)",
|
||||
}),
|
||||
('Descriptions', {
|
||||
'fields': ('description', 'tags', 'remarks', ),
|
||||
'classes': ('collapse',),
|
||||
'description': "Tous ces champs sont héritables",
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
admin.site.register(Event, EventAdmin)
|
||||
admin.site.register(Place, PlaceAdmin)
|
||||
admin.site.register(ActivityTag, ActivityTagAdmin)
|
||||
admin.site.register(ActivityTemplate, ActivityTemplateAdmin)
|
||||
admin.site.register(Activity, ActivityAdmin)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class EventConfig(AppConfig):
|
||||
name = 'event'
|
||||
verbose_name = _("Évènement")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.4 on 2017-08-12 12:47
|
||||
# Generated by Django 1.11.15 on 2018-08-06 07:51
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -74,8 +74,8 @@ class Migration(migrations.Migration):
|
|||
('slug', models.SlugField(help_text="Seulement des lettres, des chiffres ou les caractères '_' ou '-'.", unique=True, verbose_name='identificateur')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='date de création')),
|
||||
('description', models.TextField(verbose_name='description')),
|
||||
('beginning_date', models.DateTimeField(verbose_name='date de début')),
|
||||
('ending_date', models.DateTimeField(verbose_name='date de fin')),
|
||||
('beginning_date', models.DateTimeField(help_text="date publique de l'évènement", verbose_name='date de début')),
|
||||
('ending_date', models.DateTimeField(help_text="date publique de l'évènement", verbose_name='date de fin')),
|
||||
('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_events', to=settings.AUTH_USER_MODEL, verbose_name='créé par')),
|
||||
],
|
||||
options={
|
||||
|
|
32
event/migrations/0002_auto_20180820_1529.py
Normal file
32
event/migrations/0002_auto_20180820_1529.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.11 on 2018-08-20 15:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('event', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='activitytemplate',
|
||||
name='name',
|
||||
field=models.CharField(default='change_me!', help_text='Ne sera pas affiché', max_length=200, verbose_name='Nom du template'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='activity',
|
||||
name='event',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='event.Event', verbose_name='évènement'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='activitytemplate',
|
||||
name='event',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='event.Event', verbose_name='évènement'),
|
||||
),
|
||||
]
|
113
event/models.py
113
event/models.py
|
@ -1,5 +1,5 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import FieldDoesNotExist, FieldError
|
||||
from django.core.exceptions import FieldDoesNotExist, FieldError, ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
@ -123,7 +123,6 @@ class AbstractActivityTemplate(SubscriptionMixin, models.Model):
|
|||
Event,
|
||||
verbose_name=_("évènement"),
|
||||
on_delete=models.CASCADE,
|
||||
editable=False,
|
||||
)
|
||||
is_public = models.NullBooleanField(
|
||||
_("est public"),
|
||||
|
@ -167,12 +166,34 @@ class AbstractActivityTemplate(SubscriptionMixin, models.Model):
|
|||
|
||||
|
||||
class ActivityTemplate(AbstractActivityTemplate):
|
||||
name = models.CharField(
|
||||
_("Nom du template"),
|
||||
max_length=200,
|
||||
help_text=_("Ne sera pas affiché"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("template activité")
|
||||
verbose_name_plural = _("templates activité")
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
return self.name
|
||||
|
||||
def clean(self):
|
||||
errors = []
|
||||
|
||||
# On clean les nombre de permanents
|
||||
if not self.has_perm:
|
||||
self.max_perm = None
|
||||
self.min_perm = None
|
||||
else:
|
||||
if self.min_perm > self.max_perm:
|
||||
errors.append(ValidationError(
|
||||
_("Nombres de permanents incompatibles"),
|
||||
code='wrong-nb-perm',
|
||||
))
|
||||
if errors != []:
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
class Activity(AbstractActivityTemplate):
|
||||
|
@ -193,6 +214,86 @@ class Activity(AbstractActivityTemplate):
|
|||
beginning = models.DateTimeField(_("heure de début"))
|
||||
end = models.DateTimeField(_("heure de fin"))
|
||||
|
||||
def clean(self):
|
||||
errors = []
|
||||
|
||||
# On clean les nombre de permanents
|
||||
if not self.get_herited('has_perm'):
|
||||
self.max_perm = None
|
||||
self.min_perm = None
|
||||
else:
|
||||
if self.get_herited('min_perm') > self.get_herited('max_perm'):
|
||||
errors.append(ValidationError(
|
||||
_("Nombres de permanents incompatibles"),
|
||||
code='wrong-nb-perm',
|
||||
))
|
||||
|
||||
# On valide l'héritage
|
||||
for f in self._meta.get_fields():
|
||||
try:
|
||||
# On réccupère le field du parent
|
||||
attrname = f.name
|
||||
tpl_field = ActivityTemplate._meta.get_field(attrname)
|
||||
# Peut-être que ce n'est pas un field
|
||||
# concerné par l'héritage
|
||||
except FieldDoesNotExist:
|
||||
continue
|
||||
|
||||
# Y'a certains champs dont on se moque
|
||||
if attrname in ['id', 'staff', 'tags', ]:
|
||||
continue
|
||||
|
||||
# C'est plus compliqué que ça pour les nb_perm
|
||||
if attrname in ['max_perm', 'min_perm', ]:
|
||||
if not self.get_herited('has_perm'):
|
||||
continue
|
||||
|
||||
|
||||
# On a un Many to Many, on lit différement
|
||||
if tpl_field.many_to_many:
|
||||
pass
|
||||
# # On a pas spécifié
|
||||
# if not value.exists():
|
||||
# # On a pas de parent
|
||||
# if self.parent is None:
|
||||
# errors.append(ValidationError(
|
||||
# _("N'hérite pas d'un template, spécifier le champs : %(attr)s"),
|
||||
# code='bad-overriding',
|
||||
# params={'attr': f.verbose_name},
|
||||
# ))
|
||||
# else:
|
||||
# pvalue = getattr(self.parent, attrname)
|
||||
# # On a un parent qui ne dit rien
|
||||
# if not pvalue.exists():
|
||||
# errors.append(ValidationError(
|
||||
# _("Champs non précisé chez le parent, spécifier : %(attr)s"),
|
||||
# code='bad-overriding',
|
||||
# params={'attr': f.verbose_name},
|
||||
# ))
|
||||
else:
|
||||
value = getattr(self, attrname)
|
||||
# On a pas spécifié
|
||||
if value is None:
|
||||
# On a pas de parent
|
||||
if self.parent is None:
|
||||
errors.append(ValidationError(
|
||||
_("N'hérite pas d'un template, spécifier le champs : %(attr)s"),
|
||||
code='bad-overriding',
|
||||
params={'attr': f.verbose_name},
|
||||
))
|
||||
else:
|
||||
pvalue = getattr(self.parent, attrname)
|
||||
# On a un parent qui ne dit rien
|
||||
if pvalue is None:
|
||||
errors.append(ValidationError(
|
||||
_("Champs non précisé chez le parent, spécifier : %(attr)s"),
|
||||
code='bad-overriding',
|
||||
params={'attr': f.verbose_name},
|
||||
))
|
||||
if errors != []:
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
def get_herited(self, attrname):
|
||||
try:
|
||||
tpl_field = ActivityTemplate._meta.get_field(attrname)
|
||||
|
@ -207,9 +308,9 @@ class Activity(AbstractActivityTemplate):
|
|||
if tpl_field.many_to_many:
|
||||
if value.exists():
|
||||
return value
|
||||
else:
|
||||
elif self.parent is not None:
|
||||
return getattr(self.parent, attrname)
|
||||
elif value is None:
|
||||
elif value is None and self.parent is not None:
|
||||
return getattr(self.parent, attrname)
|
||||
else:
|
||||
return value
|
||||
|
@ -219,4 +320,4 @@ class Activity(AbstractActivityTemplate):
|
|||
verbose_name_plural = _("activités")
|
||||
|
||||
def __str__(self):
|
||||
return self.get_herited('title')
|
||||
return self.get_herited('title')
|
||||
|
|
373
event/static/css/calendar.css
Normal file
373
event/static/css/calendar.css
Normal file
|
@ -0,0 +1,373 @@
|
|||
/* Calendar */
|
||||
|
||||
#cal-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
|
||||
min-height: 80vh;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
#cal-container,
|
||||
#cal-container * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Time slots */
|
||||
|
||||
#cal-container .cal-time-slot-container {
|
||||
display: grid;
|
||||
grid-template-rows: 30px auto;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% + 30px + 10px);
|
||||
padding: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot {
|
||||
border-right: 1px solid #EEE;
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot:nth-child(even) {
|
||||
background-color: #F4F4F4;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot-hour {
|
||||
padding: 0 0 0 calc(100% - 1.4rem + 7px);
|
||||
background-color: #F9F9F9;/* #3D3D3D; */
|
||||
border-bottom: 1px solid #AAA;
|
||||
color: #333;
|
||||
font-size: 1.4rem;
|
||||
line-height: 30px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot-hour:nth-child(even) {
|
||||
background-color: #FCFCFC;/* #464646; */
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot-hour:first-child {
|
||||
color: transparent;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot-hour.cal-last-hour {
|
||||
padding: 0;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot.cal-last-hour,
|
||||
#cal-container .cal-time-slot-hour.cal-last-hour {
|
||||
border-right: 1px solid #AAA;
|
||||
}
|
||||
|
||||
#cal-container .cal-time-slot.cal-first-hour,
|
||||
#cal-container .cal-time-slot-hour.cal-first-hour {
|
||||
border-left: 1px solid #AAA;
|
||||
}
|
||||
|
||||
/* Events */
|
||||
|
||||
#cal-container .cal-event-container {
|
||||
display: grid;
|
||||
/* grid-template-columns: repeat(24, 1fr); */
|
||||
/* grid-template-rows: repeat(12, auto); */
|
||||
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#cal-container .cal-event {
|
||||
position: relative;
|
||||
height: 80px;
|
||||
margin: 2px 0;
|
||||
/* padding: 5px; */
|
||||
/* background-color: #EFEFEF; */
|
||||
border-radius: 3px;
|
||||
/* border: 1px solid #CCC; */
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* z-index: 500; */
|
||||
transition: 50ms ease-in;
|
||||
}
|
||||
|
||||
#cal-container .cal-event:hover {
|
||||
/* background-color: #FEDDDD; */
|
||||
}
|
||||
|
||||
#cal-container .cal-event > * {
|
||||
display: none;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#cal-container .cal-event > .cal-event-name,
|
||||
#cal-container .cal-event > .cal-event-location,
|
||||
#cal-container .cal-event > .cal-event-perm-count {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#cal-container .cal-event > .cal-event-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#cal-container .cal-event > .cal-event-location {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#cal-container .cal-event > .cal-event-perm-count {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#cal-container .cal-event:not(.cal-event-subscribed) > .cal-event-perm-count.cal-perms-missing {
|
||||
width: calc(100% - 10px);
|
||||
right: auto;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
background-color: #FFF;
|
||||
border: 2px solid #E44;
|
||||
color: #E44;
|
||||
font-weight: bold;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#cal-container .cal-event.cal-event-subscribed {
|
||||
border-width: 3px;
|
||||
border-color: #000;
|
||||
}
|
||||
|
||||
#cal-container .cal-event.cal-event-subscribed::after {
|
||||
content: "✔";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 1px;
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
/* Event details popup */
|
||||
|
||||
#cal-container .cal-event-details {
|
||||
position: absolute;
|
||||
min-height: 100px;
|
||||
/* min-width: 40%; */
|
||||
max-width: 80%;
|
||||
padding: 20px;
|
||||
background-color: #333;
|
||||
color: #FFF;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.6);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details:after {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-bottom-color: #333;
|
||||
border-width: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details.above-event:after {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-top-color: #333;
|
||||
border-width: 20px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details * {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-close-button {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
font-size: 1.6rem;
|
||||
color: #BBB;
|
||||
transition: 100ms ease-out;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-close-button:hover {
|
||||
background-color: #484848;
|
||||
color: #EFEFEF;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details a,
|
||||
#cal-container .cal-event-details a:hover {
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-name > h3 {
|
||||
margin: 0 0 25px 26px;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
color: #FFF;
|
||||
font-size: 2.5rem;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-name > h3::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0 0 0 10px;
|
||||
vertical-align: middle;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAA90lEQVQoz2WRsS6DURiGn/yaaNqBySpGicFg6iJcAYuuNBaJpJEg6gLcADFIdWR0BZZKS4SBRTe3YFf/Y/j/c3LUe4Zz3vd78+U734uUJ7Ptg7k68NDpoIdyU9V3TzzwRdXt1HCq9pwR510W616oZ8Gwoe6KWFN1ScStogui3sRJ9uxYLd/n6hTuaCy3XHfNWuR6hM+OojBQdSXyJ0cZizwSsMo3kEc+ZCEjxZgxE8j4oBFZhRww8gaff4fEL3UuGfK4uG5L4VLVfvrNCrDJHfd0gSrXQB2AJvu0+Cm8HbXnbGw9seqwWH2z65Wv/8MKcfdVHaZx/wKtOg5kifzQhwAAAABJRU5ErkJggg==);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-name:hover > h3 {
|
||||
margin-right: 26px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #484848;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-name:hover > h3::after {
|
||||
content: "Cliquez pour afficher les détails.";
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 15px;
|
||||
padding: 2px 0 0 0;
|
||||
font-size: 1.2rem;
|
||||
background-image: none;
|
||||
color: #DDD;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-name:hover + .cal-detail-close-button {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details table {
|
||||
margin: 0 auto;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details td.cal-detail-label {
|
||||
padding: 0 10px 10px 0;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details td.cal-detail-value {
|
||||
padding: 0 0 10px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-area {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background-color: #DFDFDF;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-title {
|
||||
display: block;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-area .cal-detail-perm-count {
|
||||
margin: 0 10px 0 0;
|
||||
font-size: 2.5rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-area .cal-detail-perm-count.cal-perms-missing {
|
||||
color: #E44;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-area .cal-detail-perm-count.cal-perms-full {
|
||||
color: #393;
|
||||
}
|
||||
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-area .cal-detail-perm-subscription-switch {
|
||||
margin: 0 0 0 10px;
|
||||
padding: 10px;
|
||||
font-size: 1.8rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-perm-area .cal-detail-perm-nb-missing-perms {
|
||||
margin: 20px 0 0 0;
|
||||
padding: 5px;
|
||||
background-color: #FFF;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
color: #E44;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-description {
|
||||
margin: 20px 0 20px 0;
|
||||
color: #DDD;
|
||||
font-size: 1.8rem;
|
||||
font-style: italic;
|
||||
text-align: justify;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
#cal-container .cal-event-details .cal-detail-tag {
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
border: 1px solid #DDD;
|
||||
}
|
108
event/static/css/tipso.css
Normal file
108
event/static/css/tipso.css
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* Tipso Bubble Styles */
|
||||
.tipso_bubble, .tipso_bubble > .tipso_arrow{
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.tipso_bubble {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
z-index: 9999;
|
||||
}
|
||||
.tipso_style{
|
||||
/* cursor: help; */
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
.tipso_title {
|
||||
padding: 3px 0;
|
||||
border-radius: 6px 6px 0 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
.tipso_content {
|
||||
word-wrap: break-word;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
/* Tipso Bubble size classes - Similar to Foundation's syntax*/
|
||||
.tipso_bubble.tiny {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
.tipso_bubble.small {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.tipso_bubble.default {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.tipso_bubble.large {
|
||||
font-size: 1.2rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tipso_bubble.cal_small {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
/* Tipso Bubble Div */
|
||||
.tipso_bubble > .tipso_arrow{
|
||||
position: absolute;
|
||||
width: 0; height: 0;
|
||||
border: 8px solid;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tipso_bubble.top > .tipso_arrow {
|
||||
border-top-color: #000;
|
||||
border-right-color: transparent;
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
}
|
||||
.tipso_bubble.bottom > .tipso_arrow {
|
||||
border-bottom-color: #000;
|
||||
border-right-color: transparent;
|
||||
border-left-color: transparent;
|
||||
border-top-color: transparent;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
}
|
||||
.tipso_bubble.left > .tipso_arrow {
|
||||
border-left-color: #000;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-right-color: transparent;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.tipso_bubble.right > .tipso_arrow {
|
||||
border-right-color: #000;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
top: 50%;
|
||||
right: 100%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.tipso_bubble .top_right_corner,
|
||||
.tipso_bubble.top_right_corner {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.tipso_bubble .bottom_right_corner,
|
||||
.tipso_bubble.bottom_right_corner {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.tipso_bubble .top_left_corner,
|
||||
.tipso_bubble.top_left_corner {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.tipso_bubble .bottom_left_corner,
|
||||
.tipso_bubble.bottom_left_corner {
|
||||
border-top-right-radius: 0;
|
||||
}
|
1184
event/static/js/calendar.js
Normal file
1184
event/static/js/calendar.js
Normal file
File diff suppressed because it is too large
Load diff
18
event/static/js/enrol_event.js
Normal file
18
event/static/js/enrol_event.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
$(function(){
|
||||
function initEnrolment(elt) {
|
||||
elt = $(elt);
|
||||
elt.find("form.enrolment").on("submit", function() {
|
||||
elt.addClass("sending-request");
|
||||
var form = this;
|
||||
var url = form.action + "?ajax";
|
||||
$.post(url, $(form).serialize(), function(data) {
|
||||
elt.html(data);
|
||||
elt.removeClass("sending-request");
|
||||
initEnrolment(elt);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
$.each($(".activity-summary"), function(i, item) { initEnrolment(item) });
|
||||
});
|
84
event/static/js/interval_coloration.js
Normal file
84
event/static/js/interval_coloration.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Interval graph coloring algorithm, by Twal
|
||||
|
||||
class IntervalColoration {
|
||||
constructor (intervals) {
|
||||
this.intervals = intervals;
|
||||
this.n = this.intervals.length;
|
||||
this.computeInterferenceGraph();
|
||||
this.computePEO();
|
||||
this.computeColoration();
|
||||
}
|
||||
|
||||
computeInterferenceGraph() {
|
||||
this.adj = new Array(this.n);
|
||||
for (let i = 0; i < this.n; ++i) {
|
||||
this.adj[i] = [];
|
||||
}
|
||||
for (let i = 0; i < this.n; ++i) {
|
||||
for (let j = 0; j < i; ++j) {
|
||||
let inti = this.intervals[i];
|
||||
let intj = this.intervals[j];
|
||||
if (inti[0] < intj[1] && intj[0] < inti[1]) {
|
||||
this.adj[i].push(j);
|
||||
this.adj[j].push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Perfect elimination order using Maximum Cardinality Search
|
||||
//Runs in O(n^2), could be optimized in O(n log n)
|
||||
computePEO() {
|
||||
let marked = new Array(this.n);
|
||||
let nbMarkedNeighbor = new Array(this.n);
|
||||
this.perm = new Array(this.n);
|
||||
for (let i = 0; i < this.n; ++i) {
|
||||
marked[i] = false;
|
||||
nbMarkedNeighbor[i] = 0;
|
||||
}
|
||||
for (let k = this.n-1; k >= 0; --k) {
|
||||
let maxi = -1;
|
||||
for (let i = 0; i < this.n; ++i) {
|
||||
if (!marked[i] && (maxi == -1 || nbMarkedNeighbor[i] >= nbMarkedNeighbor[maxi])) {
|
||||
maxi = i;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < this.adj[maxi].length; ++i) {
|
||||
nbMarkedNeighbor[this.adj[maxi][i]] += 1;
|
||||
}
|
||||
this.perm[maxi] = k;
|
||||
marked[maxi] = true;
|
||||
}
|
||||
// console.log(this.perm);
|
||||
}
|
||||
|
||||
computeColoration() {
|
||||
this.colors = new Array(this.n);
|
||||
let isColorUsed = new Array(this.n);
|
||||
for (let i = 0; i < this.n; ++i) {
|
||||
this.colors[i] = -1;
|
||||
isColorUsed[i] = false;
|
||||
}
|
||||
for (let i = 0; i < this.n; ++i) {
|
||||
let ind = this.perm[i];
|
||||
for (let j = 0; j < this.adj[ind].length; ++j) {
|
||||
let neigh = this.adj[ind][j];
|
||||
if (this.colors[neigh] >= 0) {
|
||||
isColorUsed[this.colors[neigh]] = true;
|
||||
}
|
||||
}
|
||||
for (let j = 0; j < this.n; ++j) {
|
||||
if (!isColorUsed[j]) {
|
||||
this.colors[ind] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let j = 0; j < this.adj[ind].length; ++j) {
|
||||
let neigh = this.adj[ind][j];
|
||||
if (this.colors[neigh] >= 0) {
|
||||
isColorUsed[this.colors[neigh]] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
event/static/js/tipso.min.js
vendored
Normal file
1
event/static/js/tipso.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
52
event/templates/event/activity.html
Normal file
52
event/templates/event/activity.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends "shared/base.html" %}
|
||||
{% load i18n staticfiles event_tags %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{% trans "Activity" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ activity.title}}
|
||||
{% if user.is_staff %}
|
||||
<a class='glyphicon glyphicon-cog pull-right' href='{% url "admin:event_activity_change" activity.id %}'></a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% include "event/activity_summary.html" with activity=activity %}
|
||||
|
||||
<h2>Description</h2>
|
||||
<p><strong>Description</strong>{{activity.description|default:" - "}}</p>
|
||||
<p><strong>Remarque (staff)</strong>{{activity.remark|default:" - "}}</p>
|
||||
<button class="collapsible active"><h3>Matériel</h3></button>
|
||||
<div class="content fluid">
|
||||
<table class="table table-responsive table-striped">
|
||||
<tr>
|
||||
<th>Matériel</th>
|
||||
<th>Quantité</th>
|
||||
<th>Propriétaire</th>
|
||||
<th>Remarque</th>
|
||||
</tr>
|
||||
{% for att in attributions %}
|
||||
<tr>
|
||||
<td><a href="{% url 'equipment:detail' att.equipment.id %}">
|
||||
{{ att.equipment }}</a></td>
|
||||
<td>{{ att.amount }}</td>
|
||||
<td>{{ att.equipment.owner }}</td>
|
||||
<td>{{ att.remark }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block aside %}
|
||||
<div class="text">
|
||||
Du
|
||||
<strong>
|
||||
{{ activity.beginning | date:"l d F Y H:i" }}
|
||||
</strong>
|
||||
au
|
||||
<strong>
|
||||
{{ activity.end | date:"l d F Y H:i" }}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
79
event/templates/event/activity_summary.html
Normal file
79
event/templates/event/activity_summary.html
Normal file
|
@ -0,0 +1,79 @@
|
|||
{% load i18n event_tags %}
|
||||
{% with activity|get_herited:'has_perm' as has_perm %}
|
||||
<table class="table table-responsive table-striped">
|
||||
<tr>
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-tree-deciduous"></span>
|
||||
{% with activity|get_herited:'places' as places %}
|
||||
{% if places.all %}
|
||||
<span>{{ places.all |join:", "}}</span>
|
||||
{% else %}
|
||||
<span> - </span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
<td>
|
||||
<strong>public</strong>
|
||||
:
|
||||
{% with activity|get_herited:'is_public' as is_public %}
|
||||
<span class="glyphicon {{ is_public|yesno:"yes glyphicon-ok-sign, no glyphicon-remove-sign, dunno glyphicon-question-sign"}}"></span>
|
||||
{% endwith %}
|
||||
</td>
|
||||
<tr>
|
||||
</tr>
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-duplicate"></span>
|
||||
{% if activity.parent %}
|
||||
<span>{{ activity.parent}}</span>
|
||||
{% else %}
|
||||
<span> - </span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<strong>perm</strong>
|
||||
:
|
||||
<span class="glyphicon {{ has_perm|yesno:"yes glyphicon-ok-sign, no glyphicon-remove-sign, dunno glyphicon-question-sign"}}"></span>
|
||||
</td>
|
||||
<tr>
|
||||
</tr>
|
||||
<td>
|
||||
<span class="glyphicon glyphicon-tag"></span>
|
||||
{% with activity|get_herited:'tags' as tags %}
|
||||
{% if tags.all %}
|
||||
<span>{{ tags.all |join:", "}}</span>
|
||||
{% else %}
|
||||
<span> - </span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
<td>
|
||||
{% if has_perm %}
|
||||
{{ activity|get_herited:'min_perm' }}
|
||||
≤
|
||||
<strong>{{ activity.staff.count }}</strong>
|
||||
≤
|
||||
{{ activity|get_herited:'max_perm' }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% if has_perm %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<strong>En perm : </strong>
|
||||
{% with activity.staff.all as staff %}
|
||||
{% if staff %}
|
||||
<span>{{ staff |join:", "}}</span>
|
||||
{% else %}
|
||||
<span> - </span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
{% enrol_btn activity request.user %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
|
@ -1,25 +1,2 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "shared/base.html" %}
|
||||
|
||||
{% block sidenav %}
|
||||
<div class="centered">
|
||||
<h5 class="centered banner-text">La Nuit 2017</h5>
|
||||
</div>
|
||||
|
||||
<li>
|
||||
<a href="index.html">
|
||||
<i class="fa fa-dashboard"></i>
|
||||
<span>Looool</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sub-menu">
|
||||
<a href="javascript:;" >
|
||||
<i class="fa fa-desktop"></i>
|
||||
<span>Prout</span>
|
||||
</a>
|
||||
<ul class="sub">
|
||||
<li><a href="general.html">Lolilol</a></li>
|
||||
<li><a href="buttons.html">Lorem</a></li>
|
||||
<li><a href="panels.html">Ipsum</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
|
116
event/templates/event/calendar.html
Normal file
116
event/templates/event/calendar.html
Normal file
|
@ -0,0 +1,116 @@
|
|||
{% extends "shared/fluid.html" %}
|
||||
{% load i18n staticfiles event_tags %}
|
||||
|
||||
{% block extra_css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" href="{% static "css/tipso.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/calendar.css" %}">
|
||||
|
||||
<style>
|
||||
#cal-toggle-unsubscribed-events-display {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
left: 30px;
|
||||
border-bottom: 2px solid rgb(150, 50, 50);
|
||||
font-size: 1.6rem;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4),
|
||||
0 1px 1px rgba(150, 50, 50, 0.7);
|
||||
text-shadow: 0 0 7px rgb(150, 50, 50);
|
||||
z-index: 5000;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript" src="{% static "js/tipso.min.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "js/interval_coloration.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "js/calendar.js" %}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(() => {
|
||||
let calendar = new Calendar({
|
||||
startDate: new Date(2018, 10, 30, 8),
|
||||
endDate: new Date(2018, 11, 2, 6),
|
||||
eventDetailURLFormat: "https://cof.ens.fr/poulpe/event/activity/999999",
|
||||
subscriptionURLFormat: "{% url "event:enrol_activity" 999999 %}?ajax=json",
|
||||
csrfToken: $(".planning [name=csrfmiddlewaretoken]").val(),
|
||||
groupEventsByLocation: true
|
||||
});
|
||||
|
||||
// TODO: move this elsewhere
|
||||
// Button to switch between:
|
||||
// - displaying all events (default);
|
||||
// - only displaying events for which the current user is enroled.
|
||||
|
||||
// Create the button
|
||||
let toggleUnsubscribedEventDisplayButton = $("<button>")
|
||||
.attr("type", "button")
|
||||
.attr("id", "cal-toggle-unsubscribed-events-display")
|
||||
.addClass("btn btn-primary")
|
||||
.appendTo(calendar.containerNode);
|
||||
|
||||
// Set/update its label
|
||||
function updateToggleButtonLabel () {
|
||||
if (calendar.onlyDisplaySubscribedEvents) {
|
||||
toggleUnsubscribedEventDisplayButton.html("Afficher toutes les activités");
|
||||
}
|
||||
else {
|
||||
toggleUnsubscribedEventDisplayButton.html("Afficher seulement mes permanences");
|
||||
}
|
||||
}
|
||||
|
||||
updateToggleButtonLabel();
|
||||
|
||||
// Switch between display modes on click
|
||||
toggleUnsubscribedEventDisplayButton.on("click", () => {
|
||||
calendar.toggleEventsNotSubscribedByUser();
|
||||
updateToggleButtonLabel();
|
||||
});
|
||||
|
||||
|
||||
// DEBUG: js console helpers, to be removed
|
||||
console.log(calendar);
|
||||
window["cal"] = calendar;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="planning">
|
||||
{% csrf_token %}
|
||||
{% regroup activities by beginning|date:"Y-m-d" as days_list %}
|
||||
<div class="content fluid" id="cal-container">
|
||||
{% for day in days_list %}
|
||||
{% for activity in day.list %}
|
||||
<div class="cal-event">
|
||||
<span class="cal-event-id">{{ activity.id }}</span>
|
||||
<span class="cal-event-name">{{ activity|get_herited:'title' }}</span>
|
||||
<span class="cal-event-start-date">{{ activity.beginning | date:"j/m/Y H:i" }}</span>
|
||||
<span class="cal-event-end-date">{{ activity.end | date:"j/m/Y H:i" }}</span>
|
||||
{% with activity|get_herited:'places' as places %}
|
||||
<span class="cal-event-location">{{ places.all | join:", " }}</span>
|
||||
{% endwith %}
|
||||
<span class="cal-event-description">{{ activity.description }}</span>
|
||||
<span class="cal-event-url">{% url "event:activity" activity.id %}</span>
|
||||
|
||||
{% if activity|get_herited:'has_perm' %}
|
||||
<span class="cal-event-has-perms">1</span>
|
||||
<span class="cal-event-min-nb-perms">{{ activity|get_herited:'min_perm' }}</span>
|
||||
<span class="cal-event-max-nb-perms">{{ activity|get_herited:'max_perm' }}</span>
|
||||
<span class="cal-event-nb-perms">{{ activity.staff.count }}</span>
|
||||
<span class="cal-event-subscribed">{% is_enrolled activity request.user %}</span>
|
||||
{% endif %}
|
||||
|
||||
{% with activity|get_herited:'tags' as tags %}
|
||||
{% for tag in tags.all %}
|
||||
<span class="cal-event-tag">{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
101
event/templates/event/event.html
Normal file
101
event/templates/event/event.html
Normal file
|
@ -0,0 +1,101 @@
|
|||
{% extends "shared/base.html" %}
|
||||
{% load i18n staticfiles event_tags %}
|
||||
|
||||
{% block title %}{% trans "Évènement" %}{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript" src="{% static "js/enrol_event.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ event.title}}
|
||||
{% if perms.event.event_can_change and user.is_staff %}
|
||||
<a class='glyphicon glyphicon-cog pull-right' href='{% url "admin:event_event_change" event.id %}'></a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<p>{{ event.description }}</p>
|
||||
<h2>Boîte à outils</h2>
|
||||
<div class="module-list">
|
||||
<a href="#TODO" class="module">
|
||||
<span class="glyphicon glyphicon-duplicate"></span>
|
||||
Templates d'activité
|
||||
</a>
|
||||
<a href="#TODO" class="module">
|
||||
<span class="glyphicon glyphicon-tag"></span>
|
||||
Tags spécifiques
|
||||
</a>
|
||||
<a href="#todo" class="module">
|
||||
<span class="glyphicon glyphicon-tree-deciduous"></span>
|
||||
lieux spécifiques
|
||||
</a>
|
||||
<a href="{% url "event:calendar" event.slug %}" class="module">
|
||||
Calendrier
|
||||
</a>
|
||||
{% if staffuser %}
|
||||
<a href="{% url "event:event" event.slug %}" class="module">
|
||||
Toutes les activités
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url "event:event-staff" event.slug user.username %}" class="module">
|
||||
Mes perms
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if staffuser %}
|
||||
<h2>Perms de {{ staffuser.first_name }} {{ staffuser.last_name }}</h2>
|
||||
{% else %}
|
||||
<h2>Planning</h2>
|
||||
{% endif %}
|
||||
|
||||
<div class="planning">
|
||||
{% regroup activities by beginning|date:"Y-m-d" as days_list %}
|
||||
{% for day in days_list %}
|
||||
{% with day.list|first as f_act %}
|
||||
<button class="collapsible active"><h3>{{ f_act.beginning|date:"l d F" }}</h3></button>
|
||||
<div class="content fluid">
|
||||
{% endwith %}
|
||||
{% for activity in day.list %}
|
||||
<div class="{% cycle "" "inverted" %} activity">
|
||||
<div class="activity-title">
|
||||
<h4>
|
||||
{% if perms.event.activity_can_change and user.is_staff %}
|
||||
<a class='glyphicon glyphicon-cog' href='{% url "admin:event_activity_change" activity.id %}'></a>
|
||||
{% endif %}
|
||||
<a href="{% url "event:activity" activity.id %}">
|
||||
{{ activity|get_herited:'title' }}
|
||||
</a>
|
||||
</h4>
|
||||
<span class="pull-right">
|
||||
de <strong>{{ activity.beginning | time:"H:i" }}</strong>
|
||||
à <strong>{{ activity.end| time:"H:i" }}</strong>
|
||||
</span>
|
||||
<div class="activity-summary">
|
||||
{% include "event/activity_summary.html" with activity=activity %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block aside %}
|
||||
<div class="heading separator">
|
||||
{{ activities.count }} <span class="sub">activité{{ activities.count|pluralize }}</span>
|
||||
</div>
|
||||
<div class="text inverted">
|
||||
<p>Créé le {{ event.created_at | date:"l d F Y à H:i" }} par {{ event.created_by }}</p>
|
||||
<p>
|
||||
Du
|
||||
<strong>
|
||||
{{ event.beginning_date | date:"l d F Y H:i" }}
|
||||
</strong>
|
||||
au
|
||||
<strong>
|
||||
{{ event.ending_date | date:"l d F Y H:i" }}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
6
event/templates/event/tags/enrol_btn.html
Normal file
6
event/templates/event/tags/enrol_btn.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
{% load i18n %}
|
||||
<form method="POST" action="{% url "event:enrol_activity" activity.pk %}" class="enrolment {{ enrolled|yesno:"enrolled,unenrolled" }}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="goal" value="{{ enrolled|yesno:"unenrol,enrol" }}" />
|
||||
{{ enrolled|yesno:_("Inscrit,") }} <input type="submit" value="{{ enrolled|yesno:_("Se désinscrire,S'inscrire") }}" class="btn btn-warning"/>
|
||||
</form>
|
21
event/templatetags/event_tags.py
Normal file
21
event/templatetags/event_tags.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from django import template
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter()
|
||||
def get_herited(activity, attrname):
|
||||
return activity.get_herited(attrname)
|
||||
|
||||
@register.inclusion_tag("event/tags/enrol_btn.html")
|
||||
def enrol_btn(activity, user):
|
||||
return {
|
||||
"enrolled": activity.staff.filter(id=user.id).exists(),
|
||||
"activity": activity,
|
||||
}
|
||||
|
||||
@register.simple_tag
|
||||
def is_enrolled(activity, user):
|
||||
user_is_enrolled = activity.staff.filter(id=user.id).exists()
|
||||
return "1" if user_is_enrolled else "0"
|
|
@ -1,7 +1,14 @@
|
|||
from django.conf.urls import url
|
||||
from event.views import Index
|
||||
from event.views import Index, EventView, EventViewStaff, ActivityView, EnrolActivityView, EventCalendar
|
||||
|
||||
app_name = 'event'
|
||||
urlpatterns = [
|
||||
url(r'^$', Index.as_view(), name='index'),
|
||||
# url(r'^$', Index.as_view(), name='index'),
|
||||
url(r'^(?P<slug>[-\w]+)/$', EventView.as_view(), name='event'),
|
||||
url(r'^(?P<slug>[-\w]+)/s/(?P<username>[-\w]+)/$', EventViewStaff.as_view(), name='event-staff'),
|
||||
url(r'^(?P<slug>[-\w]+)/calendar/$', EventCalendar.as_view(), name='calendar'),
|
||||
url(r'^activity/(?P<pk>[0-9]+)/$', ActivityView.as_view(),
|
||||
name='activity'),
|
||||
url(r'^activity/(?P<pk>[0-9]+)/enrol/$',
|
||||
EnrolActivityView.as_view(), name="enrol_activity"),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,97 @@
|
|||
from django.views.generic import TemplateView
|
||||
from django.views.generic import TemplateView, DetailView, View
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.http import JsonResponse, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
|
||||
from .models import Event, Activity
|
||||
from equipment.models import EquipmentAttribution
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class Index(TemplateView):
|
||||
template_name = "event/index.html"
|
||||
|
||||
|
||||
class EventCalendar(LoginRequiredMixin, DetailView):
|
||||
model = Event
|
||||
template_name = 'event/calendar.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
event = self.object
|
||||
context['activities'] = (Activity.objects.filter(event=event)
|
||||
.order_by('beginning').prefetch_related(
|
||||
'tags', 'places', 'staff', 'parent'))
|
||||
return context
|
||||
|
||||
|
||||
class EventView(LoginRequiredMixin, DetailView):
|
||||
model = Event
|
||||
template_name = 'event/event.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
event = self.object
|
||||
context['activities'] = (Activity.objects.filter(event=event)
|
||||
.order_by('beginning').prefetch_related(
|
||||
'tags', 'places', 'staff', 'parent'))
|
||||
return context
|
||||
|
||||
|
||||
class EventViewStaff(LoginRequiredMixin, DetailView):
|
||||
model = Event
|
||||
template_name = 'event/event.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user = User.objects.get(username=self.kwargs['username'])
|
||||
event = self.object
|
||||
context['staffuser'] = user
|
||||
context['activities'] = (user.in_perm_activities
|
||||
.filter(event=event)
|
||||
.order_by('beginning')
|
||||
.prefetch_related(
|
||||
'tags', 'places', 'staff', 'parent')
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
class ActivityView(LoginRequiredMixin, DetailView):
|
||||
model = Activity
|
||||
template_name = 'event/activity.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
activity = self.object
|
||||
context['attributions'] = (EquipmentAttribution.objects
|
||||
.filter(activity=activity)
|
||||
.prefetch_related('equipment'))
|
||||
return context
|
||||
|
||||
|
||||
class EnrolActivityView(LoginRequiredMixin, View):
|
||||
http_method_names = ['post']
|
||||
|
||||
def post(self, request, pk, *args, **kwargs):
|
||||
activity = get_object_or_404(Activity, id=pk)
|
||||
action = request.POST.get("goal", None)
|
||||
success = True
|
||||
if action == "enrol":
|
||||
activity.staff.add(request.user)
|
||||
elif action == "unenrol":
|
||||
activity.staff.remove(request.user)
|
||||
else:
|
||||
success = False
|
||||
if "ajax" in request.GET:
|
||||
if request.GET["ajax"] == "json":
|
||||
enrols = activity.staff
|
||||
return JsonResponse({
|
||||
"enrolled": enrols.filter(id=request.user.id).exists(),
|
||||
"number": enrols.count(),
|
||||
})
|
||||
return render(request, "event/activity_summary.html",
|
||||
{"activity": activity})
|
||||
return HttpResponseRedirect(reverse("event:activity", kwargs={"pk":pk}))
|
||||
|
|
|
@ -3,7 +3,7 @@ import os
|
|||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "evenementiel.settings.devlocal")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "poulpe.settings.devlocal")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
|
|
0
poulpe/__init__.py
Normal file
0
poulpe/__init__.py
Normal file
|
@ -2,6 +2,6 @@ import os
|
|||
from channels.asgi import get_channel_layer
|
||||
|
||||
if "DJANGO_SETTINGS_MODULE" not in os.environ:
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "evenementiel.settings")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "poulpe.settings")
|
||||
|
||||
channel_layer = get_channel_layer()
|
|
@ -10,6 +10,8 @@ We also load the secrets in this file.
|
|||
|
||||
import os
|
||||
from . import secret
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib import messages
|
||||
|
||||
|
||||
def import_secret(name):
|
||||
|
@ -25,6 +27,7 @@ def import_secret(name):
|
|||
|
||||
SECRET_KEY = import_secret("SECRET_KEY")
|
||||
ADMINS = import_secret("ADMINS")
|
||||
SERVER_EMAIL = import_secret("SERVER_EMAIL")
|
||||
|
||||
DBNAME = import_secret("DBNAME")
|
||||
DBUSER = import_secret("DBUSER")
|
||||
|
@ -44,31 +47,46 @@ BASE_DIR = os.path.dirname(
|
|||
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'communication.apps.CommunicationConfig',
|
||||
'equipment.apps.EquipmentConfig',
|
||||
'event.apps.EventConfig',
|
||||
'users.apps.UsersConfig',
|
||||
'shared.apps.SharedConfig',
|
||||
# 'shared.apps.CustomAdminConfig',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'channels',
|
||||
'rest_framework',
|
||||
'django.contrib.sites',
|
||||
|
||||
# 'channels',
|
||||
# 'rest_framework',
|
||||
'bootstrapform',
|
||||
'widget_tweaks',
|
||||
'api',
|
||||
'taggit',
|
||||
'django_tables2',
|
||||
'django_filters',
|
||||
'bootstrap3',
|
||||
|
||||
'allauth_ens',
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
'allauth_cas',
|
||||
'allauth_ens.providers.clipper',
|
||||
|
||||
# 'api',
|
||||
'communication',
|
||||
'equipment',
|
||||
'event',
|
||||
'shared',
|
||||
'users',
|
||||
]
|
||||
|
||||
MIDDLEWARE_CLASSES = [
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
# 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
@ -81,7 +99,7 @@ REST_FRAMEWORK = {
|
|||
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
|
||||
}
|
||||
|
||||
ROOT_URLCONF = 'evenementiel.urls'
|
||||
ROOT_URLCONF = 'poulpe.urls'
|
||||
|
||||
STATIC_URL = "/static/"
|
||||
MEDIA_URL = "/media/"
|
||||
|
@ -119,19 +137,19 @@ DATABASES = {
|
|||
}
|
||||
}
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "asgi_redis.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [(
|
||||
"redis://:{passwd}@{host}:{port}/{db}"
|
||||
.format(passwd=REDIS_PASSWD, host=REDIS_HOST,
|
||||
port=REDIS_PORT, db=REDIS_DB)
|
||||
)],
|
||||
},
|
||||
"ROUTING": "evenementiel.routing.channel_routing",
|
||||
}
|
||||
}
|
||||
# CHANNEL_LAYERS = {
|
||||
# "default": {
|
||||
# "BACKEND": "asgi_redis.RedisChannelLayer",
|
||||
# "CONFIG": {
|
||||
# "hosts": [(
|
||||
# "redis://:{passwd}@{host}:{port}/{db}"
|
||||
# .format(passwd=REDIS_PASSWD, host=REDIS_HOST,
|
||||
# port=REDIS_PORT, db=REDIS_DB)
|
||||
# )],
|
||||
# },
|
||||
# "ROUTING": "poulpe.routing.channel_routing",
|
||||
# }
|
||||
# }
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
|
||||
|
@ -148,12 +166,55 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = 'fr-fr'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
TIME_ZONE = 'Europe/Paris'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
)
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
|
||||
CAS_SERVER_URL = "https://cas.eleves.ens.fr/" # SPI CAS
|
||||
CAS_VERIFY_URL = "https://cas.eleves.ens.fr/"
|
||||
CAS_VERSION = "2"
|
||||
CAS_IGNORE_REFERER = True
|
||||
CAS_LOGIN_MSG = None
|
||||
CAS_REDIRECT_URL = reverse_lazy('shared:home')
|
||||
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
|
||||
CAS_FORCE_CHANGE_USERNAME_CASE = "lower"
|
||||
|
||||
LOGIN_URL = reverse_lazy('account_login')
|
||||
LOGOUT_URL = reverse_lazy('account_logout')
|
||||
LOGIN_REDIRECT_URL = reverse_lazy('shared:home')
|
||||
ACCOUNT_HOME_URL = reverse_lazy('shared:home')
|
||||
ACCOUNT_DETAILS_URL = reverse_lazy('shared:home')
|
||||
|
||||
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
# …
|
||||
|
||||
'clipper': {
|
||||
|
||||
# These settings control whether a message containing a link to
|
||||
# disconnect from the CAS server is added when users log out.
|
||||
'MESSAGE_SUGGEST_LOGOUT_ON_LOGOUT': True,
|
||||
'MESSAGE_SUGGEST_LOGOUT_ON_LOGOUT_LEVEL': messages.INFO,
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
ACCOUNT_ADAPTER = 'shared.allauth_adapter.AccountAdapter'
|
||||
#SOCIALACCOUNT_ADAPTER='allauth_ens.adapter.LongTermClipperAccountAdapter'
|
||||
SOCIALACCOUNT_ADAPTER= 'shared.allauth_adapter.SocialAccountAdapter'
|
||||
|
|
@ -11,10 +11,12 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|||
DEBUG = True
|
||||
|
||||
# Add some debugging tools
|
||||
INSTALLED_APPS += ["debug_toolbar", "debug_panel"] # NOQA
|
||||
MIDDLEWARE_CLASSES = (
|
||||
["debug_panel.middleware.DebugPanelMiddleware"]
|
||||
+ MIDDLEWARE_CLASSES # NOQA
|
||||
INSTALLED_APPS += ["debug_toolbar", ] # NOQA
|
||||
MIDDLEWARE = (
|
||||
[
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
]
|
||||
+ MIDDLEWARE # NOQA
|
||||
)
|
||||
|
||||
|
|
@ -18,6 +18,6 @@ DATABASES = {
|
|||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "asgiref.inmemory.ChannelLayer",
|
||||
"ROUTING": "evenementiel.routing.channel_routing",
|
||||
"ROUTING": "poulpe.routing.channel_routing",
|
||||
},
|
||||
}
|
|
@ -5,11 +5,23 @@ from django.conf import settings
|
|||
from django.conf.urls import url, include
|
||||
from django.contrib import admin
|
||||
|
||||
#from django_cas_ng import views as django_cas_views
|
||||
from allauth_ens.views import capture_login, capture_logout
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/login/$', capture_login),
|
||||
url(r'^admin/logout/$', capture_logout),
|
||||
url(r'^compte/', include('allauth.urls')),
|
||||
# Admin
|
||||
url(r'^admin/', admin.site.urls),
|
||||
# Apps
|
||||
url(r'^equipment/', include('equipment.urls')),
|
||||
url(r'^event/', include('event.urls')),
|
||||
url(r'^user/', include('users.urls')),
|
||||
#url(r'^user/', include('users.urls')),
|
||||
# REST
|
||||
url(r'^api/', include('api.urls')),
|
||||
# Reste
|
||||
url(r'^', include('shared.urls')),
|
||||
]
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
WSGI config for evenementiel project.
|
||||
WSGI config for GestionEvenementiel project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
|
@ -11,6 +11,6 @@ import os
|
|||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "evenementiel.settings")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "poulpe.settings")
|
||||
|
||||
application = get_wsgi_application()
|
|
@ -10,7 +10,7 @@ DBPASSWD="4KZt3nGPLVeWSvtBZPsd9jdssdJMds78"
|
|||
REDIS_PASSWD="dummy"
|
||||
|
||||
# It is used in quite a few places
|
||||
SETTINGS="evenementiel.settings.dev"
|
||||
SETTINGS="poulpe.settings.dev"
|
||||
|
||||
# Fills a "templated file" with the information specified in the variables above
|
||||
# e.g. every occurrence of {{DBUSER}} in the file will be replaced by the value
|
||||
|
@ -90,9 +90,9 @@ redis-cli -a $REDIS_PASSWD CONFIG REWRITE
|
|||
cd /vagrant
|
||||
|
||||
# Setup the secrets
|
||||
sudo -H -u vagrant cp evenementiel/settings/secret_example.py \
|
||||
evenementiel/settings/secret.py
|
||||
fill_template evenementiel/settings/secret.py
|
||||
sudo -H -u vagrant cp poulpe/settings/secret_example.py \
|
||||
poulpe/settings/secret.py
|
||||
fill_template poulpe/settings/secret.py
|
||||
|
||||
# Run the usual django admin commands
|
||||
function venv_python {
|
||||
|
|
|
@ -10,7 +10,7 @@ TimeoutSec=300
|
|||
WorkingDirectory=/vagrant
|
||||
Environment="DJANGO_SETTINGS_MODULE={{SETTINGS}}"
|
||||
ExecStart=/home/vagrant/venv/bin/daphne -u /srv/GE/GE.sock \
|
||||
evenementiel.asgi:channel_layer
|
||||
poulpe.asgi:channel_layer
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
-r requirements.txt
|
||||
django-debug-toolbar
|
||||
django-debug-panel
|
||||
ipython
|
||||
|
|
6
requirements-prod.txt
Normal file
6
requirements-prod.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
-r requirements.txt
|
||||
|
||||
# Production specific
|
||||
daphne==1.3.0
|
||||
psycopg2
|
||||
gunicorn
|
|
@ -1,14 +1,14 @@
|
|||
Django==1.11.*
|
||||
psycopg2
|
||||
asgi-redis
|
||||
Pillow
|
||||
channels
|
||||
django-bootstrap-form==3.2.1
|
||||
django-widget-tweaks
|
||||
djangorestframework==3.6.3
|
||||
asgi-redis==1.3.0
|
||||
asgiref==1.1.1
|
||||
django-bootstrap3==10.0.1
|
||||
channels==1.1.5
|
||||
Django==2.1
|
||||
django-tables2==2.0.0a2
|
||||
django-filter==2.0.0
|
||||
git+https://git.eleves.ens.fr/klub-dev-ens/django-allauth-ens.git@1.1.3
|
||||
django-bootstrap-form==3.4
|
||||
drf-nested-routers==0.90.0
|
||||
django-notifications
|
||||
django-contrib-comments
|
||||
|
||||
# Production specific
|
||||
daphne
|
||||
django-notifications==0.1.dev0
|
||||
django-contrib-comments==1.8.0
|
||||
django-taggit==0.22.2
|
||||
Pillow==5.0.0
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'shared.apps.SharedConfig'
|
|
@ -1,3 +1,34 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.admin import AdminSite
|
||||
from django.contrib.sites.admin import SiteAdmin
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
# Register your models here.
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.admin import UserAdmin, GroupAdmin
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class CustomAdminSite(AdminSite):
|
||||
site_header = "Administration du Poulpe"
|
||||
site_title = "Poulpe"
|
||||
index_title = "Administration"
|
||||
|
||||
def index(self, request, extra_context=None):
|
||||
if extra_context is None:
|
||||
extra_context = {}
|
||||
|
||||
# Move last app to the top of `app_list`.
|
||||
# TODO fournir un bon ordre
|
||||
app_list = self.get_app_list(request)
|
||||
app_list.insert(0, app_list.pop(-1))
|
||||
|
||||
extra_context['app_list'] = app_list
|
||||
return super().index(request, extra_context)
|
||||
|
||||
|
||||
# admin.site = CustomAdminSite(name='admin')
|
||||
admin.site.register(User, UserAdmin)
|
||||
# admin.site.register(Group, GroupAdmin)
|
||||
# admin.site.register(Site, SiteAdmin)
|
||||
|
|
20
shared/allauth_adapter.py
Normal file
20
shared/allauth_adapter.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from allauth.account.adapter import DefaultAccountAdapter
|
||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||
|
||||
|
||||
class AccountAdapter(DefaultAccountAdapter):
|
||||
def is_open_for_signup(self, request):
|
||||
return False
|
||||
|
||||
|
||||
class SocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
def is_open_for_signup(self, request, sociallogin):
|
||||
# sociallogin.account is a SocialAccount instance.
|
||||
# See https://github.com/pennersr/django-allauth/blob/master/allauth/socialaccount/models.py
|
||||
|
||||
if sociallogin.account.provider == 'clipper':
|
||||
return True
|
||||
|
||||
# It returns AccountAdapter.is_open_for_signup().
|
||||
# See https://github.com/pennersr/django-allauth/blob/master/allauth/socialaccount/adapter.py
|
||||
return super().is_open_for_signup(request, sociallogin)
|
|
@ -1,5 +1,10 @@
|
|||
from django.apps import AppConfig
|
||||
from django.contrib.admin.apps import AdminConfig
|
||||
|
||||
|
||||
class SharedConfig(AppConfig):
|
||||
name = 'shared'
|
||||
|
||||
|
||||
class CustomAdminConfig(AdminConfig):
|
||||
default_site = 'shared.admin.CustomAdminSite'
|
||||
|
|
|
@ -11,6 +11,7 @@ class EventSpecificMixin(models.Model):
|
|||
verbose_name=_("évènement"),
|
||||
help_text=_("Si spécifié, l'instance du modèle"
|
||||
"est spécifique à l'évènement en question"),
|
||||
on_delete=models.CASCADE,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
|
25
shared/static/config.rb
Normal file
25
shared/static/config.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
require 'compass/import-once/activate'
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "css"
|
||||
sass_dir = "sass"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "javascripts"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
|
||||
# If you prefer the indented syntax, you might want to regenerate this
|
||||
# project again passing --syntax sass, or you can uncomment this:
|
||||
# preferred_syntax = :sass
|
||||
# and then run:
|
||||
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
|
114
shared/static/css/_dashed-shadows.scss
Normal file
114
shared/static/css/_dashed-shadows.scss
Normal file
|
@ -0,0 +1,114 @@
|
|||
//@import "compass/css3";
|
||||
|
||||
//Variables here:
|
||||
//(alongside with commented suggestions)
|
||||
$foreground-color:#b85b3f;//black;
|
||||
$background-color:#e8e3c7;//white
|
||||
$shadow-color:#ba9186;//$foreground-color;
|
||||
$distance:8px;
|
||||
$cut-distance:3px;//$distance/4;
|
||||
$strips-size:6px; //10px
|
||||
$strips-ratio:50%;//70%
|
||||
$strips-angle:45deg;//90deg;
|
||||
|
||||
//cray stuff yo. be sure to try (if you please)
|
||||
$animate:false;//true
|
||||
$fixed:false;//true
|
||||
|
||||
body{
|
||||
font-family: 'Open Sans Condensed', sans-serif;
|
||||
font-size:85pt;
|
||||
background-color:$background-color;
|
||||
text-align:center;
|
||||
line-height:1.2em;
|
||||
padding-top:70px;
|
||||
}
|
||||
.dashed-shadow{
|
||||
position:relative;
|
||||
top:$distance;
|
||||
left:$distance;
|
||||
display:inline-block;
|
||||
color:$shadow-color;
|
||||
}
|
||||
.dashed-shadow:before{
|
||||
content:" ";
|
||||
display:block;
|
||||
|
||||
position:absolute;
|
||||
$bleeding-horizontal:10px;
|
||||
$bleeding-vertical:0px;
|
||||
top:-$bleeding-vertical - $distance;
|
||||
left:-$bleeding-vertical - $distance;
|
||||
bottom:-$bleeding-horizontal + $distance;
|
||||
right:-$bleeding-horizontal + $distance;
|
||||
z-index:1;
|
||||
$color:$background-color;
|
||||
$size:$strips-ratio/2;
|
||||
$halfSize:$size/2;
|
||||
$p1:$halfSize;
|
||||
$p2:50%-$halfSize;
|
||||
$p3:50%+$halfSize;
|
||||
$p4:100%-$halfSize;
|
||||
$transparent:transparentize($color,1);
|
||||
@include background-image(linear-gradient($strips-angle,$color $p1, $transparent $p1, $transparent $p2,$color $p2, $color $p3, $transparent $p3, $transparent $p4, $color $p4));
|
||||
background-size:$strips-size $strips-size;
|
||||
@if($animate){
|
||||
animation:dash-animation 30s infinite linear;
|
||||
}
|
||||
@if($fixed){
|
||||
background-attachment:fixed;
|
||||
}
|
||||
}
|
||||
.dashed-shadow:hover:before{
|
||||
animation:dash-animation 30s infinite linear;
|
||||
}
|
||||
|
||||
.dashed-shadow:after{
|
||||
z-index:2;
|
||||
content:attr(data-text);
|
||||
position:absolute;
|
||||
left:-$distance;
|
||||
top:-$distance;
|
||||
color:$foreground-color;
|
||||
text-shadow:$cut-distance $cut-distance $background-color;
|
||||
}
|
||||
|
||||
//fancy stuff - just useless fluff, don't mind from here onwards
|
||||
|
||||
.hello{
|
||||
font-family:'Cookie',cursive;
|
||||
font-size:140pt;
|
||||
}
|
||||
.sorta-block{
|
||||
font-size:50pt;
|
||||
line-height:1.1em;
|
||||
@include transform(skew(0,-5deg));
|
||||
z-index:3;
|
||||
position:relative;
|
||||
margin-top:20px;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
.sorta{
|
||||
border-top:4px solid $foreground-color;
|
||||
border-bottom:4px solid $foreground-color;
|
||||
|
||||
text-transform:uppercase;
|
||||
z-index:3;
|
||||
//position:relative;
|
||||
//display:block;
|
||||
//width:300px;
|
||||
font-style:italic;
|
||||
}
|
||||
.hipsterish{
|
||||
font-family: 'Sancreek', cursive;
|
||||
font-size:70pt;
|
||||
}
|
||||
.dashed-shadow-text{
|
||||
font-size:140pt;
|
||||
line-height:0.7em;
|
||||
//left:-10px;
|
||||
}
|
||||
.shadow{
|
||||
font-size:120pt;
|
||||
line-height:0.8em;
|
||||
}
|
18
shared/static/css/_filters.scss
Normal file
18
shared/static/css/_filters.scss
Normal file
|
@ -0,0 +1,18 @@
|
|||
// main: global.scss
|
||||
#filter_form {
|
||||
.form-group {
|
||||
.col-md-3, .col-md-9 {
|
||||
float: none;
|
||||
}
|
||||
ul.form-control {
|
||||
padding-left: 15px;
|
||||
list-style: none;
|
||||
height: auto;
|
||||
|
||||
a.selected {
|
||||
text-decoration: underline;
|
||||
color: darken($main_soft_color, 40%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
122
shared/static/css/_forms.scss
Normal file
122
shared/static/css/_forms.scss
Normal file
|
@ -0,0 +1,122 @@
|
|||
// main: global.scss
|
||||
@mixin active {
|
||||
&:active,
|
||||
&.active {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin hover-focus {
|
||||
&:focus,
|
||||
&.focus,
|
||||
&:hover {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin disabled {
|
||||
&.disabled,
|
||||
&[disabled],
|
||||
fieldset[disabled] & {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dropdown-open {
|
||||
.open > &.dropdown-toggle { @content }
|
||||
}
|
||||
|
||||
@mixin btn-special {
|
||||
/**
|
||||
* This mixin applies content if the button is in at least one of the
|
||||
* following states:
|
||||
*
|
||||
* - hovered,
|
||||
* - focused,
|
||||
* - actived,
|
||||
* - is responsible of an opened dropdown.
|
||||
*
|
||||
* Where possible, state is checked from class attribute and
|
||||
* :pseudo-classes.
|
||||
*
|
||||
* ## Bootstrap compatibility
|
||||
*
|
||||
* If content defines 'color', 'background-color' and 'border', it is safe
|
||||
* to use this mixin with Bootstrap buttons as it will overrides all
|
||||
* Bootstrap color defaults of the previous cases.
|
||||
* To be precise, this covers all special important-like cases of the
|
||||
* Bootstrap mixin 'button-variant' (except the 'disabled' case).
|
||||
*
|
||||
*/
|
||||
@include hover-focus { @content }
|
||||
|
||||
@include active {
|
||||
@content;
|
||||
@include hover-focus { @content }
|
||||
}
|
||||
|
||||
@include dropdown-open {
|
||||
@content;
|
||||
@include hover-focus { @content }
|
||||
}
|
||||
}
|
||||
|
||||
@mixin button-variant-2modes($color, $background-base, $background-special, $border) {
|
||||
/**
|
||||
* This mixins allows defining color-related properties of buttons.
|
||||
*
|
||||
* It sets the following properties:
|
||||
* color: $color, except for disabled-like buttons.
|
||||
* border-color: $border.
|
||||
* background-color: Depending on button state:
|
||||
* - Default, disabled:
|
||||
* $background-base
|
||||
* - Hovered, focused, actived, responsible of an opened dropdown:
|
||||
* (one is sufficent)
|
||||
* $background-special
|
||||
*
|
||||
* ## Bootstrap compatibility
|
||||
*
|
||||
* This mixin can be used to replace colors behaviors of Bootstrap buttons.
|
||||
* Indeed, this mixin aims to replace each definition done by the
|
||||
* 'button-variant' Bootstrap mixin.
|
||||
*
|
||||
*/
|
||||
color: $color;
|
||||
background-color: $background-base;
|
||||
border-color: $border;
|
||||
|
||||
@include btn-special {
|
||||
color: $color;
|
||||
background-color: $background-special;
|
||||
border-color: $border;
|
||||
}
|
||||
|
||||
@include disabled {
|
||||
@include hover-focus {
|
||||
background-color: $background-base;
|
||||
border-color: $border;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: $background-base;
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@include button-variant-2modes($btn-font-color, $btn-bg-base, $btn-bg-special, $btn-border);
|
||||
}
|
||||
|
||||
form#filter_form {
|
||||
.form-group {
|
||||
padding-right:20px;
|
||||
}
|
||||
|
||||
ul.form-control {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
91
shared/static/css/_header.scss
Normal file
91
shared/static/css/_header.scss
Normal file
|
@ -0,0 +1,91 @@
|
|||
// main: global.scss
|
||||
.strong-banner {
|
||||
padding-top : 20px;
|
||||
padding-bottom : 10px ;
|
||||
background-color : $header-background;
|
||||
color: $header-color;
|
||||
}
|
||||
|
||||
|
||||
.navbar-inverse {
|
||||
background-color : $header-background;
|
||||
background-color : transparent ;
|
||||
border-style : none ;
|
||||
.navbar-nav {
|
||||
& > .open > a,
|
||||
& > .open > a:focus,
|
||||
& > .open > a:hover {
|
||||
color: #fff;
|
||||
background-color: $header-second-backgroud;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
border-top: 0px solid transparent ;
|
||||
padding : 0px ;
|
||||
|
||||
/* only < 768px*/
|
||||
background-color : $header-second-backgroud;
|
||||
padding-left: 25px;
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
background-color : transparent;
|
||||
padding-left: 0px;
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.navbar-nav {
|
||||
width: 100%;
|
||||
@media (min-width: 768px) {
|
||||
float : right ;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-inverse {
|
||||
/* BRAND */
|
||||
.navbar-brand {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: white;
|
||||
font-family: $font_brand;
|
||||
font-size: xx-large;
|
||||
border-bottom: 5px solid $underline-brand;
|
||||
}
|
||||
}
|
||||
/* ICONE */
|
||||
.navbar-toggle {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $second_bold_color;
|
||||
border-color: $second_bold_color;
|
||||
}
|
||||
.icon-bar {
|
||||
background-color: $second_white_color;
|
||||
}
|
||||
}
|
||||
/* LINKS */
|
||||
.navbar-nav {
|
||||
& > li > a {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
font-family: $font_nav;
|
||||
font-size: large;
|
||||
color: $second_bold_color;
|
||||
background: transparent;
|
||||
@media (min-width: 768px) {
|
||||
color: $second_white_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
shared/static/css/_messages.scss
Normal file
24
shared/static/css/_messages.scss
Normal file
|
@ -0,0 +1,24 @@
|
|||
// main: global.scss
|
||||
.message-info {
|
||||
color : #31708f;
|
||||
background-color: #d9edf7;
|
||||
border-color : #bce8f1;
|
||||
}
|
||||
.message-success {
|
||||
color : #3c763d;
|
||||
background-color: #dff0d8;
|
||||
border-color : #d6e9c6;
|
||||
}
|
||||
.message-warning {
|
||||
color : #8a6d3b;
|
||||
background-color: #fcf8e3;
|
||||
border-color : #faebcc;
|
||||
}
|
||||
.message-error {
|
||||
color : #a94442;
|
||||
background-color: #f2dede;
|
||||
border-color : #ebccd1;
|
||||
}
|
||||
.alert {
|
||||
margin-bottom: 0;
|
||||
}
|
38
shared/static/css/_tree.scss
Normal file
38
shared/static/css/_tree.scss
Normal file
|
@ -0,0 +1,38 @@
|
|||
// main: global.scss
|
||||
.tree {
|
||||
font-size:large;
|
||||
ul, li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
li::before,
|
||||
li::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
}
|
||||
li::before {
|
||||
border-top: 3px solid $second_soft_color;
|
||||
top: 9px;
|
||||
width: 8px;
|
||||
height: 0;
|
||||
}
|
||||
li::after {
|
||||
border-left: 3px solid $second_soft_color;
|
||||
height: 100%;
|
||||
width: 0px;
|
||||
top: 2px;
|
||||
}
|
||||
ul > li:last-child::after {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.category_node {
|
||||
|
||||
}
|
||||
}
|
99
shared/static/css/_variables.scss
Normal file
99
shared/static/css/_variables.scss
Normal file
|
@ -0,0 +1,99 @@
|
|||
// main: global.scss
|
||||
$main_c7: #375362;
|
||||
$main_c6: #4F778C;
|
||||
$main_c5: #5D8CA6;
|
||||
$main_c3: #BDD2DE;
|
||||
$main_c1: #F0FAFF;
|
||||
|
||||
$neutral_c2 : #F2EDDC;
|
||||
$neutral_c1: #FFFBEF;
|
||||
|
||||
$activity_c8: #4FADB8;
|
||||
$activity_c7: #5ED1DC;
|
||||
$activity_c3: #CAE4E7;
|
||||
|
||||
$event_c8: #3488A6;
|
||||
$event_c7: #3999BA;
|
||||
$event_c3: #AAD5E2;
|
||||
|
||||
$todo_c8: #F19F5D;
|
||||
$todo_c7: #FF9C4D;
|
||||
$todo_c3: #FFDEBC;
|
||||
|
||||
$equipment_c8: #E75571;
|
||||
$equipment_c7: #FF5C79;
|
||||
$equipment_c3: #FECAD6;
|
||||
|
||||
$staff_c8: #3BAD89;
|
||||
$staff_c7: #42C2A2;
|
||||
$staff_c3: #A9E1D7;
|
||||
|
||||
/* LEGACY COLORS*/
|
||||
$main_bold_color: #FF6969;
|
||||
$main_soft_color: #FF9191;
|
||||
$main_white_color: #FFEBEB;
|
||||
|
||||
$second_bold_color: #FFB363;
|
||||
$second_soft_color: #FFC282;
|
||||
$second_white_color: #FFF5EB;
|
||||
|
||||
$third_bold_color: #48B0C7;
|
||||
$third_soft_color: #8FD4E3;
|
||||
$third_white_color: #DCEAED;
|
||||
|
||||
/* COLORS */
|
||||
/* Header */
|
||||
$header-background: $main_c7;
|
||||
$header-second-backgroud: $main_c5;
|
||||
$header-color: white;
|
||||
$underline-brand: $equipment_c7;
|
||||
|
||||
/* Général */
|
||||
$html-background: $neutral_c1;
|
||||
$content-background: $neutral_c1;
|
||||
$content-border: $main_c7;
|
||||
$aside-background: $main_c7;
|
||||
$aside-inverted-background: $main_c5;
|
||||
|
||||
$title_border: $main_c7;
|
||||
|
||||
/* Le reste */
|
||||
$btn-font-color: white;
|
||||
$btn-bg-base: $main_bold_color;
|
||||
$btn-bg-special: $main_soft_color;
|
||||
$btn-border: $main_bold_color;
|
||||
|
||||
$yes_color: #55C487;
|
||||
$no_color: #E36268;
|
||||
$dunno_color: #5599C4;
|
||||
|
||||
/* Titres */
|
||||
$h1_background: $main_c6;
|
||||
$h2_background: $main_c3;
|
||||
$h3_background: $main_c1;
|
||||
|
||||
$h1_background_activity: $activity_c8;
|
||||
$h2_background_activity: $activity_c7;
|
||||
$h3_background_activity: $main_c1;
|
||||
|
||||
$h1_background_event: $event_c8;
|
||||
$h2_background_event: $event_c7;
|
||||
$h3_background_event: $main_c1;
|
||||
|
||||
$h1_background_todo: $todo_c8;
|
||||
$h2_background_todo: $todo_c7;
|
||||
$h3_background_todo: $main_c1;
|
||||
|
||||
$h1_background_equipment: $equipment_c8;
|
||||
$h2_background_equipment: $equipment_c7;
|
||||
$h3_background_equipment: $main_c1;
|
||||
|
||||
$h1_background_staff: $staff_c8;
|
||||
$h2_background_staff: $staff_c7;
|
||||
$h3_background_staff: $main_c1;
|
||||
|
||||
/* FONTS */
|
||||
$font_brand:'Lily Script One', cursive;
|
||||
$font_nav:'Work Sans', cursive;
|
||||
$font_bold:'Capriola', sans-serif;
|
||||
$font_normal:'Saira Semi Condensed', sans-serif;
|
1
shared/static/css/bootstrap.min.css.map
Normal file
1
shared/static/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
1
shared/static/css/global.css
Symbolic link
1
shared/static/css/global.css
Symbolic link
|
@ -0,0 +1 @@
|
|||
global.min.css
|
7
shared/static/css/global.css.map
Normal file
7
shared/static/css/global.css.map
Normal file
File diff suppressed because one or more lines are too long
1
shared/static/css/global.min.css
vendored
Normal file
1
shared/static/css/global.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
416
shared/static/css/global.scss
Normal file
416
shared/static/css/global.scss
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*NE PAS MODIFIER LE FICHIER .CSS, MAIS PLUTÔT LE
|
||||
FICHIER .SCSS */
|
||||
@import '_variables';
|
||||
@import '_messages';
|
||||
@import '_header';
|
||||
@import '_forms';
|
||||
@import '_tree';
|
||||
@import '_filters';
|
||||
//@import '_dashed-shadows';
|
||||
|
||||
/* MISE EN FORME GÉNÉRALE */
|
||||
html {
|
||||
height : 100% ;
|
||||
background-color: $html-background;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: $font_normal;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
#principal {
|
||||
background-color: $html-background;
|
||||
}
|
||||
|
||||
/*MAIN*/
|
||||
main {
|
||||
background-color:$content-background;
|
||||
border-width: 2px;
|
||||
border-color: $content-border;
|
||||
border-style: none;
|
||||
border-collapse: collapse;
|
||||
display: table-cell;
|
||||
margin-top:0px;
|
||||
padding: 0px;
|
||||
|
||||
.fuid, h1, h2, h3 {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
margin-top: -2px;
|
||||
margin-bottom: -2px;
|
||||
margin-left: -17px;
|
||||
margin-right: -17px;
|
||||
color: white;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
h1 {
|
||||
font-family: $font_bold;
|
||||
font-weight: 600;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 15px;
|
||||
|
||||
background-color: $h1_background;
|
||||
&.activity {
|
||||
background-color: $h1_background_activity;
|
||||
}
|
||||
&.event {
|
||||
background-color: $h1_background_event;
|
||||
}
|
||||
&.todo {
|
||||
background-color: $h1_background_todo;
|
||||
}
|
||||
&.equipment {
|
||||
background-color: $h1_background_equipment;
|
||||
}
|
||||
&.staff {
|
||||
background-color: $h1_background_staff;
|
||||
}
|
||||
}
|
||||
h2, h3 {
|
||||
border-bottom : 2px solid $main_c5;
|
||||
padding-bottom : 5px ;
|
||||
padding-top: 10px;
|
||||
font-family: $font_bold;
|
||||
font-weight: 600;
|
||||
font-size: x-large;
|
||||
|
||||
background-color: $h2_background;
|
||||
&.activity {
|
||||
background-color: $h2_background_activity;
|
||||
border-color: $activity_c8;
|
||||
}
|
||||
&.event {
|
||||
background-color: $h2_background_event;
|
||||
border-color: $event_c8;
|
||||
}
|
||||
&.todo {
|
||||
background-color: $h2_background_todo;
|
||||
border-color: $todo_c8;
|
||||
}
|
||||
&.equipment {
|
||||
background-color: $h2_background_equipment;
|
||||
border-color: $equipment_c8;
|
||||
}
|
||||
&.staff {
|
||||
background-color: $h2_background_staff;
|
||||
border-color: $staff_c8;
|
||||
}
|
||||
}
|
||||
h3 {
|
||||
background-color: $h3_background;
|
||||
color: $main_c6;
|
||||
&.activity {
|
||||
background-color: $h3_background_activity;
|
||||
color: $activity_c7;
|
||||
}
|
||||
&.event {
|
||||
background-color: $h3_background_event;
|
||||
color: $event_c7;
|
||||
}
|
||||
&.todo {
|
||||
background-color: $h3_background_todo;
|
||||
color: $todo_c7;
|
||||
}
|
||||
&.equipment {
|
||||
background-color: $h3_background_equipment;
|
||||
color: $equipment_c7;
|
||||
}
|
||||
&.staff {
|
||||
background-color: $h3_background_staff;
|
||||
color: $staff_c7;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: $main_bold_color;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color : $main_bold_color;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/*ASIDE*/
|
||||
aside {
|
||||
background-color:$aside-background;
|
||||
color: white;
|
||||
margin-top: 0px;
|
||||
padding: 0px!important;
|
||||
|
||||
a {
|
||||
color: $main_soft_color;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color : $main_soft_color;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
color: $main_c7;
|
||||
background-color: $main_c3;
|
||||
}
|
||||
|
||||
.heading {
|
||||
padding: 8px 15px;
|
||||
font-size: 32px;
|
||||
line-height: 1.3;
|
||||
text-align:center;
|
||||
|
||||
&.inverted {
|
||||
background-color:$second_white_color;
|
||||
color:black;
|
||||
}
|
||||
|
||||
&.small {
|
||||
font-size: 25px;
|
||||
|
||||
.sub {
|
||||
font-size: 0.7em;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 0.7em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&.separator {
|
||||
border-bottom-color: $main_soft_color;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
padding: 15px;
|
||||
|
||||
&.inverted {
|
||||
background-color:$second_white_color;
|
||||
color:black;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
main {
|
||||
margin-top:20px;
|
||||
border-style: dashed;
|
||||
}
|
||||
aside {
|
||||
margin-top:20px;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
border-top : 1px solid $second_bold_color ;
|
||||
}
|
||||
|
||||
|
||||
span.vsep {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
div.tag-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.module-list {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
a.module {
|
||||
padding: 20px 40px;
|
||||
margin: 5px;
|
||||
border-bottom-style: solid;
|
||||
font-size: large;
|
||||
display: block;
|
||||
|
||||
border-bottom-color: $main_c7;
|
||||
background-color: $main_c3;
|
||||
color: $main_c7;
|
||||
|
||||
|
||||
&.activity {
|
||||
border-bottom-color: $activity_c8;
|
||||
background-color: $activity_c3;
|
||||
color: $activity_c8;
|
||||
}
|
||||
|
||||
&.event {
|
||||
border-bottom-color: $event_c8;
|
||||
background-color: $event_c3;
|
||||
color: $event_c8;
|
||||
}
|
||||
|
||||
&.todo {
|
||||
border-bottom-color: $todo_c8;
|
||||
background-color: $todo_c3;
|
||||
color: $todo_c8;
|
||||
}
|
||||
|
||||
&.equipment {
|
||||
border-bottom-color: $equipment_c8;
|
||||
background-color: $equipment_c3;
|
||||
color: $equipment_c8;
|
||||
}
|
||||
|
||||
&.staff {
|
||||
border-bottom-color: $staff_c8;
|
||||
background-color: $staff_c3;
|
||||
color: $staff_c8;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
color: $main_c1;
|
||||
background-color: $main_c7;
|
||||
|
||||
&.activity {
|
||||
background-color: $activity_c7;
|
||||
}
|
||||
&.event {
|
||||
background-color: $event_c7;
|
||||
}
|
||||
&.equipment {
|
||||
background-color: $equipment_c7;
|
||||
}
|
||||
&.todo {
|
||||
background-color: $todo_c7;
|
||||
}
|
||||
&.staff {
|
||||
background-color: $staff_c7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collapsible {
|
||||
background-color: #777;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 18px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
font-size: 15px;
|
||||
|
||||
&:after {
|
||||
content: '\002B';
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #555;
|
||||
|
||||
&:after {
|
||||
content: "\2212";
|
||||
}
|
||||
}
|
||||
.content {
|
||||
padding: 8px 18px;
|
||||
display : none;
|
||||
background-color: #f1f1f1;
|
||||
|
||||
&.fluid {
|
||||
padding: 0px 0px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: x-large;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
margin: 0px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.planning {
|
||||
.activity {
|
||||
padding: 8px 18px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 12px;
|
||||
|
||||
border-left: none;
|
||||
border-right: 6px solid $main_soft_color;
|
||||
|
||||
&.inverted {
|
||||
border-left: 6px solid $main_soft_color;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
font-size: large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.glyphicon {
|
||||
&.yes {
|
||||
color:$yes_color!important;
|
||||
}
|
||||
&.no {
|
||||
color:$no_color!important;
|
||||
}
|
||||
&.dunno {
|
||||
color:$dunno_color!important;
|
||||
}
|
||||
}
|
||||
|
||||
.sending-request {
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: "Chargement...";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
opacity: 0.8;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
z-index: 5;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 8%;
|
||||
}
|
||||
}
|
71
shared/static/css/header.css
Normal file
71
shared/static/css/header.css
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Error: Undefined variable: "$main_bold_color".
|
||||
on line 6 of header.scss
|
||||
|
||||
1: /* BANNER *\/
|
||||
2:
|
||||
3: .strong-banner {
|
||||
4: padding-top : 20px;
|
||||
5: padding-bottom : 10px ;
|
||||
6: background-color : $main_bold_color;
|
||||
7: color: $second_bold_color;
|
||||
8: }
|
||||
9:
|
||||
10:
|
||||
11: .navbar-inverse {
|
||||
|
||||
Backtrace:
|
||||
header.scss:6
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/script/tree/variable.rb:49:in `_perform'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/script/tree/node.rb:50:in `perform'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:402:in `visit_prop'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:36:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:162:in `block in visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/stack.rb:79:in `block in with_base'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/stack.rb:135:in `with_frame'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/stack.rb:79:in `with_base'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:162:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:444:in `block (2 levels) in visit_rule'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:444:in `map'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:444:in `block in visit_rule'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:183:in `with_environment'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:442:in `visit_rule'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:36:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:162:in `block in visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/stack.rb:79:in `block in with_base'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/stack.rb:135:in `with_frame'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/stack.rb:79:in `with_base'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:162:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:52:in `block in visit_children'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:52:in `map'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:52:in `visit_children'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:171:in `block in visit_children'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:183:in `with_environment'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:170:in `visit_children'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:36:in `block in visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:190:in `visit_root'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/base.rb:36:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:161:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/visitors/perform.rb:10:in `visit'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/root_node.rb:36:in `css_tree'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/tree/root_node.rb:29:in `render_with_sourcemap'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/engine.rb:389:in `_render_with_sourcemap'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/engine.rb:307:in `render_with_sourcemap'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/plugin/compiler.rb:462:in `update_stylesheet'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/plugin/compiler.rb:215:in `block in update_stylesheets'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/plugin/compiler.rb:209:in `each'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/plugin/compiler.rb:209:in `update_stylesheets'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/plugin/compiler.rb:294:in `watch'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/plugin.rb:109:in `method_missing'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/exec/sass_scss.rb:360:in `watch_or_update'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/exec/sass_scss.rb:51:in `process_result'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/exec/base.rb:52:in `parse'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/lib/sass/exec/base.rb:19:in `parse!'
|
||||
/usr/lib/ruby/gems/2.5.0/gems/sass-3.5.5/bin/sass:13:in `<top (required)>'
|
||||
/usr/bin/sass:23:in `load'
|
||||
/usr/bin/sass:23:in `<main>'
|
||||
*/
|
||||
body:before {
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
content: "Error: Undefined variable: \"$main_bold_color\".\A on line 6 of header.scss\A \A 1: /* BANNER */\A 2: \A 3: .strong-banner {\A 4: padding-top : 20px;\A 5: padding-bottom : 10px ;\A 6: background-color : $main_bold_color;\A 7: color: $second_bold_color;\A 8: }\A 9: \A 10: \A 11: .navbar-inverse {"; }
|
|
@ -1,126 +0,0 @@
|
|||
@media (max-width: 768px) {
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/*sidebar*/
|
||||
|
||||
#sidebar {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
|
||||
/* body container */
|
||||
#main-content {
|
||||
margin: 0px!important;
|
||||
position: none !important;
|
||||
}
|
||||
|
||||
#sidebar > ul > li > a > span {
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
#sidebar > ul > li {
|
||||
margin: 0 10px 5px 10px;
|
||||
}
|
||||
#sidebar > ul > li > a {
|
||||
height:35px;
|
||||
line-height:35px;
|
||||
padding: 0 10px;
|
||||
text-align: left;
|
||||
}
|
||||
#sidebar > ul > li > a i{
|
||||
/*display: none !important;*/
|
||||
}
|
||||
|
||||
#sidebar ul > li > a .arrow, #sidebar > ul > li > a .arrow.open {
|
||||
margin-right: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#sidebar ul > li.active > a .arrow, #sidebar ul > li > a:hover .arrow, #sidebar ul > li > a:focus .arrow,
|
||||
#sidebar > ul > li.active > a .arrow.open, #sidebar > ul > li > a:hover .arrow.open, #sidebar > ul > li > a:focus .arrow.open{
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#sidebar > ul > li > a, #sidebar > ul > li > ul.sub > li {
|
||||
width: 100%;
|
||||
}
|
||||
#sidebar > ul > li > ul.sub > li > a {
|
||||
background: transparent !important ;
|
||||
}
|
||||
#sidebar > ul > li > ul.sub > li > a:hover {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* sidebar */
|
||||
#sidebar {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
/* sidebar collabler */
|
||||
#sidebar .btn-navbar.collapsed .arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sidebar .btn-navbar .arrow {
|
||||
position: absolute;
|
||||
right: 35px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
top:48px;
|
||||
border-bottom: 15px solid #282e36;
|
||||
border-left: 15px solid transparent;
|
||||
border-right: 15px solid transparent;
|
||||
}
|
||||
|
||||
|
||||
/*---------*/
|
||||
|
||||
.btn {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li ul.sub li a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*---*/
|
||||
|
||||
.img-responsive {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
||||
#top_menu .nav > li, ul.top-menu > li {
|
||||
float: right;
|
||||
}
|
||||
.hidden-phone {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:320px) {
|
||||
#top_menu .nav > li, ul.top-menu > li {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.hidden-phone {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,918 +0,0 @@
|
|||
/* Import fonts */
|
||||
@import url(http://fonts.googleapis.com/css?family=Ruda:400,700,900);
|
||||
|
||||
/* BASIC THEME CONFIGURATION */
|
||||
body {
|
||||
color: #797979;
|
||||
background: #f2f2f2;
|
||||
font-family: 'Ruda', sans-serif;
|
||||
padding: 0px !important;
|
||||
margin: 0px !important;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a, a:hover, a:focus {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
::selection {
|
||||
|
||||
background: #FF9C77;
|
||||
color: #fff;
|
||||
}
|
||||
::-moz-selection {
|
||||
background: #FF9C77;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Bootstrap Modifications */
|
||||
.modal-header {
|
||||
background: #FF9C77;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-round {
|
||||
border-radius: 20px;
|
||||
-webkit-border-radius: 20px;
|
||||
}
|
||||
|
||||
.accordion-heading .accordion-toggle {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
border-top: 1px solid #F5F5F5;
|
||||
padding: 5px 0px;
|
||||
line-height: 28.75px;
|
||||
text-transform: uppercase;
|
||||
color: #1a1a1a;
|
||||
background-color: #ffffff;
|
||||
outline: none !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* centered columns styles */
|
||||
.row-centered {
|
||||
text-align:center;
|
||||
}
|
||||
.col-centered {
|
||||
display:inline-block;
|
||||
float:none;
|
||||
/* reset the text-align */
|
||||
text-align:left;
|
||||
/* inline-block space fix */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*Helpers*/
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goleft {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.goright {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mt {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.mb {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.ml {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/*sidebar navigation*/
|
||||
|
||||
#sidebar {
|
||||
width: 210px;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
background: #424a5d;
|
||||
}
|
||||
|
||||
#sidebar h5 {
|
||||
color: #f2f2f2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#sidebar ul li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#sidebar .sub-menu > .sub li {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
#sidebar .sub-menu > .sub li:last-child {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
/*LEFT NAVIGATION ICON*/
|
||||
.dcjq-icon {
|
||||
height:17px;
|
||||
width:17px;
|
||||
display:inline-block;
|
||||
background: url("../img/nav-expand.png") no-repeat top;
|
||||
border-radius:3px;
|
||||
-moz-border-radius:3px;
|
||||
-webkit-border-radius:3px;
|
||||
position:absolute;
|
||||
right:10px;
|
||||
top:15px;
|
||||
}
|
||||
.active .dcjq-icon {
|
||||
background: url("../img/nav-expand.png") no-repeat bottom;
|
||||
border-radius:3px;
|
||||
-moz-border-radius:3px;
|
||||
-webkit-border-radius:3px;
|
||||
}
|
||||
/*---*/
|
||||
|
||||
.nav-collapse.collapse {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
ul.sidebar-menu , ul.sidebar-menu li ul.sub{
|
||||
margin: -2px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.sidebar-menu {
|
||||
margin-top: 75px;
|
||||
}
|
||||
|
||||
.sidebar-menu hr {
|
||||
margin: 5px 10px;
|
||||
border-color: #818899;
|
||||
}
|
||||
|
||||
.sidebar-menu .banner-text {
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
border-color: white;
|
||||
border-width: 1px 0px;
|
||||
border-style: solid;
|
||||
margin: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.sidebar-menu .username {
|
||||
color: #818899!important;
|
||||
}
|
||||
|
||||
#sidebar > ul > li > ul.sub {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sidebar > ul > li.active > ul.sub, #sidebar > ul > li > ul.sub > li > a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li ul.sub li{
|
||||
background: #424a5d;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li ul.sub li:last-child{
|
||||
border-radius: 0 0 4px 4px;
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li ul.sub li a {
|
||||
font-size: 12px;
|
||||
padding: 6px 0;
|
||||
line-height: 35px;
|
||||
height: 35px;
|
||||
-webkit-transition: all 0.3s ease;
|
||||
-moz-transition: all 0.3s ease;
|
||||
-o-transition: all 0.3s ease;
|
||||
-ms-transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
color: #aeb2b7;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li ul.sub li a:hover {
|
||||
color: white;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li ul.sub li.active a {
|
||||
color: #FF9C77;
|
||||
-webkit-transition: all 0.3s ease;
|
||||
-moz-transition: all 0.3s ease;
|
||||
-o-transition: all 0.3s ease;
|
||||
-ms-transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li{
|
||||
/*line-height: 20px !important;*/
|
||||
margin-bottom: 5px;
|
||||
margin-left:10px;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li.sub-menu{
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li a span{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li a{
|
||||
color: #aeb2b7;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 15px 0 15px 10px;
|
||||
font-size: 12px;
|
||||
outline: none;
|
||||
-webkit-transition: all 0.3s ease;
|
||||
-moz-transition: all 0.3s ease;
|
||||
-o-transition: all 0.3s ease;
|
||||
-ms-transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li a.active, ul.sidebar-menu li a:hover, ul.sidebar-menu li a:focus {
|
||||
background: #FF9C77;
|
||||
color: #fff;
|
||||
display: block;
|
||||
|
||||
-webkit-transition: all 0.3s ease;
|
||||
-moz-transition: all 0.3s ease;
|
||||
-o-transition: all 0.3s ease;
|
||||
-ms-transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
ul.sidebar-menu li a i {
|
||||
font-size: 15px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li a:hover i, ul.sidebar-menu li a:focus i {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
ul.sidebar-menu li a.active i {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* MAIN CONTENT CONFIGURATION */
|
||||
#main-content {
|
||||
margin-left: 210px;
|
||||
}
|
||||
|
||||
.header, .footer {
|
||||
min-height: 60px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1002;
|
||||
}
|
||||
|
||||
.black-bg {
|
||||
background: #ffd777;
|
||||
border-bottom: 1px solid #c9aa5f;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
margin-top: 60px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-bottom: 15px;
|
||||
padding-top: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a.logo {
|
||||
font-size: 20px;
|
||||
color: #ffffff;
|
||||
float: left;
|
||||
margin-top: 15px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
a.logo b {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
a.logo:hover, a.logo:focus {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a.logo span {
|
||||
color: #FF9C77;
|
||||
}
|
||||
|
||||
/*
|
||||
* top links
|
||||
*/
|
||||
|
||||
.navbar-top-links li {
|
||||
display: inline-block;
|
||||
}
|
||||
.navbar-top-links li:last-child {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.navbar-top-links li > a {
|
||||
padding: 20px 10px;
|
||||
min-height: 60px;
|
||||
color: #797979;
|
||||
}
|
||||
.navbar-top-links .open > a,
|
||||
.navbar-top-links .open > a:hover,
|
||||
.navbar-top-links .open > a:focus,
|
||||
.navbar-top-links .open > a:active {
|
||||
background-color:#C9AA5F;
|
||||
}
|
||||
.navbar-top-links li > a:hover,
|
||||
.navbar-top-links li > a:focus,
|
||||
.navbar-top-links li > a:active{
|
||||
background-color:inherit;
|
||||
}
|
||||
.navbar-top-links .dropdown-menu li {
|
||||
display: block;
|
||||
}
|
||||
.navbar-top-links .dropdown-menu li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.navbar-top-links .dropdown-menu li a {
|
||||
padding: 3px 20px;
|
||||
min-height: 0;
|
||||
}
|
||||
.navbar-top-links .dropdown-menu li a div {
|
||||
white-space: normal;
|
||||
}
|
||||
.navbar-top-links .dropdown-messages,
|
||||
.navbar-top-links .dropdown-tasks,
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
width: 310px;
|
||||
min-width: 0;
|
||||
}
|
||||
.navbar-top-links .dropdown-messages {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.navbar-top-links .dropdown-tasks {
|
||||
margin-left: -59px;
|
||||
}
|
||||
.navbar-top-links .dropdown-alerts {
|
||||
margin-left: -123px;
|
||||
}
|
||||
.navbar-top-links .dropdown-user {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
|
||||
/*--sidebar toggle---*/
|
||||
|
||||
.sidebar-toggle-box {
|
||||
float: left;
|
||||
padding-right: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.sidebar-toggle-box .fa-bars {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
.sidebar-closed > #sidebar > ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-closed #main-content {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.sidebar-closed #sidebar {
|
||||
margin-left: -180px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dash Side
|
||||
* barre de droite
|
||||
*/
|
||||
|
||||
.ds {
|
||||
background: #ffffff;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.ds h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ds h5 {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ds h3 {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
padding: 0 10px;
|
||||
line-height: 60px;
|
||||
height: 60px;
|
||||
margin: 0;
|
||||
background: #ff865c;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ds i {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.ds .desc {
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
display: inline-block;
|
||||
padding: 15px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ds .desc:hover {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.ds .thumb {
|
||||
width: 30px;
|
||||
margin: 0 10px 0 20px;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ds .details {
|
||||
width: 170px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ds > .desc p {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.ds p > muted {
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
font-style: italic;
|
||||
color: #666666
|
||||
}
|
||||
|
||||
.ds a {
|
||||
color: #FF9C77;
|
||||
}
|
||||
|
||||
/* LINE ICONS CONFIGURATION */
|
||||
|
||||
.mtbox {
|
||||
margin-top: 80px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.box1 {
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: #989898;
|
||||
border-bottom: 1px solid #989898;
|
||||
}
|
||||
|
||||
.box1 span {
|
||||
font-size: 50px;
|
||||
|
||||
}
|
||||
|
||||
.box1 h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.box0:hover .box1 {
|
||||
border-bottom: 1px solid #ffffff;
|
||||
}
|
||||
|
||||
.box0 p {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #f2f2f2;
|
||||
}
|
||||
|
||||
.box0:hover p {
|
||||
color: #ff865c;
|
||||
}
|
||||
|
||||
.box0:hover {
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* *************************************************************************************
|
||||
PANELS CONFIGURATIONS
|
||||
*************************************************************************************** */
|
||||
|
||||
/*Panel Size*/
|
||||
|
||||
.pn {
|
||||
height: 250px;
|
||||
box-shadow: 0 2px 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.pn:hover {
|
||||
box-shadow: 2px 3px 2px rgba(0, 0, 0, 0.3);
|
||||
|
||||
}
|
||||
|
||||
/*Grey Panel*/
|
||||
|
||||
.grey-panel {
|
||||
text-align: center;
|
||||
background: #dfdfe1;
|
||||
}
|
||||
.grey-panel .grey-header {
|
||||
background: #ccd1d9;
|
||||
padding: 3px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.grey-panel h5 {
|
||||
font-weight: 200;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.grey-panel p {
|
||||
margin-left: 5px;
|
||||
}
|
||||
/* Specific Conf for Donut Charts*/
|
||||
.donut-chart p {
|
||||
margin-top: 5px;
|
||||
font-weight: 700;
|
||||
margin-left: 15px;
|
||||
}
|
||||
.donut-chart h2 {
|
||||
font-weight: 900;
|
||||
color: #FF6B6B;
|
||||
font-size: 38px;
|
||||
}
|
||||
|
||||
/* Dark Blue*/
|
||||
|
||||
.darkblue-panel {
|
||||
text-align: center;
|
||||
background: #444c57;
|
||||
}
|
||||
.darkblue-panel .darkblue-header {
|
||||
background: transparent;
|
||||
padding: 3px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.darkblue-panel h1 {
|
||||
color: #f2f2f2;
|
||||
}
|
||||
.darkblue-panel h5 {
|
||||
font-weight: 200;
|
||||
margin-top: 10px;
|
||||
color: white;
|
||||
}
|
||||
.darkblue-panel footer {
|
||||
color: white;
|
||||
}
|
||||
.darkblue-panel footer h5 {
|
||||
margin-left:10px;
|
||||
margin-right: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/*Green Panel*/
|
||||
.green-panel {
|
||||
text-align: center;
|
||||
background: #68dff0;
|
||||
}
|
||||
.green-panel .green-header {
|
||||
background: #43b1a9;
|
||||
padding: 3px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.green-panel h5 {
|
||||
color: white;
|
||||
font-weight: 200;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.green-panel h3 {
|
||||
color: white;
|
||||
font-weight: 100;
|
||||
}
|
||||
.green-panel p {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/*White Panel */
|
||||
.white-panel {
|
||||
text-align: center;
|
||||
background: #ffffff;
|
||||
color: #ccd1d9;
|
||||
}
|
||||
²
|
||||
.white-panel p {
|
||||
margin-top: 5px;
|
||||
font-weight: 700;
|
||||
margin-left: 15px;
|
||||
}
|
||||
.white-panel .white-header {
|
||||
background: #f4f4f4;
|
||||
padding: 3px;
|
||||
margin-bottom: 15px;
|
||||
color: #c6c6c6;
|
||||
}
|
||||
.white-panel .small {
|
||||
font-size: 10px;
|
||||
color: #ccd1d9;
|
||||
}
|
||||
|
||||
.white-panel i {
|
||||
color: #68dff0;
|
||||
padding-right: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/*Content Panel*/
|
||||
.content-panel {
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 3px 2px #aab2bd;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.content-panel h4 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.content-panel h5 {
|
||||
margin-left: 10px;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* FORMS CONFIGURATION */
|
||||
.form-panel {
|
||||
background: #ffffff;
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
box-shadow: 0px 3px 2px #aab2bd;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-horizontal.style-form .form-group {
|
||||
border-bottom: 1px solid #eff2f7;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.round-form {
|
||||
border-radius: 500px;
|
||||
-webkit-border-radius: 500px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.form-horizontal .control-label {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
#focusedInput {
|
||||
border: 1px solid #ed5565;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.add-on {
|
||||
float: right;
|
||||
margin-top: -37px;
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.add-on .btn {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
/* TOGGLE CONFIGURATION */
|
||||
.has-switch {
|
||||
border-radius: 30px;
|
||||
-webkit-border-radius: 30px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
line-height: 1.231;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 80px;
|
||||
-webkit-mask: url('../img/mask.png') 0 0 no-repeat;
|
||||
mask: url('../img/mask.png') 0 0 no-repeat;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.has-switch.deactivate {
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
cursor: default !important;
|
||||
}
|
||||
.has-switch.deactivate label,
|
||||
.has-switch.deactivate span {
|
||||
cursor: default !important;
|
||||
}
|
||||
.has-switch > div {
|
||||
width: 162%;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
.has-switch > div.switch-animate {
|
||||
-webkit-transition: left 0.25s ease-out;
|
||||
-moz-transition: left 0.25s ease-out;
|
||||
-o-transition: left 0.25s ease-out;
|
||||
transition: left 0.25s ease-out;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
.has-switch > div.switch-off {
|
||||
left: -63%;
|
||||
}
|
||||
.has-switch > div.switch-off label {
|
||||
background-color: #7f8c9a;
|
||||
border-color: #bdc3c7;
|
||||
-webkit-box-shadow: -1px 0 0 rgba(255, 255, 255, 0.5);
|
||||
-moz-box-shadow: -1px 0 0 rgba(255, 255, 255, 0.5);
|
||||
box-shadow: -1px 0 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.has-switch > div.switch-on {
|
||||
left: 0%;
|
||||
}
|
||||
.has-switch > div.switch-on label {
|
||||
background-color: #41cac0;
|
||||
}
|
||||
.has-switch input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
.has-switch span {
|
||||
cursor: pointer;
|
||||
font-size: 14.994px;
|
||||
font-weight: 700;
|
||||
float: left;
|
||||
height: 29px;
|
||||
line-height: 19px;
|
||||
margin: 0;
|
||||
padding-bottom: 6px;
|
||||
padding-top: 5px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-transition: 0.25s ease-out;
|
||||
-moz-transition: 0.25s ease-out;
|
||||
-o-transition: 0.25s ease-out;
|
||||
transition: 0.25s ease-out;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
.has-switch span.switch-left {
|
||||
border-radius: 30px 0 0 30px;
|
||||
background-color: #2A3542;
|
||||
color: #41cac0;
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
.has-switch span.switch-right {
|
||||
border-radius: 0 30px 30px 0;
|
||||
background-color: #bdc3c7;
|
||||
color: #ffffff;
|
||||
text-indent: 7px;
|
||||
}
|
||||
.has-switch span.switch-right [class*="fui-"] {
|
||||
text-indent: 0;
|
||||
}
|
||||
.has-switch label {
|
||||
border: 4px solid #2A3542;
|
||||
border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
float: left;
|
||||
height: 29px;
|
||||
margin: 0 -21px 0 -14px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 29px;
|
||||
z-index: 100;
|
||||
-webkit-transition: 0.25s ease-out;
|
||||
-moz-transition: 0.25s ease-out;
|
||||
-o-transition: 0.25s ease-out;
|
||||
transition: 0.25s ease-out;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
.switch-square {
|
||||
border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
-webkit-mask: url('../img/mask.png') 0 0 no-repeat;
|
||||
mask: url('../img/mask.png') 0 0 no-repeat;
|
||||
}
|
||||
.switch-square > div.switch-off label {
|
||||
border-color: #7f8c9a;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
.switch-square span.switch-left {
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
.switch-square span.switch-left [class*="fui-"] {
|
||||
text-indent: -10px;
|
||||
}
|
||||
.switch-square span.switch-right {
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
.switch-square span.switch-right [class*="fui-"] {
|
||||
text-indent: 5px;
|
||||
}
|
||||
.switch-square label {
|
||||
border-radius: 0 6px 6px 0;
|
||||
border-color: #41cac0;
|
||||
}
|
||||
|
||||
/*badge*/
|
||||
.badge.bg-primary {
|
||||
background: #8075c4;
|
||||
}
|
||||
|
||||
.badge.bg-success {
|
||||
background: #a9d86e;
|
||||
}
|
||||
|
||||
.badge.bg-warning {
|
||||
background: #FCB322;
|
||||
}
|
||||
|
||||
.badge.bg-important {
|
||||
background: #ff6c60;
|
||||
}
|
||||
|
||||
.badge.bg-info {
|
||||
background: #41cac0;
|
||||
}
|
||||
|
||||
.badge.bg-inverse {
|
||||
background: #2A3542;
|
||||
}
|
||||
|
||||
/*todolist*/
|
||||
/*footer*/
|
||||
.site-footer {
|
||||
background: #FF9C77;
|
||||
color: #fff;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.site-min-height {
|
||||
min-height: 900px;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project,
|
||||
Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome,
|
||||
comprehensive icon sets or copy and paste your own.
|
||||
|
||||
Please. Check it out.
|
||||
|
||||
-Dave Gandy
|
2199
shared/static/font-awesome/css/font-awesome.css
vendored
2199
shared/static/font-awesome/css/font-awesome.css
vendored
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue