diff --git a/core/admin.py b/core/admin.py index 333a30b..75c2e50 100644 --- a/core/admin.py +++ b/core/admin.py @@ -12,6 +12,7 @@ apps = [ 'gpb', 'tournaments', 'membership', + 'atu', 'core' ] diff --git a/core/templates/atu.html b/core/templates/atu.html new file mode 100644 index 0000000..695e34d --- /dev/null +++ b/core/templates/atu.html @@ -0,0 +1,9 @@ +{% extends 'base.html' %} + +{% block title %}ATU | pdlzbs{% endblock %} + +{% load static tailwind_tags %} {% block content %} +
+
{{ atu.content | safe }}
+
+{% endblock %} diff --git a/core/templates/base.html b/core/templates/base.html index 9090828..ed14bae 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -21,7 +21,7 @@
-
+

Podlaski Związek
Brydża Sportowego

@@ -34,12 +34,12 @@ - +
-
-
ATU
diff --git a/core/templates/calendar.html b/core/templates/calendar.html index d8001f4..3c5c517 100644 --- a/core/templates/calendar.html +++ b/core/templates/calendar.html @@ -2,15 +2,15 @@ load static tailwind_tags %} {% block content %}
{% for calendar in calendars %} -
+

Kalendarz {{ calendar.year }}

{{ calendar.content | safe }}
- {% empty %} + {% if not forloop.last %} +
+ {% endif %} {% empty %} Brak kalendarzy {% endfor %} {% endblock %}
diff --git a/core/templates/home.html b/core/templates/home.html index 8ebad7f..8e6642a 100644 --- a/core/templates/home.html +++ b/core/templates/home.html @@ -5,22 +5,22 @@ class="basis-3/5 mb-auto prose max-w-full bg-white shadow-md rounded-md p-4" >

Grand Prix Województwa

-
{{ gpw.content | safe }}
+ {{ gpw.content | safe }} -
+

Aktualności PodlZBS

-
+
{% for post in posts %}

{{ post.title }}

{{ post.content | safe }}
{% if not forloop.last %} -
+
{% endif %} {% empty %}

Więcej wpisów nie ma

{% endfor %} diff --git a/core/templates/tournaments.html b/core/templates/tournaments.html new file mode 100644 index 0000000..75fb4b4 --- /dev/null +++ b/core/templates/tournaments.html @@ -0,0 +1,36 @@ +{% extends 'base.html' %} + +{% block title %}Składki Członkowskie | pdlzbs{% endblock %} + +{% load static tailwind_tags %} {% block content %} +

Inne turnieje

+{% if tournaments %} +
+ {% for tournament in tournaments %} +
+

{{ tournament.title }}

+
+ +
{{ tournament.content | safe }}
+
+

+ + Wyniki + +

+
+ {% if not forloop.last %} +
+ {% endif %} {% endfor %} +
+{% else %} +

Brak dokumentów

