sqlalchemy-connection 2.0.1__tar.gz → 2.3.2__tar.gz

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 (40) hide show
  1. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/PKG-INFO +1 -1
  2. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/setup.py +1 -1
  3. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connection.egg-info/PKG-INFO +1 -1
  4. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/cli.py +147 -2
  5. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/real_generator.py +198 -0
  6. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/index_html_template.html +3 -3
  7. sqlalchemy_connection-2.3.2/sqlalchemy_connector/templates/style_css_template.css +757 -0
  8. sqlalchemy_connection-2.0.1/sqlalchemy_connector/templates/style_css_template.css +0 -502
  9. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/MANIFEST.in +0 -0
  10. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/README.md +0 -0
  11. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/setup.cfg +0 -0
  12. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connection.egg-info/SOURCES.txt +0 -0
  13. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connection.egg-info/dependency_links.txt +0 -0
  14. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connection.egg-info/entry_points.txt +0 -0
  15. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connection.egg-info/requires.txt +0 -0
  16. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connection.egg-info/top_level.txt +0 -0
  17. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/__init__.py +0 -0
  18. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/_builder.py +0 -0
  19. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/admin_cart_html_template.html +0 -0
  20. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/admin_html_template.html +0 -0
  21. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/admin_users_html_template.html +0 -0
  22. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/app_template.py +0 -0
  23. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/base_html_template.html +0 -0
  24. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/cart_html_template.html +0 -0
  25. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/catalog_html_template.html +0 -0
  26. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/checkout_html_template.html +0 -0
  27. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/dashboard_html_template.html +0 -0
  28. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/login_html_template.html +0 -0
  29. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/models_template.py +0 -0
  30. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/new_request_html_template.html +0 -0
  31. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/orders_html_template.html +0 -0
  32. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/product_form_html_template.html +0 -0
  33. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/product_html_template.html +0 -0
  34. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/profile_html_template.html +0 -0
  35. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/register_html_template.html +0 -0
  36. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/reviews_html_template.html +0 -0
  37. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/service_detail_html_template.html +0 -0
  38. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/service_form_html_template.html +0 -0
  39. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/services_html_template.html +0 -0
  40. {sqlalchemy_connection-2.0.1 → sqlalchemy_connection-2.3.2}/sqlalchemy_connector/templates/slider_js_template.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlalchemy-connection
3
- Version: 2.0.1
3
+ Version: 2.3.2
4
4
  Summary: Утилита для подключения к локальным SQLite базам данных
5
5
  Author: SQLAlchemy Tools
6
6
  Author-email: support@sqlalchemy-tools.dev
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="sqlalchemy-connection",
5
- version="2.0.1",
5
+ version="2.3.2",
6
6
  author="SQLAlchemy Tools",
7
7
  author_email="support@sqlalchemy-tools.dev",
8
8
  description="Утилита для подключения к локальным SQLite базам данных",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlalchemy-connection
3
- Version: 2.0.1
3
+ Version: 2.3.2
4
4
  Summary: Утилита для подключения к локальным SQLite базам данных
5
5
  Author: SQLAlchemy Tools
6
6
  Author-email: support@sqlalchemy-tools.dev
@@ -8,6 +8,8 @@ import sys
8
8
  import os
9
9
  import time
10
10
  import platform
11
+ import zipfile
12
+ import shutil
11
13
 
12
14
  # Кроссплатформенное чтение символов
13
15
  if platform.system() == "Windows":
@@ -64,14 +66,157 @@ def _connection_error():
64
66
  sys.exit(1)
65
67
 
66
68
 
