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> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script> | |
| tailwind.config = { | |
| darkMode: 'class', | |
| theme: { | |
| fontFamily: { | |
| sans: ['Vazir', 'sans-serif'], | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| @font-face { | |
| font-family: 'Vazir'; | |
| src: url('https://cdn.jsdelivr.net/gh/rastikerdar/vazir-font@v30.1.0/dist/Vazir.woff2') format('woff2'); | |
| } | |
| body { | |
| font-family: 'Vazir', sans-serif; | |
| } | |
| .sidebar { | |
| transition: all 0.3s ease; | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .rtl-dir { | |
| direction: rtl; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200"> | |
| <div class="flex h-screen overflow-hidden"> | |
| <!-- Sidebar --> | |
| <div id="sidebar" class="sidebar w-64 bg-white dark:bg-gray-800 shadow-lg flex flex-col"> | |
| <div class="p-4 border-b border-gray-200 dark:border-gray-700"> | |
| <div class="flex items-center justify-between"> | |
| <h1 class="text-xl font-bold text-indigo-600 dark:text-indigo-400">SugarCare 🏥</h1> | |
| <button id="darkModeToggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700"> | |
| <i data-feather="moon" class="text-gray-700 dark:text-gray-300"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <nav class="flex-1 overflow-y-auto"> | |
| <ul> | |
| <li> | |
| <a href="#" onclick="showDashboard()" class="flex items-center p-3 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"> | |
| <i data-feather="home" class="ml-2"></i> | |
| <span>داشبورد</span> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="#" onclick="showPatients()" class="flex items-center p-3 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"> | |
| <i data-feather="users" class="ml-2"></i> | |
| <span>پرونده بیماران</span> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="#" onclick="showExamination()" class="flex items-center p-3 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"> | |
| <i data-feather="file-plus" class="ml-2"></i> | |
| <span>ثبت معاینه جدید</span> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="#" onclick="showPharmacy()" class="flex items-center p-3 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"> | |
| <i data-feather="package" class="ml-2"></i> | |
| <span>داروخانه</span> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="#" onclick="showVisits()" class="flex items-center p-3 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"> | |
| <i data-feather="list" class="ml-2"></i> | |
| <span>لیست مراجعات</span> | |
| </a> | |
| </li> | |
| <li> | |
| <a href="#" onclick="showReports()" class="flex items-center p-3 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"> | |
| <i data-feather="bar-chart-2" class="ml-2"></i> | |
| <span>گزارشها و آمار</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </nav> | |
| <div class="p-4 border-t border-gray-200 dark:border-gray-700"> | |
| <button onclick="exportAllData()" class="w-full flex items-center justify-center p-2 bg-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-300 rounded hover:bg-indigo-200 dark:hover:bg-indigo-800"> | |
| <i data-feather="download" class="ml-2"></i> | |
| <span>خروجی تمام دادهها</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="flex-1 overflow-auto"> | |
| <header class="bg-white dark:bg-gray-800 shadow-sm p-4 flex items-center justify-between"> | |
| <button id="sidebarToggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 lg:hidden"> | |
| <i data-feather="menu"></i> | |
| </button> | |
| <h2 id="pageTitle" class="text-xl font-semibold">داشبورد</h2> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <div class="relative"> | |
| <input type="text" placeholder="جستجو..." class="pl-10 pr-4 py-2 border rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <i data-feather="search" class="absolute right-3 top-2.5 text-gray-400"></i> | |
| </div> | |
| <div class="h-8 w-8 rounded-full bg-indigo-100 dark:bg-indigo-900 flex items-center justify-center"> | |
| <i data-feather="user" class="text-indigo-600 dark:text-indigo-400"></i> | |
| </div> | |
| </div> | |
| </header> | |
| <main class="p-4"> | |
| <!-- Dashboard Content --> | |
| <div id="dashboardContent" class="fade-in"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6"> | |
| <!-- Today's Visits --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-indigo-100 dark:bg-indigo-900 text-indigo-600 dark:text-indigo-400"> | |
| <i data-feather="calendar"></i> | |
| </div> | |
| <div class="mr-3"> | |
| <p class="text-gray-500 dark:text-gray-400 text-sm">مراجعات امروز</p> | |
| <h3 class="font-bold text-xl" id="todayVisits">0</h3> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Weekly Visits --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-green-100 dark:bg-green-900 text-green-600 dark:text-green-400"> | |
| <i data-feather="activity"></i> | |
| </div> | |
| <div class="mr-3"> | |
| <p class="text-gray-500 dark:text-gray-400 text-sm">مراجعات هفتگی</p> | |
| <h3 class="font-bold text-xl" id="weeklyVisits">0</h3> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Hospital Transfers --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-red-100 dark:bg-red-900 text-red-600 dark:text-red-400"> | |
| <i data-feather="truck"></i> | |
| </div> | |
| <div class="mr-3"> | |
| <p class="text-gray-500 dark:text-gray-400 text-sm">اعزام به بیمارستان</p> | |
| <h3 class="font-bold text-xl" id="hospitalTransfers">0</h3> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Ambulance Status --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-yellow-100 dark:bg-yellow-900 text-yellow-600 dark:text-yellow-400"> | |
| <i data-feather="alert-triangle"></i> | |
| </div> | |
| <div class="mr-3"> | |
| <p class="text-gray-500 dark:text-gray-400 text-sm">وضعیت آمبولانس</p> | |
| <h3 class="font-bold text-xl" id="ambulanceStatus">عملیاتی</h3> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Recent Visits --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 lg:col-span-2"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="font-semibold">آخرین مراجعات</h3> | |
| <button onclick="showVisits()" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline">مشاهده همه</button> | |
| </div> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full"> | |
| <thead> | |
| <tr class="border-b border-gray-200 dark:border-gray-700"> | |
| <th class="text-right py-2 px-4">نام بیمار</th> | |
| <th class="text-right py-2 px-4">کد ملی</th> | |
| <th class="text-right py-2 px-4">واحد کاری</th> | |
| <th class="text-right py-2 px-4">تاریخ</th> | |
| </tr> | |
| </thead> | |
| <tbody id="recentVisits"> | |
| <!-- Will be populated by JS --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Medicine Stock --> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="font-semibold">موجودی داروها</h3> | |
| <button onclick="showPharmacy()" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline">مشاهده همه</button> | |
| </div> | |
| <div class="space-y-3"> | |
| <div> | |
| <div class="flex justify-between text-sm mb-1"> | |
| <span>پنیسیلین</span> | |
| <span>45 عدد</span> | |
| </div> | |
| <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2"> | |
| <div class="bg-indigo-600 h-2 rounded-full" style="width: 45%"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="flex justify-between text-sm mb-1"> | |
| <span>استامینوفن</span> | |
| <span>32 عدد</span> | |
| </div> | |
| <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2"> | |
| <div class="bg-indigo-600 h-2 rounded-full" style="width: 32%"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="flex justify-between text-sm mb-1"> | |
| <span>ایبوپروفن</span> | |
| <span>18 عدد</span> | |
| </div> | |
| <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2"> | |
| <div class="bg-indigo-600 h-2 rounded-full" style="width: 18%"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="flex justify-between text-sm mb-1"> | |
| <span>آنتیهیستامین</span> | |
| <span>56 عدد</span> | |
| </div> | |
| <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2"> | |
| <div class="bg-indigo-600 h-2 rounded-full" style="width: 56%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mt-6"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="font-semibold">آمار بیماریها در هفته جاری</h3> | |
| </div> | |
| <div class="h-64"> | |
| <canvas id="diseasesChart"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Patients Content --> | |
| <div id="patientsContent" class="fade-in hidden"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-xl font-semibold">پرونده بیماران</h2> | |
| <div class="flex space-x-2 space-x-reverse"> | |
| <button onclick="showAddPatientModal()" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded flex items-center"> | |
| <i data-feather="plus" class="ml-2"></i> | |
| <span>بیمار جدید</span> | |
| </button> | |
| <button onclick="showImportModal()" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded flex items-center"> | |
| <i data-feather="upload" class="ml-2"></i> | |
| <span>ورود از اکسل</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden"> | |
| <div class="p-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center"> | |
| <div class="relative max-w-xs"> | |
| <input type="text" id="patientSearch" placeholder="جستجوی بیمار..." class="pl-10 pr-4 py-2 border rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 w-full"> | |
| <i data-feather="search" class="absolute right-3 top-2.5 text-gray-400"></i> | |
| </div> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700"> | |
| <i data-feather="filter"></i> | |
| </button> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700"> | |
| <i data-feather="download"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> | |
| <thead class="bg-gray-50 dark:bg-gray-700"> | |
| <tr> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">نام و نام خانوادگی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">کد ملی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">کد پرسنلی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">واحد کاری</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">جنسیت</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">عملیات</th> | |
| </tr> | |
| </thead> | |
| <tbody id="patientsTableBody" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"> | |
| <!-- Will be populated by JS --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="p-4 border-t border-gray-200 dark:border-gray-700 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400"> | |
| نمایش <span id="patientStart">1</span> تا <span id="patientEnd">10</span> از <span id="patientTotal">0</span> بیمار | |
| </div> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50" id="patientPrevBtn" disabled> | |
| <i data-feather="chevron-right"></i> | |
| </button> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50" id="patientNextBtn" disabled> | |
| <i data-feather="chevron-left"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Examination Content --> | |
| <div id="examinationContent" class="fade-in hidden"> | |
| <h2 class="text-xl font-semibold mb-6">ثبت معاینه جدید</h2> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <form id="examinationForm"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <label for="patientSelect" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">بیمار</label> | |
| <select id="patientSelect" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <option value="">انتخاب بیمار...</option> | |
| <!-- Will be populated by JS --> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="examiner" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">معاینهکننده</label> | |
| <input type="text" id="examiner" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="examinationDate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">تاریخ و زمان</label> | |
| <input type="datetime-local" id="examinationDate" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="disease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">بیماری/عارضه</label> | |
| <div class="relative"> | |
| <input type="text" id="disease" list="diseaseList" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <datalist id="diseaseList"> | |
| <!-- Common diseases will be populated by JS --> | |
| </datalist> | |
| </div> | |
| </div> | |
| <div class="md:col-span-2"> | |
| <label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">شرح حال</label> | |
| <textarea id="description" rows="3" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"></textarea> | |
| </div> | |
| <div class="md:col-span-2"> | |
| <label for="actionsTaken" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">اقدامات انجام شده</label> | |
| <textarea id="actionsTaken" rows="3" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"></textarea> | |
| </div> | |
| <div class="md:col-span-2"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">داروهای تجویزی</label> | |
| <button type="button" onclick="addMedicine()" class="text-sm text-indigo-600 dark:text-indigo-400 hover:underline flex items-center"> | |
| <i data-feather="plus" class="ml-1 w-4 h-4"></i> | |
| افزودن دارو | |
| </button> | |
| </div> | |
| <div id="medicinesContainer" class="space-y-3"> | |
| <!-- Medicine items will be added here --> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex justify-end space-x-3 space-x-reverse"> | |
| <button type="reset" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700">لغو</button> | |
| <button type="submit" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg">ثبت معاینه</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Pharmacy Content --> | |
| <div id="pharmacyContent" class="fade-in hidden"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-xl font-semibold">داروخانه</h2> | |
| <div class="flex space-x-2 space-x-reverse"> | |
| <button onclick="showAddMedicineModal()" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded flex items-center"> | |
| <i data-feather="plus" class="ml-2"></i> | |
| <span>داروی جدید</span> | |
| </button> | |
| <button onclick="showImportMedicineModal()" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded flex items-center"> | |
| <i data-feather="upload" class="ml-2"></i> | |
| <span>ورود از اکسل</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden"> | |
| <div class="p-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center"> | |
| <div class="relative max-w-xs"> | |
| <input type="text" id="medicineSearch" placeholder="جستجوی دارو..." class="pl-10 pr-4 py-2 border rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 w-full"> | |
| <i data-feather="search" class="absolute right-3 top-2.5 text-gray-400"></i> | |
| </div> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700"> | |
| <i data-feather="filter"></i> | |
| </button> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700"> | |
| <i data-feather="download"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> | |
| <thead class="bg-gray-50 dark:bg-gray-700"> | |
| <tr> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">نام دارو</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">نوع دارو</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">واحد</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">دوز</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">موجودی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">تاریخ انقضا</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">عملیات</th> | |
| </tr> | |
| </thead> | |
| <tbody id="medicineTableBody" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"> | |
| <!-- Will be populated by JS --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="p-4 border-t border-gray-200 dark:border-gray-700 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400"> | |
| نمایش <span id="medicineStart">1</span> تا <span id="medicineEnd">10</span> از <span id="medicineTotal">0</span> دارو | |
| </div> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50" id="medicinePrevBtn" disabled> | |
| <i data-feather="chevron-right"></i> | |
| </button> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50" id="medicineNextBtn" disabled> | |
| <i data-feather="chevron-left"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <h3 class="font-semibold mb-4">توزیع داروها بر اساس نوع</h3> | |
| <div class="h-64"> | |
| <canvas id="medicineTypeChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <h3 class="font-semibold mb-4">داروهای در حال اتمام</h3> | |
| <div class="h-64"> | |
| <canvas id="lowStockChart"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Visits Content --> | |
| <div id="visitsContent" class="fade-in hidden"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-xl font-semibold">لیست مراجعات</h2> | |
| <div class="flex items-center space-x-3 space-x-reverse"> | |
| <div class="relative"> | |
| <input type="text" placeholder="جستجو..." class="pl-10 pr-4 py-2 border rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <i data-feather="search" class="absolute right-3 top-2.5 text-gray-400"></i> | |
| </div> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700"> | |
| <i data-feather="filter"></i> | |
| </button> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700"> | |
| <i data-feather="download"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden"> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> | |
| <thead class="bg-gray-50 dark:bg-gray-700"> | |
| <tr> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">نام بیمار</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">کد ملی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">کد پرسنلی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">واحد کاری</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">تاریخ مراجعه</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">بیماری/عارضه</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">داروهای تجویزی</th> | |
| <th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">عملیات</th> | |
| </tr> | |
| </thead> | |
| <tbody id="visitsTableBody" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"> | |
| <!-- Will be populated by JS --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="p-4 border-t border-gray-200 dark:border-gray-700 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500 dark:text-gray-400"> | |
| نمایش <span id="visitStart">1</span> تا <span id="visitEnd">10</span> از <span id="visitTotal">0</span> مراجعه | |
| </div> | |
| <div class="flex items-center space-x-2 space-x-reverse"> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50" id="visitPrevBtn" disabled> | |
| <i data-feather="chevron-right"></i> | |
| </button> | |
| <button class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50" id="visitNextBtn" disabled> | |
| <i data-feather="chevron-left"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Reports Content --> | |
| <div id="reportsContent" class="fade-in hidden"> | |
| <h2 class="text-xl font-semibold mb-6">گزارشها و آمار</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <h3 class="font-semibold mb-4">تعداد مراجعات در یک ماه اخیر</h3> | |
| <div class="h-64"> | |
| <canvas id="monthlyVisitsChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <h3 class="font-semibold mb-4">توزیع مراجعات بر اساس واحد کاری</h3> | |
| <div class="h-64"> | |
| <canvas id="departmentVisitsChart"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6"> | |
| <h3 class="font-semibold mb-4">بیماریهای شایع</h3> | |
| <div class="h-64"> | |
| <canvas id="commonDiseasesChart"></canvas> | |
| </div> | |
| </div> | |
| <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6"> | |
| <h3 class="font-semibold mb-4">وضعیت آمبولانسها</h3> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
| <div class="bg-green-50 dark:bg-green-900 rounded-lg p-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <p class="text-sm text-green-700 dark:text-green-300">آمبولانس 1</p> | |
| <h4 class="text-lg font-semibold text-green-800 dark:text-green-200">عملیاتی</h4> | |
| </div> | |
| <div class="p-2 rounded-full bg-green-100 dark:bg-green-800 text-green-600 dark:text-green-300"> | |
| <i data-feather="check-circle"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-yellow-50 dark:bg-yellow-900 rounded-lg p-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <p class="text-sm text-yellow-700 dark:text-yellow-300">آمبولانس 2</p> | |
| <h4 class="text-lg font-semibold text-yellow-800 dark:text-yellow-200">در تعمیرات</h4> | |
| </div> | |
| <div class="p-2 rounded-full bg-yellow-100 dark:bg-yellow-800 text-yellow-600 dark:text-yellow-300"> | |
| <i data-feather="alert-triangle"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-red-50 dark:bg-red-900 rounded-lg p-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <p class="text-sm text-red-700 dark:text-red-300">آمبولانس 3</p> | |
| <h4 class="text-lg font-semibold text-red-800 dark:text-red-200">نیاز به سرویس</h4> | |
| </div> | |
| <div class="p-2 rounded-full bg-red-100 dark:bg-red-800 text-red-600 dark:text-red-300"> | |
| <i data-feather="x-circle"></i> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <button onclick="showAmbulanceReportModal()" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg flex items-center"> | |
| <i data-feather="plus" class="ml-2"></i> | |
| <span>ثبت گزارش جدید</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| </div> | |
| <!-- Add Patient Modal --> | |
| <div id="addPatientModal" class="fixed inset-0 z-50 hidden overflow-y-auto"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> | |
| <div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-indigo-100 dark:bg-indigo-900 sm:mx-0 sm:h-10 sm:w-10"> | |
| <i data-feather="user-plus" class="text-indigo-600 dark:text-indigo-400"></i> | |
| </div> | |
| <div class="mt-3 text-center sm:mt-0 sm:mr-4 sm:text-right w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100"> | |
| ثبت بیمار جدید | |
| </h3> | |
| <div class="mt-4"> | |
| <form id="patientForm"> | |
| <div class="grid grid-cols-1 gap-4"> | |
| <div> | |
| <label for="firstName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">نام</label> | |
| <input type="text" id="firstName" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="lastName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">نام خانوادگی</label> | |
| <input type="text" id="lastName" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="nationalId" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">کد ملی</label> | |
| <input type="text" id="nationalId" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="employeeId" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">کد پرسنلی</label> | |
| <input type="text" id="employeeId" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="department" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">واحد کاری</label> | |
| <select id="department" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <option value="">انتخاب واحد...</option> | |
| <!-- Will be populated by JS --> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="workLocation" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">محل کار جزئی (اختیاری)</label> | |
| <input type="text" id="workLocation" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">جنسیت</label> | |
| <div class="flex space-x-4 space-x-reverse mt-1"> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="gender" value="male" class="text-indigo-600 dark:text-indigo-400 focus:ring-indigo-500 dark:focus:ring-indigo-600"> | |
| <span class="mr-2">مرد</span> | |
| </label> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="gender" value="female" class="text-indigo-600 dark:text-indigo-400 focus:ring-indigo-500 dark:focus:ring-indigo-600"> | |
| <span class="mr-2">زن</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="age" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">سن</label> | |
| <input type="number" id="age" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="phone" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">شماره تماس</label> | |
| <input type="tel" id="phone" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button type="button" onclick="savePatient()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm"> | |
| ثبت بیمار | |
| </button> | |
| <button type="button" onclick="closeModal('addPatientModal')" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-600 text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | |
| انصراف | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Import Modal --> | |
| <div id="importModal" class="fixed inset-0 z-50 hidden overflow-y-auto"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> | |
| <div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-green-100 dark:bg-green-900 sm:mx-0 sm:h-10 sm:w-10"> | |
| <i data-feather="upload" class="text-green-600 dark:text-green-400"></i> | |
| </div> | |
| <div class="mt-3 text-center sm:mt-0 sm:mr-4 sm:text-right w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100"> | |
| ورود اطلاعات از اکسل | |
| </h3> | |
| <div class="mt-4"> | |
| <div class="flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 dark:border-gray-600 border-dashed rounded-md"> | |
| <div class="space-y-1 text-center"> | |
| <div class="flex text-sm text-gray-600 dark:text-gray-300"> | |
| <label for="excelFile" class="relative cursor-pointer bg-white dark:bg-gray-800 rounded-md font-medium text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"> | |
| <span>آپلود فایل اکسل</span> | |
| <input id="excelFile" name="excelFile" type="file" class="sr-only" accept=".xlsx,.xls"> | |
| </label> | |
| <p class="mr-1">یا کشیدن و رها کردن</p> | |
| </div> | |
| <p class="text-xs text-gray-500 dark:text-gray-400"> | |
| XLS, XLSX تا 10MB | |
| </p> | |
| </div> | |
| </div> | |
| <div id="excelPreview" class="mt-4 hidden"> | |
| <h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">پیشنمایش دادهها</h4> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 border border-gray-200 dark:border-gray-700"> | |
| <thead class="bg-gray-50 dark:bg-gray-700"> | |
| <tr id="excelHeaders"> | |
| <!-- Will be populated by JS --> | |
| </tr> | |
| </thead> | |
| <tbody id="excelRows" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"> | |
| <!-- Will be populated by JS --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="mt-4"> | |
| <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">عملیات</label> | |
| <div class="space-y-2"> | |
| <div class="flex items-center"> | |
| <input id="addNewPatients" name="importAction" type="radio" value="add" checked class="focus:ring-indigo-500 dark:focus:ring-indigo-600 h-4 w-4 text-indigo-600 dark:text-indigo-400 border-gray-300 dark:border-gray-600"> | |
| <label for="addNewPatients" class="mr-2 block text-sm text-gray-700 dark:text-gray-300"> | |
| افزودن بیماران جدید | |
| </label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="updateExisting" name="importAction" type="radio" value="update" class="focus:ring-indigo-500 dark:focus:ring-indigo-600 h-4 w-4 text-indigo-600 dark:text-indigo-400 border-gray-300 dark:border-gray-600"> | |
| <label for="updateExisting" class="mr-2 block text-sm text-gray-700 dark:text-gray-300"> | |
| بهروزرسانی بیماران موجود | |
| </label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="replaceAll" name="importAction" type="radio" value="replace" class="focus:ring-indigo-500 dark:focus:ring-indigo-600 h-4 w-4 text-indigo-600 dark:text-indigo-400 border-gray-300 dark:border-gray-600"> | |
| <label for="replaceAll" class="mr-2 block text-sm text-gray-700 dark:text-gray-300"> | |
| جایگزینی همه بیماران | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button type="button" onclick="importPatients()" id="importBtn" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-base font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm hidden"> | |
| وارد کردن دادهها | |
| </button> | |
| <button type="button" onclick="closeModal('importModal')" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-600 text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | |
| انصراف | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add Medicine Modal --> | |
| <div id="addMedicineModal" class="fixed inset-0 z-50 hidden overflow-y-auto"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> | |
| <div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-indigo-100 dark:bg-indigo-900 sm:mx-0 sm:h-10 sm:w-10"> | |
| <i data-feather="package" class="text-indigo-600 dark:text-indigo-400"></i> | |
| </div> | |
| <div class="mt-3 text-center sm:mt-0 sm:mr-4 sm:text-right w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100"> | |
| ثبت داروی جدید | |
| </h3> | |
| <div class="mt-4"> | |
| <form id="medicineForm"> | |
| <div class="grid grid-cols-1 gap-4"> | |
| <div> | |
| <label for="medicineName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">نام دارو</label> | |
| <input type="text" id="medicineName" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="medicineType" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">نوع دارو</label> | |
| <select id="medicineType" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <option value="">انتخاب نوع...</option> | |
| <option value="قرص">قرص</option> | |
| <option value="کپسول">کپسول</option> | |
| <option value="آمپول">آمپول</option> | |
| <option value="شربت">شربت</option> | |
| <option value="پماد">پماد</option> | |
| <option value="قطره">قطره</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="medicineUnit" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">واحد شمارش</label> | |
| <select id="medicineUnit" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <option value="">انتخاب واحد...</option> | |
| <option value="عدد">عدد</option> | |
| <option value="بسته">بسته</option> | |
| <option value="شیشه">شیشه</option> | |
| <option value="تیوب">تیوب</option> | |
| <option value="بطری">بطری</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="medicineDose" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">دوز (اختیاری)</label> | |
| <input type="text" id="medicineDose" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="medicineQuantity" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">موجودی</label> | |
| <input type="number" id="medicineQuantity" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| <div> | |
| <label for="medicineExpiry" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">تاریخ انقضا</label> | |
| <input type="date" id="medicineExpiry" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| </div> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button type="button" onclick="saveMedicine()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm"> | |
| ثبت دارو | |
| </button> | |
| <button type="button" onclick="closeModal('addMedicineModal')" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-600 text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | |
| انصراف | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Ambulance Report Modal --> | |
| <div id="ambulanceReportModal" class="fixed inset-0 z-50 hidden overflow-y-auto"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> | |
| <div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 dark:bg-red-900 sm:mx-0 sm:h-10 sm:w-10"> | |
| <i data-feather="truck" class="text-red-600 dark:text-red-400"></i> | |
| </div> | |
| <div class="mt-3 text-center sm:mt-0 sm:mr-4 sm:text-right w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100"> | |
| ثبت گزارش آمبولانس | |
| </h3> | |
| <div class="mt-4"> | |
| <form id="ambulanceForm"> | |
| <div class="grid grid-cols-1 gap-4"> | |
| <div> | |
| <label for="ambulanceNumber" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">شماره آمبولانس</label> | |
| <select id="ambulanceNumber" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <option value="1">آمبولانس 1</option> | |
| <option value="2">آمبولانس 2</option> | |
| <option value="3">آمبولانس 3</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="ambulanceStatus" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">وضعیت</label> | |
| <select id="ambulanceStatus" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"> | |
| <option value="operational">عملیاتی</option> | |
| <option value="maintenance">در تعمیرات</option> | |
| <option value="needs_service">نیاز به سرویس</option> | |
| <option value="out_of_service">خارج از سرویس</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="ambulanceNotes" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">توضیحات (اختیاری)</label> | |
| <textarea id="ambulanceNotes" rows="3" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600"></textarea> | |
| </div> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button type="button" onclick="saveAmbulanceReport()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"> | |
| ثبت گزارش | |
| </button> | |
| <button type="button" onclick="closeModal('ambulanceReportModal')" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-600 text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | |
| انصراف | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- View Visit Modal --> | |
| <div id="viewVisitModal" class="fixed inset-0 z-50 hidden overflow-y-auto"> | |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | |
| <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | |
| <div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div> | |
| </div> | |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> | |
| <div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full"> | |
| <div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | |
| <div class="sm:flex sm:items-start"> | |
| <div class="mt-3 text-center sm:mt-0 sm:text-right w-full"> | |
| <h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100" id="visitModalTitle"> | |
| جزئیات مراجعه | |
| </h3> | |
| <div class="mt-4"> | |
| <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">بیمار:</p> | |
| <p class="font-medium" id="visitPatientName">-</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">کد ملی:</p> | |
| <p class="font-medium" id="visitNationalId">-</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">کد پرسنلی:</p> | |
| <p class="font-medium" id="visitEmployeeId">-</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">واحد کاری:</p> | |
| <p class="font-medium" id="visitDepartment">-</p> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">تاریخ و زمان:</p> | |
| <p class="font-medium" id="visitDate">-</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">معاینهکننده:</p> | |
| <p class="font-medium" id="visitExaminer">-</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">بیماری/عارضه:</p> | |
| <p class="font-medium" id="visitDisease">-</p> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">شرح حال:</p> | |
| <p class="font-medium" id="visitDescription">-</p> | |
| </div> | |
| <div class="mb-4"> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">اقدامات انجام شده:</p> | |
| <p class="font-medium" id="visitActions">-</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-500 dark:text-gray-400">داروهای تجویزی:</p> | |
| <ul class="list-disc mr-4 mt-2 space-y-1" id="visitMedicines"> | |
| <!-- Will be populated by JS --> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | |
| <button type="button" onclick="closeModal('viewVisitModal')" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm"> | |
| بستن | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Database setup | |
| let db; | |
| const dbName = "SugarCareDB"; | |
| const dbVersion = 1; | |
| // Open or create database | |
| const request = indexedDB.open(dbName, dbVersion); | |
| request.onerror = function(event) { | |
| console.error("Database error:", event.target.error); | |
| }; | |
| request.onupgradeneeded = function(event) { | |
| const db = event.target.result; | |
| // Create patients store | |
| const patientsStore = db.createObjectStore("patients", { keyPath: "nationalId" }); | |
| patientsStore.createIndex("employeeId", "employeeId", { unique: true }); | |
| patientsStore.createIndex("department", "department", { unique: false }); | |
| // Create medicines store | |
| const medicinesStore = db.createObjectStore("medicines", { keyPath: "name" }); | |
| medicinesStore.createIndex("type", "type", { unique: false }); | |
| medicinesStore.createIndex("expiry", "expiry", { unique: false }); | |
| // Create visits store | |
| const visitsStore = db.createObjectStore("visits", { keyPath: "id", autoIncrement: true }); | |
| visitsStore.createIndex("patientId", "patientId", { unique: false }); | |
| visitsStore.createIndex("date", "date", { unique: false }); | |
| // Create departments store (for predefined departments) | |
| const departmentsStore = db.createObjectStore("departments", { keyPath: "name" }); | |
| // Create common diseases store | |
| const diseasesStore = db.createObjectStore("diseases", { keyPath: "name" }); | |
| // Create ambulance reports store | |
| const ambulanceStore = db.createObjectStore("ambulances", { keyPath: "number" }); | |
| }; | |
| request.onsuccess = function(event) { | |
| db = event.target.result; | |
| console.log("Database opened successfully"); | |
| // Initialize data | |
| initializeData(); | |
| // Load initial data | |
| loadDashboardData(); | |
| loadPatients(); | |
| loadMedicines(); | |
| loadVisits(); | |
| // Set up event listeners | |
| setupEventListeners(); | |
| }; | |
| function initializeData() { | |
| // Predefined departments | |
| const departments = [ | |
| "معاونت کشاورزی", "مطالعات کاربردی", "مهندسی زراعی", "تولید یکم", "تولید دوم", | |
| "اداری", "بازرگانی", "حراست", "انبار", "کارخانه", "یارد", "تجهیزات مکانیکی", | |
| "دفتر فنی", "غیر نیشکری" | |
| ]; | |
| // Common diseases | |
| const diseases = [ | |
| "سرماخوردگی", "آنفلوآنزا", "تب", "سردرد", "درد عضلانی", "درد مفاصل", | |
| "حالت تهوع", "اسهال", "یبوست", "سوزش معده", "سرفه", "گلودرد", | |
| "تنگی نفس", "آلرژی", "خارش پوست", "جوش", "کمردرد", "درد گردن", | |
| "دست یا پا شکسته", "سوختگی", "بریدگی", "خونریزی", "سرگیجه", "ضعف", | |
| "بیخوابی", "اضطراب", "استرس", "مشکل بینایی", "مشکل شنوایی", "دندان درد" | |
| ]; | |
| // Common medicines | |
| const medicines = [ | |
| { name: "استامینوفن", type: "قرص", unit: "بسته", dose: "500mg", quantity: 100, expiry: "2024-12-31" }, | |
| { name: "ایبوپروفن", type: "قرص", unit: "بسته", dose: "400mg", quantity: 80, expiry: "2024-11-30" }, | |
| { name: "آنتیهیستامین", type: "قرص", unit: "بسته", dose: "10mg", quantity: 50, expiry: "2024-10-15" }, | |
| { name: "آموکسیسیلین", type: "کپسول", unit: "بسته", dose: "500mg", quantity: 30, expiry: "2024-09-30" }, | |
| { name: "پنیسیلین", type: "آمپول", unit: "عدد", dose: "1MIU", quantity: 20, expiry: "2024-08-31" }, | |
| { name: "دیفنهیدرامین", type: "آمپول", unit: "عدد", dose: "50mg", quantity: 15, expiry: "2024-07-31" }, | |
| { name: "ژلوفن", type: "کپسول", unit: "بسته", dose: "200mg", quantity: 40, expiry: "2024-06-30" }, | |
| { name: "کدئین", type: "قرص", unit: "بسته", dose: "30mg", quantity: 25, expiry: "2024-05-31" }, | |
| { name: "امپرازول", type: "کپسول", unit: "بسته", dose: "20mg", quantity: 35, expiry: "2024-04-30" }, | |
| { name: "متوکاربامول", type: "قرص", unit: "بسته", dose: "500mg", quantity: 45, expiry: "2024-03-31" } | |
| ]; | |
| // Ambulance initial status | |
| const ambulances = [ | |
| { number: "1", status: "operational", notes: "" }, | |
| { number: "2", status: "operational", notes: "" }, | |
| { number: "3", status: "operational", notes: "" } | |
| ]; | |
| // Add departments to database if not exists | |
| const tx = db.transaction(["departments", "diseases", "medicines", "ambulances"], "readwrite"); | |
| tx.oncomplete = function() { | |
| console.log("Initial data added successfully"); | |
| }; | |
| tx.onerror = function(event) { | |
| console.error("Error adding initial data:", event.target.error); | |
| }; | |
| const departmentsStore = tx.objectStore("departments"); | |
| departments.forEach(dept => { | |
| departmentsStore.put({ name: dept }); | |
| }); | |
| const diseasesStore = tx.objectStore("diseases"); | |
| diseases.forEach(disease => { | |
| diseasesStore.put({ name: disease }); | |
| }); | |
| const medicinesStore = tx.objectStore("medicines"); | |
| medicines.forEach(medicine => { | |
| medicinesStore.put(medicine); | |
| }); | |
| const ambulanceStore = tx.objectStore("ambulances"); | |
| ambulances.forEach(ambulance => { | |
| ambulanceStore.put(ambulance); | |
| }); | |
| } | |
| // UI Functions | |
| function setupEventListeners() { | |
| // Sidebar toggle | |
| document.getElementById('sidebarToggle').addEventListener('click', function() { | |
| document.getElementById('sidebar').classList.toggle('hidden'); | |
| document.getElementById('sidebar').classList.toggle('lg:flex'); | |
| }); | |
| // Dark mode toggle | |
| document.getElementById('darkModeToggle').addEventListener('click', function() { | |
| document.documentElement.classList.toggle('dark'); | |
| localStorage.setItem('darkMode', document.documentElement.classList.contains('dark')); | |
| }); | |
| // Check for saved dark mode preference | |
| if (localStorage.getItem('darkMode') === 'true') { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| // Excel file import | |
| document.getElementById('excelFile').addEventListener('change', handleExcelImport); | |
| } | |
| function showDashboard() { | |
| hideAllContent(); | |
| document.getElementById('dashboardContent').classList.remove('hidden'); | |
| document.getElementById('pageTitle').textContent = 'داشبورد'; | |
| loadDashboardData(); | |
| } | |
| function showPatients() { | |
| hideAllContent(); | |
| document.getElementById('patientsContent').classList.remove('hidden'); | |
| document.getElementById('pageTitle').textContent = 'پرونده بیماران'; | |
| loadPatients(); | |
| } | |
| function showExamination() { | |
| hideAllContent(); | |
| document.getElementById('examinationContent').classList.remove('hidden'); | |
| document.getElementById('pageTitle').textContent = 'ثبت معاینه جدید'; | |
| loadPatientsForExamination(); | |
| loadCommonDiseases(); | |
| } | |
| function showPharmacy() { | |
| hideAllContent(); | |
| document.getElementById('pharmacyContent').classList.remove('hidden'); | |
| document.getElementById('pageTitle').textContent = 'داروخانه'; | |
| loadMedicines(); | |
| loadMedicineCharts(); | |
| } | |
| function showVisits() { | |
| hideAllContent(); | |
| document.getElementById('visitsContent').classList.remove('hidden'); | |
| document.getElementById('pageTitle').textContent = 'لیست مراجعات'; | |
| loadVisits(); | |
| } | |
| function showReports() { | |
| hideAllContent(); | |
| document.getElementById('reportsContent').classList.remove('hidden'); | |
| document.getElementById('pageTitle').textContent = 'گزارشها و آمار'; | |
| loadReports(); | |
| } | |
| function hideAllContent() { | |
| const contents = document.querySelectorAll('[id$="Content"]'); | |
| contents.forEach(content => { | |
| content.classList.add('hidden'); | |
| }); | |
| } | |
| function showAddPatientModal() { | |
| document.getElementById('addPatientModal').classList.remove('hidden'); | |
| populateDepartments(); | |
| } | |
| function showImportModal() { | |
| document.getElementById('importModal').classList.remove('hidden'); | |
| document.getElementById('excelPreview').classList.add('hidden'); | |
| document.getElementById('importBtn').classList.add('hidden'); | |
| document.getElementById('excelFile').value = ''; | |
| } | |
| function showAddMedicineModal() { | |
| document.getElementById('addMedicineModal').classList.remove('hidden'); | |
| document.getElementById('medicineForm').reset(); | |
| } | |
| function showImportMedicineModal() { | |
| // Similar to showImportModal but for medicines | |
| alert("این ویژگی به زودی اضافه خواهد شد."); | |
| } | |
| function showAmbulanceReportModal() { | |
| document.getElementById('ambulanceReportModal').classList.remove('hidden'); | |
| } | |
| function closeModal(modalId) { | |
| document.getElementById(modalId).classList.add('hidden'); | |
| } | |
| function populateDepartments() { | |
| const departmentSelect = document.getElementById('department'); | |
| departmentSelect.innerHTML = '<option value="">انتخاب واحد...</option>'; | |
| const tx = db.transaction("departments", "readonly"); | |
| const store = tx.objectStore("departments"); | |
| const request = store.getAll(); | |
| request.onsuccess = function(event) { | |
| const departments = event.target.result; | |
| departments.forEach(dept => { | |
| const option = document.createElement('option'); | |
| option.value = dept.name; | |
| option.textContent = dept.name; | |
| departmentSelect.appendChild(option); | |
| }); | |
| }; | |
| } | |
| function loadPatientsForExamination() { | |
| const patientSelect = document.getElementById('patientSelect'); | |
| patientSelect.innerHTML = '<option value="">انتخاب بیمار...</option>'; | |
| const tx = db.transaction("patients", "readonly"); | |
| const store = tx.objectStore("patients"); | |
| const request = store.getAll(); | |
| request.onsuccess = function(event) { | |
| const patients = event.target.result; | |
| patients.forEach(patient => { | |
| const option = document.createElement('option'); | |
| option.value = patient.nationalId; | |
| option.textContent = `${patient.firstName} ${patient.lastName} - ${patient.nationalId}`; | |
| patientSelect.appendChild(option); | |
| }); | |
| }; | |
| } | |
| function loadCommonDiseases() { | |
| const diseaseList = document.getElementById('diseaseList'); | |
| diseaseList.innerHTML = ''; | |
| const tx = db.transaction("diseases", "readonly"); | |
| const store = tx.objectStore("diseases"); | |
| const request = store.getAll(); | |
| request.onsuccess = function(event) { | |
| const diseases = event.target.result; | |
| diseases.forEach(disease => { | |
| const option = document.createElement('option'); | |
| option.value = disease.name; | |
| diseaseList.appendChild(option); | |
| }); | |
| }; | |
| } | |
| function addMedicine() { | |
| const container = document.getElementById('medicinesContainer'); | |
| const medicineId = Date.now(); | |
| const medicineDiv = document.createElement('div'); | |
| medicineDiv.className = 'flex items-start gap-3'; | |
| medicineDiv.innerHTML = ` | |
| <div class="flex-1"> | |
| <input type="text" placeholder="نام دارو" list="medicineList" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 medicine-name"> | |
| </div> | |
| <div class="w-24"> | |
| <input type="number" placeholder="تعداد" class="w-full border rounded-lg py-2 px-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 medicine-quantity"> | |
| </div> | |
| <button type="button" onclick="this.parentElement.remove()" class="p-2 text-red-500 hover:text-red-700 dark:hover:text-red-400"> | |
| <i data-feather="trash-2" class="w-4 h-4"></i> | |
| </button> | |
| `; | |
| container.appendChild(medicineDiv); | |
| feather.replace(); | |
| // Populate medicine datalist if not already done | |
| if (!document.getElementById('medicineList')) { | |
| const datalist = document.createElement('datalist'); | |
| datalist.id = 'medicineList'; | |
| document.body.appendChild(datalist); | |
| const tx = db.transaction("medicines", "readonly"); | |
| const store = tx.objectStore("medicines"); | |
| const request = store.getAll(); | |
| request.onsuccess = function(event) { | |
| const medicines = event.target.result; | |
| datalist.innerHTML = ''; | |
| medicines.forEach(medicine => { | |
| const option = document.createElement('option'); | |
| option.value = medicine.name; | |
| datalist.appendChild(option); | |
| }); | |
| }; | |
| } | |
| } | |
| function savePatient() { | |
| const firstName = document.getElementById('firstName').value.trim(); | |
| const lastName = document.getElementById('lastName').value.trim(); | |
| const nationalId = document.getElementById('nationalId').value.trim(); | |
| const employeeId = document.getElementById('employeeId').value.trim(); | |
| const department = document.getElementById('department').value; | |
| const workLocation = document.getElementById('workLocation').value.trim(); | |
| const gender = document.querySelector('input[name="gender"]:checked')?.value || ''; | |
| const age = parseInt(document.getElementById('age').value) || 0; | |
| const phone = document.getElementById('phone').value.trim(); | |
| if (!firstName || !lastName || !nationalId || !employeeId || !department || !gender) { | |
| alert("لطفاً تمام فیلدهای ضروری را پر کنید."); | |
| return; | |
| } | |
| const patient = { | |
| firstName, | |
| lastName, | |
| nationalId, | |
| employeeId, | |
| department, | |
| workLocation, | |
| gender, | |
| age, | |
| phone | |
| }; | |
| const tx = db.transaction("patients", "readwrite"); | |
| const store = tx.objectStore("patients"); | |
| const request = store.put(patient); | |
| request.onsuccess = function() { | |
| alert("بیمار با موفقیت ثبت شد."); | |
| closeModal('addPatientModal'); | |
| loadPatients(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error saving patient:", event.target.error); | |
| alert("خطا در ثبت بیمار. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function saveMedicine() { | |
| const name = document.getElementById('medicineName').value.trim(); | |
| const type = document.getElementById('medicineType').value; | |
| const unit = document.getElementById('medicineUnit').value; | |
| const dose = document.getElementById('medicineDose').value.trim(); | |
| const quantity = parseInt(document.getElementById('medicineQuantity').value) || 0; | |
| const expiry = document.getElementById('medicineExpiry').value; | |
| if (!name || !type || !unit || !quantity || !expiry) { | |
| alert("لطفاً تمام فیلدهای ضروری را پر کنید."); | |
| return; | |
| } | |
| const medicine = { | |
| name, | |
| type, | |
| unit, | |
| dose, | |
| quantity, | |
| expiry | |
| }; | |
| const tx = db.transaction("medicines", "readwrite"); | |
| const store = tx.objectStore("medicines"); | |
| const request = store.put(medicine); | |
| request.onsuccess = function() { | |
| alert("دارو با موفقیت ثبت شد."); | |
| closeModal('addMedicineModal'); | |
| loadMedicines(); | |
| loadMedicineCharts(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error saving medicine:", event.target.error); | |
| alert("خطا در ثبت دارو. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function saveAmbulanceReport() { | |
| const number = document.getElementById('ambulanceNumber').value; | |
| const status = document.getElementById('ambulanceStatus').value; | |
| const notes = document.getElementById('ambulanceNotes').value.trim(); | |
| const report = { | |
| number, | |
| status, | |
| notes, | |
| reportedAt: new Date().toISOString() | |
| }; | |
| const tx = db.transaction("ambulances", "readwrite"); | |
| const store = tx.objectStore("ambulances"); | |
| const request = store.put(report); | |
| request.onsuccess = function() { | |
| alert("گزارش آمبولانس با موفقیت ثبت شد."); | |
| closeModal('ambulanceReportModal'); | |
| loadDashboardData(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error saving ambulance report:", event.target.error); | |
| alert("خطا در ثبت گزارش آمبولانس. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function handleExcelImport(event) { | |
| const file = event.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const data = new Uint8Array(e.target.result); | |
| const workbook = XLSX.read(data, { type: 'array' }); | |
| const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; | |
| const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 }); | |
| if (jsonData.length < 2) { | |
| alert("فایل اکسل انتخاب شده حاوی دادهای نیست."); | |
| return; | |
| } | |
| // Extract headers and data | |
| const headers = jsonData[0]; | |
| const rows = jsonData.slice(1); | |
| // Show preview | |
| const previewDiv = document.getElementById('excelPreview'); | |
| const headersRow = document.getElementById('excelHeaders'); | |
| const rowsBody = document.getElementById('excelRows'); | |
| headersRow.innerHTML = ''; | |
| rowsBody.innerHTML = ''; | |
| // Add headers | |
| headers.forEach(header => { | |
| const th = document.createElement('th'); | |
| th.className = 'px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider'; | |
| th.textContent = header || 'بدون عنوان'; | |
| headersRow.appendChild(th); | |
| }); | |
| // Add first 5 rows as preview | |
| rows.slice(0, 5).forEach(row => { | |
| const tr = document.createElement('tr'); | |
| headers.forEach((header, index) => { | |
| const td = document.createElement('td'); | |
| td.className = 'px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300'; | |
| td.textContent = row[index] || '-'; | |
| tr.appendChild(td); | |
| }); | |
| rowsBody.appendChild(tr); | |
| }); | |
| previewDiv.classList.remove('hidden'); | |
| document.getElementById('importBtn').classList.remove('hidden'); | |
| // Store the parsed data for later use | |
| document.getElementById('importBtn').dataset.excelData = JSON.stringify({ | |
| headers, | |
| rows | |
| }); | |
| }; | |
| reader.readAsArrayBuffer(file); | |
| } | |
| function importPatients() { | |
| const excelData = JSON.parse(document.getElementById('importBtn').dataset.excelData); | |
| const { headers, rows } = excelData; | |
| const action = document.querySelector('input[name="importAction"]:checked').value; | |
| // Map headers to patient fields (simple heuristic) | |
| const fieldMap = { | |
| 'نام': 'firstName', | |
| 'نام خانوادگی': 'lastName', | |
| 'کد ملی': 'nationalId', | |
| 'کد پرسنلی': 'employeeId', | |
| 'واحد کاری': 'department', | |
| 'محل کار جزئی': 'workLocation', | |
| 'جنسیت': 'gender', | |
| 'سن': 'age', | |
| 'شماره تماس': 'phone' | |
| }; | |
| const patients = rows.map(row => { | |
| const patient = {}; | |
| headers.forEach((header, index) => { | |
| if (fieldMap[header] && row[index]) { | |
| patient[fieldMap[header]] = row[index]; | |
| } | |
| }); | |
| return patient; | |
| }).filter(p => p.nationalId); // Only keep patients with national ID | |
| if (patients.length === 0) { | |
| alert("هیچ بیمار معتبری در فایل یافت نشد. لطفاً از تطابق نام ستونها اطمینان حاصل کنید."); | |
| return; | |
| } | |
| const tx = db.transaction("patients", "readwrite"); | |
| const store = tx.objectStore("patients"); | |
| if (action === 'replace') { | |
| // Clear all patients first | |
| const clearRequest = store.clear(); | |
| clearRequest.onsuccess = function() { | |
| addPatients(store, patients); | |
| }; | |
| } else { | |
| addPatients(store, patients, action === 'update'); | |
| } | |
| function addPatients(store, patients, updateOnly = false) { | |
| let added = 0; | |
| let updated = 0; | |
| patients.forEach(patient => { | |
| const request = store.get(patient.nationalId); | |
| request.onsuccess = function(e) { | |
| const existing = e.target.result; | |
| if (existing) { | |
| if (updateOnly) { | |
| // Update existing patient | |
| const updatedPatient = { ...existing, ...patient }; | |
| store.put(updatedPatient); | |
| updated++; | |
| } | |
| } else { | |
| // Add new patient | |
| store.add(patient); | |
| added++; | |
| } | |
| }; | |
| }); | |
| tx.oncomplete = function() { | |
| alert(`عملیات وارد کردن دادهها با موفقیت انجام شد.\n${added} بیمار جدید اضافه شد.\n${updated} بیمار بهروزرسانی شد.`); | |
| closeModal('importModal'); | |
| loadPatients(); | |
| }; | |
| tx.onerror = function(event) { | |
| console.error("Error during import:", event.target.error); | |
| alert("خطا در وارد کردن دادهها. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| } | |
| function loadDashboardData() { | |
| // Today's date for filtering | |
| const today = new Date(); | |
| today.setHours(0, 0, 0, 0); | |
| const todayISO = today.toISOString(); | |
| const weekAgo = new Date(today); | |
| weekAgo.setDate(weekAgo.getDate() - 7); | |
| const weekAgoISO = weekAgo.toISOString(); | |
| // Count today's visits | |
| const visitsTx = db.transaction("visits", "readonly"); | |
| const visitsStore = visitsTx.objectStore("visits"); | |
| const dateIndex = visitsStore.index("date"); | |
| // Today's visits | |
| const todayRange = IDBKeyRange.lowerBound(todayISO); | |
| const todayRequest = dateIndex.count(todayRange); | |
| todayRequest.onsuccess = function() { | |
| document.getElementById('todayVisits').textContent = todayRequest.result; | |
| }; | |
| // Weekly visits | |
| const weeklyRange = IDBKeyRange.lowerBound(weekAgoISO); | |
| const weeklyRequest = dateIndex.count(weeklyRange); | |
| weeklyRequest.onsuccess = function() { | |
| document.getElementById('weeklyVisits').textContent = weeklyRequest.result; | |
| }; | |
| // Hospital transfers (visits with 'اعزام' in actions) | |
| // This is simplified - you might want a better way to track hospital transfers | |
| const allVisitsRequest = visitsStore.getAll(); | |
| allVisitsRequest.onsuccess = function() { | |
| const visits = allVisitsRequest.result; | |
| const hospitalTransfers = visits.filter(v => v.actionsTaken && v.actionsTaken.includes('اعزام')).length; | |
| document.getElementById('hospitalTransfers').textContent = hospitalTransfers; | |
| }; | |
| // Ambulance status | |
| const ambulanceTx = db.transaction("ambulances", "readonly"); | |
| const ambulanceStore = ambulanceTx.objectStore("ambulances"); | |
| const ambulanceRequest = ambulanceStore.getAll(); | |
| ambulanceRequest.onsuccess = function() { | |
| const ambulances = ambulanceRequest.result; | |
| const operational = ambulances.filter(a => a.status === 'operational').length; | |
| const total = ambulances.length; | |
| if (operational === total) { | |
| document.getElementById('ambulanceStatus').textContent = 'عملیاتی'; | |
| } else { | |
| document.getElementById('ambulanceStatus').textContent = `${operational}/${total} عملیاتی`; | |
| } | |
| }; | |
| // Recent visits | |
| const recentVisitsRequest = visitsStore.getAll(); | |
| recentVisitsRequest.onsuccess = function() { | |
| const visits = recentVisitsRequest.result | |
| .sort((a, b) => new Date(b.date) - new Date(a.date)) | |
| .slice(0, 5); | |
| const recentVisitsBody = document.getElementById('recentVisits'); | |
| recentVisitsBody.innerHTML = ''; | |
| visits.forEach(visit => { | |
| const tr = document.createElement('tr'); | |
| // Get patient details | |
| const patientTx = db.transaction("patients", "readonly"); | |
| const patientStore = patientTx.objectStore("patients"); | |
| const patientRequest = patientStore.get(visit.patientId); | |
| patientRequest.onsuccess = function() { | |
| const patient = patientRequest.result; | |
| tr.innerHTML = ` | |
| <td class="py-2 px-4">${patient ? `${patient.firstName} ${patient.lastName}` : 'نامشخص'}</td> | |
| <td class="py-2 px-4">${patient ? patient.nationalId : 'نامشخص'}</td> | |
| <td class="py-2 px-4">${patient ? patient.department : 'نامشخص'}</td> | |
| <td class="py-2 px-4">${new Date(visit.date).toLocaleString('fa-IR')}</td> | |
| `; | |
| recentVisitsBody.appendChild(tr); | |
| }; | |
| }); | |
| }; | |
| // Diseases chart | |
| loadDiseasesChart(); | |
| } | |
| function loadPatients(page = 1, pageSize = 10, search = '') { | |
| const tx = db.transaction("patients", "readonly"); | |
| const store = tx.objectStore("patients"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| let patients = request.result; | |
| // Apply search filter | |
| if (search) { | |
| patients = patients.filter(p => | |
| `${p.firstName} ${p.lastName}`.includes(search) || | |
| p.nationalId.includes(search) || | |
| p.employeeId.includes(search) | |
| ); | |
| } | |
| // Pagination | |
| const total = patients.length; | |
| const start = (page - 1) * pageSize; | |
| const end = start + pageSize; | |
| const paginatedPatients = patients.slice(start, end); | |
| // Update table | |
| const tbody = document.getElementById('patientsTableBody'); | |
| tbody.innerHTML = ''; | |
| paginatedPatients.forEach(patient => { | |
| const tr = document.createElement('tr'); | |
| tr.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-200"> | |
| ${patient.firstName} ${patient.lastName} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient.nationalId} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient.employeeId} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient.department} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient.gender === 'male' ? 'مرد' : 'زن'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
| <button onclick="editPatient('${patient.nationalId}')" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-500 ml-2"> | |
| <i data-feather="edit" class="w-4 h-4"></i> | |
| </button> | |
| <button onclick="deletePatient('${patient.nationalId}')" class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-500"> | |
| <i data-feather="trash-2" class="w-4 h-4"></i> | |
| </button> | |
| </td> | |
| `; | |
| tbody.appendChild(tr); | |
| }); | |
| // Update pagination info | |
| document.getElementById('patientStart').textContent = start + 1; | |
| document.getElementById('patientEnd').textContent = Math.min(end, total); | |
| document.getElementById('patientTotal').textContent = total; | |
| // Update pagination buttons | |
| document.getElementById('patientPrevBtn').disabled = page <= 1; | |
| document.getElementById('patientNextBtn').disabled = end >= total; | |
| // Set up event listeners for pagination buttons | |
| document.getElementById('patientPrevBtn').onclick = () => loadPatients(page - 1, pageSize, search); | |
| document.getElementById('patientNextBtn').onclick = () => loadPatients(page + 1, pageSize, search); | |
| feather.replace(); | |
| }; | |
| } | |
| function loadMedicines(page = 1, pageSize = 10, search = '') { | |
| const tx = db.transaction("medicines", "readonly"); | |
| const store = tx.objectStore("medicines"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| let medicines = request.result; | |
| // Apply search filter | |
| if (search) { | |
| medicines = medicines.filter(m => | |
| m.name.includes(search) || | |
| m.type.includes(search) | |
| ); | |
| } | |
| // Pagination | |
| const total = medicines.length; | |
| const start = (page - 1) * pageSize; | |
| const end = start + pageSize; | |
| const paginatedMedicines = medicines.slice(start, end); | |
| // Update table | |
| const tbody = document.getElementById('medicineTableBody'); | |
| tbody.innerHTML = ''; | |
| paginatedMedicines.forEach(medicine => { | |
| const tr = document.createElement('tr'); | |
| const expiryDate = new Date(medicine.expiry); | |
| const today = new Date(); | |
| today.setHours(0, 0, 0, 0); | |
| // Check if expired or near expiry (within 30 days) | |
| const expiryClass = expiryDate < today ? 'text-red-600 dark:text-red-400' : | |
| (expiryDate <= new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000) ? 'text-yellow-600 dark:text-yellow-400' : ''); | |
| // Check for low stock (less than 20%) | |
| const lowStockClass = medicine.quantity < 20 ? 'text-red-600 dark:text-red-400' : | |
| (medicine.quantity < 50 ? 'text-yellow-600 dark:text-yellow-400' : ''); | |
| tr.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-200"> | |
| ${medicine.name} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${medicine.type} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${medicine.unit} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${medicine.dose || '-'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm ${lowStockClass}"> | |
| ${medicine.quantity} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm ${expiryClass}"> | |
| ${new Date(medicine.expiry).toLocaleDateString('fa-IR')} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
| <button onclick="editMedicine('${medicine.name}')" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-500 ml-2"> | |
| <i data-feather="edit" class="w-4 h-4"></i> | |
| </button> | |
| <button onclick="deleteMedicine('${medicine.name}')" class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-500"> | |
| <i data-feather="trash-2" class="w-4 h-4"></i> | |
| </button> | |
| </td> | |
| `; | |
| tbody.appendChild(tr); | |
| }); | |
| // Update pagination info | |
| document.getElementById('medicineStart').textContent = start + 1; | |
| document.getElementById('medicineEnd').textContent = Math.min(end, total); | |
| document.getElementById('medicineTotal').textContent = total; | |
| // Update pagination buttons | |
| document.getElementById('medicinePrevBtn').disabled = page <= 1; | |
| document.getElementById('medicineNextBtn').disabled = end >= total; | |
| // Set up event listeners for pagination buttons | |
| document.getElementById('medicinePrevBtn').onclick = () => loadMedicines(page - 1, pageSize, search); | |
| document.getElementById('medicineNextBtn').onclick = () => loadMedicines(page + 1, pageSize, search); | |
| feather.replace(); | |
| }; | |
| } | |
| function loadVisits(page = 1, pageSize = 10) { | |
| const tx = db.transaction("visits", "readonly"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| const visits = request.result.sort((a, b) => new Date(b.date) - new Date(a.date)); | |
| // Pagination | |
| const total = visits.length; | |
| const start = (page - 1) * pageSize; | |
| const end = start + pageSize; | |
| const paginatedVisits = visits.slice(start, end); | |
| // Update table | |
| const tbody = document.getElementById('visitsTableBody'); | |
| tbody.innerHTML = ''; | |
| paginatedVisits.forEach(visit => { | |
| const tr = document.createElement('tr'); | |
| // Get patient details | |
| const patientTx = db.transaction("patients", "readonly"); | |
| const patientStore = patientTx.objectStore("patients"); | |
| const patientRequest = patientStore.get(visit.patientId); | |
| patientRequest.onsuccess = function() { | |
| const patient = patientRequest.result; | |
| tr.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-200"> | |
| ${patient ? `${patient.firstName} ${patient.lastName}` : 'نامشخص'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient ? patient.nationalId : 'نامشخص'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient ? patient.employeeId : 'نامشخص'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${patient ? patient.department : 'نامشخص'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${new Date(visit.date).toLocaleString('fa-IR')} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${visit.disease} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-300"> | |
| ${visit.medicines ? visit.medicines.map(m => m.name).join(', ') : '-'} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
| <button onclick="viewVisit(${visit.id})" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-500 ml-2"> | |
| <i data-feather="eye" class="w-4 h-4"></i> | |
| </button> | |
| <button onclick="editVisit(${visit.id})" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-500 ml-2"> | |
| <i data-feather="edit" class="w-4 h-4"></i> | |
| </button> | |
| <button onclick="deleteVisit(${visit.id})" class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-500"> | |
| <i data-feather="trash-2" class="w-4 h-4"></i> | |
| </button> | |
| </td> | |
| `; | |
| tbody.appendChild(tr); | |
| }; | |
| }); | |
| // Update pagination info | |
| document.getElementById('visitStart').textContent = start + 1; | |
| document.getElementById('visitEnd').textContent = Math.min(end, total); | |
| document.getElementById('visitTotal').textContent = total; | |
| // Update pagination buttons | |
| document.getElementById('visitPrevBtn').disabled = page <= 1; | |
| document.getElementById('visitNextBtn').disabled = end >= total; | |
| // Set up event listeners for pagination buttons | |
| document.getElementById('visitPrevBtn').onclick = () => loadVisits(page - 1, pageSize); | |
| document.getElementById('visitNextBtn').onclick = () => loadVisits(page + 1, pageSize); | |
| feather.replace(); | |
| }; | |
| } | |
| function loadReports() { | |
| // Monthly visits chart | |
| loadMonthlyVisitsChart(); | |
| // Department visits chart | |
| loadDepartmentVisitsChart(); | |
| // Common diseases chart | |
| loadCommonDiseasesChart(); | |
| } | |
| function viewVisit(visitId) { | |
| const tx = db.transaction("visits", "readonly"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.get(visitId); | |
| request.onsuccess = function() { | |
| const visit = request.result; | |
| // Get patient details | |
| const patientTx = db.transaction("patients", "readonly"); | |
| const patientStore = patientTx.objectStore("patients"); | |
| const patientRequest = patientStore.get(visit.patientId); | |
| patientRequest.onsuccess = function() { | |
| const patient = patientRequest.result; | |
| // Populate modal | |
| document.getElementById('visitPatientName').textContent = patient ? `${patient.firstName} ${patient.lastName}` : 'نامشخص'; | |
| document.getElementById('visitNationalId').textContent = patient ? patient.nationalId : 'نامشخص'; | |
| document.getElementById('visitEmployeeId').textContent = patient ? patient.employeeId : 'نامشخص'; | |
| document.getElementById('visitDepartment').textContent = patient ? patient.department : 'نامشخص'; | |
| document.getElementById('visitDate').textContent = new Date(visit.date).toLocaleString('fa-IR'); | |
| document.getElementById('visitExaminer').textContent = visit.examiner || 'نامشخص'; | |
| document.getElementById('visitDisease').textContent = visit.disease || 'نامشخص'; | |
| document.getElementById('visitDescription').textContent = visit.description || '-'; | |
| document.getElementById('visitActions').textContent = visit.actionsTaken || '-'; | |
| // Populate medicines | |
| const medicinesList = document.getElementById('visitMedicines'); | |
| medicinesList.innerHTML = ''; | |
| if (visit.medicines && visit.medicines.length > 0) { | |
| visit.medicines.forEach(medicine => { | |
| const li = document.createElement('li'); | |
| li.textContent = `${medicine.name} (${medicine.quantity} ${medicine.unit})`; | |
| medicinesList.appendChild(li); | |
| }); | |
| } else { | |
| const li = document.createElement('li'); | |
| li.textContent = '-'; | |
| medicinesList.appendChild(li); | |
| } | |
| // Show modal | |
| document.getElementById('viewVisitModal').classList.remove('hidden'); | |
| }; | |
| }; | |
| } | |
| function editPatient(nationalId) { | |
| const tx = db.transaction("patients", "readonly"); | |
| const store = tx.objectStore("patients"); | |
| const request = store.get(nationalId); | |
| request.onsuccess = function() { | |
| const patient = request.result; | |
| // Populate form | |
| document.getElementById('firstName').value = patient.firstName; | |
| document.getElementById('lastName').value = patient.lastName; | |
| document.getElementById('nationalId').value = patient.nationalId; | |
| document.getElementById('employeeId').value = patient.employeeId; | |
| document.getElementById('department').value = patient.department; | |
| document.getElementById('workLocation').value = patient.workLocation; | |
| document.querySelector(`input[name="gender"][value="${patient.gender}"]`).checked = true; | |
| document.getElementById('age').value = patient.age; | |
| document.getElementById('phone').value = patient.phone; | |
| // Change save function to update | |
| document.querySelector('#addPatientModal button[onclick="savePatient()"]').onclick = function() { | |
| updatePatient(nationalId); | |
| }; | |
| // Show modal | |
| document.getElementById('addPatientModal').classList.remove('hidden'); | |
| }; | |
| } | |
| function updatePatient(nationalId) { | |
| const firstName = document.getElementById('firstName').value.trim(); | |
| const lastName = document.getElementById('lastName').value.trim(); | |
| const employeeId = document.getElementById('employeeId').value.trim(); | |
| const department = document.getElementById('department').value; | |
| const workLocation = document.getElementById('workLocation').value.trim(); | |
| const gender = document.querySelector('input[name="gender"]:checked')?.value || ''; | |
| const age = parseInt(document.getElementById('age').value) || 0; | |
| const phone = document.getElementById('phone').value.trim(); | |
| if (!firstName || !lastName || !employeeId || !department || !gender) { | |
| alert("لطفاً تمام فیلدهای ضروری را پر کنید."); | |
| return; | |
| } | |
| const patient = { | |
| firstName, | |
| lastName, | |
| nationalId, | |
| employeeId, | |
| department, | |
| workLocation, | |
| gender, | |
| age, | |
| phone | |
| }; | |
| const tx = db.transaction("patients", "readwrite"); | |
| const store = tx.objectStore("patients"); | |
| const request = store.put(patient); | |
| request.onsuccess = function() { | |
| alert("اطلاعات بیمار با موفقیت بهروزرسانی شد."); | |
| closeModal('addPatientModal'); | |
| loadPatients(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error updating patient:", event.target.error); | |
| alert("خطا در بهروزرسانی بیمار. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function deletePatient(nationalId) { | |
| if (!confirm("آیا از حذف این بیمار مطمئن هستید؟")) return; | |
| const tx = db.transaction("patients", "readwrite"); | |
| const store = tx.objectStore("patients"); | |
| const request = store.delete(nationalId); | |
| request.onsuccess = function() { | |
| alert("بیمار با موفقیت حذف شد."); | |
| loadPatients(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error deleting patient:", event.target.error); | |
| alert("خطا در حذف بیمار. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function editMedicine(medicineName) { | |
| const tx = db.transaction("medicines", "readonly"); | |
| const store = tx.objectStore("medicines"); | |
| const request = store.get(medicineName); | |
| request.onsuccess = function() { | |
| const medicine = request.result; | |
| // Populate form | |
| document.getElementById('medicineName').value = medicine.name; | |
| document.getElementById('medicineType').value = medicine.type; | |
| document.getElementById('medicineUnit').value = medicine.unit; | |
| document.getElementById('medicineDose').value = medicine.dose || ''; | |
| document.getElementById('medicineQuantity').value = medicine.quantity; | |
| document.getElementById('medicineExpiry').value = medicine.expiry; | |
| // Change save function to update | |
| document.querySelector('#addMedicineModal button[onclick="saveMedicine()"]').onclick = function() { | |
| updateMedicine(medicineName); | |
| }; | |
| // Show modal | |
| document.getElementById('addMedicineModal').classList.remove('hidden'); | |
| }; | |
| } | |
| function updateMedicine(medicineName) { | |
| const name = document.getElementById('medicineName').value.trim(); | |
| const type = document.getElementById('medicineType').value; | |
| const unit = document.getElementById('medicineUnit').value; | |
| const dose = document.getElementById('medicineDose').value.trim(); | |
| const quantity = parseInt(document.getElementById('medicineQuantity').value) || 0; | |
| const expiry = document.getElementById('medicineExpiry').value; | |
| if (!name || !type || !unit || !quantity || !expiry) { | |
| alert("لطفاً تمام فیلدهای ضروری را پر کنید."); | |
| return; | |
| } | |
| const medicine = { | |
| name, | |
| type, | |
| unit, | |
| dose, | |
| quantity, | |
| expiry | |
| }; | |
| const tx = db.transaction("medicines", "readwrite"); | |
| const store = tx.objectStore("medicines"); | |
| // If name changed, we need to delete old record and add new one | |
| if (name !== medicineName) { | |
| const deleteRequest = store.delete(medicineName); | |
| deleteRequest.onsuccess = function() { | |
| const addRequest = store.add(medicine); | |
| addRequest.onsuccess = function() { | |
| alert("دارو با موفقیت بهروزرسانی شد."); | |
| closeModal('addMedicineModal'); | |
| loadMedicines(); | |
| loadMedicineCharts(); | |
| }; | |
| addRequest.onerror = function(event) { | |
| console.error("Error adding updated medicine:", event.target.error); | |
| alert("خطا در بهروزرسانی دارو. لطفاً دوباره امتحان کنید."); | |
| }; | |
| }; | |
| } else { | |
| const request = store.put(medicine); | |
| request.onsuccess = function() { | |
| alert("دارو با موفقیت بهروزرسانی شد."); | |
| closeModal('addMedicineModal'); | |
| loadMedicines(); | |
| loadMedicineCharts(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error updating medicine:", event.target.error); | |
| alert("خطا در بهروزرسانی دارو. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| } | |
| function deleteMedicine(medicineName) { | |
| if (!confirm("آیا از حذف این دارو مطمئن هستید؟")) return; | |
| const tx = db.transaction("medicines", "readwrite"); | |
| const store = tx.objectStore("medicines"); | |
| const request = store.delete(medicineName); | |
| request.onsuccess = function() { | |
| alert("دارو با موفقیت حذف شد."); | |
| loadMedicines(); | |
| loadMedicineCharts(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error deleting medicine:", event.target.error); | |
| alert("خطا در حذف دارو. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function editVisit(visitId) { | |
| // Similar to viewVisit but with editable form | |
| alert("این ویژگی به زودی اضافه خواهد شد."); | |
| } | |
| function deleteVisit(visitId) { | |
| if (!confirm("آیا از حذف این مراجعه مطمئن هستید؟")) return; | |
| const tx = db.transaction("visits", "readwrite"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.delete(visitId); | |
| request.onsuccess = function() { | |
| alert("مراجعه با موفقیت حذف شد."); | |
| loadVisits(); | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error deleting visit:", event.target.error); | |
| alert("خطا در حذف مراجعه. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| // Chart functions | |
| function loadDiseasesChart() { | |
| const ctx = document.getElementById('diseasesChart').getContext('2d'); | |
| const tx = db.transaction("visits", "readonly"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| const visits = request.result; | |
| // Count diseases (simple example) | |
| const diseaseCount = {}; | |
| visits.forEach(visit => { | |
| if (visit.disease) { | |
| diseaseCount[visit.disease] = (diseaseCount[visit.disease] || 0) + 1; | |
| } | |
| }); | |
| // Get top 5 diseases | |
| const topDiseases = Object.entries(diseaseCount) | |
| .sort((a, b) => b[1] - a[1]) | |
| .slice(0, 5); | |
| const labels = topDiseases.map(d => d[0]); | |
| const data = topDiseases.map(d => d[1]); | |
| new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| label: 'تعداد مراجعات', | |
| data: data, | |
| backgroundColor: '#4f46e5', | |
| borderColor: '#4f46e5', | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| scales: { | |
| y: { | |
| beginAtZero: true | |
| } | |
| }, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| } | |
| } | |
| }); | |
| }; | |
| } | |
| function loadMedicineCharts() { | |
| // Medicine type distribution chart | |
| const typeCtx = document.getElementById('medicineTypeChart').getContext('2d'); | |
| const tx = db.transaction("medicines", "readonly"); | |
| const store = tx.objectStore("medicines"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| const medicines = request.result; | |
| // Count by type | |
| const typeCount = {}; | |
| medicines.forEach(medicine => { | |
| typeCount[medicine.type] = (typeCount[medicine.type] || 0) + 1; | |
| }); | |
| new Chart(typeCtx, { | |
| type: 'pie', | |
| data: { | |
| labels: Object.keys(typeCount), | |
| datasets: [{ | |
| data: Object.values(typeCount), | |
| backgroundColor: [ | |
| '#4f46e5', | |
| '#10b981', | |
| '#f59e0b', | |
| '#ef4444', | |
| '#8b5cf6', | |
| '#ec4899' | |
| ] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| position: 'bottom' | |
| } | |
| } | |
| } | |
| }); | |
| // Low stock chart | |
| const lowStockCtx = document.getElementById('lowStockChart').getContext('2d'); | |
| // Sort by quantity ascending and take top 5 | |
| const lowStock = medicines | |
| .sort((a, b) => a.quantity - b.quantity) | |
| .slice(0, 5); | |
| new Chart(lowStockCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: lowStock.map(m => m.name), | |
| datasets: [{ | |
| label: 'موجودی', | |
| data: lowStock.map(m => m.quantity), | |
| backgroundColor: lowStock.map(m => | |
| m.quantity < 20 ? '#ef4444' : | |
| m.quantity < 50 ? '#f59e0b' : '#10b981' | |
| ) | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| scales: { | |
| y: { | |
| beginAtZero: true | |
| } | |
| }, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| } | |
| } | |
| }); | |
| }; | |
| } | |
| function loadMonthlyVisitsChart() { | |
| const ctx = document.getElementById('monthlyVisitsChart').getContext('2d'); | |
| const tx = db.transaction("visits", "readonly"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| const visits = request.result; | |
| // Group by day for last 30 days | |
| const now = new Date(); | |
| const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); | |
| const dailyCount = {}; | |
| for (let i = 0; i < 30; i++) { | |
| const date = new Date(thirtyDaysAgo.getTime() + i * 24 * 60 * 60 * 1000); | |
| const dateStr = date.toISOString().split('T')[0]; | |
| dailyCount[dateStr] = 0; | |
| } | |
| visits.forEach(visit => { | |
| const visitDate = new Date(visit.date); | |
| if (visitDate >= thirtyDaysAgo) { | |
| const dateStr = visitDate.toISOString().split('T')[0]; | |
| if (dailyCount[dateStr] !== undefined) { | |
| dailyCount[dateStr]++; | |
| } | |
| } | |
| }); | |
| // Persian date labels | |
| const persianMonths = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند']; | |
| const labels = Object.keys(dailyCount).map(dateStr => { | |
| const date = new Date(dateStr); | |
| return `${date.getDate()} ${persianMonths[date.getMonth()]}`; | |
| }); | |
| const data = Object.values(dailyCount); | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| label: 'تعداد مراجعات', | |
| data: data, | |
| fill: false, | |
| borderColor: '#4f46e5', | |
| tension: 0.1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true | |
| } | |
| } | |
| } | |
| }); | |
| }; | |
| } | |
| function loadDepartmentVisitsChart() { | |
| const ctx = document.getElementById('departmentVisitsChart').getContext('2d'); | |
| const visitsTx = db.transaction("visits", "readonly"); | |
| const visitsStore = visitsTx.objectStore("visits"); | |
| const visitsRequest = visitsStore.getAll(); | |
| visitsRequest.onsuccess = function() { | |
| const visits = visitsRequest.result; | |
| // Get all departments | |
| const deptTx = db.transaction("departments", "readonly"); | |
| const deptStore = deptTx.objectStore("departments"); | |
| const deptRequest = deptStore.getAll(); | |
| deptRequest.onsuccess = function() { | |
| const departments = deptRequest.result.map(d => d.name); | |
| // Count visits by department | |
| const deptCount = {}; | |
| departments.forEach(dept => { | |
| deptCount[dept] = 0; | |
| }); | |
| // We need patient details for each visit | |
| let processed = 0; | |
| const total = visits.length; | |
| if (total === 0) { | |
| createChart(departments, Array(departments.length).fill(0)); | |
| return; | |
| } | |
| visits.forEach(visit => { | |
| const patientTx = db.transaction("patients", "readonly"); | |
| const patientStore = patientTx.objectStore("patients"); | |
| const patientRequest = patientStore.get(visit.patientId); | |
| patientRequest.onsuccess = function() { | |
| const patient = patientRequest.result; | |
| if (patient && patient.department) { | |
| deptCount[patient.department] = (deptCount[patient.department] || 0) + 1; | |
| } | |
| processed++; | |
| if (processed === total) { | |
| createChart(departments, departments.map(dept => deptCount[dept] || 0)); | |
| } | |
| }; | |
| }); | |
| }; | |
| function createChart(labels, data) { | |
| new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| label: 'تعداد مراجعات', | |
| data: data, | |
| backgroundColor: '#4f46e5' | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| }; | |
| } | |
| function loadCommonDiseasesChart() { | |
| const ctx = document.getElementById('commonDiseasesChart').getContext('2d'); | |
| const tx = db.transaction("visits", "readonly"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.getAll(); | |
| request.onsuccess = function() { | |
| const visits = request.result; | |
| // Count diseases | |
| const diseaseCount = {}; | |
| visits.forEach(visit => { | |
| if (visit.disease) { | |
| diseaseCount[visit.disease] = (diseaseCount[visit.disease] || 0) + 1; | |
| } | |
| }); | |
| // Sort by count and take top 10 | |
| const sorted = Object.entries(diseaseCount) | |
| .sort((a, b) => b[1] - a[1]) | |
| .slice(0, 10); | |
| const labels = sorted.map(d => d[0]); | |
| const data = sorted.map(d => d[1]); | |
| new Chart(ctx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| data: data, | |
| backgroundColor: [ | |
| '#4f46e5', | |
| '#10b981', | |
| '#f59e0b', | |
| '#ef4444', | |
| '#8b5cf6', | |
| '#ec4899', | |
| '#14b8a6', | |
| '#f97316', | |
| '#84cc16', | |
| '#64748b' | |
| ] | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| position: 'bottom' | |
| } | |
| } | |
| } | |
| }); | |
| }; | |
| } | |
| function exportAllData() { | |
| // Export all data as Excel file | |
| alert("این ویژگی به زودی اضافه خواهد شد."); | |
| } | |
| // Initialize when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Set current date and time for examination form | |
| const now = new Date(); | |
| const timezoneOffset = now.getTimezoneOffset() * 60000; | |
| const localISOTime = (new Date(now - timezoneOffset)).toISOString().slice(0, 16); | |
| document.getElementById('examinationDate').value = localISOTime; | |
| // Set up form submission for examination | |
| document.getElementById('examinationForm').addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| saveExamination(); | |
| }); | |
| // Set up search for patients | |
| document.getElementById('patientSearch').addEventListener('input', function(e) { | |
| loadPatients(1, 10, e.target.value); | |
| }); | |
| // Set up search for medicines | |
| document.getElementById('medicineSearch').addEventListener('input', function(e) { | |
| loadMedicines(1, 10, e.target.value); | |
| }); | |
| // Initialize feather icons | |
| feather.replace(); | |
| // Show dashboard by default | |
| showDashboard(); | |
| }); | |
| function saveExamination() { | |
| const patientId = document.getElementById('patientSelect').value; | |
| const examiner = document.getElementById('examiner').value.trim(); | |
| const date = document.getElementById('examinationDate').value; | |
| const disease = document.getElementById('disease').value.trim(); | |
| const description = document.getElementById('description').value.trim(); | |
| const actionsTaken = document.getElementById('actionsTaken').value.trim(); | |
| if (!patientId || !examiner || !date || !disease) { | |
| alert("لطفاً تمام فیلدهای ضروری را پر کنید."); | |
| return; | |
| } | |
| // Collect prescribed medicines | |
| const medicines = []; | |
| const medicineElements = document.querySelectorAll('#medicinesContainer > div'); | |
| medicineElements.forEach(div => { | |
| const name = div.querySelector('.medicine-name').value.trim(); | |
| const quantity = parseInt(div.querySelector('.medicine-quantity').value) || 0; | |
| if (name && quantity > 0) { | |
| medicines.push({ name, quantity, unit: 'عدد' }); // Default unit, can be enhanced | |
| } | |
| }); | |
| const visit = { | |
| patientId, | |
| examiner, | |
| date, | |
| disease, | |
| description, | |
| actionsTaken, | |
| medicines, | |
| createdAt: new Date().toISOString() | |
| }; | |
| // Save visit | |
| const tx = db.transaction("visits", "readwrite"); | |
| const store = tx.objectStore("visits"); | |
| const request = store.add(visit); | |
| request.onsuccess = function() { | |
| // If medicines were prescribed, update their quantities | |
| if (medicines.length > 0) { | |
| updateMedicineQuantities(medicines); | |
| } else { | |
| alert("معاینه با موفقیت ثبت شد."); | |
| document.getElementById('examinationForm').reset(); | |
| document.getElementById('medicinesContainer').innerHTML = ''; | |
| } | |
| }; | |
| request.onerror = function(event) { | |
| console.error("Error saving examination:", event.target.error); | |
| alert("خطا در ثبت معاینه. لطفاً دوباره امتحان کنید."); | |
| }; | |
| } | |
| function updateMedicineQuantities(medicines) { | |
| const tx = db.transaction("medicines", "readwrite"); | |
| const store = tx.objectStore("medicines"); | |
| let processed = 0; | |
| const total = medicines.length; | |
| medicines.forEach(medicine => { | |
| const request = store.get(medicine.name); | |
| request.onsuccess = function() { | |
| const existing = request.result; | |
| if (existing) { | |
| const newQuantity = Math.max(0, existing.quantity - medicine.quantity); | |
| existing.quantity = newQuantity; | |
| const updateRequest = store.put(existing); | |
| updateRequest.onsuccess = function() { | |
| processed++; | |
| if (processed === total) { | |
| alert("معاینه با موفقیت ثبت شد و موجودی داروها بهروزرسانی شد."); | |
| document.getElementById('examinationForm').reset(); | |
| document.getElementById('medicinesContainer').innerHTML = ''; | |
| loadMedicines(); | |
| loadMedicineCharts(); | |
| } | |
| }; | |
| } else { | |
| processed++; | |
| if (processed === total) { | |
| alert("معاینه با موفقیت ثبت شد (برخی داروهای تجویز شده در سیستم موجود نیستند)."); | |
| document.getElementById('examinationForm').reset(); | |
| document.getElementById('medicinesContainer').innerHTML = ''; | |
| } | |
| } | |
| }; | |
| const now = new persianDate(); | |
| document.getElementById('current-date').textContent = now.format('YYYY/MM/DD'); | |
| request.onerror = function() { | |
| processed++; | |
| if (processed === total) { | |
| alert("معاینه با موفقیت ثبت شد (خطا در بهروزرسانی موجودی داروها)."); | |
| document.getElementById('examinationForm').reset(); | |
| document.getElementById('medicinesContainer').innerHTML = ''; | |
| } | |
| }; | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> | |