Dokumentacja Techniczna

Technical Documentation

Platforma korepetycji
matematyki online

Online math tutoring
platform

Kompletna aplikacja webowa łącząca studenta z korepetytorem — z systemem rezerwacji lekcji, płatnościami online i dwujęzycznym interfejsem. Zbudowana na solidnym stosie technologicznym w architekturze kontenerowej.

A complete web application connecting students with a tutor — featuring a lesson booking system, online payments, and a bilingual interface. Built on a solid technology stack using a containerized architecture.

Django 5 Vue 3 Docker PostgreSQL Celery Stripe FullCalendar JWT Auth vue-i18n REST API

Stos technologiczny

Technology stack

Użyte technologie

Technologies used

🐍

Django + DRF

Django 5.x / DRF 3.x

Backend aplikacji oparty na Django z Django REST Framework. Obsługa modeli danych, autentykacji JWT, systemu rezerwacji, panelu administracyjnego oraz logiki biznesowej aplikacji.

Application backend based on Django with Django REST Framework. Handles data models, JWT authentication, booking system, admin panel, and all core business logic.

Vue 3 + Vite

Vue 3.x / Vite 5.x

Frontned zbudowany w Vue 3 z Composition API i Vite jako narzędziem buildowym. Reaktywny interfejs użytkownika z dynamicznym przełączaniem widoków i obsługą stanu aplikacji przez Pinia.

Frontend built with Vue 3 using the Composition API and Vite as the build tool. Reactive user interface with dynamic view transitions and application state managed through Pinia.

📅

FullCalendar

v6 / Vue 3 adapter

Interaktywny kalendarz rezerwacji z widokami miesięcznym, tygodniowym i dziennym. Responsywny — na urządzeniach mobilnych automatycznie przełącza się na widok dzienny lub tygodniowy. Obsługuje locale pl i en.

Interactive booking calendar with monthly, weekly, and daily views. Responsive — on mobile devices it automatically switches to daily or weekly view. Supports pl and en locales.

🐘

PostgreSQL

16.x

Relacyjna baza danych dla wszystkich danych aplikacji — użytkownicy, rezerwacje, płatności, dostępność terminów. Uruchamiana jako osobny serwis w Docker Compose.

Relational database for all application data — users, bookings, payments, and available time slots. Runs as a separate service within Docker Compose.

🔴

Redis + Celery

Redis 7 / Celery 5

Asynchroniczne zadania w tle: wysyłka maili z potwierdzeniami rezerwacji, przypomnień przed lekcją oraz powiadomień po odbyciu lekcji próbnej. Redis pełni rolę brokera wiadomości.

Asynchronous background tasks: sending booking confirmation emails, pre-lesson reminders, and notifications after trial lessons. Redis serves as the message broker.

💳

Stripe

Stripe API v3

Obsługa płatności online za pełne lekcje. Integracja przez Stripe Payment Intent z modelem autoryzacji karty bez natychmiastowego pobierania środków — opłata pobierana dopiero po potwierdzeniu rezerwacji.

Online payment handling for full lessons. Integrated via Stripe Payment Intent with card authorization without immediate charge — funds are captured only after booking confirmation.

🐳

Docker Compose

Docker 25 / Compose v2

Całość aplikacji uruchamiana jako zestaw kontenerów: backend Django, frontend Vue/Nginx, PostgreSQL, Redis, Celery worker. Jeden plik docker-compose.yml dla środowisk dev i produkcji.

The entire application runs as a set of containers: Django backend, Vue/Nginx frontend, PostgreSQL, Redis, and a Celery worker. A single docker-compose.yml file covers both dev and production environments.

🌐

vue-i18n

v9 (Composition API)

Dwujęzyczność PL/EN w całej aplikacji. Język przechowywany w Pinia store i localStorage — przełącznik reaguje natychmiastowo bez przeładowania strony. FullCalendar automatycznie zmienia locale.

PL/EN bilingual support throughout the application. Language is stored in Pinia store and localStorage — the switcher responds instantly without page reload. FullCalendar automatically updates its locale.

🔐

JWT Auth

djangorestframework-simplejwt

Bezstanowa autentykacja oparta na tokenach JWT. Access token przechowywany w pamięci (Pinia), refresh token w httpOnly cookie. Vue Router chroni trasy wymagające logowania przez navigation guard.

Stateless authentication based on JWT tokens. Access token stored in memory (Pinia), refresh token in an httpOnly cookie. Vue Router protects auth-required routes via navigation guards.


Architektura

Architecture

Diagram warstw

Layer diagram

Przeglądarka Browser
Vue 3 Pinia Vue Router vue-i18n FullCalendar Axios
Nginx
serwowanie plików statycznych static file serving reverse proxy HTTPS
Django + DRF
REST API JWT modele models logika biznesowa business logic Admin
Dane i zadania Data and tasks
PostgreSQL Redis Celery Stripe API SMTP

Przepływ użytkownika

User flow

Ścieżka studenta

Student journey