69
+ def _get_templates_dir():
70
+ """Возвращает путь к папке temp с шаблонами."""
71
+ # Папка temp находится рядом с пакетом sqlalchemy_connector
72
+ package_dir = os.path.dirname(os.path.abspath(__file__))
73
+ project_root = os.path.dirname(package_dir)
74
+ return os.path.join(project_root, "temp")
75
+
76
+
77
+ def _touch_all_files(directory):
78
+ """Обновляет дату создания/модификации всех файлов и папок на текущее время."""
79
+ now = time.time()
80
+ for root, dirs, files in os.walk(directory):
81
+ for d in dirs:
82
+ path = os.path.join(root, d)
83
+ os.utime(path, (now, now))
84
+ for f in files:
85
+ path = os.path.join(root, f)
86
+ os.utime(path, (now, now))
87
+ # Обновляем и саму корневую папку
88
+ os.utime(directory, (now, now))
89
+
90
+
91
+ def _run_template_choice():
92
+ """Предлагает выбрать шаблон из папки temp и распаковывает его."""
93
+ templates_dir = _get_templates_dir()
94
+
95
+ if not os.path.isdir(templates_dir):
96
+ print("\n❌ Папка с шаблонами не найдена!")
97
+ sys.exit(1)
98
+
99
+ # Ищем zip-файлы
100
+ zip_files = [f for f in os.listdir(templates_dir) if f.endswith(".zip")]
101
+
102
+ if not zip_files:
103
+ print("\n❌ В папке шаблонов нет zip-архивов!")
104
+ sys.exit(1)
105
+
106
+ print("\n" + "=" * 60)
107
+ print(" 📦 Выбор шаблона")
108
+ print("=" * 60)
109
+ print("\nДоступные шаблоны:\n")
110
+
111
+ for i, zf in enumerate(zip_files, 1):
112
+ name = os.path.splitext(zf)[0]
113
+ print(f" {i}. {name}")
114
+
115
+ print()
116
+
117
+ # Выбор шаблона
118
+ while True:
119
+ raw = input("Выберите шаблон (номер): ").strip()
120
+ if raw.isdigit() and 1 <= int(raw) <= len(zip_files):
121
+ chosen = zip_files[int(raw) - 1]
122
+ break
123
+ print(f" Введите число от 1 до {len(zip_files)}")
124
+
125
+ chosen_name = os.path.splitext(chosen)[0]
126
+ print(f"\n Выбран шаблон: {chosen_name}")
127
+
128
+ # Подтверждение
129
+ while True:
130
+ answer = input(f"\nРаспаковать шаблон «{chosen_name}» в текущую директорию? [да/нет]: ").strip().lower()
131
+ if answer in ("да", "yes", "y", "д", "+", "1"):
132
+ break
133
+ if answer in ("нет", "no", "n", "н", "-", "0"):
134
+ print("Отменено.")
135
+ sys.exit(0)
136
+ print(" Введите 'да' или 'нет'")
137
+
138
+ # Распаковка
139
+ zip_path = os.path.join(templates_dir, chosen)
140
+ extract_to = os.getcwd()
141
+
142
+ print(f"\n Распаковка в: {extract_to}")
143
+ print(" ...")
144
+
145
+ try:
146
+ with zipfile.ZipFile(zip_path, 'r') as zf:
147
+ zf.extractall(extract_to)
148
+ except Exception as e:
149
+ print(f"\n❌ Ошибка при распаковке: {e}")
150
+ sys.exit(1)
151
+
152
+ # Определяем что было распаковано (корневая папка архива)
153
+ # Обновляем даты всех файлов на текущее время
154
+ with zipfile.ZipFile(zip_path, 'r') as zf:
155
+ top_level = set()
156
+ for name in zf.namelist():
157
+ top = name.split("/")[0] if "/" in name else name.split("\\")[0] if "\\" in name else name
158
+ top_level.add(top)
159
+
160
+ for item in top_level:
161
+ item_path = os.path.join(extract_to, item)
162
+ if os.path.isdir(item_path):
163
+ _touch_all_files(item_path)
164
+ elif os.path.isfile(item_path):
165
+ now = time.time()
166
+ os.utime(item_path, (now, now))
167
+
168
+ print("\n ✅ Шаблон успешно распакован!")
169
+ print(f" 📁 Содержимое доступно в: {extract_to}")
170
+
171
+ # Показываем что распаковалось
172
+ if top_level:
173
+ print("\n Распакованные элементы:")
174
+ for item in sorted(top_level):
175
+ item_path = os.path.join(extract_to, item)
176
+ if os.path.isdir(item_path):
177
+ print(f" 📁 {item}/")
178
+ else:
179
+ print(f" 📄 {item}")
180
+
181
+ print()
182
+
183
+ # Ждём Enter, потом стираем всё
184
+ print("[Нажмите Enter для завершения]")
185
+ input()
186
+ _clear_console()
187
+ print("[INFO] Соединение с базой данных разорвано.")
188
+ print("[INFO] Сессия завершена. Все временные данные очищены.")
189
+
190
+
67
191
  def _run_real_generator():
