Compare commits

..

2 commits

Author SHA1 Message Date
Nikola Kubeczkova
6df90a66d3 add wagtail 2025-03-13 08:14:46 +01:00
Nikola Kubeczkova
b9aac50aa4 add wagtail 2025-03-12 16:30:38 +01:00
70 changed files with 1000 additions and 1499 deletions

3
.gitignore vendored
View file

@ -3,6 +3,3 @@ node_modules/
.idea/
services/backend/db.sqlite3
__pycache__/
htmlcov/
media/
.coverage

View file

@ -2,10 +2,9 @@
## Running
1. create .env from .env.example
2. `docker compose run --rm frontend npm install`
3. `docker compose build`
4. `docker compose up`
1. `docker compose run --rm frontend npm install`
2. `docker compose build`
3. `docker compose up`
## Adding new frontend packages
@ -14,4 +13,3 @@
## Adding new backend packages
`docker compose run --rm backend uv add <package>`
`docker compose build` :)

15
TODO Normal file
View file

@ -0,0 +1,15 @@
Nastylovat:
- footer
- lazyload načítat o něco dříve (neli zrušit alespoň mimo galerii)
Naprogramovat:
- tlačítko "načíst další aktuality" v komponentě News (možná přidat i novou stránku /aktuality
- přidat v galerii zvětsovač na obrázky
- přidat volání na api
Naplánovat:
- kurzy + cena: vyskakovací okno na rezervaci s jménem lekce? jak to vyřešit s kalendářem? (stránka /rezervace)
DAVID:
- přidat posílání mailu panu vrchnímu

View file

@ -20,9 +20,6 @@ env = environ.Env()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
@ -31,11 +28,7 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
SECRET_KEY = env.str('SECRET_KEY', 'django-insecure-x&7qy$na7*@u_4(izkfaz2yiea9+t(nf&#p9cnlq6x)_)jkacf')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": lambda request: False,
"IS_RUNNING_TESTS": False,
}
DEBUG = True
DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.history.HistoryPanel',
@ -69,6 +62,20 @@ CORS_ALLOWED_ORIGINS = [
# Application definition
INSTALLED_APPS = [
'wagtail.contrib.forms',
'wagtail.contrib.redirects',
'wagtail.embeds',
'wagtail.sites',
'wagtail.users',
'wagtail.snippets',
'wagtail.documents',
'wagtail.images',
'wagtail.search',
'wagtail.admin',
'wagtail',
'taggit',
'modelcluster',
'corsheaders',
'django.contrib.admin',
'django.contrib.auth',
@ -79,7 +86,6 @@ INSTALLED_APPS = [
'tko.apps.TkoConfig',
'debug_toolbar',
'rest_framework',
'post_office',
]
MIDDLEWARE = [
@ -92,6 +98,8 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
'wagtail.contrib.redirects.middleware.RedirectMiddleware',
]
ROOT_URLCONF = 'backend.urls'
@ -123,7 +131,6 @@ DATABASES = {
'default': env.db('DATABASE_URL')
}
EMAIL_BACKEND = 'post_office.EmailBackend'
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
@ -161,12 +168,20 @@ USE_TZ = True
STATIC_URL = '/static/'
# STATICFILES_DIRS = [
# os.path.join(BASE_DIR, 'static')
# ]
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
WAGTAIL_SITE_NAME = 'Taneční klub Ostrava'

View file

@ -15,21 +15,25 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include
from debug_toolbar.toolbar import debug_toolbar_urls
from wagtail import urls as wagtail_urls
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
from django.conf.urls.static import static
from django.conf import settings
from tko.views import ContactView, NewArticleListView, AllArticleListView, EventListView, GalleryView
from tko.views import ContactView, NewArticleListView, AllArticleListView, EventListView
urlpatterns = [
path('', include(wagtailadmin_urls)),
path('admin/', admin.site.urls),
path('create-contact/', ContactView.as_view(), name='create-contact'),
path('load-articles/', NewArticleListView.as_view(), name='load-articles'),
path('load-all-articles/', AllArticleListView.as_view(), name='load-all-articles'),
path('load-events/', EventListView.as_view(), name='load-events'),
path('load-gallery/', GalleryView.as_view(), name='load-gallery'),
] + debug_toolbar_urls()
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -4,8 +4,7 @@ version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.12"
authors = [
{name = "Jakub Kropáček", email = "kropikuba@gmail.com"},
{name = "Nikola Kubeczková", email = "kubeczkova.n@gmail.com"}
{name = "Jakub Kropáček", email = "kropikuba@gmail.com"}
]
dependencies = [
"django>=5.1.5",
@ -16,10 +15,7 @@ dependencies = [
"django-environ>=0.12.0",
"wait-for-it>=2.3.0",
"django-cors-headers>=4.7.0",
"django-post-office>=3.9.1",
"pytest-django>=4.10.0",
"factory-boy>=3.3.3",
"pytest-cov>=6.0.0",
"wagtail>=6.4.1",
]
[tool.uv]
@ -28,6 +24,3 @@ package = false
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "backend.settings"

View file

@ -19,15 +19,9 @@ class ContactAdmin(admin.ModelAdmin):
return False
class ArticleImageInline(admin.TabularInline): # Or admin.StackedInline for a different layout
model = models.ArticleImage
extra = 1
@admin.register(models.Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'date', 'author']
inlines = [ArticleImageInline]
@admin.register(models.Event)

View file

@ -0,0 +1,39 @@
# Generated by Django 5.1.5 on 2025-03-12 17:04
import datetime
import django.db.models.deletion
import wagtail.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tko', '0004_alter_article_image'),
('wagtailcore', '0094_alter_page_locale'),
('wagtailimages', '0027_image_description'),
]
operations = [
migrations.AlterField(
model_name='article',
name='date',
field=models.DateField(auto_now_add=True),
),
migrations.CreateModel(
name='ArticlePage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('name', models.CharField(max_length=100)),
('content', wagtail.fields.RichTextField()),
('date', models.DateField(default=datetime.date.today)),
('author', models.CharField(max_length=100)),
('active_to', models.DateField(null=True)),
('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='wagtailimages.image')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]

View file

@ -1,32 +0,0 @@
# Generated by Django 5.1.5 on 2025-03-05 15:36
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tko', '0004_alter_article_image'),
]
operations = [
migrations.RemoveField(
model_name='article',
name='image',
),
migrations.AlterField(
model_name='article',
name='date',
field=models.DateField(auto_now_add=True),
),
migrations.CreateModel(
name='ArticleImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.FileField(default='default.png', upload_to='images/%Y')),
('is_main', models.BooleanField(default=False)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='tko.article')),
],
),
]

View file

@ -0,0 +1,47 @@
# Generated by Django 5.1.5 on 2025-03-12 19:01
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tko', '0005_alter_article_date_articlepage'),
('wagtailcore', '0094_alter_page_locale'),
('wagtailimages', '0027_image_description'),
]
operations = [
migrations.CreateModel(
name='EventPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('start_date', models.DateTimeField()),
('end_date', models.DateTimeField()),
('color', models.CharField(max_length=100)),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
migrations.RemoveField(
model_name='articlepage',
name='active_to',
),
migrations.AlterField(
model_name='articlepage',
name='content',
field=models.CharField(max_length=500),
),
migrations.RemoveField(
model_name='articlepage',
name='image',
),
migrations.AddField(
model_name='articlepage',
name='image',
field=models.ManyToManyField(to='wagtailimages.image'),
),
]

View file

@ -1,27 +0,0 @@
# Generated by Django 5.1.5 on 2025-03-10 16:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tko', '0005_remove_article_image_alter_article_date_articleimage'),
]
operations = [
migrations.RemoveField(
model_name='articleimage',
name='is_main',
),
migrations.AddField(
model_name='article',
name='image',
field=models.FileField(blank=True, default='default.png', upload_to='images/%Y'),
),
migrations.AlterField(
model_name='articleimage',
name='image',
field=models.FileField(upload_to='images/%Y'),
),
]

View file

@ -1,27 +0,0 @@
# Generated by Django 5.1.5 on 2025-03-10 18:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tko', '0006_remove_articleimage_is_main_article_image_and_more'),
]
operations = [
migrations.RemoveField(
model_name='article',
name='image',
),
migrations.AddField(
model_name='articleimage',
name='main',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='articleimage',
name='image',
field=models.FileField(default='default.png', upload_to='images/%Y'),
),
]

View file

