Kgshop commited on
Commit
04e2bd4
·
verified ·
1 Parent(s): 26922f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +351 -346
app.py CHANGED
@@ -144,155 +144,212 @@ LANDING_TEMPLATE = '''
144
  <title>ОсОО "Раина" - Вентиляция и Кондиционирование</title>
145
  <meta name="description" content="Профессиональные услуги по проектированию, монтажу и обслуживанию систем вентиляции и кондиционирования в Кыргызстане. 15 лет опыта, более 1000 проектов.">
146
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
147
- <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
 
 
148
  <style>
149
  :root {
150
- --dark-bg: #0a0a10;
151
- --card-bg: #1a1e28;
152
- --primary-color: #6a0dad;
153
- --secondary-color: #a955ff;
154
- --text-color: #e0e0e0;
155
- --text-muted: #b0b0c0;
156
- --accent-glow: rgba(106, 13, 173, 0.4);
157
- --card-border: #2a3040;
 
 
158
  }
159
  * { margin: 0; padding: 0; box-sizing: border-box; scroll-behavior: smooth; }
160
- body { font-family: 'Montserrat', sans-serif; background-color: var(--dark-bg); color: var(--text-color); line-height: 1.7; font-size: 16px; }
 
 
 
 
 
 
 
 
161
  .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; }
162
- section { padding: clamp(4rem, 10vw, 6rem) 0; overflow: hidden; }
163
- h1, h2, h3 { font-weight: 700; color: #fff; line-height: 1.3; font-family: 'Poppins', sans-serif;}
164
- h1 { font-size: clamp(2.5rem, 7vw, 4.5rem); }
165
- h2 { font-size: clamp(2rem, 6vw, 3.5rem); text-align: center; margin-bottom: 70px; position: relative; font-family: 'Poppins', sans-serif;}
166
- h2::after { content: ''; display: block; width: 100px; height: 5px; background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); margin: 20px auto 0; border-radius: 3px; }
167
- h3 { font-size: clamp(1.3rem, 3.5vw, 1.8rem); color: var(--primary-color); margin-bottom: 15px; font-family: 'Poppins', sans-serif;}
168
- p { margin-bottom: 1.2rem; color: var(--text-muted); }
169
- .btn { display: inline-block; padding: 14px 30px; background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); color: #fff; border-radius: 60px; text-decoration: none; font-weight: 600; font-size: 1.1rem; transition: all 0.4s ease; box-shadow: 0 6px 20px var(--accent-glow); border: none; cursor: pointer;}
170
- .btn:hover { transform: translateY(-4px) scale(1.05); box-shadow: 0 10px 30px var(--accent-glow); }
171
- .header { position: fixed; top: 0; left: 0; width: 100%; z-index: 1000; padding: 20px 0; background-color: rgba(10, 10, 16, 0.9); backdrop-filter: blur(12px); transition: all 0.3s ease; }
172
- .header.scrolled { padding: 15px 0; box-shadow: 0 4px 15px rgba(0,0,0,0.4); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  .navbar { display: flex; justify-content: space-between; align-items: center; }
174
- .logo { font-size: clamp(1.7rem, 4.5vw, 2.2rem); font-weight: 700; color: #fff; text-decoration: none; letter-spacing: 1px; }
175
  .nav-links { display: flex; gap: 35px; list-style: none; }
176
- .nav-links a { color: var(--text-color); text-decoration: none; font-weight: 600; transition: color 0.3s ease; position: relative; }
177
- .nav-links a::after { content: ''; position: absolute; bottom: -5px; left: 0; width: 0; height: 2px; background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); transition: width 0.3s; }
178
- .nav-links a:hover { color: #fff; }
 
 
 
 
179
  .nav-links a:hover::after { width: 100%; }
180
- .menu-toggle { display: none; font-size: 1.8rem; cursor: pointer; border: none; background: none; color: white; z-index: 1001; }
181
- #hero { min-height: 100vh; display: flex; align-items: center; background-image: linear-gradient(rgba(10, 10, 16, 0.8), rgba(10, 10, 16, 1)), url(https://images.unsplash.com/photo-1558221639-2c7158995165?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80); background-size: cover; background-position: center; }
182
- .hero-content { text-align: center; max-width: 850px; margin: 0 auto; animation: fadeIn 1.5s ease-out; }
183
- .hero-content p { font-size: clamp(1.1rem, 3vw, 1.3rem); margin: 40px 0; max-width: 700px; margin-left: auto; margin-right: auto; opacity: 0.9;}
184
- .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 70px; align-items: center; }
185
- .about-img { width: 100%; border-radius: 18px; box-shadow: 0 15px 40px rgba(0,0,0,0.5); transition: transform 0.3s ease; }
186
- .about-img:hover { transform: scale(1.02) translateY(-5px); }
187
- .services-grid, .equipment-grid, .projects-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 40px; }
188
- .service-card, .turnkey-card { background-color: var(--card-bg); padding: 35px; border-radius: 18px; border: 1px solid var(--card-border); transition: all 0.4s ease; }
189
- .service-card:hover, .turnkey-card:hover { transform: translateY(-8px); border-color: var(--primary-color); box-shadow: 0 12px 35px var(--accent-glow); }
190
- .service-card i { font-size: 3rem; color: var(--primary-color); margin-bottom: 25px; transition: transform 0.3s ease;}
191
- .service-card:hover i { transform: scale(1.1); }
192
- .turnkey-card { padding: 0; display: flex; flex-direction: column; }
193
- .turnkey-img { width: 100%; height: 220px; object-fit: cover; border-radius: 18px 18px 0 0; }
194
- .turnkey-content { padding: 35px; flex-grow: 1;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  .equipment-filters { display: flex; justify-content: center; flex-wrap: wrap; gap: 15px; margin-bottom: 50px; }
196
- .filter-btn { padding: 10px 22px; border: 2px solid var(--primary-color); background-color: transparent; color: var(--primary-color); border-radius: 30px; cursor: pointer; transition: all 0.3s; font-weight: 600; font-size: 1rem;}
197
- .filter-btn.active, .filter-btn:hover { background-color: var(--primary-color); color: #fff; box-shadow: 0 4px 15px var(--accent-glow);}
198
- .equipment-card { background-color: var(--card-bg); border-radius: 18px; overflow: hidden; text-align: center; padding: 25px; border: 1px solid var(--card-border); transition: all 0.4s ease; cursor: pointer; display: flex; flex-direction: column; justify-content: space-between;}
199
- .equipment-card:hover { transform: translateY(-8px); border-color: var(--primary-color); box-shadow: 0 12px 35px var(--accent-glow); }
200
- .equipment-card img { width: 100%; height: 180px; object-fit: contain; margin-bottom: 20px; transition: transform 0.3s ease;}
 
 
 
 
 
201
  .equipment-card:hover img { transform: scale(1.05); }
202
- .equipment-card h3 { font-size: 1.3rem; margin-bottom: 10px; }
203
- .equipment-card .price { font-size: 1.4rem; font-weight: 700; color: #fff; margin: 10px 0 20px;}
204
- .equipment-card .btn { padding: 10px 20px; font-size: 0.95rem; background: linear-gradient(90deg, #3498db, #2980b9); box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3); }
205
- .equipment-card .btn:hover { background: linear-gradient(90deg, #2980b9, #3498db); }
206
- .project-card { position: relative; border-radius: 18px; overflow: hidden; min-height: 350px; cursor: pointer; display: flex; align-items: flex-end; }
207
- .project-card img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.4s ease, filter 0.4s ease; }
208
- .project-overlay { position: absolute; bottom: 0; left: 0; right: 0; background: linear-gradient(to top, rgba(10, 10, 16, 0.9) 20%, rgba(10, 10, 16, 0) 70%); padding: 50px 25px 25px; z-index: 1; transition: padding 0.3s ease; }
209
- .project-card:hover .project-overlay { padding-bottom: 40px; }
210
- .project-card h3 { margin-bottom: 8px; font-size: 1.5rem; color: #fff; }
211
- .project-card p { margin-bottom: 0; transition: opacity 0.4s ease, max-height 0.4s ease; opacity: 0; max-height: 0; color: var(--text-muted); font-size: 0.95rem;}
212
- .project-card:hover img { transform: scale(1.03); filter: brightness(0.7); }
213
- .project-card:hover p { opacity: 1; max-height: 150px; }
214
- #contact { background: linear-gradient(rgba(26, 30, 40, 0.9), rgba(26, 30, 40, 0.9)), url('https://images.unsplash.com/photo-1592911677971-2684320e94e1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80'); background-size: cover; background-position: center; background-attachment: fixed;}
 
 
 
 
 
 
 
 
 
 
 
 
215
  .contact-content { text-align: center; }
216
  .contact-info { margin-top: 40px; display: flex; flex-direction: column; align-items: center; gap: 25px; }
217
- .contact-info p { font-size: 1.25rem; margin-bottom: 0; }
218
- .contact-info a { color: var(--primary-color); text-decoration: none; font-weight: 600; }
219
- .footer { text-align: center; padding: 40px 0; background-color: #07070d; color: var(--text-muted); font-size: 0.95rem; }
 
220
  @media (max-width: 992px) {
221
- .grid-2 { grid-template-columns: 1fr; text-align: center; gap: 50px;}
222
- .about-img { margin-bottom: 30px; max-width: 500px; margin-left: auto; margin-right: auto;}
223
- h1 { font-size: clamp(2rem, 6vw, 3.5rem); }
224
- h2 { margin-bottom: 50px; }
225
  }
226
  @media (max-width: 768px) {
227
- .nav-links { position: fixed; top: 0; right: -100%; width: min(80vw, 350px); height: 100vh; background-color: var(--card-bg); backdrop-filter: blur(15px); flex-direction: column; justify-content: center; align-items: center; transition: right 0.4s ease-in-out; box-shadow: -5px 0 25px rgba(0,0,0,0.3); padding-top: 60px; }
 
 
 
 
 
 
228
  .nav-links.active { right: 0; }
229
  .menu-toggle { display: block; z-index: 1001; }
230
- .navbar { padding-right: 15px; }
231
- .hero-content p { font-size: clamp(1rem, 2.5vw, 1.2rem); margin: 30px 0; }
232
- .btn { padding: 12px 25px; font-size: 1rem; }
233
- .service-card, .turnkey-card { padding: 30px; }
234
- .turnkey-img { height: 180px; }
235
  }
236
- @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
237
-
238
  .modal {
239
- display: none;
240
- position: fixed;
241
- z-index: 1001;
242
- left: 0; top: 0; width: 100%; height: 100%;
243
- overflow: auto;
244
- background-color: rgba(0,0,0,0.85);
245
  padding-top: 60px;
246
- backdrop-filter: blur(5px);
247
  }
248
  .modal-content {
249
- position: relative;
250
- margin: 5% auto;
251
- padding: 30px;
252
- width: 90%;
253
- max-width: 800px;
254
- background-color: var(--card-bg);
255
- border-radius: 18px;
256
- text-align: center;
257
- box-shadow: 0 15px 50px rgba(0,0,0,0.5);
258
- }
259
- .modal-content img {
260
- max-width: 100%;
261
- max-height: 70vh;
262
- border-radius: 12px;
263
- margin-bottom: 20px;
264
- border: 2px solid var(--primary-color);
265
  }
266
- .modal-content h3 { margin-bottom: 12px; font-size: 1.8rem; }
267
- .modal-content p { color: var(--text-muted); font-size: 1.15rem; }
268
- .modal-content .price { font-size: 1.7rem; color: #fff; font-weight: 700; margin-top: 15px;}
269
  .close-button {
270
- position: absolute;
271
- top: 20px;
272
- right: 30px;
273
- font-size: 2.8rem;
274
- font-weight: bold;
275
- color: #aaa;
276
- cursor: pointer;
277
- background: none;
278
- border: none;
279
- transition: color 0.3s ease;
280
  }
281
- .close-button:hover, .close-button:focus { color: var(--primary-color); }
282
  .carousel-nav { margin-top: 20px; }
283
  .carousel-nav button {
284
- background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
285
- color: white;
286
- border: none;
287
- padding: 12px 18px;
288
- border-radius: 50px;
289
- margin: 0 8px;
290
- cursor: pointer;
291
- transition: all 0.3s ease;
292
- font-size: 1.3rem;
293
- box-shadow: 0 5px 15px var(--accent-glow);
294
  }
295
- .carousel-nav button:hover { background: linear-gradient(90deg, var(--secondary-color), var(--primary-color)); transform: scale(1.1); box-shadow: 0 8px 25px var(--accent-glow); }
 
 
 
 
 
296
  </style>
297
  </head>
298
  <body>
@@ -313,67 +370,67 @@ LANDING_TEMPLATE = '''
313
 