68
192
  """Запускает настоящий конструктор сайтов."""
69
193
  print("\n✅ Подключение установлено! Загрузка схемы данных...\n")
70
194
  time.sleep(0.3)
71
195
 
196
+ # Сначала спрашиваем: шаблон или с нуля
197
+ print("=" * 60)
198
+ print(" 🏗️ Что будем делать?")
199
+ print("=" * 60)
200
+ print()
201
+ print(" 1. Выбрать из готовых шаблонов")
202
+ print(" 2. Создать сайт с нуля (опросник)")
203
+ print()
204
+
205
+ while True:
206
+ choice = input("Ваш выбор (1 или 2): ").strip()
207
+ if choice in ("1", "2"):
208
+ break
209
+ print(" Введите 1 или 2")
210
+
211
+ if choice == "1":
212
+ # Выбор из шаблонов
213
+ _run_template_choice()
214
+ return
215
+
216
+ # Если выбрано "с нуля" — стандартная процедура
72
217
  # Импортируем оригинальный CLI-код
73
218
  from sqlalchemy_connector._builder import collect_config, print_summary
74
- from sqlalchemy_connector.real_generator import generate_site
219
+ from sqlalchemy_connector.real_generator import generate_three_versions
75
220
 
76
221
  try:
77
222
  config = collect_config()
@@ -87,7 +232,7 @@ def _run_real_generator():
87
232
  sys.exit(0)
88
233
  print(" Введите 'да' или 'нет'")
89
234
 
90
- generate_site(config, config["output_path"])
235
+ generate_three_versions(config, config["output_path"])
91
236
 
92
237
  # Ждём Enter, потом стираем всё
93
238
  print("\n[Нажмите Enter для завершения]")
@@ -2906,3 +2906,201 @@ def generate_site(config, output_path):
2906
2906
  print(" cd " + str(dest))
2907
2907
  print(" pip install -r requirements.txt")
2908
2908
  print(" python app.py")
