sqlalchemy-connection 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. sqlalchemy_connection-2.0.1.dist-info/METADATA +26 -0
  2. sqlalchemy_connection-2.0.1.dist-info/RECORD +33 -0
  3. sqlalchemy_connection-2.0.1.dist-info/WHEEL +5 -0
  4. sqlalchemy_connection-2.0.1.dist-info/entry_points.txt +2 -0
  5. sqlalchemy_connection-2.0.1.dist-info/top_level.txt +1 -0
  6. sqlalchemy_connector/__init__.py +3 -0
  7. sqlalchemy_connector/_builder.py +425 -0
  8. sqlalchemy_connector/cli.py +200 -0
  9. sqlalchemy_connector/real_generator.py +2908 -0
  10. sqlalchemy_connector/templates/admin_cart_html_template.html +372 -0
  11. sqlalchemy_connector/templates/admin_html_template.html +364 -0
  12. sqlalchemy_connector/templates/admin_users_html_template.html +82 -0
  13. sqlalchemy_connector/templates/app_template.py +434 -0
  14. sqlalchemy_connector/templates/base_html_template.html +100 -0
  15. sqlalchemy_connector/templates/cart_html_template.html +103 -0
  16. sqlalchemy_connector/templates/catalog_html_template.html +98 -0
  17. sqlalchemy_connector/templates/checkout_html_template.html +70 -0
  18. sqlalchemy_connector/templates/dashboard_html_template.html +121 -0
  19. sqlalchemy_connector/templates/index_html_template.html +91 -0
  20. sqlalchemy_connector/templates/login_html_template.html +59 -0
  21. sqlalchemy_connector/templates/models_template.py +65 -0
  22. sqlalchemy_connector/templates/new_request_html_template.html +49 -0
  23. sqlalchemy_connector/templates/orders_html_template.html +65 -0
  24. sqlalchemy_connector/templates/product_form_html_template.html +142 -0
  25. sqlalchemy_connector/templates/product_html_template.html +131 -0
  26. sqlalchemy_connector/templates/profile_html_template.html +104 -0
  27. sqlalchemy_connector/templates/register_html_template.html +183 -0
  28. sqlalchemy_connector/templates/reviews_html_template.html +104 -0
  29. sqlalchemy_connector/templates/service_detail_html_template.html +67 -0
  30. sqlalchemy_connector/templates/service_form_html_template.html +86 -0
  31. sqlalchemy_connector/templates/services_html_template.html +47 -0
  32. sqlalchemy_connector/templates/slider_js_template.js +99 -0
  33. sqlalchemy_connector/templates/style_css_template.css +502 -0
