Kgshop commited on
Commit
8fc1afc
·
verified ·
1 Parent(s): 945d9a7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +274 -210
app.py CHANGED
@@ -22,7 +22,7 @@ import requests
22
  load_dotenv()
23
 
24
  app = Flask(__name__)
25
- app.secret_key = 'your_unique_secret_key_oco_shop_12345_no_login'
26
  DATA_FILE = 'data.json'
27
 
28
  SYNC_FILES = [DATA_FILE]
@@ -129,7 +129,7 @@ def periodic_backup():
129
 
130
  def load_data():
131
  default_organization_info = {
132
- "about_us": "Мы — O&CO, ваш надежный партнер в мире уникальных товаров. Мы предлагаем широкий ассортимент продукции, от электроники до товаров для дома, всегда стремясь к качеству и доступности. Наша миссия — сделать ваш шопинг приятным и удобным, предлагая только лучшие товары, тщательно отобранные для вас.",
133
  "shipping": "Доставка осуществляется по всему Кыргызстану. Стоимость и сроки доставки зависят от региона и веса товара. По Бишкеку доставка возможна в течение 1-2 рабочих дней, в регионы — от 3 до 7 дней. Для уточнения деталей свяжитесь с нами.",
134
  "returns": "Возврат и обмен товара возможен в течение 14 дней с момента покупки, при условии сохранения товарного вида, упаковки и чека. Некоторые категории товаров могут иметь особые условия возврата. Пожалуйста, свяжитесь с нами для оформления возврата или обмена.",
135
  "contact": f"Наш магазин находится по адресу: {STORE_ADDRESS}. Связаться с нами можно по телефону: {WHATSAPP_NUMBER} или через WhatsApp по этому же номеру. Мы работаем ежедневно с 9:00 до 18:00."
@@ -305,7 +305,7 @@ def generate_chat_response(message, chat_history_from_client):
305
 
306
 
307
  system_instruction_content = (
308
- "Ты - доброжелательный и очень полезный виртуальный консультант для магазина O&CO. "
309
  "Твоя задача - помогать пользователям находить товары, отвечать на вопросы о них, предлагать варианты, а также предоставлять информацию о магазине. "
310
  "Всегда будь вежлив, информативен и стремись решить проблему пользователя. "
311
  "Никогда не выдумывай товары или категории, которых нет в предоставленных списках. "
@@ -375,17 +375,27 @@ CATALOG_TEMPLATE = '''
375
  <head>
376
  <meta charset="UTF-8">
377
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
378
- <title>O&CO - Каталог</title>
379
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
380
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
381
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
382
  <style>
 
 
 
 
 
 
 
 
 
 
383
  * { margin: 0; padding: 0; box-sizing: border-box; }
384
  html { -webkit-tap-highlight-color: transparent; }
385
  body {
386
- font-family: 'Poppins', sans-serif;
387
- background-color: #0A2A66;
388
- color: #ffffff;
389
  line-height: 1.6;
390
  }
391
  .container {
@@ -400,8 +410,18 @@ CATALOG_TEMPLATE = '''
400
  gap: 15px;
401
  position: sticky;
402
  top: 0;
403
- background-color: #0A2A66;
404
  z-index: 999;
 
 
 
 
 
 
 
 
 
 
405
  }
406
  .search-wrapper {
407
  flex-grow: 1;
@@ -414,33 +434,19 @@ CATALOG_TEMPLATE = '''
414
  border: none;
415
  border-radius: 25px;
416
  outline: none;
417
- background-color: #004282;
418
- color: #ffffff;
419
  transition: all 0.3s ease;
420
  }
421
- #search-input::placeholder { color: rgba(255, 255, 255, 0.6); }
422
  .search-wrapper .fa-search {
423
  position: absolute;
424
  top: 50%;
425
  left: 18px;
426
  transform: translateY(-50%);
427
- color: rgba(255, 255, 255, 0.6);
428
  font-size: 1rem;
429
  }
430
- .top-bar-icon {
431
- flex-shrink: 0;
432
- width: 48px;
433
- height: 48px;
434
- background-color: #004282;
435
- border-radius: 50%;
436
- display: flex;
437
- align-items: center;
438
- justify-content: center;
439
- color: white;
440
- font-size: 1.2rem;
441
- cursor: pointer;
442
- text-decoration: none;
443
- }
444
  .category-section {
445
  margin-top: 20px;
446
  }
@@ -449,7 +455,7 @@ CATALOG_TEMPLATE = '''
449
  justify-content: space-between;
450
  align-items: center;
451
  padding: 0 20px;
452
- margin-bottom: 10px;
453
  }
454
  .category-header h2 {
455
  font-size: 1.5rem;
@@ -457,7 +463,7 @@ CATALOG_TEMPLATE = '''
457
  }
458
  .category-header .view-all-arrow {
459
  font-size: 1.8rem;
460
- color: #fff;
461
  text-decoration: none;
462
  font-weight: 300;
463
  }
@@ -478,10 +484,14 @@ CATALOG_TEMPLATE = '''
478
  flex-shrink: 0;
479
  overflow: hidden;
480
  cursor: pointer;
481
- transition: transform 0.2s ease-in-out;
482
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
483
  position: relative;
484
  }
 
 
 
 
485
  .product-card:active { transform: scale(0.96); }
486
  .product-image-container {
487
  width: 100%;
@@ -497,10 +507,8 @@ CATALOG_TEMPLATE = '''
497
  bottom: 0;
498
  left: 0;
499
  right: 0;
500
- background: linear-gradient(to top, rgba(0, 0, 0, 0.75) 0%, transparent 100%);
501
  padding: 20px 12px 10px;
502
- border-bottom-left-radius: 16px;
503
- border-bottom-right-radius: 16px;
504
  color: #fff;
505
  pointer-events: none;
506
  }
@@ -512,8 +520,8 @@ CATALOG_TEMPLATE = '''
512
  position: absolute;
513
  top: 8px;
514
  right: 8px;
515
- background-color: rgba(255, 215, 0, 0.9);
516
- color: #333;
517
  padding: 2px 8px;
518
  font-size: 0.7rem;
519
  border-radius: 10px;
@@ -531,8 +539,8 @@ CATALOG_TEMPLATE = '''
531
  z-index: 1000;
532
  }
533
  .floating-button {
534
- background-color: #0070D1;
535
- color: white;
536
  border: none;
537
  border-radius: 50%;
538
  width: 55px;
@@ -542,137 +550,90 @@ CATALOG_TEMPLATE = '''
542
  display: flex;
543
  align-items: center;
544
  justify-content: center;
545
- box-shadow: 0 4px 15px rgba(0, 112, 209, 0.4);
546
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
547
  }
548
  .floating-button:hover {
549
- background-color: #005CBF;
550
  transform: translateY(-3px);
551
- box-shadow: 0 6px 20px rgba(0, 112, 209, 0.5);
552
  }
553
  #cart-button { position: relative; }
554
  #cart-count {
555
  position: absolute;
556
  top: -2px;
557
  right: -2px;
558
- background-color: #D32F2F;
559
  color: white;
560
  border-radius: 50%;
561
  padding: 2px 6px;
562
  font-size: 0.7rem;
563
  font-weight: bold;
564
- border: 2px solid #0070D1;
565
  }
566
  .modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); backdrop-filter: blur(5px); overflow-y: auto; }