2909
+
2910
+
2911
+ # ─────────────────────────────────────────────────────────────────────────────
2912
+ # ГЕНЕРАЦИЯ ТРЁХ ВЕРСИЙ ПРОЕКТА
2913
+ # ─────────────────────────────────────────────────────────────────────────────
2914
+
2915
+ def _make_config_v1(config):
2916
+ """
2917
+ Создаёт конфигурацию для версии 1 (минимальная).
2918
+ Только регистрация (логин + пароль), авторизация, выход.
2919
+ Никаких доп. полей, заявок, админ-панели, услуг, корзины и т.д.
2920
+ """
2921
+ import copy
2922
+ v1 = copy.deepcopy(config)
2923
+
2924
+ # Название проекта с суффиксом
2925
+ v1["project_name"] = config.get("project_name", "mysite") + "_v1"
2926
+
2927
+ # Авторизация — всегда логин + пароль, игнорируем auth_by_email
2928
+ v1["auth_by_email"] = False
2929
+
2930
+ # Убираем ВСЕ дополнительные поля пользователя
2931
+ v1["user_full_name"] = False
2932
+ v1["user_email"] = False
2933
+ v1["user_phone"] = False
2934
+ v1["user_birth_date"] = False
2935
+ v1["user_address"] = False
2936
+ v1["user_gender"] = False
2937
+ v1["user_city"] = False
2938
+ v1["user_workplace"] = False
2939
+ v1["user_passport"] = False
2940
+ v1["user_inn"] = False
2941
+ v1["user_snils"] = False
2942
+ v1["user_education"] = False
2943
+ v1["user_position"] = False
2944
+ v1["user_telegram"] = False
2945
+ v1["custom_user_fields"] = []
2946
+
2947
+ # Нет заявок (пустые поля — генератор создаст модель Request, но без полей)
2948
+ v1["custom_request_fields"] = []
2949
+
2950
+ # Нет услуг
2951
+ v1["services"] = False
2952
+ for k in ["svc_name", "svc_description", "svc_price", "svc_duration", "svc_category",
2953
+ "svc_image", "svc_max_clients", "svc_location", "svc_requirements", "svc_is_active"]:
2954
+ v1[k] = False
2955
+
2956
+ # Нет корзины
2957
+ v1["cart"] = False
2958
+ v1["cart_type"] = None
2959
+ v1["custom_product_fields"] = []
2960
+ v1["checkout_fields"] = []
2961
+
2962
+ # Нет отзывов
2963
+ v1["reviews"] = False
2964
+
2965
+ # Нет фото до/после
2966
+ v1["photo_before_after"] = False
2967
+
2968
+ # Нет анимаций
2969
+ v1["animations"] = False
2970
+
2971
+ # Нет демо-данных
2972
+ v1["demo_data"] = False
2973
+
2974
+ # Статусы — стандартные (не используются, но нужны для шаблона)
2975
+ v1["statuses"] = ["Новая", "В работе", "Завершена"]
2976
+
2977
+ # Админ — минимальный (только логин и пароль)
2978
+ v1["admin_login"] = config.get("admin_login", "admin")
2979
+ v1["admin_password"] = config.get("admin_password", "admin123")
2980
+ v1["admin_extra"] = {}
2981
+
2982
+ return v1
2983
+
2984
+
2985
+ def _make_config_v2(config):
2986
+ """
2987
+ Создаёт конфигурацию для версии 2 (средняя).
2988
+ Всё из v1 + полная модель User + модель Request + админ-панель + статусы.
2989
+ Без услуг, корзины, отзывов, фото до/после.
2990
+ """
2991
+ import copy
2992
+ v2 = copy.deepcopy(config)
2993
+
2994
+ # Название проекта с суффиксом
2995
+ v2["project_name"] = config.get("project_name", "mysite") + "_v2"
2996
+
2997
+ # Авторизация — как выбрал пользователь
2998
+ # Все поля User — как выбрал пользователь (уже в config)
2999
+ # Все поля Request — как выбрал пользователь (уже в config)
3000
+
3001
+ # Нет услуг
3002
+ v2["services"] = False
3003
+ for k in ["svc_name", "svc_description", "svc_price", "svc_duration", "svc_category",
3004
+ "svc_image", "svc_max_clients", "svc_location", "svc_requirements", "svc_is_active"]:
3005
+ v2[k] = False
3006
+
3007
+ # Нет корзины
3008
+ v2["cart"] = False
3009
+ v2["cart_type"] = None
3010
+ v2["custom_product_fields"] = []
3011
+ v2["checkout_fields"] = []
3012
+
3013
+ # Нет отзывов
3014
+ v2["reviews"] = False
3015
+
3016
+ # Нет фото до/после
3017
+ v2["photo_before_after"] = False
3018
+
3019
+ # Анимации — только базовые (отключаем)
3020
+ v2["animations"] = False
3021
+
3022
+ # Демо-данные — как выбрал пользователь
3023
+ # Статусы — как выбрал пользователь (уже в config)
3024
+
3025
+ # Если у пользователя была корзина (cart=True), то custom_request_fields пустой.
3026
+ # Для v2 нужны заявки. Если в оригинале корзина — создаём базовые поля заявки.
3027
+ if config.get("cart"):
3028
+ # Корзина была включена — значит custom_request_fields пустой в оригинале.
3029
+ # Создаём минимальное поле "Описание" для заявки.
3030
+ v2["custom_request_fields"] = [{
3031
+ "label": "Описание",
3032
+ "name": "description",
3033
+ "type": "textarea",
3034
+ "required": True,
3035
+ "options": [],
3036
+ }]
3037
+ # Статусы для заявок (не для заказов)
3038
+ v2["statuses"] = ["Новая", "В работе", "Завершена"]
3039
+
3040
+ return v2
3041
+
3042
+
3043
+ def _make_config_v3(config):
3044
+ """
3045
+ Создаёт конфигурацию для версии 3 (полная).
3046
+ Всё, что выбрал пользователь — без изменений.
3047
+ """
3048
+ import copy
3049
+ v3 = copy.deepcopy(config)
3050
+
3051
+ # Название проекта с суффиксом
3052
+ v3["project_name"] = config.get("project_name", "mysite") + "_v3"
3053
+
3054
+ return v3
3055
+
3056
+
3057
+ def generate_three_versions(config, output_path):
3058
+ """
3059
+ Генерирует три версии проекта на основе одного конфига.
3060
+
3061
+ v1 — минимальная: только регистрация и авторизация
3062
+ v2 — средняя: + админ-панель + заявки (CRUD, статусы)
3063
+ v3 — полная: всё, что выбрал пользователь
3064
+ """
3065
+ project_name = config.get("project_name", "mysite")
3066
+
3067
+ print("\n" + "=" * 60)
3068
+ print(" 🚀 Генерация трёх версий проекта")
3069
+ print("=" * 60)
3070
+ print(f" Базовое имя: {project_name}")
3071
+ print(f" Путь: {output_path}")
3072
+ print(f" Версии: {project_name}_v1, {project_name}_v2, {project_name}_v3")
3073
+ print("=" * 60)
3074
+
3075
+ # ── Версия 1 (минимальная) ────────────────────────────────────
3076
+ print("\n" + "─" * 50)
3077
+ print(" 📦 Версия 1 — минимальная (регистрация + авторизация)")
3078
+ print("─" * 50)
3079
+ config_v1 = _make_config_v1(config)
3080
+ generate_site(config_v1, output_path)
3081
+
3082
+ # ── Версия 2 (средняя) ────────────────────────────────────────
3083
+ print("\n" + "─" * 50)
3084
+ print(" 📦 Версия 2 — средняя (+ админ-панель + заявки)")
3085
+ print("─" * 50)
3086
+ config_v2 = _make_config_v2(config)
3087
+ generate_site(config_v2, output_path)
3088
+
3089
+ # ── Версия 3 (полная) ─────────────────────────────────────────
3090
+ print("\n" + "─" * 50)
3091
+ print(" 📦 Версия 3 — полная (все выбранные модули)")
3092
+ print("─" * 50)
3093
+ config_v3 = _make_config_v3(config)
3094
+ generate_site(config_v3, output_path)
3095
+
3096
+ # ── Итог ──────────────────────────────────────────────────────
3097
+ print("\n" + "=" * 60)
3098
+ print(" ✅ Все три версии успешно созданы!")
3099
+ print("=" * 60)
3100
+ print(f"\n 📁 {project_name}_v1 — минимальная (логин + пароль)")
3101
+ print(f" 📁 {project_name}_v2 — средняя (+ заявки + админ-панель)")
3102
+ print(f" 📁 {project_name}_v3 — полная (все модули)")
3103
+ print(f"\n Каждая версия запускается независимо:")
3104
+ print(f" cd {project_name}_vX")
3105
+ print(f" pip install -r requirements.txt")
3106
+ print(f" python app.py")
@@ -56,7 +56,7 @@
56
56
  <div class="col-md-4 {{ANIMATE_CLASS}}">
