# FitnessCenter — эксплуатационная документация

## Назначение
Одностраничный лендинг и личный кабинет фитнес‑клуба с регистрацией, входом и покупкой абонементов. Рендеринг на сервере (Django), без JSON API.

## Стек
- Python 3.13, Django 5.1.4
- Bootstrap 5.3 (CDN), шрифт Manrope, кастомный тёмный UI `static/css/main.css`
- БД по умолчанию: SQLite (`db.sqlite3`); совместимо с Postgres
- Контейнеризация: Dockerfile (python:3.13-slim) + `docker-compose.yml` (порт 1337→8000)

## Архитектура и файлы
- `gymclub/` — настройки Django, URL‑маршрутизация верхнего уровня.
- `club/` — доменная логика: модели, формы, views, urls, context processor с адресами клубов.
- `templates/` — серверные шаблоны: `base.html`, `home.html`, `register.html`, `login.html`, `dashboard.html`.
- `static/` — стили и логотип, подключаются через `{% static %}`; собранные файлы кладутся в `staticfiles/`.
- `entrypoint.sh` — миграции + `collectstatic` (ошибки игнорируются) и запуск `runserver` на 0.0.0.0:8000.
- `www/` — архивная статическая версия лендинга (не используется приложением).

## Данные и бизнес‑логика
- `MembershipPlan`: имя, описание, длительность в днях, цена. Начальные тарифы создаются миграцией `0002_default_plans`.
- `Profile`: OneToOne к пользователю, хранит телефон.
- `Subscription`: ссылка на пользователя и план, даты начала/окончания; `is_active` проверяет `end_date` ≥ сегодня.
- Хелпер `Subscription.create_for_plan(user, plan)` рассчитывает дату окончания от текущей даты и создаёт запись — используйте его вместо ручного сохранения.
- Проверка телефона (RegistrationForm): нормализует «8» в «+7», требует 11 цифр формата +7XXXXXXXXXX.
- Парольные валидаторы стандартные, но проверка «простых паролей» отключена (CommonPasswordValidator не подключён).

## Маршруты (HTML API)
Все пути объявлены в `club/urls.py`, подключены в `gymclub/urls.py`.
- `GET /` — витрина с планами, адресами клубов.
- `GET /register/`, `POST /register/` — регистрация пользователя, автологин, создание Profile.
- `GET /login/`, `POST /login/` — аутентификация через Django AuthenticationForm.
- `GET /logout/` — logout + redirect домой (view использует `logout`, форма не нужна).
- `GET /dashboard/` (требует авторизации) — активный абонемент, история, покупка новых.
- `POST /subscribe/<int:plan_id>/` — покупка плана через `Subscription.create_for_plan`.
- `admin/` — стандартная админка для планов, подписок и профилей.
Все формы защищены CSRF; сообщения пользователю выводятся через Django messages.

## UI и контент
- Тёмная тема с акцентным цветом `--accent` в `static/css/main.css`; поменяйте переменные в корне файла для ребрендинга.
- Список клубов задаётся константой `CLUBS` в `club/context_processors.py` и доступен в шаблонах как `clubs`.
- Главная страница содержит секции «Абонементы», «Тренеры», «Клубы», «О нас» и форму заявки в футере (пока без обработки).

## Переменные окружения
Файл примера `.env.example`:
- `DJANGO_DEBUG` (True/False)
- `DJANGO_ALLOWED_HOSTS` (список через запятую, `*` по умолчанию)
- `DJANGO_SECRET_KEY` (смените в проде)
Дополнительно в `settings.py`: язык `ru-ru`, часовой пояс `Europe/Moscow`.

## Запуск
### В Docker
```bash
cp .env.example .env
docker compose build
docker compose up
# Откройте http://localhost:1337
```
Контейнер монтирует текущую директорию, так что правки видны без пересборки.

### Локально (без Docker)
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env  # при необходимости отредактируйте
python manage.py migrate
python manage.py runserver
```

### Админка и данные
- Создайте суперпользователя: `python manage.py createsuperuser`.
- Начальные планы создаются миграцией; новые можно добавить через /admin/ или фикстуры.

### Переключение на Postgres (кратко)
1. Добавьте сервис в `docker-compose.yml` или поднимите внешнюю БД.
2. В `gymclub/settings.py` задайте `ENGINE = 'django.db.backends.postgresql'` и параметры `NAME/USER/PASSWORD/HOST/PORT` (лучше через переменные окружения).
3. Выполните миграции заново (`python manage.py migrate`).

## Хитрости и рекомендации
- Используйте `Subscription.create_for_plan` — он правильно считает `end_date` от текущего дня.
- Телефон хранится в нормализованном формате `+7XXXXXXXXXX`; это можно использовать для SMS‑интеграций.
- Планы отсортированы по цене (`ordering = ['price']`), поэтому вывод на главной будет от дешёвого к дорогому.
- `entrypoint.sh` не падает, если `collectstatic` без настроек S3; удобнее для локалки, но в проде задайте `STATIC_ROOT` и уберите `|| true` при необходимости.
- Блок футера «Позвоните мне» пока статический — если нужна отправка заявки, повесьте action на форму и добавьте view.

## Полезные пути
- Основной код: `club/views.py`, `club/forms.py`, `club/models.py`
- Стили: `static/css/main.css`
- Шаблоны: `templates/`
- Контейнеризация: `Dockerfile`, `docker-compose.yml`, `entrypoint.sh`
