Integrate Bootstrap 5 and Bootstrap Icons into UI

- Add Bootstrap 5.3.3 + Icons via CDN to base.html
- Replace 315-line hand-written CSS with 35-line brand overrides
- Update all 13 templates with Bootstrap utility classes:
  - Responsive navbar with mobile hamburger menu
  - Consistent card-based layout for forms and profile
  - Proper button alignment with d-flex and d-grid utilities
  - List groups for data display (profile info)
  - Professional alerts and icons
- No backend changes, no build toolchain needed
- Responsive design works on mobile/tablet/desktop

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-03-05 21:05:30 +03:00
parent eea2d84260
commit bd0ff8f449
18 changed files with 486 additions and 338 deletions

View File

@@ -4,40 +4,57 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}EvoSync{% endblock %}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<nav class="navbar">
<div class="container nav-content">
<a href="/" class="nav-logo">EvoSync</a>
<div class="nav-links">
{% if user %}
<a href="/profile">Личный кабинет</a>
<a href="/logout">Выход</a>
{% else %}
<a href="/login">Вход</a>
<a href="/register">Регистрация</a>
{% endif %}
<nav class="navbar navbar-expand-lg bg-white border-bottom border-2 brand-border">
<div class="container">
<a href="/" class="navbar-brand brand-logo">EvoSync</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
{% if user %}
<li class="nav-item">
<a href="/profile" class="nav-link"><i class="bi bi-person-circle me-1"></i>Личный кабинет</a>
</li>
<li class="nav-item">
<a href="/logout" class="nav-link text-muted">Выход</a>
</li>
{% else %}
<li class="nav-item">
<a href="/login" class="nav-link">Вход</a>
</li>
<li class="nav-item">
<a href="/register" class="nav-link">Регистрация</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<main class="container">
<main class="container py-4">
{% if errors %}
<div class="alert alert-error">
<div class="alert alert-danger">
{% for error in errors %}
<p>{{ error }}</p>
<p class="mb-1">{{ error }}</p>
{% endfor %}
</div>
{% endif %}
{% if success %}
<div class="alert alert-success">
<p>{{ success }}</p>
<p class="mb-0">{{ success }}</p>
</div>
{% endif %}
{% block content %}{% endblock %}
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -2,9 +2,16 @@
{% block title %}Подтверждение email — EvoSync{% endblock %}
{% block content %}
<div class="message-card">
<h1>Подтвердите ваш email</h1>
<p>Ссылка для подтверждения email выведена в консоль сервера.</p>
<p>Скопируйте её и откройте в браузере.</p>
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-5 text-center">
<div class="card-body p-5">
<i class="bi bi-envelope-check display-4 text-primary mb-3"></i>
<h1 class="h4 mb-3">Подтвердите ваш email</h1>
<p class="text-muted">Ссылка для подтверждения email выведена в консоль сервера.</p>
<p class="text-muted">Скопируйте её и откройте в браузере.</p>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -2,9 +2,16 @@
{% block title %}Email подтвержден — EvoSync{% endblock %}
{% block content %}
<div class="message-card">
<h1>Email подтвержден!</h1>
<p>Ваш email успешно подтвержден. Теперь вы можете войти в систему.</p>
<p><a href="/login">Войти</a></p>
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-5 text-center">
<div class="card-body p-5">
<i class="bi bi-check-circle display-4 text-success mb-3"></i>
<h1 class="h4 mb-3">Email подтвержден!</h1>
<p class="text-muted">Ваш email успешно подтвержден. Теперь вы можете войти в систему.</p>
<a href="/login" class="btn btn-primary mt-2">Войти</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -2,20 +2,26 @@
{% block title %}Забыли пароль — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Забыли пароль?</h1>
<p style="font-size: 14px; color: #555; margin-bottom: 20px;">
Введите email, указанный при регистрации.
</p>
<form method="post" action="/forgot-password">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-4">
<div class="card-body p-4">
<h1 class="card-title h4 mb-2">Забыли пароль?</h1>
<p class="text-muted small mb-4">Введите email, указанный при регистрации.</p>
<form method="post" action="/forgot-password">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" id="email" name="email" class="form-control" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Отправить ссылку для сброса</button>
</div>
</form>
<div class="mt-3 text-center small">
<a href="/login">Вернуться ко входу</a>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Отправить ссылку для сброса</button>
</form>
<div class="form-links">
<a href="/login">Вернуться ко входу</a>
</div>
</div>
{% endblock %}

View File

@@ -2,22 +2,31 @@
{% block title %}Вход — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Вход</h1>
<form method="post" action="/login">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" value="{{ form.email if form else '' }}" required>
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-4">
<div class="card-body p-4">
<h1 class="card-title h4 mb-4">Вход</h1>
<form method="post" action="/login">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" id="email" name="email" class="form-control"
value="{{ form.email if form else '' }}" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Войти</button>
</div>
</form>
<div class="mt-3 text-center small">
<a href="/forgot-password">Забыли пароль?</a><br>
<a href="/register">Зарегистрироваться</a>
</div>
</div>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Войти</button>
</form>
<div class="form-links">
<a href="/forgot-password">Забыли пароль?</a><br>
<a href="/register">Зарегистрироваться</a>
</div>
</div>
{% endblock %}