314
  <section id="hero">
315
  <div class="container hero-content">
316
- <h1>Ваш Климат - Наша Забота</h1>
317
- <p>Проектирование, монтаж и сервис систем вентиляции и кондиционирования от ОсОО "Раина". 15 лет опыта, более 1000 довольных клиентов.</p>
318
- <a href="#contact" class="btn">Заказать консультацию</a>
319
  </div>
320
  </section>
321
 
322
  <section id="about">
323
- <div class="container">
324
  <h2>О Нашей Компании</h2>
325
  <div class="grid-2">
326
  <img src="https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80" alt="Команда Раина" class="about-img">
327
  <div>
328
- <h3>Экспертиза и Инновации</h3>
329
- <p 2009 года ОсОО "Раина" является лидером в сфере климатических решений в Кыргызстане. Мы сочетаем многолетний опыт с передовыми технологиями для создания идеального микроклимата.</p>
330
- <h3>Миссия и Ценности</h3>
331
- <p>Наша миссия — обеспечивать максимальный комфорт и безопасность для наших клиентов, предлагая энергоэффективные и надежные системы, отвечающие самым высоким стандартам качества.</p>
332
- <h3>Квалифицированная Команда</h3>
333
- <p>Наша команда это опытные инженеры, монтажники и консультанты, готовые решить задачи любой сложности, от бытовых систем до комплексных промышленных решений.</p>
334
  </div>
335
  </div>
336
  </div>
337
  </section>
338
 
339
  <section id="services">
340
- <div class="container">
341
- <h2>Наши Ключевые Услуги</h2>
342
  <div class="services-grid">
343
- <div class="service-card"><i class="fas fa-chart-line"></i><h3>Проектирование</h3><p>Разработка детализированных проектов систем вентиляции и кондиционирования с учетом всех ваших требований и норм.</p></div>
344
- <div class="service-card"><i class="fas fa-cogs"></i><h3>Монтаж</h3><p>Профессиональная установка климатического оборудования любой сложности с гарантией качества и долговечности.</p></div>
345
- <div class="service-card"><i class="fas fa-wrench"></i><h3>Сервисное Обслуживание</h3><p>Регулярное техническое обслуживание и оперативный ремонт для бесперебойной работы ваших систем 24/7.</p></div>
346
- <div class="service-card"><i class="fas fa-redo-alt"></i><h3>Модернизация и Автоматизация</h3><p>Повышение эффективности существующих систем, внедрение современных решений для оптимизации энергопотребления и управления.</p></div>
347
  </div>
348
  </div>
349
  </section>
350
 
351
- <section id="turnkey" style="background-color: var(--card-bg);">
352
- <div class="container">
353
- <h2>Компл��ксные Решения "Под Ключ"</h2>
354
  {% if services %}
355
  <div class="services-grid">
356
  {% for service in services %}
357
- <div class="turnkey-card" onclick="showDetailsModal('service', {{ loop.index0 }})">
358
  {% if service.photo %}
359
  <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/{{ service.photo }}" alt="{{ service.title }}" class="turnkey-img">
360
  {% endif %}
361
  <div class="turnkey-content">
362
- <h3><i class="{{ service.icon }} fa-fw" style="margin-right: 8px;"></i>{{ service.title }}</h3>
363
  <p>{{ service.description }}</p>
364
  </div>
365
  </div>
366
  {% endfor %}
367
  </div>
368
  {% else %}
369
- <p style="text-align: center;">Наши комплексные решения скоро будут представлены здесь.</p>
370
  {% endif %}
371
  </div>
372
  </section>
373
 
374
  <section id="equipment">
375
- <div class="container">
376
- <h2>Качественное Оборудование</h2>
377
  {% if equipment %}
378
  <div class="equipment-filters">
379
  <button class="filter-btn active" data-filter="all">Все</button>
@@ -383,29 +440,27 @@ LANDING_TEMPLATE = '''
383
  </div>
384
  <div class="equipment-grid">
385
  {% for item in equipment %}
386
- <div class="equipment-card" data-category="{{ item.get('category', 'all') }}" onclick="showDetailsModal('equipment', {{ loop.index0 }})">
387
- <div>
388
- {% if item.photo %}
389
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/{{ item.photo }}" alt="{{ item.name }}">
390
- {% else %}
391
- <img src="https://via.placeholder.com/290x180.png?text=No+Image" alt="No Image">
392
- {% endif %}
393
- <h3>{{ item.name }}</h3>
394
- <p class="price">{{ "%.2f"|format(item.price) }} KGS</p>
395
- </div>
396
- <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: {{ item.name }}" target="_blank" class="btn">Запросить</a>
397
  </div>
398
  {% endfor %}
399
  </div>
400
  {% else %}
401
- <p style="text-align: center;">Наш каталог оборудования будет доступен в ближайшее время.</p>
402
  {% endif %}
403
  </div>
404
  </section>
405
 
406
  <section id="projects">
407
- <div class="container">
408
- <h2>Наши Реализованные Проекты</h2>
409
  {% if projects %}
410
  <div class="projects-grid">
411
  {% for project in projects %}
@@ -419,18 +474,18 @@ LANDING_TEMPLATE = '''
419
  {% endfor %}
420
  </div>
421
  {% else %}
