Kgshop commited on
Commit
83829ea
·
verified ·
1 Parent(s): b159c61

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -18
app.py CHANGED
@@ -60,7 +60,7 @@ def download_db_from_hf(specific_file=None, retries=DOWNLOAD_RETRIES, delay=DOWN
60
  if not os.path.exists(file_name):
61
  try:
62
  with open(file_name, 'w', encoding='utf-8') as f:
63
- json.dump({'equipment': [], 'categories': [], 'services': [], 'projects': []}, f)
64
  except Exception as create_e:
65
  logging.error(f"Failed to create empty local file {file_name}: {create_e}")
66
  success = True
@@ -106,7 +106,7 @@ def periodic_backup():
106
  logging.info("Periodic backup finished.")
107
 
108
  def load_data():
109
- default_data = {'equipment': [], 'categories': [], 'services': [], 'projects': []}
110
  try:
111
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
112
  data = json.load(file)
@@ -116,6 +116,8 @@ def load_data():
116
  if 'categories' not in data: data['categories'] = []
117
  if 'services' not in data: data['services'] = []
118
  if 'projects' not in data: data['projects'] = []
 
 
119
  return data
120
  except (FileNotFoundError, json.JSONDecodeError, ValueError):
121
  logging.warning(f"Local file {DATA_FILE} not found or corrupt. Attempting download.")
@@ -358,7 +360,7 @@ LANDING_TEMPLATE = '''
358
  .contact-info p { font-size: 1.3rem; margin-bottom: 0; color: var(--text-primary); }
359
  .contact-info a { color: var(--accent-primary); text-decoration: none; font-weight: 600; transition: color 0.3s ease; }
360
  .contact-info a:hover { color: var(--accent-secondary); }
361
- .contact-info .btn { font-size: 1.1rem; color: #fff !important; } /* Force white color */
362
  .contact-info .btn i { margin-right: 10px; }
363
 
364
  .footer { text-align: center; padding: 40px 0; background-color: #0c111d; border-top: 1px solid var(--border-color); }
@@ -524,6 +526,7 @@ LANDING_TEMPLATE = '''
524
  <section id="equipment">
525
  <div class="container">
526
  <h2>Наше Оборудование</h2>
 
527
  {% if equipment %}
528
  <div class="equipment-filters">
529
  <button class="filter-btn active" data-filter="all">Все</button>
@@ -540,8 +543,13 @@ LANDING_TEMPLATE = '''
540
  <img src="https://via.placeholder.com/250x180.png?text=No+Image" alt="No Image" style="filter: grayscale(0.8) opacity(0.6);">
541
  {% endif %}
542
  <h3>{{ item.name }}</h3>
543
- <p class="price">{{ "%.2f"|format(item.price) }} KGS</p>
544
- <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: {{ item.name|urlencode }}" target="_blank" class="btn">Запросить</a>
 
 
 
 
 
545
  </div>
546
  {% endfor %}
547
  </div>
@@ -611,6 +619,10 @@ LANDING_TEMPLATE = '''
611
  modalBody.innerHTML = '';
612
  let content = '';
613
 
 
 
 
 
614
  if (currentType === 'service') {
615
  content = `
616
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/${item.photo}" alt="${item.title}">` : ''}
@@ -618,12 +630,28 @@ LANDING_TEMPLATE = '''
618
  <p>${item.description || 'Описание отсутствует.'}</p>
619
  `;
620
  } else if (currentType === 'equipment') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  content = `
622
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/${item.photo}" alt="${item.name}">` : '<img src="https://via.placeholder.com/400x300.png?text=No+Image" alt="No Image Available">'}
623
- <h3>${item.name || 'Оборудование'}</h3>
624
  <p><strong>Категория:</strong> ${item.category || 'Не указана'}</p>
625
- <p class="price" style="font-size: 1.8rem; color: var(--accent-primary); margin: 20px 0;">${(item.price || 0).toFixed(2)} KGS</p>
626
- <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: ${encodeURIComponent(item.name || '')}" target="_blank" class="btn" style="padding: 14px 30px; font-size: 1.05rem;">Запросить</a>
627
  `;
628
  } else if (currentType === 'project') {
629
  content = `
@@ -654,8 +682,9 @@ LANDING_TEMPLATE = '''
654
  } else {
655
  prevBtn.style.display = 'inline-block';
656
  nextBtn.style.display = 'inline-block';
657
- prevBtn.disabled = currentIndex === 0;
658
- nextBtn.disabled = currentIndex === allItems.length - 1;
 
659
  }
660
  }
