Skip to content

Commit dd5e838

Browse files
Merge pull request #79 from BUMETCS673/search-function-feature
Add Search Function for Prescription
2 parents 4133ea6 + 5b60215 commit dd5e838

File tree

10 files changed

+343
-6
lines changed

10 files changed

+343
-6
lines changed

code/docker-compose-dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ services:
1616
- ./mymedic:/usr/src/mymedic
1717

1818
# Command to run the Django development server
19-
command: python manage.py runserver 0.0.0:8000
19+
command: sh -c "python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0:8000"
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"user_id": 1,
4+
"doctor": "Dr. Smith",
5+
"prescription": "Amoxicillin",
6+
"dosage": "500mg",
7+
"date": "2025-05-22",
8+
"notes": "Take with food to avoid stomach upset. Prescribed for sinus infection."
9+
},
10+
{
11+
"user_id": 1,
12+
"doctor": "Dr. Adams",
13+
"prescription": "Ibuprofen",
14+
"dosage": "200mg",
15+
"date": "2025-06-01",
16+
"notes": "Take every 6 hours for pain relief. Do not exceed 1200mg per day."
17+
},
18+
{
19+
"user_id": 1,
20+
"doctor": "Dr. Smith",
21+
"prescription": "Paracetamol",
22+
"dosage": "650mg",
23+
"date": "2025-06-05",
24+
"notes": "For headaches and mild fever. Safe with other medications."
25+
},
26+
{
27+
"user_id": 1,
28+
"doctor": "Dr. Gomez",
29+
"prescription": "Azithromycin",
30+
"dosage": "250mg",
31+
"date": "2025-06-03",
32+
"notes": "5-day course for respiratory infection. Avoid antacids during treatment."
33+
},
34+
{
35+
"user_id": 1,
36+
"doctor": "Dr. Patel",
37+
"prescription": "Lisinopril",
38+
"dosage": "10mg",
39+
"date": "2025-05-28",
40+
"notes": "Monitor blood pressure. Report any dizziness or swelling."
41+
},
42+
{
43+
"user_id": 1,
44+
"doctor": "Dr. Nguyen",
45+
"prescription": "Metformin",
46+
"dosage": "1000mg",
47+
"date": "2025-06-06",
48+
"notes": "For blood sugar control. Take with meals to reduce GI upset."
49+
}
50+
]

code/mymedic/static/css/dashboard.css

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,72 @@
33
padding: 2rem 0;
44
}
55