422
- <p style="text-align: center;">Информация о наших проектах скоро появится на сайте.</p>
423
  {% endif %}
424
  </div>
425
  </section>
426
 
427
  <section id="contact">
428
- <div class="container contact-content">
429
- <h2>Свяжитесь с Нами</h2>
430
- <p>Мы готовы ответить на ваши вопросы и предложить оптимальное решение для вашего объекта.</p>
431
  <div class="contact-info">
432
- <p><strong>Телефон:</strong> <a href="tel:{{ contact_phone }}">{{ contact_phone }}</a></p>
433
- <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, я хотел(а) бы получить консультацию по вашим услугам." target="_blank" class="btn"><i class="fab fa-whatsapp"></i> Написать в WhatsApp</a>
434
  </div>
435
  <div style="margin-top: 40px; font-size: 0.9rem; color: var(--text-muted);">
436
  <p><strong>Реквизиты:</strong> ОсОО «Раина», ИНН: 00812202110194, ОКПО: 31290279</p>
@@ -460,15 +515,32 @@ LANDING_TEMPLATE = '''
460
  let allItems = [];
461
 
462
  function showDetailsModal(type, index) {
 
463
  const data = {{ data | tojson }};
464
  currentData = data;
465
  currentType = type;
466
- currentIndex = index;
467
-
468
  if (type === 'service') allItems = data.services;
469
- else if (type === 'equipment') allItems = data.equipment;
 
 
 
 
 
 
 
 
 
470
  else if (type === 'project') allItems = data.projects;
 
 
 
 
471
 
 
 
 
 
472
  updateModalContent();
473
  document.getElementById('detailsModal').style.display = 'block';
474
  document.body.style.overflow = 'hidden';
@@ -485,7 +557,7 @@ LANDING_TEMPLATE = '''
485
  if (currentType === 'service') {
486
  content = `
487
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/${item.photo}" alt="${item.title}">` : ''}
488
- <h3><i class="${item.icon} fa-fw" style="margin-right: 8px; color: var(--primary-color);"></i>${item.title}</h3>
489
  <p>${item.description}</p>
490
  `;
491
  } else if (currentType === 'equipment') {
@@ -493,7 +565,7 @@ LANDING_TEMPLATE = '''
493
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/${item.photo}" alt="${item.name}">` : ''}
494
  <h3>${item.name}</h3>
495
  <p><strong>Категория:</strong> ${item.category || 'Не указана'}</p>
496
- <p class="price">${item.price.toFixed(2)} KGS</p>
497
  <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: ${encodeURIComponent(item.name)}" target="_blank" class="btn" style="padding: 12px 28px; font-size: 1rem;">Запросить</a>
