diff --git a/TODO b/TODO index 5dae3b0..cfa3b95 100644 --- a/TODO +++ b/TODO @@ -9,3 +9,7 @@ Naprogramovat: 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 \ No newline at end of file diff --git a/services/backend/articles/2025/02/19/terkadavid.jpg b/services/backend/articles/2025/02/19/terkadavid.jpg deleted file mode 100644 index 5089136..0000000 Binary files a/services/backend/articles/2025/02/19/terkadavid.jpg and /dev/null differ diff --git a/services/backend/backend/settings.py b/services/backend/backend/settings.py index d3333bb..bfd011f 100644 --- a/services/backend/backend/settings.py +++ b/services/backend/backend/settings.py @@ -9,6 +9,8 @@ https://docs.djangoproject.com/en/5.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ +import os.path + import environ from pathlib import Path @@ -109,12 +111,6 @@ WSGI_APPLICATION = 'backend.wsgi.application' # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases -# DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.sqlite3', -# 'NAME': BASE_DIR / 'db.sqlite3', -# } -# } DATABASES = { 'default': env.db('DATABASE_URL') } @@ -142,9 +138,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'cs-cz' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Europe/Prague' USE_I18N = True @@ -156,8 +152,14 @@ USE_TZ = True STATIC_URL = '/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 diff --git a/services/backend/backend/urls.py b/services/backend/backend/urls.py index 2fd02fb..0e29a78 100644 --- a/services/backend/backend/urls.py +++ b/services/backend/backend/urls.py @@ -18,11 +18,17 @@ from django.contrib import admin from django.urls import path from debug_toolbar.toolbar import debug_toolbar_urls -from tko.views import ContactView, ArticleListView, EventListView +from django.conf.urls.static import static +from django.conf import settings + +from tko.views import ContactView, NewArticleListView, AllArticleListView, EventListView urlpatterns = [ path('admin/', admin.site.urls), path('create-contact/', ContactView.as_view(), name='create-contact'), - path('load-articles/', ArticleListView.as_view(), name='load-articles'), + 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'), ] + debug_toolbar_urls() + +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/services/backend/tko/migrations/0004_alter_article_image.py b/services/backend/tko/migrations/0004_alter_article_image.py new file mode 100644 index 0000000..826a3a9 --- /dev/null +++ b/services/backend/tko/migrations/0004_alter_article_image.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.5 on 2025-02-21 16:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tko', '0003_rename_first_name_contact_name_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='article', + name='image', + field=models.FileField(blank=True, default='default.png', upload_to=''), + ), + ] diff --git a/services/backend/tko/models.py b/services/backend/tko/models.py index 95ea5fa..f2a4083 100644 --- a/services/backend/tko/models.py +++ b/services/backend/tko/models.py @@ -14,8 +14,8 @@ class Contact(models.Model): class Article(models.Model): title = models.CharField(max_length=100) content = models.TextField() - image = models.FileField(upload_to='articles/%Y/%m/%d', null=True) - date = models.DateField() + 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 diff --git a/services/backend/tko/serializers.py b/services/backend/tko/serializers.py index 9fbb671..e662dd1 100644 --- a/services/backend/tko/serializers.py +++ b/services/backend/tko/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers +from django.utils.timezone import localtime from tko.models import Contact, Article, Event @@ -10,12 +11,29 @@ class ContactSerializer(serializers.ModelSerializer): class ArticleListSerializer(serializers.ModelSerializer): + date = serializers.SerializerMethodField() + class Meta: model = Article - fields = '__all__' + fields = ["id", "author", "content", "date", "image", "title"] + + @staticmethod + def get_date(obj): + return obj.date.strftime("%-d. %-m. %Y") class EventListSerializer(serializers.ModelSerializer): + start = serializers.SerializerMethodField() + end = serializers.SerializerMethodField() + class Meta: model = Event - fields = '__all__' \ No newline at end of file + fields = ["title", "end", "start", "color"] + + @staticmethod + def get_start(obj): + return localtime(obj.start_date).isoformat() if obj.start_date else None + + @staticmethod + def get_end(obj): + return localtime(obj.end_date).isoformat() if obj.end_date else None \ No newline at end of file diff --git a/services/backend/tko/views.py b/services/backend/tko/views.py index c98a7ec..590c233 100644 --- a/services/backend/tko/views.py +++ b/services/backend/tko/views.py @@ -1,3 +1,6 @@ +from django.utils import timezone +from django.db.models import Q + from rest_framework.generics import ListAPIView, CreateAPIView from rest_framework import permissions @@ -10,11 +13,25 @@ class ContactView(CreateAPIView): permission_classes = [permissions.AllowAny] -class ArticleListView(ListAPIView): - queryset = Article.objects.all() +class NewArticleListView(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')[:2] + + +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') + class EventListView(ListAPIView): queryset = Event.objects.all() diff --git a/services/frontend/assets/css/main.css b/services/frontend/assets/css/main.css index cafc57b..139916a 100644 --- a/services/frontend/assets/css/main.css +++ b/services/frontend/assets/css/main.css @@ -1,22 +1,22 @@ @font-face { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-weight: 700; font-style: normal; font-display: block; } h1 { - font-family: 'Playfair Display', serif; /* Or a similar elegant font */ + font-family: 'Futura', sans-serif; font-size: 3rem; - color: #333; /* Dark gray */ + color: #333; text-align: center; margin-bottom: 1rem; margin-top: 2rem; - text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); /* Subtle shadow */ + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); } h2 { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1.5rem; color: #CF3476; text-align: center; @@ -24,25 +24,25 @@ h2 { } h3 { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1.5rem; - color: #666; /* Light gray */ + color: #666; text-align: center; margin-bottom: 0.5rem; } h4 { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1rem; - color: #666; /* Light gray */ + color: #666; text-align: center; margin-bottom: 0.5rem; } h5 { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1rem; - color: #aaa; /* Light gray */ + color: #aaa; margin-top: 1rem; } @@ -52,25 +52,30 @@ h5 { margin-right: calc(20% - 2.5rem); } +.app__logo { + height: 3.5rem; + width: 8rem; +} + .app__title { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 2rem; - color: #333; /* Light gray */ + color: #333; } .app__tab { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 2rem; - color: #CF3476; /* Light gray */ + color: #CF3476; } .to_left { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1rem; } .to_right { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; float: right; } @@ -84,9 +89,9 @@ h5 { .contact__title { text-align: center; font-size: 3rem; - color: #333; /* Dark gray */ + color: #333; margin-bottom: 1rem; - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-weight: bold; } @@ -96,71 +101,114 @@ h5 { background-color: #CF3476; } +.articles { + margin-left: calc(20% - 2.5rem); + margin-right: calc(20% - 2.5rem); +} + .article { - padding: 20px; + padding: 10px; min-width: 40%; border-radius: 15px; box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); - margin: 50px + margin-bottom: 1rem; +} + +.article__image { + border-radius: 0.5rem; } .article__title { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1.5rem; - color: #CF3476; /* Dark gray */ + color: #CF3476; text-align: left; margin-top: 0.1rem; font-weight: bold; } .article__date { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 0.8rem; text-align: left; } .article__text { - font-family: 'Playfair Display', serif; /* Or a similar elegant font */ + font-family: 'Futura', sans-serif; font-size: 1rem; text-align: left; - color: #666; /* Dark gray */ + min-height: 6rem; + color: #666; } .article__sign { - font-family: 'Playfair Display', serif; /* Or a similar elegant font */ + font-family: 'Futura', sans-serif; font-size: 1rem; float: right; - color: #333; /* Dark gray */ + padding-right: 1rem; + color: #333; +} + +.show_more { + font-family: 'Futura', sans-serif; + text-align: center; + color: #CF3476; + background: transparent; + box-shadow: none; + width: 300px; + margin: 0 auto; +} + +.show_more:hover { + box-shadow: none; +} + +.pricing { + margin-left: calc(20% - 2.5rem); + margin-right: calc(20% - 2.5rem); } .pricing-box { - padding: 20px; + padding: 1rem; text-align: center; min-width: 25rem; border-radius: 1rem; height: 230px; box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); - margin: 50px } -.pricing-box:not(:last-child) { - margin: 20px; +.pricing-box:hover { + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.8); } .pricing__price { - font-family: 'Playfair Display', serif; /* Or a similar elegant font */ + font-family: 'Futura', sans-serif; font-size: 3.5rem; - color: #CF3476; /* Dark gray */ + color: #CF3476; text-align: center; } .pricing__subtitle { - font-family: 'Playfair Display', serif; /* Or a similar elegant font */ + font-family: 'Futura', sans-serif; font-size: 1rem; - color: #333; /* Dark gray */ + color: #333; text-align: center; } +.trainers__parallax { + max-height: 34rem; + margin-top: 2rem; +} + +.trainers { + min-width: 25rem; + background: transparent; + box-shadow: none; + height: 100%; + margin-left: calc(20% - 2.5rem); + margin-right: calc(20% - 2.5rem); +} + .advantage { min-width: 25rem; border-radius: 1rem; @@ -172,7 +220,7 @@ h5 { } .advantage__title { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; color: #CF3476; font-size: 1.5rem; font-weight: bold; @@ -180,34 +228,37 @@ h5 { } .advantage__text { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 1rem; color: #333; } .about { min-width: 25rem; - border-radius: 0; background: transparent; + box-shadow: none; + height: 100%; + margin-left: calc(20% - 2.5rem); + margin-right: calc(20% - 2.5rem); } .about__parallax { - max-height: 36rem; + max-height: 30rem; + margin-top: 2rem; } .about__title { - font-family: 'Playfair Display', serif; + font-family: 'Futura', sans-serif; font-size: 3rem; text-align: center; - color: #ddd; /* Dark gray */ - margin-bottom: 1rem; + color: #fff; margin-top: 1rem; font-weight: bold; } .about__subtitle { - font-family: 'Playfair Display', serif; - color: #CF3476; + font-family: 'Futura', sans-serif; + color: #FF3D8C; font-size: 1.5rem; text-align: center; margin-bottom: 0.5rem; @@ -215,8 +266,12 @@ h5 { } .about__text { - font-family: 'Playfair Display', serif; - color: #aaa; /* Dark gray */ + font-family: 'Futura', sans-serif; + color: #ddd; font-size: 1.2rem; text-align: center; +} + +.footer { + background-color: black; } \ No newline at end of file diff --git a/services/frontend/components/About.vue b/services/frontend/components/About.vue index 3e3fb35..ed4ec5a 100644 --- a/services/frontend/components/About.vue +++ b/services/frontend/components/About.vue @@ -1,35 +1,21 @@ diff --git a/services/frontend/components/Advantages.vue b/services/frontend/components/Advantages.vue index f0d520f..da5fd26 100644 --- a/services/frontend/components/Advantages.vue +++ b/services/frontend/components/Advantages.vue @@ -1,6 +1,6 @@ @@ -19,16 +20,28 @@ import './assets/css/main.css' import {useAPI} from "~/composables/useAPI"; +interface Event { + title: string; + end: Date; + start: Date; + color: string; +} -const { error, data } = await useAPI('load-events/', {method: "GET"}); +const events = ref([]); -const value = [new Date()]; -const events = [ - { - title: "ahoj", - start: new Date(), - end: new Date(), - color: 'red', +await loadEvents(); + +async function loadEvents() { + const { error, data } = await useAPI('load-events/', { method: "GET" }); + + if (data.value && Array.isArray(data.value)) { + events.value = data.value as Event[]; + for(const i in data.value){ + data.value[i].end = new Date((data.value[i].end)); + data.value[i].start = new Date((data.value[i].start)); + } + } else if (error.value) { + console.error("Error loading events:", error.value); } -]; +} \ No newline at end of file diff --git a/services/frontend/components/ContactUs.vue b/services/frontend/components/ContactUs.vue index f2f6119..4e2409c 100644 --- a/services/frontend/components/ContactUs.vue +++ b/services/frontend/components/ContactUs.vue @@ -72,8 +72,8 @@ const email = ref(""); const phone = ref(""); const textField = ref(""); -function sendContact() { - useAPI('create-contact/', { +async function sendContact() { + const { data, error } = await useAPI('create-contact/', { method: "POST", body: { name: fullName.value, diff --git a/services/frontend/components/Courses.vue b/services/frontend/components/Courses.vue index fad3989..3ec11ad 100644 --- a/services/frontend/components/Courses.vue +++ b/services/frontend/components/Courses.vue @@ -1,30 +1,41 @@ \ No newline at end of file diff --git a/services/frontend/components/History.vue b/services/frontend/components/History.vue new file mode 100644 index 0000000..5996f78 --- /dev/null +++ b/services/frontend/components/History.vue @@ -0,0 +1,27 @@ + + + \ No newline at end of file diff --git a/services/frontend/components/News.vue b/services/frontend/components/News.vue index 3390728..74082ba 100644 --- a/services/frontend/components/News.vue +++ b/services/frontend/components/News.vue @@ -1,10 +1,10 @@ -