Update app.py
Browse files
app.py
CHANGED
|
@@ -253,8 +253,7 @@ def catalog():
|
|
| 253 |
.category-filter.active, .category-filter:hover { background-color: #1C6758; color: white; border-color: #1C6758; box-shadow: 0 2px 10px rgba(28, 103, 88, 0.3); }
|
| 254 |
body.dark-mode .category-filter.active, body.dark-mode .category-filter:hover { background-color: #3D8361; border-color: #3D8361; color: #1a2b26; box-shadow: 0 2px 10px rgba(61, 131, 97, 0.4); }
|
| 255 |
|
| 256 |
-
.products-grid { display: grid; grid-template-columns: repeat(
|
| 257 |
-
@media (min-width: 600px) { .products-grid { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); } }
|
| 258 |
.product { background: #fff; border-radius: 15px; padding: 0; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; height: 100%; border: 1px solid #e1f0e9;}
|
| 259 |
body.dark-mode .product { background: #253f37; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); border-color: #2c4a41; }
|
| 260 |
.product:hover { transform: translateY(-5px) scale(1.02); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); }
|
|
@@ -439,7 +438,6 @@ def catalog():
|
|
| 439 |
const products = {{ products|tojson }};
|
| 440 |
const repoId = '{{ repo_id }}';
|
| 441 |
const currencyCode = '{{ currency_code }}';
|
| 442 |
-
const storeAddress = '{{ store_address }}';
|
| 443 |
const isAuthenticated = {{ is_authenticated|tojson }};
|
| 444 |
let selectedProductIndex = null;
|
| 445 |
let cart = JSON.parse(localStorage.getItem('soolaCart') || '[]');
|
|
@@ -704,49 +702,43 @@ def catalog():
|
|
| 704 |
alert("Корзина пуста! Добавьте товары перед заказом.");
|
| 705 |
return;
|
| 706 |
}
|
| 707 |
-
|
| 708 |
let total = 0;
|
| 709 |
-
let orderText = "
|
| 710 |
-
orderText += "
|
| 711 |
-
|
| 712 |
cart.forEach((item, index) => {
|
| 713 |
const itemTotal = item.price * item.quantity;
|
| 714 |
total += itemTotal;
|
| 715 |
-
const colorText = item.color !== 'N/A' ? ` (Цвет: ${item.color})` : '';
|
| 716 |
-
orderText += `
|
| 717 |
orderText += ` Цена: ${item.price.toFixed(2)} ${currencyCode}\n`;
|
| 718 |
orderText += ` Кол-во: ${item.quantity}\n`;
|
| 719 |
-
orderText += ` Сумма:
|
| 720 |
});
|
| 721 |
-
|
| 722 |
-
orderText += `\n
|
| 723 |
-
orderText +=
|
| 724 |
-
orderText += `--------------------\n\n`;
|
| 725 |
|
| 726 |
const userInfo = {{ session.get('user_info', {})|tojson }};
|
| 727 |
if (userInfo && userInfo.login) {
|
| 728 |
-
orderText +=
|
| 729 |
-
orderText += `
|
| 730 |
-
orderText += `
|
| 731 |
-
orderText += `
|
| 732 |
-
orderText += `
|
| 733 |
-
orderText += `*Город:* ${userInfo.city || 'Не указан'}\n`;
|
| 734 |
} else {
|
| 735 |
-
orderText +=
|
| 736 |
}
|
| 737 |
|
| 738 |
const now = new Date();
|
| 739 |
-
const dateTimeString = now.toLocaleString('ru-RU', {
|
| 740 |
-
orderText += `\n
|
| 741 |
-
orderText += `\n📍 *Адрес магазина:* ${storeAddress}`;
|
| 742 |
|
| 743 |
-
const whatsappNumber = "996997703090"; //
|
|
|
|
| 744 |
const whatsappUrl = `https://api.whatsapp.com/send?phone=${whatsappNumber}&text=${encodeURIComponent(orderText)}`;
|
| 745 |
-
|
| 746 |
window.open(whatsappUrl, '_blank');
|
| 747 |
}
|
| 748 |
|
| 749 |
-
|
| 750 |
function filterProducts() {
|
| 751 |
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
|
| 752 |
const activeCategoryButton = document.querySelector('.category-filter.active');
|
|
@@ -972,7 +964,7 @@ def login():
|
|
| 972 |
'last_name': user_info.get('last_name', ''),
|
| 973 |
'country': user_info.get('country', ''),
|
| 974 |
'city': user_info.get('city', ''),
|
| 975 |
-
'
|
| 976 |
}
|
| 977 |
logging.info(f"Пользователь {login} успешно вошел в систему.")
|
| 978 |
login_response_html = f'''
|
|
@@ -1015,7 +1007,7 @@ def auto_login():
|
|
| 1015 |
'last_name': user_info.get('last_name', ''),
|
| 1016 |
'country': user_info.get('country', ''),
|
| 1017 |
'city': user_info.get('city', ''),
|
| 1018 |
-
'
|
| 1019 |
}
|
| 1020 |
logging.info(f"Автоматический вход для пользователя {login} выполнен.")
|
| 1021 |
return "OK", 200
|
|
@@ -1183,9 +1175,9 @@ ADMIN_TEMPLATE = '''
|
|
| 1183 |
<label for="login">Логин *:</label>
|
| 1184 |
<input type="text" id="login" name="login" required>
|
| 1185 |
<label for="password">Пароль *:</label>
|
| 1186 |
-
<input type="password" id="password" name="password" required title="Пароль будет сохранен в открытом виде.">
|
| 1187 |
-
<label for="
|
| 1188 |
-
<input type="tel" id="
|
| 1189 |
<p style="font-size: 0.8rem; color: #777;">Логин и пароль обязательны.</p>
|
| 1190 |
<label for="first_name">Имя:</label>
|
| 1191 |
<input type="text" id="first_name" name="first_name">
|
|
@@ -1207,7 +1199,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1207 |
<div class="item">
|
| 1208 |
<p><strong>Логин:</strong> {{ login }}</p>
|
| 1209 |
<p><strong>Имя:</strong> {{ user_data.get('first_name', 'N/A') }} {{ user_data.get('last_name', '') }}</p>
|
| 1210 |
-
<p><strong>Телефон:</strong> {{ user_data.get('
|
| 1211 |
<p><strong>Локация:</strong> {{ user_data.get('city', 'N/A') }}, {{ user_data.get('country', 'N/A') }}</p>
|
| 1212 |
<div class="item-actions">
|
| 1213 |
<form method="POST" style="margin: 0;" onsubmit="return confirm('Вы уверены, что хотите удалить пользователя \'{{ login }}\'?');">
|
|
@@ -1215,6 +1207,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1215 |
<input type="hidden" name="login" value="{{ login }}">
|
| 1216 |
<button type="submit" class="delete-button"><i class="fas fa-user-slash"></i> Удалить</button>
|
| 1217 |
</form>
|
|
|
|
| 1218 |
</div>
|
| 1219 |
</div>
|
| 1220 |
{% endfor %}
|
|
@@ -1651,11 +1644,11 @@ def admin():
|
|
| 1651 |
elif action == 'add_user':
|
| 1652 |
login = request.form.get('login', '').strip()
|
| 1653 |
password = request.form.get('password', '').strip()
|
| 1654 |
-
phone_number = request.form.get('phone_number', '').strip() # Added phone number
|
| 1655 |
first_name = request.form.get('first_name', '').strip()
|
| 1656 |
last_name = request.form.get('last_name', '').strip()
|
| 1657 |
country = request.form.get('country', '').strip()
|
| 1658 |
city = request.form.get('city', '').strip()
|
|
|
|
| 1659 |
|
| 1660 |
if not login or not password:
|
| 1661 |
flash("Логин и пароль пользователя обязательны.", 'error')
|
|
@@ -1666,9 +1659,9 @@ def admin():
|
|
| 1666 |
|
| 1667 |
users[login] = {
|
| 1668 |
'password': password,
|
| 1669 |
-
'phone_number': phone_number, # Added phone number
|
| 1670 |
'first_name': first_name, 'last_name': last_name,
|
| 1671 |
-
'country': country, 'city': city
|
|
|
|
| 1672 |
}
|
| 1673 |
save_users(users)
|
| 1674 |
logging.info(f"Пользователь '{login}' добавлен.")
|
|
@@ -1746,4 +1739,3 @@ if __name__ == '__main__':
|
|
| 1746 |
port = int(os.environ.get('PORT', 7860))
|
| 1747 |
logging.info(f"Запуск Flask приложения на хосте 0.0.0.0 и порту {port}")
|
| 1748 |
app.run(debug=False, host='0.0.0.0', port=port)
|
| 1749 |
-
|
|
|
|
| 253 |
.category-filter.active, .category-filter:hover { background-color: #1C6758; color: white; border-color: #1C6758; box-shadow: 0 2px 10px rgba(28, 103, 88, 0.3); }
|
| 254 |
body.dark-mode .category-filter.active, body.dark-mode .category-filter:hover { background-color: #3D8361; border-color: #3D8361; color: #1a2b26; box-shadow: 0 2px 10px rgba(61, 131, 97, 0.4); }
|
| 255 |
|
| 256 |
+
.products-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; padding: 10px; }
|
|
|
|
| 257 |
.product { background: #fff; border-radius: 15px; padding: 0; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; height: 100%; border: 1px solid #e1f0e9;}
|
| 258 |
body.dark-mode .product { background: #253f37; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); border-color: #2c4a41; }
|
| 259 |
.product:hover { transform: translateY(-5px) scale(1.02); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); }
|
|
|
|
| 438 |
const products = {{ products|tojson }};
|
| 439 |
const repoId = '{{ repo_id }}';
|
| 440 |
const currencyCode = '{{ currency_code }}';
|
|
|
|
| 441 |
const isAuthenticated = {{ is_authenticated|tojson }};
|
| 442 |
let selectedProductIndex = null;
|
| 443 |
let cart = JSON.parse(localStorage.getItem('soolaCart') || '[]');
|
|
|
|
| 702 |
alert("Корзина пуста! Добавьте товары перед заказом.");
|
| 703 |
return;
|
| 704 |
}
|
|
|
|
| 705 |
let total = 0;
|
| 706 |
+
let orderText = "*Новый Заказ от Soola Cosmetics*\n\n";
|
| 707 |
+
orderText += "--- Товары ---\n";
|
|
|
|
| 708 |
cart.forEach((item, index) => {
|
| 709 |
const itemTotal = item.price * item.quantity;
|
| 710 |
total += itemTotal;
|
| 711 |
+
const colorText = item.color !== 'N/A' ? ` (Цвет/Вар-т: ${item.color})` : '';
|
| 712 |
+
orderText += `${index + 1}. ${item.name}${colorText}\n`;
|
| 713 |
orderText += ` Цена: ${item.price.toFixed(2)} ${currencyCode}\n`;
|
| 714 |
orderText += ` Кол-во: ${item.quantity}\n`;
|
| 715 |
+
orderText += ` *Сумма: ${itemTotal.toFixed(2)} ${currencyCode}*\n\n`;
|
| 716 |
});
|
| 717 |
+
orderText += `---------------\n`;
|
| 718 |
+
orderText += `*Итого: ${total.toFixed(2)} ${currencyCode}*\n\n`;
|
| 719 |
+
orderText += "--- Заказчик ---\n";
|
|
|
|
| 720 |
|
| 721 |
const userInfo = {{ session.get('user_info', {})|tojson }};
|
| 722 |
if (userInfo && userInfo.login) {
|
| 723 |
+
orderText += `Имя: ${userInfo.first_name || ''} ${userInfo.last_name || ''}\n`;
|
| 724 |
+
orderText += `Логин: ${userInfo.login}\n`;
|
| 725 |
+
orderText += `Телефон: ${userInfo.phone || 'Не указан'}\n`; // Added phone
|
| 726 |
+
orderText += `Страна: ${userInfo.country || 'Не указана'}\n`;
|
| 727 |
+
orderText += `Город: ${userInfo.city || 'Не указан'}\n`;
|
|
|
|
| 728 |
} else {
|
| 729 |
+
orderText += `Заказчик: (Не авторизован)\n`;
|
| 730 |
}
|
| 731 |
|
| 732 |
const now = new Date();
|
| 733 |
+
const dateTimeString = now.toLocaleString('ru-RU', { dateStyle: 'short', timeStyle: 'medium'});
|
| 734 |
+
orderText += `\nДата заказа: ${dateTimeString}`;
|
|
|
|
| 735 |
|
| 736 |
+
const whatsappNumber = "996997703090"; // Replace with your actual number if needed
|
| 737 |
+
// Encode the text for the URL, WhatsApp will format it correctly
|
| 738 |
const whatsappUrl = `https://api.whatsapp.com/send?phone=${whatsappNumber}&text=${encodeURIComponent(orderText)}`;
|
|
|
|
| 739 |
window.open(whatsappUrl, '_blank');
|
| 740 |
}
|
| 741 |
|
|
|
|
| 742 |
function filterProducts() {
|
| 743 |
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
|
| 744 |
const activeCategoryButton = document.querySelector('.category-filter.active');
|
|
|
|
| 964 |
'last_name': user_info.get('last_name', ''),
|
| 965 |
'country': user_info.get('country', ''),
|
| 966 |
'city': user_info.get('city', ''),
|
| 967 |
+
'phone': user_info.get('phone', '') # Added phone
|
| 968 |
}
|
| 969 |
logging.info(f"Пользователь {login} успешно вошел в систему.")
|
| 970 |
login_response_html = f'''
|
|
|
|
| 1007 |
'last_name': user_info.get('last_name', ''),
|
| 1008 |
'country': user_info.get('country', ''),
|
| 1009 |
'city': user_info.get('city', ''),
|
| 1010 |
+
'phone': user_info.get('phone', '') # Added phone
|
| 1011 |
}
|
| 1012 |
logging.info(f"Автоматический вход для пользователя {login} выполнен.")
|
| 1013 |
return "OK", 200
|
|
|
|
| 1175 |
<label for="login">Логин *:</label>
|
| 1176 |
<input type="text" id="login" name="login" required>
|
| 1177 |
<label for="password">Пароль *:</label>
|
| 1178 |
+
<input type="password" id="password" name="password" required title="Пароль будет сохранен в открытом виде. Рекомендуется использовать менеджер паролей.">
|
| 1179 |
+
<label for="phone">Номер телефона:</label>
|
| 1180 |
+
<input type="tel" id="phone" name="phone" placeholder="Например: +996...">
|
| 1181 |
<p style="font-size: 0.8rem; color: #777;">Логин и пароль обязательны.</p>
|
| 1182 |
<label for="first_name">Имя:</label>
|
| 1183 |
<input type="text" id="first_name" name="first_name">
|
|
|
|
| 1199 |
<div class="item">
|
| 1200 |
<p><strong>Логин:</strong> {{ login }}</p>
|
| 1201 |
<p><strong>Имя:</strong> {{ user_data.get('first_name', 'N/A') }} {{ user_data.get('last_name', '') }}</p>
|
| 1202 |
+
<p><strong>Телефон:</strong> {{ user_data.get('phone', 'Не указан') }}</p>
|
| 1203 |
<p><strong>Локация:</strong> {{ user_data.get('city', 'N/A') }}, {{ user_data.get('country', 'N/A') }}</p>
|
| 1204 |
<div class="item-actions">
|
| 1205 |
<form method="POST" style="margin: 0;" onsubmit="return confirm('Вы уверены, что хотите удалить пользователя \'{{ login }}\'?');">
|
|
|
|
| 1207 |
<input type="hidden" name="login" value="{{ login }}">
|
| 1208 |
<button type="submit" class="delete-button"><i class="fas fa-user-slash"></i> Удалить</button>
|
| 1209 |
</form>
|
| 1210 |
+
<!-- TODO: Add Edit User button/form here later if needed -->
|
| 1211 |
</div>
|
| 1212 |
</div>
|
| 1213 |
{% endfor %}
|
|
|
|
| 1644 |
elif action == 'add_user':
|
| 1645 |
login = request.form.get('login', '').strip()
|
| 1646 |
password = request.form.get('password', '').strip()
|
|
|
|
| 1647 |
first_name = request.form.get('first_name', '').strip()
|
| 1648 |
last_name = request.form.get('last_name', '').strip()
|
| 1649 |
country = request.form.get('country', '').strip()
|
| 1650 |
city = request.form.get('city', '').strip()
|
| 1651 |
+
phone = request.form.get('phone', '').strip() # Added phone
|
| 1652 |
|
| 1653 |
if not login or not password:
|
| 1654 |
flash("Логин и пароль пользователя обязательны.", 'error')
|
|
|
|
| 1659 |
|
| 1660 |
users[login] = {
|
| 1661 |
'password': password,
|
|
|
|
| 1662 |
'first_name': first_name, 'last_name': last_name,
|
| 1663 |
+
'country': country, 'city': city,
|
| 1664 |
+
'phone': phone # Added phone
|
| 1665 |
}
|
| 1666 |
save_users(users)
|
| 1667 |
logging.info(f"Пользователь '{login}' добавлен.")
|
|
|
|
| 1739 |
port = int(os.environ.get('PORT', 7860))
|
| 1740 |
logging.info(f"Запуск Flask приложения на хосте 0.0.0.0 и порту {port}")
|
| 1741 |
app.run(debug=False, host='0.0.0.0', port=port)
|
|
|