create-jinmankn-app 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 (117) hide show
  1. package/bin/index.js +76 -0
  2. package/package.json +20 -0
  3. package/templates/blueprint/BLUEPRINT_REPRODUCTION_PROMPT.md +996 -0
  4. package/templates/blueprint/HOW_IT_WORKS.md +286 -0
  5. package/templates/blueprint/README.md +123 -0
  6. package/templates/blueprint/backend/config/db.js +12 -0
  7. package/templates/blueprint/backend/controllers/authController.js +90 -0
  8. package/templates/blueprint/backend/controllers/itemController.js +74 -0
  9. package/templates/blueprint/backend/middleware/auth.js +32 -0
  10. package/templates/blueprint/backend/middleware/errorHandler.js +23 -0
  11. package/templates/blueprint/backend/models/Item.js +26 -0
  12. package/templates/blueprint/backend/models/User.js +28 -0
  13. package/templates/blueprint/backend/package-lock.json +2190 -0
  14. package/templates/blueprint/backend/package.json +23 -0
  15. package/templates/blueprint/backend/routes/authRoutes.js +11 -0
  16. package/templates/blueprint/backend/routes/healthRoutes.js +9 -0
  17. package/templates/blueprint/backend/routes/itemRoutes.js +21 -0
  18. package/templates/blueprint/backend/server.js +29 -0
  19. package/templates/blueprint/frontend/.env.example +1 -0
  20. package/templates/blueprint/frontend/index.html +13 -0
  21. package/templates/blueprint/frontend/package-lock.json +2844 -0
  22. package/templates/blueprint/frontend/package.json +23 -0
  23. package/templates/blueprint/frontend/public/favicon.svg +4 -0
  24. package/templates/blueprint/frontend/src/App.jsx +78 -0
  25. package/templates/blueprint/frontend/src/assets/logo.svg +4 -0
  26. package/templates/blueprint/frontend/src/components/DashboardLayout.jsx +103 -0
  27. package/templates/blueprint/frontend/src/components/ProtectedRoute.jsx +18 -0
  28. package/templates/blueprint/frontend/src/index.css +1 -0
  29. package/templates/blueprint/frontend/src/main.jsx +13 -0
  30. package/templates/blueprint/frontend/src/pages/DashboardHome.jsx +74 -0
  31. package/templates/blueprint/frontend/src/pages/Items.jsx +243 -0
  32. package/templates/blueprint/frontend/src/pages/Login.jsx +101 -0
  33. package/templates/blueprint/frontend/src/pages/Profile.jsx +79 -0
  34. package/templates/blueprint/frontend/src/pages/Register.jsx +122 -0
  35. package/templates/blueprint/frontend/src/pages/Report.jsx +124 -0
  36. package/templates/blueprint/frontend/vite.config.js +10 -0
  37. package/templates/blueprint/package.json +13 -0
  38. package/templates/blueprint/scripts/pack-blueprint.ps1 +18 -0
  39. package/templates/chom/Backend/app.js +25 -0
  40. package/templates/chom/Backend/package-lock.json +1551 -0
  41. package/templates/chom/Backend/package.json +23 -0
  42. package/templates/chom/Backend/seedAdmin.js +21 -0
  43. package/templates/chom/Backend/src/controllers/payment.c.js +57 -0
  44. package/templates/chom/Backend/src/controllers/students.c.js +58 -0
  45. package/templates/chom/Backend/src/controllers/users.c.js +62 -0
  46. package/templates/chom/Backend/src/middleware/authentication.js +18 -0
  47. package/templates/chom/Backend/src/models/payment.m.js +13 -0
  48. package/templates/chom/Backend/src/models/students.m.js +10 -0
  49. package/templates/chom/Backend/src/models/users.m.js +11 -0
  50. package/templates/chom/Backend/src/routes/users.r.js +21 -0
  51. package/templates/chom/Frontend/README.md +16 -0
  52. package/templates/chom/Frontend/eslint.config.js +21 -0
  53. package/templates/chom/Frontend/index.html +13 -0
  54. package/templates/chom/Frontend/package-lock.json +3075 -0
  55. package/templates/chom/Frontend/package.json +31 -0
  56. package/templates/chom/Frontend/public/favicon.svg +1 -0
  57. package/templates/chom/Frontend/public/icons.svg +24 -0
  58. package/templates/chom/Frontend/src/App.css +189 -0
  59. package/templates/chom/Frontend/src/App.jsx +28 -0
  60. package/templates/chom/Frontend/src/api/api.jsx +27 -0
  61. package/templates/chom/Frontend/src/assets/hero.png +0 -0
  62. package/templates/chom/Frontend/src/assets/react.svg +1 -0
  63. package/templates/chom/Frontend/src/assets/vite.svg +1 -0
  64. package/templates/chom/Frontend/src/components/Navbar.jsx +21 -0
  65. package/templates/chom/Frontend/src/index.css +8 -0
  66. package/templates/chom/Frontend/src/main.jsx +10 -0
  67. package/templates/chom/Frontend/src/pages/Dashboard.jsx +21 -0
  68. package/templates/chom/Frontend/src/pages/Landing.jsx +39 -0
  69. package/templates/chom/Frontend/src/pages/Login.jsx +49 -0
  70. package/templates/chom/Frontend/src/pages/Overview.jsx +42 -0
  71. package/templates/chom/Frontend/src/pages/Register.jsx +76 -0
  72. package/templates/chom/Frontend/src/pages/Students.jsx +14 -0
  73. package/templates/chom/Frontend/vite.config.js +8 -0
  74. package/templates/chom/package.json +13 -0
  75. package/templates/hospital-faisal/backend/.env.example +9 -0
  76. package/templates/hospital-faisal/backend/config/db.js +96 -0
  77. package/templates/hospital-faisal/backend/controllers/appointmentController.js +164 -0
  78. package/templates/hospital-faisal/backend/controllers/authController.js +106 -0
  79. package/templates/hospital-faisal/backend/controllers/hospitalReportController.js +72 -0
  80. package/templates/hospital-faisal/backend/controllers/medicalReportController.js +105 -0
  81. package/templates/hospital-faisal/backend/controllers/patientController.js +98 -0
  82. package/templates/hospital-faisal/backend/database/schema.sql +47 -0
  83. package/templates/hospital-faisal/backend/middleware/auth.js +30 -0
  84. package/templates/hospital-faisal/backend/middleware/errorHandler.js +23 -0
  85. package/templates/hospital-faisal/backend/middleware/role.js +6 -0
  86. package/templates/hospital-faisal/backend/package-lock.json +2092 -0
  87. package/templates/hospital-faisal/backend/package.json +23 -0
  88. package/templates/hospital-faisal/backend/routes/appointmentRoutes.js +25 -0
  89. package/templates/hospital-faisal/backend/routes/authRoutes.js +12 -0
  90. package/templates/hospital-faisal/backend/routes/healthRoutes.js +9 -0
  91. package/templates/hospital-faisal/backend/routes/hospitalReportRoutes.js +10 -0
  92. package/templates/hospital-faisal/backend/routes/medicalReportRoutes.js +16 -0
  93. package/templates/hospital-faisal/backend/routes/patientRoutes.js +22 -0
  94. package/templates/hospital-faisal/backend/server.js +46 -0
  95. package/templates/hospital-faisal/frontend/.env.example +1 -0
  96. package/templates/hospital-faisal/frontend/index.html +10 -0
  97. package/templates/hospital-faisal/frontend/package-lock.json +2844 -0
  98. package/templates/hospital-faisal/frontend/package.json +23 -0
  99. package/templates/hospital-faisal/frontend/public/favicon.svg +4 -0
  100. package/templates/hospital-faisal/frontend/src/App.jsx +56 -0
  101. package/templates/hospital-faisal/frontend/src/api.js +20 -0
  102. package/templates/hospital-faisal/frontend/src/assets/logo.svg +4 -0
  103. package/templates/hospital-faisal/frontend/src/components/DashboardLayout.jsx +114 -0
  104. package/templates/hospital-faisal/frontend/src/components/ProtectedRoute.jsx +18 -0
  105. package/templates/hospital-faisal/frontend/src/components/RoleRoute.jsx +14 -0
  106. package/templates/hospital-faisal/frontend/src/index.css +1 -0
  107. package/templates/hospital-faisal/frontend/src/main.jsx +13 -0
  108. package/templates/hospital-faisal/frontend/src/pages/Appointments.jsx +305 -0
  109. package/templates/hospital-faisal/frontend/src/pages/DashboardHome.jsx +105 -0
  110. package/templates/hospital-faisal/frontend/src/pages/Login.jsx +98 -0
  111. package/templates/hospital-faisal/frontend/src/pages/MedicalReports.jsx +182 -0
  112. package/templates/hospital-faisal/frontend/src/pages/Patients.jsx +237 -0
  113. package/templates/hospital-faisal/frontend/src/pages/Profile.jsx +78 -0
  114. package/templates/hospital-faisal/frontend/src/pages/Register.jsx +133 -0
  115. package/templates/hospital-faisal/frontend/src/pages/Report.jsx +167 -0
  116. package/templates/hospital-faisal/frontend/vite.config.js +10 -0
  117. package/templates/hospital-faisal/package.json +13 -0
