humanvprojectceo commited on
Commit
4433e44
·
verified ·
1 Parent(s): 228f8fd

Update cafe.html

Browse files
Files changed (1) hide show
  1. cafe.html +134 -51
cafe.html CHANGED
@@ -121,7 +121,6 @@
121
 
122
  .arch-shape { border-radius: 50% 50% 1rem 1rem / 35% 35% 1rem 1rem; }
123
 
124
- /* Chat Markdown */
125
  .chat-markdown { line-height: 1.85; word-wrap: break-word; overflow-wrap: break-word; }
126
  .chat-markdown > * + * { margin-top: 0.6em; }
127
  .chat-markdown h1, .chat-markdown h2, .chat-markdown h3,
@@ -189,7 +188,6 @@
189
  .chat-markdown tr:last-child td { border-bottom: none; }
190
  .chat-markdown img { max-width: 100%; border-radius: 10px; border: 2px solid rgba(201, 169, 97, 0.3); }
191
 
192
- /* Typing dots */
193
  .typing-dots span {
194
  display: inline-block; width: 6px; height: 6px;
195
  border-radius: 50%; background: #c9a961;
@@ -202,7 +200,6 @@
202
  30% { transform: translateY(-8px); opacity: 1; }
203
  }
204
 
205
- /* Buttons */
206
  .btn-primary {
207
  background: linear-gradient(135deg, #c9a961 0%, #9c7d3a 100%);
208
  color: #1a0d07; font-weight: 800;
@@ -240,7 +237,6 @@
240
  transform: translateY(-1px);
241
  }
242
 
243
- /* Drop zone */
244
  .drop-zone {
245
  position: relative;
246
  border: 2px dashed rgba(201, 169, 97, 0.3);
@@ -259,7 +255,7 @@
259
  pointer-events: none;
260
  }
261
 
262
- /* Order cards */
263
  .order-card {
264
  background: linear-gradient(135deg, rgba(45, 24, 16, 0.6), rgba(26, 13, 7, 0.5));
265
  border: 1px solid rgba(201, 169, 97, 0.2);
@@ -270,6 +266,28 @@
270
  box-shadow: 0 12px 30px -10px rgba(139, 30, 63, 0.3);
271
  transform: translateY(-2px);
272
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  .menu-row { transition: all 0.3s ease; }
275
  .menu-row:hover { background: rgba(201, 169, 97, 0.05); }
@@ -280,7 +298,6 @@
280
  box-shadow: 0 0 0 3px rgba(201, 169, 97, 0.15);
281
  }
282
 
283
- /* Checkbox */
284
  input[type="checkbox"] {
285
  appearance: none; width: 18px; height: 18px;
286
  border: 2px solid rgba(201, 169, 97, 0.4);
@@ -342,7 +359,6 @@
342
 
343
  button, .no-select { -webkit-user-select: none; user-select: none; }
344
 
345
- /* ============ MAIN LAYOUT ============ */
346
  .app-wrapper {
347
  display: flex;
348
  flex-direction: column;
@@ -351,8 +367,6 @@
351
  overflow: hidden;
352
  }
353
 
354
- /* ============ CHAT COLUMN HEIGHT CONTROL ============ */
355
- /* چت هرگز بزرگ‌تر از viewport نمی‌شود و اسکرول داخلی دارد */
356
  .chat-column {
357
  display: flex;
358
  flex-direction: column;
@@ -375,7 +389,6 @@
375
  flex-shrink: 0;
376
  }
377
 
378
- /* Desktop: ستون چت کل ارتفاع main را می‌گیرد */
379
  @media (min-width: 1024px) {
380
  .main-grid {
381
  overflow: hidden !important;
@@ -392,14 +405,12 @@
392
  }
393
  }
394
 
395
- /* Tablet & Mobile: main scroll می‌کند، چت ارتفاع مشخص */
396
  @media (max-width: 1023px) {
397
  .main-grid {
398
  overflow-y: auto;
399
  overflow-x: hidden;
400
  }
401
  .chat-column {
402
- /* ارتفاع کنترل‌شده: حداکثر 65vh ولی نه کمتر از 400px */
403
  height: 65vh;
404
  height: calc(var(--vh, 1vh) * 65);
405
  min-height: 380px;
@@ -408,7 +419,6 @@
408
  }
409
  }
410
 
411
- /* Small Mobile */
412
  @media (max-width: 480px) {
413
  .chat-column {
414
  height: 60vh;
@@ -418,7 +428,6 @@
418
  }
419
  }
420
 
421
- /* Landscape mobile */
422
  @media (max-height: 500px) and (orientation: landscape) {
423
  .chat-column {
424
  height: 75vh !important;
@@ -426,7 +435,6 @@
426
  }
427
  }
428
 
429
- /* iPad Portrait */
430
  @media (min-width: 768px) and (max-width: 1023px) {
431
  .chat-column {
432
  height: 70vh;
@@ -486,7 +494,7 @@
486
  <!-- Main grid -->
487
  <main class="main-grid flex-1 min-h-0 w-full mx-auto p-3 md:p-5 lg:p-6 grid grid-cols-1 lg:grid-cols-12 gap-3 md:gap-5 lg:gap-6 z-10 relative max-w-7xl">
488
 
489
- <!-- Right column: orders + menu + upload -->
490
  <section class="left-column lg:col-span-7 flex flex-col gap-3 md:gap-5 lg:gap-6 min-h-0">
491
 
492
  <!-- Live orders -->
@@ -505,7 +513,7 @@
505
  سفارشات جدید آشپزخانه
506
  <span class="w-2 h-2 rounded-full bg-rug-light animate-pulse flex-shrink-0"></span>
507
  </h2>
508
- <p class="text-[9px] md:text-[10px] text-cream/50 mt-0.5 truncate">لیست سفارشات در صف آماده‌سازی</p>
509
  </div>
510
  </div>
511
  <button onclick="fetchActiveOrders()" class="btn-ghost text-[9px] md:text-[10px] flex items-center gap-1 px-2 md:px-3 py-1.5 rounded-lg md:rounded-xl flex-shrink-0">
@@ -649,7 +657,6 @@
649
  <div class="chat-column glass-card-azure rounded-2xl md:rounded-3xl shadow-xl relative">
650
  <div class="absolute inset-0 bg-arch opacity-15 pointer-events-none"></div>
651
 
652
- <!-- Chat header -->
653
  <div class="chat-header-box relative px-3.5 md:px-5 py-2.5 md:py-3 border-b border-turk/25 flex items-center gap-2.5 bg-gradient-to-l from-azure-950/60 to-azure-900/40">
654
  <div class="w-9 h-9 md:w-10 md:h-10 rounded-xl bg-gradient-to-br from-turk/30 to-azure-700/30 border border-turk/40 flex items-center justify-center shadow-mosque flex-shrink-0">
655
  <svg class="w-5 h-5 md:w-6 md:h-6 text-turk-glow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
@@ -666,7 +673,6 @@
666
  </div>
667
  </div>
668
 
669
- <!-- Chat messages (SCROLLABLE - this is the key fix) -->
670
  <div id="chatMessages" class="chat-messages-container p-3 md:p-4 space-y-3 relative">
671
  <div class="flex gap-2 md:gap-2.5 max-w-[88%]">
672
  <div class="w-7 h-7 md:w-8 md:h-8 rounded-xl bg-gradient-to-br from-turk/20 to-azure-700/20 border border-turk/30 flex items-center justify-center flex-shrink-0">
@@ -678,7 +684,6 @@
678
  </div>
679
  </div>
680
 
681
- <!-- Chat loader -->
682
  <div id="chatLoader" class="chat-loader-box hidden relative px-3 py-2 md:px-4 md:py-2.5 flex items-center justify-between bg-azure-950/70 text-[9px] md:text-[10px] text-turk-light font-semibold border-t border-turk/20">
683
  <div class="flex items-center gap-2">
684
  <div class="typing-dots flex gap-1">
@@ -691,7 +696,6 @@
691
  </button>
692
  </div>
693
 
694
- <!-- Chat input -->
695
  <form id="chatForm" onsubmit="sendAdminMessage(event)" class="chat-input-box relative p-2 md:p-3 border-t border-turk/20 bg-bazaar-950/60 flex gap-2 safe-bottom">
696
  <input type="text" id="chatInput" placeholder="تغییر وضعیت یا ویرایش منو..." autocomplete="off"
697
  class="flex-1 min-w-0 bg-azure-950/60 border border-azure-700/40 text-xs md:text-sm text-cream placeholder-ochre/40 rounded-lg md:rounded-xl px-3 md:px-4 py-2 md:py-3 focus:outline-none focus:border-ochre/60 transition-all duration-300">
@@ -705,7 +709,6 @@
705
  </section>
706
  </main>
707
 
708
- <!-- Footer -->
709
  <footer class="flex-shrink-0 border-t border-ochre/15 py-2.5 md:py-3 text-center text-[9px] md:text-[10px] text-cream/40 bg-bazaar-950/60 backdrop-blur safe-bottom">
710
  طراحی و توسعه توسط الگوریتم داده نسترن | با الهام از فرهنگ تبریز © ۲۰۲۶
711
  </footer>
@@ -721,7 +724,6 @@
721
  window.addEventListener('resize', setVh);
722
  window.addEventListener('orientationchange', () => setTimeout(setVh, 100));
723
 
724
- // ============ AUTO SCROLL HELPER ============
725
  function scrollChatToBottom(smooth = true) {
726
  const chatScroll = document.getElementById('chatMessages');
727
  if (chatScroll) {
@@ -733,13 +735,11 @@
733
  }
734
  }
735
 
736
- // Auto scroll on resize (keyboard open/close)
737
  window.addEventListener('resize', () => {
738
  setVh();
739
  setTimeout(scrollChatToBottom, 150);
740
  });
741
 
742
- // ============ MARKED CONFIG ============
743
  marked.setOptions({ sanitize: true, breaks: true });
744
 
745
  let chatHistory = [
@@ -752,11 +752,9 @@
752
  fetchActiveOrders();
753
  fetchCurrentMenu();
754
  setInterval(fetchActiveOrders, 10000);
755
- // Initial scroll to bottom
756
  setTimeout(scrollChatToBottom, 100);
757
  });
758
 
759
- // Mobile keyboard handling
760
  const chatInput = document.getElementById('chatInput');
761
  if (chatInput) {
762
  chatInput.addEventListener('focus', () => {
@@ -765,7 +763,7 @@
765
  });
766
  }
767
 
768
- // ============ ORDER FUNCTIONS ============
769
  async function fetchActiveOrders() {
770
  const container = document.getElementById('ordersContainer');
771
  try {
@@ -784,10 +782,47 @@
784
  `;
785
  return;
786
  }
787
- container.innerHTML = '';
 
 
788
  orders.forEach(order => {
789
- let itemsHtml = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  order.items.forEach(item => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  itemsHtml += `
792
  <div class="flex items-center justify-between text-[10px] md:text-[11px] bg-bazaar-950/60 px-2.5 py-1.5 md:px-3 md:py-2 rounded-lg border border-ochre/20">
793
  <span class="text-cream font-bold flex items-center gap-2 min-w-0">
@@ -798,33 +833,65 @@
798
  </div>
799
  `;
800
  });
801
- const timeString = new Date(order.timestamp * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
802
  const cardHtml = `
803
- <div class="order-card rounded-xl md:rounded-2xl p-3 md:p-4 flex flex-col md:flex-row justify-between items-start md:items-center gap-2.5 md:gap-3">
804
  <div class="flex-1 space-y-2 w-full min-w-0">
805
  <div class="flex items-center justify-between md:justify-start gap-2 md:gap-3 w-full flex-wrap">
806
  <span class="badge-table text-[10px] md:text-xs font-bold px-2.5 md:px-3 py-1 rounded-lg flex items-center gap-1.5 flex-shrink-0">
807
  <svg class="w-3 h-3 md:w-3.5 md:h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
808
  <path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"/>
809
  </svg>
810
- میز ${order.table}
811
- </span>
812
- <span class="text-[9px] md:text-[10px] text-turk-light/70 font-medium flex items-center gap-1 flex-shrink-0">
813
- <svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
814
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
815
- </svg>
816
- ${timeString}
817
  </span>
 
 
818
  </div>
819
  <div class="grid grid-cols-1 sm:grid-cols-2 gap-1 md:gap-1.5 mt-1.5 md:mt-2">
820
  ${itemsHtml}
821
  </div>
822
  </div>
823
- <button onclick="completeOrder(${order.id})" class="w-full md:w-auto btn-ghost hover:bg-emerald-dark/40 hover:border-emerald-light/50 hover:text-emerald-light px-3 md:px-4 py-2 md:py-2.5 rounded-lg md:rounded-xl text-[9px] md:text-[10px] font-bold flex items-center justify-center gap-1.5 flex-shrink-0 group">
824
  <svg class="w-3.5 h-3.5 md:w-4 md:h-4 transition-transform group-hover:scale-110" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
825
  <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
826
  </svg>
827
- تحویل نهایی شد
828
  </button>
829
  </div>
830
  `;
@@ -838,18 +905,35 @@
838
  }
839
  }
840
 
841
- async function completeOrder(orderId) {
 
 
842
  try {
843
- const response = await fetch('/api/admin/complete_order', {
844
- method: 'POST',
845
- headers: { 'Content-Type': 'application/json' },
846
- body: JSON.stringify({ order_id: orderId })
847
- });
848
- const result = await response.json();
849
- if (response.ok && result.success) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
850
  fetchActiveOrders();
851
  } else {
852
- alert(result.error || "خطا در نهایی‌سازی.");
 
853
  }
854
  } catch (err) {
855
  alert("خطای شبکه: " + err.message);
@@ -1219,7 +1303,6 @@
1219
  }
1220
 
1221
  container.insertAdjacentHTML('beforeend', html);
1222
- // Always scroll to bottom after adding message
1223
  scrollChatToBottom(true);
1224
  return bubbleId;
1225
  }
 
121
 
122
  .arch-shape { border-radius: 50% 50% 1rem 1rem / 35% 35% 1rem 1rem; }
123
 
 
124
  .chat-markdown { line-height: 1.85; word-wrap: break-word; overflow-wrap: break-word; }
125
  .chat-markdown > * + * { margin-top: 0.6em; }
126
  .chat-markdown h1, .chat-markdown h2, .chat-markdown h3,
 
188
  .chat-markdown tr:last-child td { border-bottom: none; }
189
  .chat-markdown img { max-width: 100%; border-radius: 10px; border: 2px solid rgba(201, 169, 97, 0.3); }
190
 
 
191
  .typing-dots span {
192
  display: inline-block; width: 6px; height: 6px;
193
  border-radius: 50%; background: #c9a961;
 
200
  30% { transform: translateY(-8px); opacity: 1; }
201
  }
202
 
 
203
  .btn-primary {
204
  background: linear-gradient(135deg, #c9a961 0%, #9c7d3a 100%);
205
  color: #1a0d07; font-weight: 800;
 
237
  transform: translateY(-1px);
238
  }
239
 
 
240
  .drop-zone {
241
  position: relative;
242
  border: 2px dashed rgba(201, 169, 97, 0.3);
 
255
  pointer-events: none;
256
  }
257
 
258
+ /* کارت سفارش - با افکت ویژه برای سفارشات چندگانه */
259
  .order-card {
260
  background: linear-gradient(135deg, rgba(45, 24, 16, 0.6), rgba(26, 13, 7, 0.5));
261
  border: 1px solid rgba(201, 169, 97, 0.2);
 
266
  box-shadow: 0 12px 30px -10px rgba(139, 30, 63, 0.3);
267
  transform: translateY(-2px);
268
  }
269
+ .order-card.multi-order {
270
+ border: 1px solid rgba(194, 61, 95, 0.5);
271
+ background: linear-gradient(135deg, rgba(90, 16, 40, 0.25), rgba(26, 13, 7, 0.5));
272
+ }
273
+ .order-card.multi-order::before {
274
+ content: '';
275
+ position: absolute;
276
+ top: -1px; left: -1px; right: -1px; bottom: -1px;
277
+ border-radius: inherit;
278
+ background: linear-gradient(135deg, rgba(201, 169, 97, 0.3), transparent);
279
+ z-index: -1;
280
+ pointer-events: none;
281
+ }
282
+
283
+ /* Badge برای تعداد سفارش‌ها */
284
+ .orders-count-badge {
285
+ background: linear-gradient(135deg, #8b1e3f, #5a1028);
286
+ color: #f5e6d3;
287
+ border: 1px solid rgba(201, 169, 97, 0.5);
288
+ font-family: 'Lalezar', serif;
289
+ animation: pulseGlow 2s ease-in-out infinite;
290
+ }
291
 
292
  .menu-row { transition: all 0.3s ease; }
293
  .menu-row:hover { background: rgba(201, 169, 97, 0.05); }
 
298
  box-shadow: 0 0 0 3px rgba(201, 169, 97, 0.15);
299
  }
300
 
 
301
  input[type="checkbox"] {
302
  appearance: none; width: 18px; height: 18px;
303
  border: 2px solid rgba(201, 169, 97, 0.4);
 
359
 
360
  button, .no-select { -webkit-user-select: none; user-select: none; }
361
 
 
362
  .app-wrapper {
363
  display: flex;
364
  flex-direction: column;
 
367
  overflow: hidden;
368
  }
369
 
 
 
370
  .chat-column {
371
  display: flex;
372
  flex-direction: column;
 
389
  flex-shrink: 0;
390
  }
391
 
 
392
  @media (min-width: 1024px) {
393
  .main-grid {
394
  overflow: hidden !important;
 
405
  }
406
  }
407
 
 
408
  @media (max-width: 1023px) {
409
  .main-grid {
410
  overflow-y: auto;
411
  overflow-x: hidden;
412
  }
413
  .chat-column {
 
414
  height: 65vh;
415
  height: calc(var(--vh, 1vh) * 65);
416
  min-height: 380px;
 
419
  }
420
  }
421
 
 
422
  @media (max-width: 480px) {
423
  .chat-column {
424
  height: 60vh;
 
428
  }
429
  }
430
 
 
431
  @media (max-height: 500px) and (orientation: landscape) {
432
  .chat-column {
433
  height: 75vh !important;
 
435
  }
436
  }
437
 
 
438
  @media (min-width: 768px) and (max-width: 1023px) {
439
  .chat-column {
440
  height: 70vh;
 
494
  <!-- Main grid -->
495
  <main class="main-grid flex-1 min-h-0 w-full mx-auto p-3 md:p-5 lg:p-6 grid grid-cols-1 lg:grid-cols-12 gap-3 md:gap-5 lg:gap-6 z-10 relative max-w-7xl">
496
 
497
+ <!-- Right column -->
498
  <section class="left-column lg:col-span-7 flex flex-col gap-3 md:gap-5 lg:gap-6 min-h-0">
499
 
500
  <!-- Live orders -->
 
513
  سفارشات جدید آشپزخانه
514
  <span class="w-2 h-2 rounded-full bg-rug-light animate-pulse flex-shrink-0"></span>
515
  </h2>
516
+ <p class="text-[9px] md:text-[10px] text-cream/50 mt-0.5 truncate">گروه‌بندی شده بر اساس میز (ادغام خودکار)</p>
517
  </div>
518
  </div>
519
  <button onclick="fetchActiveOrders()" class="btn-ghost text-[9px] md:text-[10px] flex items-center gap-1 px-2 md:px-3 py-1.5 rounded-lg md:rounded-xl flex-shrink-0">
 
657
  <div class="chat-column glass-card-azure rounded-2xl md:rounded-3xl shadow-xl relative">
658
  <div class="absolute inset-0 bg-arch opacity-15 pointer-events-none"></div>
659
 
 
660
  <div class="chat-header-box relative px-3.5 md:px-5 py-2.5 md:py-3 border-b border-turk/25 flex items-center gap-2.5 bg-gradient-to-l from-azure-950/60 to-azure-900/40">
661
  <div class="w-9 h-9 md:w-10 md:h-10 rounded-xl bg-gradient-to-br from-turk/30 to-azure-700/30 border border-turk/40 flex items-center justify-center shadow-mosque flex-shrink-0">
662
  <svg class="w-5 h-5 md:w-6 md:h-6 text-turk-glow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
 
673
  </div>
674
  </div>
675
 
 
676
  <div id="chatMessages" class="chat-messages-container p-3 md:p-4 space-y-3 relative">
677
  <div class="flex gap-2 md:gap-2.5 max-w-[88%]">
678
  <div class="w-7 h-7 md:w-8 md:h-8 rounded-xl bg-gradient-to-br from-turk/20 to-azure-700/20 border border-turk/30 flex items-center justify-center flex-shrink-0">
 
684
  </div>
685
  </div>
686
 
 
687
  <div id="chatLoader" class="chat-loader-box hidden relative px-3 py-2 md:px-4 md:py-2.5 flex items-center justify-between bg-azure-950/70 text-[9px] md:text-[10px] text-turk-light font-semibold border-t border-turk/20">
688
  <div class="flex items-center gap-2">
689
  <div class="typing-dots flex gap-1">
 
696
  </button>
697
  </div>
698
 
 
699
  <form id="chatForm" onsubmit="sendAdminMessage(event)" class="chat-input-box relative p-2 md:p-3 border-t border-turk/20 bg-bazaar-950/60 flex gap-2 safe-bottom">
700
  <input type="text" id="chatInput" placeholder="تغییر وضعیت یا ویرایش منو..." autocomplete="off"
701
  class="flex-1 min-w-0 bg-azure-950/60 border border-azure-700/40 text-xs md:text-sm text-cream placeholder-ochre/40 rounded-lg md:rounded-xl px-3 md:px-4 py-2 md:py-3 focus:outline-none focus:border-ochre/60 transition-all duration-300">
 
709
  </section>
710
  </main>
711
 
 
712
  <footer class="flex-shrink-0 border-t border-ochre/15 py-2.5 md:py-3 text-center text-[9px] md:text-[10px] text-cream/40 bg-bazaar-950/60 backdrop-blur safe-bottom">
713
  طراحی و توسعه توسط الگوریتم داده نسترن | با الهام از فرهنگ تبریز © ۲۰۲۶
714
  </footer>
 
724
  window.addEventListener('resize', setVh);
725
  window.addEventListener('orientationchange', () => setTimeout(setVh, 100));
726
 
 
727
  function scrollChatToBottom(smooth = true) {
728
  const chatScroll = document.getElementById('chatMessages');
729
  if (chatScroll) {
 
735
  }
736
  }
737
 
 
738
  window.addEventListener('resize', () => {
739
  setVh();
740
  setTimeout(scrollChatToBottom, 150);
741
  });
742
 
 
743
  marked.setOptions({ sanitize: true, breaks: true });
744
 
745
  let chatHistory = [
 
752
  fetchActiveOrders();
753
  fetchCurrentMenu();
754
  setInterval(fetchActiveOrders, 10000);
 
755
  setTimeout(scrollChatToBottom, 100);
756
  });
757
 
 
758
  const chatInput = document.getElementById('chatInput');
759
  if (chatInput) {
760
  chatInput.addEventListener('focus', () => {
 
763
  });
764
  }
765
 
766
+ // ============ ORDER FUNCTIONS - با ادغام سفارشات هر میز ============
767
  async function fetchActiveOrders() {
768
  const container = document.getElementById('ordersContainer');
769
  try {
 
782
  `;
783
  return;
784
  }
785
+
786
+ // ✅ گروه‌بندی سفارشات بر اساس میز
787
+ const tableGroups = {};
788
  orders.forEach(order => {
789
+ const tableKey = String(order.table);
790
+ if (!tableGroups[tableKey]) {
791
+ tableGroups[tableKey] = {
792
+ table: order.table,
793
+ orderIds: [],
794
+ items: {}, // name -> {name, quantity}
795
+ timestamps: [],
796
+ totalItemCount: 0
797
+ };
798
+ }
799
+ const group = tableGroups[tableKey];
800
+ group.orderIds.push(order.id);
801
+ group.timestamps.push(order.timestamp);
802
+ group.totalItemCount += order.items.reduce((sum, it) => sum + it.quantity, 0);
803
+
804
+ // ✅ ادغام آیتم‌های تکراری با جمع تعداد
805
  order.items.forEach(item => {
806
+ if (group.items[item.name]) {
807
+ group.items[item.name].quantity += item.quantity;
808
+ } else {
809
+ group.items[item.name] = { name: item.name, quantity: item.quantity };
810
+ }
811
+ });
812
+ });
813
+
814
+ container.innerHTML = '';
815
+ // مرتب‌سازی بر اساس جدیدترین timestamp هر گروه
816
+ const sortedGroups = Object.values(tableGroups).sort((a, b) => {
817
+ const aMax = Math.max(...a.timestamps);
818
+ const bMax = Math.max(...b.timestamps);
819
+ return bMax - aMax;
820
+ });
821
+
822
+ sortedGroups.forEach(group => {
823
+ const itemsArray = Object.values(group.items);
824
+ let itemsHtml = '';
825
+ itemsArray.forEach(item => {
826
  itemsHtml += `
827
  <div class="flex items-center justify-between text-[10px] md:text-[11px] bg-bazaar-950/60 px-2.5 py-1.5 md:px-3 md:py-2 rounded-lg border border-ochre/20">
828
  <span class="text-cream font-bold flex items-center gap-2 min-w-0">
 
833
  </div>
834
  `;
835
  });
836
+
837
+ // محاسبه زمان‌ها
838
+ const firstTime = new Date(Math.min(...group.timestamps) * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
839
+ const lastTime = new Date(Math.max(...group.timestamps) * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
840
+ const orderCount = group.orderIds.length;
841
+ const isMulti = orderCount > 1;
842
+
843
+ // نمایش زمان
844
+ let timeDisplay = '';
845
+ if (isMulti) {
846
+ timeDisplay = `<span class="text-[9px] md:text-[10px] text-turk-light/70 font-medium flex items-center gap-1 flex-shrink-0">
847
+ <svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
848
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
849
+ </svg>
850
+ ${firstTime} → ${lastTime}
851
+ </span>`;
852
+ } else {
853
+ timeDisplay = `<span class="text-[9px] md:text-[10px] text-turk-light/70 font-medium flex items-center gap-1 flex-shrink-0">
854
+ <svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
855
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
856
+ </svg>
857
+ ${lastTime}
858
+ </span>`;
859
+ }
860
+
861
+ // Badge برای سفارشات چندگانه
862
+ const multiBadge = isMulti ? `
863
+ <span class="orders-count-badge text-[10px] md:text-[11px] px-2 py-0.5 rounded-md flex items-center gap-1 flex-shrink-0">
864
+ <svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
865
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4"/>
866
+ </svg>
867
+ ${orderCount} سفارش
868
+ </span>
869
+ ` : '';
870
+
871
+ const orderIdsJson = JSON.stringify(group.orderIds).replace(/"/g, '&quot;');
872
+
873
  const cardHtml = `
874
+ <div class="order-card ${isMulti ? 'multi-order' : ''} relative rounded-xl md:rounded-2xl p-3 md:p-4 flex flex-col md:flex-row justify-between items-start md:items-center gap-2.5 md:gap-3">
875
  <div class="flex-1 space-y-2 w-full min-w-0">
876
  <div class="flex items-center justify-between md:justify-start gap-2 md:gap-3 w-full flex-wrap">
877
  <span class="badge-table text-[10px] md:text-xs font-bold px-2.5 md:px-3 py-1 rounded-lg flex items-center gap-1.5 flex-shrink-0">
878
  <svg class="w-3 h-3 md:w-3.5 md:h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
879
  <path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"/>
880
  </svg>
881
+ میز ${group.table}
 
 
 
 
 
 
882
  </span>
883
+ ${multiBadge}
884
+ ${timeDisplay}
885
  </div>
886
  <div class="grid grid-cols-1 sm:grid-cols-2 gap-1 md:gap-1.5 mt-1.5 md:mt-2">
887
  ${itemsHtml}
888
  </div>
889
  </div>
890
+ <button onclick='completeOrdersForTable(${orderIdsJson})' class="w-full md:w-auto btn-ghost hover:bg-emerald-dark/40 hover:border-emerald-light/50 hover:text-emerald-light px-3 md:px-4 py-2 md:py-2.5 rounded-lg md:rounded-xl text-[9px] md:text-[10px] font-bold flex items-center justify-center gap-1.5 flex-shrink-0 group">
891
  <svg class="w-3.5 h-3.5 md:w-4 md:h-4 transition-transform group-hover:scale-110" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
892
  <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
893
  </svg>
894
+ تحویل نهایی ${isMulti ? `(${orderCount} سفارش)` : 'شد'}
895
  </button>
896
  </div>
897
  `;
 
905
  }
906
  }
907
 
908
+ // تکمیل همه سفارش‌های یک میز (چندگانه)
909
+ async function completeOrdersForTable(orderIds) {
910
+ if (!Array.isArray(orderIds) || orderIds.length === 0) return;
911
  try {
912
+ // انجام sequential همه درخواست‌ها
913
+ let successCount = 0;
914
+ let failCount = 0;
915
+ for (const orderId of orderIds) {
916
+ try {
917
+ const response = await fetch('/api/admin/complete_order', {
918
+ method: 'POST',
919
+ headers: { 'Content-Type': 'application/json' },
920
+ body: JSON.stringify({ order_id: orderId })
921
+ });
922
+ const result = await response.json();
923
+ if (response.ok && result.success) {
924
+ successCount++;
925
+ } else {
926
+ failCount++;
927
+ }
928
+ } catch (e) {
929
+ failCount++;
930
+ }
931
+ }
932
+ if (failCount === 0) {
933
  fetchActiveOrders();
934
  } else {
935
+ alert(`${successCount} سفارش تکمیل شد. ${failCount} خطا رخ داد.`);
936
+ fetchActiveOrders();
937
  }
938
  } catch (err) {
939
  alert("خطای شبکه: " + err.message);
 
1303
  }
1304
 
1305
  container.insertAdjacentHTML('beforeend', html);
 
1306
  scrollChatToBottom(true);
1307
  return bubbleId;
1308
  }