diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -27,16 +27,22 @@ def load_data(): data = json.load(file) if not isinstance(data, dict): logging.warning("Данные не в формате dict, инициализация пустой базы") - return {'posts': [], 'users': {}} + return {'posts': [], 'users': {}, 'pending_organizations': [], 'orders': {}, 'user_orders': {}} if 'posts' not in data: data['posts'] = [] if 'users' not in data: data['users'] = {} + if 'pending_organizations' not in data: + data['pending_organizations'] = [] + if 'orders' not in data: + data['orders'] = {} + if 'user_orders' not in data: + data['user_orders'] = {} logging.info("Данные успешно загружены") return data except Exception as e: logging.error(f"Ошибка загрузки данных: {e}") - return {'posts': [], 'users': {}} + return {'posts': [], 'users': {}, 'pending_organizations': [], 'orders': {}, 'user_orders': {}} def save_data(data): try: @@ -78,7 +84,7 @@ def download_db_from_hf(): logging.error(f"Ошибка скачивания базы: {e}") if not os.path.exists(DATA_FILE): with open(DATA_FILE, 'w', encoding='utf-8') as f: - json.dump({'posts': [], 'users': {}}, f) + json.dump({'posts': [], 'users': {}, 'pending_organizations': [], 'orders': {}, 'user_orders': {}}, f) def periodic_backup(): while True: @@ -99,6 +105,7 @@ BASE_STYLE = ''' --shadow: 0 10px 40px rgba(0, 0, 0, 0.15); --glass-bg: rgba(255, 255, 255, 0.15); --transition: all 0.4s ease; + --cart-btn: #10b981; } * { margin: 0; padding: 0; box-sizing: border-box; } body { @@ -152,6 +159,7 @@ BASE_STYLE = ''' border-radius: 15px; font-size: 1.1em; transition: var(--transition); + position: relative; } body.dark .nav-link { background: var(--card-bg-dark); @@ -169,6 +177,21 @@ BASE_STYLE = ''' .logout-btn:hover { background: #e63970; } + .order-count { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + background: var(--secondary); + color: white; + border-radius: 50%; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.9em; + } .menu-btn { display: none; font-size: 32px; @@ -206,7 +229,13 @@ BASE_STYLE = ''' transform: scale(1.08); background: #5439cc; } - input, textarea { + .cart-btn { + background: var(--cart-btn); + } + .cart-btn:hover { + background: #0d9f6e; + } + input, textarea, select { width: 100%; padding: 14px; margin: 15px 0; @@ -217,11 +246,11 @@ BASE_STYLE = ''' font-size: 1.1em; transition: var(--transition); } - body.dark input, body.dark textarea { + body.dark input, body.dark textarea, body.dark select { border: 2px solid rgba(255, 255, 255, 0.1); color: var(--text-dark); } - input:focus, textarea:focus { + input:focus, textarea:focus, select:focus { outline: none; border-color: var(--primary); background: rgba(255, 255, 255, 0.2); @@ -258,6 +287,68 @@ BASE_STYLE = ''' box-shadow: var(--shadow); transition: var(--transition); } + .cart-float { + position: fixed; + bottom: 20px; + right: 20px; + background: var(--cart-btn); + color: white; + border-radius: 50%; + width: 60px; + height: 60px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: var(--shadow); + cursor: pointer; + z-index: 1000; + transition: var(--transition); + } + .cart-float:hover { + transform: scale(1.1); + background: #0d9f6e; + } + .cart-modal { + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: var(--card-bg-light); + padding: 30px; + border-radius: 25px; + box-shadow: var(--shadow); + z-index: 2000; + max-width: 500px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + } + body.dark .cart-modal { + background: var(--card-bg-dark); + } + .cart-modal h2 { + font-size: 1.8em; + margin-bottom: 20px; + background: linear-gradient(45deg, var(--primary), var(--secondary)); + -webkit-background-clip: text; + color: transparent; + } + .cart-item { + background: var(--glass-bg); + padding: 15px; + border-radius: 15px; + margin-bottom: 15px; + } + .cart-item h3 { + font-size: 1.2em; + margin-bottom: 10px; + } + .cart-total { + font-size: 1.2em; + font-weight: 600; + margin-top: 20px; + } @media (max-width: 768px) { .sidebar { transform: translateX(-100%); @@ -277,10 +368,16 @@ BASE_STYLE = ''' top: 80px; right: 15px; } - .btn, input, textarea { + .btn, input, textarea, select { font-size: 1em; padding: 12px; } + .cart-float { + bottom: 15px; + right: 15px; + width: 50px; + height: 50px; + } } ''' @@ -294,6 +391,10 @@ NAV_HTML = ''' {% if is_authenticated %} 👤 Профиль ({{ username }}) ⬆️ Загрузить + {% if user_type == 'seller' and verified %} + 🛒 Заказы {% if order_count > 0 %}{{ order_count }}{% endif %} + {% endif %} + 📦 Мои заказы 🚪 Выйти {% else %} 🔑 Войти @@ -307,19 +408,84 @@ NAV_HTML = ''' @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': + logging.debug(f"Получен POST-запрос: {request.form}") + username = request.form.get('username') password = request.form.get('password') + user_type = request.form.get('user_type') + phone = request.form.get('phone') + address = request.form.get('address') data = load_data() - + + if not username or not password or not phone or (user_type == 'buyer' and not address): + flash('Заполните все обязательные поля!', 'error') + logging.debug("Не все обязательные поля заполнены") + return redirect(url_for('register')) + if username in data['users']: - flash('Пользователь уже существует!') + flash('Пользователь уже существует!', 'error') + logging.debug(f"Пользователь {username} уже существует") return redirect(url_for('register')) - - data['users'][username] = {'password': password, 'bio': '', 'link': '', 'avatar': None} - save_data(data) - flash('Регистрация успешна! Войдите в систему.') - return redirect(url_for('login')) - + + if 'register_seller' in request.form: + logging.debug("Нажата кнопка регистрации продавца") + org_name = request.form.get('org_name') + org_phone = request.form.get('org_phone') + is_online = request.form.get('is_online') == 'on' + org_address = request.form.get('org_address') if not is_online else None + + if not org_name or not org_phone: + flash('Укажите название организации и рабочий номер!', 'error') + logging.debug("Не указаны org_name или org_phone") + return redirect(url_for('register')) + + data['users'][username] = { + 'password': password, + 'bio': '', + 'link': '', + 'avatar': None, + 'type': 'seller', + 'verified': False, + 'phone': phone, + 'org_phone': org_phone, + 'org_address': org_address, + 'is_online': is_online + } + data['pending_organizations'].append({ + 'username': username, + 'org_name': org_name, + 'org_phone': org_phone, + 'org_address': org_address, + 'is_online': is_online, + 'submitted_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S') + }) + save_data(data) + flash('Ваша заявка принята, мы с вами свяжемся в течение 2 суток', 'success') + logging.debug(f"Продавец {username} зарегистрирован и отправлен на верификацию") + return redirect(url_for('login')) + + elif 'register_buyer' in request.form: + logging.debug("Нажата кнопка регистрации покупателя") + data['users'][username] = { + 'password': password, + 'bio': '', + 'link': '', + 'avatar': None, + 'type': 'buyer', + 'verified': True, + 'phone': phone, + 'address': address + } + save_data(data) + flash('Регистрация успешна! Войдите в систему.', 'success') + logging.debug(f"Покупатель {username} зарегистрирован") + return redirect(url_for('login')) + + else: + flash('Неизвестная ошибка при выборе типа пользователя!', 'error') + logging.debug("Ни одна из кнопок регистрации не была нажата") + return redirect(url_for('register')) + is_authenticated = 'username' in session username = session.get('username', None) html = ''' @@ -333,7 +499,7 @@ def register():
@@ -377,17 +559,30 @@ def register():Итого: 0
+ + +{{ post['description'] }}
+Цена: {{ post['price'] }} {{ post['currency'] }}
Загрузил: {{ post['uploader'] }} | {{ post['upload_date'] }}
Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}
- + {% if is_authenticated and user_type == 'buyer' and verified %} + + + {% endif %} +Цена: ${item.price} ${item.currency} x ${item.quantity} = ${itemTotal.toFixed(2)} ${item.currency}
+Продавец: ${item.uploader}
+ + `; + cartItems.appendChild(div); + }); + const totalCurrency = cart.length > 0 ? cart[0].currency : ''; + cartTotal.textContent = `Итого: ${total.toFixed(2)} ${totalCurrency}`; + } + function removeFromCart(postId) { + let cart = JSON.parse(localStorage.getItem('cart') || '[]'); + cart = cart.filter(item => item.postId !== postId); + localStorage.setItem('cart', JSON.stringify(cart)); + if (cart.length === 0) document.getElementById('cartFloat').style.display = 'none'; + renderCart(); + } + function checkout() { + const cart = JSON.parse(localStorage.getItem('cart') || '[]'); + if (cart.length === 0) { + alert('Корзина пуста!'); + return; + } + const form = document.createElement('form'); + form.method = 'POST'; + form.action = '/profile'; + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = 'checkout'; + input.value = 'true'; + const cartData = document.createElement('input'); + cartData.type = 'hidden'; + cartData.name = 'cart_data'; + cartData.value = JSON.stringify(cart); + form.appendChild(input); + form.appendChild(cartData); + document.body.appendChild(form); + form.submit(); + localStorage.removeItem('cart'); + document.getElementById('cartFloat').style.display = 'none'; + } window.onload = () => { if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark'); + const cart = JSON.parse(localStorage.getItem('cart') || '[]'); + if (cart.length > 0) document.getElementById('cartFloat').style.display = 'block'; const videos = document.querySelectorAll('.post-preview'); videos.forEach(video => { - if (video.tagName === 'VIDEO') { - video.addEventListener('loadedmetadata', () => { - video.currentTime = Math.random() * video.duration; - }); - } + video.addEventListener('loadedmetadata', () => { + video.currentTime = Math.random() * video.duration; + }); }); };