567
- .modal-content { background: #ffffff; color: #333; margin: 5% auto; padding: 25px; border-radius: 15px; width: 90%; max-width: 700px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); animation: slideIn 0.3s ease-out; position: relative; }
568
  @keyframes slideIn { from { transform: translateY(-30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
569
  .close { position: absolute; top: 15px; right: 15px; font-size: 1.8rem; color: #aaa; cursor: pointer; transition: color 0.3s; line-height: 1; }
570
  .close:hover { color: #666; }
571
- .modal-content h2 { margin-top: 0; margin-bottom: 20px; color: #0A2A66; display: flex; align-items: center; gap: 10px;}
572
- .cart-item { display: grid; grid-template-columns: auto 1fr auto auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #e0e0e0; }
573
  .cart-item:last-child { border-bottom: none; }
574
- .cart-item img { width: 60px; height: 60px; object-fit: contain; border-radius: 8px; background-color: #fff; padding: 5px; grid-column: 1; border: 1px solid #e0e0e0;}
575
  .cart-item-details { grid-column: 2; }
576
- .cart-item-details strong { display: block; margin-bottom: 5px; font-size: 1rem; color: #333;}
577
  .cart-item-price { font-size: 0.9rem; color: #666; }
578
- .cart-item-total { font-weight: bold; text-align: right; grid-column: 3; font-size: 1rem; color: #0A2A66;}
579
- .cart-item-remove { grid-column: 4; background:none; border:none; color:#dc3545; cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
580
- .cart-item-remove:hover { color: #c82333; }
 
 
581
  .quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; }
582
- .quantity-input:focus, .color-select:focus { border-color: #7986CB; outline: none; box-shadow: 0 0 0 2px rgba(121, 134, 203, 0.1); }
583
  .cart-summary { margin-top: 20px; text-align: right; border-top: 1px solid #e0e0e0; padding-top: 15px; }
584
- .cart-summary strong { font-size: 1.2rem; color: #0A2A66;}
585
  .cart-actions { margin-top: 25px; display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
586
  .product-button { display: block; width: auto; flex-grow: 1; padding: 10px; border: none; border-radius: 8px; color: white; font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-align: center; text-decoration: none; }
587
  .product-button i { margin-right: 5px; }
588
  .clear-cart { background-color: #6c757d; }
589
  .clear-cart:hover { background-color: #5a6268; }
590
- .formulate-order-button { background-color: #0070D1; }
591
- .formulate-order-button:hover { background-color: #005CBF; }
592
- .notification { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); background-color: #0070D1; color: white; padding: 10px 20px; border-radius: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); z-index: 1002; opacity: 0; transition: opacity 0.5s ease; font-size: 0.9rem;}
593
  .notification.show { opacity: 1;}
594
  #chatModal .modal-content { max-width: 450px; }
595
  #chat-messages { height: 350px; overflow-y: auto; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; margin-bottom: 15px; display: flex; flex-direction: column; gap: 10px; background-color: #fcfcfc; }
596
  .chat-message { padding: 10px 15px; border-radius: 15px; max-width: 80%; word-wrap: break-word; line-height: 1.4;}
597
- .chat-message.user { align-self: flex-end; background-color: #0070D1; color: white; border-bottom-right-radius: 2px; }
598
- .chat-message.ai { align-self: flex-start; background-color: #e6e6e6; color: #333; border-bottom-left-radius: 2px; }
599
  .chat-input-container { display: flex; gap: 10px; }
600
  #chat-input { flex-grow: 1; padding: 10px 15px; border: 1px solid #e0e0e0; border-radius: 20px; font-size: 0.95rem; outline: none; }
601
- #chat-input:focus { border-color: #0070D1; box-shadow: 0 0 0 2px rgba(0, 112, 209, 0.15); }
602
- #chat-send-button { background-color: #0070D1; color: white; border: none; border-radius: 20px; width: 40px; height: 40px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: background-color 0.3s; }
603
- #chat-send-button:hover { background-color: #005CBF; }
604
  #chat-send-button:disabled { background-color: #cccccc; cursor: not-allowed; }
605
 
606
- .chat-product-card {
607
- background-color: #f0f2f5;
608
- border-radius: 12px;
609
- padding: 10px;
610
- margin-top: 8px;
611
- display: flex;
612
- align-items: center;
613
- gap: 12px;
614
- border: 1px solid #e0e0e0;
615
- }
616
- .chat-product-card img {
617
- width: 50px;
618
- height: 50px;
619
- object-fit: cover;
620
- border-radius: 8px;
621
- flex-shrink: 0;
622
- }
623
- .chat-product-card-info {
624
- flex-grow: 1;
625
- }
626
- .chat-product-card-info strong {
627
- display: block;
628
- font-size: 0.9rem;
629
- color: #333;
630
- margin-bottom: 2px;
631
- }
632
- .chat-product-card-info span {
633
- font-size: 0.85rem;
634
- color: #0A2A66;
635
- font-weight: 500;
636
- }
637
- .chat-product-card-actions {
638
- display: flex;
639
- flex-direction: column;
640
- gap: 5px;
641
- }
642
- .chat-product-link, .chat-add-to-cart {
643
- display: inline-block;
644
- background-color: #E3F2FD;
645
- color: #0070D1;
646
- padding: 5px 10px;
647
- border-radius: 15px;
648
- cursor: pointer;
649
- font-size: 0.85rem;
650
- text-decoration: none;
651
- transition: background-color 0.2s;
652
- font-weight: 500;
653
- text-align: center;
654
- box-sizing: border-box;
655
- width: 100%;
656
- }
657
- .chat-product-link:hover, .chat-add-to-cart:hover {
658
- background-color: #BBDEFB;
659
- }
660
- .chat-product-card-actions .fa-cart-plus {
661
- font-size: 0.9em;
662
- }
663
 
664
  </style>
665
  </head>
666
  <body>
667
  <div class="container">
668
  <div class="top-bar">
 
 
 
669
  <div class="search-wrapper">
670
  <i class="fas fa-search"></i>
671
- <input type="text" id="search-input" placeholder="Search products">
672
  </div>
673
- <a href="#" class="top-bar-icon" onclick="openChatModal()">
674
- <i class="fas fa-comment-dots"></i>
675
- </a>
676
  </div>
677
 
678
  <div id="catalog-content">
@@ -701,7 +662,7 @@ CATALOG_TEMPLATE = '''
701
  alt="{{ product.name }}"
702
  loading="lazy">
703
  {% else %}
704
- <img src="https://via.placeholder.com/170x170.png?text=O%26CO" alt="No Image" loading="lazy">
705
  {% endif %}
706
  </div>
707
  <div class="product-info-overlay">
@@ -768,7 +729,7 @@ CATALOG_TEMPLATE = '''
768
  <input type="text" id="chat-input" placeholder="Напишите сообщение...">
769
  <button id="chat-send-button"><i class="fas fa-paper-plane"></i></button>
770
  </div>
771
- <button id="clear-chat-button" class="product-button" style="background-color: #dc3545; margin-top: 15px;"><i class="fas fa-trash"></i> Очистить чат</button>
772
  </div>
773
  </div>
774
 
@@ -841,7 +802,7 @@ CATALOG_TEMPLATE = '''
841
  })
842
  .catch(error => {
843
  console.error('Ошибка загрузки деталей продукта:', error);
844
- modalContent.innerHTML = `<p style="color: #dc3545; text-align:center; padding: 40px;">Не удалось загрузить информацию о товаре. ${error.message}</p>`;
845
  });
846
  }
847
 
@@ -968,10 +929,15 @@ CATALOG_TEMPLATE = '''
968
  <img src="${photoUrl}" alt="${item.name}">
969
  <div class="cart-item-details">
970
  <strong>${item.name}${colorText}</strong>
971
- <p class="cart-item-price">${item.price.toFixed(2)} ${currencyCode} × ${item.quantity}</p>
972
  </div>
973
- <span class="cart-item-total">${itemTotal.toFixed(2)} ${currencyCode}</span>
974
- <button class="cart-item-remove" onclick="removeFromCart('${item.id}')" title="Удалить товар">×</button>
 
 
 
 
 
975
  </div>
976
  `;
977
  }).join('');
@@ -984,6 +950,29 @@ CATALOG_TEMPLATE = '''
984
  }
985
  }
986
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
987
  function removeFromCart(itemId) {
988
  cart = cart.filter(item => item.id !== itemId);
989
  localStorage.setItem('mekaCart', JSON.stringify(cart));
@@ -1129,7 +1118,7 @@ CATALOG_TEMPLATE = '''
1129
  if (product) {
1130
  const photoUrl = product.photos && product.photos.length > 0
1131
  ? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${product.photos[0]}`
1132
- : 'https://via.placeholder.com/50x50.png?text=O%26CO';
1133
 
1134
  const card = document.createElement('div');
1135
  card.className = 'chat-product-card';
@@ -1249,7 +1238,7 @@ CATALOG_TEMPLATE = '''
1249
 
1250
  PRODUCT_DETAIL_TEMPLATE = '''
1251
  <div style="padding: 10px;">
1252
- <h2 style="font-size: 1.6rem; font-weight: 600; margin-bottom: 15px; text-align: center; color: #0A2A66;">{{ product['name'] }}</h2>
1253
  <div class="swiper-container" style="max-width: 450px; margin: 0 auto 20px; border-radius: 10px; overflow: hidden; background-color: #fff; border: 1px solid #e0e0e0;">
1254
  <div class="swiper-wrapper">
1255
  {% if product.get('photos') and product['photos']|length > 0 %}
@@ -1270,13 +1259,13 @@ PRODUCT_DETAIL_TEMPLATE = '''
1270
  </div>
1271
  {% if product.get('photos') and product['photos']|length > 1 %}
1272
  <div class="swiper-pagination" style="position: relative; bottom: 5px;"></div>
1273
- <div class="swiper-button-next" style="color: #0A2A66;"></div>
1274
- <div class="swiper-button-prev" style="color: #0A2A66;"></div>
1275
  {% endif %}
1276
  </div>
1277
 
1278
  <div style="text-align:center; margin-top:20px; padding: 0 10px;">
1279
- <p style="font-size: 1.5rem; font-weight: bold; color: #0A2A66; margin-bottom: 15px;"><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
1280
  <button class="product-button formulate-order-button" style="padding: 12px 30px; width: 100%; max-width: 300px;" onclick="closeModal('productModal'); openQuantityModalById('{{ product.get('product_id', '') }}')">
1281
  <i class="fas fa-cart-plus"></i> В корзину
1282
  </button>
@@ -1299,33 +1288,43 @@ ORDER_TEMPLATE = '''
1299
  <head>
1300
  <meta charset="UTF-8">
1301
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1302
- <title>Заказ №{{ order.id }} - O&CO</title>
1303
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
1304
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
1305
  <style>
1306
- body { font-family: 'Poppins', sans-serif; background: #f4f6f9; color: #333; line-height: 1.6; padding: 20px; }
 
 
 
 
 
 
 
1307
  .container { max-width: 800px; margin: 20px auto; padding: 30px; background: #fff; border-radius: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); border: 1px solid #e0e0e0; }
1308
- h1 { text-align: center; color: #0A2A66; margin-bottom: 25px; font-size: 1.8rem; font-weight: 600; }
1309
- h2 { color: #004282; margin-top: 30px; margin-bottom: 15px; font-size: 1.4rem; border-bottom: 1px solid #e0e0e0; padding-bottom: 8px;}
1310
  .order-meta { font-size: 0.9rem; color: #999; margin-bottom: 20px; text-align: center; }
1311
- .order-item { display: grid; grid-template-columns: 60px 1fr auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #f0f0f0; }
1312
  .order-item:last-child { border-bottom: none; }
1313
- .order-item img { width: 60px; height: 60px; object-fit: contain; border-radius: 8px; background-color: #fff; padding: 5px; border: 1px solid #e0e0e0;}
1314
- .item-details strong { display: block; margin-bottom: 5px; font-size: 1.05rem; color: #333;}
1315
  .item-details span { font-size: 0.9rem; color: #666; display: block;}
1316
- .item-total { font-weight: bold; text-align: right; font-size: 1rem; color: #0A2A66;}
1317
- .order-summary { margin-top: 30px; padding-top: 20px; border-top: 2px solid #0070D1; text-align: right; }
 
 
 
1318
  .order-summary p { margin-bottom: 10px; font-size: 1.1rem; }
1319
- .order-summary strong { font-size: 1.3rem; color: #0A2A66; }
1320
  .customer-info { margin-top: 30px; background-color: #f9f9f9; padding: 20px; border-radius: 8px; border: 1px solid #e0e0e0;}
1321
  .customer-info p { margin-bottom: 8px; font-size: 0.95rem; }
1322
- .customer-info strong { color: #004282; }
1323
  .actions { margin-top: 30px; text-align: center; }
1324
- .button { padding: 12px 25px; border: none; border-radius: 8px; background-color: #0070D1; color: white; font-weight: 600; cursor: pointer; transition: background-color 0.3s ease, transform 0.1s ease; font-size: 1rem; display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
1325
- .button:hover { background-color: #005CBF; }
1326
  .button:active { transform: scale(0.98); }
1327
  .button i { font-size: 1.2rem; }
1328
- .catalog-link { display: block; text-align: center; margin-top: 25px; color: #0070D1; text-decoration: none; font-size: 0.9rem; }
1329
  .catalog-link:hover { text-decoration: underline; }
1330
  .not-found { text-align: center; color: #dc3545; font-size: 1.2rem; padding: 40px 0;}
1331
  </style>
@@ -1333,33 +1332,19 @@ ORDER_TEMPLATE = '''
1333
  <body>
1334
  <div class="container">
1335
  {% if order %}
1336
- <h1><i class="fas fa-receipt"></i> Ваш Заказ {{ order.id }}</h1>
1337
  <p class="order-meta">Дата создания: {{ order.created_at }}</p>
1338
 
1339
  <h2><i class="fas fa-shopping-bag"></i> Товары в заказе</h2>
1340
- <div id="orderItems">
1341
- {% for item in order.cart %}
1342
- <div class="order-item">
1343
- <img src="{{ item.photo_url }}" alt="{{ item.name }}">
1344
- <div class="item-details">
1345
- <strong>{{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %}</strong>
1346
- <span>{{ "%.2f"|format(item.price) }} {{ currency_code }} × {{ item.quantity }}</span>
1347
- </div>
1348
- <div class="item-total">
1349
- {{ "%.2f"|format(item.price * item.quantity) }} {{ currency_code }}
1350
- </div>
1351
- </div>
1352
- {% endfor %}
1353
- </div>
1354
 
1355
  <div class="order-summary">
1356
- <p>Общая сумма товаров: <strong>{{ "%.2f"|format(order.total_price) }} {{ currency_code }}</strong></p>
1357
- <p><strong>ИТОГО К ОПЛАТЕ: {{ "%.2f"|format(order.total_price) }} {{ currency_code }}</strong></p>
1358
  </div>
1359
 
1360
  <div class="customer-info">
1361
  <h2><i class="fas fa-info-circle"></i> Статус заказа</h2>
1362
- <p>Этот заказ был оформлен без входа в систему.</p>
1363
  <p>Пожалуйста, свяжитесь с нами по WhatsApp для подтверждения и уточнения деталей.</p>
1364
  </div>
1365
 
@@ -1370,21 +1355,92 @@ ORDER_TEMPLATE = '''
1370
  <a href="{{ url_for('catalog') }}" class="catalog-link">← Вернуться в каталог</a>
1371
 
1372
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1373
  function sendOrderViaWhatsApp() {
1374
- const orderId = '{{ order.id }}';
1375
- const orderUrl = `{{ request.url }}`;
 
 
 
1376
  const whatsappNumber = "{{ whatsapp_number }}";
1377
-
1378
- let message = `Здравствуйте! Хочу подтвердить свой заказ на OCO:%0A%0A`;
1379
- message += `*Номер заказа:* ${orderId}%0A`;
1380
- message += `*Ссылка на заказ:* ${encodeURIComponent(orderUrl)}%0A%0A`;
1381
- message += `Пожалуйста, свяжитесь со мной для уточнения деталей оплаты и доставки.`;
 
 
 
 
 
 
1382
 
1383
  const whatsappUrl = `https://api.whatsapp.com/send?phone=${whatsappNumber}&text=${message}`;
1384
  window.open(whatsappUrl, '_blank');
1385
  }
 
 
1386
  </script>
1387
-
1388
  {% else %}
1389
  <h1 style="color: #dc3545;"><i class="fas fa-exclamation-triangle"></i> Ошибка</h1>
1390
  <p class="not-found">Заказ с таким ID не найден.</p>
@@ -1401,60 +1457,68 @@ ADMIN_TEMPLATE = '''
1401
  <head>
1402
  <meta charset="UTF-8">
1403
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1404
- <title>Админ-панель - O&CO</title>
1405
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
1406
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
1407
  <style>
1408
- body { font-family: 'Poppins', sans-serif; background-color: #f4f6f9; color: #333; padding: 20px; line-height: 1.6; }
 
 
 
 
 
 
 
 
 
 
1409
  .container { max-width: 1200px; margin: 0 auto; background-color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.05); }
1410
  .header { padding-bottom: 15px; margin-bottom: 25px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;}
1411
- .header .logo-title-container img { height: 50px; width: 50px; border-radius: 50%; object-fit: cover; border: 2px solid #3F51B5;}
1412
- h1, h2, h3 { font-weight: 600; color: #3F51B5; margin-bottom: 15px; }
1413
  h1 { font-size: 1.8rem; }
1414
  h2 { font-size: 1.5rem; margin-top: 30px; display: flex; align-items: center; gap: 8px; }
1415
- h3 { font-size: 1.2rem; color: #303F9F; margin-top: 20px; }
1416
  .section { margin-bottom: 30px; padding: 20px; background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; }
1417
  form { margin-bottom: 20px; }
1418
  label { font-weight: 500; margin-top: 10px; display: block; color: #666; font-size: 0.9rem;}
1419
  input[type="text"], input[type="number"], input[type="password"], input[type="tel"], textarea, select { width: 100%; padding: 10px 12px; margin-top: 5px; border: 1px solid #e0e0e0; border-radius: 6px; font-size: 0.95rem; box-sizing: border-box; transition: border-color 0.3s ease; background-color: #fff; }
1420
- input:focus, textarea:focus, select:focus { border-color: #3F51B5; outline: none; box-shadow: 0 0 0 2px rgba(63, 81, 181, 0.1); }
1421
  textarea { min-height: 80px; resize: vertical; }
1422
  input[type="file"] { padding: 8px; background-color: #ffffff; cursor: pointer; border: 1px solid #e0e0e0;}
1423
  input[type="file"]::file-selector-button { padding: 5px 10px; border-radius: 4px; background-color: #f0f0f0; border: 1px solid #e0e0e0; cursor: pointer; margin-right: 10px;}
1424
  input[type="checkbox"] { margin-right: 5px; vertical-align: middle; }
1425
  label.inline-label { display: inline-block; margin-top: 10px; font-weight: normal; }
1426
- button, .button { padding: 10px 18px; border: none; border-radius: 6px; background-color: #7986CB; color: white; font-weight: 500; cursor: pointer; transition: background-color 0.3s ease, transform 0.1s ease; margin-top: 15px; font-size: 0.95rem; display: inline-flex; align-items: center; gap: 5px; text-decoration: none; line-height: 1.2;}
1427
- button:hover, .button:hover { background-color: #3F51B5; }
1428
  button:active, .button:active { transform: scale(0.98); }
1429
  button[type="submit"] { min-width: 120px; justify-content: center; }
1430
- .delete-button { background-color: #dc3545; }
1431
- .delete-button:hover { background-color: #c82333; }
1432
- .add-button { background-color: #3F51B5; }
1433
- .add-button:hover { background-color: #303F9F; }
1434
  .item-list { display: grid; gap: 20px; }
1435
  .item { background: #fff; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.03); border: 1px solid #f0f0f0; }
1436
  .item p { margin: 5px 0; font-size: 0.9rem; color: #666; }
1437
- .item strong { color: #333; }
1438
  .item .description { font-size: 0.85rem; color: #999; max-height: 60px; overflow: hidden; text-overflow: ellipsis; }
1439
  .item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
1440
- .item-actions button:not(.delete-button) { background-color: #7986CB; }
1441
- .item-actions button:not(.delete-button):hover { background-color: #3F51B5; }
1442
- .edit-form-container { margin-top: 15px; padding: 20px; background: #edf2ff; border: 1px dashed #e0e0e0; border-radius: 6px; display: none; }
1443
  details { background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px; }
1444
- details > summary { cursor: pointer; font-weight: 600; color: #303F9F; display: block; padding: 15px; border-bottom: 1px solid #e0e0e0; list-style: none; position: relative; }
1445
- details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; color: #3F51B5; }
1446
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
1447
  details[open] > summary { border-bottom: 1px solid #e0e0e0; }
1448
  details .form-content { padding: 20px; }
1449
  .color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
1450
  .color-input-group input { flex-grow: 1; margin: 0; }
1451
- .remove-color-btn { background-color: #dc3545; padding: 6px 10px; font-size: 0.8rem; margin-top: 0; line-height: 1; }
1452
- .remove-color-btn:hover { background-color: #c82333; }
1453
- .add-color-btn { background-color: #C5CAE9; color: #303F9F; border: 1px solid #e0e0e0; }
1454
- .add-color-btn:hover { background-color: #3F51B5; color: white; border-color: #3F51B5; }
1455
  .photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; border: 1px solid #e0e0e0; object-fit: cover;}
1456
  .sync-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
1457
- .download-hf-button { background-color: #6c757d; }
1458
  .download-hf-button:hover { background-color: #5a6268; }
1459
  .flex-container { display: flex; flex-wrap: wrap; gap: 20px; }
1460
  .flex-item { flex: 1; min-width: 350px; }
@@ -1465,8 +1529,8 @@ ADMIN_TEMPLATE = '''
1465
  .status-indicator { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 0.8rem; font-weight: 500; margin-left: 10px; vertical-align: middle; }
1466
  .status-indicator.in-stock { background-color: #d4edda; color: #155724; }
1467
  .status-indicator.out-of-stock { background-color: #f8d7da; color: #721c24; }
1468
- .status-indicator.top-product { background-color: #fff3cd; color: #856404; margin-left: 5px;}
1469
- .ai-generate-button { background-color: #8D6EC8; margin-top: 5px; margin-bottom: 10px; }
1470
  .ai-generate-button:hover { background-color: #7B4DB5; }
1471
  </style>
1472
  </head>
@@ -1474,10 +1538,10 @@ ADMIN_TEMPLATE = '''
1474
  <div class="container">
1475
  <div class="header">
1476
  <div class="logo-title-container" style="display: flex; align-items: center; gap: 15px;">
1477
- <img src="https://huggingface.co/spaces/oco-kerben/admin/resolve/main/IMG_20250706_113258_228.jpg" alt="O&CO Logo" style="height: 50px; width: 50px; border-radius: 50%; object-fit: cover; border: 2px solid #3F51B5;">
1478
- <h1><i class="fas fa-tools"></i> Админ-панель O&CO</h1>
1479
  </div>
1480
- <a href="{{ url_for('catalog') }}" class="button" style="background-color: #3F51B5;"><i class="fas fa-store"></i> Перейти в каталог</a>
1481
  </div>
1482
 
1483
  {% with messages = get_flashed_messages(with_categories=true) %}
@@ -1492,7 +1556,7 @@ ADMIN_TEMPLATE = '''
1492
  <h2><i class="fas fa-sync-alt"></i> Синхронизация с Датацентром</h2>
1493
  <div class="sync-buttons">
1494
  <form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно загрузить локальные данные на сервер? Это перезапишет данные на сервере.');">
1495
- <button type="submit" class="button" title="Загрузить локальные файлы на Hugging Face"><i class="fas fa-upload"></i> Загрузить БД</button>
1496
  </form>
1497
  <form method="POST" action="{{ url_for('force_download') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно скачать данные с сервера? Это перезапишет ваши локальные файлы.');">
1498
  <button type="submit" class="button download-hf-button" title="Скачать файлы (перезапишет локальные)"><i class="fas fa-download"></i> Скачать БД</button>
@@ -1553,7 +1617,7 @@ ADMIN_TEMPLATE = '''
1553
  <textarea id="returns" name="returns" rows="4">{{ organization_info.get('returns', '') }}</textarea>
1554
  <label for="contact">Контактная информация (для ИИ-ассистента):</label>
1555
  <textarea id="contact" name="contact" rows="4">{{ organization_info.get('contact', '') }}</textarea>
1556
- <button type="submit" class="add-button"><i class="fas fa-save"></i> Сохранить информацию о магазине</button>
1557
  </form>
1558
  <p style="font-size: 0.85rem; color: #999;">Эта информация будет использоваться ИИ-ассистентом для ответов на вопросы о вашем магазине.</p>
1559
  </div>
@@ -1599,7 +1663,7 @@ ADMIN_TEMPLATE = '''
1599
  <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
1600
  </div>
1601
  </div>
1602
- <button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('add-color-inputs')"><i class="fas fa-palette"></i> Добавить поле для цвета/варианта</button>
1603
  <br>
1604
  <div style="margin-top: 15px;">
1605
  <input type="checkbox" id="add_in_stock" name="in_stock" checked>
@@ -1631,7 +1695,7 @@ ADMIN_TEMPLATE = '''
1631
  {% endif %}
1632
  </div>
1633
  <div style="flex-grow: 1;">
1634
- <h3 style="margin-top: 0; margin-bottom: 5px; color: #333;">
1635
  {{ product['name'] }}
1636
  {% if product.get('in_stock', True) %}
1637
  <span class="status-indicator in-stock">В наличии</span>
@@ -1646,7 +1710,7 @@ ADMIN_TEMPLATE = '''
1646
  <p><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
1647
  <p class="description" title="{{ product.get('description', '') }}"><strong>Описание:</strong> {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}</p>
1648
  {% set colors = product.get('colors', []) %}
1649
- <p><strong>Цвета/Вар-ts:</strong> {{ colors|select('ne', '')|join(', ') if colors|select('ne', '')|list|length > 0 else 'Нет' }}</p>
1650
  {% if product.get('photos') and product['photos']|length > 1 %}
1651
  <p style="font-size: 0.8rem; color: #999;">(Всего фото: {{ product['photos']|length }})</p>
1652
  {% endif %}
@@ -1683,7 +1747,7 @@ ADMIN_TEMPLATE = '''
1683
  {% endif %}
1684
  <label>Описание:</label>
1685
  <textarea id="edit_description_{{ loop.index0 }}" name="description" rows="4">{{ product.get('description', '') }}</textarea>
1686
- <button type="button" class="button ai-generate-button" onclick="generateDescription('edit_photos_{{ loop.index0 }}', 'edit_description_{{ loop.index0 }}', 'edit_gen_lang_{{ loop.index0 }}')"><i class="fas fa-magic"></i> Сгенерировать описание</button>
1687
  <label for="edit_gen_lang_{{ loop.index0 }}">Язык генерации:</label>
1688
  <select id="edit_gen_lang_{{ loop.index0 }}" name="gen_lang" style="width: auto; display: inline-block; margin-left: 10px;">
1689
  <option value="Русский">Русский</option>
@@ -1717,7 +1781,7 @@ ADMIN_TEMPLATE = '''
1717
  </div>
1718
  {% endif %}
1719
  </div>
1720
- <button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')"><i class="fas fa-palette"></i> Добавить поле ��ля цвета</button>
1721
  <br>
1722
  <div style="margin-top: 15px;">
1723
  <input type="checkbox" id="edit_in_stock_{{ loop.index0 }}" name="in_stock" {% if product.get('in_stock', True) %}checked{% endif %}>
@@ -1728,7 +1792,7 @@ ADMIN_TEMPLATE = '''
1728
  <label for="edit_is_top_{{ loop.index0 }}" class="inline-label">Топ товар</label>
1729
  </div>
1730
  <br>
1731
- <button type="submit" class="add-button" style="margin-top: 20px;"><i class="fas fa-save"></i> Сохранить изменения</button>
1732
  </form>
1733
  </div>
1734
  </div>
 
22
  load_dotenv()
23
 
24
  app = Flask(__name__)
25
+ app.secret_key = 'your_unique_secret_key_gippo_312_shop_54321_no_login'
26
  DATA_FILE = 'data.json'
27
 
28
  SYNC_FILES = [DATA_FILE]
 
129
 
130
  def load_data():
131
  default_organization_info = {
132
+ "about_us": "Мы — Gippo312, ваш надежный партнер в мире уникальных товаров. Мы предлагаем широкий ассортимент продукции, от электроники до товаров для дома, всегда стремясь к качеству и доступности. Наша миссия — сделать ваш шопинг приятным и удобным, предлагая только лучшие товары, тщательно отобранные для вас.",
133
  "shipping": "Доставка осуществляется по всему Кыргызстану. Стоимость и сроки доставки зависят от региона и веса товара. По Бишкеку доставка возможна в течение 1-2 рабочих дней, в регионы — от 3 до 7 дней. Для уточнения деталей свяжитесь с нами.",
134
  "returns": "Возврат и обмен товара возможен в течение 14 дней с момента покупки, при условии сохранения товарного вида, упаковки и чека. Некоторые категории товаров могут иметь особые условия возврата. Пожалуйста, свяжитесь с нами для оформления возврата или обмена.",
135
  "contact": f"Наш магазин находится по адресу: {STORE_ADDRESS}. Связаться с нами можно по телефону: {WHATSAPP_NUMBER} или через WhatsApp по этому же номеру. Мы работаем ежедневно с 9:00 до 18:00."
 
305
 
306
 
307
  system_instruction_content = (
308
+ "Ты - доброжелательный и очень полезный виртуальный консультант для магазина Gippo312. "
309
  "Твоя задача - помогать пользователям находить товары, отвечать на вопросы о них, предлагать варианты, а также предоставлять информацию о магазине. "
310
  "Всегда будь вежлив, информативен и стремись решить проблему пользователя. "
311
  "Никогда не выдумывай товары или категории, которых нет в предоставленных списках. "
 
375
  <head>
376
  <meta charset="UTF-8">
377
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
378
+ <title>Gippo312 - Каталог</title>
379
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
380
+ <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
381
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
382
  <style>
383
+ :root {
384
+ --bg-dark: #003C43;
385
+ --bg-medium: #135D66;
386
+ --accent: #48D1CC;
387
+ --accent-hover: #77E4D8;
388
+ --text-light: #E3FEF7;
389
+ --text-dark: #333;
390
+ --danger: #E57373;
391
+ --danger-hover: #EF5350;
392
+ }
393
  * { margin: 0; padding: 0; box-sizing: border-box; }
394
  html { -webkit-tap-highlight-color: transparent; }
395
  body {
396
+ font-family: 'Montserrat', sans-serif;
397
+ background-color: var(--bg-dark);
398
+ color: var(--text-light);
399
  line-height: 1.6;
400
  }
401
  .container {
 
410
  gap: 15px;
411
  position: sticky;
412
  top: 0;
413
+ background-color: var(--bg-dark);
414
  z-index: 999;
415
+ border-bottom: 1px solid var(--bg-medium);
416
+ }
417
+ .logo {
418
+ flex-shrink: 0;
419
+ }
420
+ .logo img {
421
+ width: 45px;
422
+ height: 45px;
423
+ border-radius: 50%;
424
+ border: 2px solid var(--accent);
425
  }
426
  .search-wrapper {
427
  flex-grow: 1;
 
434
  border: none;
435
  border-radius: 25px;
436
  outline: none;
437
+ background-color: var(--bg-medium);
438
+ color: var(--text-light);
439
  transition: all 0.3s ease;
440
  }
441
+ #search-input::placeholder { color: rgba(227, 254, 247, 0.6); }
442
  .search-wrapper .fa-search {
443
  position: absolute;
444
  top: 50%;
445
  left: 18px;
446
  transform: translateY(-50%);
447
+ color: rgba(227, 254, 247, 0.6);
448
  font-size: 1rem;
449
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  .category-section {
451
  margin-top: 20px;
452
  }
 
455
  justify-content: space-between;
456
  align-items: center;
457
  padding: 0 20px;
458
+ margin-bottom: 15px;
459
  }
460
  .category-header h2 {
461
  font-size: 1.5rem;
 
463
  }
464
  .category-header .view-all-arrow {
465
  font-size: 1.8rem;
466
+ color: var(--accent);
467
  text-decoration: none;
468
  font-weight: 300;
469
  }
 
484
  flex-shrink: 0;
485
  overflow: hidden;
486
  cursor: pointer;
487
+ transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
488
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
489
  position: relative;
490
  }
491
+ .product-card:hover {
492
+ transform: translateY(-5px);
493
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
494
+ }
495
  .product-card:active { transform: scale(0.96); }
496
  .product-image-container {
497
  width: 100%;
 
507
  bottom: 0;
508
  left: 0;
509
  right: 0;
510
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 0%, transparent 100%);
511
  padding: 20px 12px 10px;
 
 
512
  color: #fff;
513
  pointer-events: none;
514
  }
 
520
  position: absolute;
521
  top: 8px;
522
  right: 8px;
523
+ background-color: var(--accent);
524
+ color: var(--bg-dark);
525
  padding: 2px 8px;
526
  font-size: 0.7rem;
527
  border-radius: 10px;
 
539
  z-index: 1000;
540
  }
541
  .floating-button {
542
+ background-color: var(--accent);
543
+ color: var(--bg-dark);
544
  border: none;
545
  border-radius: 50%;
546
  width: 55px;
 
550
  display: flex;
551
  align-items: center;
552
  justify-content: center;
553
+ box-shadow: 0 4px 15px rgba(72, 209, 204, 0.4);
554
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
555
  }
556
  .floating-button:hover {
557
+ background-color: var(--accent-hover);
558
  transform: translateY(-3px);
 
559
  }
560
  #cart-button { position: relative; }
561
  #cart-count {
562
  position: absolute;
563
  top: -2px;
564
  right: -2px;
565
+ background-color: var(--danger);
566
  color: white;
567
  border-radius: 50%;
568
  padding: 2px 6px;
569
  font-size: 0.7rem;
570
  font-weight: bold;
571
+ border: 2px solid var(--accent);
572
  }
573
  .modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); backdrop-filter: blur(5px); overflow-y: auto; }
574
+ .modal-content { background: #ffffff; color: var(--text-dark); margin: 5% auto; padding: 25px; border-radius: 15px; width: 90%; max-width: 700px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); animation: slideIn 0.3s ease-out; position: relative; }
575
  @keyframes slideIn { from { transform: translateY(-30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
576
  .close { position: absolute; top: 15px; right: 15px; font-size: 1.8rem; color: #aaa; cursor: pointer; transition: color 0.3s; line-height: 1; }
577
  .close:hover { color: #666; }
578
+ .modal-content h2 { margin-top: 0; margin-bottom: 20px; color: var(--bg-medium); display: flex; align-items: center; gap: 10px;}
579
+ .cart-item { display: grid; grid-template-columns: 60px 1fr auto auto auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #e0e0e0; }
580
  .cart-item:last-child { border-bottom: none; }
581
+ .cart-item img { width: 60px; height: 60px; object-fit: cover; border-radius: 8px; }
582
  .cart-item-details { grid-column: 2; }
583
+ .cart-item-details strong { display: block; margin-bottom: 5px; font-size: 1rem; color: var(--text-dark);}
584
  .cart-item-price { font-size: 0.9rem; color: #666; }
585
+ .cart-item-quantity { display: flex; align-items: center; gap: 8px; grid-column: 3;}
586
+ .quantity-btn { background-color: #eee; border: 1px solid #ddd; border-radius: 50%; width: 28px; height: 28px; cursor: pointer; font-size: 1.1rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
587
+ .cart-item-total { font-weight: bold; text-align: right; grid-column: 4; font-size: 1rem; color: var(--bg-medium);}
588
+ .cart-item-remove { grid-column: 5; background:none; border:none; color: var(--danger); cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
589
+ .cart-item-remove:hover { color: var(--danger-hover); }
590
  .quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; }
591
+ .quantity-input:focus, .color-select:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 2px rgba(72, 209, 204, 0.2); }
592
  .cart-summary { margin-top: 20px; text-align: right; border-top: 1px solid #e0e0e0; padding-top: 15px; }
593
+ .cart-summary strong { font-size: 1.2rem; color: var(--bg-medium);}
594
  .cart-actions { margin-top: 25px; display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
595
  .product-button { display: block; width: auto; flex-grow: 1; padding: 10px; border: none; border-radius: 8px; color: white; font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-align: center; text-decoration: none; }
596
  .product-button i { margin-right: 5px; }
597
  .clear-cart { background-color: #6c757d; }
598
  .clear-cart:hover { background-color: #5a6268; }
599
+ .formulate-order-button { background-color: var(--accent); color: var(--bg-dark); }
600
+ .formulate-order-button:hover { background-color: var(--accent-hover); }
601
+ .notification { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); background-color: var(--accent); color: var(--bg-dark); padding: 10px 20px; border-radius: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); z-index: 1002; opacity: 0; transition: opacity 0.5s ease; font-size: 0.9rem;}
602
  .notification.show { opacity: 1;}
603
  #chatModal .modal-content { max-width: 450px; }
604
  #chat-messages { height: 350px; overflow-y: auto; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; margin-bottom: 15px; display: flex; flex-direction: column; gap: 10px; background-color: #fcfcfc; }
605
  .chat-message { padding: 10px 15px; border-radius: 15px; max-width: 80%; word-wrap: break-word; line-height: 1.4;}
606
+ .chat-message.user { align-self: flex-end; background-color: var(--bg-medium); color: white; border-bottom-right-radius: 2px; }
607
+ .chat-message.ai { align-self: flex-start; background-color: #e6e6e6; color: var(--text-dark); border-bottom-left-radius: 2px; }
608
  .chat-input-container { display: flex; gap: 10px; }
609
  #chat-input { flex-grow: 1; padding: 10px 15px; border: 1px solid #e0e0e0; border-radius: 20px; font-size: 0.95rem; outline: none; }
610
+ #chat-input:focus { border-color: var(--bg-medium); box-shadow: 0 0 0 2px rgba(19, 93, 102, 0.15); }
611
+ #chat-send-button { background-color: var(--bg-medium); color: white; border: none; border-radius: 50%; width: 40px; height: 40px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: background-color 0.3s; flex-shrink: 0; }
612
+ #chat-send-button:hover { background-color: var(--bg-dark); }
613
  #chat-send-button:disabled { background-color: #cccccc; cursor: not-allowed; }
614
 
615
+ .chat-product-card { background-color: #f0f2f5; border-radius: 12px; padding: 10px; margin-top: 8px; display: flex; align-items: center; gap: 12px; border: 1px solid #e0e0e0; }
616
+ .chat-product-card img { width: 50px; height: 50px; object-fit: cover; border-radius: 8px; flex-shrink: 0; }
617
+ .chat-product-card-info { flex-grow: 1; }
618
+ .chat-product-card-info strong { display: block; font-size: 0.9rem; color: var(--text-dark); margin-bottom: 2px; }
619
+ .chat-product-card-info span { font-size: 0.85rem; color: var(--bg-medium); font-weight: 500; }
620
+ .chat-product-card-actions { display: flex; flex-direction: column; gap: 5px; }
621
+ .chat-product-link, .chat-add-to-cart { display: inline-block; background-color: #E0F2F1; color: var(--bg-medium); padding: 5px 10px; border-radius: 15px; cursor: pointer; font-size: 0.85rem; text-decoration: none; transition: background-color 0.2s; font-weight: 500; text-align: center; width: 100%; }
622
+ .chat-product-link:hover, .chat-add-to-cart:hover { background-color: #B2DFDB; }
623
+ .chat-product-card-actions .fa-cart-plus { font-size: 0.9em; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
 
625
  </style>
626
  </head>
627
  <body>
628
  <div class="container">
629
  <div class="top-bar">
630
+ <a href="/" class="logo">
631
+ <img src="https://huggingface.co/spaces/gippo312/admin/resolve/main/Picsart_25-11-04_12-02-21-390.png" alt="Gippo312 Logo">
632
+ </a>
633
  <div class="search-wrapper">
634
  <i class="fas fa-search"></i>
635
+ <input type="text" id="search-input" placeholder="Искать товары">
636
  </div>
 
 
 
637
  </div>
638
 
639
  <div id="catalog-content">
 
662
  alt="{{ product.name }}"
663
  loading="lazy">
664
  {% else %}
665
+ <img src="https://via.placeholder.com/170x170.png?text=Gippo312" alt="No Image" loading="lazy">
666
  {% endif %}
667
  </div>
668
  <div class="product-info-overlay">
 
729
  <input type="text" id="chat-input" placeholder="Напишите сообщение...">
730
  <button id="chat-send-button"><i class="fas fa-paper-plane"></i></button>
731
  </div>
732
+ <button id="clear-chat-button" class="product-button" style="background-color: var(--danger); margin-top: 15px;"><i class="fas fa-trash"></i> Очистить чат</button>
733
  </div>
734
  </div>
735
 
 
802
  })
803
  .catch(error => {
804
  console.error('Ошибка загрузки деталей продукта:', error);
805
+ modalContent.innerHTML = `<p style="color: var(--danger); text-align:center; padding: 40px;">Не удалось загрузить информацию о товаре. ${error.message}</p>`;
806
  });
807
  }
808
 
 
929
  <img src="${photoUrl}" alt="${item.name}">
930
  <div class="cart-item-details">
931
  <strong>${item.name}${colorText}</strong>
932
+ <p class="cart-item-price">${item.price.toFixed(2)} ${currencyCode}</p>
933
  </div>
934
+ <div class="cart-item-quantity">
935
+ <button class="quantity-btn" onclick="decrementCartItem('${item.id}')">-</button>
936
+ <span>${item.quantity}</span>
937
+ <button class="quantity-btn" onclick="incrementCartItem('${item.id}')">+</button>
938
+ </div>
939
+ <span class="cart-item-total">${itemTotal.toFixed(2)}</span>
940
+ <button class="cart-item-remove" onclick="removeFromCart('${item.id}')" title="Удалить товар"><i class="fas fa-trash-alt"></i></button>
941
  </div>
942
  `;
943
  }).join('');
 
950
  }
951
  }
952
 
953
+ function incrementCartItem(itemId) {
954
+ const itemIndex = cart.findIndex(item => item.id === itemId);
955
+ if (itemIndex > -1) {
956
+ cart[itemIndex].quantity++;
957
+ localStorage.setItem('mekaCart', JSON.stringify(cart));
958
+ openCartModal();
959
+ updateCartButton();
960
+ }
961
+ }
962
+
963
+ function decrementCartItem(itemId) {
964
+ const itemIndex = cart.findIndex(item => item.id === itemId);
965
+ if (itemIndex > -1) {
966
+ cart[itemIndex].quantity--;
967
+ if (cart[itemIndex].quantity <= 0) {
968
+ cart.splice(itemIndex, 1);
969
+ }
970
+ localStorage.setItem('mekaCart', JSON.stringify(cart));
971
+ openCartModal();
972
+ updateCartButton();
973
+ }
974
+ }
975
+
976
  function removeFromCart(itemId) {
977
  cart = cart.filter(item => item.id !== itemId);
978
  localStorage.setItem('mekaCart', JSON.stringify(cart));
 
1118
  if (product) {
1119
  const photoUrl = product.photos && product.photos.length > 0
1120
  ? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${product.photos[0]}`
1121
+ : 'https://via.placeholder.com/50x50.png?text=Gippo312';
1122
 
1123
  const card = document.createElement('div');
1124
  card.className = 'chat-product-card';
 
1238
 
1239
  PRODUCT_DETAIL_TEMPLATE = '''
1240
  <div style="padding: 10px;">
1241
+ <h2 style="font-size: 1.6rem; font-weight: 600; margin-bottom: 15px; text-align: center; color: #135D66;">{{ product['name'] }}</h2>
1242
  <div class="swiper-container" style="max-width: 450px; margin: 0 auto 20px; border-radius: 10px; overflow: hidden; background-color: #fff; border: 1px solid #e0e0e0;">
1243
  <div class="swiper-wrapper">
1244
  {% if product.get('photos') and product['photos']|length > 0 %}
 
1259
  </div>
1260
  {% if product.get('photos') and product['photos']|length > 1 %}
1261
  <div class="swiper-pagination" style="position: relative; bottom: 5px;"></div>
1262
+ <div class="swiper-button-next" style="color: #135D66;"></div>
1263
+ <div class="swiper-button-prev" style="color: #135D66;"></div>
1264
  {% endif %}
1265
  </div>
1266
 
1267
  <div style="text-align:center; margin-top:20px; padding: 0 10px;">
1268
+ <p style="font-size: 1.5rem; font-weight: bold; color: #135D66; margin-bottom: 15px;"><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
1269
  <button class="product-button formulate-order-button" style="padding: 12px 30px; width: 100%; max-width: 300px;" onclick="closeModal('productModal'); openQuantityModalById('{{ product.get('product_id', '') }}')">
1270
  <i class="fas fa-cart-plus"></i> В корзину
1271
  </button>
 
1288
  <head>
1289
  <meta charset="UTF-8">
1290
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1291
+ <title>Заказ №{{ order.id }} - Gippo312</title>
1292
+ <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;600&display=swap" rel="stylesheet">
1293
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
1294
  <style>
1295
+ :root {
1296
+ --bg-light: #f4f6f9;
1297
+ --bg-medium: #135D66;
1298
+ --accent: #48D1CC;
1299
+ --text-dark: #333;
1300
+ --text-light: #E3FEF7;
1301
+ }
1302
+ body { font-family: 'Montserrat', sans-serif; background: var(--bg-light); color: var(--text-dark); line-height: 1.6; padding: 20px; }
1303
  .container { max-width: 800px; margin: 20px auto; padding: 30px; background: #fff; border-radius: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); border: 1px solid #e0e0e0; }
1304
+ h1 { text-align: center; color: var(--bg-medium); margin-bottom: 25px; font-size: 1.8rem; font-weight: 600; }
1305
+ h2 { color: var(--bg-medium); margin-top: 30px; margin-bottom: 15px; font-size: 1.4rem; border-bottom: 1px solid #e0e0e0; padding-bottom: 8px;}
1306
  .order-meta { font-size: 0.9rem; color: #999; margin-bottom: 20px; text-align: center; }
1307
+ .order-item { display: grid; grid-template-columns: 60px 1fr auto auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #f0f0f0; }
1308
  .order-item:last-child { border-bottom: none; }
1309
+ .order-item img { width: 60px; height: 60px; object-fit: cover; border-radius: 8px; }
1310
+ .item-details strong { display: block; margin-bottom: 5px; font-size: 1.05rem; color: var(--text-dark);}
1311
  .item-details span { font-size: 0.9rem; color: #666; display: block;}
1312
+ .item-quantity { display: flex; align-items: center; gap: 8px; }
1313
+ .quantity-btn { background-color: #eee; border: 1px solid #ddd; border-radius: 50%; width: 28px; height: 28px; cursor: pointer; font-size: 1.1rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
1314
+ .quantity-btn:hover { background-color: #e0e0e0; }
1315
+ .item-total { font-weight: bold; text-align: right; font-size: 1rem; color: var(--bg-medium);}
1316
+ .order-summary { margin-top: 30px; padding-top: 20px; border-top: 2px solid var(--accent); text-align: right; }
1317
  .order-summary p { margin-bottom: 10px; font-size: 1.1rem; }
1318
+ .order-summary strong { font-size: 1.3rem; color: var(--bg-medium); }
1319
  .customer-info { margin-top: 30px; background-color: #f9f9f9; padding: 20px; border-radius: 8px; border: 1px solid #e0e0e0;}
1320
  .customer-info p { margin-bottom: 8px; font-size: 0.95rem; }
1321
+ .customer-info strong { color: var(--bg-medium); }
1322
  .actions { margin-top: 30px; text-align: center; }
1323
+ .button { padding: 12px 25px; border: none; border-radius: 8px; background-color: var(--accent); color: var(--text-dark); font-weight: 600; cursor: pointer; transition: background-color 0.3s ease, transform 0.1s ease; font-size: 1rem; display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
1324
+ .button:hover { background-color: #77E4D8; }
1325
  .button:active { transform: scale(0.98); }
1326
  .button i { font-size: 1.2rem; }
1327
+ .catalog-link { display: block; text-align: center; margin-top: 25px; color: var(--bg-medium); text-decoration: none; font-size: 0.9rem; }
1328
  .catalog-link:hover { text-decoration: underline; }
1329
  .not-found { text-align: center; color: #dc3545; font-size: 1.2rem; padding: 40px 0;}
1330
  </style>
 
1332
  <body>
1333
  <div class="container">
1334
  {% if order %}
1335
+ <h1><i class="fas fa-receipt"></i> Ваш Заказ №<span id="orderId">{{ order.id }}</span></h1>
1336
  <p class="order-meta">Дата создания: {{ order.created_at }}</p>
1337
 
1338
  <h2><i class="fas fa-shopping-bag"></i> Товары в заказе</h2>
1339
+ <div id="orderItems"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
1340
 
1341
  <div class="order-summary">
1342
+ <p><strong>ИТОГО К ОПЛАТЕ: <span id="orderTotal">{{ "%.2f"|format(order.total_price) }}</span> {{ currency_code }}</strong></p>
 
1343
  </div>
1344
 
1345
  <div class="customer-info">
1346
  <h2><i class="fas fa-info-circle"></i> Статус заказа</h2>
1347
+ <p>Этот заказ был оформлен без входа в систему. Вы можете скорректировать его и отправить в WhatsApp.</p>
1348
  <p>Пожалуйста, свяжитесь с нами по WhatsApp для подтверждения и уточнения деталей.</p>
1349
  </div>
1350
 
 
1355
  <a href="{{ url_for('catalog') }}" class="catalog-link">← Вернуться в каталог</a>
1356
 
1357
  <script>
1358
+ let order = {{ order|tojson|safe }};
1359
+
1360
+ function renderOrderItems() {
1361
+ const container = document.getElementById('orderItems');
1362
+ if (!container) return;
1363
+
1364
+ if (order.cart.length === 0) {
1365
+ container.innerHTML = '<p style="text-align:center; padding: 20px;">Заказ пуст.</p>';
1366
+ document.querySelector('.actions .button').disabled = true;
1367
+ } else {
1368
+ container.innerHTML = order.cart.map((item, index) => `
1369
+ <div class="order-item">
1370
+ <img src="${item.photo_url}" alt="${item.name}">
1371
+ <div class="item-details">
1372
+ <strong>${item.name} ${item.color !== 'N/A' ? `(${item.color})` : ''}</strong>
1373
+ <span>${item.price.toFixed(2)} {{ currency_code }}</span>
1374
+ </div>
1375
+ <div class="item-quantity">
1376
+ <button class="quantity-btn" onclick="decrementItem(${index})">-</button>
1377
+ <span>${item.quantity}</span>
1378
+ <button class="quantity-btn" onclick="incrementItem(${index})">+</button>
1379
+ </div>
1380
+ <div class="item-total">
1381
+ ${(item.price * item.quantity).toFixed(2)} {{ currency_code }}
1382
+ </div>
1383
+ </div>
1384
+ `).join('');
1385
+ document.querySelector('.actions .button').disabled = false;
1386
+ }
1387
+ updateOrderTotal();
1388
+ }
1389
+
1390
+ function updateOrderTotal() {
1391
+ const totalElement = document.getElementById('orderTotal');
1392
+ if (!totalElement) return;
1393
+ const total = order.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
1394
+ order.total_price = total;
1395
+ totalElement.textContent = total.toFixed(2);
1396
+ }
1397
+
1398
+ function incrementItem(index) {
1399
+ if (order.cart[index]) {
1400
+ order.cart[index].quantity++;
1401
+ renderOrderItems();
1402
+ }
1403
+ }
1404
+
1405
+ function decrementItem(index) {
1406
+ if (order.cart[index]) {
1407
+ order.cart[index].quantity--;
1408
+ if (order.cart[index].quantity <= 0) {
1409
+ if (confirm(`Удалить "${order.cart[index].name}" из заказа?`)) {
1410
+ order.cart.splice(index, 1);
1411
+ } else {
1412
+ order.cart[index].quantity = 1;
1413
+ }
1414
+ }
1415
+ renderOrderItems();
1416
+ }
1417
+ }
1418
+
1419
  function sendOrderViaWhatsApp() {
1420
+ if (order.cart.length === 0) {
1421
+ alert("Нельзя отправить пустой заказ.");
1422
+ return;
1423
+ }
1424
+ const orderId = document.getElementById('orderId').textContent;
1425
  const whatsappNumber = "{{ whatsapp_number }}";
1426
+ let message = `Здравствуйте! Хочу подтвердить или изменить свой заказ на Gippo312:%0A%0A`;
1427
+ message += `*Номер заказа:* ${orderId}%0A%0A`;
1428
+
1429
+ order.cart.forEach(item => {
1430
+ message += `*${item.name}* ${item.color !== 'N/A' ? `(${item.color})` : ''}%0A`;
1431
+ message += ` - Количество: ${item.quantity}%0A`;
1432
+ message += ` - Цена: ${item.price.toFixed(2)} {{ currency_code }}%0A`;
1433
+ });
1434
+
1435
+ message += `%0A*Итоговая сумма:* ${order.total_price.toFixed(2)} {{ currency_code }}%0A%0A`;
1436
+ message += `Пожалуйста, свяжитесь со мной для уточнения деталей.`;
1437
 
1438
  const whatsappUrl = `https://api.whatsapp.com/send?phone=${whatsappNumber}&text=${message}`;
1439
  window.open(whatsappUrl, '_blank');
1440
  }
1441
+
1442
+ document.addEventListener('DOMContentLoaded', renderOrderItems);
1443
  </script>
 
1444
  {% else %}
1445
  <h1 style="color: #dc3545;"><i class="fas fa-exclamation-triangle"></i> Ошибка</h1>
1446
  <p class="not-found">Заказ с таким ID не найден.</p>
 
1457
  <head>
1458
  <meta charset="UTF-8">
1459
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1460
+ <title>Админ-панель - Gippo312</title>
1461
+ <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap" rel="stylesheet">
1462
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
1463
  <style>
1464
+ :root {
1465
+ --bg-light: #f4f6f9;
1466
+ --bg-medium: #135D66;
1467
+ --accent: #48D1CC;
1468
+ --accent-hover: #77E4D8;
1469
+ --text-dark: #333;
1470
+ --text-on-accent: #003C43;
1471
+ --danger: #E57373;
1472
+ --danger-hover: #EF5350;
1473
+ }
1474
+ body { font-family: 'Montserrat', sans-serif; background-color: var(--bg-light); color: var(--text-dark); padding: 20px; line-height: 1.6; }
1475
  .container { max-width: 1200px; margin: 0 auto; background-color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.05); }
1476
  .header { padding-bottom: 15px; margin-bottom: 25px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;}
1477
+ .header .logo-title-container img { height: 50px; width: 50px; border-radius: 50%; object-fit: cover; border: 2px solid var(--bg-medium);}
1478
+ h1, h2, h3 { font-weight: 600; color: var(--bg-medium); margin-bottom: 15px; }
1479
  h1 { font-size: 1.8rem; }
1480
  h2 { font-size: 1.5rem; margin-top: 30px; display: flex; align-items: center; gap: 8px; }
1481
+ h3 { font-size: 1.2rem; color: #004D40; margin-top: 20px; }
1482
  .section { margin-bottom: 30px; padding: 20px; background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; }
1483
  form { margin-bottom: 20px; }
1484
  label { font-weight: 500; margin-top: 10px; display: block; color: #666; font-size: 0.9rem;}
1485
  input[type="text"], input[type="number"], input[type="password"], input[type="tel"], textarea, select { width: 100%; padding: 10px 12px; margin-top: 5px; border: 1px solid #e0e0e0; border-radius: 6px; font-size: 0.95rem; box-sizing: border-box; transition: border-color 0.3s ease; background-color: #fff; }
1486
+ input:focus, textarea:focus, select:focus { border-color: var(--bg-medium); outline: none; box-shadow: 0 0 0 2px rgba(19, 93, 102, 0.1); }
1487
  textarea { min-height: 80px; resize: vertical; }
1488
  input[type="file"] { padding: 8px; background-color: #ffffff; cursor: pointer; border: 1px solid #e0e0e0;}
1489
  input[type="file"]::file-selector-button { padding: 5px 10px; border-radius: 4px; background-color: #f0f0f0; border: 1px solid #e0e0e0; cursor: pointer; margin-right: 10px;}
1490
  input[type="checkbox"] { margin-right: 5px; vertical-align: middle; }
1491
  label.inline-label { display: inline-block; margin-top: 10px; font-weight: normal; }
1492
+ button, .button { padding: 10px 18px; border: none; border-radius: 6px; background-color: var(--accent); color: var(--text-on-accent); font-weight: 600; cursor: pointer; transition: background-color 0.3s ease, transform 0.1s ease; margin-top: 15px; font-size: 0.95rem; display: inline-flex; align-items: center; gap: 5px; text-decoration: none; line-height: 1.2;}
1493
+ button:hover, .button:hover { background-color: var(--accent-hover); }
1494
  button:active, .button:active { transform: scale(0.98); }
1495
  button[type="submit"] { min-width: 120px; justify-content: center; }
1496
+ .delete-button { background-color: var(--danger); color: white; }
1497
+ .delete-button:hover { background-color: var(--danger-hover); }
1498
+ .add-button { background-color: var(--bg-medium); color: white; }
1499
+ .add-button:hover { background-color: #003C43; }
1500
  .item-list { display: grid; gap: 20px; }
1501
  .item { background: #fff; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.03); border: 1px solid #f0f0f0; }
1502
  .item p { margin: 5px 0; font-size: 0.9rem; color: #666; }
1503
+ .item strong { color: var(--text-dark); }
1504
  .item .description { font-size: 0.85rem; color: #999; max-height: 60px; overflow: hidden; text-overflow: ellipsis; }
1505
  .item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
1506
+ .edit-form-container { margin-top: 15px; padding: 20px; background: #E0F2F1; border: 1px dashed #B2DFDB; border-radius: 6px; display: none; }
 
 
1507
  details { background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px; }
1508
+ details > summary { cursor: pointer; font-weight: 600; color: var(--bg-medium); display: block; padding: 15px; border-bottom: 1px solid #e0e0e0; list-style: none; position: relative; }
1509
+ details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; color: var(--bg-medium); }
1510
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
1511
  details[open] > summary { border-bottom: 1px solid #e0e0e0; }
1512
  details .form-content { padding: 20px; }
1513
  .color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
1514
  .color-input-group input { flex-grow: 1; margin: 0; }
1515
+ .remove-color-btn { background-color: var(--danger); color: white; padding: 6px 10px; font-size: 0.8rem; margin-top: 0; line-height: 1; }
1516
+ .remove-color-btn:hover { background-color: var(--danger-hover); }
1517
+ .add-color-btn { background-color: #B2DFDB; color: var(--bg-medium); border: 1px solid #e0e0e0; }
1518
+ .add-color-btn:hover { background-color: var(--bg-medium); color: white; border-color: var(--bg-medium); }
1519
  .photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; border: 1px solid #e0e0e0; object-fit: cover;}
1520
  .sync-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
1521
+ .download-hf-button { background-color: #6c757d; color: white; }
1522
  .download-hf-button:hover { background-color: #5a6268; }
1523
  .flex-container { display: flex; flex-wrap: wrap; gap: 20px; }
1524
  .flex-item { flex: 1; min-width: 350px; }
 
1529
  .status-indicator { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 0.8rem; font-weight: 500; margin-left: 10px; vertical-align: middle; }
1530
  .status-indicator.in-stock { background-color: #d4edda; color: #155724; }
1531
  .status-indicator.out-of-stock { background-color: #f8d7da; color: #721c24; }
1532
+ .status-indicator.top-product { background-color: #FFF9C4; color: #F57F17; margin-left: 5px;}
1533
+ .ai-generate-button { background-color: #8D6EC8; color: white; margin-top: 5px; margin-bottom: 10px; }
1534
  .ai-generate-button:hover { background-color: #7B4DB5; }
1535
  </style>
1536
  </head>
 
1538
  <div class="container">
1539
  <div class="header">
1540
  <div class="logo-title-container" style="display: flex; align-items: center; gap: 15px;">
1541
+ <img src="https://huggingface.co/spaces/gippo312/admin/resolve/main/Picsart_25-11-04_12-02-21-390.png" alt="Gippo312 Logo">
1542
+ <h1><i class="fas fa-tools"></i> Админ-панель Gippo312</h1>
1543
  </div>
1544
+ <a href="{{ url_for('catalog') }}" class="button" style="background-color: var(--bg-medium); color: white;"><i class="fas fa-store"></i> Перейти в каталог</a>
1545
  </div>
1546
 
1547
  {% with messages = get_flashed_messages(with_categories=true) %}
 
1556
  <h2><i class="fas fa-sync-alt"></i> Синхронизация с Датацентром</h2>
1557
  <div class="sync-buttons">
1558
  <form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно загрузить локальные данные на сервер? Это перезапишет данные на сервере.');">
1559
+ <button type="submit" class="button add-button" title="Загрузить локальные файлы на Hugging Face"><i class="fas fa-upload"></i> Загрузить БД</button>
1560
  </form>
1561
  <form method="POST" action="{{ url_for('force_download') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно скачать данные с сервера? Это перезапишет ваши локальные файлы.');">
1562
  <button type="submit" class="button download-hf-button" title="Скачать файлы (перезапишет локальные)"><i class="fas fa-download"></i> Скачать БД</button>
 
1617
  <textarea id="returns" name="returns" rows="4">{{ organization_info.get('returns', '') }}</textarea>
1618
  <label for="contact">Контактная информация (для ИИ-ассистента):</label>
1619
  <textarea id="contact" name="contact" rows="4">{{ organization_info.get('contact', '') }}</textarea>
1620
+ <button type="submit" class="add-button"><i class="fas fa-save"></i> Сохранить</button>
1621
  </form>
1622
  <p style="font-size: 0.85rem; color: #999;">Эта информация будет использоваться ИИ-ассистентом для ответов на вопросы о вашем магазине.</p>
1623
  </div>
 
1663
  <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
1664
  </div>
1665
  </div>
1666
+ <button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('add-color-inputs')"><i class="fas fa-palette"></i> Добавить поле</button>
1667
  <br>
1668
  <div style="margin-top: 15px;">
1669
  <input type="checkbox" id="add_in_stock" name="in_stock" checked>
 
1695
  {% endif %}
1696
  </div>
1697
  <div style="flex-grow: 1;">
1698
+ <h3 style="margin-top: 0; margin-bottom: 5px; color: var(--text-dark);">
1699
  {{ product['name'] }}
1700
  {% if product.get('in_stock', True) %}
1701
  <span class="status-indicator in-stock">В наличии</span>
 
1710
  <p><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
1711
  <p class="description" title="{{ product.get('description', '') }}"><strong>Описание:</strong> {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}</p>
1712
  {% set colors = product.get('colors', []) %}
1713
+ <p><strong>Цвета/Вар-ты:</strong> {{ colors|select('ne', '')|join(', ') if colors|select('ne', '')|list|length > 0 else 'Нет' }}</p>
1714
  {% if product.get('photos') and product['photos']|length > 1 %}
1715
  <p style="font-size: 0.8rem; color: #999;">(Всего фото: {{ product['photos']|length }})</p>
1716
  {% endif %}
 
1747
  {% endif %}
1748
  <label>Описание:</label>
1749
  <textarea id="edit_description_{{ loop.index0 }}" name="description" rows="4">{{ product.get('description', '') }}</textarea>
1750
+ <button type="button" class="button ai-generate-button" onclick="generateDescription('edit_photos_{{ loop.index0 }}', 'edit_description_{{ loop.index0 }}', 'edit_gen_lang_{{ loop.index0 }}')"><i class="fas fa-magic"></i> Сгенерировать</button>
1751
  <label for="edit_gen_lang_{{ loop.index0 }}">Язык генерации:</label>
1752
  <select id="edit_gen_lang_{{ loop.index0 }}" name="gen_lang" style="width: auto; display: inline-block; margin-left: 10px;">
1753
  <option value="Русский">Русский</option>
 
1781
  </div>
1782
  {% endif %}
1783
  </div>
1784
+ <button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')"><i class="fas fa-palette"></i> Добавить поле</button>
1785
  <br>
1786
  <div style="margin-top: 15px;">
1787
  <input type="checkbox" id="edit_in_stock_{{ loop.index0 }}" name="in_stock" {% if product.get('in_stock', True) %}checked{% endif %}>
 
1792
  <label for="edit_is_top_{{ loop.index0 }}" class="inline-label">Топ товар</label>
1793
  </div>
1794
  <br>
1795
+ <button type="submit" class="add-button" style="margin-top: 20px;"><i class="fas fa-save"></i> Сохранить</button>
1796
  </form>
1797
  </div>
1798
  </div>