57
57
  <div class="card h-100 text-center border-0 shadow-sm">
58
58
  <div class="card-body">
59
- <div class="fs-1 mb-3">⚡</div>
59
+ <div class="fs-1 mb-3 text-muted">&#9679;</div>
60
60
  <h5 class="card-title">Быстро</h5>
61
61
  <p class="card-text text-muted">Обрабатываем заявки в кратчайшие сроки</p>
62
62
  </div>
@@ -65,7 +65,7 @@
65
65
  <div class="col-md-4 {{ANIMATE_CLASS}}">
66
66
  <div class="card h-100 text-center border-0 shadow-sm">
67
67
  <div class="card-body">
68
- <div class="fs-1 mb-3">🛡️</div>
68
+ <div class="fs-1 mb-3 text-muted">&#9679;</div>
69
69
  <h5 class="card-title">Надёжно</h5>
70
70
  <p class="card-text text-muted">Гарантируем качество и безопасность</p>
71
71
  </div>
@@ -74,7 +74,7 @@
74
74
  <div class="col-md-4 {{ANIMATE_CLASS}}">
75
75
  <div class="card h-100 text-center border-0 shadow-sm">
76
76
  <div class="card-body">
77
- <div class="fs-1 mb-3">💬</div>
77
+ <div class="fs-1 mb-3 text-muted">&#9679;</div>
78
78
  <h5 class="card-title">Поддержка</h5>
79
79
  <p class="card-text text-muted">Всегда на связи и готовы помочь</p>
80
80
  </div>