med-cms 1.0.0-beta.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE.txt +13 -0
  2. package/README.md +117 -0
  3. package/database/config/db-init.js +67 -53
  4. package/database/models/appointments.js +35 -20
  5. package/database/models/clinics.js +25 -13
  6. package/database/models/diseases.js +21 -13
  7. package/database/models/districts.js +55 -0
  8. package/database/models/news.js +25 -0
  9. package/database/models/user-addresses.js +21 -14
  10. package/database/models/users.js +69 -26
  11. package/database/services/admin/create-clinic.js +18 -6
  12. package/database/services/admin/db-analytics.js +42 -16
  13. package/database/services/admin/index.js +6 -6
  14. package/database/services/admin/manage-staff.js +47 -17
  15. package/database/services/doctor/conduct-exam.js +26 -20
  16. package/database/services/doctor/get-schedule.js +26 -18
  17. package/database/services/doctor/index.js +6 -6
  18. package/database/services/doctor/patient-access.js +34 -25
  19. package/database/services/patient/book-appointment.js +37 -17
  20. package/database/services/patient/calc-district.js +70 -21
  21. package/database/services/patient/get-profile.js +49 -21
  22. package/database/services/patient/index.js +5 -5
  23. package/env_init.js +55 -0
  24. package/index.d.ts +38 -24
  25. package/index.js +48 -25
  26. package/migrate-diseases.js +192 -120
  27. package/package.json +3 -3
  28. package/public/css/admin.css +80 -80
  29. package/public/css/auth.css +52 -44
  30. package/public/css/doctor.css +131 -125
  31. package/public/css/home.css +155 -0
  32. package/public/html/dashboard-admin.html +423 -101
  33. package/public/html/dashboard-doctor.html +155 -66
  34. package/public/html/dashboard-patient.html +199 -94
  35. package/public/html/home/about.html +0 -0
  36. package/public/html/home/contact.html +0 -0
  37. package/public/html/home/info.html +0 -0
  38. package/public/html/home.html +159 -0
  39. package/public/html/login.html +73 -43
  40. package/public/js/admin.js +329 -165
  41. package/public/js/auth.js +45 -41
  42. package/public/js/doctor.js +117 -103
  43. package/public/js/home.js +113 -0
  44. package/public/js/patient.js +130 -108
  45. package/server/midleware.js +33 -13
  46. package/server/permissions.js +105 -43
  47. package/server/server.js +517 -176
  48. package/README +0 -161
  49. package/test.js +0 -27
@@ -1,137 +1,151 @@
1
- document.addEventListener('DOMContentLoaded', () => {
2
- loadSchedule();
3
- loadDiseasesCatalog();
4
- loadDoctorProfile();
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ loadSchedule();
3
+ loadDiseasesCatalog();
4
+ loadDoctorProfile();
5
5
  });
6
6
 