661
 
@@ -759,6 +788,9 @@ ADMIN_TEMPLATE = '''
759
  .message.success { background-color: #d4edda; color: #155724; }
760
  .message.error { background-color: #f8d7da; color: #721c24; }
761
  .message.warning { background-color: #fff3cd; color: #856404; }
 
 
 
762
  </style>
763
  </head>
764
  <body>
@@ -767,7 +799,18 @@ ADMIN_TEMPLATE = '''
767
  {% with messages = get_flashed_messages(with_categories=true) %}{% if messages %}{% for category, message in messages %}<div class="message {{ category }}">{{ message }}</div>{% endfor %}{% endif %}{% endwith %}
768
 
769
  <div class="section">
770
- <h2><i class="fas fa-sync-alt"></i> Синхронизация</h2>
 
 
 
 
 
 
 
 
 
 
 
771
  <form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;"><button type="submit" class="button">Загрузить на сервер</button></form>
772
  <form method="POST" action="{{ url_for('force_download') }}" style="display: inline;"><button type="submit" class="button">Скачать с сервера</button></form>
773
  </div>
@@ -855,7 +898,7 @@ ADMIN_TEMPLATE = '''
855
  <details style="margin-top:20px;"><summary>Добавить оборудование</summary>
856
  <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="add_equipment">
857
  <label>Название*:</label><input type="text" name="name" required>
858
- <label>Цена (KGS)*:</label><input type="number" name="price" step="0.01" min="0" required>
859
  <label>Категория:</label><select name="category"><option value="Без категории">Без категории</option>{% for cat in categories %}<option value="{{ cat }}">{{ cat }}</option>{% endfor %}</select>
860
  <label>Фото:</label><input type="file" name="photo" accept="image/*">
861
  <button type="submit">Добавить</button>
@@ -873,7 +916,7 @@ ADMIN_TEMPLATE = '''
873
  <div id="edit-eq-{{ loop.index0 }}" class="edit-form-container">
874
  <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="edit_equipment"><input type="hidden" name="index" value="{{ loop.index0 }}">
875
  <label>Название*:</label><input type="text" name="name" value="{{ item.name }}" required>
876
- <label>Цена (KGS)*:</label><input type="number" name="price" value="{{ item.price }}" step="0.01" min="0" required>
877
  <label>Категория:</label><select name="category">{% for cat in categories %}<option value="{{ cat }}" {% if item.category == cat %}selected{% endif %}>{{ cat }}</option>{% endfor %}</select>
878
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
879
  <button type="submit">Сохранить</button>
@@ -912,7 +955,11 @@ def admin():
912
  action = request.form.get('action')
913
  logging.info(f"Admin action: {action}")
914
  try:
915
- if action == 'add_category':
 
 
 
 
916
  name = request.form.get('category_name', '').strip()
917
  if name and name not in data['categories']:
918
  data['categories'].append(name)
@@ -927,11 +974,12 @@ def admin():
927
 
928
  elif action in ['add_equipment', 'edit_equipment']:
929
  name = request.form.get('name', '').strip()
930
- price_str = request.form.get('price')
931
  price = round(float(price_str), 2) if price_str else 0
932
  category = request.form.get('category')
933
- if not name or price <= 0:
934
- flash("Название и цена обязательны.", 'error')
 
935
  return redirect(url_for('admin'))
936
 
937
  item_data = {'name': name, 'price': price, 'category': category}
@@ -1035,7 +1083,8 @@ def admin():
1035
  categories=sorted(data.get('categories', [])),
1036
  services=data.get('services', []),
1037
  projects=data.get('projects', []),
1038
- repo_id=REPO_ID
 
1039
  )
1040
 
1041
  def upload_photo_to_hf(photo, item_name, folder):
 
60
  if not os.path.exists(file_name):
61
  try:
62
  with open(file_name, 'w', encoding='utf-8') as f:
63
+ json.dump({'equipment': [], 'categories': [], 'services': [], 'projects': [], 'settings': {'prices_enabled': True}}, f)
64
  except Exception as create_e:
65
  logging.error(f"Failed to create empty local file {file_name}: {create_e}")
66
  success = True
 
106
  logging.info("Periodic backup finished.")
107
 
108
  def load_data():
109
+ default_data = {'equipment': [], 'categories': [], 'services': [], 'projects': [], 'settings': {'prices_enabled': True}}
110
  try:
111
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
112
  data = json.load(file)
 
116
  if 'categories' not in data: data['categories'] = []
117
  if 'services' not in data: data['services'] = []