498
  `;
499
  } else if (currentType === 'project') {
@@ -567,6 +639,28 @@ LANDING_TEMPLATE = '''
567
  });
568
  });
569
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
  });
571
  </script>
572
  </body>
@@ -580,121 +674,75 @@ ADMIN_TEMPLATE = '''
580
  <meta charset="UTF-8">
581
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
582
  <title>Админ-панель - Раина</title>
583
- <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
584
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
585
  <style>
586
- :root {
587
- --admin-bg: #f4f7f9;
588
- --admin-card-bg: #ffffff;
589
- --primary-color: #6a0dad;
590
- --secondary-color: #a955ff;
591
- --text-color: #333;
592
- --text-muted: #555;
593
- --border-color: #e0e0e0;
594
- --button-bg: #9b59b6;
595
- --button-hover-bg: #8e44ad;
596
- --delete-button-bg: #e74c3c;
597
- --delete-button-hover-bg: #c0392b;
598
- }
599
- body { font-family: 'Poppins', sans-serif; background-color: var(--admin-bg); color: var(--text-color); padding: 20px; line-height: 1.6; }
600
- .container { max-width: 1200px; margin: 0 auto; background-color: var(--admin-card-bg); padding: 25px; border-radius: 12px; box-shadow: 0 5px 20px rgba(0,0,0,0.07); }
601
- .header { padding-bottom: 20px; margin-bottom: 30px; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px;}
602
- h1, h2, h3 { font-weight: 600; color: var(--primary-color); margin-bottom: 15px; font-family: 'Montserrat', sans-serif;}
603
- h1 { font-size: 2rem; color: var(--text-color); }
604
- h2 { font-size: 1.7rem; margin-top: 35px; display: flex; align-items: center; gap: 10px; border-bottom: 2px solid var(--primary-color); padding-bottom: 10px; }
605
- .section { margin-bottom: 40px; padding: 25px; background-color: #fdfdfd; border: 1px solid var(--border-color); border-radius: 10px; }
606
  form { margin-bottom: 20px; }
607
- label { font-weight: 500; margin-top: 12px; display: block; color: var(--text-muted); font-size: 0.95rem;}
608
- input[type="text"], input[type="number"], textarea, select { width: 100%; padding: 12px 15px; margin-top: 8px; border: 1px solid var(--border-color); border-radius: 8px; font-size: 1rem; box-sizing: border-box; transition: border-color 0.3s ease;}
609
- input[type="text"]:focus, input[type="number"]:focus, textarea:focus, select:focus { border-color: var(--secondary-color); outline: none; box-shadow: 0 0 0 2px rgba(169, 85, 255, 0.2); }
610
- input[type="file"] { padding: 10px; cursor: pointer; border: 1px solid var(--border-color); border-radius: 8px;}
611
- input[type="file"]:focus { border-color: var(--secondary-color); }
612
- button, .button { padding: 12px 20px; border: none; border-radius: 8px; background-color: var(--button-bg); color: white; font-weight: 600; cursor: pointer; transition: all 0.3s ease; margin-top: 15px; text-decoration: none; font-size: 1rem;}
613
- button:hover, .button:hover { background-color: var(--button-hover-bg); transform: translateY(-2px); box-shadow: 0 4px 10px rgba(142, 68, 173, 0.3); }
614
- .delete-button { background-color: var(--delete-button-bg); }
615
- .delete-button:hover { background-color: var(--delete-button-hover-bg); }
616
  .item-list { display: grid; gap: 20px; }
617
- .item { background: #fcfcfd; padding: 20px 25px; border-radius: 10px; border: 1px solid var(--border-color); box-shadow: 0 2px 8px rgba(0,0,0,0.04); display: flex; flex-direction: column; gap: 10px;}
618
- .item p { margin-bottom: 0;}
619
- .item-actions { margin-top: 15px; display: flex; gap: 15px; flex-wrap: wrap; align-items: center;}
620
- .item-actions button { padding: 8px 15px; font-size: 0.9rem; margin-top: 0;}
621
- .edit-form-container { margin-top: 15px; padding: 20px; background: #f9f8ff; border: 1px dashed #e0cfff; border-radius: 8px; display: none; }
622
- details { background-color: #fcfcfd; border: 1px solid var(--border-color); border-radius: 10px; margin-bottom: 20px; overflow: hidden; }
623
- details > summary { cursor: pointer; font-weight: 600; color: var(--primary-color); display: block; padding: 15px 20px; position: relative; list-style: none; background-color: #f4f2ff; transition: background-color 0.3s ease;}
624
- details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); color: var(--text-muted); transition: transform 0.3s ease;}
625
- details[open] > summary { background-color: #eedeff; }
626
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
627
- .photo-preview img { max-width: 80px; max-height: 80px; border-radius: 8px; margin: 5px 5px 0 0; object-fit: cover; border: 1px solid var(--border-color);}
628
- .message { padding: 12px 20px; border-radius: 8px; margin-bottom: 20px; color: #fff; font-weight: 500; border-left: 5px solid; display: flex; align-items: center; gap: 10px;}
629
- .message.success { background-color: #28a745; border-color: #1e7e34; }
630
- .message.error { background-color: #dc3545; border-color: #a71d2a; }
631
- .message.warning { background-color: #ffc107; color: #333; border-color: #d39e00; }
632
- .sync-buttons { display: flex; gap: 15px; margin-bottom: 20px; }
633
  </style>
634
  </head>
635
  <body>
636
  <div class="container">
637
- <div class="header">
638
- <h1><i class="fas fa-tools"></i> Администрирование ОсОО "Раина"</h1>
639
- <a href="{{ url_for('landing') }}" class="button"><i class="fas fa-home"></i> На сайт</a>
640
- </div>
641
-
642
- {% with messages = get_flashed_messages(with_categories=true) %}
643
- {% if messages %}
644
- {% for category, message in messages %}
645
- <div class="message {{ category }}"><i class="fas fa-{{ category == 'success' ? 'check' : (category == 'error' ? 'times' : 'exclamation-triangle') }}"></i> {{ message }}</div>
646
- {% endfor %}
647
- {% endif %}
648
- {% endwith %}
649
 
650
  <div class="section">
651
- <h2><i class="fas fa-sync-alt"></i> Синхронизация данных</h2>
652
- <div class="sync-buttons">
653
- <form method="POST" action="{{ url_for('force_upload') }}" style="margin:0;">
654
- <button type="submit" class="button"><i class="fas fa-upload"></i> Загрузить на сервер</button>
655
- </form>
656
- <form method="POST" action="{{ url_for('force_download') }}" style="margin:0;">
657
- <button type="submit" class="button"><i class="fas fa-download"></i> Скачать с сервера</button>
658
- </form>
659
- </div>
660
  </div>
661
 
662
  <div class="section">
663
- <h2><i class="fas fa-images"></i> Реализованные проекты</h2>
664
- <details><summary><i class="fas fa-plus-circle"></i> Добавить новый проект</summary>
665
- <form method="POST" enctype="multipart/form-data">
666
- <input type="hidden" name="action" value="add_project">
667
- <label>Название проекта*:</label><input type="text" name="title" required>
668
- <label>Краткое описание*:</label><textarea name="description" rows="3" required></textarea>
669
- <label>Фото проекта* (рекомендуется 16:9):</label><input type="file" name="photo" accept="image/*" required>
670
- <button type="submit"><i class="fas fa-save"></i> Добавить проект</button>
671
  </form>
672
  </details>
673
  <div class="item-list">
674
  {% for project in projects %}
675
  <div class="item">
676
  <p><strong>{{ project.title }}</strong>: {{ project.description }}</p>
677
- {% if project.photo %}
678
- <div class="photo-preview">
679
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/projects/{{ project.photo }}" alt="Project Photo">
680
- </div>
681
- {% endif %}
682
  <div class="item-actions">
683
- <button onclick="toggleEditForm('edit-project-{{ loop.index0 }}')"><i class="fas fa-edit"></i> Редактировать</button>
684
- <form method="POST" style="margin:0;">
685
- <input type="hidden" name="action" value="delete_project">
686
- <input type="hidden" name="index" value="{{ loop.index0 }}">
687
- <button type="submit" class="delete-button"><i class="fas fa-trash-alt"></i> Удалить</button>
688
- </form>
689
  </div>
690
  <div id="edit-project-{{ loop.index0 }}" class="edit-form-container">
691
- <form method="POST" enctype="multipart/form-data">
692
- <input type="hidden" name="action" value="edit_project">
693
- <input type="hidden" name="index" value="{{ loop.index0 }}">
694
- <label>Название проекта*:</label><input type="text" name="title" value="{{ project.title }}" required>
695
- <label>Краткое описание*:</label><textarea name="description" rows="3" required>{{ project.description }}</textarea>
696
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
697
- <button type="submit"><i class="fas fa-save"></i> Сохранить изменения</button>
698
  </form>
699
  </div>
700
  </div>
@@ -703,43 +751,32 @@ ADMIN_TEMPLATE = '''
703
  </div>
704
 
705
  <div class="section">
706
- <h2><i class="fas fa-briefcase"></i> Услуги "под ключ"</h2>
707
- <details><summary><i class="fas fa-plus-circle"></i> Добавить новую услугу</summary>
708
- <form method="POST" enctype="multipart/form-data">
709
- <input type="hidden" name="action" value="add_service">
710
- <label>Заголовок услуги*:</label><input type="text" name="title" required>
711
- <label>Иконка (FontAwesome Class)*:</label><input type="text" name="icon" placeholder="fas fa-tools" required>
712
- <label>Описание услуги*:</label><textarea name="description" rows="3" required></textarea>
713
- <label>Фото услуги (необязательно):</label><input type="file" name="photo" accept="image/*">
714
- <button type="submit"><i class="fas fa-save"></i> Добавить услугу</button>
715
  </form>
716
  </details>
717
  <div class="item-list">
718
  {% for service in services %}
719
  <div class="item">
720
- <p><i class="{{ service.icon }} fa-fw fa-2x" style="color: var(--primary-color); margin-right: 10px;"></i> <strong>{{ service.title }}</strong>: {{ service.description }}</p>
721
- {% if service.photo %}
722
- <div class="photo-preview">
723
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/{{ service.photo }}" alt="Service Photo">
724
- </div>
725
- {% endif %}
726
  <div class="item-actions">
727
- <button onclick="toggleEditForm('edit-service-{{ loop.index0 }}')"><i class="fas fa-edit"></i> Редактировать</button>
728
- <form method="POST" style="margin:0;">
729
- <input type="hidden" name="action" value="delete_service">
730
- <input type="hidden" name="index" value="{{ loop.index0 }}">
731
- <button type="submit" class="delete-button"><i class="fas fa-trash-alt"></i> Удалить</button>
732
- </form>
733
  </div>
734
  <div id="edit-service-{{ loop.index0 }}" class="edit-form-container">
735
- <form method="POST" enctype="multipart/form-data">
736
- <input type="hidden" name="action" value="edit_service">
737
- <input type="hidden" name="index" value="{{ loop.index0 }}">
738
- <label>Заголовок услуги*:</label><input type="text" name="title" value="{{ service.title }}" required>
739
  <label>Иконка*:</label><input type="text" name="icon" value="{{ service.icon }}" required>
740
- <label>Описание услуги*:</label><textarea name="description" rows="3" required>{{ service.description }}</textarea>
741
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
742
- <button type="submit"><i class="fas fa-save"></i> Сохранить изменения</button>
743
  </form>
744
  </div>
745
  </div>
@@ -749,82 +786,52 @@ ADMIN_TEMPLATE = '''
749
 
750
  <div class="section">
751
  <h2><i class="fas fa-box-open"></i> Оборудование</h2>
752
-
753
- <details><summary><i class="fas fa-plus-circle"></i> Добавить новую категорию</summary>
754
- <form method="POST">
755
- <input type="hidden" name="action" value="add_category">
756
- <label>Название категории:</label><input type="text" name="category_name" required>
757
- <button type="submit"><i class="fas fa-save"></i> Добавить категорию</button>
758
- </form>
759
  </details>
760
  <div class="item-list">
761
  {% for category in categories %}
762
- <div class="item" style="flex-direction: row; justify-content: space-between; align-items: center;">
763
- <span style="font-weight: 500;">{{ category }}</span>
764
- <form method="POST" style="margin: 0;">
765
- <input type="hidden" name="action" value="delete_category">
766
- <input type="hidden" name="category_name" value="{{ category }}">
767
- <button type="submit" class="delete-button" style="margin:0;"><i class="fas fa-trash-alt"></i> Удалить</button>
768
- </form>
769
  </div>
770
  {% endfor %}
771
  </div>
772
 
773
- <details style="margin-top:30px;"><summary><i class="fas fa-plus-circle"></i> Добавить оборудование</summary>
774
- <form method="POST" enctype="multipart/form-data">
775
- <input type="hidden" name="action" value="add_equipment">
776
- <label>Название оборудования*:</label><input type="text" name="name" required>
777
  <label>Цена (KGS)*:</label><input type="number" name="price" step="0.01" min="0" required>
778
- <label>Категория:</label><select name="category">
779
- <option value="Без категории">Без категории</option>
780
- {% for cat in categories %}
781
- <option value="{{ cat }}">{{ cat }}</option>
782
- {% endfor %}
783
- </select>
784
- <label>Фото оборудования (необязательно):</label><input type="file" name="photo" accept="image/*">
785
- <button type="submit"><i class="fas fa-save"></i> Добавить оборудование</button>
786
  </form>
787
  </details>
788
  <div class="item-list">
789
  {% for item in equipment %}
790
  <div class="item">
791
- <p><strong style="color: var(--primary-color);">{{ item.name }}</strong> ({{ item.category | default('Без категории') }}) - {{ "%.2f"|format(item.price) }} KGS</p>
792
- {% if item.photo %}
793
- <div class="photo-preview">
794
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/{{ item.photo }}" alt="Equipment Photo">
795
- </div>
796
- {% endif %}
797
  <div class="item-actions">
798
- <button onclick="toggleEditForm('edit-eq-{{ loop.index0 }}')"><i class="fas fa-edit"></i> Редактировать</button>
799
- <form method="POST" style="margin:0;">
800
- <input type="hidden" name="action" value="delete_equipment">
801
- <input type="hidden" name="index" value="{{ loop.index0 }}">
802
- <button type="submit" class="delete-button"><i class="fas fa-trash-alt"></i> Удалить</button>
803
- </form>
804
  </div>
805
  <div id="edit-eq-{{ loop.index0 }}" class="edit-form-container">
806
- <form method="POST" enctype="multipart/form-data">
807
- <input type="hidden" name="action" value="edit_equipment">
808
- <input type="hidden" name="index" value="{{ loop.index0 }}">
809
- <label>Название оборудования*:</label><input type="text" name="name" value="{{ item.name }}" required>
810
  <label>Цена (KGS)*:</label><input type="number" name="price" value="{{ item.price }}" step="0.01" min="0" required>
811
- <label>Категория:</label><select name="category">
812
- {% for cat in categories %}
813
- <option value="{{ cat }}" {% if item.category == cat %}selected{% endif %}>{{ cat }}</option>
814
- {% endfor %}
815
- </select>
816
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
817
- <button type="submit"><i class="fas fa-save"></i> Сохранить изменения</button>
818
  </form>
819
  </div>
820
  </div>
821
  {% endfor %}
822
  </div>
823
  </div>
824
- </div>
825
- <script>function toggleEditForm(id) { const element = document.getElementById(id); if (element.style.display === 'block') { element.style.display = 'none'; } else { element.style.display = 'block'; } }</script>
826
- </body>
827
- </html>
828
  '''
829
 
830
  @app.route('/')
@@ -1009,15 +1016,13 @@ def delete_photo_from_hf(photo_filename, folder):
1009
  @app.route('/force_upload', methods=['POST'])
1010
  def force_upload():
1011
  upload_db_to_hf()
1012
- flash("Данные успешно загружены на Hugging Face.", 'success')
1013
  return redirect(url_for('admin'))
1014
 
1015
  @app.route('/force_download', methods=['POST'])
1016
  def force_download():
1017
- if download_db_from_hf():
1018
- flash("Данные успешно скачаны с Hugging Face.", 'success')
1019
- else:
1020
- flash("Не удалось скачать данные с Hugging Face.", 'error')
1021
  return redirect(url_for('admin'))
1022
 
1023
  if __name__ == '__main__':
@@ -1026,4 +1031,4 @@ if __name__ == '__main__':
1026
  if HF_TOKEN_WRITE:
1027
  threading.Thread(target=periodic_backup, daemon=True).start()
1028
  port = int(os.environ.get('PORT', 7860))
1029
- app.run(host='0.0.0.0', port=port)
 
144
  <title>ОсОО "Раина" - Вентиляция и Кондиционирование</title>
145
  <meta name="description" content="Профессиональные услуги по проектированию, монтажу и обслуживанию систем вентиляции и кондиционирования в Кыргызстане. 15 лет опыта, более 1000 проектов.">
146
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
147
+ <link rel="preconnect" href="https://fonts.googleapis.com">
148
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
149
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
150
  <style>
151
  :root {
152
+ --bg-dark: #0f172a;
153
+ --bg-card: rgba(30, 41, 59, 0.5);
154
+ --bg-card-hover: rgba(51, 65, 85, 0.7);
155
+ --border-color: rgba(255, 255, 255, 0.1);
156
+ --text-light: #f8fafc;
157
+ --text-normal: #cbd5e1;
158
+ --text-muted: #94a3b8;
159
+ --primary-gradient: linear-gradient(90deg, #4f46e5, #a855f7, #ec4899);
160
+ --primary-color: #a855f7;
161
+ --accent-glow: rgba(168, 85, 247, 0.15);
162
  }
163
  * { margin: 0; padding: 0; box-sizing: border-box; scroll-behavior: smooth; }
164
+ body {
165
+ font-family: 'Inter', sans-serif;
166
+ background-color: var(--bg-dark);
167
+ color: var(--text-normal);
168
+ line-height: 1.7;
169
+ font-size: 16px;
170
+ -webkit-font-smoothing: antialiased;
171
+ -moz-osx-font-smoothing: grayscale;
172
+ }
173
  .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; }
174
+ section { padding: clamp(4rem, 10vw, 6rem) 0; overflow: hidden; position: relative; }
175
+ h1, h2, h3 { font-weight: 700; color: var(--text-light); line-height: 1.3; }
176
+ h1 { font-size: clamp(2.5rem, 6vw, 4rem); font-weight: 800; }
177
+ h2 { font-size: clamp(2rem, 5vw, 3rem); text-align: center; margin-bottom: 60px; position: relative; }
178
+ h2::after {
179
+ content: ''; display: block; width: 100px; height: 4px;
180
+ background: var(--primary-gradient);
181
+ margin: 15px auto 0; border-radius: 2px;
182
+ }
183
+ h3 { font-size: clamp(1.2rem, 3vw, 1.5rem); margin-bottom: 15px; }
184
+ p { margin-bottom: 1rem; color: var(--text-muted); }
185
+ .btn {
186
+ display: inline-block; padding: 14px 32px;
187
+ background: var(--primary-gradient); background-size: 200% 100%;
188
+ color: #fff; border-radius: 50px; text-decoration: none;
189
+ font-weight: 600; transition: all 0.4s ease;
190
+ border: none; box-shadow: 0 5px 20px rgba(168, 85, 247, 0.2);
191
+ }
192
+ .btn:hover {
193
+ background-position: right center;
194
+ transform: translateY(-3px) scale(1.02);
195
+ box-shadow: 0 8px 25px rgba(168, 85, 247, 0.3);
196
+ }
197
+ .header {
198
+ position: fixed; top: 0; left: 0; width: 100%; z-index: 1000;
199
+ padding: 15px 0; transition: all 0.3s ease;
200
+ }
201
+ .header.scrolled {
202
+ background-color: rgba(15, 23, 42, 0.8);
203
+ backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
204
+ border-bottom: 1px solid var(--border-color);
205
+ padding: 10px 0;
206
+ }
207
  .navbar { display: flex; justify-content: space-between; align-items: center; }
208
+ .logo { font-size: clamp(1.5rem, 4vw, 1.8rem); font-weight: 800; color: #fff; text-decoration: none; }
209
  .nav-links { display: flex; gap: 35px; list-style: none; }
210
+ .nav-links a { color: var(--text-normal); text-decoration: none; font-weight: 600; transition: color 0.3s ease; position: relative; padding-bottom: 5px; }
211
+ .nav-links a::after {
212
+ content: ''; position: absolute; bottom: 0; left: 0;
213
+ width: 0; height: 2px; background: var(--primary-gradient);
214
+ transition: width 0.3s ease;
215
+ }
216
+ .nav-links a:hover { color: var(--text-light); }
217
  .nav-links a:hover::after { width: 100%; }
218
+ .menu-toggle { display: none; font-size: 1.5rem; cursor: pointer; border: none; background: none; color: white; }
219
+ #hero {
220
+ min-height: 100vh; display: flex; align-items: center;
221
+ background-image: linear-gradient(rgba(15, 23, 42, 0.7), var(--bg-dark)), url(https://images.unsplash.com/photo-1558221639-2c7158995165?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80);
222
+ background-size: cover; background-position: center; position: relative;
223
+ }
224
+ #hero::before {
225
+ content: ''; position: absolute; top: 0; left: 50%;
226
+ transform: translateX(-50%); width: 70%; height: 100%;
227
+ background: radial-gradient(circle, rgba(79, 70, 229, 0.15) 0%, rgba(79, 70, 229, 0) 70%);
228
+ pointer-events: none; z-index: 0;
229
+ }
230
+ .hero-content { text-align: center; max-width: 800px; margin: 0 auto; position: relative; z-index: 1; }
231
+ .hero-content h1 {
232
+ background: var(--primary-gradient);
233
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
234
+ background-clip: text; text-fill-color: transparent;
235
+ }
236
+ .hero-content p { font-size: clamp(1rem, 2.5vw, 1.25rem); margin: 30px 0; max-width: 650px; margin-left: auto; margin-right: auto; color: var(--text-normal); }
237
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; }
238
+ .about-img { width: 100%; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); }
239
+ .services-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 30px; }
240
+ .interactive-card {
241
+ background: var(--bg-card); padding: 35px 30px; border-radius: 16px;
242
+ border: 1px solid var(--border-color);
243
+ transition: all 0.3s ease-out; position: relative; overflow: hidden;
244
+ }
245
+ .interactive-card:hover {
246
+ transform: translateY(-8px); background: var(--bg-card-hover);
247
+ border-color: rgba(168, 85, 247, 0.5); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
248
+ }
249
+ .interactive-card::before {
250
+ content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%;
251
+ background: radial-gradient(400px circle at var(--mouse-x) var(--mouse-y), var(--accent-glow), transparent 40%);
252
+ opacity: 0; transition: opacity 0.5s; pointer-events: none;
253
+ }
254
+ .interactive-card:hover::before { opacity: 1; }
255
+ .service-card i { font-size: 2.5rem; color: var(--primary-color); margin-bottom: 20px; display: block; }
256
+ #turnkey { background-color: #0b1222; }
257
+ .turnkey-card { padding: 0; display: flex; flex-direction: column; cursor: pointer;}
258
+ .turnkey-img { width: 100%; height: 200px; object-fit: cover; border-radius: 16px 16px 0 0; }
259
+ .turnkey-content { padding: 30px; flex-grow: 1; }
260
  .equipment-filters { display: flex; justify-content: center; flex-wrap: wrap; gap: 15px; margin-bottom: 50px; }
261
+ .filter-btn {
262
+ padding: 10px 25px; border: 1px solid var(--border-color); background-color: transparent;
263
+ color: var(--text-normal); border-radius: 50px; cursor: pointer; transition: all 0.3s; font-weight: 600;
264
+ }
265
+ .filter-btn.active, .filter-btn:hover {
266
+ background: var(--primary-gradient); color: #fff; border-color: transparent;
267
+ }
268
+ .equipment-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 30px; }
269
+ .equipment-card { text-align: center; padding: 25px; cursor: pointer; }
270
+ .equipment-card img { width: 100%; height: 180px; object-fit: contain; margin-bottom: 20px; transition: transform 0.3s ease; }
271
  .equipment-card:hover img { transform: scale(1.05); }
272
+ .equipment-card h3 { font-size: 1.2rem; }
273
+ .equipment-card .price { font-size: 1.4rem; font-weight: 700; color: var(--text-light); margin: 10px 0; }
274
+ .equipment-card .btn { padding: 10px 22px; font-size: 0.9rem; }
275
+ .projects-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 30px; }
276
+ .project-card {
277
+ position: relative; border-radius: 16px; overflow: hidden;
278
+ min-height: 450px; cursor: pointer;
279
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
280
+ transition: all 0.4s ease;
281
+ }
282
+ .project-card:hover { transform: translateY(-8px); box-shadow: 0 15px 30px rgba(0,0,0,0.3); }
283
+ .project-card img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.4s ease; }
284
+ .project-overlay {
285
+ position: absolute; bottom: 0; left: 0; right: 0;
286
+ background: linear-gradient(to top, rgba(15, 23, 42, 1) 10%, rgba(15, 23, 42, 0) 100%);
287
+ padding: 50px 25px 25px;
288
+ }
289
+ .project-card h3 { margin-bottom: 5px; font-size: 1.4rem; }
290
+ .project-card p {
291
+ margin-bottom: 0; transition: all 0.4s ease; opacity: 0;
292
+ max-height: 0; overflow: hidden; transform: translateY(10px);
293
+ }
294
+ .project-card:hover img { transform: scale(1.05); }
295
+ .project-card:hover p { opacity: 1; max-height: 200px; transform: translateY(0); }
296
+ #contact { background-color: #0b1222; }
297
  .contact-content { text-align: center; }
298
  .contact-info { margin-top: 40px; display: flex; flex-direction: column; align-items: center; gap: 25px; }
299
+ .contact-info p { font-size: 1.2rem; margin-bottom: 0; }
300
+ .contact-info a { color: var(--primary-color); text-decoration: none; font-weight: 600; font-size: 1.2rem; transition: color 0.3s; }
301
+ .contact-info a:hover { color: var(--text-light); }
302
+ .footer { text-align: center; padding: 40px 0; background-color: #020617; }
303
  @media (max-width: 992px) {
304
+ .grid-2 { grid-template-columns: 1fr; text-align: center; }
305
+ .grid-2 > div { order: 1; }
306
+ .about-img { margin-bottom: 40px; max-width: 500px; margin-left: auto; margin-right: auto; order: 2; }
 
307
  }
308
  @media (max-width: 768px) {
309
+ body { font-size: 15px; }
310
+ .nav-links {
311
+ position: fixed; top: 0; right: -100%; width: min(80vw, 320px); height: 100vh;
312
+ background-color: var(--bg-dark); flex-direction: column; justify-content: center;
313
+ align-items: center; transition: right 0.4s ease-in-out;
314
+ box-shadow: -5px 0 15px rgba(0,0,0,0.2); z-index: 1000;
315
+ }
316
  .nav-links.active { right: 0; }
317
  .menu-toggle { display: block; z-index: 1001; }
318
+ h2 { margin-bottom: 40px; }
319
+ .projects-grid { grid-template-columns: 1fr; }
 
 
 
320
  }
 
 
321
  .modal {
322
+ display: none; position: fixed; z-index: 1001; left: 0; top: 0;
323
+ width: 100%; height: 100%; overflow: auto;
324
+ background-color: rgba(15, 23, 42, 0.9); backdrop-filter: blur(8px);
 
 
 
325
  padding-top: 60px;
 
326
  }
327
  .modal-content {
328
+ position: relative; margin: 5% auto; padding: 30px;
329
+ width: 90%; max-width: 800px; background-color: #0b1222;
330
+ border-radius: 16px; text-align: center; border: 1px solid var(--border-color);
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  }
332
+ .modal-content img { max-width: 100%; max-height: 70vh; border-radius: 10px; margin-bottom: 25px; }
333
+ .modal-content h3 { color: var(--primary-color); margin-bottom: 10px; }
334
+ .modal-content p { color: var(--text-normal); font-size: 1.1rem; }
335
  .close-button {
336
+ position: absolute; top: 15px; right: 25px; font-size: 2.5rem;
337
+ font-weight: bold; color: #fff; cursor: pointer;
338
+ background: none; border: none; transition: color 0.3s, transform 0.3s;
 
 
 
 
 
 
 
339
  }
340
+ .close-button:hover, .close-button:focus { color: var(--primary-color); transform: rotate(90deg); }
341
  .carousel-nav { margin-top: 20px; }
342
  .carousel-nav button {
343
+ background-color: var(--primary-color); color: white; border: none;
344
+ padding: 10px 15px; border-radius: 50px; margin: 0 8px;
345
+ cursor: pointer; transition: all 0.3s ease; font-size: 1.2rem;
 
 
 
 
 
 
 
346
  }
347
+ .carousel-nav button:hover { background-color: #4f46e5; transform: scale(1.1); }
348
+ .animate-on-scroll {
349
+ opacity: 0; transform: translateY(40px);
350
+ transition: opacity 0.7s ease-out, transform 0.7s ease-out;
351
+ }
352
+ .animate-on-scroll.is-visible { opacity: 1; transform: translateY(0); }
353
  </style>
354
  </head>
355
  <body>
 
370
 
371
  <section id="hero">
372
  <div class="container hero-content">
373
+ <h1>ОсОО "Раина": Ваш Партнер в Вентиляции и Кондиционировании</h1>
374
+ <p>15 лет опыта, более 1000 реализованных проектов. Мы создаем комфорт и здоровье в любом помещении с помощью самых современных климатических систем.</p>
375
+ <a href="#contact" class="btn">Получить консультацию</a>
376
  </div>
377
  </section>
378
 
379
  <section id="about">
380
+ <div class="container animate-on-scroll">
381
  <h2>О Нашей Компании</h2>
382
  <div class="grid-2">
383
  <img src="https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80" alt="Команда Раина" class="about-img">
384
  <div>
385
+ <h3>Основание и История</h3>
386
+ <p>Компания "Раина" была основана в 2009 году. За эти годы мы зарекомендовали себя как надежный партнер, стремящийся к инновациям и совершенству в области климатических решений.</p>
387
+ <h3>Наша Миссия</h3>
388
+ <p>Наша миссия — создание оптимального микроклимата для наших клиентов, обеспечивающего комфорт, здоровье и высокую производительность.</p>
389
+ <h3>Профессиональная Команда</h3>
390
+ <p>Наша команда состоит из высококвалифицированных инженеров и техников, обладающих глубокими знаниями и опытом в области HVAC.</p>
391
  </div>
392
  </div>
393
  </div>
394
  </section>
395
 
396
  <section id="services">
397
+ <div class="container animate-on-scroll">
398
+ <h2>Наши Услуги</h2>
399
  <div class="services-grid">
400
+ <div class="service-card interactive-card"><i class="fas fa-drafting-compass"></i><h3>Проектирование</h3><p>Точные расчеты, 3D-модели и вся необходимая проектная документация.</p></div>
401
+ <div class="service-card interactive-card"><i class="fas fa-tools"></i><h3>Монтаж</h3><p>Профессиональная установка всех типов систем HVAC, от бытовых до промышленных.</p></div>
402
+ <div class="service-card interactive-card"><i class="fas fa-headset"></i><h3>Сервис 24/7</h3><p>Плановое обслуживание и оперативный аварийный ремонт в любое время.</p></div>
403
+ <div class="service-card interactive-card"><i class="fas fa-sync-alt"></i><h3>Модернизация</h3><p>Повышение энергоэффективности и снижение расходов на эксплуатацию.</p></div>
404
  </div>
405
  </div>
406
  </section>
407
 
408
+ <section id="turnkey">
409
+ <div class="container animate-on-scroll">
410
+ <h2>Услуги "под ключ"</h2>
411
  {% if services %}
412
  <div class="services-grid">
413
  {% for service in services %}
414
+ <div class="turnkey-card interactive-card" onclick="showDetailsModal('service', {{ loop.index0 }})">
415
  {% if service.photo %}
416
  <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/{{ service.photo }}" alt="{{ service.title }}" class="turnkey-img">
417
  {% endif %}
418
  <div class="turnkey-content">
419
+ <h3><i class="{{ service.icon }} fa-fw" style="margin-right: 8px; color: var(--primary-color);"></i>{{ service.title }}</h3>
420
  <p>{{ service.description }}</p>
421
  </div>
422
  </div>
423
  {% endfor %}
424
  </div>
425
  {% else %}
426
+ <p style="text-align: center;">Информация об услугах "под ключ" скоро появится на сайте.</p>
427
  {% endif %}
428
  </div>
429
  </section>
430
 
431
  <section id="equipment">
432
+ <div class="container animate-on-scroll">
433
+ <h2>Наше Оборудование</h2>
434
  {% if equipment %}
435
  <div class="equipment-filters">
436
  <button class="filter-btn active" data-filter="all">Все</button>
 
440
  </div>
441
  <div class="equipment-grid">
442
  {% for item in equipment %}
443
+ <div class="equipment-card interactive-card" data-category="{{ item.get('category', 'all') }}" onclick="showDetailsModal('equipment', {{ loop.index0 }})">
444
+ {% if item.photo %}
445
+ <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/{{ item.photo }}" alt="{{ item.name }}">
446
+ {% else %}
447
+ <img src="https://via.placeholder.com/250x180.png?text=No+Image" alt="No Image">
448
+ {% endif %}
449
+ <h3>{{ item.name }}</h3>
450
+ <p class="price">{{ "%.2f"|format(item.price) }} KGS</p>
451
+ <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: {{ item.name }}" target="_blank" class="btn" onclick="event.stopPropagation()">Запросить</a>
 
 
452
  </div>
453
  {% endfor %}
454
  </div>
455
  {% else %}
456
+ <p style="text-align: center;">Каталог оборудования скоро будет доступен.</p>
457
  {% endif %}
458
  </div>
459
  </section>
460
 
461
  <section id="projects">
462
+ <div class="container animate-on-scroll">
463
+ <h2>Реализованные Проекты</h2>
464
  {% if projects %}
465
  <div class="projects-grid">
466
  {% for project in projects %}
 
474
  {% endfor %}
475
  </div>
476
  {% else %}
477
+ <p style="text-align: center;">Информация о реализованных проектах скоро появится на сайте.</p>
478
  {% endif %}
479
  </div>
480
  </section>
481
 
482
  <section id="contact">
483
+ <div class="container contact-content animate-on-scroll">
484
+ <h2>Контакты</h2>
485
+ <p>Готовы стать вашим надежным партнером в создании идеального климата.</p>
486
  <div class="contact-info">
487
+ <p><strong>Свяжитесь с нами:</strong> <a href="tel:{{ contact_phone }}">{{ contact_phone }}</a></p>
488
+ <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, я хотел(а) бы получить консультацию по вашим услугам." target="_blank" class="btn"><i class="fab fa-whatsapp" style="margin-right: 8px;"></i> Написать в WhatsApp</a>
489
  </div>
490
  <div style="margin-top: 40px; font-size: 0.9rem; color: var(--text-muted);">
491
  <p><strong>Реквизиты:</strong> ОсОО «Раина», ИНН: 00812202110194, ОКПО: 31290279</p>
 
515
  let allItems = [];
516
 
517
  function showDetailsModal(type, index) {
518
+ event.stopPropagation();
519
  const data = {{ data | tojson }};
520
  currentData = data;
521
  currentType = type;
522
+
 
523
  if (type === 'service') allItems = data.services;
524
+ else if (type === 'equipment') {
525
+ const activeFilter = document.querySelector('.filter-btn.active').dataset.filter;
526
+ allItems = (activeFilter === 'all') ? data.equipment : data.equipment.filter(i => i.category === activeFilter);
527
+ const originalItem = data.equipment[index];
528
+ currentIndex = allItems.findIndex(i => i.name === originalItem.name && i.price === originalItem.price);
529
+ if (currentIndex === -1) {
530
+ allItems = data.equipment;
531
+ currentIndex = index;
532
+ }
533
+ }
534
  else if (type === 'project') allItems = data.projects;
535
+ else {
536
+ allItems = [];
537
+ currentIndex = -1;
538
+ }
539
 
540
+ if (allItems.length > 0 && type !== 'equipment') {
541
+ currentIndex = index;
542
+ }
543
+
544
  updateModalContent();
545
  document.getElementById('detailsModal').style.display = 'block';
546
  document.body.style.overflow = 'hidden';
 
557
  if (currentType === 'service') {
558
  content = `