6+
/* Search Section */
7+
.search-section {
8+
margin-bottom: 1rem;
9+
}
10+
11+
.search-card {
12+
background: rgba(255, 255, 255, 0.95);
13+
border-radius: 15px;
14+
padding: 2rem;
15+
padding-top: 1rem;
16+
padding-bottom: 0.25rem;
17+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
18+
backdrop-filter: blur(10px);
19+
}
20+
21+
.search-title {
22+
color: #343a40;
23+
font-size: 1.5rem;
24+
font-weight: 700;
25+
margin-bottom: 0.5rem;
26+
}
27+
28+
.search-row {
29+
display: flex;
30+
align-items: center;
31+
gap: 10px;
32+
margin-top: 10px;
33+
}
34+
35+
#search-input {
36+
flex: 1;
37+
padding: 10px;
38+
font-size: 16px;
39+
border: 1px solid #ccc;
40+
border-radius: 5px;
41+
box-sizing: border-box;
42+
}
43+
44+
.btn {
45+
border-radius: 5px !important;
46+
font-weight: 600;
47+
}
48+
49+
.btn-success {
50+
background-color: #20b2aa !important;
51+
border-color: #20b2aa !important;
52+
}
53+
54+
.btn-success:hover:not(:disabled) {
55+
background-color: #1a9999 !important;
56+
border-color: #1a9999 !important;
57+
box-shadow: 0 5px 15px rgba(32, 178, 170, 0.3);
58+
}
59+
60+
.btn:disabled {
61+
opacity: 0.6;
62+
cursor: not-allowed;
63+
}
64+
65+
#search-results {
66+
margin-top: 20px;
67+
font-size: 15px;
68+
color: #555;
69+
text-align: center;
70+
}
71+
672
/* Welcome Section */
773
.welcome-section {
874
margin-bottom: 2rem;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
function logout() {
2+
const logoutButton = document.querySelector('button[onclick="logout()"]');
3+
4+
if (logoutButton) {
5+
logoutButton.disabled = true;
6+
logoutButton.innerText = "Logging out...";
7+
}
8+
9+
localStorage.removeItem("access");
10+
11+
setTimeout(() => {
12+
window.location.href = "/login/";
13+
}, 500);
14+
}
15+
16+
document.addEventListener('DOMContentLoaded', function() {
17+
18+
const scheduleButtons = document.querySelectorAll('.action-card .btn');
19+
if (scheduleButtons[0]) {
20+
scheduleButtons[0].addEventListener('click', function() {
21+
alert('Schedule appointment feature coming soon!');
22+
});
23+
}
24+
25+
if (scheduleButtons[1]) {
26+
scheduleButtons[1].addEventListener('click', function() {
27+
alert('View records feature coming soon!');
28+
});
29+
}
30+
31+
if (scheduleButtons[2]) {
32+
scheduleButtons[2].addEventListener('click', function() {
33+
alert('Manage prescriptions feature coming soon!');
34+
});
35+
}
36+
37+
const statCards = document.querySelectorAll('.stat-card');
38+
statCards.forEach(card => {
39+
card.addEventListener('click', function() {
40+
41+
const statLabel = this.querySelector('.stat-label').textContent;
42+
alert(`${statLabel} details coming soon!`);
43+
});
44+
45+
card.style.cursor = 'pointer';
46+
});
47+
48+
const actionCards = document.querySelectorAll('.action-card');
49+
actionCards.forEach(card => {
50+
card.addEventListener('mouseenter', function() {
51+
this.style.transform = 'translateY(-5px)';
52+
});
53+
54+
card.addEventListener('mouseleave', function() {
55+
this.style.transform = 'translateY(0)';
56+
});
57+
});
58+
});
59+
60+
async function validateToken() {
61+
const token = localStorage.getItem("access");
62+
if (!token) return false;
63+
64+
try {
65+
const res = await fetch("/api/validate-token/", {
66+
method: "GET",
67+
headers: {
68+
"Authorization": `Bearer ${token}`,
69+
"Content-Type": "application/json"
70+
}
71+
});
72+
73+
if (!res.ok) {
74+
localStorage.removeItem("access");
75+
window.location.href = "/login/";
76+
return false;
77+
}
78+
79+
return true;
80+
} catch (error) {
81+
console.log("Token validation failed:", error);
82+
return true;
83+
}
84+
}

code/mymedic/static/js/search.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
async function searchRecords() {
2+
const input = document.getElementById("search-input").value.trim();
3+
const resultsDiv = document.getElementById("search-results");
4+
5+
resultsDiv.innerHTML = "";
6+
7+
if (!input) {
8+
resultsDiv.innerHTML = `<p class="text-danger">⚠️ Please enter a search term.</p>`;
9+
return;
10+
}
11+
12+
try {
13+
const response = await fetch(`/api/search/?q=${encodeURIComponent(input)}`);
14+
const data = await response.json();
15+
16+
if (data.length === 0) {
17+
resultsDiv.innerHTML = `<p>No results found for "<strong>${input}</strong>".</p>`;
18+
return;
19+
}
20+
21+
/**
22+
@ai-generated
23+
Tool: ChatGPT (OpenAI)
24+
Prompt: "Render search results as an HTML list from filtered JSON data"
25+
Generated on: 2025-06-08
26+
Modified by: Mengliang Tan
27+
Modifications: Display doctor, prescription, date, dosage, and notes in formatted HTML
28+
29+
Verified: ✅ Tested via frontend display
30+
*/
31+
let html = `<ul class="list-group mt-3">`;
32+
data.forEach(record => {
33+
html += `<li class="list-group-item">
34+
<strong>Doctor:</strong> ${record.doctor} <br>
35+
<strong>Prescription:</strong> ${record.prescription} <br>
36+
<strong>Date:</strong> ${record.date} <br>
37+
<strong>Dosage:</strong> ${record.dosage} <br>
38+
<strong>Notes:</strong> ${record.notes}
39+
</li>`;
40+
});
41+
html += `</ul>`;
42+
resultsDiv.innerHTML = html;
43+
44+
} catch (err) {
45+
resultsDiv.innerHTML = `<p class="text-danger">❌ Error searching records. Please try again later.</p>`;
46+
}
47+
}
48+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 5.2.1 on 2025-06-15 14:46
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('users', '0002_appointment'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='Prescription',
16+
fields=[
17+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('doctor', models.CharField(max_length=100)),
19+
('prescription', models.CharField(max_length=255)),
20+
('date', models.DateField(blank=True, null=True)),
21+
('dosage', models.CharField(blank=True, max_length=100, null=True)),
22+
('notes', models.TextField(blank=True, null=True)),
23+
('patient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.patient')),
24+
],
25+
),
26+
]