118
  if 'projects' not in data: data['projects'] = []
119
+ if 'settings' not in data: data['settings'] = {'prices_enabled': True}
120
+ if 'prices_enabled' not in data['settings']: data['settings']['prices_enabled'] = True
121
  return data
122
  except (FileNotFoundError, json.JSONDecodeError, ValueError):
123
  logging.warning(f"Local file {DATA_FILE} not found or corrupt. Attempting download.")
 
360
  .contact-info p { font-size: 1.3rem; margin-bottom: 0; color: var(--text-primary); }
361
  .contact-info a { color: var(--accent-primary); text-decoration: none; font-weight: 600; transition: color 0.3s ease; }
362
  .contact-info a:hover { color: var(--accent-secondary); }
363
+ .contact-info .btn { font-size: 1.1rem; color: #fff !important; }
364
  .contact-info .btn i { margin-right: 10px; }
365
 
366
  .footer { text-align: center; padding: 40px 0; background-color: #0c111d; border-top: 1px solid var(--border-color); }
 
526
  <section id="equipment">
527
  <div class="container">
528
  <h2>Наше Оборудование</h2>
529
+ {% set prices_enabled = data.settings.prices_enabled %}
530
  {% if equipment %}
531
  <div class="equipment-filters">
532
  <button class="filter-btn active" data-filter="all">Все</button>
 
543
  <img src="https://via.placeholder.com/250x180.png?text=No+Image" alt="No Image" style="filter: grayscale(0.8) opacity(0.6);">
544
  {% endif %}
545
  <h3>{{ item.name }}</h3>
546
+ {% if prices_enabled and item.price > 0 %}
547
+ <p class="price">{{ "%.2f"|format(item.price) }} KGS</p>
548
+ <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: {{ item.name|urlencode }}" target="_blank" class="btn">Запросить</a>
549
+ {% else %}
550
+ <p class="price">Уточнить цену</p>
551
+ <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, хочу узнать цену {{ item.name|urlencode }}" target="_blank" class="btn">Уточнить цену</a>
552
+ {% endif %}
553
  </div>
554
  {% endfor %}
555
  </div>
 
619
  modalBody.innerHTML = '';
620
  let content = '';
621
 
622
+ const dataSettings = {{ data.settings | tojson }};
623
+ const pricesEnabled = dataSettings.prices_enabled;
624
+ const whatsappPhone = '{{ whatsapp_phone }}';
625
+
626
  if (currentType === 'service') {
627
  content = `
628
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/${item.photo}" alt="${item.title}">` : ''}
 
630
  <p>${item.description || 'Описание отсутствует.'}</p>
631
  `;
632
  } else if (currentType === 'equipment') {
633
+ let priceHtml = '';
634
+ let buttonHref = '';
635
+ let buttonText = '';
636
+ const itemName = item.name || '';
637
+ const itemPrice = item.price || 0;
638
+
639
+ if (pricesEnabled && itemPrice > 0) {
640
+ priceHtml = `<p class="price" style="font-size: 1.8rem; color: var(--accent-primary); margin: 20px 0;">${itemPrice.toFixed(2)} KGS</p>`;
641
+ buttonText = 'Запросить';
642
+ buttonHref = `https://api.whatsapp.com/send?phone=${whatsappPhone}&text=Здравствуйте, интересует оборудование: ${encodeURIComponent(itemName)}`;
643
+ } else {
644
+ priceHtml = `<p class="price" style="font-size: 1.8rem; color: var(--accent-primary); margin: 20px 0;">Уточнить цену</p>`;
645
+ buttonText = 'Уточнить цену';
646
+ buttonHref = `https://api.whatsapp.com/send?phone=${whatsappPhone}&text=Здравствуйте, хочу узнать цену ${encodeURIComponent(itemName)}`;
647
+ }
648
+
649
  content = `
650
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/${item.photo}" alt="${item.name}">` : '<img src="https://via.placeholder.com/400x300.png?text=No+Image" alt="No Image Available">'}
651
+ <h3>${itemName}</h3>
652
  <p><strong>Категория:</strong> ${item.category || 'Не указана'}</p>
653
+ ${priceHtml}
654
+ <a href="${buttonHref}" target="_blank" class="btn" style="padding: 14px 30px; font-size: 1.05rem;">${buttonText}</a>
655
  `;
656
  } else if (currentType === 'project') {
657
  content = `
 