@@ -0,0 +1,76 @@
1
+ import React, { useState } from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { useNavigate } from "react-router-dom";
4
+ import API from "../api/api";
5
+
6
+ const Register = () => {
7
+ const navigate = useNavigate()
8
+
9
+ const [form, setForm] = useState({
10
+ names: "",
11
+ email: "",
12
+ password: "",
13
+ role: ""
14
+ })
15
+
16
+ const handleChange = (e) => {
17
+ setForm({
18
+ ...form, [e.target.name]: e.target.value
19
+ })
20
+ }
21
+
22
+
23
+ const handleSubmit = async (e) => {
24
+ e.preventDefault()
25
+ try {
26
+ const res = await API.post("/register", form)
27
+ } catch (error) {
28
+ return alert(error)
29
+ }
30
+
31
+ navigate("/dashboard")
32
+ }
33
+ return (
34
+ <>
35
+ <div className="w-full h-screen flex items-center justify-center">
36
+ <div className="w-1/4 h-8/12 border-2 border-cyan-500 rounded-2xl">
37
+ <div className="w-full flex items-center justify-center">
38
+ <p className="font-semibold text-2xl text-(--text-main)">Register</p>
39
+ </div>
40
+ <div className="w-full h-5/7 flex flex-col items-center justify-evenly">
41
+ <input
42
+ type="text"
43
+ onChange={handleChange}
44
+ name="names"
45
+ placeholder="Username"
46
+ className="border border-(--bg-maj) text-(--text-maj) w-10/11 h-1/8 rounded-xl placeholder:text-(--text-maj)/40 text-sm pl-4 pr-3" />
47
+ <input
48
+ type="email"
49
+ onChange={handleChange}
50
+ name="email"
51
+ placeholder="Email"
52
+ className="border border-(--bg-maj) text-(--text-maj) w-10/11 h-1/8 rounded-xl placeholder:text-(--text-maj)/40 text-sm pl-4 pr-3" />
53
+ <input
54
+ type="password"
55
+ onChange={handleChange}
56
+ name="password"
57
+ placeholder="Password"
58
+ className="border border-(--bg-maj) text-(--text-maj) w-10/11 h-1/8 rounded-xl placeholder:text-(--text-maj)/40 text-sm pl-4 pr-3" />
59
+ <input
60
+ type="text"
61
+ onChange={handleChange}
62
+ name="role"
63
+ placeholder="Role"
64
+ className="border border-(--bg-maj) text-(--text-maj) w-10/11 h-1/8 rounded-xl placeholder:text-(--text-maj)/40 text-sm pl-4 pr-3" />
65
+ </div>
66
+ <div className="w-full h-1/8 flex justify-center">
67
+ <button onClick={handleSubmit} className="w-10/11 h-10 rounded bg-(--bg-maj)">Submit</button>
68
+ </div>
69
+ <div className="w-full flex items-center justify-center"><p className="text-(--text-main) text-sm font-light">Already have an account? <Link to="/login" className="text-(--text-maj) hover:underline">Login</Link></p></div>
70
+ </div>
71
+ </div>
72
+ </>
73
+ )
74
+ }
75
+
76
+ export default Register
@@ -0,0 +1,14 @@
1
+
2
+ const Students = () => {
3
+ return (
4
+ <>
5
+ <div className="w-full min-h-screen pt-20 bg-amber-500">
6
+ <div className="w-full h-150 bg-blue-400">
7
+ hii
8
+ </div>
9
+ </div>
10
+ </>
11
+ )
12
+ }
13
+
14
+ export default Students
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import tailwindcss from "@tailwindcss/vite"
4
+
5
+ // https://vite.dev/config/
6
+ export default defineConfig({
7
+ plugins: [react(), tailwindcss()],
8
+ })
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "chom",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "type": "commonjs"
13
+ }
@@ -0,0 +1,9 @@
1
+ # PORT=5000
2
+ # CLIENT_URL=http://localhost:5173
3
+
4
+ # DB_HOST=localhost
5
+ # DB_USER=root
6
+ # DB_PASSWORD=
7
+ # DB_NAME=kf_hospital
8
+
9
+ # JWT_SECRET=dev_secret_change_in_production
@@ -0,0 +1,96 @@
1
+ const mysql = require('mysql2/promise');
2
+
3
+ let pool;
4
+
5
+ function getPool() {
6
+ if (!pool) {
7
+ pool = mysql.createPool({
8
+ host: process.env.DB_HOST || 'localhost',
9
+ user: process.env.DB_USER || 'root',
10
+ password: process.env.DB_PASSWORD || '',
11
+ database: process.env.DB_NAME || 'kf_hospital',
12
+ waitForConnections: true,
13
+ connectionLimit: 10,
14
+ dateStrings: true,
15
+ });
16
+ }
17
+ return pool;
18
+ }
19
+
20
+ async function initDatabase() {
21
+ const host = process.env.DB_HOST || 'localhost';
22
+ const user = process.env.DB_USER || 'root';
23
+ const password = process.env.DB_PASSWORD || '';
24
+ const dbName = process.env.DB_NAME || 'kf_hospital';
25
+
26
+ let bootstrap;
27
+ try {
28
+ bootstrap = await mysql.createConnection({ host, user, password });
29
+ await bootstrap.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\``);
30
+ await bootstrap.query(`USE \`${dbName}\``);
31
+
32
+ await bootstrap.query(`
33
+ CREATE TABLE IF NOT EXISTS users (
34
+ id INT AUTO_INCREMENT PRIMARY KEY,
35
+ name VARCHAR(255) NOT NULL,
36
+ email VARCHAR(255) NOT NULL UNIQUE,
37
+ password VARCHAR(255) NOT NULL,
38
+ role ENUM('receptionist', 'doctor') NOT NULL,
39
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
40
+ )
41
+ `);
42
+
43
+ await bootstrap.query(`
44
+ CREATE TABLE IF NOT EXISTS patients (
45
+ id INT AUTO_INCREMENT PRIMARY KEY,
46
+ full_name VARCHAR(255) NOT NULL,
47
+ gender ENUM('Male', 'Female', 'Other') NOT NULL,
48
+ date_of_birth DATE NOT NULL,
49
+ phone VARCHAR(50) NOT NULL,
50
+ address TEXT NOT NULL,
51
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
52
+ )
53
+ `);
54
+
55
+ await bootstrap.query(`
56
+ CREATE TABLE IF NOT EXISTS appointments (
57
+ id INT AUTO_INCREMENT PRIMARY KEY,
58
+ patient_id INT NOT NULL,
59
+ doctor_id INT NOT NULL,
60
+ appointment_date DATE NOT NULL,
61
+ appointment_time TIME NOT NULL,
62
+ status ENUM('scheduled', 'completed', 'cancelled') NOT NULL DEFAULT 'scheduled',
63
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
64
+ FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE,
65
+ FOREIGN KEY (doctor_id) REFERENCES users(id) ON DELETE RESTRICT
66
+ )
67
+ `);
68
+
69
+ await bootstrap.query(`
70
+ CREATE TABLE IF NOT EXISTS medical_reports (
71
+ id INT AUTO_INCREMENT PRIMARY KEY,
72
+ appointment_id INT NOT NULL UNIQUE,
73
+ patient_id INT NOT NULL,
74
+ doctor_id INT NOT NULL,
75
+ diagnosis TEXT NOT NULL,
76
+ prescription TEXT NOT NULL,
77
+ report_date DATE NOT NULL,
78
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
79
+ FOREIGN KEY (appointment_id) REFERENCES appointments(id) ON DELETE CASCADE,
80
+ FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE,
81
+ FOREIGN KEY (doctor_id) REFERENCES users(id) ON DELETE RESTRICT
82
+ )
83
+ `);
84
+
85
+ await bootstrap.end();
86
+ getPool();
87
+ console.log('MySQL connected and schema ready');
88
+ } catch (err) {
89
+ if (bootstrap) await bootstrap.end().catch(() => {});
90
+ const hint =
91
+ 'Ensure MySQL is running and DB_USER/DB_PASSWORD in backend/.env are correct.';
92
+ throw new Error(`${err.message || err.code || 'Database connection failed'}. ${hint}`);
93
+ }
94
+ }
95
+
96
+ module.exports = { getPool, initDatabase };
@@ -0,0 +1,164 @@
1
+ const { getPool } = require('../config/db');
2
+
3
+ function formatAppointment(row) {
4
+ return {
5
+ id: row.id,
6
+ patientId: row.patient_id,
7
+ doctorId: row.doctor_id,
8
+ patientName: row.patient_name,
9
+ doctorName: row.doctor_name,
10
+ appointmentDate: row.appointment_date,
11
+ appointmentTime: row.appointment_time,
12
+ status: row.status,
13
+ createdAt: row.created_at,
14
+ hasReport: Boolean(row.has_report),
15
+ };
16
+ }
17
+
18
+ const appointmentSelect = `
19
+ SELECT a.*,
20
+ p.full_name AS patient_name,
21
+ u.name AS doctor_name,
22
+ (SELECT COUNT(*) FROM medical_reports mr WHERE mr.appointment_id = a.id) AS has_report
23
+ FROM appointments a
24
+ JOIN patients p ON p.id = a.patient_id
25
+ JOIN users u ON u.id = a.doctor_id
26
+ `;
27
+
28
+ exports.getAppointments = async (req, res) => {
29
+ try {
30
+ const pool = getPool();
31
+ let query = `${appointmentSelect}`;
32
+ const params = [];
33
+
34
+ if (req.user.role === 'doctor') {
35
+ query += ' WHERE a.doctor_id = ?';
36
+ params.push(req.user.id);
37
+ }
38
+
39
+ query += ' ORDER BY a.appointment_date DESC, a.appointment_time DESC';
40
+
41
+ const [rows] = await pool.query(query, params);
42
+ res.json({ appointments: rows.map(formatAppointment) });
43
+ } catch (err) {
44
+ res.status(500).json({ message: err.message });
45
+ }
46
+ };
47
+
48
+ exports.getAppointment = async (req, res) => {
49
+ try {
50
+ const pool = getPool();
51
+ const [rows] = await pool.query(`${appointmentSelect} WHERE a.id = ?`, [req.params.id]);
52
+
53
+ if (!rows.length) {
54
+ return res.status(404).json({ message: 'Appointment not found' });
55
+ }
56
+
57
+ if (req.user.role === 'doctor' && rows[0].doctor_id !== req.user.id) {
58
+ return res.status(403).json({ message: 'Access denied' });
59
+ }
60
+
61
+ res.json({ appointment: formatAppointment(rows[0]) });
62
+ } catch (err) {
63
+ res.status(500).json({ message: err.message });
64
+ }
65
+ };
66
+
67
+ exports.createAppointment = async (req, res) => {
68
+ try {
69
+ const { patientId, doctorId, appointmentDate, appointmentTime, status } = req.body;
70
+
71
+ if (!patientId || !doctorId || !appointmentDate || !appointmentTime) {
72
+ return res.status(400).json({ message: 'Patient, doctor, date, and time are required' });
73
+ }
74
+
75
+ const pool = getPool();
76
+
77
+ const [doctor] = await pool.query('SELECT id FROM users WHERE id = ? AND role = ?', [
78
+ doctorId,
79
+ 'doctor',
80
+ ]);
81
+ if (!doctor.length) {
82
+ return res.status(400).json({ message: 'Invalid doctor selected' });
83
+ }
84
+
85
+ const [result] = await pool.query(
86
+ `INSERT INTO appointments (patient_id, doctor_id, appointment_date, appointment_time, status)
87
+ VALUES (?, ?, ?, ?, ?)`,
88
+ [patientId, doctorId, appointmentDate, appointmentTime, status || 'scheduled'],
89
+ );
90
+
91
+ const [rows] = await pool.query(`${appointmentSelect} WHERE a.id = ?`, [result.insertId]);
92
+ res.status(201).json({ appointment: formatAppointment(rows[0]) });
93
+ } catch (err) {
94
+ res.status(500).json({ message: err.message });
95
+ }
96
+ };
97
+
98
+ exports.updateAppointment = async (req, res) => {
99
+ try {
100
+ const { patientId, doctorId, appointmentDate, appointmentTime, status } = req.body;
101
+ const pool = getPool();
102
+
103
+ const [existing] = await pool.query('SELECT id FROM appointments WHERE id = ?', [req.params.id]);
104
+ if (!existing.length) {
105
+ return res.status(404).json({ message: 'Appointment not found' });
106
+ }
107
+
108
+ if (doctorId) {
109
+ const [doctor] = await pool.query('SELECT id FROM users WHERE id = ? AND role = ?', [
110
+ doctorId,
111
+ 'doctor',
112
+ ]);
113
+ if (!doctor.length) {
114
+ return res.status(400).json({ message: 'Invalid doctor selected' });
115
+ }
116
+ }
117
+
118
+ await pool.query(
119
+ `UPDATE appointments
120
+ SET patient_id = ?, doctor_id = ?, appointment_date = ?, appointment_time = ?, status = ?
121
+ WHERE id = ?`,
122
+ [patientId, doctorId, appointmentDate, appointmentTime, status, req.params.id],
123
+ );
124
+
125
+ const [rows] = await pool.query(`${appointmentSelect} WHERE a.id = ?`, [req.params.id]);
126
+ res.json({ appointment: formatAppointment(rows[0]) });
127
+ } catch (err) {
128
+ res.status(500).json({ message: err.message });
129
+ }
130
+ };
131
+
132
+ exports.cancelAppointment = async (req, res) => {
133
+ try {
134
+ const pool = getPool();
135
+ const [result] = await pool.query(
136
+ "UPDATE appointments SET status = 'cancelled' WHERE id = ?",
137
+ [req.params.id],
138
+ );
139
+
140
+ if (result.affectedRows === 0) {
141
+ return res.status(404).json({ message: 'Appointment not found' });
142
+ }
143
+
144
+ const [rows] = await pool.query(`${appointmentSelect} WHERE a.id = ?`, [req.params.id]);
145
+ res.json({ appointment: formatAppointment(rows[0]) });
146
+ } catch (err) {
147
+ res.status(500).json({ message: err.message });
148
+ }
149
+ };
150
+
151
+ exports.deleteAppointment = async (req, res) => {
152
+ try {
153
+ const pool = getPool();
154
+ const [result] = await pool.query('DELETE FROM appointments WHERE id = ?', [req.params.id]);
155
+
156
+ if (result.affectedRows === 0) {
157
+ return res.status(404).json({ message: 'Appointment not found' });
158
+ }
159
+
160
+ res.json({ message: 'Appointment deleted' });
161
+ } catch (err) {
162
+ res.status(500).json({ message: err.message });
163
+ }
164
+ };
@@ -0,0 +1,106 @@
1
+ const bcrypt = require('bcrypt');
2
+ const jwt = require('jsonwebtoken');
3
+ const { getPool } = require('../config/db');
4
+
5
+ function createToken(userId) {
6
+ return jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '7d' });
7
+ }
8
+
9
+ function formatUser(user) {
10
+ return {
11
+ id: user.id,
12
+ name: user.name,
13
+ email: user.email,
14
+ role: user.role,
15
+ };
16
+ }
17
+
18
+ exports.register = async (req, res) => {
19
+ try {
20
+ const { name, email, password, role } = req.body;
21
+
22
+ if (!name || !email || !password || !role) {
23
+ return res.status(400).json({ message: 'Name, email, password, and role are required' });
24
+ }
25
+
26
+ if (!['receptionist', 'doctor'].includes(role)) {
27
+ return res.status(400).json({ message: 'Role must be receptionist or doctor' });
28
+ }
29
+
30
+ if (password.length < 6) {
31
+ return res.status(400).json({ message: 'Password must be at least 6 characters' });
32
+ }
33
+
34
+ const pool = getPool();
35
+ const [existing] = await pool.query('SELECT id FROM users WHERE email = ?', [
36
+ email.toLowerCase(),
37
+ ]);
38
+
39
+ if (existing.length) {
40
+ return res.status(409).json({ message: 'Email already registered' });
41
+ }
42
+
43
+ const hashedPassword = await bcrypt.hash(password, 10);
44
+ const [result] = await pool.query(
45
+ 'INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)',
46
+ [name, email.toLowerCase(), hashedPassword, role],
47
+ );
48
+
49
+ const user = { id: result.insertId, name, email: email.toLowerCase(), role };
50
+ const token = createToken(user.id);
51
+
52
+ res.status(201).json({ token, user: formatUser(user) });
53
+ } catch (err) {
54
+ res.status(500).json({ message: err.message });
55
+ }
56
+ };
57
+
58
+ exports.login = async (req, res) => {
59
+ try {
60
+ const { email, password } = req.body;
61
+
62
+ if (!email || !password) {
63
+ return res.status(400).json({ message: 'Email and password are required' });
64
+ }
65
+
66
+ const pool = getPool();
67
+ const [rows] = await pool.query('SELECT * FROM users WHERE email = ?', [email.toLowerCase()]);
68
+
69
+ if (!rows.length) {
70
+ return res.status(401).json({ message: 'Invalid email or password' });
71
+ }
72
+
73
+ const user = rows[0];
74
+ const match = await bcrypt.compare(password, user.password);
75
+
76
+ if (!match) {
77
+ return res.status(401).json({ message: 'Invalid email or password' });
78
+ }
79
+
80
+ const token = createToken(user.id);
81
+ res.json({ token, user: formatUser(user) });
82
+ } catch (err) {
83
+ res.status(500).json({ message: err.message });
84
+ }
85
+ };
86
+
87
+ exports.getMe = async (req, res) => {
88
+ try {
89
+ res.json({ user: formatUser(req.user) });
90
+ } catch (err) {
91
+ res.status(500).json({ message: err.message });
92
+ }
93
+ };
94
+
95
+ exports.getDoctors = async (req, res) => {
96
+ try {
97
+ const pool = getPool();
98
+ const [doctors] = await pool.query(
99
+ 'SELECT id, name, email FROM users WHERE role = ? ORDER BY name',
100
+ ['doctor'],
101
+ );
102
+ res.json({ doctors });
103
+ } catch (err) {
104
+ res.status(500).json({ message: err.message });
105
+ }
106
+ };
@@ -0,0 +1,72 @@
1
+ const { getPool } = require('../config/db');
2
+
3
+ exports.generateReport = async (req, res) => {
4
+ try {
5
+ const { startDate, endDate } = req.query;
6
+
7
+ if (!startDate || !endDate) {
8
+ return res.status(400).json({ message: 'Start date and end date are required' });
9
+ }
10
+
11
+ const pool = getPool();
12
+
13
+ const [appointments] = await pool.query(
14
+ `SELECT a.id, a.appointment_date, a.appointment_time, a.status,
15
+ p.full_name AS patient_name,
16
+ u.name AS doctor_name
17
+ FROM appointments a
18
+ JOIN patients p ON p.id = a.patient_id
19
+ JOIN users u ON u.id = a.doctor_id
20
+ WHERE a.appointment_date BETWEEN ? AND ?
21
+ ORDER BY a.appointment_date, a.appointment_time`,
22
+ [startDate, endDate],
23
+ );
24
+
25
+ const [stats] = await pool.query(
26
+ `SELECT
27
+ COUNT(*) AS totalAppointments,
28
+ SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS totalCompleted,
29
+ SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS totalCancelled
30
+ FROM appointments
31
+ WHERE appointment_date BETWEEN ? AND ?`,
32
+ [startDate, endDate],
33
+ );
34
+
35
+ const [attended] = await pool.query(
36
+ `SELECT DISTINCT p.id, p.full_name AS patient_name, p.phone
37
+ FROM appointments a
38
+ JOIN patients p ON p.id = a.patient_id
39
+ WHERE a.status = 'completed' AND a.appointment_date BETWEEN ? AND ?
40
+ ORDER BY p.full_name`,
41
+ [startDate, endDate],
42
+ );
43
+
44
+ const [medicalReports] = await pool.query(
45
+ `SELECT mr.id, mr.diagnosis, mr.prescription, mr.report_date,
46
+ p.full_name AS patient_name,
47
+ u.name AS doctor_name
48
+ FROM medical_reports mr
49
+ JOIN patients p ON p.id = mr.patient_id
50
+ JOIN users u ON u.id = mr.doctor_id
51
+ WHERE mr.report_date BETWEEN ? AND ?
52
+ ORDER BY mr.report_date DESC`,
53
+ [startDate, endDate],
54
+ );
55
+
56
+ res.json({
57
+ startDate,
58
+ endDate,
59
+ summary: {
60
+ totalAppointments: Number(stats[0].totalAppointments),
61
+ totalCompleted: Number(stats[0].totalCompleted),
62
+ totalCancelled: Number(stats[0].totalCancelled),
63
+ patientsAttended: attended.length,
64
+ },
65
+ appointments,
66
+ patientsAttended: attended,
67
+ medicalReports,
68
+ });
69
+ } catch (err) {
70
+ res.status(500).json({ message: err.message });
71
+ }
72
+ };
@@ -0,0 +1,105 @@
1
+ const { getPool } = require('../config/db');
2
+
3
+ function formatReport(row) {
4
+ return {
5
+ id: row.id,
6
+ appointmentId: row.appointment_id,
7
+ patientId: row.patient_id,
8
+ doctorId: row.doctor_id,
9
+ patientName: row.patient_name,
10
+ doctorName: row.doctor_name,
11
+ diagnosis: row.diagnosis,
12
+ prescription: row.prescription,
13
+ reportDate: row.report_date,
14
+ createdAt: row.created_at,
15
+ };
16
+ }
17
+
18
+ const reportSelect = `
19
+ SELECT mr.*,
20
+ p.full_name AS patient_name,
21
+ u.name AS doctor_name
22
+ FROM medical_reports mr
23
+ JOIN patients p ON p.id = mr.patient_id
24
+ JOIN users u ON u.id = mr.doctor_id
25
+ `;
26
+
27
+ exports.getMedicalReports = async (req, res) => {
28
+ try {
29
+ const pool = getPool();
30
+ let query = `${reportSelect}`;
31
+ const params = [];
32
+
33
+ if (req.user.role === 'doctor') {
34
+ query += ' WHERE mr.doctor_id = ?';
35
+ params.push(req.user.id);
36
+ }
37
+
38
+ query += ' ORDER BY mr.report_date DESC';
39
+
40
+ const [rows] = await pool.query(query, params);
41
+ res.json({ reports: rows.map(formatReport) });
42
+ } catch (err) {
43
+ res.status(500).json({ message: err.message });
44
+ }
45
+ };
46
+
47
+ exports.createMedicalReport = async (req, res) => {
48
+ try {
49
+ const { appointmentId, diagnosis, prescription, reportDate } = req.body;
50
+
51
+ if (!appointmentId || !diagnosis || !prescription) {
52
+ return res.status(400).json({ message: 'Appointment, diagnosis, and prescription are required' });
53
+ }
54
+
55
+ const pool = getPool();
56
+ const [appointments] = await pool.query(
57
+ 'SELECT * FROM appointments WHERE id = ?',
58
+ [appointmentId],
59
+ );
60
+
61
+ if (!appointments.length) {
62
+ return res.status(404).json({ message: 'Appointment not found' });
63
+ }
64
+
65
+ const appointment = appointments[0];
66
+
67
+ if (req.user.role === 'doctor' && appointment.doctor_id !== req.user.id) {
68
+ return res.status(403).json({ message: 'You can only report on your assigned appointments' });
69
+ }
70
+
71
+ if (appointment.status === 'cancelled') {
72
+ return res.status(400).json({ message: 'Cannot create report for cancelled appointment' });
73
+ }
74
+
75
+ const [existingReport] = await pool.query(
76
+ 'SELECT id FROM medical_reports WHERE appointment_id = ?',
77
+ [appointmentId],
78
+ );
79
+ if (existingReport.length) {
80
+ return res.status(409).json({ message: 'Medical report already exists for this appointment' });
81
+ }
82
+
83
+ const finalReportDate = reportDate || new Date().toISOString().slice(0, 10);
84
+
85
+ const [result] = await pool.query(
86
+ `INSERT INTO medical_reports (appointment_id, patient_id, doctor_id, diagnosis, prescription, report_date)
87
+ VALUES (?, ?, ?, ?, ?, ?)`,
88
+ [
89
+ appointmentId,
90
+ appointment.patient_id,
91
+ appointment.doctor_id,
92
+ diagnosis,
93
+ prescription,
94
+ finalReportDate,
95
+ ],
96
+ );
97
+
98
+ await pool.query("UPDATE appointments SET status = 'completed' WHERE id = ?", [appointmentId]);
99
+
100
+ const [rows] = await pool.query(`${reportSelect} WHERE mr.id = ?`, [result.insertId]);
101
+ res.status(201).json({ report: formatReport(rows[0]) });
102
+ } catch (err) {
103
+ res.status(500).json({ message: err.message });
104
+ }
105
+ };