+{% endif %} {% endblock %} diff --git a/core/urls.py b/core/urls.py index e326455..b402184 100644 --- a/core/urls.py +++ b/core/urls.py @@ -15,6 +15,7 @@ urlpatterns = [ path('liga', LeagueView.as_view(), name='league'), path('kalendarz', CalendarView.as_view(), name='calendar'), path('grandprix', GrandPrixView.as_view(), name='gpx'), - path('inneturnieje', HomeView.as_view(), name='others'), + path('inneturnieje', TournamentView.as_view(), name='tournaments'), path('skladki', MembershipView.as_view(), name='membership'), + path('atu', AtuView.as_view(), name='atu'), ] diff --git a/core/views.py b/core/views.py index 1b7b7ad..bb14481 100644 --- a/core/views.py +++ b/core/views.py @@ -10,6 +10,7 @@ from db.calendar.models import * from db.gpb.models import * from db.tournaments.models import * from db.membership.models import * +from db.atu.models import * # Create your views here. @@ -18,40 +19,31 @@ class HomeView(TemplateView): template_name = 'home.html' def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - context['home'] = 'active' - context['gpw'] = GrandPrixW.load() - context['posts'] = Post.objects.all() - - return context + return { + **super().get_context_data(**kwargs), + 'home': 'active', + 'gpw': GrandPrixW.load(), + 'posts': Post.objects.all(), + } class AdministrationView(TemplateView): template_name = 'administration.html' def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - context = { - **context, + return { + **super().get_context_data(**kwargs), 'czlonkowie': Member.objects.all(), 'statut': Statute.objects.first(), } - return context - class AdministrationAnnouncementsView(TemplateView): template_name = 'administration/announcements.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - - context = { - **context, - 'ogloszenia': Announcement.objects.all(), - } + context['ogloszenia'] = Announcement.objects.all() return context @@ -61,11 +53,7 @@ class AdministrationProtocolsView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - - context = { - **context, - 'protokoly': Protocol.objects.all(), - } + context['protokoly'] = Protocol.objects.all() return context @@ -75,11 +63,7 @@ class AdministrationRegulationsView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - - context = { - **context, - 'regulaminy': Regulation.objects.all(), - } + context['regulaminy'] = Regulation.objects.all() return context @@ -89,11 +73,7 @@ class AdministrationRODOView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - - context = { - **context, - 'rodos': RODO.objects.all(), - } + context['rodos'] = RODO.objects.all() return context @@ -123,7 +103,17 @@ class GrandPrixView(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['gp'] = GrandPrixB.objects.first() + context['gp'] = GrandPrixB.load() + + return context + + +class TournamentView(TemplateView): + template_name = "tournaments.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['tournaments'] = Tournament.objects.all() return context @@ -136,3 +126,13 @@ class MembershipView(TemplateView): context['memberships'] = Membership.objects.all() return context + + +class AtuView(TemplateView): + template_name = 'atu.html' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['atu'] = Atu.load() + + return context diff --git a/db/administration/migrations/0010_alter_announcement_attachment_and_more.py b/db/administration/migrations/0010_alter_announcement_attachment_and_more.py new file mode 100644 index 0000000..328ea28 --- /dev/null +++ b/db/administration/migrations/0010_alter_announcement_attachment_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 4.0.5 on 2022-07-25 00:35 + +from django.db import migrations +import filebrowser.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('administration', '0009_protocol_extra'), + ] + + operations = [ + migrations.AlterField( + model_name='announcement', + name='attachment', + field=filebrowser.fields.FileBrowseField(max_length=200, verbose_name='Plik pdf'), + ), + migrations.AlterField( + model_name='protocol', + name='attachment', + field=filebrowser.fields.FileBrowseField(max_length=200, verbose_name='Plik pdf'), + ), + migrations.AlterField( + model_name='regulation', + name='attachment', + field=filebrowser.fields.FileBrowseField(max_length=200, verbose_name='Plik pdf'), + ), + migrations.AlterField( + model_name='rodo', + name='attachment', + field=filebrowser.fields.FileBrowseField(max_length=200, verbose_name='Plik pdf'), + ), + migrations.AlterField( + model_name='statute', + name='attachment', + field=filebrowser.fields.FileBrowseField(max_length=200, verbose_name='Plik pdf'), + ), + ] diff --git a/db/administration/models.py b/db/administration/models.py index e494e52..7b581ae 100644 --- a/db/administration/models.py +++ b/db/administration/models.py @@ -1,29 +1,34 @@ from django.db import models from admin_ordering.models import OrderableModel +from filebrowser.fields import FileBrowseField from core.utils import SingletonModel # Create your models here. class AttachmentModel(OrderableModel): - upload_to = 'zalaczniki' title = models.CharField('Tytuł', max_length=250) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title - def _upload_to(self, filename): - return f'zarzad_{self.upload_to}/{filename}' + def _upload_to(): + return None - attachment = models.FileField('Plik pdf', upload_to=_upload_to) + def upload_to(): + return 'zalaczniki' + + attachment = FileBrowseField( + 'Plik pdf', directory=upload_to, max_length=200, extensions=['.pdf', '.doc']) class Meta(OrderableModel.Meta): abstract = True class Announcement(AttachmentModel): - upload_to = 'ogloszenia' + def upload_to(): + return 'zarzad_ogloszenia' class Meta(OrderableModel.Meta): verbose_name = 'Ogłoszenie' @@ -44,7 +49,9 @@ class Member(OrderableModel): class Protocol(AttachmentModel): - upload_to = 'protokolyuchwaly' + def upload_to(): + return 'zarzad_protokolyuchwaly' + extra = models.TextField('Dodatkowy opis', default='', blank=True) year = models.IntegerField('Rok', blank=True, null=True) @@ -55,7 +62,8 @@ class Protocol(AttachmentModel): class Regulation(AttachmentModel): - upload_to = 'regulaminy' + def upload_to(): + return 'zarzad_regulaminy' class Meta(OrderableModel.Meta): verbose_name = 'Regulamin' @@ -65,7 +73,8 @@ class Regulation(AttachmentModel): class RODO(models.Model): who = models.CharField( 'Przez kogo przetwarzane dane osobowe?', max_length=512) - attachment = models.FileField('Plik pdf', upload_to='zarzad_rodo') + attachment = FileBrowseField( + 'Plik pdf', directory='zarzad_rodo', max_length=200, extensions=['.pdf', '.doc']) def __str__(self): return f'RODO - {self.who}' @@ -77,7 +86,8 @@ class RODO(models.Model): class Statute(SingletonModel): title = models.CharField('Tytuł', max_length=250) - attachment = models.FileField('Plik pdf', upload_to='zarzad_statuty') + attachment = FileBrowseField( + 'Plik pdf', directory='zarzad_statuty', max_length=200, extensions=['.pdf', '.doc']) def __str__(self): return self.title diff --git a/db/atu/__init__.py b/db/atu/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/db/atu/apps.py b/db/atu/apps.py new file mode 100644 index 0000000..a0d0d01 --- /dev/null +++ b/db/atu/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class AtuConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'db.atu' + verbose_name = 'Stowarzyszenie Brydżowe ATU' diff --git a/db/atu/migrations/0001_initial.py b/db/atu/migrations/0001_initial.py new file mode 100644 index 0000000..a49d27f --- /dev/null +++ b/db/atu/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.0.5 on 2022-07-25 00:07 + +from django.db import migrations, models +import tinymce.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Atu', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', tinymce.models.HTMLField(verbose_name='Tekst')), + ], + options={ + 'verbose_name': 'Stowarzyszenie Brydżowe ATU', + 'verbose_name_plural': 'Stowarzyszenie Brydżowe ATU', + }, + ), + ] diff --git a/db/atu/migrations/__init__.py b/db/atu/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/db/atu/models.py b/db/atu/models.py new file mode 100644 index 0000000..2a1fa99 --- /dev/null +++ b/db/atu/models.py @@ -0,0 +1,16 @@ +from django.db import models +from tinymce.models import HTMLField +from core.utils import SingletonModel + +# Create your models here. + + +class Atu(SingletonModel): + content = HTMLField('Tekst') + + def __str__(self): + return 'Stowarzyszenie Brydżowe ATU' + + class Meta: + verbose_name = 'Stowarzyszenie Brydżowe ATU' + verbose_name_plural = 'Stowarzyszenie Brydżowe ATU' diff --git a/db/tournaments/admin.py b/db/tournaments/admin.py new file mode 100644 index 0000000..ab5c413 --- /dev/null +++ b/db/tournaments/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from admin_ordering.admin import OrderableAdmin +from .models import * + +# Register your models here. + + +@admin.register(Tournament) +class TournamentModelAdmin(OrderableAdmin, admin.ModelAdmin): + list_display = ['title', 'ordering'] + list_editable = ['ordering'] + ordering_field_hide_input = True + exclude = ['ordering'] diff --git a/db/tournaments/migrations/0001_initial.py b/db/tournaments/migrations/0001_initial.py new file mode 100644 index 0000000..82c34d8 --- /dev/null +++ b/db/tournaments/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 4.0.5 on 2022-07-24 23:13 + +from django.db import migrations, models +import tinymce.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Tournament', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ordering', models.IntegerField(default=0, verbose_name='Kolejność')), + ('title', models.CharField(max_length=250, verbose_name='Tytuł')), + ('content', tinymce.models.HTMLField(verbose_name='Tekst')), + ('link', models.TextField(verbose_name='Link do wyników')), + ], + options={ + 'verbose_name': 'Inny turniej', + 'verbose_name_plural': 'Inne turnieje', + 'ordering': ['ordering'], + 'abstract': False, + }, + ), + ] diff --git a/db/tournaments/migrations/0002_tournament_photo.py b/db/tournaments/migrations/0002_tournament_photo.py new file mode 100644 index 0000000..a810f4b --- /dev/null +++ b/db/tournaments/migrations/0002_tournament_photo.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.5 on 2022-07-24 23:23 + +from django.db import migrations +import filebrowser.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='tournament', + name='photo', + field=filebrowser.fields.FileBrowseField(blank=True, max_length=200, verbose_name='Zdjęcie'), + ), + ] diff --git a/db/tournaments/models.py b/db/tournaments/models.py index 71a8362..351deb1 100644 --- a/db/tournaments/models.py +++ b/db/tournaments/models.py @@ -1,3 +1,21 @@ from django.db import models +from admin_ordering.models import OrderableModel +from tinymce.models import HTMLField +from filebrowser.fields import FileBrowseField # Create your models here. + + +class Tournament(OrderableModel): + photo = FileBrowseField( + 'Zdjęcie', directory='inneturnieje/', max_length=200, blank=True) + title = models.CharField('Tytuł', max_length=250) + content = HTMLField('Tekst') + link = models.TextField('Link do wyników') + + def __str__(self): + return self.title + + class Meta(OrderableModel.Meta): + verbose_name = 'Inny turniej' + verbose_name_plural = 'Inne turnieje' diff --git a/filebrowser/fields.py b/filebrowser/fields.py index 4441fad..8446414 100644 --- a/filebrowser/fields.py +++ b/filebrowser/fields.py @@ -44,17 +44,20 @@ class FileBrowseWidget(Input): if value != "" and not isinstance(value, FileObject): value = FileObject(value, site=self.site) final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) - final_attrs['search_icon'] = static('filebrowser/img/filebrowser_icon_show.gif') + final_attrs['search_icon'] = static( + 'filebrowser/img/filebrowser_icon_show.gif') final_attrs['url'] = url final_attrs['directory'] = self.directory final_attrs['extensions'] = self.extensions final_attrs['format'] = self.format final_attrs['ADMIN_THUMBNAIL'] = ADMIN_THUMBNAIL - final_attrs['data_attrs'] = {k: v for k, v in final_attrs.items() if k.startswith('data-')} + final_attrs['data_attrs'] = { + k: v for k, v in final_attrs.items() if k.startswith('data-')} filebrowser_site = self.site if value != "": try: - final_attrs['directory'] = os.path.split(value.original.path_relative_directory)[0] + final_attrs['directory'] = os.path.split( + value.original.path_relative_directory)[0] except: pass return render_to_string("filebrowser/custom_field.html", locals()) @@ -89,7 +92,8 @@ class FileBrowseFormField(forms.CharField): return value file_extension = os.path.splitext(value)[1].lower() if self.extensions and file_extension not in self.extensions: - raise forms.ValidationError(self.error_messages['extension'] % {'ext': file_extension, 'allowed': ", ".join(self.extensions)}) + raise forms.ValidationError(self.error_messages['extension'] % { + 'ext': file_extension, 'allowed': ", ".join(self.extensions)}) return value @@ -98,7 +102,8 @@ class FileBrowseField(CharField): def __init__(self, *args, **kwargs): self.site = kwargs.pop('filebrowser_site', site) - self.directory = kwargs.pop('directory', '') + directory = kwargs.pop('directory', '') + self.directory = directory() if callable(directory) else directory self.extensions = kwargs.pop('extensions', '') self.format = kwargs.pop('format', '') return super(FileBrowseField, self).__init__(*args, **kwargs) @@ -139,6 +144,7 @@ class FileBrowseField(CharField): } return super(FileBrowseField, self).formfield(**defaults) + FORMFIELD_FOR_DBFIELD_DEFAULTS[FileBrowseField] = {'widget': FileBrowseWidget} @@ -146,7 +152,8 @@ class FileBrowseUploadWidget(Input): input_type = 'text' class Media: - js = ('filebrowser/js/AddFileBrowser.js', 'filebrowser/js/fileuploader.js',) + js = ('filebrowser/js/AddFileBrowser.js', + 'filebrowser/js/fileuploader.js',) css = { 'all': (os.path.join('/static/filebrowser/css/uploadfield.css'),) } @@ -182,7 +189,8 @@ class FileBrowseUploadWidget(Input): final_attrs['ADMIN_THUMBNAIL'] = ADMIN_THUMBNAIL if value != "": try: - final_attrs['directory'] = os.path.split(value.original.path_relative_directory)[0] + final_attrs['directory'] = os.path.split( + value.original.path_relative_directory)[0] except: pass return render_to_string("filebrowser/custom_upload_field.html", locals()) @@ -219,7 +227,8 @@ class FileBrowseUploadFormField(forms.CharField): return value file_extension = os.path.splitext(value)[1].lower() if self.extensions and file_extension not in self.extensions: - raise forms.ValidationError(self.error_messages['extension'] % {'ext': file_extension, 'allowed': ", ".join(self.extensions)}) + raise forms.ValidationError(self.error_messages['extension'] % { + 'ext': file_extension, 'allowed': ", ".join(self.extensions)}) return value @@ -284,6 +293,7 @@ class FileBrowseUploadField(CharField): try: from south.modelsinspector import add_introspection_rules add_introspection_rules([], [r"^filebrowser\.fields\.FileBrowseField"]) - add_introspection_rules([], [r"^filebrowser\.fields\.FileBrowseUploadField"]) + add_introspection_rules( + [], [r"^filebrowser\.fields\.FileBrowseUploadField"]) except: pass diff --git a/pdlzbs/settings.py b/pdlzbs/settings.py index 10d2748..afb6ad1 100644 --- a/pdlzbs/settings.py +++ b/pdlzbs/settings.py @@ -50,6 +50,7 @@ INSTALLED_APPS = [ 'db.gpb', 'db.tournaments', 'db.membership', + 'db.atu', 'theme', 'core' ] + [ diff --git a/theme/static/css/dist/styles.css b/theme/static/css/dist/styles.css index bafd6b7..75a0c98 100644 --- a/theme/static/css/dist/styles.css +++ b/theme/static/css/dist/styles.css @@ -1255,6 +1255,10 @@ select { margin-bottom: auto; } +.ml-auto { + margin-left: auto; +} + .box-content { box-sizing: content-box; } @@ -1311,6 +1315,18 @@ select { max-height: 3rem; } +.max-h-64 { + max-height: 16rem; +} + +.max-h-96 { + max-height: 24rem; +} + +.max-h-72 { + max-height: 18rem; +} + .min-h-\[125px\] { min-height: 125px; } @@ -1327,6 +1343,10 @@ select { width: 48px; } +.w-36 { + width: 9rem; +} + .min-w-\[145px\] { min-width: 145px; } @@ -1339,6 +1359,12 @@ select { max-width: 100%; } +.max-w-fit { + max-width: -webkit-fit-content; + max-width: -moz-fit-content; + max-width: fit-content; +} + .grow-0 { flex-grow: 0; } @@ -1507,6 +1533,11 @@ select { border-color: rgb(214 211 209 / var(--tw-border-opacity)); } +.border-red-200 { + --tw-border-opacity: 1; + border-color: rgb(254 202 202 / var(--tw-border-opacity)); +} + .bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); @@ -1552,6 +1583,11 @@ select { background-color: rgb(239 246 255 / var(--tw-bg-opacity)); } +.bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} + .p-4 { padding: 1rem; } @@ -1592,6 +1628,10 @@ select { padding-bottom: 1.5rem; } +.pb-4 { + padding-bottom: 1rem; +} + .text-left { text-align: left; } @@ -1604,6 +1644,14 @@ select { text-align: right; } +.align-baseline { + vertical-align: baseline; +} + +.align-middle { + vertical-align: middle; +} + .font-serif { font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; } @@ -1686,6 +1734,14 @@ select { line-height: 1.5; } +.leading-3 { + line-height: .75rem; +} + +.leading-none { + line-height: 1; +} + .tracking-normal { letter-spacing: 0em; } @@ -1695,6 +1751,11 @@ select { color: rgb(255 255 255 / var(--tw-text-opacity)); } +.no-underline { + -webkit-text-decoration-line: none; + text-decoration-line: none; +} + .antialiased { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -1770,6 +1831,11 @@ select { border-color: rgb(165 180 252 / var(--tw-border-opacity)); } +.hover\:border-red-300:hover { + --tw-border-opacity: 1; + border-color: rgb(252 165 165 / var(--tw-border-opacity)); +} + .hover\:bg-pink-200:hover { --tw-bg-opacity: 1; background-color: rgb(251 207 232 / var(--tw-bg-opacity)); @@ -1790,6 +1856,11 @@ select { background-color: rgb(199 210 254 / var(--tw-bg-opacity)); } +.hover\:bg-red-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); +} + .prose-h2\:text-center :is(:where(h2):not(:where([class~="not-prose"] *))) { text-align: center; } @@ -1829,6 +1900,10 @@ select { font-weight: 400; } +.prose-a\:text-center :is(:where(a):not(:where([class~="not-prose"] *))) { + text-align: center; +} + @media (min-width: 1024px) { .lg\:flex-row { flex-direction: row;