code/mymedic/users/models.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class Patient(models.Model):
2525

2626
def __str__(self):
2727
return f"{self.first_name} {self.last_name} ({self.email})"
28-
2928

3029
class Appointment(models.Model):
3130
user = models.ForeignKey(User, on_delete=models.CASCADE)
@@ -35,4 +34,15 @@ class Appointment(models.Model):
3534
reason = models.CharField(max_length=200)
3635

3736
def __str__(self):
38-
return f"{self.date} - {self.patient_name} with {self.doctor_name}"
37+
return f"{self.date} - {self.patient_name} with {self.doctor_name}"
38+
39+
class Prescription(models.Model):
40+
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
41+
doctor = models.CharField(max_length=100)
42+
prescription = models.CharField(max_length=255)
43+
date = models.DateField(blank=True, null=True)
44+
dosage = models.CharField(max_length=100, blank=True, null=True)
45+
notes = models.TextField(blank=True, null=True)
46+
47+
def __str__(self):
48+
return f"{self.doctor} - {self.prescription}"

code/mymedic/users/templates/users/dashboard.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ <h1 class="header-title">MyMedic</h1>
3333
<!-- Main Content -->
3434
<main class="dashboard-main">
3535
<div class="container-fluid">
36+
<!-- Search -->
37+
<div class="search-section animate__animated animate__fadeIn">
38+
<div class="row">
39+
<div class="col-12">
40+
<div class="search-card">
41+
<h2 class="search-title">Search Records</h2>
42+
<div class="search-row">
43+
<input type="text" id="search-input" placeholder="Enter doctor or prescription" />
44+
<button id="search-btn" class="btn btn-success" onclick="searchRecords()">Search</button>
45+
</div>
46+
<div id="search-results" class="mt-3"></div>
47+
</div>
48+
</div>
49+
</div>
50+
</div>
3651

3752
<!-- Welcome Section -->
3853
<div class="welcome-section animate__animated animate__fadeIn">
@@ -195,4 +210,6 @@ <h5 class="activity-title">Prescription Refilled</h5>
195210

196211
</footer>
197212
</main>
213+
<script src="{% static 'js/dashboard.js' %}"></script>
214+
<script src="{% static 'js/search.js' %}"></script>
198215
</html>

code/mymedic/users/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
path('', views.mlogin, name=''), # Default route to login
1111
path('cancel/<int:appointment_id>/', views.cancel_appointment, name='cancel_appointment'),
1212
path("privacy/", views.privacy_policy, name="privacy_policy"),
13-
13+
path('api/search/', views.search_records, name='search'),
1414
]

0 commit comments

Comments
 (0)