7
7
  async function loadDiseasesCatalog() {
8
- try {
9
- const res = await fetch('/med/api/doctor/diseases-list');
10
- const result = await res.json();
11
-
12
- const select = document.getElementById('examDisease');
13
-
14
- select.innerHTML = '<option value="">-- Выберите диагноз из справочника МКБ --</option>';
15
-
16
- if (result.success && result.data.length > 0) {
17
- result.data.forEach(disease => {
18
- const opt = document.createElement('option');
19
- opt.value = disease.id; opt.textContent = `[${disease.code}] ${disease.name}`; select.appendChild(opt);
20
- });
21
- console.log(`🌱 [АРМ Врача] Успешно загружено ${result.data.length} диагнозов из SQLite таблицы.`);
22
- } else {
23
- select.innerHTML = '<option value="">Справочник болезней пуст (Запустите миграцию)</option>';
24
- }
25
- } catch (err) {
26
- console.error('❌ Ошибка при получении справочника болезней:', err);
8
+ try {
9
+ const res = await fetch("/med/api/doctor/diseases-list");
10
+ const result = await res.json();
11
+
12
+ const select = document.getElementById("examDisease");
13
+
14
+ select.innerHTML =
15
+ '<option value="">-- Выберите диагноз из справочника МКБ --</option>';
16
+
17
+ if (result.success && result.data.length > 0) {
18
+ result.data.forEach((disease) => {
19
+ const opt = document.createElement("option");
20
+ opt.value = disease.id;
21
+ opt.textContent = `[${disease.code}] ${disease.name}`;
22
+ select.appendChild(opt);
23
+ });
24
+ console.log(
25
+ `🌱 [АРМ Врача] Успешно загружено ${result.data.length} диагнозов из SQLite таблицы.`,
26
+ );
27
+ } else {
28
+ select.innerHTML =
29
+ '<option value="">Справочник болезней пуст (Запустите миграцию)</option>';
27
30
  }
31
+ } catch (err) {
32
+ console.error("❌ Ошибка при получении справочника болезней:", err);
33
+ }
28
34
  }
29
35
 
30
36
  async function loadSchedule() {
31
- try {
32
- const res = await fetch('/med/api/doctor/schedule');
33
- const data = await res.json();
34
-
35
- const list = document.getElementById('scheduleList');
36
- list.innerHTML = '';
37
-
38
- if (data.success && data.schedule && data.schedule.length > 0) {
39
- data.schedule.forEach(ap => {
40
- const li = document.createElement('li');
41
- li.innerHTML = `<b>${ap.time}</b> - ${ap.Patient.fullName}
37
+ try {
38
+ const res = await fetch("/med/api/doctor/schedule");
39
+ const data = await res.json();
40
+
41
+ const list = document.getElementById("scheduleList");
42
+ list.innerHTML = "";
43
+
44
+ if (data.success && data.schedule && data.schedule.length > 0) {
45
+ data.schedule.forEach((ap) => {
46
+ const li = document.createElement("li");
47
+ li.innerHTML = `<b>${ap.time}</b> - ${ap.Patient.fullName}
42
48
  <button onclick="startExamination('${ap.id}', '${ap.Patient.fullName}')" class="btn-action">Принять</button>`;
43
- list.appendChild(li);
44
- });
45
- } else {
46
- list.innerHTML = '<li>Ближайших записей на сегодня нет</li>';
47
- }
48
- } catch (err) {
49
- console.error('Ошибка загрузки расписания:', err);
49
+ list.appendChild(li);
50
+ });
51
+ } else {
52
+ list.innerHTML = "<li>Ближайших записей на сегодня нет</li>";
50
53
  }
54
+ } catch (err) {
55
+ console.error("Ошибка загрузки расписания:", err);
56
+ }
51
57
  }
52
58
 
53
- document.getElementById('examForm').addEventListener('submit', async (e) => {
54
- e.preventDefault();
55
-
56
- const selectedDiseaseId = document.getElementById('examDisease').value;
59
+ document.getElementById("examForm").addEventListener("submit", async (e) => {
60
+ e.preventDefault();
57
61
 
58
- const payload = {
59
- appointmentId: document.getElementById('activeAppointmentId').value,
60
- complaints: document.getElementById('examComplaints').value,
61
- objectively: document.getElementById('examObjective').value,
62
- doctorNotes: document.getElementById('examNotes').value,
63
- diseaseId: selectedDiseaseId ? parseInt(selectedDiseaseId, 10) : null
64
- };
62
+ const selectedDiseaseId = document.getElementById("examDisease").value;
65
63
 
66
- try {
67
- const res = await fetch('/med/api/doctor/complete-examination', {
68
- method: 'POST',
69
- headers: { 'Content-Type': 'application/json' },
70
- body: JSON.stringify(payload)
71
- });
72
-
73
- const result = await res.json();
74
- if (result.success) {
75
- alert('Прием успешно завершен! Карточка обновлена, статистика внесена в SQLite.');
76
- document.getElementById('examBlock').classList.add('hidden');
77
- document.getElementById('examForm').reset();
78
- await loadSchedule(); } else {
79
- alert('Ошибка завершения приема: ' + result.error);
80
- }
81
- } catch (err) {
82
- alert('Ошибка сети при отправке данных осмотра');
64
+ const payload = {
65
+ appointmentId: document.getElementById("activeAppointmentId").value,
66
+ complaints: document.getElementById("examComplaints").value,
67
+ objectively: document.getElementById("examObjective").value,
68
+ doctorNotes: document.getElementById("examNotes").value,
69
+ diseaseId: selectedDiseaseId ? parseInt(selectedDiseaseId, 10) : null,
70
+ };
71
+
72
+ try {
73
+ const res = await fetch("/med/api/doctor/complete-examination", {
74
+ method: "POST",
75
+ headers: { "Content-Type": "application/json" },
76
+ body: JSON.stringify(payload),
77
+ });
78
+
79
+ const result = await res.json();
80
+ if (result.success) {
81
+ alert(
82
+ "Прием успешно завершен! Карточка обновлена, статистика внесена в SQLite.",
83
+ );
84
+ document.getElementById("examBlock").classList.add("hidden");
85
+ document.getElementById("examForm").reset();
86
+ await loadSchedule();
87
+ } else {
88
+ alert("Ошибка завершения приема: " + result.error);
83
89
  }
90
+ } catch (err) {
91
+ alert("Ошибка сети при отправке данных осмотра");
92
+ }
84
93
  });
85
94
 
86
95
  function startExamination(appointmentId, patientName) {
87
- document.getElementById('examBlock').classList.remove('hidden');
88
- document.getElementById('activeAppointmentId').value = appointmentId;
89
- document.getElementById('currentPatientName').textContent = patientName;
96
+ document.getElementById("examBlock").classList.remove("hidden");
97
+ document.getElementById("activeAppointmentId").value = appointmentId;
98
+ document.getElementById("currentPatientName").textContent = patientName;
90
99
  }
91
100
 
92
101
  async function logout() {
93
- await fetch('/med/api/auth/logout', { method: 'POST' });
94
- window.location.href = '/med/med-login';
102
+ await fetch("/med/api/auth/logout", { method: "POST" });
103
+ window.location.href = "/med/html/login.html";
95
104
  }
96
105
  async function loadDoctorProfile() {
97
- try {
98
- const res = await fetch('/med/api/doctor/me');
99
- const result = await res.json();
100
- if (result.success && result.doctor) {
101
- document.getElementById('doctorName').textContent = result.doctor.fullName;
102
- document.getElementById('doctorPosition').textContent = result.doctor.position || 'Врач';
103
- }
104
- } catch (err) {
105
- console.error('Ошибка загрузки профиля сотрудника:', err);
106
+ try {
107
+ const res = await fetch("/med/api/doctor/me");
108
+ const result = await res.json();
109
+ if (result.success && result.doctor) {
110
+ document.getElementById("doctorName").textContent =
111
+ result.doctor.fullName;
112
+ document.getElementById("doctorPosition").textContent =
113
+ result.doctor.position || "Врач";
106
114
  }
115
+ } catch (err) {
116
+ console.error("Ошибка загрузки профиля сотрудника:", err);
117
+ }
107
118
  }
108
119
 
109
120
  function togglePasswordModal() {
110
- const modal = document.getElementById('passwordModal');
111
- modal.classList.toggle('hidden');
112
- document.getElementById('changePasswordForm').reset();
121
+ const modal = document.getElementById("passwordModal");
122
+ modal.classList.toggle("hidden");
123
+ document.getElementById("changePasswordForm").reset();
113
124
  }
114
125
 
115
- document.getElementById('changePasswordForm').addEventListener('submit', async (e) => {
126
+ document
127
+ .getElementById("changePasswordForm")
128
+ .addEventListener("submit", async (e) => {
116
129
  e.preventDefault();
117
130
 
118
- const oldPassword = document.getElementById('oldPassword').value;
119
- const newPassword = document.getElementById('newPassword').value;
131
+ const oldPassword = document.getElementById("oldPassword").value;
132
+ const newPassword = document.getElementById("newPassword").value;
120
133
 
121
134
  try {
122
- const response = await fetch('/med/api/auth/change-password', {
123
- method: 'POST',
124
- headers: { 'Content-Type': 'application/json' },
125
- body: JSON.stringify({ oldPassword, newPassword })
126
- });
127
-
128
- const result = await response.json();
129
- if (result.success) {
130
- alert('Ваш пароль успешно изменен в базе данных SQLite!');
131
- togglePasswordModal(); } else {
132
- alert('Ошибка смены пароля: ' + result.message);
133
- }
135
+ const response = await fetch("/med/api/auth/change-password", {
136
+ method: "POST",
137
+ headers: { "Content-Type": "application/json" },
138
+ body: JSON.stringify({ oldPassword, newPassword }),
139
+ });
140
+
141
+ const result = await response.json();
142
+ if (result.success) {
143
+ alert("Ваш пароль успешно изменен в базе данных SQLite!");
144
+ togglePasswordModal();
145
+ } else {
146
+ alert("Ошибка смены пароля: " + result.message);
147
+ }
134
148
  } catch (err) {
135
- alert('Ошибка сети при попытке изменить пароль');
149
+ alert("Ошибка сети при попытке изменить пароль");
136
150
  }
137
- });
151
+ });
@@ -0,0 +1,113 @@
1
+ // Запуск подгрузки живой ленты новостей при открытии сайта
2
+ document.addEventListener("DOMContentLoaded", () => {
3
+ loadLiveNews();
4
+ });
5
+
6
+ // Статические вкладки информационного портала
7
+ const staticSections = {
8
+ about: {
9
+ title: "О нашей клинике",
10
+ text: "Мы — современный цифровой медицинский центр РБ, работающий по стандартам автоматизированного распределения пациентов. Наша цель — сделать медицинскую помощь доступной и быстрой.",
11
+ },
12
+ info: {
13
+ title: "Информационный источник",
14
+ text: "Полезные статьи о профилактике сезонных ОРВИ, контроле артериального давления при гипертонии и правилах подготовки к плановым диагностическим осмотрам в кабинетах поликлиники.",
15
+ },
16
+ contacts: {
17
+ title: "Контактная информация",
18
+ text: "Телефон регистратуры: +375 (17) 000-00-00. Режим работы окон справок: Пн-Пт с 8:00 до 20:00. Адрес: г. Минск, центральный лечебный корпус.",
19
+ },
20
+ };
21
+
22
+ // Динамическое скачивание новостей из SQLite без авторизации
23
+ async function loadLiveNews() {
24
+ const box = document.getElementById("news-box");
25
+ box.innerHTML =
26
+ '<p style="color: #64748b;">Загрузка свежих новостей клиники...</p>';
27
+
28
+ try {
29
+ const res = await fetch("/med/api/public/news-list");
30
+ const result = await res.json();
31
+
32
+ if (result.success && result.data.length > 0) {
33
+ let html = "";
34
+ result.data.forEach((post) => {
35
+ const date = new Date(post.createdAt).toLocaleDateString("ru-RU");
36
+ html += `
37
+ <div style="margin-bottom: 25px; border-bottom: 1px solid #e2e8f0; padding-bottom: 15px;">
38
+ <span style="font-size:12px; color:#64748b;">📅 Опубликовано: ${date}</span>
39
+ <h2 style="margin: 5px 0 10px 0; color:#1e3a8a;">${post.title}</h2>
40
+ <p style="line-height: 1.6; color:#475569; margin:0;">${post.content}</p>
41
+ </div>
42
+ `;
43
+ });
44
+ box.innerHTML = html;
45
+ } else {
46
+ box.innerHTML =
47
+ '<h2>Лента новостей</h2><p style="color:#64748b;">На данный момент новостей и объявлений нет.</p>';
48
+ }
49
+ } catch (err) {
50
+ console.error("Ошибка загрузки новостей:", err);
51
+ box.innerHTML =
52
+ '<p style="color:red;">Не удалось загрузить ленту новостей сервера.</p>';
53
+ }
54
+ }
55
+
56
+ // Переключатель вкладок навигации по чертежу
57
+ function switchSection(sectionKey, element) {
58
+ document
59
+ .querySelectorAll(".nav-btn")
60
+ .forEach((btn) => btn.classList.remove("active"));
61
+ element.classList.add("active");
62
+ const elements = {
63
+ news: document.getElementById("news-box"),
64
+ about: document.getElementById("about-box"),
65
+ info: document.getElementById("info-box"),
66
+ contact: document.getElementById("contacts-box"), // изменил ключ на contact для соответствия строке
67
+ };
68
+
69
+ // 1. Вызываем функцию для новостей, если выбрана эта секция
70
+ if (sectionKey === "news" && typeof loadLiveNews === "function") {
71
+ loadLiveNews();
72
+ }
73
+
74
+ // 2. Скрываем все элементы и показываем только нужный
75
+ Object.keys(elements).forEach((key) => {
76
+ if (elements[key]) {
77
+ // проверка, что элемент нашелся в DOM
78
+ elements[key].style.display = key === sectionKey ? "block" : "none";
79
+ }
80
+ });
81
+ }
82
+ // Онлайн-проверка лечебного участка по полному адресу кнопкой "ОК"
83
+ async function checkDistrictOnline() {
84
+ const city = document.getElementById("checkCityInput").value;
85
+ const street = document.getElementById("checkStreetInput").value;
86
+ const house = document.getElementById("checkHouseInput").value;
87
+ const resultBox = document.getElementById("checkResultBox");
88
+
89
+ if (!city || !street || !house) {
90
+ resultBox.innerHTML =
91
+ '<span style="color:red;">Заполните все три поля: Город, Улица, Дом!</span>';
92
+ return;
93
+ }
94
+
95
+ resultBox.innerHTML = "Проверка адреса в SQLite...";
96
+
97
+ try {
98
+ // Формируем безопасный GET-запрос со всеми параметрами адреса
99
+ const url = `/med/api/public/check-district?clinicId=1&city=${encodeURIComponent(city)}&street=${encodeURIComponent(street)}&house=${encodeURIComponent(house)}`;
100
+ const res = await fetch(url);
101
+ const result = await res.json();
102
+
103
+ if (result.success) {
104
+ resultBox.innerHTML = `🩺 ${result.data.assignedDistrict} <br> 🚪 ${result.data.assignedCabinet}`;
105
+ } else {
106
+ resultBox.innerHTML = `<span style="color:red;">Ошибка: ${result.error}</span>`;
107
+ }
108
+ } catch (err) {
109
+ console.error(err);
110
+ resultBox.innerHTML =
111
+ '<span style="color:red;">Ошибка соединения с базой данных</span>';
112
+ }
113
+ }