View File

@@ -2,11 +2,17 @@
{% block title %}{{ title }} — EvoSync{% endblock %}
{% block content %}
<div class="message-card">
<h1>{{ title }}</h1>
<p>{{ message }}</p>
{% if link %}
<p><a href="{{ link }}">{{ link_text }}</a></p>
{% endif %}
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-5 text-center">
<div class="card-body p-5">
<h1 class="h4 mb-3">{{ title }}</h1>
<p class="text-muted">{{ message }}</p>
{% if link %}
<a href="{{ link }}" class="btn btn-primary mt-2">{{ link_text }}</a>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,30 +0,0 @@
{% extends "base.html" %}
{% block title %}Личный кабинет — EvoSync{% endblock %}
{% block content %}
<div class="profile-card">
<h1>Личный кабинет</h1>
<form method="post" action="/profile">
<div class="form-group">
<label for="first_name">Имя</label>
<input type="text" id="first_name" name="first_name"
value="{{ form.first_name if form else user.first_name }}">
</div>
<div class="form-group">
<label for="last_name">Фамилия</label>
<input type="text" id="last_name" name="last_name"
value="{{ form.last_name if form else user.last_name }}">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" value="{{ user.email }}" disabled>
</div>
<div class="form-group">
<label for="phone">Телефон</label>
<input type="tel" id="phone" name="phone"
value="{{ form.phone if form else user.phone }}">
</div>
<button type="submit" class="btn btn-primary">Сохранить</button>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block title %}Изменить пароль — EvoSync{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-4">
<div class="card-header">
<h1 class="h5 mb-0"><i class="bi bi-key me-2"></i>Изменить пароль</h1>
</div>
<div class="card-body p-4">
{% if success %}
<div class="alert alert-success">{{ success }}</div>
{% endif %}
{% if errors %}
<div class="alert alert-danger">
{% for error in errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
<form method="post" action="/profile/change-password">
<div class="mb-3">
<label for="current_password" class="form-label">Текущий пароль</label>
<input type="password" id="current_password" name="current_password" class="form-control" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Новый пароль</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="mb-4">
<label for="password_confirm" class="form-label">Подтвердить пароль</label>
<input type="password" id="password_confirm" name="password_confirm" class="form-control" required>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">Изменить пароль</button>
<a href="/profile" class="btn btn-outline-secondary">Отмена</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block title %}Удалить аккаунт — EvoSync{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-4 border-danger">
<div class="card-header bg-danger text-white">
<h1 class="h5 mb-0"><i class="bi bi-trash me-2"></i>Удалить аккаунт</h1>
</div>
<div class="card-body p-4">
<div class="alert alert-warning">
<i class="bi bi-exclamation-triangle me-1"></i>
<strong>Внимание!</strong> Это действие необратимо. Все ваши данные будут удалены.
</div>
{% if errors %}
<div class="alert alert-danger">
{% for error in errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
<form method="post" action="/profile/delete">
<div class="mb-4">
<label for="password" class="form-label">Введите пароль для подтверждения</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-danger">
<i class="bi bi-trash me-1"></i>Удалить мой аккаунт
</button>
<a href="/profile" class="btn btn-outline-secondary">Отмена</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,55 @@
{% extends "base.html" %}
{% block title %}Редактировать профиль — EvoSync{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-sm-10 col-md-7 col-lg-6">
<div class="card shadow-sm mt-4">
<div class="card-header">
<h1 class="h5 mb-0"><i class="bi bi-pencil me-2"></i>Редактировать профиль</h1>
</div>
<div class="card-body p-4">
{% if success %}
<div class="alert alert-success">{{ success }}</div>
{% endif %}
{% if errors %}
<div class="alert alert-danger">
{% for error in errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
<form method="post" action="/profile/edit">
<div class="row g-3 mb-3">
<div class="col-sm-6">
<label for="first_name" class="form-label">Имя</label>
<input type="text" id="first_name" name="first_name" class="form-control"
value="{{ form.first_name if form else user.first_name }}" required>
</div>
<div class="col-sm-6">
<label for="last_name" class="form-label">Фамилия</label>
<input type="text" id="last_name" name="last_name" class="form-control"
value="{{ form.last_name if form else user.last_name }}" required>
</div>
</div>
<div class="mb-3">
<label class="form-label text-muted">Email</label>
<input type="email" class="form-control" value="{{ user.email }}" disabled>
</div>
<div class="mb-4">
<label for="phone" class="form-label">Телефон</label>
<input type="tel" id="phone" name="phone" class="form-control"
value="{{ form.phone if form else user.phone }}" required>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">Сохранить</button>
<a href="/profile" class="btn btn-outline-secondary">Отмена</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block title %}Личный кабинет — EvoSync{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-sm-10 col-md-7 col-lg-6">
<div class="card shadow-sm mt-4">
<div class="card-header">
<h1 class="h5 mb-0"><i class="bi bi-person-circle me-2"></i>Личный кабинет</h1>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted small">Имя</span>
<span>{{ user.first_name }}</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted small">Фамилия</span>
<span>{{ user.last_name }}</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted small">Email</span>
<span>{{ user.email }}</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<span class="text-muted small">Телефон</span>
<span>{{ user.phone }}</span>
</li>
</ul>
<div class="card-body d-grid gap-2">
<a href="/profile/edit" class="btn btn-primary">
<i class="bi bi-pencil me-1"></i>Редактировать профиль
</a>
<a href="/profile/change-password" class="btn btn-secondary">
<i class="bi bi-key me-1"></i>Изменить пароль
</a>
<a href="/logout" class="btn btn-outline-secondary">
<i class="bi bi-box-arrow-right me-1"></i>Выход
</a>
<a href="/profile/delete" class="btn btn-outline-danger btn-sm mt-2">
<i class="bi bi-trash me-1"></i>Удалить аккаунт
</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -2,37 +2,51 @@
{% block title %}Регистрация — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Регистрация</h1>
<form method="post" action="/register">
<div class="form-group">
<label for="first_name">Имя</label>
<input type="text" id="first_name" name="first_name" value="{{ form.first_name if form else '' }}">
<div class="row justify-content-center">
<div class="col-sm-10 col-md-7 col-lg-6">
<div class="card shadow-sm mt-4">
<div class="card-body p-4">
<h1 class="card-title h4 mb-4">Регистрация</h1>
<form method="post" action="/register">
<div class="row g-3 mb-3">
<div class="col-sm-6">
<label for="first_name" class="form-label">Имя</label>
<input type="text" id="first_name" name="first_name" class="form-control"
value="{{ form.first_name if form else '' }}">
</div>
<div class="col-sm-6">
<label for="last_name" class="form-label">Фамилия</label>
<input type="text" id="last_name" name="last_name" class="form-control"
value="{{ form.last_name if form else '' }}">
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email <span class="text-danger">*</span></label>
<input type="email" id="email" name="email" class="form-control"
value="{{ form.email if form else '' }}" required>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Телефон <span class="text-danger">*</span></label>
<input type="tel" id="phone" name="phone" class="form-control"
value="{{ form.phone if form else '' }}" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль <span class="text-danger">*</span></label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="mb-4">
<label for="password_confirm" class="form-label">Подтверждение пароля <span class="text-danger">*</span></label>
<input type="password" id="password_confirm" name="password_confirm" class="form-control" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</div>
</form>
<div class="mt-3 text-center small">
<a href="/login">Уже есть аккаунт? Войти</a>
</div>
</div>
</div>
<div class="form-group">
<label for="last_name">Фамилия</label>
<input type="text" id="last_name" name="last_name" value="{{ form.last_name if form else '' }}">
</div>
<div class="form-group">
<label for="email">Email *</label>
<input type="email" id="email" name="email" value="{{ form.email if form else '' }}" required>
</div>
<div class="form-group">
<label for="phone">Телефон *</label>
<input type="tel" id="phone" name="phone" value="{{ form.phone if form else '' }}" required>
</div>
<div class="form-group">
<label for="password">Пароль *</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="password_confirm">Подтверждение пароля *</label>
<input type="password" id="password_confirm" name="password_confirm" required>
</div>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
<div class="form-links">
<a href="/login">Уже есть аккаунт? Войти</a>
</div>
</div>
{% endblock %}

View File

@@ -2,18 +2,26 @@
{% block title %}Новый пароль — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Новый пароль</h1>
<form method="post" action="/reset-password?token={{ token }}">
<div class="form-group">
<label for="password">Новый пароль</label>
<input type="password" id="password" name="password" required>
<div class="row justify-content-center">
<div class="col-sm-10 col-md-6 col-lg-5">
<div class="card shadow-sm mt-4">
<div class="card-body p-4">
<h1 class="card-title h4 mb-4">Новый пароль</h1>
<form method="post" action="/reset-password?token={{ token }}">
<div class="mb-3">
<label for="password" class="form-label">Новый пароль</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="mb-4">
<label for="password_confirm" class="form-label">Подтверждение пароля</label>
<input type="password" id="password_confirm" name="password_confirm" class="form-control" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Сменить пароль</button>
</div>
</form>
</div>
</div>
<div class="form-group">
<label for="password_confirm">Подтверждение пароля</label>
<input type="password" id="password_confirm" name="password_confirm" required>
</div>
<button type="submit" class="btn btn-primary">Сменить пароль</button>
</form>
</div>
</div>
{% endblock %}