@ -1,6 +1,11 @@
from django.db import models
from wagtail.admin.panels import FieldPanel
from wagtail.fields import RichTextField
from wagtail.models import Page
from datetime import date
# Create your models here.
class Contact(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
@ -14,23 +19,16 @@ class Contact(models.Model):
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
image = models.FileField(default="default.png", blank=True)
date = models.DateField(auto_now_add=True)
author = models.CharField(max_length=100)
active_to = models.DateField(null=True, blank=True) # do not show some invitation after this date
def __str__(self):
return self.title
class ArticleImage(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='images')
image = models.FileField(upload_to="images/%Y", default="default.png")
main = models.BooleanField(default=False)
def __str__(self):
return f"Image for {self.article.title}, {self.article.date}"
class Event(models.Model):
title = models.CharField(max_length=100)
start_date = models.DateTimeField()
@ -39,3 +37,21 @@ class Event(models.Model):
def __str__(self):
return self.title
class EventPage(Page):
start_date = models.DateTimeField()
end_date = models.DateTimeField()
color = models.CharField(max_length=100)
content_panels = Page.content_panels + ["start_date", "end_date", "color"]
class ArticlePage(Page):
name = models.CharField(max_length=100)
content = models.CharField(max_length=500)
image = models.ManyToManyField('wagtailimages.Image')
date = models.DateField(default=date.today)
author = models.CharField(max_length=100)
content_panels = Page.content_panels + ["name", "content", "image", "date", "author", "expire_at"]

View file

@ -1,7 +1,7 @@
from rest_framework import serializers
from django.utils.timezone import localtime
from tko.models import Contact, Article, Event, ArticleImage
from tko.models import Contact, Article, Event
class ContactSerializer(serializers.ModelSerializer):
@ -10,41 +10,17 @@ class ContactSerializer(serializers.ModelSerializer):
fields = '__all__'
class ArticleImageSerializer(serializers.ModelSerializer):
title = serializers.SerializerMethodField(read_only=True)
class Meta:
model = ArticleImage
fields = ['image', 'title']
@staticmethod
def get_title(obj):
return obj.article.title if obj.article else ''
class ArticleListSerializer(serializers.ModelSerializer):
date = serializers.SerializerMethodField()
image = serializers.SerializerMethodField()
images = ArticleImageSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = ["id", "author", "content", "date", "image", "title", "images"]
fields = ["id", "author", "content", "date", "image", "title"]
@staticmethod
def get_date(obj):
return obj.date.strftime("%-d. %-m. %Y")
def get_image(self, obj):
main_image = next(iter(obj.images.all()), None)
if main_image:
return ArticleImageSerializer(main_image, context=self.context).data
return {
"image": "http://localhost:8000/media/images/image.jpg",
"title": "Výchozí obrázek",
}
class EventListSerializer(serializers.ModelSerializer):
start = serializers.SerializerMethodField()

View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View file

@ -1,62 +0,0 @@
from random import randint
import factory
import datetime
from django.contrib.auth.models import User
from factory.fuzzy import FuzzyDate
from datetime import date
from tko.models import Article, Event, ArticleImage, Contact
class ContactFactory(factory.django.DjangoModelFactory):
class Meta:
model = Contact
name = factory.Faker("name")
email = factory.Faker("email")
phone_number = factory.Faker("phone_number")
content = factory.Faker("text")
class ArticleFactory(factory.django.DjangoModelFactory):
class Meta:
model = Article
title = factory.Faker("name")
content = factory.Faker("text")
date = FuzzyDate(datetime.date(2021, 1, 1), date.today())
author = factory.Faker("name")
active_to = FuzzyDate(datetime.date(2021, 1, 1), datetime.date(2025, 1, 1))
class GalleryFactory(factory.django.DjangoModelFactory):
class Meta:
model = ArticleImage
article = factory.SubFactory(ArticleFactory)
image = factory.django.FileField(filename='image.png')
main = factory.Faker("boolean")
class EventFactory(factory.django.DjangoModelFactory):
class Meta:
model = Event
title = factory.Faker("name")
start_date = FuzzyDate(datetime.date(2021, 1, 1), datetime.date(2025, 1, 1))
end_date = factory.LazyAttribute(
lambda obj: obj.start_date + datetime.timedelta(minutes=randint(5, 5 * 24 * 60))
)
color = factory.Faker("color")
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
username = factory.Faker("name")
email = factory.Faker("email")
password = "password"
is_superuser = True

View file

@ -1,37 +0,0 @@
import pytest
from rest_framework.test import APIClient
from django.urls import reverse
from tko.admin import ContactAdmin
from tko.models import Contact
from tko.tests.factories import UserFactory
from django.contrib.admin.sites import site
# Create your tests here.
@pytest.mark.django_db(transaction=True)
class TestAdmin:
url = reverse("admin:index")
client = APIClient()
def test_has_add_permission(self):
user = UserFactory()
admin = ContactAdmin(Contact, site)
request = self.client.get(self.url)
request.user = user
assert admin.has_add_permission(request) is False
def test_has_delete_permission(self):
user = UserFactory()
admin = ContactAdmin(Contact, site)
request = self.client.get(self.url)
request.user = user
assert admin.has_delete_permission(request) is False
def test_has_change_permission(self):
user = UserFactory()
admin = ContactAdmin(Contact, site)
request = self.client.get(self.url)
request.user = user
assert admin.has_change_permission(request) is False

View file

@ -1,53 +0,0 @@
import pytest
from rest_framework.test import APIClient
from django.urls import reverse
from tko.tests.factories import ArticleFactory
@pytest.mark.django_db(transaction=True)
class TestLoadArticle:
client = APIClient()
@staticmethod
def create_article(old, new):
for _ in range(new): ArticleFactory(active_to=None)
for _ in range(old): ArticleFactory()
@pytest.mark.parametrize(
("articles", "length", "result"),
[
((2, 5), 2, 200),
((2, 1), 1, 200),
((5, 0), 0, 200),
((3, 1), 1, 200),
]
)
def test_load_articles(self, articles, length, result):
self.create_article(*articles)
response = self.client.get(reverse("load-articles"))
assert response.status_code == result
assert len(response.data) == length
@pytest.mark.parametrize(
("articles", "length", "result"),
[
((2, 5), 5, 200),
((2, 1), 1, 200),
((5, 0), 0, 200),
((3, 1), 1, 200),
]
)
def test_load_all_articles(self, articles, length, result):
self.create_article(*articles)
response = self.client.get(
path=reverse("load-all-articles")
)
assert response.status_code == result
assert len(response.data) == length
def test_str_article(self):
title = "Test_article"
assert str(ArticleFactory(title=title)) == title

View file

@ -1,40 +0,0 @@
import pytest
from rest_framework.test import APIClient
from django.urls import reverse
from tko.tests.factories import ContactFactory
# Create your tests here.
@pytest.mark.django_db(transaction=True)
class TestContact:
url = reverse("create-contact")
client = APIClient()
@pytest.mark.parametrize(
("name", "email", "phone", "message", "result"),
[
("name", "email@tko.cz", "770707505", "message", 200),
("name", "email@tko.cz", "", "message", 400),
("name", "", "770707505", "message", 400),
("name", "", "", "message", 400),
("name ahoj", "email@tko.cz", "+420770707505", "message skdo nsdkl skd sakdksd", 200),
("", "email", "770707505", "message", 400),
])
def test_create_contact(self, name, email, phone, message, result):
response = self.client.post(
path=self.url,
data={
"name": name,
"email": email,
"phone_number": phone,
"content": message,
}
)
assert response.status_code == result
if result == 200:
assert str(ContactFactory(
name=name,
email=email,
phone_number=phone,
)) == f"{name}, {email}, {phone}"

View file

@ -1,36 +0,0 @@
import pytest
from rest_framework.test import APIClient
from django.urls import reverse
from tko.tests.factories import EventFactory
# Create your tests here.
@pytest.mark.django_db(transaction=True)
class TestEvent:
url = reverse("load-events")
client = APIClient()
@staticmethod
def create_events(length):
EventFactory.create_batch(size=length)
@pytest.mark.parametrize(
("length", "result"),
[
(2, 200),
(6, 200),
]
)
def test_load_events(self, length, result):
self.create_events(length)
response = self.client.get(self.url)
assert response.status_code == result
assert len(response.data) == length
def test_str_article_image(self):
title = "Test_event"
assert str(
EventFactory(title=title)
) == title

View file

@ -1,65 +0,0 @@
import datetime
import pytest
from rest_framework.test import APIClient
from django.urls import reverse
from tko.tests.factories import GalleryFactory, ArticleFactory
# Create your tests here.
@pytest.mark.django_db(transaction=True)
class TestGallery:
url = reverse("load-gallery")
client = APIClient()
@staticmethod
def create_images(old, new):
for _ in range(new):
article = ArticleFactory(active_to=None)
for _ in range(new): GalleryFactory(article=article)
for _ in range(old):
article = ArticleFactory()
for _ in range(old): GalleryFactory(article=article)
@pytest.mark.parametrize(
("gallery", "length", "result"),
[
((2, 5), 29, 200),
((2, 1), 5, 200),
((4, 2), 20, 200),
((3, 1), 10, 200),
]
)
def test_load_gallery(self, gallery, length, result):
self.create_images(*gallery)
response = self.client.get(self.url)
assert response.status_code == result
assert len(response.data) == length
@pytest.mark.parametrize(
("gallery", "length", "result"),
[
((2, 5), 5, 200),
((2, 1), 1, 200),
((4, 0), 0, 200),
((3, 1), 1, 200),
]
)
def test_load_article_images(self, gallery, length, result):
old, new = gallery
self.create_images(*gallery)
response = self.client.get(reverse("load-articles"))
for article in response.data:
assert len(article["images"]) == new
assert response.status_code == result
def test_str_article_image(self):
title = "Test_article"
assert str(
GalleryFactory(article=ArticleFactory(title=title))
) == f"Image for {title}, {datetime.date.today()}"

View file

@ -1,38 +1,21 @@
from django.utils import timezone
from django.db.models import Q, Prefetch
from post_office import mail
from django.db.models import Q
from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework import permissions
from tko.models import Article, Event, ArticleImage
from tko.serializers import ArticleListSerializer, EventListSerializer, ContactSerializer, ArticleImageSerializer
from tko.models import Article, Event
from tko.serializers import ArticleListSerializer, EventListSerializer, ContactSerializer
class ContactView(CreateAPIView):
serializer_class = ContactSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
mail.send(
recipients="kubeczkova.n@gmail.com",
subject="Zpráva z webu TKO",
message=f"Jméno: {serializer.data['name']}\n"
f"Email: {serializer.data['email']}\n"
f"Telefón: {serializer.data['phone_number']}\n\n"
f"Zpráva: {serializer.data['content']}",
)
return Response(serializer.data)
permission_classes = [permissions.AllowAny]
class NewArticleListView(ListAPIView):
serializer_class = ArticleListSerializer
permission_classes = [permissions.AllowAny]
def get_queryset(self):
return Article.objects.filter(
@ -42,21 +25,15 @@ class NewArticleListView(ListAPIView):
class AllArticleListView(ListAPIView):
serializer_class = ArticleListSerializer
permission_classes = [permissions.AllowAny]
def get_queryset(self):
return Article.objects.filter(
Q(active_to__gte=timezone.now()) | Q(active_to__isnull=True)
).order_by('-date').prefetch_related(
Prefetch('images', queryset=ArticleImage.objects.order_by('-main'))
)
class GalleryView(ListAPIView):
permission=[AllowAny]
queryset = ArticleImage.objects.all().order_by("-article_id", "main")
serializer_class = ArticleImageSerializer
).order_by('-date')
class EventListView(ListAPIView):
queryset = Event.objects.all()
serializer_class = EventListSerializer
permission_classes = [permissions.AllowAny]

View file

@ -0,0 +1,23 @@
from wagtail import hooks
from wagtail.admin.viewsets.pages import PageListingViewSet
from tko.models import ArticlePage
from wagtail.admin.wagtail_hooks import ExplorerMenuItem
class ArticlePageListingViewSet(PageListingViewSet):
icon = "globe"
menu_label = "Articles"
add_to_admin_menu = True
model = ArticlePage
article_page_listing_viewset = ArticlePageListingViewSet("article_pages")
@hooks.register('register_admin_viewset')
def register_article_page_listing_viewset():
return article_page_listing_viewset
# @hooks.register('construct_main_menu')
# def hide_snippets_menu_item(request, menu_items):
# menu_items[:] = [item for item in menu_items if not isinstance(item, ExplorerMenuItem)]

View file

@ -2,6 +2,15 @@ version = 1
revision = 1
requires-python = ">=3.12"
[[package]]
name = "anyascii"
version = "0.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9f/52/93b9ea99063f7cf37fb67f5e3f49480686cbe7f228c48b9d713326223b6e/anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730", size = 214052 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/7b/a9a747e0632271d855da379532b05a62c58e979813814a57fa3b3afeb3a4/anyascii-0.3.2-py3-none-any.whl", hash = "sha256:3b3beef6fc43d9036d3b0529050b0c48bfad8bc960e9e562d7223cfb94fe45d4", size = 289923 },
]
[[package]]
name = "anyio"
version = "4.8.0"
@ -34,13 +43,10 @@ dependencies = [
{ name = "django-cors-headers" },
{ name = "django-debug-toolbar" },
{ name = "django-environ" },
{ name = "django-post-office" },
{ name = "djangorestframework" },
{ name = "factory-boy" },
{ name = "psycopg2-binary" },
{ name = "pytest-cov" },
{ name = "pytest-django" },
{ name = "uvicorn", extra = ["standard"] },
{ name = "wagtail" },
{ name = "wait-for-it" },
]
@ -50,31 +56,68 @@ requires-dist = [
{ name = "django-cors-headers", specifier = ">=4.7.0" },
{ name = "django-debug-toolbar", specifier = ">=5.0.1" },
{ name = "django-environ", specifier = ">=0.12.0" },
{ name = "django-post-office", specifier = ">=3.9.1" },
{ name = "djangorestframework", specifier = ">=3.15.2" },
{ name = "factory-boy", specifier = ">=3.3.3" },
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
{ name = "pytest-django", specifier = ">=4.10.0" },
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.34.0" },
{ name = "wagtail", specifier = ">=6.4.1" },
{ name = "wait-for-it", specifier = ">=2.3.0" },
]
[[package]]
name = "bleach"
version = "6.2.0"
name = "beautifulsoup4"
version = "4.13.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "webencodings" },
{ name = "soupsieve" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/9a/0e33f5054c54d349ea62c277191c020c2d6ef1d65ab2cb1993f91ec846d1/bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f", size = 203083 }
sdist = { url = "https://files.pythonhosted.org/packages/f0/3c/adaf39ce1fb4afdd21b611e3d530b183bb7759c9b673d60db0e347fd4439/beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", size = 619516 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", size = 163406 },
{ url = "https://files.pythonhosted.org/packages/f9/49/6abb616eb3cbab6a7cca303dc02fdf3836de2e0b834bf966a7f5271a34d8/beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16", size = 186015 },
]
[package.optional-dependencies]
css = [
{ name = "tinycss2" },
[[package]]
name = "certifi"
version = "2025.1.31"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
]
[[package]]
name = "charset-normalizer"
version = "3.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
{ url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
{ url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
{ url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
{ url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
{ url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
{ url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
{ url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
{ url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
{ url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
{ url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
{ url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
{ url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
{ url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
{ url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
{ url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
{ url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
{ url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
{ url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
{ url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
{ url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
{ url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
{ url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
{ url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
{ url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
{ url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
]
[[package]]
@ -99,42 +142,12 @@ wheels = [
]
[[package]]
name = "coverage"
version = "7.7.1"
name = "defusedxml"
version = "0.7.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6b/bf/3effb7453498de9c14a81ca21e1f92e6723ce7ebdc5402ae30e4dcc490ac/coverage-7.7.1.tar.gz", hash = "sha256:199a1272e642266b90c9f40dec7fd3d307b51bf639fa0d15980dc0b3246c1393", size = 810332 }
sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/b0/4eaba302a86ec3528231d7cfc954ae1929ec5d42b032eb6f5b5f5a9155d2/coverage-7.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:eff187177d8016ff6addf789dcc421c3db0d014e4946c1cc3fbf697f7852459d", size = 211253 },
{ url = "https://files.pythonhosted.org/packages/fd/68/21b973e6780a3f2457e31ede1aca6c2f84bda4359457b40da3ae805dcf30/coverage-7.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2444fbe1ba1889e0b29eb4d11931afa88f92dc507b7248f45be372775b3cef4f", size = 211504 },
{ url = "https://files.pythonhosted.org/packages/d1/b4/c19e9c565407664390254252496292f1e3076c31c5c01701ffacc060e745/coverage-7.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:177d837339883c541f8524683e227adcaea581eca6bb33823a2a1fdae4c988e1", size = 245566 },
{ url = "https://files.pythonhosted.org/packages/7b/0e/f9829cdd25e5083638559c8c267ff0577c6bab19dacb1a4fcfc1e70e41c0/coverage-7.7.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15d54ecef1582b1d3ec6049b20d3c1a07d5e7f85335d8a3b617c9960b4f807e0", size = 242455 },
{ url = "https://files.pythonhosted.org/packages/29/57/a3ada2e50a665bf6d9851b5eb3a9a07d7e38f970bdd4d39895f311331d56/coverage-7.7.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c82b27c56478d5e1391f2e7b2e7f588d093157fa40d53fd9453a471b1191f2", size = 244713 },
{ url = "https://files.pythonhosted.org/packages/0f/d3/f15c7d45682a73eca0611427896016bad4c8f635b0fc13aae13a01f8ed9d/coverage-7.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:315ff74b585110ac3b7ab631e89e769d294f303c6d21302a816b3554ed4c81af", size = 244476 },
{ url = "https://files.pythonhosted.org/packages/19/3b/64540074e256082b220e8810fd72543eff03286c59dc91976281dc0a559c/coverage-7.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4dd532dac197d68c478480edde74fd4476c6823355987fd31d01ad9aa1e5fb59", size = 242695 },
{ url = "https://files.pythonhosted.org/packages/8a/c1/9cad25372ead7f9395a91bb42d8ae63e6cefe7408eb79fd38797e2b763eb/coverage-7.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:385618003e3d608001676bb35dc67ae3ad44c75c0395d8de5780af7bb35be6b2", size = 243888 },
{ url = "https://files.pythonhosted.org/packages/66/c6/c3e6c895bc5b95ccfe4cb5838669dbe5226ee4ad10604c46b778c304d6f9/coverage-7.7.1-cp312-cp312-win32.whl", hash = "sha256:63306486fcb5a827449464f6211d2991f01dfa2965976018c9bab9d5e45a35c8", size = 213744 },
{ url = "https://files.pythonhosted.org/packages/cc/8a/6df2fcb4c3e38ec6cd7e211ca8391405ada4e3b1295695d00aa07c6ee736/coverage-7.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:37351dc8123c154fa05b7579fdb126b9f8b1cf42fd6f79ddf19121b7bdd4aa04", size = 214546 },
{ url = "https://files.pythonhosted.org/packages/ec/2a/1a254eaadb01c163b29d6ce742aa380fc5cfe74a82138ce6eb944c42effa/coverage-7.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eebd927b86761a7068a06d3699fd6c20129becf15bb44282db085921ea0f1585", size = 211277 },
{ url = "https://files.pythonhosted.org/packages/cf/00/9636028365efd4eb6db71cdd01d99e59f25cf0d47a59943dbee32dd1573b/coverage-7.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2a79c4a09765d18311c35975ad2eb1ac613c0401afdd9cb1ca4110aeb5dd3c4c", size = 211551 },
{ url = "https://files.pythonhosted.org/packages/6f/c8/14aed97f80363f055b6cd91e62986492d9fe3b55e06b4b5c82627ae18744/coverage-7.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b1c65a739447c5ddce5b96c0a388fd82e4bbdff7251396a70182b1d83631019", size = 245068 },
{ url = "https://files.pythonhosted.org/packages/d6/76/9c5fe3f900e01d7995b0cda08fc8bf9773b4b1be58bdd626f319c7d4ec11/coverage-7.7.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:392cc8fd2b1b010ca36840735e2a526fcbd76795a5d44006065e79868cc76ccf", size = 242109 },
{ url = "https://files.pythonhosted.org/packages/c0/81/760993bb536fb674d3a059f718145dcd409ed6d00ae4e3cbf380019fdfd0/coverage-7.7.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bb47cc9f07a59a451361a850cb06d20633e77a9118d05fd0f77b1864439461b", size = 244129 },
{ url = "https://files.pythonhosted.org/packages/00/be/1114a19f93eae0b6cd955dabb5bee80397bd420d846e63cd0ebffc134e3d/coverage-7.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b4c144c129343416a49378e05c9451c34aae5ccf00221e4fa4f487db0816ee2f", size = 244201 },
{ url = "https://files.pythonhosted.org/packages/06/8d/9128fd283c660474c7dc2b1ea5c66761bc776b970c1724989ed70e9d6eee/coverage-7.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bc96441c9d9ca12a790b5ae17d2fa6654da4b3962ea15e0eabb1b1caed094777", size = 242282 },
{ url = "https://files.pythonhosted.org/packages/d4/2a/6d7dbfe9c1f82e2cdc28d48f4a0c93190cf58f057fa91ba2391b92437fe6/coverage-7.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d03287eb03186256999539d98818c425c33546ab4901028c8fa933b62c35c3a", size = 243570 },
{ url = "https://files.pythonhosted.org/packages/cf/3e/29f1e4ce3bb951bcf74b2037a82d94c5064b3334304a3809a95805628838/coverage-7.7.1-cp313-cp313-win32.whl", hash = "sha256:8fed429c26b99641dc1f3a79179860122b22745dd9af36f29b141e178925070a", size = 213772 },
{ url = "https://files.pythonhosted.org/packages/bc/3a/cf029bf34aefd22ad34f0e808eba8d5830f297a1acb483a2124f097ff769/coverage-7.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:092b134129a8bb940c08b2d9ceb4459af5fb3faea77888af63182e17d89e1cf1", size = 214575 },
{ url = "https://files.pythonhosted.org/packages/92/4c/fb8b35f186a2519126209dce91ab8644c9a901cf04f8dfa65576ca2dd9e8/coverage-7.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3154b369141c3169b8133973ac00f63fcf8d6dbcc297d788d36afbb7811e511", size = 212113 },
{ url = "https://files.pythonhosted.org/packages/59/90/e834ffc86fd811c5b570a64ee1895b20404a247ec18a896b9ba543b12097/coverage-7.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:264ff2bcce27a7f455b64ac0dfe097680b65d9a1a293ef902675fa8158d20b24", size = 212333 },
{ url = "https://files.pythonhosted.org/packages/a5/a1/27f0ad39569b3b02410b881c42e58ab403df13fcd465b475db514b83d3d3/coverage-7.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba8480ebe401c2f094d10a8c4209b800a9b77215b6c796d16b6ecdf665048950", size = 256566 },
{ url = "https://files.pythonhosted.org/packages/9f/3b/21fa66a1db1b90a0633e771a32754f7c02d60236a251afb1b86d7e15d83a/coverage-7.7.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:520af84febb6bb54453e7fbb730afa58c7178fd018c398a8fcd8e269a79bf96d", size = 252276 },
{ url = "https://files.pythonhosted.org/packages/d6/e5/4ab83a59b0f8ac4f0029018559fc4c7d042e1b4552a722e2bfb04f652296/coverage-7.7.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88d96127ae01ff571d465d4b0be25c123789cef88ba0879194d673fdea52f54e", size = 254616 },
{ url = "https://files.pythonhosted.org/packages/db/7a/4224417c0ccdb16a5ba4d8d1fcfaa18439be1624c29435bb9bc88ccabdfb/coverage-7.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0ce92c5a9d7007d838456f4b77ea159cb628187a137e1895331e530973dcf862", size = 255707 },
{ url = "https://files.pythonhosted.org/packages/51/20/ff18a329ccaa3d035e2134ecf3a2e92a52d3be6704c76e74ca5589ece260/coverage-7.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0dab4ef76d7b14f432057fdb7a0477e8bffca0ad39ace308be6e74864e632271", size = 253876 },
{ url = "https://files.pythonhosted.org/packages/e4/e8/1d6f1a6651672c64f45ffad05306dad9c4c189bec694270822508049b2cb/coverage-7.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7e688010581dbac9cab72800e9076e16f7cccd0d89af5785b70daa11174e94de", size = 254687 },
{ url = "https://files.pythonhosted.org/packages/6b/ea/1b9a14cf3e2bc3fd9de23a336a8082091711c5f480b500782d59e84a8fe5/coverage-7.7.1-cp313-cp313t-win32.whl", hash = "sha256:e52eb31ae3afacdacfe50705a15b75ded67935770c460d88c215a9c0c40d0e9c", size = 214486 },
{ url = "https://files.pythonhosted.org/packages/cc/bb/faa6bcf769cb7b3b660532a30d77c440289b40636c7f80e498b961295d07/coverage-7.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a6b6b3bd121ee2ec4bd35039319f3423d0be282b9752a5ae9f18724bc93ebe7c", size = 215647 },
{ url = "https://files.pythonhosted.org/packages/52/26/9f53293ff4cc1d47d98367ce045ca2e62746d6be74a5c6851a474eabf59b/coverage-7.7.1-py3-none-any.whl", hash = "sha256:822fa99dd1ac686061e1219b67868e25d9757989cf2259f735a4802497d6da31", size = 203006 },
{ url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 },
]
[[package]]
@ -187,16 +200,90 @@ wheels = [
]
[[package]]
name = "django-post-office"
version = "3.9.1"
name = "django-filter"
version = "25.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "bleach", extra = ["css"] },
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/e9/7369fb453269ded8d2fff58299fe653033702bd15a427bf35353d4606df3/django-post_office-3.9.1.tar.gz", hash = "sha256:3d8f502f7829e4cf83e830d9efd6909bb44690af6bc41c7e4fc5a85d7b04df10", size = 76624 }
sdist = { url = "https://files.pythonhosted.org/packages/b5/40/c702a6fe8cccac9bf426b55724ebdf57d10a132bae80a17691d0cf0b9bac/django_filter-25.1.tar.gz", hash = "sha256:1ec9eef48fa8da1c0ac9b411744b16c3f4c31176c867886e4c48da369c407153", size = 143021 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/12/19fdf17fb1a0d5e4418798cf9ed66b8aee39da564e875ec025627e08abdf/django_post_office-3.9.1-py3-none-any.whl", hash = "sha256:0082c4dd17854f66077ef457cf868c9bc5b6247de99d5755c45ddea1c518ed6f", size = 85671 },
{ url = "https://files.pythonhosted.org/packages/07/a6/70dcd68537c434ba7cb9277d403c5c829caf04f35baf5eb9458be251e382/django_filter-25.1-py3-none-any.whl", hash = "sha256:4fa48677cf5857b9b1347fed23e355ea792464e0fe07244d1fdfb8a806215b80", size = 94114 },
]
[[package]]
name = "django-modelcluster"
version = "6.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/74/6a/08b0c902916d401b59d89f355949ef98e539d6abc6029138d819eb55d702/django-modelcluster-6.4.tar.gz", hash = "sha256:0102d00e6b884721ba21e32edb716548e0dead7880e130aa2e04854bd384a42f", size = 28966 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/60/c3/74539a9481e9311393e16d8f2d880f384e43c1535c05e0fa949b2fe487ef/django_modelcluster-6.4-py2.py3-none-any.whl", hash = "sha256:839b0ddb4a1a6f5fc7d9f2f029614a84350b41315bdb80a9c8b755baaa2297f2", size = 29041 },
]
[[package]]
name = "django-permissionedforms"
version = "0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/94/4b/364fe61a161ead607dc6af311901ba8e62f537f8fdab94b5252cb6efe6d7/django-permissionedforms-0.1.tar.gz", hash = "sha256:4340bb20c4477fffb13b4cc5cccf9f1b1010b64f79956c291c72d2ad2ed243f8", size = 5856 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/5b/216157ff053f955b15b9dcdc13bfb6e406666445164fee9e332e141be96d/django_permissionedforms-0.1-py2.py3-none-any.whl", hash = "sha256:d341a961a27cc77fde8cc42141c6ab55cc1f0cb886963cc2d6967b9674fa47d6", size = 5744 },
]
[[package]]
name = "django-stubs-ext"
version = "5.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/06/7b210e0073c6cb8824bde82afc25f268e8c410a99d3621297f44fa3f6a6c/django_stubs_ext-5.1.3.tar.gz", hash = "sha256:3e60f82337f0d40a362f349bf15539144b96e4ceb4dbd0239be1cd71f6a74ad0", size = 9613 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/52/50125afcf29382b7f9d88a992e44835108dd2f1694d6d17d6d3d6fe06c81/django_stubs_ext-5.1.3-py3-none-any.whl", hash = "sha256:64561fbc53e963cc1eed2c8eb27e18b8e48dcb90771205180fe29fc8a59e55fd", size = 9034 },
]
[[package]]
name = "django-taggit"
version = "6.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/a6/f1beaf8f552fe90c153cc039316ebab942c23dfbc88588dde081fefca816/django_taggit-6.1.0.tar.gz", hash = "sha256:c4d1199e6df34125dd36db5eb0efe545b254dec3980ce5dd80e6bab3e78757c3", size = 38151 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/34/4185c345530b91d05cb82e05d07148f481a5eb5dc2ac44e092b3daa6f206/django_taggit-6.1.0-py3-none-any.whl", hash = "sha256:ab776264bbc76cb3d7e49e1bf9054962457831bd21c3a42db9138b41956e4cf0", size = 75749 },
]
[[package]]
name = "django-tasks"
version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
{ name = "django-stubs-ext" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/63/46f3c2deb2a9df48d46c2f7a067f1286adc674a2a16e99c0344e327a2d4b/django_tasks-0.6.1.tar.gz", hash = "sha256:4086e7eb9e965f79c4ac76f5c3690ec3bf41c461585237b71b4bde729ced9826", size = 26575 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bf/a3/f9c6634f67c5bcd309b17fe756ee4a321779806ab515e7ccc6333a439275/django_tasks-0.6.1-py3-none-any.whl", hash = "sha256:b3648e28bdcda809cb7831f3aff98aa46c327025447c462b8943cce9dfbb0281", size = 36330 },
]
[[package]]
name = "django-treebeard"
version = "4.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bb/24/eaccbce17355380cb3a8fe6ad92a85b76453dc1f0ecd04f48bfe8929065b/django-treebeard-4.7.1.tar.gz", hash = "sha256:846e462904b437155f76e04907ba4e48480716855f88b898df4122bdcfbd6e98", size = 294139 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/79/259966820614746cc4d81762edf97a53bf1e3b8e74797c010d310c6f4a8f/django_treebeard-4.7.1-py3-none-any.whl", hash = "sha256:995c7120153ab999898fe3043bbdcd8a0fc77cc106eb94de7350e9d02c885135", size = 93210 },
]
[[package]]
@ -212,27 +299,30 @@ wheels = [
]
[[package]]
name = "factory-boy"
version = "3.3.3"
name = "draftjs-exporter"
version = "5.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "faker" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/98/75cacae9945f67cfe323829fc2ac451f64517a8a330b572a06a323997065/factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03", size = 164146 }
sdist = { url = "https://files.pythonhosted.org/packages/d1/52/8b98525ab5477410bdbaf279c5fe0a99108211d40818bb769460784c1c41/draftjs_exporter-5.1.0.tar.gz", hash = "sha256:9f44b8dcecb702540e3aab24af2fad8683aec910fe0034c12cfab5d716ac5f84", size = 33500 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/27/8d/2bc5f5546ff2ccb3f7de06742853483ab75bf74f36a92254702f8baecc79/factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc", size = 37036 },
{ url = "https://files.pythonhosted.org/packages/dc/99/26d5524aaa3e89266e0af19332053aa9f8a61b1c39b29c0dc709f43fbb29/draftjs_exporter-5.1.0-py3-none-any.whl", hash = "sha256:c32932b7933b994fd5ea74c1decf47b5c41e13ba06363f5b69b76ef10137d4e9", size = 26302 },
]
[[package]]
name = "faker"
version = "37.1.0"
name = "et-xmlfile"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/a6/b77f42021308ec8b134502343da882c0905d725a4d661c7adeaf7acaf515/faker-37.1.0.tar.gz", hash = "sha256:ad9dc66a3b84888b837ca729e85299a96b58fdaef0323ed0baace93c9614af06", size = 1875707 }
sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/a1/8936bc8e79af80ca38288dd93ed44ed1f9d63beb25447a4c59e746e01f8d/faker-37.1.0-py3-none-any.whl", hash = "sha256:dc2f730be71cb770e9c715b13374d80dbcee879675121ab51f9683d262ae9a1c", size = 1918783 },
{ url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 },
]
[[package]]
name = "filetype"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 },
]
[[package]]
@ -276,30 +366,90 @@ wheels = [
]
[[package]]
name = "iniconfig"
version = "2.1.0"
name = "laces"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 }
dependencies = [
{ name = "django" },
]
sdist = { url = "https://files.pythonhosted.org/packages/74/9a/9192d6a74e2c6db4f705dd98f56be488e47373172c13f4916aeabc4d68b8/laces-0.1.2.tar.gz", hash = "sha256:3218e09c1889ae5cf3fc7a82f5bb63ec0c7879889b6a9760bfc42323c694b84d", size = 29264 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
{ url = "https://files.pythonhosted.org/packages/60/fe/31f76f5cb2579bdda208aa257ce5482653f22ab1bad3e128fe2f803fa2f1/laces-0.1.2-py3-none-any.whl", hash = "sha256:980cdaf9a31e883a2b8198132e2388931a4eb8814f5bfa5d8bba13ff9f657b7c", size = 22462 },
]
[[package]]
name = "packaging"
version = "24.2"
name = "openpyxl"
version = "3.1.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
dependencies = [
{ name = "et-xmlfile" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
{ url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 },
]
[[package]]
name = "pluggy"
version = "1.5.0"
name = "pillow"
version = "11.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
{ url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 },
{ url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 },
{ url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 },
{ url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 },
{ url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 },
{ url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 },
{ url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 },
{ url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 },
{ url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 },
{ url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 },
{ url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 },
{ url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 },
{ url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 },
{ url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 },
{ url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 },
{ url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 },
{ url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 },
{ url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 },
{ url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 },
{ url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 },
{ url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 },
{ url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 },
{ url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 },
{ url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 },
{ url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 },
{ url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 },
{ url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 },
{ url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 },
{ url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 },
{ url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 },
]
[[package]]
name = "pillow-heif"
version = "0.21.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pillow" },
]
sdist = { url = "https://files.pythonhosted.org/packages/65/f5/993804c7c626256e394f2dcb90ee739862ae22151bd7df00e014f5206573/pillow_heif-0.21.0.tar.gz", hash = "sha256:07aee1bff05e5d61feb989eaa745ae21b367011fd66ee48f7732931f8a12b49b", size = 16178019 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/66/e0/5fc46d46c564cc955e83eb7ba7de4686270f88c242529673b4b30084a364/pillow_heif-0.21.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0aaea6ea45257cf74e76666b80b6109f8f56217009534726fa7f6a5694ebd563", size = 5400784 },
{ url = "https://files.pythonhosted.org/packages/f1/ab/9f7095e8c66d6cbb8a9dfeaf107c06e4b570d5fe8cbb8388196499d1578e/pillow_heif-0.21.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f28c2c934f547823de3e204e48866c571d81ebb6b3e8646c32fe2104c570c7b2", size = 3967392 },
{ url = "https://files.pythonhosted.org/packages/85/1b/37817bc4ab5f58386ce335851807d97ed407c81dabed0281cd2941ed5647/pillow_heif-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e10ab63559346fc294b9612502221ddd6bfac8cd74091ace7328fefc1163a167", size = 6965387 },
{ url = "https://files.pythonhosted.org/packages/07/db/1107f9a5c7c8d627367b4741f3bdb67917f9e6bb10ffd76498d2b828432e/pillow_heif-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2a015cfe4afec75551190d93c99dda13410aec89dc468794885b90f870f657", size = 7802240 },
{ url = "https://files.pythonhosted.org/packages/5c/be/2e05c9038236933091be982894e1098fea6e00a0951b923fa6876516c1e5/pillow_heif-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:41693f5d87ed2b5fd01df4a6215045aff14d148a750aa0708c77e71139698154", size = 8301374 },
{ url = "https://files.pythonhosted.org/packages/c5/e6/0ea6a7596c0d1bee265b51f233b4858cb2fe87fb6fa715a31bc412efe4c5/pillow_heif-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8b27031c561ee3485a119c769fc2ef41d81fae1de530857beef935683e09615e", size = 9051087 },
{ url = "https://files.pythonhosted.org/packages/ff/a3/126e24519825c063bb7e70ad28644ae522cea917d2a3e17413ef4bce99cb/pillow_heif-0.21.0-cp312-cp312-win_amd64.whl", hash = "sha256:60196c08e9c256e81054c5da468eb5a0266c931b8564c96283a43e5fd2d7ce0e", size = 8691151 },
{ url = "https://files.pythonhosted.org/packages/c1/87/229c4c3f558e9fd827f5ac7716e190c71ff94440a9dcb41576240aaff55f/pillow_heif-0.21.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:9e67aae3c22a90bc7dfd42c9f0033c53a7d358e0f0d5d29aa42f2f193162fb01", size = 5400786 },
{ url = "https://files.pythonhosted.org/packages/91/07/825f0ada5976faa92fdadd837522d907e01b39e6aed096178eaeda6b2f5f/pillow_heif-0.21.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ee2d68cbc0df8ba6fd9103ac6b550ebafcaa3a179416737a96becf6e5f079586", size = 3967393 },
{ url = "https://files.pythonhosted.org/packages/3f/de/e5e50e0debb5765aa6b1ea0eaad19c347972b9f7954a4ef37f1dc2304317/pillow_heif-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e5c0df7b8c84e4a8c249ba45ceca2453f205028d8a6525612ec6dd0553d925d", size = 6965345 },
{ url = "https://files.pythonhosted.org/packages/ac/c6/3683070be2a9f3ac5c58058fcd91687dea652613b4358ddce6b687012617/pillow_heif-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaedb7f16f3f18fbb315648ba576d0d7bb26b18b50c16281665123c38f73101e", size = 7802164 },
{ url = "https://files.pythonhosted.org/packages/a1/cf/06a9e7e7c24b12f1109f26afa17f4969175ef8e0b98be8d524458914c0fb/pillow_heif-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6724d6a2561f36b06e14e1cd396c004d32717e81528cb03565491ac8679ed760", size = 8301428 },
{ url = "https://files.pythonhosted.org/packages/bf/c5/d3f8d90577085682183028ccc4cb2010d8accd0c5efab0e96146bb480acf/pillow_heif-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf2e2b0abad455a0896118856e82a8d5358dfe5480bedd09ddd6a04b23773899", size = 9051141 },
{ url = "https://files.pythonhosted.org/packages/8b/38/4ee2ed6584b6a00cac9f805f36610691c37d58df609d221d2708e49cf23b/pillow_heif-0.21.0-cp313-cp313-win_amd64.whl", hash = "sha256:1b6ba6c3c4de739a1abf4f7fe0cdd04acd9e0c7fc661985b9a5288d94893a4b1", size = 8691151 },
]
[[package]]
@ -333,46 +483,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
]
[[package]]
name = "pytest"
version = "8.3.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
]
[[package]]
name = "pytest-cov"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage" },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 },
]
[[package]]
name = "pytest-django"
version = "4.10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a5/10/a096573b4b896f18a8390d9dafaffc054c1f613c60bf838300732e538890/pytest_django-4.10.0.tar.gz", hash = "sha256:1091b20ea1491fd04a310fc9aaff4c01b4e8450e3b157687625e16a6b5f3a366", size = 84710 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/4c/a4fe18205926216e1aebe1f125cba5bce444f91b6e4de4f49fa87e322775/pytest_django-4.10.0-py3-none-any.whl", hash = "sha256:57c74ef3aa9d89cae5a5d73fbb69a720a62673ade7ff13b9491872409a3f5918", size = 23975 },
]
[[package]]
name = "python-dotenv"
version = "1.0.1"
@ -408,6 +518,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
]
[[package]]
name = "requests"
version = "2.32.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
@ -417,6 +542,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "soupsieve"
version = "2.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
]
[[package]]
name = "sqlparse"
version = "0.5.3"
@ -427,15 +561,12 @@ wheels = [
]
[[package]]
name = "tinycss2"
version = "1.4.0"
name = "telepath"
version = "0.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "webencodings" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 }
sdist = { url = "https://files.pythonhosted.org/packages/65/58/5de8765687d6f9dfcbb72c89251036c3b901126404f872b936b13377a3fe/telepath-0.3.1.tar.gz", hash = "sha256:925c0609e0a8a6488ec4a55b19d485882cf72223b2b19fe2359a50fddd813c9c", size = 11622 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 },
{ url = "https://files.pythonhosted.org/packages/5f/50/0f246d316f223b14ba5a88fbe2ff0b480d4a6064b866d48008dd57fd5930/telepath-0.3.1-py38-none-any.whl", hash = "sha256:c280aa8e77ad71ce80e96500a4e4d4a32f35b7e0b52e896bb5fde9a5bcf0699a", size = 10926 },
]
[[package]]
@ -456,6 +587,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 },
]
[[package]]
name = "urllib3"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
]
[[package]]
name = "uvicorn"
version = "0.34.0"
@ -500,6 +640,34 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 },
]
[[package]]
name = "wagtail"
version = "6.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyascii" },
{ name = "beautifulsoup4" },
{ name = "django" },
{ name = "django-filter" },
{ name = "django-modelcluster" },
{ name = "django-permissionedforms" },
{ name = "django-taggit" },
{ name = "django-tasks" },
{ name = "django-treebeard" },
{ name = "djangorestframework" },
{ name = "draftjs-exporter" },
{ name = "laces" },
{ name = "openpyxl" },
{ name = "pillow" },
{ name = "requests" },
{ name = "telepath" },
{ name = "willow", extra = ["heif"] },
]
sdist = { url = "https://files.pythonhosted.org/packages/4f/0f/4ad523cf1c2aee06e32e279b1b98d3a4e5577ee952acbc486034d6f191f2/wagtail-6.4.1.tar.gz", hash = "sha256:cec3e6d4920a6d178fa1eb6f4af80b370fdd5570ed924b979104e157f10ca097", size = 6630337 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/44/e2383decf738b3d5491084f6ae758f0115d3bdeddb6f059f8724078c4950/wagtail-6.4.1-py3-none-any.whl", hash = "sha256:85206f86b04876d5596190e042d30de0430fdb5cdd71b6005748c8a0d45066ec", size = 9110827 },
]
[[package]]
name = "wait-for-it"
version = "2.3.0"
@ -548,15 +716,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 },
]
[[package]]
name = "webencodings"
version = "0.5.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 },
]
[[package]]
name = "websockets"
version = "14.2"
@ -587,3 +746,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/13/8b7fc4cb551b9cfd9890f0fd66e53c18a06240319915533b033a56a3d520/websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f", size = 164420 },
{ url = "https://files.pythonhosted.org/packages/7b/c8/d529f8a32ce40d98309f4470780631e971a5a842b60aec864833b3615786/websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b", size = 157416 },
]
[[package]]
name = "willow"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "defusedxml" },
{ name = "filetype" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2b/07/7937bb91ba3373133d903ec6c8a7a3fe0bec6ac964c7f2e532188e230c9b/willow-1.9.0.tar.gz", hash = "sha256:ffac1406275ae30b60e7c6cbd1245f0bc359d1b5731002b18a712aaf424a5102", size = 113373 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/c0/10a11f2dc0dc485a397bd3f66098805b6e39e7317f5acb72b415d1d7a559/willow-1.9.0-py3-none-any.whl", hash = "sha256:11a13097cffe501898cd434bb5761fb6cdbdb774a7853094cb56a4ba57cbbff7", size = 119156 },
]
[package.optional-dependencies]
heif = [
{ name = "pillow-heif" },
]