01
Wejście na stronę i rejestracja Landing and registration Student trafia na stronę główną. Kalendarz dostępnych terminów jest widoczny wyłącznie po zalogowaniu — niezalogowany widzi komunikat zachęcający do założenia konta. The student arrives at the home page. The availability calendar is visible only to logged-in users — guests see a prompt encouraging account creation.
02
Weryfikacja adresu e-mail Email address verification Po rejestracji Celery wysyła mail z linkiem aktywacyjnym. Jeden adres e-mail = jedno konto = jedna darmowa lekcja próbna. Zabezpieczenie przed wielokrotnym korzystaniem z oferty. After registration, Celery sends a verification email with an activation link. One email = one account = one free trial lesson. This prevents repeated abuse of the trial offer.
03
Rezerwacja lekcji próbnej (20 min, bezpłatna) Booking a trial lesson (20 min, free) Student wybiera dostępny termin w kalendarzu FullCalendar. Rezerwacja lekcji próbnej jest automatycznie potwierdzana — brak płatności, natychmiastowe potwierdzenie mailem z linkiem Google Meet. The student selects an available slot in the FullCalendar view. Trial lesson bookings are confirmed automatically — no payment required, instant email confirmation with a Google Meet link.
04
Odbycie lekcji próbnej Attending the trial lesson Lekcja odbywa się przez Google Meet. Po upływie zaplanowanego czasu system automatycznie oznacza lekcję jako odbytą i odblokowuje możliwość rezerwacji pełnych lekcji. The lesson takes place via Google Meet. After the scheduled time elapses, the system automatically marks the lesson as completed and unlocks the ability to book full lessons.
05
Rezerwacja pełnej lekcji z płatnością Stripe Booking a full lesson with Stripe payment Student wybiera termin i przechodzi do płatności przez Stripe Payment Intent. Karta jest autoryzowana (blokada środków), ale pobrana dopiero po ewentualnym potwierdzeniu przez administratora. The student selects a slot and proceeds to checkout via Stripe Payment Intent. The card is authorized (funds held), but charged only after admin confirmation if needed.
06
Potwierdzenie i przypomnienie Confirmation and reminder Celery wysyła mail z potwierdzeniem i linkiem do Google Meet. Na 24h przed lekcją student otrzymuje automatyczne przypomnienie. Administrator zarządza wszystkimi rezerwacjami z panelu Django Admin. Celery sends a confirmation email with a Google Meet link. 24 hours before the lesson, the student receives an automatic reminder. The administrator manages all bookings from the Django Admin panel.

Rozwiązania techniczne

Technical solutions

Zastosowane wzorce

Applied patterns

Navigation guard w Vue RouterVue Router navigation guard

Trasa /calendar chroniona przez meta: { requiresAuth: true }. Guard sprawdza stan Pinia store — niezalogowany użytkownik trafia na /login z parametrem redirect, po zalogowaniu wraca na właściwą stronę.

The /calendar route is protected via meta: { requiresAuth: true }. The guard checks Pinia store state — unauthenticated users are redirected to /login with a redirect parameter, then returned to the correct page after login.

router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !auth.isLoggedIn)
next('/login?redirect=' + to.path)
else next()
})

Stripe Payment Intent — autoryzacja bez pobieraniaStripe Payment Intent — authorize only

Przy rezerwacji środki są blokowane na karcie studenta, ale pobierane dopiero po potwierdzeniu terminu. W razie odwołania — automatyczny zwrot bez dodatkowych działań administracyjnych.

During booking, funds are held on the student's card but only captured after slot confirmation. If cancelled, an automatic refund is issued without any manual admin action required.

intent = stripe.payment_intents.create(
amount=price,
capture_method='manual'
)

Reaktywne locale FullCalendarReactive FullCalendar locale

Locale kalendarza jest wyliczanym computed property zależnym od languageStore. Zmiana języka w navbarze natychmiastowo aktualizuje nazwy dni, miesięcy i przycisków nawigacji bez przeładowania komponentu.

The calendar locale is a computed property dependent on languageStore. Changing the language in the navbar instantly updates day names, months, and navigation button labels without reloading the component.

const calLocale = computed(() =>
langStore.lang === 'pl' ? plLocale : enLocale
)

Jedna lekcja próbna na kontoOne trial lesson per account

Model Booking w Django przechowuje typ lekcji (trial/standard). API rezerwacji lekcji próbnej sprawdza na poziomie backendu, czy dany użytkownik nie odbył już próbnej — zabezpieczenie niezależne od frontendu.

Django's Booking model stores the lesson type (trial/standard). The trial booking API endpoint checks at the backend level whether the user has already taken a trial — protection independent of the frontend.

has_trial = Booking.objects.filter(
student=request.user,
lesson_type='trial'
).exists()

Responsywny widok kalendarzaResponsive calendar view

ResizeObserver monitoruje szerokość kontenera kalendarza. Poniżej 768px initialView przełącza się na timeGridDay — sloty mają minimalną wysokość 44px, wygodną do obsługi palcem na ekranie dotykowym.

A ResizeObserver monitors the calendar container width. Below 768px, initialView switches to timeGridDay — slots have a minimum height of 44px, comfortable for touch interaction on mobile screens.

const view = computed(() =>
isMobile.value ? 'timeGridDay'
: 'dayGridMonth'
)

Celery — asynchroniczne maileCelery — asynchronous emails

Wysyłka maili odbywa się w tle przez Celery, żeby nie blokować odpowiedzi API. Zadania: potwierdzenie rejestracji, potwierdzenie rezerwacji, przypomnienie 24h przed lekcją, powiadomienie po lekcji próbnej.

Emails are sent in the background via Celery so they don't block API responses. Tasks include: registration confirmation, booking confirmation, 24h pre-lesson reminder, and post-trial notification.

@shared_task
def send_booking_confirmation(booking_id):
booking = Booking.objects.get(pk=booking_id)
send_mail(...)