682
  } else {
683
  prevBtn.style.display = 'inline-block';
684
  nextBtn.style.display = 'inline-block';
685
+ // Since we are wrapping around, navigation is always possible if length > 1
686
+ prevBtn.disabled = false; // currentIndex === 0;
687
+ nextBtn.disabled = false; // currentIndex === allItems.length - 1;
688
  }
689
  }
690
 
 
788
  .message.success { background-color: #d4edda; color: #155724; }
789
  .message.error { background-color: #f8d7da; color: #721c24; }
790
  .message.warning { background-color: #fff3cd; color: #856404; }
791
+ .settings-input-group { display: flex; flex-direction: column; align-items: flex-start;}
792
+ .settings-input-group label { display: flex; align-items: center; gap: 10px; margin-top: 15px;}
793
+ .settings-input-group input[type="checkbox"] { width: auto; margin: 0; }
794
  </style>
795
  </head>
796
  <body>
 
799
  {% with messages = get_flashed_messages(with_categories=true) %}{% if messages %}{% for category, message in messages %}<div class="message {{ category }}">{{ message }}</div>{% endfor %}{% endif %}{% endwith %}
800
 
801
  <div class="section">
802
+ <h2><i class="fas fa-cog"></i> Настройки сайта</h2>
803
+ <div class="settings-input-group">
804
+ <form method="POST"><input type="hidden" name="action" value="update_settings">
805
+ <label>
806
+ <input type="checkbox" name="prices_enabled" value="true" {% if settings.prices_enabled %}checked{% endif %}>
807
+ Цены включены (Снимите флажок, чтобы скрыть цены на сайте)
808
+ </label>
809
+ <button type="submit">Сохранить настройки</button>
810
+ </form>
811
+ </div>
812
+
813
+ <h2 style="margin-top: 20px;"><i class="fas fa-sync-alt"></i> Синхронизация</h2>
814
  <form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;"><button type="submit" class="button">Загрузить на сервер</button></form>
815
  <form method="POST" action="{{ url_for('force_download') }}" style="display: inline;"><button type="submit" class="button">Скачать с сервера</button></form>
816
  </div>
 
898
  <details style="margin-top:20px;"><summary>Добавить оборудование</summary>
899
  <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="add_equipment">
900
  <label>Название*:</label><input type="text" name="name" required>
901
+ <label>Цена (KGS):</label><input type="number" name="price" step="0.01" min="0">
902
  <label>Категория:</label><select name="category"><option value="Без категории">Без категории</option>{% for cat in categories %}<option value="{{ cat }}">{{ cat }}</option>{% endfor %}</select>
903
  <label>Фото:</label><input type="file" name="photo" accept="image/*">
904
  <button type="submit">Добавить</button>
 
916
  <div id="edit-eq-{{ loop.index0 }}" class="edit-form-container">
917
  <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="edit_equipment"><input type="hidden" name="index" value="{{ loop.index0 }}">
918
  <label>Название*:</label><input type="text" name="name" value="{{ item.name }}" required>
919
+ <label>Цена (KGS):</label><input type="number" name="price" value="{{ item.price if item.price else '' }}" step="0.01" min="0">
920
  <label>Категория:</label><select name="category">{% for cat in categories %}<option value="{{ cat }}" {% if item.category == cat %}selected{% endif %}>{{ cat }}</option>{% endfor %}</select>
921
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
922
  <button type="submit">Сохранить</button>
 
955
  action = request.form.get('action')
956
  logging.info(f"Admin action: {action}")
957
  try:
958
+ if action == 'update_settings':
959
+ data['settings']['prices_enabled'] = 'prices_enabled' in request.form
960
+ flash("Настройки сайта обновлены.", 'success')
961
+
962
+ elif action == 'add_category':
963
  name = request.form.get('category_name', '').strip()
964
  if name and name not in data['categories']:
965
  data['categories'].append(name)
 
974
 
975
  elif action in ['add_equipment', 'edit_equipment']:
976
  name = request.form.get('name', '').strip()
977
+ price_str = request.form.get('price', '').strip()
978
  price = round(float(price_str), 2) if price_str else 0
979
  category = request.form.get('category')
980
+
981
+ if not name:
982
+ flash("Название оборудования обязательно.", 'error')
983
  return redirect(url_for('admin'))
984
 
985
  item_data = {'name': name, 'price': price, 'category': category}
 
1083
  categories=sorted(data.get('categories', [])),
1084
  services=data.get('services', []),
1085
  projects=data.get('projects', []),
1086
+ repo_id=REPO_ID,
1087
+ settings=data.get('settings', {'prices_enabled': True})
1088
  )
1089
 
1090
  def upload_photo_to_hf(photo, item_name, folder):