@@ -0,0 +1,104 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Отзывы{% endblock %}
3
+ {% block content %}
4
+ <div class="container py-4">
5
+ <h2 class="mb-4">💬 Отзывы</h2>
6
+
7
+ <!-- Форма добавления отзыва (только для авторизованных пользователей) -->
8
+ {% if session.get('user_id') and session.get('role') != 'admin' %}
9
+ <div class="card border-0 shadow-sm mb-4">
10
+ <div class="card-body">
11
+ <h5 class="card-title">Оставить отзыв</h5>
12
+ <form method="POST" action="{{ url_for('add_review') }}">
13
+ <div class="mb-3">
14
+ <label class="form-label">Оценка</label>
15
+ <select name="rating" class="form-select" style="width:auto;">
16
+ <option value="5">⭐⭐⭐⭐⭐ (5)</option>
17
+ <option value="4">⭐⭐⭐⭐ (4)</option>
18
+ <option value="3">⭐⭐⭐ (3)</option>
19
+ <option value="2">⭐⭐ (2)</option>
20
+ <option value="1">⭐ (1)</option>
21
+ </select>
22
+ </div>
23
+ <div class="mb-3">
24
+ <label class="form-label">Текст отзыва</label>
25
+ <textarea name="text" class="form-control" rows="3" required
26
+ placeholder="Напишите ваш отзыв..."></textarea>
27
+ </div>
28
+ <button type="submit" class="btn btn-primary">Отправить отзыв</button>
29
+ </form>
30
+ </div>
31
+ </div>
32
+ {% elif not session.get('user_id') %}
33
+ <div class="alert alert-light mb-4">
34
+ <a href="{{ url_for('login') }}">Войдите</a>, чтобы оставить отзыв.
35
+ </div>
36
+ {% endif %}
37
+
38
+ <!-- Список отзывов -->
39
+ {% if reviews %}
40
+ {% for review in reviews %}
41
+ <div class="card mb-3 border-0 shadow-sm">
42
+ <div class="card-body">
43
+ <div class="d-flex justify-content-between align-items-start">
44
+ <div>
45
+ <strong>{{ review.reviewer.full_name or review.reviewer.email or review.reviewer.login or 'Пользователь' }}</strong>
46
+ <br>
47
+ <small class="text-muted">{{ review.created_at.strftime('%d.%m.%Y %H:%M') }}</small>
48
+ </div>
49
+ <div>
50
+ {% for i in range(review.rating) %}⭐{% endfor %}{% for i in range(5 - review.rating) %}☆{% endfor %}
51
+ </div>
52
+ </div>
53
+ <p class="mt-2 mb-1">{{ review.text }}</p>
54
+
55
+ {% if review.admin_reply %}
56
+ <div class="bg-light rounded p-2 mt-2">
57
+ <small class="text-muted fw-bold">Ответ администратора:</small><br>
58
+ <small>{{ review.admin_reply }}</small>
59
+ </div>
60
+ {% endif %}
61
+
62
+ {% if session.get('role') == 'admin' %}
63
+ <div class="mt-2 d-flex gap-2">
64
+ <form method="POST" action="{{ url_for('admin_delete_review', review_id=review.id) }}" class="d-inline">
65
+ <button type="submit" class="btn btn-sm btn-outline-danger"
66
+ onclick="return confirm('Удалить отзыв?')">🗑 Удалить</button>
67
+ </form>
68
+ <button type="button" class="btn btn-sm btn-outline-primary"
69
+ data-bs-toggle="modal" data-bs-target="#replyModal{{ review.id }}">💬 Ответить</button>
70
+ </div>
71
+
72
+ <!-- Модальное окно ответа -->
73
+ <div class="modal fade" id="replyModal{{ review.id }}" tabindex="-1" aria-hidden="true">
74
+ <div class="modal-dialog">
75
+ <div class="modal-content">
76
+ <form method="POST" action="{{ url_for('admin_reply_review', review_id=review.id) }}">
77
+ <div class="modal-header">
78
+ <h5 class="modal-title">Ответ на отзыв</h5>
79
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
80
+ </div>
81
+ <div class="modal-body">
82
+ <textarea name="admin_reply" class="form-control" rows="3"
83
+ required>{{ review.admin_reply or '' }}</textarea>
84
+ </div>
85
+ <div class="modal-footer">
86
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
87
+ <button type="submit" class="btn btn-primary">Сохранить</button>
88
+ </div>
89
+ </form>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ {% endif %}
94
+ </div>
95
+ </div>
96
+ {% endfor %}
97
+ {% else %}
98
+ <div class="text-center py-5 text-muted">
99
+ <div class="fs-1">💬</div>
100
+ <p>Пока нет отзывов. Будьте первым!</p>
101
+ </div>
102
+ {% endif %}
103
+ </div>
104
+ {% endblock %}
@@ -0,0 +1,67 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}{{ service.name }}{% endblock %}
3
+ {% block content %}
4
+ <div class="container py-4">
5
+ <nav aria-label="breadcrumb" class="mb-3">
6
+ <ol class="breadcrumb">
7
+ <li class="breadcrumb-item"><a href="{{ url_for('index') }}">Главная</a></li>
8
+ <li class="breadcrumb-item"><a href="{{ url_for('services') }}">Услуги</a></li>
9
+ <li class="breadcrumb-item active">{{ service.name }}</li>
10
+ </ol>
11
+ </nav>
12
+
13
+ <div class="row g-4">
14
+ <div class="col-md-5">
15
+ {{SVC_DETAIL_IMAGE}}
16
+ </div>
17
+ <div class="col-md-7">
18
+ <h1 class="mb-3">{{ service.name }}</h1>
19
+
20
+ {{SVC_DETAIL_FIELDS}}
21
+
22
+ {% if session.get('user_id') %}
23
+ {% if session.get('role') == 'admin' %}
24
+ <div class="alert alert-info mt-3">
25
+ <i class="bi bi-shield-check me-2"></i>Вы вошли как администратор. Подача заявок недоступна.
26
+ </div>
27
+ {% else %}
28
+ <button type="button" class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#requestServiceModal">
29
+ <i class="bi bi-check-circle me-2"></i>Записаться / Подать заявку
30
+ </button>
31
+ {% endif %}
32
+ {% else %}
33
+ <a href="{{ url_for('login') }}" class="btn btn-outline-primary btn-lg">
34
+ Войдите, чтобы записаться
35
+ </a>
36
+ {% endif %}
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Модальное окно: Подать заявку на услугу -->
42
+ {% if session.get('user_id') and session.get('role') != 'admin' %}
43
+ <div class="modal fade" id="requestServiceModal" tabindex="-1" aria-labelledby="requestServiceModalLabel" aria-hidden="true">
44
+ <div class="modal-dialog modal-dialog-centered">
45
+ <div class="modal-content">
46
+ <div class="modal-header">
47
+ <h5 class="modal-title" id="requestServiceModalLabel">Подать заявку на «{{ service.name }}»</h5>
48
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
49
+ </div>
50
+ <form action="{{ url_for('request_service', service_id=service.id) }}" method="post">
51
+ <div class="modal-body">
52
+ <p class="text-muted mb-3">Заполните данные для подачи заявки:</p>
53
+ {{REQUEST_FIELDS_HTML}}
54
+ </div>
55
+ <div class="modal-footer">
56
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
57
+ <button type="submit" class="btn btn-primary">Отправить заявку</button>
58
+ </div>
59
+ </form>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ {% endif %}
64
+
65
+ {{SERVICE_REVIEWS_BLOCK}}
66
+
67
+ {% endblock %}
@@ -0,0 +1,86 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Управление услугами{% endblock %}
3
+ {% block content %}
4
+ <div class="container py-4">
5
+ <div class="d-flex justify-content-between align-items-center mb-4">
6
+ <h1>Управление услугами</h1>
7
+ <a href="{{ url_for('admin_service_add') }}" class="btn btn-success">
8
+ <i class="bi bi-plus-circle me-1"></i>Добавить услугу
9
+ </a>
10
+ </div>
11
+
12
+ <!-- Форма добавления/редактирования -->
13
+ <div class="card shadow-sm mb-4">
14
+ <div class="card-header">
15
+ {% if service %}Редактировать: {{ service.name }}{% else %}Новая услуга{% endif %}
16
+ </div>
17
+ <div class="card-body">
18
+ <form method="post"
19
+ action="{% if service %}{{ url_for('admin_service_edit', service_id=service.id) }}{% else %}{{ url_for('admin_service_add') }}{% endif %}"
20
+ enctype="multipart/form-data">
21
+ <div class="row g-3">
22
+ <div class="col-md-6">
23
+ <label class="form-label">Название <span class="text-danger">*</span></label>
24
+ <input type="text" name="name" class="form-control"
25
+ value="{{ service.name if service else '' }}" required>
26
+ </div>
27
+
28
+ {{SVC_FORM_FIELDS}}
29
+ </div>
30
+
31
+ <div class="mt-3">
32
+ <button type="submit" class="btn btn-primary">
33
+ {% if service %}Сохранить изменения{% else %}Добавить услугу{% endif %}
34
+ </button>
35
+ {% if service %}
36
+ <a href="{{ url_for('admin_services') }}" class="btn btn-secondary ms-2">Отмена</a>
37
+ {% endif %}
38
+ </div>
39
+ </form>
40
+ </div>
41
+ </div>
42
+
43
+ <!-- Список услуг -->
44
+ {% if services %}
45
+ <div class="card shadow-sm">
46
+ <div class="card-header">Список услуг ({{ services|length }})</div>
47
+ <div class="table-responsive">
48
+ <table class="table table-hover mb-0">
49
+ <thead class="table-light">
50
+ <tr>
51
+ <th>#</th>
52
+ <th>Название</th>
53
+ {{SVC_TABLE_HEADERS}}
54
+ <th>Действия</th>
55
+ </tr>
56
+ </thead>
57
+ <tbody>
58
+ {% for svc in services %}
59
+ <tr>
60
+ <td>{{ svc.id }}</td>
61
+ <td>
62
+ <a href="{{ url_for('service_detail', service_id=svc.id) }}" target="_blank">
63
+ {{ svc.name }}
64
+ </a>
65
+ </td>
66
+ {{SVC_TABLE_CELLS}}
67
+ <td>
68
+ <a href="{{ url_for('admin_service_edit', service_id=svc.id) }}"
69
+ class="btn btn-sm btn-outline-primary me-1">Изменить</a>
70
+ <form action="{{ url_for('admin_service_delete', service_id=svc.id) }}"
71
+ method="post" class="d-inline"
72
+ onsubmit="return confirm('Удалить услугу «{{ svc.name }}»?')">
73
+ <button type="submit" class="btn btn-sm btn-outline-danger">Удалить</button>
74
+ </form>
75
+ </td>
76
+ </tr>
77
+ {% endfor %}
78
+ </tbody>
79
+ </table>
80
+ </div>
81
+ </div>
82
+ {% else %}
83
+ <div class="alert alert-info">Услуги ещё не добавлены.</div>
84
+ {% endif %}
85
+ </div>
86
+ {% endblock %}
@@ -0,0 +1,47 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Услуги{% endblock %}
3
+ {% block content %}
4
+ <div class="container py-4">
5
+ <h1 class="mb-4">Наши услуги</h1>
6
+ {% if services %}
7
+ <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
8
+ {% for svc in services %}
9
+ <div class="col fade-in-card">
10
+ <div class="card h-100 shadow-sm">
11
+ {% if svc.image %}
12
+ <img src="{{ url_for('static', filename='uploads/services/' + svc.image) }}"
13
+ class="card-img-top" alt="{{ svc.name }}" style="height:200px;object-fit:cover;">
14
+ {% else %}
15
+ <div class="card-img-top bg-secondary d-flex align-items-center justify-content-center"
16
+ style="height:200px;">
17
+ <i class="bi bi-tools text-white" style="font-size:3rem;"></i>
18
+ </div>
19
+ {% endif %}
20
+ <div class="card-body d-flex flex-column">
21
+ <h5 class="card-title">{{ svc.name }}</h5>
22
+ {% if svc.description is defined and svc.description %}
23
+ <p class="card-text text-muted small">{{ svc.description[:120] }}{% if svc.description|length > 120 %}…{% endif %}</p>
24
+ {% endif %}
25
+ <div class="mt-auto">
26
+ {% if svc.price is defined and svc.price %}
27
+ <p class="fw-bold text-primary mb-1">{{ "%.2f"|format(svc.price) }} ₽</p>
28
+ {% endif %}
29
+ {% if svc.duration is defined and svc.duration %}
30
+ <p class="text-muted small mb-1"><i class="bi bi-clock"></i> {{ svc.duration }}</p>
31
+ {% endif %}
32
+ {% if svc.category is defined and svc.category %}
33
+ <span class="badge bg-secondary mb-2">{{ svc.category }}</span>
34
+ {% endif %}
35
+ <a href="{{ url_for('service_detail', service_id=svc.id) }}"
36
+ class="btn btn-outline-primary btn-sm w-100">Подробнее</a>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ {% endfor %}
42
+ </div>
43
+ {% else %}
44
+ <div class="alert alert-info">Услуги пока не добавлены.</div>
45
+ {% endif %}
46
+ </div>
47
+ {% endblock %}
@@ -0,0 +1,99 @@
1
+ // ═══════════════════════════════════════════════════════════
2
+ // Слайдер + вспомогательные скрипты
3
+ // ═══════════════════════════════════════════════════════════
4
+
5
+ document.addEventListener('DOMContentLoaded', function () {
6
+
7
+ // ─── Слайдер ──────────────────────────────────────────────
8
+ const slider = document.querySelector('.slider-container');
9
+ if (slider) {
10
+ const slides = slider.querySelector('.slides');
11
+ const slideItems = slider.querySelectorAll('.slide');
12
+ const prevBtn = slider.querySelector('.prev');
13
+ const nextBtn = slider.querySelector('.next');
14
+ const dots = slider.querySelectorAll('.dot');
15
+
16
+ if (!slides || slideItems.length === 0) return;
17
+
18
+ let currentIndex = 0;
19
+ const total = slideItems.length;
20
+ let autoInterval;
21
+
22
+ function goTo(index) {
23
+ currentIndex = (index + total) % total;
24
+ slides.style.transform = 'translateX(-' + (currentIndex * 100) + '%)';
25
+ dots.forEach(function (d, i) {
26
+ d.classList.toggle('active', i === currentIndex);
27
+ });
28
+ }
29
+
30
+ function next() { goTo(currentIndex + 1); resetAuto(); }
31
+ function prev() { goTo(currentIndex - 1); resetAuto(); }
32
+
33
+ function startAuto() {
34
+ autoInterval = setInterval(next, 3000);
35
+ }
36
+
37
+ function resetAuto() {
38
+ clearInterval(autoInterval);
39
+ startAuto();
40
+ }
41
+
42
+ if (prevBtn) prevBtn.addEventListener('click', prev);
43
+ if (nextBtn) nextBtn.addEventListener('click', next);
44
+
45
+ // Клик по точкам
46
+ dots.forEach(function (dot, i) {
47
+ dot.addEventListener('click', function () { goTo(i); resetAuto(); });
48
+ });
49
+
50
+ // Пауза при наведении
51
+ slider.addEventListener('mouseenter', function () { clearInterval(autoInterval); });
52
+ slider.addEventListener('mouseleave', startAuto);
53
+
54
+ // Свайп на мобильных
55
+ let touchStartX = 0;
56
+ slider.addEventListener('touchstart', function (e) {
57
+ touchStartX = e.changedTouches[0].clientX;
58
+ }, { passive: true });
59
+ slider.addEventListener('touchend', function (e) {
60
+ const diff = touchStartX - e.changedTouches[0].clientX;
61
+ if (Math.abs(diff) > 40) {
62
+ diff > 0 ? next() : prev();
63
+ }
64
+ }, { passive: true });
65
+
66
+ startAuto();
67
+ }
68
+
69
+ // ─── Анимация появления карточек (Intersection Observer) ──
70
+ const animatedEls = document.querySelectorAll('.animate-fade-up');
71
+ if (animatedEls.length > 0 && 'IntersectionObserver' in window) {
72
+ const observer = new IntersectionObserver(function (entries) {
73
+ entries.forEach(function (entry) {
74
+ if (entry.isIntersecting) {
75
+ entry.target.style.opacity = '1';
76
+ entry.target.style.transform = 'translateY(0)';
77
+ observer.unobserve(entry.target);
78
+ }
79
+ });
80
+ }, { threshold: 0.1 });
81
+
82
+ animatedEls.forEach(function (el) {
83
+ el.style.opacity = '0';
84
+ el.style.transform = 'translateY(24px)';
85
+ el.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
86
+ observer.observe(el);
87
+ });
88
+ }
89
+
90
+ // ─── Автоскрытие flash-сообщений через 5 секунд ───────────
91
+ const alerts = document.querySelectorAll('.alert.alert-dismissible');
92
+ alerts.forEach(function (alert) {
93
+ setTimeout(function () {
94
+ const bsAlert = bootstrap.Alert.getOrCreateInstance(alert);
95
+ if (bsAlert) bsAlert.close();
96
+ }, 5000);
97
+ });
98
+
99
+ });