Spaces:
Running
Running
| <html lang="fa" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>اطلاعات ایستگاه</title> | |
| <link rel="stylesheet" href="https://cdn.tailwindcss.com"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script> | |
| <style> | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .animate-pulse { | |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| } | |
| #cy { | |
| width: 100%; | |
| height: 400px; | |
| background-color: #2d3748; | |
| } | |
| @media (max-width: 640px) { | |
| #cy { | |
| height: 300px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white p-4 md:p-6"> | |
| <div class="container mx-auto" id="station-info"> | |
| <h1 class="text-3xl md:text-4xl font-extrabold mb-4 md:mb-6 text-center">اطلاعات ایستگاه</h1> | |
| <div id="dataInfo" class="text-center text-base md:text-lg text-blue-400 mb-4 md:mb-6 font-bold"></div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> | |
| <div> | |
| <h2 class="text-2xl md:text-3xl font-semibold mb-3">اطلاعات ایستگاه: {{ station_name }} (ID: {{ station_id }})</h2> | |
| <h3 class="text-xl md:text-2xl font-semibold mb-2">زمانبندی اتوبوسها ({{ 'رفت' if direction == '76' else 'برگشت' }}):</h3> | |
| <div class="overflow-x-auto mb-4"> | |
| <table class="w-full border-collapse"> | |
| <thead> | |
| <tr class="bg-gray-800"> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">کد مسیر</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">مبدأ</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">مقصد</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">ترتیب</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">نوع مسیر</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">زمان رسیدن (دقیقه)</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for stop in stops_eta %} | |
| <tr class="border-b border-gray-700 hover:bg-gray-800"> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ stop.route_code }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ stop.origination_name }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ stop.destination_name }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ stop.order }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ stop.route_type }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ stop.eta }}</td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-xl md:text-2xl font-semibold mb-2">چهار ایستگاه بعدی:</h3> | |
| <div class="overflow-x-auto mb-4"> | |
| <table class="w-full border-collapse"> | |
| <thead> | |
| <tr class="bg-gray-800"> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">کد ایستگاه</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">نام ایستگاه</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">کد مسیر</th> | |
| <th class="p-2 md:p-3 text-right text-xs md:text-sm">زمان رسیدن (دقیقه)</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for next_station in next_stations_eta[:4] %} | |
| <tr class="border-b border-gray-700 hover:bg-gray-800"> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ next_station.code }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm">{{ next_station.name }}</td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm"> | |
| {% for eta in next_station.eta %} | |
| {{ eta.route_code }}{% if not loop.last %}, {% endif %} | |
| {% endfor %} | |
| </td> | |
| <td class="p-2 md:p-3 text-xs md:text-sm"> | |
| {% for eta in next_station.eta %} | |
| {{ eta.eta }}{% if not loop.last %}, {% endif %} | |
| {% endfor %} | |
| </td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <h3 class="text-xl md:text-2xl font-semibold mb-3">نمودار ارتباط ایستگاهها:</h3> | |
| <p class="text-gray-400 mb-4 text-sm md:text-base">این نمودار ارتباط ایستگاه فعلی را با ایستگاههای قبلی و بعدی نشان میدهد.</p> | |
| <div id="cy" class="mb-6"></div> | |
| <div id="station-details" class="mt-6"> | |
| <h3 class="text-xl md:text-2xl font-semibold mb-3">جزئیات ایستگاهها:</h3> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> | |
| {% if previous_station %} | |
| <div class="bg-gray-800 p-4 rounded-lg"> | |
| <h4 class="text-lg md:text-xl font-semibold mb-2">ایستگاه قبلی</h4> | |
| <p class="text-sm md:text-base"><strong>نام:</strong> {{ previous_station.name }}</p> | |
| <p class="text-sm md:text-base"><strong>کد:</strong> {{ previous_station.code }}</p> | |
| <p class="text-sm md:text-base"><strong>فاصله:</strong> {{ previous_station.distance }} متر</p> | |
| </div> | |
| {% endif %} | |
| <div class="bg-gray-800 p-4 rounded-lg"> | |
| <h4 class="text-lg md:text-xl font-semibold mb-2">ایستگاه فعلی</h4> | |
| <p class="text-sm md:text-base"><strong>نام:</strong> {{ station_name }}</p> | |
| <p class="text-sm md:text-base"><strong>کد:</strong> {{ station_id }}</p> | |
| <p class="text-sm md:text-base"><strong>جهت:</strong> {{ 'رفت' if direction == '76' else 'برگشت' }}</p> | |
| </div> | |
| {% for next_station in next_stations_eta[:4] %} | |
| <div class="bg-gray-800 p-4 rounded-lg"> | |
| <h4 class="text-lg md:text-xl font-semibold mb-2">ایستگاه بعدی {{ loop.index }}</h4> | |
| <p class="text-sm md:text-base"><strong>نام:</strong> {{ next_station.name }}</p> | |
| <p class="text-sm md:text-base"><strong>کد:</strong> {{ next_station.code }}</p> | |
| <p class="text-sm md:text-base"><strong>فاصله:</strong> {{ next_station.distance }} متر</p> | |
| <p class="text-sm md:text-base"><strong>زمان رسیدن:</strong> | |
| {% for eta in next_station.eta %} | |
| {{ eta.eta }} دقیقه ({{ eta.route_code }}){% if not loop.last %}, {% endif %} | |
| {% endfor %} | |
| </p> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| </div> | |
| <div class="flex justify-between mb-6 mt-6"> | |
| {% if previous_station %} | |
| <a href="{{ url_for('station_info', station_id=previous_station['code'], direction=direction) }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out transform hover:scale-105 text-sm md:text-base">ایستگاه قبلی</a> | |
| {% endif %} | |
| {% if next_station %} | |
| <a href="{{ url_for('station_info', station_id=next_station['code'], direction=direction) }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out transform hover:scale-105 text-sm md:text-base">ایستگاه بعدی</a> | |
| {% endif %} | |
| </div> | |
| </div> | |
| <script> | |
| (function() { | |
| let cy; | |
| let updateTimer; | |
| let countdownTimer; | |
| const UPDATE_INTERVAL = 60000; | |
| function updateTime() { | |
| const now = new Date(); | |
| const receivedAt = now.toLocaleString('fa-IR', { timeZone: 'Asia/Tehran' }); | |
| const dataInfoElement = document.getElementById('dataInfo'); | |
| dataInfoElement.textContent = 'اطلاعات دریافت شده در: ' + receivedAt; | |
| dataInfoElement.classList.add('animate-pulse'); | |
| setTimeout(() => dataInfoElement.classList.remove('animate-pulse'), 1000); | |
| } | |
| function updateCountdown(seconds) { | |
| const countdown = document.getElementById('countdown'); | |
| clearInterval(countdownTimer); | |
| countdownTimer = setInterval(function() { | |
| countdown.textContent = `بروزرسانی بعدی در ${seconds} ثانیه`; | |
| seconds--; | |
| if (seconds < 0) { | |
| clearInterval(countdownTimer); | |
| } | |
| }, 1000); | |
| } | |
| function updateContent() { | |
| clearTimeout(updateTimer); | |
| fetch(window.location.href) | |
| .then(response => { | |
| if (!response.ok) { | |
| throw new Error('Network response was not ok'); | |
| } | |
| return response.text(); | |
| }) | |
| .then(html => { | |
| const parser = new DOMParser(); | |
| const doc = parser.parseFromString(html, 'text/html'); | |
| const newContent = doc.getElementById('station-info').innerHTML; | |
| const currentContent = document.getElementById('station-info'); | |
| const cyDiv = currentContent.querySelector('#cy'); | |
| currentContent.innerHTML = newContent; | |
| currentContent.querySelector('#cy').replaceWith(cyDiv); | |
| updateTime(); | |
| initializeCytoscape(); | |
| }) | |
| .catch(error => { | |
| console.error('Error:', error); | |
| updateTime(); | |
| }) | |
| .finally(() => { | |
| updateCountdown(60); | |
| updateTimer = setTimeout(updateContent, UPDATE_INTERVAL); | |
| }); | |
| } | |
| function initializeCytoscape() { | |
| if (cy) { | |
| cy.destroy(); | |
| } | |
| const stations = [ | |
| {% if previous_station %} | |
| { id: 'previous', label: '{{ previous_station.name }}' }, | |
| {% endif %} | |
| { id: 'current', label: '{{ station_name }}' }, | |
| {% for next_station in next_stations_eta[:4] %} | |
| { id: '{{ next_station.code }}', label: '{{ next_station.name }}' }, | |
| {% endfor %} | |
| ]; | |
| cy = cytoscape({ | |
| container: document.getElementById('cy'), | |
| elements: stations.map((station, index) => ({ | |
| data: { id: station.id, label: station.label } | |
| })).concat(stations.slice(1).map((station, index) => ({ | |
| data: { id: `e${index}`, source: stations[index].id, target: station.id } | |
| }))), | |
| style: [ | |
| { | |
| selector: 'node', | |
| style: { | |
| 'background-color': '#4299e1', | |
| 'label': 'data(label)', | |
| 'color': '#ffffff', | |
| 'text-valign': 'center', | |
| 'text-halign': 'center', | |
| 'text-wrap': 'wrap', | |
| 'text-max-width': '80px', | |
| 'font-size': '12px', | |
| 'width': '80px', | |
| 'height': '80px' | |
| } | |
| }, | |
| { | |
| selector: 'edge', | |
| style: { | |
| 'width': 2, | |
| 'line-color': '#63b3ed', | |
| 'curve-style': 'straight', | |
| 'target-arrow-shape': 'triangle', | |
| 'target-arrow-color': '#63b3ed', | |
| 'arrow-scale': 1.2 | |
| } | |
| }, | |
| { | |
| selector: '#current', | |
| style: { | |
| 'background-color': '#48bb78', | |
| 'width': '100px', | |
| 'height': '100px', | |
| 'font-size': '14px', | |
| 'font-weight': 'bold' | |
| } | |
| }, | |
| { | |
| selector: '#previous', | |
| style: { | |
| 'background-color': '#ed8936' | |
| } | |
| } | |
| ], | |
| layout: { | |
| name: 'grid', | |
| rows: 1 | |
| }, | |
| userZoomingEnabled: false, | |
| userPanningEnabled: false, | |
| boxSelectionEnabled: false, | |
| autoungrabify: true, | |
| autounselectify: true | |
| }); | |
| cy.fit(); | |
| } | |
| document.addEventListener('DOMContentLoaded', function() { | |
| updateTime(); | |
| updateCountdown(60); | |
| initializeCytoscape(); | |
| updateTimer = setTimeout(updateContent, UPDATE_INTERVAL); | |
| }); | |
| window.addEventListener('resize', function() { | |
| if (cy) { | |
| cy.fit(); | |
| } | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> |