\n"
@@ -13,83 +13,189 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: apps/activity/apps.py:10 apps/activity/models.py:76
+#: apps/activity/apps.py:10 apps/activity/models.py:111
+#: apps/activity/models.py:120
msgid "activity"
msgstr "activité"
-#: apps/activity/models.py:19 apps/activity/models.py:44
-#: apps/member/models.py:63 apps/member/models.py:114
-#: apps/note/models/notes.py:188 apps/note/models/transactions.py:25
-#: apps/note/models/transactions.py:45 apps/note/models/transactions.py:232
-#: templates/member/profile_detail.html:15
+#: apps/activity/forms.py:45 apps/activity/models.py:217
+msgid "You can't invite someone once the activity is started."
+msgstr "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré."
+
+#: apps/activity/forms.py:48 apps/activity/models.py:220
+msgid "This activity is not validated yet."
+msgstr "Cette activité n'est pas encore validée."
+
+#: apps/activity/forms.py:58 apps/activity/models.py:228
+msgid "This person has been already invited 5 times this year."
+msgstr "Cette personne a déjà été invitée 5 fois cette année."
+
+#: apps/activity/forms.py:62 apps/activity/models.py:232
+msgid "This person is already invited."
+msgstr "Cette personne est déjà invitée."
+
+#: apps/activity/forms.py:66 apps/activity/models.py:236
+msgid "You can't invite more than 3 people to this activity."
+msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
+
+#: apps/activity/models.py:23 apps/activity/models.py:48
+#: apps/member/models.py:64 apps/member/models.py:122
+#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
+#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:231
+#: templates/member/club_info.html:13 templates/member/profile_info.html:14
msgid "name"
msgstr "nom"
-#: apps/activity/models.py:23
+#: apps/activity/models.py:27 templates/activity/activity_detail.html:39
msgid "can invite"
msgstr "peut inviter"
-#: apps/activity/models.py:26
+#: apps/activity/models.py:30 templates/activity/activity_detail.html:43
msgid "guest entry fee"
msgstr "cotisation de l'entrée invité"
-#: apps/activity/models.py:30
+#: apps/activity/models.py:34
msgid "activity type"
msgstr "type d'activité"
-#: apps/activity/models.py:31
+#: apps/activity/models.py:35
msgid "activity types"
msgstr "types d'activité"
-#: apps/activity/models.py:48 apps/note/models/transactions.py:70
-#: apps/permission/models.py:91
+#: apps/activity/models.py:53 apps/note/models/transactions.py:69
+#: apps/permission/models.py:90 templates/activity/activity_detail.html:16
msgid "description"
msgstr "description"
-#: apps/activity/models.py:54 apps/note/models/notes.py:164
-#: apps/note/models/transactions.py:63
+#: apps/activity/models.py:60 apps/note/models/notes.py:164
+#: apps/note/models/transactions.py:62
+#: templates/activity/activity_detail.html:19
msgid "type"
msgstr "type"
-#: apps/activity/models.py:60
+#: apps/activity/models.py:66 apps/logs/models.py:21
+#: apps/note/models/notes.py:117
+msgid "user"
+msgstr "utilisateur"
+
+#: apps/activity/models.py:73 templates/activity/activity_detail.html:33
msgid "organizer"
msgstr "organisateur"
-#: apps/activity/models.py:66
-msgid "attendees club"
-msgstr ""
+#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14
+#: apps/note/models/notes.py:58
+msgid "note"
+msgstr "note"
-#: apps/activity/models.py:69
+#: apps/activity/models.py:89 templates/activity/activity_detail.html:36
+msgid "attendees club"
+msgstr "club attendu"
+
+#: apps/activity/models.py:93 templates/activity/activity_detail.html:22
msgid "start date"
msgstr "date de début"
-#: apps/activity/models.py:72
+#: apps/activity/models.py:97 templates/activity/activity_detail.html:25
msgid "end date"
msgstr "date de fin"
-#: apps/activity/models.py:77
+#: apps/activity/models.py:102 apps/note/models/transactions.py:134
+#: templates/activity/activity_detail.html:47
+msgid "valid"
+msgstr "valide"
+
+#: apps/activity/models.py:107 templates/activity/activity_detail.html:61
+msgid "open"
+msgstr "ouvrir"
+
+#: apps/activity/models.py:112
msgid "activities"
msgstr "activités"
-#: apps/activity/models.py:108
+#: apps/activity/models.py:125
+msgid "entry time"
+msgstr "heure d'entrée"
+
+#: apps/activity/models.py:148
+msgid "Already entered on "
+msgstr "Déjà rentré le "
+
+#: apps/activity/models.py:148 apps/activity/tables.py:54
+msgid "{:%Y-%m-%d %H:%M:%S}"
+msgstr "{:%d/%m/%Y %H:%M:%S}"
+
+#: apps/activity/models.py:156
+msgid "The balance is negative."
+msgstr "La note est en négatif."
+
+#: apps/activity/models.py:188
+msgid "last name"
+msgstr "nom de famille"
+
+#: apps/activity/models.py:193 templates/member/profile_info.html:14
+msgid "first name"
+msgstr "prénom"
+
+#: apps/activity/models.py:200
+msgid "inviter"
+msgstr "hôte"
+
+#: apps/activity/models.py:241
msgid "guest"
msgstr "invité"
-#: apps/activity/models.py:109
+#: apps/activity/models.py:242
msgid "guests"
msgstr "invités"
+#: apps/activity/models.py:254
+msgid "Invitation"
+msgstr "Invitation"
+
+#: apps/activity/tables.py:54
+msgid "Entered on "
+msgstr "Entré le "
+
+#: apps/activity/tables.py:55
+msgid "remove"
+msgstr "supprimer"
+
+#: apps/activity/tables.py:75 apps/treasury/models.py:126
+msgid "Type"
+msgstr "Type"
+
+#: apps/activity/tables.py:77 apps/treasury/forms.py:120
+msgid "Last name"
+msgstr "Nom de famille"
+
+#: apps/activity/tables.py:79 apps/treasury/forms.py:122
+#: templates/note/transaction_form.html:92
+msgid "First name"
+msgstr "Prénom"
+
+#: apps/activity/tables.py:81 apps/note/models/notes.py:67
+msgid "Note"
+msgstr "Note"
+
+#: apps/activity/tables.py:83
+msgid "Balance"
+msgstr "Solde du compte"
+
+#: apps/activity/views.py:44 templates/base.html:94
+msgid "Activities"
+msgstr "Activités"
+
+#: apps/activity/views.py:149
+msgid "Entry for activity \"{}\""
+msgstr "Entrées pour l'activité « {} »"
+
#: apps/api/apps.py:10
msgid "API"
-msgstr ""
+msgstr "API"
#: apps/logs/apps.py:11
msgid "Logs"
-msgstr ""
-
-#: apps/logs/models.py:21 apps/note/models/notes.py:117
-msgid "user"
-msgstr "utilisateur"
+msgstr "Logs"
#: apps/logs/models.py:27
msgid "IP Address"
@@ -115,11 +221,12 @@ msgstr "Nouvelles données"
msgid "create"
msgstr "Créer"
-#: apps/logs/models.py:61 apps/note/tables.py:147
+#: apps/logs/models.py:61 apps/note/tables.py:142
+#: templates/activity/activity_detail.html:67
msgid "edit"
msgstr "Modifier"
-#: apps/logs/models.py:62 apps/note/tables.py:151
+#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146
msgid "delete"
msgstr "Supprimer"
@@ -139,61 +246,65 @@ msgstr "Les logs ne peuvent pas être détruits."
msgid "member"
msgstr "adhérent"
-#: apps/member/models.py:25
+#: apps/member/models.py:26
msgid "phone number"
msgstr "numéro de téléphone"
-#: apps/member/models.py:31 templates/member/profile_detail.html:28
+#: apps/member/models.py:32 templates/member/profile_info.html:27
msgid "section"
msgstr "section"
-#: apps/member/models.py:32
+#: apps/member/models.py:33
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
-#: apps/member/models.py:38 templates/member/profile_detail.html:31
+#: apps/member/models.py:39 templates/member/profile_info.html:30
msgid "address"
msgstr "adresse"
-#: apps/member/models.py:44
+#: apps/member/models.py:45
msgid "paid"
msgstr "payé"
-#: apps/member/models.py:49 apps/member/models.py:50
+#: apps/member/models.py:50 apps/member/models.py:51
msgid "user profile"
msgstr "profil utilisateur"
-#: apps/member/models.py:68
+#: apps/member/models.py:69 templates/member/club_info.html:36
msgid "email"
msgstr "courriel"
-#: apps/member/models.py:73
+#: apps/member/models.py:76
+msgid "parent club"
+msgstr "club parent"
+
+#: apps/member/models.py:81 templates/member/club_info.html:30
msgid "membership fee"
msgstr "cotisation pour adhérer"
-#: apps/member/models.py:77
+#: apps/member/models.py:85 templates/member/club_info.html:27
msgid "membership duration"
msgstr "durée de l'adhésion"
-#: apps/member/models.py:78
+#: apps/member/models.py:86
msgid "The longest time a membership can last (NULL = infinite)."
msgstr "La durée maximale d'une adhésion (NULL = infinie)."
-#: apps/member/models.py:83
+#: apps/member/models.py:91 templates/member/club_info.html:21
msgid "membership start"
msgstr "début de l'adhésion"
-#: apps/member/models.py:84
+#: apps/member/models.py:92
msgid "How long after January 1st the members can renew their membership."
msgstr ""
"Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur "
"adhésion."
-#: apps/member/models.py:89
+#: apps/member/models.py:97 templates/member/club_info.html:24
msgid "membership end"
msgstr "fin de l'adhésion"
-#: apps/member/models.py:90
+#: apps/member/models.py:98
msgid ""
"How long the membership can last after January 1st of the next year after "
"members can renew their membership."
@@ -201,81 +312,68 @@ msgstr ""
"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
"suivante avant que les adhérents peuvent renouveler leur adhésion."
-#: apps/member/models.py:96 apps/note/models/notes.py:139
+#: apps/member/models.py:104 apps/note/models/notes.py:139
msgid "club"
msgstr "club"
-#: apps/member/models.py:97
+#: apps/member/models.py:105
msgid "clubs"
msgstr "clubs"
-#: apps/member/models.py:120 apps/permission/models.py:276
+#: apps/member/models.py:128 apps/permission/models.py:275
msgid "role"
msgstr "rôle"
-#: apps/member/models.py:121
+#: apps/member/models.py:129
msgid "roles"
msgstr "rôles"
-#: apps/member/models.py:145
+#: apps/member/models.py:153
msgid "membership starts on"
msgstr "l'adhésion commence le"
-#: apps/member/models.py:148
+#: apps/member/models.py:156
msgid "membership ends on"
msgstr "l'adhésion finie le"
-#: apps/member/models.py:152
+#: apps/member/models.py:160
msgid "fee"
msgstr "cotisation"
-#: apps/member/models.py:162
+#: apps/member/models.py:172
+msgid "User is not a member of the parent club"
+msgstr "L'utilisateur n'est pas membre du club parent"
+
+#: apps/member/models.py:176
msgid "membership"
msgstr "adhésion"
-#: apps/member/models.py:163
+#: apps/member/models.py:177
msgid "memberships"
msgstr "adhésions"
-#: apps/member/views.py:80 templates/member/profile_detail.html:46
+#: apps/member/views.py:76 templates/member/profile_info.html:45
msgid "Update Profile"
msgstr "Modifier le profil"
-#: apps/member/views.py:93
+#: apps/member/views.py:89
msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà."
-#: apps/member/views.py:146
-#, python-format
-msgid "Account #%(id)s: %(username)s"
-msgstr "Compte n°%(id)s : %(username)s"
-
-#: apps/member/views.py:216
-msgid "Alias successfully deleted"
-msgstr "L'alias a bien été supprimé"
-
-#: apps/note/admin.py:120 apps/note/models/transactions.py:95
+#: apps/note/admin.py:120 apps/note/models/transactions.py:94
msgid "source"
msgstr "source"
#: apps/note/admin.py:128 apps/note/admin.py:156
-#: apps/note/models/transactions.py:54 apps/note/models/transactions.py:108
+#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107
msgid "destination"
msgstr "destination"
-#: apps/note/apps.py:14 apps/note/models/notes.py:58
-msgid "note"
-msgstr "note"
-
-#: apps/note/forms.py:20
-msgid "New Alias"
-msgstr "Nouvel alias"
-
-#: apps/note/forms.py:25
+#: apps/note/forms.py:14
msgid "select an image"
msgstr "Choisissez une image"
-#: apps/note/forms.py:26
+#: apps/note/forms.py:15
msgid "Maximal size: 2MB"
msgstr "Taille maximale : 2 Mo"
@@ -310,7 +408,7 @@ msgstr ""
msgid "display image"
msgstr "image affichée"
-#: apps/note/models/notes.py:53 apps/note/models/transactions.py:118
+#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117
msgid "created at"
msgstr "créée le"
@@ -318,10 +416,6 @@ msgstr "créée le"
msgid "notes"
msgstr "notes"
-#: apps/note/models/notes.py:67
-msgid "Note"
-msgstr "Note"
-
#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
msgid "This alias is already taken."
msgstr "Cet alias est déjà pris."
@@ -368,7 +462,8 @@ msgstr "Alias invalide"
msgid "alias"
msgstr "alias"
-#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
+#: apps/note/models/notes.py:211 templates/member/club_info.html:33
+#: templates/member/profile_info.html:36
msgid "aliases"
msgstr "alias"
@@ -380,102 +475,114 @@ msgstr "L'alias est trop long."
msgid "An alias with a similar name already exists: {} "
msgstr "Un alias avec un nom similaire existe déjà : {}"
-#: apps/note/models/notes.py:247
+#: apps/note/models/notes.py:251
msgid "You can't delete your main alias."
msgstr "Vous ne pouvez pas supprimer votre alias principal."
-#: apps/note/models/transactions.py:31
+#: apps/note/models/transactions.py:30
msgid "transaction category"
msgstr "catégorie de transaction"
-#: apps/note/models/transactions.py:32
+#: apps/note/models/transactions.py:31
msgid "transaction categories"
msgstr "catégories de transaction"
-#: apps/note/models/transactions.py:48
+#: apps/note/models/transactions.py:47
msgid "A template with this name already exist"
msgstr "Un modèle de transaction avec un nom similaire existe déjà."
-#: apps/note/models/transactions.py:57 apps/note/models/transactions.py:126
+#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:125
msgid "amount"
msgstr "montant"
-#: apps/note/models/transactions.py:58
+#: apps/note/models/transactions.py:57
msgid "in centimes"
msgstr "en centimes"
-#: apps/note/models/transactions.py:76
+#: apps/note/models/transactions.py:75
msgid "transaction template"
msgstr "modèle de transaction"
-#: apps/note/models/transactions.py:77
+#: apps/note/models/transactions.py:76
msgid "transaction templates"
msgstr "modèles de transaction"
-#: apps/note/models/transactions.py:101 apps/note/models/transactions.py:114
+#: apps/note/models/transactions.py:100 apps/note/models/transactions.py:113
#: apps/note/tables.py:33 apps/note/tables.py:42
msgid "used alias"
msgstr "alias utilisé"
-#: apps/note/models/transactions.py:122
+#: apps/note/models/transactions.py:121
msgid "quantity"
msgstr "quantité"
-#: apps/note/models/transactions.py:115
+#: apps/note/models/transactions.py:129
msgid "reason"
msgstr "raison"
-#: apps/note/models/transactions.py:119
-msgid "valid"
-msgstr "valide"
+#: apps/note/models/transactions.py:139 apps/note/tables.py:95
+msgid "invalidity reason"
+msgstr "Motif d'invalidité"
-#: apps/note/models/transactions.py:124
+#: apps/note/models/transactions.py:146
msgid "transaction"
msgstr "transaction"
-#: apps/note/models/transactions.py:125
+#: apps/note/models/transactions.py:147
msgid "transactions"
msgstr "transactions"
-#: apps/note/models/transactions.py:168 templates/base.html:98
+#: apps/note/models/transactions.py:201 templates/base.html:84
#: templates/note/transaction_form.html:19
-#: templates/note/transaction_form.html:145
+#: templates/note/transaction_form.html:140
msgid "Transfer"
msgstr "Virement"
-#: apps/note/models/transactions.py:188
+#: apps/note/models/transactions.py:221
msgid "Template"
msgstr "Bouton"
-#: apps/note/models/transactions.py:203
+#: apps/note/models/transactions.py:236
msgid "first_name"
msgstr "prénom"
-#: apps/note/models/transactions.py:208
+#: apps/note/models/transactions.py:241
msgid "bank"
msgstr "banque"
-#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:24
+#: apps/note/models/transactions.py:247 templates/note/transaction_form.html:24
msgid "Credit"
msgstr "Crédit"
-#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:28
+#: apps/note/models/transactions.py:247 templates/note/transaction_form.html:28
msgid "Debit"
msgstr "Débit"
-#: apps/note/models/transactions.py:230 apps/note/models/transactions.py:235
+#: apps/note/models/transactions.py:263 apps/note/models/transactions.py:268
msgid "membership transaction"
msgstr "transaction d'adhésion"
-#: apps/note/models/transactions.py:231
+#: apps/note/models/transactions.py:264
msgid "membership transactions"
msgstr "transactions d'adhésion"
-#: apps/note/views.py:39
+#: apps/note/tables.py:57
+msgid "Click to invalidate"
+msgstr "Cliquez pour dévalider"
+
+#: apps/note/tables.py:57
+msgid "Click to validate"
+msgstr "Cliquez pour valider"
+
+#: apps/note/tables.py:93
+msgid "No reason specified"
+msgstr "Pas de motif spécifié"
+
+#: apps/note/views.py:41
msgid "Transfer money"
msgstr "Transférer de l'argent"
-#: apps/note/views.py:145 templates/base.html:79
+#: apps/note/views.py:102 templates/base.html:79
msgid "Consumptions"
msgstr "Consommations"
@@ -497,41 +604,35 @@ msgstr "Rang"
msgid "Specifying field applies only to view and change permission types."
msgstr ""
-#: apps/treasury/apps.py:11 templates/base.html:102
+#: apps/treasury/apps.py:12 templates/base.html:99
msgid "Treasury"
msgstr "Trésorerie"
-#: apps/treasury/forms.py:56 apps/treasury/forms.py:95
+#: apps/treasury/forms.py:84 apps/treasury/forms.py:132
+#: templates/activity/activity_form.html:9
+#: templates/activity/activity_invite.html:8
#: templates/django_filters/rest_framework/form.html:5
-#: templates/member/club_form.html:10 templates/treasury/invoice_form.html:47
+#: templates/member/club_form.html:9 templates/treasury/invoice_form.html:46
msgid "Submit"
msgstr "Envoyer"
-#: apps/treasury/forms.py:58
+#: apps/treasury/forms.py:86
msgid "Close"
msgstr "Fermer"
-#: apps/treasury/forms.py:65
+#: apps/treasury/forms.py:95
msgid "Remittance is already closed."
msgstr "La remise est déjà fermée."
-#: apps/treasury/forms.py:70
+#: apps/treasury/forms.py:100
msgid "You can't change the type of the remittance."
msgstr "Vous ne pouvez pas changer le type de la remise."
-#: apps/treasury/forms.py:84
-msgid "Last name"
-msgstr "Nom de famille"
-
-#: apps/treasury/forms.py:86 templates/note/transaction_form.html:92
-msgid "First name"
-msgstr "Prénom"
-
-#: apps/treasury/forms.py:88 templates/note/transaction_form.html:98
+#: apps/treasury/forms.py:124 templates/note/transaction_form.html:98
msgid "Bank"
msgstr "Banque"
-#: apps/treasury/forms.py:90 apps/treasury/tables.py:40
+#: apps/treasury/forms.py:126 apps/treasury/tables.py:47
#: templates/note/transaction_form.html:128
#: templates/treasury/remittance_form.html:18
msgid "Amount"
@@ -585,10 +686,6 @@ msgstr "Prix unitaire"
msgid "Date"
msgstr "Date"
-#: apps/treasury/models.py:126
-msgid "Type"
-msgstr "Type"
-
#: apps/treasury/models.py:131
msgid "Comment"
msgstr "Commentaire"
@@ -597,38 +694,38 @@ msgstr "Commentaire"
msgid "Closed"
msgstr "Fermée"
-#: apps/treasury/models.py:159
+#: apps/treasury/models.py:169
msgid "Remittance #{:d}: {}"
msgstr "Remise n°{:d} : {}"
-#: apps/treasury/models.py:178 apps/treasury/tables.py:64
-#: apps/treasury/tables.py:72 templates/treasury/invoice_list.html:13
+#: apps/treasury/models.py:188 apps/treasury/tables.py:76
+#: apps/treasury/tables.py:84 templates/treasury/invoice_list.html:13
#: templates/treasury/remittance_list.html:13
msgid "Remittance"
msgstr "Remise"
-#: apps/treasury/tables.py:16
+#: apps/treasury/tables.py:19
msgid "Invoice #{:d}"
msgstr "Facture n°{:d}"
-#: apps/treasury/tables.py:19 templates/treasury/invoice_list.html:10
+#: apps/treasury/tables.py:22 templates/treasury/invoice_list.html:10
#: templates/treasury/remittance_list.html:10
msgid "Invoice"
msgstr "Facture"
-#: apps/treasury/tables.py:38
+#: apps/treasury/tables.py:45
msgid "Transaction count"
msgstr "Nombre de transactions"
-#: apps/treasury/tables.py:43 apps/treasury/tables.py:45
+#: apps/treasury/tables.py:50 apps/treasury/tables.py:52
msgid "View"
msgstr "Voir"
-#: apps/treasury/tables.py:66
+#: apps/treasury/tables.py:78
msgid "Add"
msgstr "Ajouter"
-#: apps/treasury/tables.py:74
+#: apps/treasury/tables.py:86
msgid "Remove"
msgstr "supprimer"
@@ -639,30 +736,86 @@ msgid ""
"again unless your session expires or you logout."
msgstr ""
-#: note_kfet/settings/base.py:151
+#: note_kfet/settings/base.py:152
msgid "German"
msgstr ""
-#: note_kfet/settings/base.py:152
+#: note_kfet/settings/base.py:153
msgid "English"
msgstr ""
-#: note_kfet/settings/base.py:153
+#: note_kfet/settings/base.py:154
msgid "French"
msgstr ""
+#: templates/activity/activity_detail.html:29
+msgid "creater"
+msgstr "Créateur"
+
+#: templates/activity/activity_detail.html:50
+msgid "opened"
+msgstr "ouvert"
+
+#: templates/activity/activity_detail.html:57
+msgid "Entry page"
+msgstr "Page des entrées"
+
+#: templates/activity/activity_detail.html:61
+msgid "close"
+msgstr "fermer"
+
+#: templates/activity/activity_detail.html:64
+msgid "invalidate"
+msgstr "dévalider"
+
+#: templates/activity/activity_detail.html:64
+msgid "validate"
+msgstr "valider"
+
+#: templates/activity/activity_detail.html:70
+msgid "Invite"
+msgstr "Inviter"
+
+#: templates/activity/activity_detail.html:77
+msgid "Guests list"
+msgstr "Liste des invités"
+
+#: templates/activity/activity_entry.html:10
+msgid "Return to activity page"
+msgstr "Retour à la page de l'activité"
+
+#: templates/activity/activity_entry.html:18
+msgid "entries"
+msgstr "entrées"
+
+#: templates/activity/activity_entry.html:18
+msgid "entry"
+msgstr "entrée"
+
+#: templates/activity/activity_list.html:5
+msgid "Upcoming activities"
+msgstr "Activités à venir"
+
+#: templates/activity/activity_list.html:10
+msgid "There is no planned activity."
+msgstr "Il n'y a pas d'activité prévue."
+
+#: templates/activity/activity_list.html:14
+msgid "New activity"
+msgstr "Nouvelle activité"
+
+#: templates/activity/activity_list.html:18
+msgid "All activities"
+msgstr "Toutes les activités"
+
#: templates/base.html:13
msgid "The ENS Paris-Saclay BDE note."
msgstr "La note du BDE de l'ENS Paris-Saclay."
-#: templates/base.html:87
+#: templates/base.html:89
msgid "Clubs"
msgstr "Clubs"
-#: templates/base.html:92
-msgid "Activities"
-msgstr "Activités"
-
#: templates/cas_server/base.html:7
msgid "Central Authentication Service"
msgstr ""
@@ -720,33 +873,49 @@ msgstr ""
msgid "Field filters"
msgstr ""
-#: templates/member/club_detail.html:10
-msgid "Membership starts on"
-msgstr "L'adhésion commence le"
+#: templates/member/alias_update.html:5
+msgid "Add alias"
+msgstr "Ajouter un alias"
-#: templates/member/club_detail.html:12
-msgid "Membership ends on"
-msgstr "L'adhésion finie le"
+#: templates/member/club_info.html:17
+msgid "Club Parent"
+msgstr "Club parent"
-#: templates/member/club_detail.html:14
-msgid "Membership duration"
-msgstr "Durée de l'adhésion"
+#: templates/member/club_info.html:41
+msgid "Add member"
+msgstr "Ajouter un membre"
-#: templates/member/club_detail.html:18 templates/member/profile_detail.html:34
-msgid "balance"
-msgstr "solde du compte"
+#: templates/member/club_info.html:42 templates/note/conso_form.html:121
+msgid "Edit"
+msgstr "Éditer"
-#: templates/member/club_detail.html:51 templates/member/profile_detail.html:75
-msgid "Transaction history"
-msgstr "Historique des transactions"
+#: templates/member/club_info.html:43
+msgid "Add roles"
+msgstr "Ajouter des rôles"
-#: templates/member/club_form.html:6
-msgid "Clubs list"
-msgstr "Liste des clubs"
+#: templates/member/club_info.html:46 templates/member/profile_info.html:48
+msgid "View Profile"
+msgstr "Voir le profil"
#: templates/member/club_list.html:8
-msgid "New club"
-msgstr "Nouveau club"
+msgid "search clubs"
+msgstr "Chercher un club"
+
+#: templates/member/club_list.html:12
+msgid "Créer un club"
+msgstr ""
+
+#: templates/member/club_list.html:19
+msgid "club listing "
+msgstr "Liste des clubs"
+
+#: templates/member/club_tables.html:9
+msgid "Member of the Club"
+msgstr "Membre du club"
+
+#: templates/member/club_tables.html:22 templates/member/profile_tables.html:22
+msgid "Transaction history"
+msgstr "Historique des transactions"
#: templates/member/manage_auth_tokens.html:16
msgid "Token"
@@ -760,35 +929,31 @@ msgstr "Créé le"
msgid "Regenerate token"
msgstr "Regénérer le jeton"
-#: templates/member/profile_alias.html:10
-msgid "Add alias"
-msgstr "Ajouter un alias"
+#: templates/member/profile_info.html:5
+msgid "Account #"
+msgstr "Compte n°"
-#: templates/member/profile_detail.html:15
-msgid "first name"
-msgstr "prénom"
-
-#: templates/member/profile_detail.html:18
+#: templates/member/profile_info.html:17
msgid "username"
msgstr "pseudo"
-#: templates/member/profile_detail.html:21
+#: templates/member/profile_info.html:20
msgid "password"
msgstr "mot de passe"
-#: templates/member/profile_detail.html:24
+#: templates/member/profile_info.html:23
msgid "Change password"
msgstr "Changer le mot de passe"
-#: templates/member/profile_detail.html:42
+#: templates/member/profile_info.html:33
+msgid "balance"
+msgstr "solde du compte"
+
+#: templates/member/profile_info.html:41
msgid "Manage auth token"
msgstr "Gérer les jetons d'authentification"
-#: templates/member/profile_detail.html:49
-msgid "View Profile"
-msgstr "Voir le profil"
-
-#: templates/member/profile_detail.html:62
+#: templates/member/profile_tables.html:9
msgid "View my memberships"
msgstr "Voir mes adhésions"
@@ -817,10 +982,6 @@ msgstr "Consommer !"
msgid "Most used buttons"
msgstr "Boutons les plus utilisés"
-#: templates/note/conso_form.html:121
-msgid "Edit"
-msgstr "Éditer"
-
#: templates/note/conso_form.html:126
msgid "Single consumptions"
msgstr "Consommations simples"
@@ -829,7 +990,7 @@ msgstr "Consommations simples"
msgid "Double consumptions"
msgstr "Consommations doubles"
-#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
+#: templates/note/conso_form.html:141 templates/note/transaction_form.html:147
msgid "Recent transactions history"
msgstr "Historique des transactions récentes"
@@ -845,37 +1006,21 @@ msgstr "Paiement externe"
msgid "Transfer type"
msgstr "Type de transfert"
-#: templates/note/transaction_form.html:86
-msgid "Name"
-msgstr "Nom"
-
-#: templates/note/transaction_form.html:92
-msgid "First name"
-msgstr "Prénom"
-
-#: templates/note/transaction_form.html:98
-msgid "Bank"
-msgstr "Banque"
-
#: templates/note/transaction_form.html:111
-#: templates/note/transaction_form.html:169
-#: templates/note/transaction_form.html:176
+#: templates/note/transaction_form.html:164
+#: templates/note/transaction_form.html:171
msgid "Select receivers"
msgstr "Sélection des destinataires"
-#: templates/note/transaction_form.html:128
-msgid "Amount"
-msgstr "Montant"
-
-#: templates/note/transaction_form.html:138
+#: templates/note/transaction_form.html:133
msgid "Reason"
msgstr "Raison"
-#: templates/note/transaction_form.html:183
+#: templates/note/transaction_form.html:178
msgid "Credit note"
msgstr "Note à recharger"
-#: templates/note/transaction_form.html:190
+#: templates/note/transaction_form.html:185
msgid "Debit note"
msgstr "Note à débiter"
@@ -887,6 +1032,10 @@ msgstr "Liste des boutons"
msgid "search button"
msgstr "Chercher un bouton"
+#: templates/note/transactiontemplate_list.html:13
+msgid "New button"
+msgstr "Nouveau bouton"
+
#: templates/note/transactiontemplate_list.html:20
msgid "buttons listing "
msgstr "Liste des boutons"
@@ -989,11 +1138,11 @@ msgstr ""
msgid "Invoices list"
msgstr "Liste des factures"
-#: templates/treasury/invoice_form.html:42
+#: templates/treasury/invoice_form.html:41
msgid "Add product"
msgstr "Ajouter produit"
-#: templates/treasury/invoice_form.html:43
+#: templates/treasury/invoice_form.html:42
msgid "Remove product"
msgstr "Retirer produit"
diff --git a/note_kfet/inputs.py b/note_kfet/inputs.py
new file mode 100644
index 00000000..a3170007
--- /dev/null
+++ b/note_kfet/inputs.py
@@ -0,0 +1,302 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from json import dumps as json_dumps
+
+from django.forms.widgets import DateTimeBaseInput, NumberInput, TextInput
+
+
+class AmountInput(NumberInput):
+ """
+ This input type lets the user type amounts in euros, but forms receive data in cents
+ """
+ template_name = "note/amount_input.html"
+
+ def format_value(self, value):
+ return None if value is None or value == "" else "{:.02f}".format(value / 100, )
+
+ def value_from_datadict(self, data, files, name):
+ val = super().value_from_datadict(data, files, name)
+ return str(int(100 * float(val))) if val else val
+
+
+class Autocomplete(TextInput):
+ template_name = "member/autocomplete_model.html"
+
+ def __init__(self, model, attrs=None):
+ super().__init__(attrs)
+
+ self.model = model
+ self.model_pk = None
+
+ class Media:
+ """JS/CSS resources needed to render the date-picker calendar."""
+
+ js = ('js/autocomplete_model.js', )
+
+ def format_value(self, value):
+ if value:
+ self.attrs["model_pk"] = int(value)
+ return str(self.model.objects.get(pk=int(value)))
+ return ""
+
+
+"""
+The remaining of this file comes from the project `django-bootstrap-datepicker-plus` available on Github:
+https://github.com/monim67/django-bootstrap-datepicker-plus
+This is distributed under Apache License 2.0.
+
+This adds datetime pickers with bootstrap.
+"""
+
+"""Contains Base Date-Picker input class for widgets of this package."""
+
+
+class DatePickerDictionary:
+ """Keeps track of all date-picker input classes."""
+
+ _i = 0
+ items = dict()
+
+ @classmethod
+ def generate_id(cls):
+ """Return a unique ID for each date-picker input class."""
+ cls._i += 1
+ return 'dp_%s' % cls._i
+
+
+class BasePickerInput(DateTimeBaseInput):
+ """Base Date-Picker input class for widgets of this package."""
+
+ template_name = 'bootstrap_datepicker_plus/date_picker.html'
+ picker_type = 'DATE'
+ format = '%Y-%m-%d'
+ config = {}
+ _default_config = {
+ 'id': None,
+ 'picker_type': None,
+ 'linked_to': None,
+ 'options': {} # final merged options
+ }
+ options = {} # options extended by user
+ options_param = {} # options passed as parameter
+ _default_options = {
+ 'showClose': True,
+ 'showClear': True,
+ 'showTodayButton': True,
+ "locale": "fr",
+ }
+
+ # source: https://github.com/tutorcruncher/django-bootstrap3-datetimepicker
+ # file: /blob/31fbb09/bootstrap3_datetime/widgets.py#L33
+ format_map = (
+ ('DDD', r'%j'),
+ ('DD', r'%d'),
+ ('MMMM', r'%B'),
+ ('MMM', r'%b'),
+ ('MM', r'%m'),
+ ('YYYY', r'%Y'),
+ ('YY', r'%y'),
+ ('HH', r'%H'),
+ ('hh', r'%I'),
+ ('mm', r'%M'),
+ ('ss', r'%S'),
+ ('a', r'%p'),
+ ('ZZ', r'%z'),
+ )
+
+ class Media:
+ """JS/CSS resources needed to render the date-picker calendar."""
+
+ js = (
+ 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/'
+ 'moment-with-locales.min.js',
+ 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/'
+ '4.17.47/js/bootstrap-datetimepicker.min.js',
+ 'bootstrap_datepicker_plus/js/datepicker-widget.js'
+ )
+ css = {'all': (
+ 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/'
+ '4.17.47/css/bootstrap-datetimepicker.css',
+ 'bootstrap_datepicker_plus/css/datepicker-widget.css'
+ ), }
+
+ @classmethod
+ def format_py2js(cls, datetime_format):
+ """Convert python datetime format to moment datetime format."""
+ for js_format, py_format in cls.format_map:
+ datetime_format = datetime_format.replace(py_format, js_format)
+ return datetime_format
+
+ @classmethod
+ def format_js2py(cls, datetime_format):
+ """Convert moment datetime format to python datetime format."""
+ for js_format, py_format in cls.format_map:
+ datetime_format = datetime_format.replace(js_format, py_format)
+ return datetime_format
+
+ def __init__(self, attrs=None, format=None, options=None):
+ """Initialize the Date-picker widget."""
+ self.format_param = format
+ self.options_param = options if options else {}
+ self.config = self._default_config.copy()
+ self.config['id'] = DatePickerDictionary.generate_id()
+ self.config['picker_type'] = self.picker_type
+ self.config['options'] = self._calculate_options()
+ attrs = attrs if attrs else {}
+ if 'class' not in attrs:
+ attrs['class'] = 'form-control'
+ super().__init__(attrs, self._calculate_format())
+
+ def _calculate_options(self):
+ """Calculate and Return the options."""
+ _options = self._default_options.copy()
+ _options.update(self.options)
+ if self.options_param:
+ _options.update(self.options_param)
+ return _options
+
+ def _calculate_format(self):
+ """Calculate and Return the datetime format."""
+ _format = self.format_param if self.format_param else self.format
+ if self.config['options'].get('format'):
+ _format = self.format_js2py(self.config['options'].get('format'))
+ else:
+ self.config['options']['format'] = self.format_py2js(_format)
+ return _format
+
+ def get_context(self, name, value, attrs):
+ """Return widget context dictionary."""
+ context = super().get_context(
+ name, value, attrs)
+ context['widget']['attrs']['dp_config'] = json_dumps(self.config)
+ return context
+
+ def start_of(self, event_id):
+ """
+ Set Date-Picker as the start-date of a date-range.
+
+ Args:
+ - event_id (string): User-defined unique id for linking two fields
+ """
+ DatePickerDictionary.items[str(event_id)] = self
+ return self
+
+ def end_of(self, event_id, import_options=True):
+ """
+ Set Date-Picker as the end-date of a date-range.
+
+ Args:
+ - event_id (string): User-defined unique id for linking two fields
+ - import_options (bool): inherit options from start-date input,
+ default: TRUE
+ """
+ event_id = str(event_id)
+ if event_id in DatePickerDictionary.items:
+ linked_picker = DatePickerDictionary.items[event_id]
+ self.config['linked_to'] = linked_picker.config['id']
+ if import_options:
+ backup_moment_format = self.config['options']['format']
+ self.config['options'].update(linked_picker.config['options'])
+ self.config['options'].update(self.options_param)
+ if self.format_param or 'format' in self.options_param:
+ self.config['options']['format'] = backup_moment_format
+ else:
+ self.format = linked_picker.format
+ # Setting useCurrent is necessary, see following issue
+ # https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075
+ self.config['options']['useCurrent'] = False
+ self._link_to(linked_picker)
+ else:
+ raise KeyError(
+ 'start-date not specified for event_id "%s"' % event_id)
+ return self
+
+ def _link_to(self, linked_picker):
+ """
+ Executed when two date-inputs are linked together.
+
+ This method for sub-classes to override to customize the linking.
+ """
+ pass
+
+
+class DatePickerInput(BasePickerInput):
+ """
+ Widget to display a Date-Picker Calendar on a DateField property.
+
+ Args:
+ - attrs (dict): HTML attributes of rendered HTML input
+ - format (string): Python DateTime format eg. "%Y-%m-%d"
+ - options (dict): Options to customize the widget, see README
+ """
+
+ picker_type = 'DATE'
+ format = '%Y-%m-%d'
+ format_key = 'DATE_INPUT_FORMATS'
+
+
+class TimePickerInput(BasePickerInput):
+ """
+ Widget to display a Time-Picker Calendar on a TimeField property.
+
+ Args:
+ - attrs (dict): HTML attributes of rendered HTML input
+ - format (string): Python DateTime format eg. "%Y-%m-%d"
+ - options (dict): Options to customize the widget, see README
+ """
+
+ picker_type = 'TIME'
+ format = '%H:%M'
+ format_key = 'TIME_INPUT_FORMATS'
+ template_name = 'bootstrap_datepicker_plus/time_picker.html'
+
+
+class DateTimePickerInput(BasePickerInput):
+ """
+ Widget to display a DateTime-Picker Calendar on a DateTimeField property.
+
+ Args:
+ - attrs (dict): HTML attributes of rendered HTML input
+ - format (string): Python DateTime format eg. "%Y-%m-%d"
+ - options (dict): Options to customize the widget, see README
+ """
+
+ picker_type = 'DATETIME'
+ format = '%Y-%m-%d %H:%M'
+ format_key = 'DATETIME_INPUT_FORMATS'
+
+
+class MonthPickerInput(BasePickerInput):
+ """
+ Widget to display a Month-Picker Calendar on a DateField property.
+
+ Args:
+ - attrs (dict): HTML attributes of rendered HTML input
+ - format (string): Python DateTime format eg. "%Y-%m-%d"
+ - options (dict): Options to customize the widget, see README
+ """
+
+ picker_type = 'MONTH'
+ format = '01/%m/%Y'
+ format_key = 'DATE_INPUT_FORMATS'
+
+
+class YearPickerInput(BasePickerInput):
+ """
+ Widget to display a Year-Picker Calendar on a DateField property.
+
+ Args:
+ - attrs (dict): HTML attributes of rendered HTML input
+ - format (string): Python DateTime format eg. "%Y-%m-%d"
+ - options (dict): Options to customize the widget, see README
+ """
+
+ picker_type = 'YEAR'
+ format = '01/01/%Y'
+ format_key = 'DATE_INPUT_FORMATS'
+
+ def _link_to(self, linked_picker):
+ """Customize the options when linked with other date-time input"""
+ yformat = self.config['options']['format'].replace('-01-01', '-12-31')
+ self.config['options']['format'] = yformat
\ No newline at end of file
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index d49b2542..61e5ea51 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -48,12 +48,10 @@ INSTALLED_APPS = [
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'django.forms',
# API
'rest_framework',
'rest_framework.authtoken',
- # Autocomplete
- 'dal',
- 'dal_select2',
# Note apps
'activity',
@@ -100,6 +98,8 @@ TEMPLATES = [
},
]
+FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
+
WSGI_APPLICATION = 'note_kfet.wsgi.application'
# Password validation
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index 40a9a614..a7afab29 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -15,13 +15,14 @@ urlpatterns = [
# Include project routers
path('note/', include('note.urls')),
+ path('accounts/', include('member.urls')),
+ path('activity/', include('activity.urls')),
path('treasury/', include('treasury.urls')),
# Include Django Contrib and Core routers
path('i18n/', include('django.conf.urls.i18n')),
path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/', admin.site.urls),
- path('accounts/', include('member.urls')),
path('accounts/login/', CustomLoginView.as_view()),
path('accounts/', include('django.contrib.auth.urls')),
path('api/', include('api.urls')),
@@ -42,7 +43,7 @@ if "cas" in settings.INSTALLED_APPS:
# Include CAS Client routers
path('accounts/login/cas/', cas_views.login, name='cas_login'),
path('accounts/logout/cas/', cas_views.logout, name='cas_logout'),
-
+
]
if "debug_toolbar" in settings.INSTALLED_APPS:
import debug_toolbar
diff --git a/requirements/base.txt b/requirements/base.txt
index 6c5fbc4c..9c978ed0 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -3,7 +3,6 @@ chardet==3.0.4
defusedxml==0.6.0
Django~=2.2
django-allauth==0.39.1
-django-autocomplete-light==3.5.1
django-crispy-forms==1.7.2
django-extensions==2.1.9
django-filter==2.2.0
diff --git a/static/bootstrap_datepicker_plus/css/datepicker-widget.css b/static/bootstrap_datepicker_plus/css/datepicker-widget.css
new file mode 100644
index 00000000..baeec507
--- /dev/null
+++ b/static/bootstrap_datepicker_plus/css/datepicker-widget.css
@@ -0,0 +1,121 @@
+@font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot');
+ src: url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
+ url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2') format('woff2'),
+ url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff') format('woff'),
+ url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
+ url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.glyphicon-time:before {
+ content: "\e023";
+}
+
+.glyphicon-chevron-left:before {
+ content: "\e079";
+}
+
+.glyphicon-chevron-right:before {
+ content: "\e080";
+}
+
+.glyphicon-chevron-up:before {
+ content: "\e113";
+}
+
+.glyphicon-chevron-down:before {
+ content: "\e114";
+}
+
+.glyphicon-calendar:before {
+ content: "\e109";
+}
+
+.glyphicon-screenshot:before {
+ content: "\e087";
+}
+
+.glyphicon-trash:before {
+ content: "\e020";
+}
+
+.glyphicon-remove:before {
+ content: "\e014";
+}
+
+.bootstrap-datetimepicker-widget .btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+
+.bootstrap-datetimepicker-widget.dropdown-menu {
+ position: absolute;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ font-size: 14px;
+ text-align: left;
+ list-style: none;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, .15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+}
+
+.bootstrap-datetimepicker-widget .list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+
+.bootstrap-datetimepicker-widget .collapse {
+ display: none;
+}
+
+.bootstrap-datetimepicker-widget .collapse.in {
+ display: block;
+}
+
+/* fix for bootstrap4 */
+.bootstrap-datetimepicker-widget .table-condensed > thead > tr > th,
+.bootstrap-datetimepicker-widget .table-condensed > tbody > tr > td,
+.bootstrap-datetimepicker-widget .table-condensed > tfoot > tr > td {
+ padding: 5px;
+}
diff --git a/static/bootstrap_datepicker_plus/js/datepicker-widget.js b/static/bootstrap_datepicker_plus/js/datepicker-widget.js
new file mode 100644
index 00000000..2288b46b
--- /dev/null
+++ b/static/bootstrap_datepicker_plus/js/datepicker-widget.js
@@ -0,0 +1,55 @@
+jQuery(function ($) {
+ var datepickerDict = {};
+ var isBootstrap4 = $.fn.collapse.Constructor.VERSION.split('.').shift() == "4";
+ function fixMonthEndDate(e, picker) {
+ e.date && picker.val().length && picker.val(e.date.endOf('month').format('YYYY-MM-DD'));
+ }
+ $("[dp_config]:not([disabled])").each(function (i, element) {
+ var $element = $(element), data = {};
+ try {
+ data = JSON.parse($element.attr('dp_config'));
+ }
+ catch (x) { }
+ if (data.id && data.options) {
+ data.$element = $element.datetimepicker(data.options);
+ data.datepickerdata = $element.data("DateTimePicker");
+ datepickerDict[data.id] = data;
+ data.$element.next('.input-group-addon').on('click', function(){
+ data.datepickerdata.show();
+ });
+ if(isBootstrap4){
+ data.$element.on("dp.show", function (e) {
+ $('.collapse.in').addClass('show');
+ });
+ }
+ }
+ });
+ $.each(datepickerDict, function (id, to_picker) {
+ if (to_picker.linked_to) {
+ var from_picker = datepickerDict[to_picker.linked_to];
+ from_picker.datepickerdata.maxDate(to_picker.datepickerdata.date() || false);
+ to_picker.datepickerdata.minDate(from_picker.datepickerdata.date() || false);
+ from_picker.$element.on("dp.change", function (e) {
+ to_picker.datepickerdata.minDate(e.date || false);
+ });
+ to_picker.$element.on("dp.change", function (e) {
+ if (to_picker.picker_type == 'MONTH') fixMonthEndDate(e, to_picker.$element);
+ from_picker.datepickerdata.maxDate(e.date || false);
+ });
+ if (to_picker.picker_type == 'MONTH') {
+ to_picker.$element.on("dp.hide", function (e) {
+ fixMonthEndDate(e, to_picker.$element);
+ });
+ fixMonthEndDate({ date: to_picker.datepickerdata.date() }, to_picker.$element);
+ }
+ }
+ });
+ if(isBootstrap4) {
+ $('body').on('show.bs.collapse','.bootstrap-datetimepicker-widget .collapse',function(e){
+ $(e.target).addClass('in');
+ });
+ $('body').on('hidden.bs.collapse','.bootstrap-datetimepicker-widget .collapse',function(e){
+ $(e.target).removeClass('in');
+ });
+ }
+});
diff --git a/static/js/autocomplete_model.js b/static/js/autocomplete_model.js
new file mode 100644
index 00000000..e2a3f0cb
--- /dev/null
+++ b/static/js/autocomplete_model.js
@@ -0,0 +1,34 @@
+$(document).ready(function () {
+ $(".autocomplete").keyup(function(e) {
+ let target = $("#" + e.target.id);
+ let prefix = target.attr("id");
+ let api_url = target.attr("api_url");
+ let api_url_suffix = target.attr("api_url_suffix");
+ if (!api_url_suffix)
+ api_url_suffix = "";
+ let name_field = target.attr("name_field");
+ if (!name_field)
+ name_field = "name";
+ let input = target.val();
+
+ $.getJSON(api_url + "?format=json&search=^" + input + api_url_suffix, function(objects) {
+ let html = "";
+
+ objects.results.forEach(function (obj) {
+ html += li(prefix + "_" + obj.id, obj[name_field]);
+ });
+
+ $("#" + prefix + "_list").html(html);
+
+ objects.results.forEach(function (obj) {
+ $("#" + prefix + "_" + obj.id).click(function() {
+ target.val(obj[name_field]);
+ $("#" + prefix + "_pk").val(obj.id);
+ });
+
+ if (input === obj[name_field])
+ $("#" + prefix + "_pk").val(obj.id);
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/static/js/base.js b/static/js/base.js
index d21bd433..22d1366a 100644
--- a/static/js/base.js
+++ b/static/js/base.js
@@ -28,15 +28,35 @@ function addMsg(msg, alert_type) {
+ msg + "\n";
msgDiv.html(html);
}
+
/**
* add Muliple error message from err_obj
- * @param err_obj {error_code:erro_message}
+ * @param errs_obj [{error_code:erro_message}]
*/
function errMsg(errs_obj){
for (const err_msg of Object.values(errs_obj)) {
addMsg(err_msg,'danger');
}
}
+
+var reloadWithTurbolinks = (function () {
+ var scrollPosition;
+
+ function reload () {
+ scrollPosition = [window.scrollX, window.scrollY];
+ Turbolinks.visit(window.location.toString(), { action: 'replace' })
+ }
+
+ document.addEventListener('turbolinks:load', function () {
+ if (scrollPosition) {
+ window.scrollTo.apply(window, scrollPosition);
+ scrollPosition = null
+ }
+ });
+
+ return reload;
+})();
+
/**
* Reload the balance of the user on the right top corner
*/
diff --git a/templates/activity/activity_detail.html b/templates/activity/activity_detail.html
new file mode 100644
index 00000000..0ed3c719
--- /dev/null
+++ b/templates/activity/activity_detail.html
@@ -0,0 +1,139 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load render_table from django_tables2 %}
+{% load pretty_money %}
+{% load perms %}
+
+{% block content %}
+
+
+
+
+
+ {% trans 'description'|capfirst %}
+ {{ activity.description }}
+
+ {% trans 'type'|capfirst %}
+ {{ activity.activity_type }}
+
+ {% trans 'start date'|capfirst %}
+ {{ activity.date_start }}
+
+ {% trans 'end date'|capfirst %}
+ {{ activity.date_end }}
+
+ {% if "view_"|has_perm:activity.creater %}
+ {% trans 'creater'|capfirst %}
+ {{ activity.creater }}
+ {% endif %}
+
+ {% trans 'organizer'|capfirst %}
+ {{ activity.organizer }}
+
+ {% trans 'attendees club'|capfirst %}
+ {{ activity.attendees_club }}
+
+ {% trans 'can invite'|capfirst %}
+ {{ activity.activity_type.can_invite|yesno }}
+
+ {% if activity.activity_type.can_invite %}
+ {% trans 'guest entry fee'|capfirst %}
+ {{ activity.activity_type.guest_entry_fee|pretty_money }}
+ {% endif %}
+
+ {% trans 'valid'|capfirst %}
+ {{ activity.valid|yesno }}
+
+ {% trans 'opened'|capfirst %}
+ {{ activity.open|yesno }}
+
+
+
+
+
+
+ {% if guests.data %}
+
+ {% trans "Guests list" %}
+
+ {% render_table guests %}
+
+ {% endif %}
+
+{% endblock %}
+
+{% block extrajavascript %}
+
+{% endblock %}
diff --git a/templates/activity/activity_entry.html b/templates/activity/activity_entry.html
new file mode 100644
index 00000000..c06a5188
--- /dev/null
+++ b/templates/activity/activity_entry.html
@@ -0,0 +1,126 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load render_table from django_tables2 %}
+{% load pretty_money %}
+{% load perms %}
+
+{% block content %}
+
+ {% trans "Return to activity page" %}
+
+
+
+
+
+
+
+
{{ entries.count }} {% if entries.count >= 2 %}{% trans "entries" %}{% else %}{% trans "entry" %}{% endif %}
+ {% render_table table %}
+
+{% endblock %}
+
+{% block extrajavascript %}
+
+{% endblock %}
diff --git a/templates/activity/activity_form.html b/templates/activity/activity_form.html
new file mode 100644
index 00000000..99c254e3
--- /dev/null
+++ b/templates/activity/activity_form.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% load static %}
+{% load i18n %}
+{% load crispy_forms_tags %}
+{% block content %}
+
+{% endblock %}
diff --git a/templates/activity/activity_invite.html b/templates/activity/activity_invite.html
new file mode 100644
index 00000000..8bdb1965
--- /dev/null
+++ b/templates/activity/activity_invite.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+{% load i18n crispy_forms_tags %}
+{% block content %}
+
+{% endblock %}
+
+{% block extrajavascript %}
+
+{% endblock %}
diff --git a/templates/activity/activity_list.html b/templates/activity/activity_list.html
new file mode 100644
index 00000000..f2871fd6
--- /dev/null
+++ b/templates/activity/activity_list.html
@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+{% load render_table from django_tables2 %}
+{% load i18n crispy_forms_tags%}
+{% block content %}
+ {% trans "Upcoming activities" %}
+ {% if upcoming.data %}
+ {% render_table upcoming %}
+ {% else %}
+
+ {% trans "There is no planned activity." %}
+
+ {% endif %}
+
+ {% trans 'New activity' %}
+
+
+
+ {% trans "All activities" %}
+
+ {% render_table table %}
+{% endblock %}
+
+{% block extrajavascript %}
+
+{% endblock %}
diff --git a/templates/base.html b/templates/base.html
index 6a688fc9..26903c2f 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -91,7 +91,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %}
{% if "activity.activity"|not_empty_model_list %}
- {% trans 'Activities' %}
+ {% trans 'Activities' %}
{% endif %}
{% if "treasury.invoice"|not_empty_model_change_list %}
diff --git a/templates/bootstrap_datepicker_plus/date_picker.html b/templates/bootstrap_datepicker_plus/date_picker.html
new file mode 100644
index 00000000..67a11df1
--- /dev/null
+++ b/templates/bootstrap_datepicker_plus/date_picker.html
@@ -0,0 +1,6 @@
+
diff --git a/templates/bootstrap_datepicker_plus/input.html b/templates/bootstrap_datepicker_plus/input.html
new file mode 100644
index 00000000..b2f8c403
--- /dev/null
+++ b/templates/bootstrap_datepicker_plus/input.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/templates/bootstrap_datepicker_plus/time_picker.html b/templates/bootstrap_datepicker_plus/time_picker.html
new file mode 100644
index 00000000..2bd509a3
--- /dev/null
+++ b/templates/bootstrap_datepicker_plus/time_picker.html
@@ -0,0 +1,6 @@
+
diff --git a/templates/member/autocomplete_model.html b/templates/member/autocomplete_model.html
new file mode 100644
index 00000000..2236c6ef
--- /dev/null
+++ b/templates/member/autocomplete_model.html
@@ -0,0 +1,9 @@
+
+
+
diff --git a/templates/member/club_info.html b/templates/member/club_info.html
index 539d9867..1c8e8661 100644
--- a/templates/member/club_info.html
+++ b/templates/member/club_info.html
@@ -13,8 +13,10 @@
{% trans 'name'|capfirst %}
{{ club.name}}
- {% trans 'Club Parent'|capfirst %}
- {{ club.parent_club.name}}
+ {% if club.parent_club %}
+ {% trans 'Club Parent'|capfirst %}
+ {{ club.parent_club.name}}
+ {% endif %}
{% trans 'membership start'|capfirst %}
{{ club.membership_start }}
diff --git a/templates/note/amount_input.html b/templates/note/amount_input.html
new file mode 100644
index 00000000..6ef4a53a
--- /dev/null
+++ b/templates/note/amount_input.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/templates/note/transaction_form.html b/templates/note/transaction_form.html
index d2cd85e9..65aaa635 100644
--- a/templates/note/transaction_form.html
+++ b/templates/note/transaction_form.html
@@ -126,12 +126,7 @@ SPDX-License-Identifier: GPL-2.0-or-later