559
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/${item.photo}" alt="${item.title}">` : ''}
560
+ <h3><i class="${item.icon} fa-fw" style="margin-right: 8px;"></i>${item.title}</h3>
561
  <p>${item.description}</p>
562
  `;
563
  } else if (currentType === 'equipment') {
 
565
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/${item.photo}" alt="${item.name}">` : ''}
566
  <h3>${item.name}</h3>
567
  <p><strong>Категория:</strong> ${item.category || 'Не указана'}</p>
568
+ <p class="price" style="font-size: 1.5rem; color: var(--primary-color);">${item.price.toFixed(2)} KGS</p>
569
  <a href="https://api.whatsapp.com/send?phone={{ whatsapp_phone }}&text=Здравствуйте, интересует оборудование: ${encodeURIComponent(item.name)}" target="_blank" class="btn" style="padding: 12px 28px; font-size: 1rem;">Запросить</a>
570
  `;
571
  } else if (currentType === 'project') {
 
639
  });
640
  });
641
  }
642
+
643
+ document.querySelectorAll('.interactive-card').forEach(card => {
644
+ card.addEventListener('mousemove', e => {
645
+ const rect = card.getBoundingClientRect();
646
+ const x = e.clientX - rect.left;
647
+ const y = e.clientY - rect.top;
648
+ card.style.setProperty('--mouse-x', `${x}px`);
649
+ card.style.setProperty('--mouse-y', `${y}px`);
650
+ });
651
+ });
652
+
653
+ const scrollElements = document.querySelectorAll(".animate-on-scroll");
654
+ const elementInView = (el, dividend = 1) => {
655
+ const elementTop = el.getBoundingClientRect().top;
656
+ return (elementTop <= (window.innerHeight || document.documentElement.clientHeight) / dividend);
657
+ };
658
+ const displayScrollElement = (element) => { element.classList.add("is-visible"); };
659
+ const handleScrollAnimation = () => {
660
+ scrollElements.forEach((el) => { if (elementInView(el, 1.15)) { displayScrollElement(el); } })
661
+ }
662
+ window.addEventListener("scroll", () => { handleScrollAnimation(); });
663
+ handleScrollAnimation();
664
  });
665
  </script>
666
  </body>
 
674
  <meta charset="UTF-8">
675
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
676
  <title>Админ-панель - Раина</title>
677
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
678
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
679
  <style>
680
+ body { font-family: 'Poppins', sans-serif; background-color: #f4f7f9; color: #333; padding: 20px; line-height: 1.6; }
681
+ .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); }
682
+ .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;}
683
+ h1, h2, h3 { font-weight: 600; color: #6a0dad; margin-bottom: 15px; }
684
+ h1 { font-size: 1.8rem; }
685
+ h2 { font-size: 1.5rem; margin-top: 30px; display: flex; align-items: center; gap: 8px; }
686
+ .section { margin-bottom: 30px; padding: 20px; background-color: #fafafa; border: 1px solid #e9e9e9; border-radius: 8px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
687
  form { margin-bottom: 20px; }
688
+ label { font-weight: 500; margin-top: 10px; display: block; color: #555; font-size: 0.9rem;}
689
+ input[type="text"], input[type="number"], textarea, select { width: 100%; padding: 10px 12px; margin-top: 5px; border: 1px solid #ddd; border-radius: 6px; font-size: 0.95rem; box-sizing: border-box; }
690
+ input[type="file"] { padding: 8px; cursor: pointer; border: 1px solid #ddd;}
691
+ button, .button { padding: 10px 18px; border: none; border-radius: 6px; background-color: #9b59b6; color: white; font-weight: 500; cursor: pointer; transition: all 0.3s ease; margin-top: 15px; text-decoration: none; }
692
+ button:hover, .button:hover { background-color: #8e44ad; }
693
+ .delete-button { background-color: #e74c3c; }
694
+ .delete-button:hover { background-color: #c0392b; }
 
 
695
  .item-list { display: grid; gap: 20px; }
696
+ .item { background: #fff; padding: 15px 20px; border-radius: 8px; border: 1px solid #eee; }
697
+ .item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; }
698
+ .edit-form-container { margin-top: 15px; padding: 20px; background: #fdf9ff; border: 1px dashed #ddd; border-radius: 6px; display: none; }
699
+ details { background-color: #fafafa; border: 1px solid #e9e9e9; border-radius: 8px; margin-bottom: 20px; }
700
+ details > summary { cursor: pointer; font-weight: 600; color: #8e44ad; display: block; padding: 15px; position: relative; list-style: none; }
701
+ details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); }
 
 
 
702
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
703
+ .photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; object-fit: cover;}
704
+ .message { padding: 10px 15px; border-radius: 6px; margin-bottom: 15px; }
705
+ .message.success { background-color: #d4edda; color: #155724; }
706
+ .message.error { background-color: #f8d7da; color: #721c24; }
707
+ .message.warning { background-color: #fff3cd; color: #856404; }
 
708
  </style>
709
  </head>
710
  <body>
711
  <div class="container">
712
+ <div class="header"><h1><i class="fas fa-tools"></i> Админ-панель "Раина"</h1><a href="{{ url_for('landing') }}" class="button"><i class="fas fa-home"></i> Перейти на сайт</a></div>
713
+ {% with messages = get_flashed_messages(with_categories=true) %}{% if messages %}{% for category, message in messages %}<div class="message {{ category }}">{{ message }}</div>{% endfor %}{% endif %}{% endwith %}
 
 
 
 
 
 
 
 
 
 
714
 
715
  <div class="section">
716
+ <h2><i class="fas fa-sync-alt"></i> Синхронизация</h2>
717
+ <form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;"><button type="submit" class="button">Загрузить на сервер</button></form>
718
+ <form method="POST" action="{{ url_for('force_download') }}" style="display: inline;"><button type="submit" class="button">Скачать с сервера</button></form>
 
 
 
 
 
 
719
  </div>
720
 
721
  <div class="section">
722
+ <h2><i class="fas fa-star"></i> Реализованные проекты</h2>
723
+ <details><summary>Добавить проект</summary>
724
+ <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="add_project">
725
+ <label>Название*:</label><input type="text" name="title" required>
726
+ <label>Описание*:</label><textarea name="description" rows="3" required></textarea>
727
+ <label>Фото*:</label><input type="file" name="photo" accept="image/*" required>
728
+ <button type="submit">Добавить проект</button>
 
729
  </form>
730
  </details>
731
  <div class="item-list">
732
  {% for project in projects %}
733
  <div class="item">
734
  <p><strong>{{ project.title }}</strong>: {{ project.description }}</p>
735
+ {% if project.photo %}<div class="photo-preview"><img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/projects/{{ project.photo }}" alt="Project Photo"></div>{% endif %}
 
 
 
 
736
  <div class="item-actions">
737
+ <button onclick="toggleEditForm('edit-project-{{ loop.index0 }}')">Редактировать</button>
738
+ <form method="POST" style="margin:0;"><input type="hidden" name="action" value="delete_project"><input type="hidden" name="index" value="{{ loop.index0 }}"><button type="submit" class="delete-button">Удалить</button></form>
 
 
 
 
739
  </div>
740
  <div id="edit-project-{{ loop.index0 }}" class="edit-form-container">
741
+ <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="edit_project"><input type="hidden" name="index" value="{{ loop.index0 }}">
742
+ <label>Название*:</label><input type="text" name="title" value="{{ project.title }}" required>
743
+ <label>Описание*:</label><textarea name="description" rows="3" required>{{ project.description }}</textarea>
 
 
744
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
745
+ <button type="submit">Сохранить</button>
746
  </form>
747
  </div>
748
  </div>
 
751
  </div>
752
 
753
  <div class="section">
754
+ <h2><i class="fas fa-concierge-bell"></i> Услуги "под ключ"</h2>
755
+ <details><summary>Добавить услугу</summary>
756
+ <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="add_service">
757
+ <label>Заголовок*:</label><input type="text" name="title" required>
758
+ <label>Иконка (FontAwesome)*:</label><input type="text" name="icon" placeholder="fas fa-tools" required>
759
+ <label>Описание*:</label><textarea name="description" rows="3" required></textarea>
760
+ <label>Фото:</label><input type="file" name="photo" accept="image/*">
761
+ <button type="submit">Добавить услугу</button>
 
762
  </form>
763
  </details>
764
  <div class="item-list">
765
  {% for service in services %}
766
  <div class="item">
767
+ <p><i class="{{ service.icon }} fa-fw"></i> <strong>{{ service.title }}</strong>: {{ service.description }}</p>
768
+ {% if service.photo %}<div class="photo-preview"><img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/services/{{ service.photo }}" alt="Service Photo"></div>{% endif %}
 
 
 
 
769
  <div class="item-actions">
770
+ <button onclick="toggleEditForm('edit-service-{{ loop.index0 }}')">Редактировать</button>
771
+ <form method="POST" style="margin:0;"><input type="hidden" name="action" value="delete_service"><input type="hidden" name="index" value="{{ loop.index0 }}"><button type="submit" class="delete-button">Удалить</button></form>
 
 
 
 
772
  </div>
773
  <div id="edit-service-{{ loop.index0 }}" class="edit-form-container">
774
+ <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="edit_service"><input type="hidden" name="index" value="{{ loop.index0 }}">
775
+ <label>Заголовок*:</label><input type="text" name="title" value="{{ service.title }}" required>
 
 
776
  <label>Иконка*:</label><input type="text" name="icon" value="{{ service.icon }}" required>
777
+ <label>Описание*:</label><textarea name="description" rows="3" required>{{ service.description }}</textarea>
778
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
779
+ <button type="submit">Сохранить</button>
780
  </form>
781
  </div>
782
  </div>
 
786
 
787
  <div class="section">
788
  <h2><i class="fas fa-box-open"></i> Оборудование</h2>
789
+ <details><summary>Добавить категорию</summary>
790
+ <form method="POST"><input type="hidden" name="action" value="add_category"><label>Название:</label><input type="text" name="category_name" required><button type="submit">Добавить</button></form>
 
 
 
 
 
791
  </details>
792
  <div class="item-list">
793
  {% for category in categories %}
794
+ <div class="item" style="display: flex; justify-content: space-between; align-items: center;">
795
+ <span>{{ category }}</span>
796
+ <form method="POST" style="margin: 0;"><input type="hidden" name="action" value="delete_category"><input type="hidden" name="category_name" value="{{ category }}"><button type="submit" class="delete-button" style="margin:0;">Удалить</button></form>
 
 
 
 
797
  </div>
798
  {% endfor %}
799
  </div>
800
 
801
+ <details style="margin-top:20px;"><summary>Добавить оборудование</summary>
802
+ <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="add_equipment">
803
+ <label>Название*:</label><input type="text" name="name" required>
 
804
  <label>Цена (KGS)*:</label><input type="number" name="price" step="0.01" min="0" required>
805
+ <label>Категория:</label><select name="category"><option value="Без категории">Без категории</option>{% for cat in categories %}<option value="{{ cat }}">{{ cat }}</option>{% endfor %}</select>
806
+ <label>Фото:</label><input type="file" name="photo" accept="image/*">
807
+ <button type="submit">Добавить</button>
 
 
 
 
 
808
  </form>
809
  </details>
810
  <div class="item-list">
811
  {% for item in equipment %}
812
  <div class="item">
813
+ <p><strong>{{ item.name }}</strong> ({{ item.category }}) - {{ "%.2f"|format(item.price) }} KGS</p>
814
+ {% if item.photo %}<div class="photo-preview"><img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/equipment/{{ item.photo }}" alt="Equipment Photo"></div>{% endif %}
 
 
 
 
815
  <div class="item-actions">
816
+ <button onclick="toggleEditForm('edit-eq-{{ loop.index0 }}')">Редактировать</button>
817
+ <form method="POST" style="margin:0;"><input type="hidden" name="action" value="delete_equipment"><input type="hidden" name="index" value="{{ loop.index0 }}"><button type="submit" class="delete-button">Удалить</button></form>
 
 
 
 
818
  </div>
819
  <div id="edit-eq-{{ loop.index0 }}" class="edit-form-container">
820
+ <form method="POST" enctype="multipart/form-data"><input type="hidden" name="action" value="edit_equipment"><input type="hidden" name="index" value="{{ loop.index0 }}">
821
+ <label>Название*:</label><input type="text" name="name" value="{{ item.name }}" required>
 
 
822
  <label>Цена (KGS)*:</label><input type="number" name="price" value="{{ item.price }}" step="0.01" min="0" required>
823
+ <label>Категория:</label><select name="category">{% for cat in categories %}<option value="{{ cat }}" {% if item.category == cat %}selected{% endif %}>{{ cat }}</option>{% endfor %}</select>
 
 
 
 
824
  <label>Заменить фото:</label><input type="file" name="photo" accept="image/*">
825
+ <button type="submit">Сохранить</button>
826
  </form>
827
  </div>
828
  </div>
829
  {% endfor %}
830
  </div>
831
  </div>
832
+ <script>function toggleEditForm(id) { document.getElementById(id).style.display = document.getElementById(id).style.display === 'block' ? 'none' : 'block'; }</script>
833
+ </body>
834
+ </html>
 
835
  '''
836
 
837
  @app.route('/')
 
1016
  @app.route('/force_upload', methods=['POST'])
1017
  def force_upload():
1018
  upload_db_to_hf()
1019
+ flash("Данные загружены на сервер.", 'success')
1020
  return redirect(url_for('admin'))
1021
 
1022
  @app.route('/force_download', methods=['POST'])
1023
  def force_download():
1024
+ download_db_from_hf()
1025
+ flash("Данные скачаны с сервера.", 'success')
 
 
1026
  return redirect(url_for('admin'))
1027
 
1028
  if __name__ == '__main__':
 
1031
  if HF_TOKEN_WRITE:
1032
  threading.Thread(target=periodic_backup, daemon=True).start()
1033
  port = int(os.environ.get('PORT', 7860))
1034
+ app.run(debug=False, host='0.0.0.0', port=port)