View file

@ -6,63 +6,50 @@
}
h1 {
font-family: 'Futura', sans-serif;
font-size: 3rem;
color: #333;
text-align: center;
margin-bottom: 1rem;
margin-top: 2rem;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
cursor: default;
}
h2 {
font-family: 'Futura', sans-serif;
font-size: 1.5rem;
color: #CF3476;
text-align: center;
word-break: break-word;
margin-bottom: 2rem;
cursor: default;
margin-bottom: 0.5rem;
}
h3 {
font-family: 'Futura', sans-serif;
font-size: 1.5rem;
color: #666;
text-align: center;
margin-bottom: 0.5rem;
cursor: default;
}
h4 {
font-family: 'Futura', sans-serif;
font-size: 1rem;
color: #666;
text-align: center;
margin-bottom: 0.5rem;
cursor: default;
}
h5 {
font-family: 'Futura', sans-serif;
font-size: 1rem;
color: #aaa;
margin-top: 1rem;
cursor: default;
}
@media (max-width: 600px) {
h1 {
font-size: 1.8rem; /* Smaller size for mobile */
margin-top: 1.5rem;
}
h2 {
font-size: 1.2rem; /* Slightly smaller h2 for mobile */
}
}
.sheet__box {
min-width: 25rem;
margin-left: calc(20% - 2.5rem);
margin-right: calc(20% - 2.5rem);
margin-bottom: 3rem;
}
.app__logo {
@ -71,26 +58,30 @@ h5 {
}
.app__title {
font-family: 'Futura', sans-serif;
font-size: 2rem;
color: #333;
cursor: default;
}
.app__tab {
font-family: 'Futura', sans-serif;
font-size: 2rem;
color: #CF3476;
}
.to_left {
font-family: 'Futura', sans-serif;
font-size: 1rem;
}
.to_right {
font-family: 'Futura', sans-serif;
float: right;
}
.contact {
margin: 20px calc(20% - 40px) 50px;
min-width: 25rem;
border-radius: 15px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
}
@ -100,17 +91,8 @@ h5 {
font-size: 3rem;
color: #333;
margin-bottom: 1rem;
font-family: 'Futura', sans-serif;
font-weight: bold;
cursor: default;
}
.contact__dialog__title {
font-size: 2rem;
color: #333;
margin-bottom: 1rem;
font-weight: bold;
word-break: break-word !important;
cursor: default;
}
.contact__button {
@ -119,81 +101,56 @@ h5 {
background-color: #CF3476;
}
@media (max-width: 600px) {
.contact__title {
font-size: 1.5rem;
}
.contact {
margin: 10px;
padding: 1rem;
}
.v-dialog > .v-card {
border-radius: 0 !important;
height: 100vh;
padding: 1rem;
}
.contact__dialog__title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
word-break: break-word;
}
}
.articles {
display: flex;
justify-content: center;
margin: 0 auto;
max-width: 1200px;
cursor: default;
margin-left: calc(20% - 2.5rem);
margin-right: calc(20% - 2.5rem);
}
.article {
padding: 1rem;
padding: 10px;
min-width: 40%;
border-radius: 0;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
border-radius: 15px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
margin-bottom: 1rem;
cursor: default;
}
.article__image {
border-radius: 0.5rem;
}
.article__title {
font-size: 1.4rem;
font-family: 'Futura', sans-serif;
font-size: 1.5rem;
color: #CF3476;
text-align: left;
margin-top: 0.1rem;
font-weight: bold;
cursor: default;
}
.article__date {
font-family: 'Futura', sans-serif;
font-size: 0.8rem;
text-align: left;
cursor: default;
}
.article__text {
font-family: 'Futura', sans-serif;
font-size: 1rem;
color: #333;
line-height: 1.5;
padding: 0.5rem;
margin-bottom: 1rem;
cursor: default;
}
.article__image:hover {
cursor: pointer;
text-align: left;
min-height: 6rem;
color: #666;
}
.article__sign {
text-align: right;
font-family: 'Futura', sans-serif;
font-size: 1rem;
font-style: italic;
color: #555;
cursor: default;
float: right;
padding-right: 1rem;
color: #333;
}
.show_more {
font-family: 'Futura', sans-serif;
text-align: center;
color: #CF3476;
background: transparent;
@ -207,73 +164,35 @@ h5 {
}
.pricing {
margin-left: auto;
margin-right: auto;
max-width: 1500px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
margin-left: calc(20% - 2.5rem);
margin-right: calc(20% - 2.5rem);
}
.pricing-box {
padding: 1.5rem;
padding: 1rem;
text-align: center;
min-width: 18rem;
min-width: 25rem;
border-radius: 1rem;
height: 100%;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: transform 0.3s ease, box-shadow 0.3s ease;
background: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 230px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
}
.pricing-box:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25);
}
.pricing-content {
flex-grow: 1;
}
.pricing-title {
min-height: 3rem;
cursor: pointer;
}
.pricing-desc {
min-height: 3rem;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.8);
}
.pricing__price {
font-size: 3rem;
font-weight: bold;
font-family: 'Futura', sans-serif;
font-size: 3.5rem;
color: #CF3476;
margin-top: 0.5rem;
text-align: center;
}
.pricing__subtitle {
font-family: 'Futura', sans-serif;
font-size: 1rem;
color: #555;
word-break: break-word;
margin-bottom: 0.5rem;
}
@media (max-width: 600px) {
.pricing-box {
min-width: 100%;
}
.pricing-title {
font-size: 1.4rem;
}
.pricing-desc {
min-height: unset;
}
color: #333;
text-align: center;
}
.trainers__parallax {
@ -282,45 +201,12 @@ h5 {
}
.trainers {
margin-left: auto;
margin-right: auto;
max-width: 1500px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
min-width: 25rem;
background: transparent;
box-shadow: none;
height: 100%;
text-align: center;
padding: 2rem;
cursor: default;
}
.trainer-avatar {
width: 150px;
height: 150px;
margin: 20px auto;
border-radius: 50%;
border: 3px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
@media (max-width: 600px) {
.trainers__parallax {
max-height: 30rem;
}
.trainers {
max-width: 100%;
padding: 1rem;
}
.trainer-avatar {
width: 120px;
height: 120px;
}
margin-left: calc(20% - 2.5rem);
margin-right: calc(20% - 2.5rem);
}
.advantage {
@ -331,140 +217,61 @@ h5 {
margin-top: 1.2rem;
margin-left: calc(20% - 2.5rem);
margin-right: calc(20% - 2.5rem);
cursor: default;
}
.advantage__title {
padding: 0.5rem 0;
font-family: 'Futura', sans-serif;
color: #CF3476;
font-size: 1.5rem;
font-weight: bold;
margin-top: 0.1rem;
cursor: default;
}
.advantage__text {
padding-bottom: 1rem;
font-family: 'Futura', sans-serif;
font-size: 1rem;
color: #333;
cursor: default;
}
@media (max-width: 600px) {
.advantage {
min-width: 100%;
margin: 0.5rem auto;
padding: 0.5rem;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
.advantage__title {
font-size: 1.3rem;
}
.advantage__text {
font-size: 1rem;
padding: 0 1.5rem;
}
}
.about {
min-width: 25rem;
background: transparent;
box-shadow: none;
height: auto;
max-width: 80%;
margin: 0 auto;
text-align: center;
padding: 2rem;
cursor: default;
height: 100%;
margin-left: calc(20% - 2.5rem);
margin-right: calc(20% - 2.5rem);
}
.about__parallax {
max-height: 28rem;
max-height: 30rem;
margin-top: 2rem;
}
.history__parallax {
max-height: 34rem;
margin-top: 2rem;
}
@media (max-width: 600px) {
.history__parallax {
max-height: 58rem;
}
}
.about__title {
font-size: 2.5rem;
font-family: 'Futura', sans-serif;
font-size: 3rem;
text-align: center;
color: #fff;
margin-top: 1rem;
font-weight: bold;
word-break: break-word;
white-space: normal;
display: block;
cursor: default;
}
.about__subtitle {
font-family: 'Futura', sans-serif;
color: #FF3D8C;
font-size: 1.25rem;
margin-bottom: 1rem;
word-break: break-word;
font-size: 1.5rem;
text-align: center;
margin-bottom: 0.5rem;
font-weight: bold;
display: block;
cursor: default;
}
.about__text {
font-family: 'Futura', sans-serif;
color: #ddd;
font-size: 1.125rem;
padding: 0 1rem;
line-height: 1.6;
cursor: default;
}
@media (max-width: 600px) {
.about {
max-width: 95%;
padding: 1rem;
}
.about__parallax {
min-height: 32rem;
}
.about__title {
font-size: 1.5rem;
}
.about__subtitle {
font-size: 1rem;
word-break: break-word;
}
.about__text {
font-size: 0.95rem;
}
}
.masonry-gallery {
columns: 3;
column-gap: 16px;
}
@media (max-width: 600px) {
.masonry-gallery {
columns: 1;
}
}
.masonry-item {
break-inside: avoid;
margin-bottom: 16px;
font-size: 1.2rem;
text-align: center;
}
.footer {
background-color: black;
cursor: default;
}

View file

@ -4,12 +4,12 @@
src="public/dark-dance.jpg"
scale="0.8"
>
<v-container>
<v-card class="about" id="about">
<v-container id="about">
<v-card class="about">
<v-card-title class="about__title">Vítejte v tanečním klubu!</v-card-title>
<v-card-subtitle class="about__subtitle">Objevte kouzlo tance s námi!</v-card-subtitle>
<v-card-subtitle class="about__subtitle">Vítejte na webu Tanečního klubu Ostrava!</v-card-subtitle>
<v-card-text class="about__text">
jste začátečník nebo zkušený tanečník, v našem klubu najdete místo, kde se můžete rozvíjet, bavit a sdílet svou vášeň pro pohyb. Nabízíme kurzy pro všechny věkové kategorie, od společenských tanců po moderní styly.
Objevte kouzlo tance s námi! jste začátečník nebo zkušený tanečník, v našem klubu najdete místo, kde se můžete rozvíjet, bavit a sdílet svou vášeň pro pohyb. Nabízíme kurzy pro všechny věkové kategorie, od společenských tanců po moderní styly.
<br><br>
Přidejte se k nám a nechte tanec proměnit váš život! 💃🕺
<br><br>

View file

@ -1,23 +1,21 @@
<template>
<h1>Přínosy tance</h1>
<h2>Více než jen pohyb</h2>
<h1>Přínosy tance: Více než jen pohyb</h1>
<h2>Tancem k lepšímu životu</h2>
<v-card
v-for="advantage in advantages"
:key="advantage.id"
class="advantage"
>
<v-row align="center" class="pa-2">
<v-col cols="auto">
<v-row>
<v-col cols="12" md="1">
<small-icon :icon="advantage.icon" />
</v-col>
<v-col>
<v-card-title class="advantage__title">
{{ advantage.title }}
{{advantage.title}}
</v-card-title>
</v-col>
</v-row>
<v-card-text class="advantage__text">
{{ advantage.subtitle }}
</v-card-text>

View file

@ -1,9 +1,87 @@
<template>
<v-card class="contact pa-4">
<v-card-title class="contact__title">Kontaktujte nás!</v-card-title>
<lazy-dialog-contact-form />
<v-card class="contact">
<v-form v-model="valid">
<v-container>
<v-card-title class="contact__title">Kontaktujte nás!</v-card-title>
<v-row>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="fullName"
label="Jméno"
variant="underlined"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="email"
label="Emailová adresa"
variant="underlined"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="phone"
label="Telefonní číslo"
variant="underlined"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea
v-model="textField"
label="Váš dotaz *"
variant="underlined"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-btn
class="contact__button"
type="submit"
@click.prevent="sendContact"
>
Poslat
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card>
</template>
<script setup lang="ts">
import './assets/css/main.css'
import {useAPI} from "~/composables/useAPI";
const valid = ref<boolean>(false);
const fullName = ref<string>("");
const email = ref<string>("");
const phone = ref<string>("");
const textField = ref<string>("");
async function sendContact() {
const { data, error } = await useAPI('create-contact/', {
method: "POST",
body: {
name: fullName.value,
email: email.value,
phone_number: phone.value,
content: textField.value,
}
});
}
</script>

View file

@ -2,69 +2,32 @@
<h1 id="courses">Kurzy</h1>
<h2>Vyberte, co Vám nejlépe vyhovuje</h2>
<v-row class="pricing">
<v-col
cols="12"
sm="6"
md="4"
lg="3"
v-for="(course, index) in courses"
:key="index"
>
<v-card class="pricing-box" @click="openDialog = true; chosenCourse = course">
<div class="pricing-content">
<h3 class="pricing-title">{{ course.name }}</h3>
<v-card-subtitle v-if="course.time">
<div class="pricing__subtitle">{{ course.time }}</div>
</v-card-subtitle>
<v-card-title v-if="course.price">
<div class="pricing__price">{{ course.price }}</div>
</v-card-title>
<v-card-text v-if="course.desc">
<div class="pricing__subtitle pricing-desc">{{ course.desc }}</div>
</v-card-text>
</div>
<v-btn class="show_more">
Kontaktujte nás
</v-btn>
</v-card>
</v-col>
</v-row>
<template>
<v-dialog
v-model="openDialog"
:fullscreen="$vuetify.display.smAndDown"
scrollable
transition="dialog-bottom-transition"
>
<v-card class="contact pa-4">
<h1 class="contact__dialog__title d-flex align-center">
<span class="flex-grow-1 text-center" style="margin-left: 40px;">{{ chosenCourse.name }}</span>
<v-btn icon="mdi-close" variant="text" @click="openDialog = false"></v-btn>
</h1>
<v-card-text v-if="chosenCourse.desc" class="text-center">
<div class="pricing__subtitle text-body-1">{{ chosenCourse.desc }}</div>
</v-card-text>
<v-divider class="my-4"></v-divider>
<v-card-title class="contact__dialog__title text-center">
Kontaktujte nás!
</v-card-title>
<dialog-contact-form class="mt-2" />
</v-card>
</v-dialog>
</template>
<!-- <v-btn to="/kurzy" class="show_more">-->
<!-- <v-icon icon="mdi-chevron-down"/>Více informací<v-icon icon="mdi-chevron-down"/>-->
<!-- </v-btn>-->
<v-row class="pricing">
<v-col
cols="12"
md="4"
v-for="(course, index) in courses"
:key="index"
>
<v-card class="pricing-box">
<h3>{{ course.name }}</h3>
<v-card-subtitle><div class="pricing__subtitle">{{ course.time }}</div></v-card-subtitle>
<v-card-title><div class="pricing__price">{{ course.price }}</div></v-card-title>
<v-card-text v-if="course.desc"><div class="pricing__subtitle">{{ course.desc }}</div></v-card-text>
<v-btn class="show_more">
Kontaktujte nás
</v-btn>
</v-card>
</v-col>
</v-row>
<!-- <v-btn to="/kurzy" class="show_more">-->
<!-- <v-icon icon="mdi-chevron-down"/>Více informací<v-icon icon="mdi-chevron-down"/>-->
<!-- </v-btn>-->
</template>
<script setup lang="ts">
import './assets/css/main.css'
const openDialog = ref<boolean>(false);
// TODO: vyskakovací okno s kontaktním formulářem po rozkliku :)
const courses = [
{name: "Sportovní taneční klub", time: "", price: "", desc: "Připojte se k našemu tanečnímu klubu a rozvíjejte své taneční dovednosti v přátelském prostředí. Nabízíme různé styly tance pro všechny úrovně."},
@ -75,5 +38,4 @@ const courses = [
{name: "Pronájem sálu", time: "", price: "", desc: "Hledáte ideální prostor pro tanec nebo jinou aktivitu? Náš taneční sál je k dispozici k pronájmu pro Vaše akce."},
]
const chosenCourse = ref(courses[0]);
</script>

View file

@ -1,6 +1,6 @@
<template>
<v-parallax
class="history__parallax"
class="about__parallax"
src="public/img/black-pink.jpg"
>
<v-container id="about">

View file

@ -1,75 +1,59 @@
<template v-if="articles">
<h1 id="article">Aktuality</h1>
<h2>Přečtěte si aktuality z našeho klubu</h2>
<v-row class="articles">
<v-col
v-for="article in articles"
:key="article.id"
cols="12"
md="6"
>
<v-row class="articles">
<v-col
v-for="article in articles"
:key="article.id"
cols="12"
md="6"
>
<v-card class="article">
<v-row class="article__title">{{ article.title }}</v-row>
<v-row>
<v-col>
<v-img
:src="article.image.image"
:lazy-src="article.image.image"
class="article__image rounded-lg"
@click="openCarousel(article.image, article.images)"
:lazy-src="article.image"
:src="article.image"
aspect-ratio="1"
cover
class="article__image"
>
<template v-slot:placeholder>
<v-row justify="center" align="center" class="fill-height">
<v-progress-circular color="grey-lighten-5" indeterminate />
<v-row>
<v-progress-circular
color="grey-lighten-5"
indeterminate
></v-progress-circular>
</v-row>
</template>
</v-img>
</v-col>
<v-col cols="12" md="8">
<v-col
cols="12"
md="8"
>
<v-row class="article__title">{{ article.title }}</v-row>
<v-row class="article__date">{{ article.date}}</v-row>
<v-row class="article__text">{{ article.content }}</v-row>
<v-row class="article__sign">{{ article.author }}</v-row>
</v-col>
</v-row>
<v-row class="article__sign">Dne {{ article.date }}, {{ article.author }}</v-row>
</v-card>
</v-col>
</v-row>
<v-dialog v-model="showCarousel" max-width="90vw" v-if="selectedImages.length > 0">
<dialog-carousel
v-if="selectedImage"
:image="selectedImage"
:images="selectedImages"
@isActive="showCarousel = false"
/>
</v-dialog>
<v-btn to="/aktuality" class="show_more" v-if="!props.forAll">
<v-icon icon="mdi-chevron-down" />Další aktuality<v-icon icon="mdi-chevron-down" />
<v-btn to="/aktuality" class="show_more">
<v-icon icon="mdi-chevron-down"/>Dalsí aktuality<v-icon icon="mdi-chevron-down"/>
</v-btn>
</template>
<script setup lang="ts">
import './assets/css/main.css'
import { useAPI } from "~/composables/useAPI";
const showCarousel = ref(false);
const selectedImage = ref<ArticleImage | null>(null);
const selectedImages = ref<ArticleImage[]>([]);
const props = defineProps<{
forAll: boolean
}>();
interface ArticleImage {
image: string;
title: string;
}
interface Article {
id: number;
title: string;
image: ArticleImage;
images: ArticleImage[];
image: string;
date: string;
content: string;
author: string;
@ -77,18 +61,14 @@ interface Article {
const articles = ref<Article[]>([]);
function openCarousel(image: ArticleImage, images: ArticleImage[]) {
selectedImage.value = image;
selectedImages.value = images;
showCarousel.value = true;
const { error, data } = await useAPI<Article[]>('load-articles/', {method: "GET"});
if ( data.value ){
articles.value = data.value;
}
const endpoint = props.forAll ? 'load-all-articles/' : 'load-articles/';
const { error, data } = await useAPI<Article[]>(endpoint, { method: "GET" });
if (data.value) {
articles.value = data.value;
} else if (error.value) {
else if (error.value) {
console.error("Error loading articles:", error.value);
}
</script>

View file

@ -1,36 +1,26 @@
<template>
<v-parallax class="trainers__parallax" src="public/img/black-pink.jpg">
<v-container>
<v-card class="about" id="trainers">
<v-parallax
class="trainers__parallax"
src="public/img/black-pink.jpg"
>
<v-container id="trainers">
<v-card class="trainers">
<v-card-title class="about__title">Naši trenéři a lektoři</v-card-title>
<v-card-subtitle class="about__subtitle">Seznamte se s námi!</v-card-subtitle>
<v-carousel style="height: auto" cycle interval="4000" hide-delimiters :show-arrows="trainerGroups.length > 1 ? 'hover' : false">
<v-carousel-item v-for="(group, index) in trainerGroups" :key="index">
<v-row justify="center">
<v-col
v-for="(lector, index) in group"
:key="index"
cols="12"
sm="6"
md="4"
lg="3"
class="text-center"
<v-row>
<v-col class="text-center" cols="12" md="3" v-for="lector in lectors" :key="lector.id">
<v-avatar
color="none"
rounded="1"
size="150"
style="margin: 30px"
>
<v-avatar
color="none"
rounded="1"
size="150"
style="margin: 30px"
>
<v-img :src="lector.img" cover></v-img>
</v-avatar>
<div class="about__subtitle">{{ lector.name }}</div>
<div class="about__text">{{ lector.desc }}</div>
</v-col>
</v-row>
</v-carousel-item>
</v-carousel>
<v-img :src="lector.img" cover></v-img>
</v-avatar>
<v-card-subtitle class="about__subtitle">{{lector.name}}</v-card-subtitle>
<v-card-text class="about__text">{{ lector.desc }}</v-card-text>
</v-col>
</v-row>
</v-card>
</v-container>
</v-parallax>
@ -40,43 +30,28 @@ import './assets/css/main.css'
const lectors = [
{
id: 1,
name: "Ondřej Gilar",
img: "/trainers/img.png",
desc: "Trenér - latinskoamerické tance a Pro-AM",
},
{
id: 2,
name: "Leona Hruštincová",
img: "/trainers/img.png",
desc: "Lektorka - tance pro děti",
},
{
id: 1,
name: "Ondřej Gilar",
img: "/trainers/img.png",
desc: "Trenér - latinskoamerické tance a Pro-AM",
},
{
id: 2,
name: "Leona Hruštincová",
img: "/trainers/img.png",
desc: "Lektorka - tance pro děti",
},
{
name: "Leona Hruštincová",
img: "/trainers/img.png",
desc: "Lektorka - tance pro děti",
},
{
name: "Ondřej Gilar",
img: "/trainers/img.png",
desc: "Trenér - latinskoamerické tance a Pro-AM",
},
]
const trainerGroups = computed(() => {
const groups: typeof lectors[] = [];
const perSlide = window.innerWidth >= 1280 ? 4 : window.innerWidth >= 600 ? 2 : 1;
for (let i = 0; i < lectors.length; i += perSlide) {
groups.push(lectors.slice(i, i + perSlide));
}
return groups;
});
</script>

View file

@ -1,46 +0,0 @@
<template>
<v-card class="article__image">
<v-card-title>
{{ images[model] && images[model].title }}
<v-icon class="to_right" @click="$emit('isActive', false)">mdi-close</v-icon>
</v-card-title>
<v-carousel
v-model="model"
:hide-delimiters="true"
height="90vh"
>
<v-carousel-item
v-for="(item, i) in props.images"
:key="i"
:src="item.image"
cover
class="carousel__image"
/>
</v-carousel>
</v-card>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import './assets/css/main.css';
interface ArticleImage {
image: string;
title: string;
}
const props = defineProps<{
image: ArticleImage;
images: ArticleImage[];
}>();
defineEmits(["isActive"]);
const model = ref(0);
watch(() => props.image, (newImage) => {
const index = props.images.findIndex((img) => img.image === newImage.image);
if (index !== -1) model.value = index;
}, { immediate: true });
</script>

View file

@ -1,142 +0,0 @@
<template>
<v-form v-model="valid">
<v-snackbar
v-model="showSnackbar"
:color="snackbarColor"
:timeout="5000"
>
{{ snackbarMessage }}
</v-snackbar>
<v-container>
<v-row>
<v-col
cols="12"
md="4"
>
<v-text-field
dense
hide-details="auto"
v-model="fullName"
:error-messages="get(errorMessages, 'name')"
@change="hideError('name')"
label="Jméno"
variant="underlined"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
dense
hide-details="auto"
type="email"
autocomplete="email"
v-model="email"
:error-messages="get(errorMessages, 'email')"
@change="hideError('email')"
label="Emailová adresa"
variant="underlined"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
dense
hide-details="auto"
type="tel"
v-model="phone"
:error-messages="get(errorMessages, 'phone_number')"
@change="hideError('phone_number')"
label="Telefonní číslo"
variant="underlined"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea
v-model="textField"
:error-messages="get(errorMessages, 'content')"
@change="hideError('content')"
label="Váš dotaz"
variant="underlined"
/>
</v-col>
</v-row>
<v-row>
<v-col>
<v-btn
auto-grow
rows="3"
class="contact__button"
type="submit"
@click.prevent="sendContact"
>
Poslat
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</template>
<script setup lang="ts">
import './assets/css/main.css'
import {useAPI} from "~/composables/useAPI";
const valid = ref<boolean>(false);
const errorMessages = ref<Record<string, string[]>>({});
const fullName = ref<string>("");
const email = ref<string>("");
const phone = ref<string>("");
const textField = ref<string>("");
const showSnackbar = ref(false);
const snackbarMessage = ref("");
const snackbarColor = ref("red-accent-4")
function get(obj: any, path: string, defaultValue = undefined) {
return path.split('.').reduce((acc, part) => acc && acc[part], obj) ?? defaultValue;
}
function resetContractData() {
fullName.value = "";
email.value = "";
phone.value = "";
textField.value = "";
}
function hideError (key: string) {
delete errorMessages.value[key];
}
async function sendContact() {
const { error } = await useAPI('create-contact/', {
method: "POST",
body: {
name: fullName.value,
email: email.value,
phone_number: phone.value,
content: textField.value,
}
});
if (error.value) {
snackbarMessage.value = "Něco se pokazilo. Zkuste to znovu.";
snackbarColor.value = "red-accent-4";
errorMessages.value = error.value.data;
} else {
snackbarMessage.value = "Děkujeme! Vaše zpráva byla odeslána.";
snackbarColor.value = "success";
resetContractData();
}
showSnackbar.value = true;
}
</script>

View file

@ -1,142 +1,77 @@
<template>
<ClientOnly>
<v-layout style="margin-bottom: 3rem">
<v-layout>
<v-app-bar style="position: fixed">
<v-app-bar-nav-icon
@click="drawer = !drawer"
class="d-flex d-sm-none"
/>
<div>
<a href="/"><v-img class="app__logo" src="/logo.png" /></a>
</div>
<v-app-bar-title class="app__title" v-if="$vuetify.display.smAndUp">
Taneční klub Ostrava
</v-app-bar-title>
<v-app-bar-title class="app__title">Taneční klub Ostrava</v-app-bar-title>
<v-tabs
v-if="$vuetify.display.smAndUp"
v-model="currentTabName"
class="app__tab"
v-for="(tab, index) in tabs" :key="index"
v-model="currentTab"
align-with-title
class="d-none d-sm-flex app__tab"
>
<v-tab
v-for="(tab, index) in tabs"
:key="index"
:value="tab.name"
@click="handleNavigation(tab)"
>
{{ tab.name }}
</v-tab>
<v-tab v-if="tab.ref" :text="tab.name" :value="tab.name" @click="useGoTo(tab.ref)"></v-tab>
<v-tab v-if="tab.href" :text="tab.name" :value="tab.name" :href="tab.href"></v-tab>
</v-tabs>
<v-menu transition="slide-y-transition">
<template v-slot:activator="{ props }">
<v-app-bar-nav-icon
class="d-flex d-sm-none app__tab ml-auto"
style="padding-right: 1rem"
v-bind="props"
/>
</template>
<v-list>
<v-list-item
v-for="(item, i) in homeTabs"
:key="i"
:value="i"
@click="handleNavigation(item)"
>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<!-- <v-col-->
<!-- class="text-right"-->
<!-- @click="toggleTheme"-->
<!-- >-->
<!-- <big-icon icon="mdi-lightbulb-cfl"/>-->
<!-- </v-col>-->
</v-app-bar>
<v-fab
:key="currentTab.ref"
class="ms-4 mb-4"
></v-fab>
<v-navigation-drawer
v-model="drawer"
absolute
fixed
left
>
<v-list
nav
dense
>
<v-list-item v-for="(tab, index) in tabs" :key="index">
<v-list-item-title @click="useGoTo(tab.ref)">{{ tab.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
</v-layout>
</ClientOnly>
</template>
<script setup lang="ts">
import { computed, onMounted, watch, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useGoTo } from "~/composables/useGoTo";
const route = useRoute();
const router = useRouter();
const currentTab = ref({name: 'O nás', ref: "#about", href: "o-nas"});
const drawer = ref(null)
const tabs = [
{name: 'O nás', ref: "#about", href: ""},
{name: "Trenéři", ref: "#trainers", href: ""},
{name: 'Kurzy', ref: "#courses", href: ""},
{name: 'Galerie', ref: "", href: "/galerie"},
{name: 'Aktuality', ref: "#article", href: ""},
{name: 'Kontakty', ref: "", href: "/kontakty"},
]
const homeTabs = [
{ name: "O nás", href: "#about" },
{ name: "Trenéři", href: "#trainers" },
{ name: "Kurzy", href: "#courses" },
{ name: "Galerie", href: "/galerie" },
{ name: "Aktuality", href: "#article" },
{ name: "Kontakty", href: "/kontakty" },
];
const otherTabs = [
{ name: "O nás", href: "/#about" },
{ name: "Trenéři", href: "/#trainers" },
{ name: "Kurzy", href: "/#courses" },
{ name: "Galerie", href: "/galerie" },
{ name: "Aktuality", href: "/aktuality" },
{ name: "Kontakty", href: "/kontakty" },
];
const tabs = computed(() => (route.path === "/" ? homeTabs : otherTabs));
type Tab = {
name: string;
href: string;
};
const currentTab = ref<Tab>({ name: "O nás", href: "#about" });
const currentTabName = computed<string>({
get: () => currentTab.value.name,
set: (newName: string) => {
const tab = tabs.value.find(t => t.name === newName);
if (tab) handleNavigation(tab);
}
});
const handleNavigation = async (tab: { name: string, href: string }) => {
currentTab.value = tab;
if (tab.href.startsWith("#")) {
await scrollToHash(tab.href);
} else if (tab.href.startsWith("/#")) {
await router.push(tab.href);
} else {
await router.push(tab.href);
}
};
async function scrollToHash(hash: string) {
await nextTick();
const el = document.querySelector(hash);
if (el) {
const yOffset = -80;
const y = el.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({ top: y, behavior: 'smooth' });
}
}
onMounted(() => {
if (route.path === "/" && route.hash) {
const match = homeTabs.find(tab => tab.href === route.hash);
if (match) {
currentTab.value = match;
}
scrollToHash(route.hash);
}
});
watch(() => route.hash, (newHash) => {
if (route.path === "/" && newHash) {
const match = homeTabs.find(tab => tab.href === newHash);
if (match) currentTab.value = match;
scrollToHash(newHash);
}
});
watch(() => route.path, (newPath) => {
if (newPath !== "/") {
const match = otherTabs.find(tab => tab.href === newPath || tab.href === `${newPath}${route.hash}`);
if (match) currentTab.value = match;
}
}, { immediate: true });
// import { useTheme } from 'vuetify'
//
// const theme = useTheme()
//
// function toggleTheme () {
// theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
// }
</script>

View file

@ -1,71 +1,40 @@
import { useRouter, useRoute } from "vue-router";
import { nextTick } from "vue";
export async function useGoTo(
selector: string,
props: { offset?: number } = {}
): Promise<void> {
const router = useRouter();
const route = useRoute();
const yOffset = props?.offset ?? -150;
const hash = selector.startsWith("#") ? selector : "";
const basePath = route.path.split("#")[0];
const targetPath = selector.startsWith("/") ? selector : basePath + hash;
const [pathOnly, hashOnly] = targetPath.split("#");
if (route.path !== pathOnly) {
// Navigate to target page
await router.push(pathOnly + (hashOnly ? `#${hashOnly}` : ""));
await nextTick();
if (hashOnly) {
try {
const element = await waitForElement(`#${hashOnly}`);
scrollToElement(element, yOffset);
} catch (err) {
console.warn(err);
}
}
} else {
// Same page — scroll directly
if (hashOnly || selector.startsWith("#")) {
try {
const element = await waitForElement(`#${hashOnly ?? selector}`);
scrollToElement(element, yOffset);
} catch (err) {
console.warn(err);
}
}
}
}
// Scroll to the element smoothly
function scrollToElement(element: Element, offset: number) {
const y = element.getBoundingClientRect().top + window.pageYOffset + offset;
window.scrollTo({ top: y, behavior: "smooth" });
}
// Wait for the element to exist
function waitForElement(selector: string, timeout = 2000): Promise<Element> {
function waitForElement (selector: string, timeout = 2000) : Promise<Element> {
return new Promise((resolve, reject) => {
const startTime = Date.now();
// eslint-disable-next-line prefer-const
let observer: MutationObserver;
function checkElement() {
function checkElement () {
const element = document.querySelector(selector);
if (element) {
observer.disconnect(); // Stop observing DOM changes
resolve(element);
} else if (Date.now() - startTime >= timeout) {
observer.disconnect(); // Stop observing DOM changes
reject(new Error(`Timeout exceeded while waiting for element '${selector}'`));
} else if (Date.now() - startTime >= timeout) {
reject(new Error(`Timeout exceeded while waiting for element with selector '${selector}'`));
observer.disconnect(); // Stop observing DOM changes
}
}
observer = new MutationObserver(checkElement);
observer.observe(document.body, { childList: true, subtree: true });
checkElement(); // Initial check in case the element already exists
checkElement(); // Check initially in case the element is already present
});
}
export async function useGoTo (selector: string, props: {offset? :number} = {}): Promise<void> {
console.log(selector);
let element: Element;
try {
element = await waitForElement(selector);
} catch {
// element not found
return;
}
const yOffset = props?.offset ?? -80;
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({ top: y, behavior: "smooth" });
}

View file

@ -1,3 +1,70 @@
<template>
<lazy-news :for-all="true"/>
<template v-if="articles">
<h1 id="article">Aktuality</h1>
<h2>Přečtěte si aktuality z našeho klubu</h2>
<v-row>
<v-col
v-for="article in articles"
:key="article.id"
cols="12"
md="6"
>
<v-card class="article">
<v-row>
<v-col>
<v-img
:lazy-src="article.image"
:src="article.image"
aspect-ratio="1"
cover
class="article__image"
>
<template v-slot:placeholder>
<v-row>
<v-progress-circular
color="grey-lighten-5"
indeterminate
></v-progress-circular>
</v-row>
</template>
</v-img>
</v-col>
<v-col
cols="12"
md="8"
>
<v-row class="article__title">{{ article.title }}</v-row>
<v-row class="article__date">{{ article.date}}</v-row>
<v-row class="article__text">{{ article.content }}</v-row>
<v-row class="article__sign">{{ article.author }}</v-row>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
</template>
<script setup lang="ts">
import './assets/css/main.css'
import {useAPI} from "~/composables/useAPI";
interface Article {
id: number;
title: string;
image: string;
date: string;
content: string;
author: string;
}
const articles = ref<Article[]>([]);
const { error, data } = await useAPI('load-all-articles/', {method: "GET"});
if ( data.value ){
articles.value = data.value as Article[];
}
else if (error.value) {
console.error("Error loading articles:", error.value);
}
</script>

View file

@ -1,69 +1,63 @@
<template>
<h1>Galerie</h1>
<div class="masonry-gallery">
<div
class="masonry-item"
v-for="(photo, index) in gallery"
<v-row>
<v-col
v-for="(photo, index) in photos"
:key="index"
@click="openCarousel(photo)"
cols="4"
style="padding: 0;"
>
<v-img
:src="photo.image"
:lazy-src="photo.image"
aspect-ratio=""
class="article__image rounded-lg"
:lazy-src="photo.src"
:src="photo.src"
aspect-ratio="1"
cover
>
<template #placeholder>
<v-row justify="center" align="center" style="height: 100px;">
<v-progress-circular indeterminate color="grey-lighten-1" />
<template v-slot:placeholder>
<v-row>
<v-progress-circular
color="grey-lighten-5"
indeterminate
></v-progress-circular>
</v-row>
</template>
</v-img>
</div>
<v-dialog v-model="showCarousel">
<dialog-carousel
v-if="selectedImage"
:image="selectedImage"
:images="gallery"
@isActive="showCarousel = false"
/>
</v-dialog>
</div>
</v-col>
</v-row>
</template>
<script setup lang="ts">
import './assets/css/main.css'
import { useAPI } from "~/composables/useAPI";
const showCarousel = ref(false);
const selectedImage = ref<ArticleImage | null>(null);
function openCarousel(photo: ArticleImage) {
selectedImage.value = photo;
showCarousel.value = true;
}
interface ArticleImage {
image: string;
title: string;
}
const gallery = ref<ArticleImage[]>([]);
loadImages();
async function loadImages(){
const { error, data } = await useAPI<ArticleImage[]>('load-gallery/', {method: "GET"});
if ( data.value ){
gallery.value = data.value as ArticleImage[];
}
else if (error.value) {
console.error("Error loading gallery:", error.value);
}
}
const photos = [
{src: "https://www.danceus.org/parse/files/Bjy5anNVI0Q81M8bmrwIiuU20x4kepQTxzDBfqpR/70d831b8f51edc1f6e1a4320d52f164b_latin-dance.jpg"},
{src: "https://avatars0.githubusercontent.com/u/9064066?v=4&s=460"},
{src: "https://cdn.vuetifyjs.com/images/profiles/marcus.jpg"},
{src: "https://danceostrava.cz/wp-content/uploads/2021/10/IMG_5203-scaled.jpg"},
{src: "https://cdn11.bigcommerce.com/s-07991/product_images/uploaded_images/latin-dance.jpg"},
{src: "https://www.danceus.org/parse/files/Bjy5anNVI0Q81M8bmrwIiuU20x4kepQTxzDBfqpR/70d831b8f51edc1f6e1a4320d52f164b_latin-dance.jpg"},
{src: "https://avatars0.githubusercontent.com/u/9064066?v=4&s=460"},
{src: "https://cdn.vuetifyjs.com/images/profiles/marcus.jpg"},
{src: "https://danceostrava.cz/wp-content/uploads/2021/10/IMG_5203-scaled.jpg"},
{src: "https://cdn11.bigcommerce.com/s-07991/product_images/uploaded_images/latin-dance.jpg"},
{src: "https://www.danceus.org/parse/files/Bjy5anNVI0Q81M8bmrwIiuU20x4kepQTxzDBfqpR/70d831b8f51edc1f6e1a4320d52f164b_latin-dance.jpg"},
{src: "https://avatars0.githubusercontent.com/u/9064066?v=4&s=460"},
{src: "https://cdn.vuetifyjs.com/images/profiles/marcus.jpg"},
{src: "https://danceostrava.cz/wp-content/uploads/2021/10/IMG_5203-scaled.jpg"},
{src: "https://cdn11.bigcommerce.com/s-07991/product_images/uploaded_images/latin-dance.jpg"},
{src: "https://www.danceus.org/parse/files/Bjy5anNVI0Q81M8bmrwIiuU20x4kepQTxzDBfqpR/70d831b8f51edc1f6e1a4320d52f164b_latin-dance.jpg"},
{src: "https://avatars0.githubusercontent.com/u/9064066?v=4&s=460"},
{src: "https://cdn.vuetifyjs.com/images/profiles/marcus.jpg"},
{src: "https://danceostrava.cz/wp-content/uploads/2021/10/IMG_5203-scaled.jpg"},
{src: "https://cdn11.bigcommerce.com/s-07991/product_images/uploaded_images/latin-dance.jpg"},
{src: "https://www.danceus.org/parse/files/Bjy5anNVI0Q81M8bmrwIiuU20x4kepQTxzDBfqpR/70d831b8f51edc1f6e1a4320d52f164b_latin-dance.jpg"},
{src: "https://avatars0.githubusercontent.com/u/9064066?v=4&s=460"},
{src: "https://cdn.vuetifyjs.com/images/profiles/marcus.jpg"},
{src: "https://danceostrava.cz/wp-content/uploads/2021/10/IMG_5203-scaled.jpg"},
{src: "https://cdn11.bigcommerce.com/s-07991/product_images/uploaded_images/latin-dance.jpg"},
{src: "https://www.danceus.org/parse/files/Bjy5anNVI0Q81M8bmrwIiuU20x4kepQTxzDBfqpR/70d831b8f51edc1f6e1a4320d52f164b_latin-dance.jpg"},
{src: "https://avatars0.githubusercontent.com/u/9064066?v=4&s=460"},
{src: "https://cdn.vuetifyjs.com/images/profiles/marcus.jpg"},
{src: "https://danceostrava.cz/wp-content/uploads/2021/10/IMG_5203-scaled.jpg"},
{src: "https://cdn11.bigcommerce.com/s-07991/product_images/uploaded_images/latin-dance.jpg"},
]
</script>

View file

@ -11,12 +11,12 @@
</h2>
</div>
</v-parallax>
<lazy-news :for-all="false"/>
<lazy-about/>
<lazy-courses/>
<lazy-trainers/>
<lazy-advantages/>
<lazy-history/>
<news/>
<about/>
<courses/>
<trainers/>
<advantages/>
<history/>
</template>
<script setup lang="ts">

View file

@ -1,4 +1,37 @@
<template>
<lazy-courses />
<lazy-calendar/>
<h1 id="courses">Kurzy</h1>
<h2>Vyberte, co Vám nejlépe vyhovuje</h2>
<v-row>
<v-col
cols="12"
md="4"
v-for="(course, index) in courses"
:key="index"
>
<v-card class="pricing-box">
<h3>{{ course.name }}</h3>
<v-card-subtitle><div class="pricing__subtitle">{{ course.time }}</div></v-card-subtitle>
<v-card-title><div class="pricing__price">{{ course.price }}</div></v-card-title>
<v-card-text v-if="course.desc"><div class="pricing__subtitle">{{ course.desc }}</div></v-card-text>
<v-btn class="show_more"><v-icon icon="mdi-chevron-down"/>Kontaktujte nás<v-icon icon="mdi-chevron-down"/></v-btn>
</v-card>
</v-col>
</v-row>
<calendar/>
</template>
<script setup lang="ts">
import './assets/css/main.css'
// TODO: vyskakovací okno s kontaktním formulářem po rozkliku :)
const courses = [
{name: "Sportovní taneční klub", time: "", price: "", desc: "Připojte se k našemu tanečnímu klubu a rozvíjejte své taneční dovednosti v přátelském prostředí. Nabízíme různé styly tance pro všechny úrovně."},
{name: "Svatební tance", time: "", price: "", desc: "Udělejte svůj první tanec nezapomenutelným. Pomůžeme Vám vytvořit choreografii na míru, která bude odrážet Váš jedinečný styl."},
{name: "Příprava na plesovou sezónu", time: "", price: "", desc: "Chcete zazářit na plese? Připravíme Vás na plesovou sezónu a naučíme Vás elegantní taneční kroky a etiketu."},
{name: "Individuální taneční kurzy", time: "", price: "", desc: "Učte se tančit vlastním tempem s individuálním přístupem. Naši zkušení lektoři se zaměří na Vaše potřeby a pomohou Vám dosáhnout Vašich tanečních cílů."},
{name: "Individuální lekce", time: "", price: "", desc: "Zlepšete své taneční dovednosti s intenzivními individuálními lekcemi. Zaměřte se na konkrétní taneční techniky nebo styly, které Vás zajímají."},
{name: "Pronájem sálu", time: "", price: "", desc: "Hledáte ideální prostor pro tanec nebo jinou aktivitu? Náš taneční sál je k dispozici k pronájmu pro Vaše akce."},
]
</script>

View file

@ -49,7 +49,7 @@ export default defineNuxtPlugin((app) => {
defaults: {
SmallIcon: {
color: 'color',
style: [{ 'margin-left': '20px', 'font-size': '30px'}]
style: [{ 'margin-left': '20px', 'margin-top': '15px', 'font-size': '30px'}]
},
BigIcon: {
color: 'color',
@ -62,10 +62,6 @@ export default defineNuxtPlugin((app) => {
cs: {
calendar: {
today: "dnes",
},
carousel: {
next: "další",
prev: "předchozí",
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB