feat: add doctor and specialty management modules with full CRUD support and database seeding
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import re
|
||||
|
||||
with open('src/main/resources/templates/doctor.html', 'r', encoding='utf-8') as f:
|
||||
html = f.read()
|
||||
|
||||
# 1. Extract all unique specialties
|
||||
specialties = set()
|
||||
matches = re.finditer(r'<div class="text-primary-300 label-3 !leading-\[150%\]">(.*?)</div>', html)
|
||||
for m in matches:
|
||||
spec = m.group(1).strip()
|
||||
if spec and spec != "BS CKII." and spec != "TS BS." and spec != "ThS.":
|
||||
# Actually, let's be careful. The title (BS CKII) has the same class!
|
||||
# Let's just look at the ones that start with "Khoa" or "Đơn vị" or just filter titles
|
||||
if not spec.startswith("BS ") and not spec.startswith("TS ") and not spec.startswith("ThS.") and not spec.startswith("PGS ") and not spec.startswith("GS "):
|
||||
specialties.add(spec)
|
||||
|
||||
specialties = sorted(list(specialties))
|
||||
print(f"Found {len(specialties)} specialties.")
|
||||
|
||||
# 2. Add IDs to the buttons
|
||||
html = html.replace(
|
||||
'aria-haspopup="listbox" aria-expanded="false"><span class="text-gray-900 lg:group-hover:text-primary-600 lg:duration-150">--Theo chuyên khoa--</span>',
|
||||
'id="btn-specialty" aria-haspopup="listbox" aria-expanded="false" onclick="toggleDropdown(\'list-specialty\')"><span id="text-specialty" class="text-gray-900 lg:group-hover:text-primary-600 lg:duration-150">--Theo chuyên khoa--</span>'
|
||||
)
|
||||
|
||||
html = html.replace(
|
||||
'aria-haspopup="listbox" aria-expanded="false"><span class="text-gray-900 lg:group-hover:text-primary-600 lg:duration-150">--Theo giới tính--</span>',
|
||||
'id="btn-gender" aria-haspopup="listbox" aria-expanded="false" onclick="toggleDropdown(\'list-gender\')"><span id="text-gender" class="text-gray-900 lg:group-hover:text-primary-600 lg:duration-150">--Theo giới tính--</span>'
|
||||
)
|
||||
|
||||
# 3. Add IDs to the search input
|
||||
html = re.sub(
|
||||
r'<input(.*?)placeholder="Tìm kiếm bác sĩ..."',
|
||||
r'<input id="search-input" onkeyup="filterDoctors()"\1placeholder="Tìm kiếm bác sĩ..."',
|
||||
html
|
||||
)
|
||||
|
||||
# 4. Generate the dropdown lists HTML
|
||||
spec_list_html = '<ul id="list-specialty" class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" style="display: none;">'
|
||||
spec_list_html += '<li class="text-gray-900 cursor-pointer select-none relative py-2 pl-3 pr-9 hover:bg-gray-100" onclick="selectSpecialty(\'\')">--Tất cả chuyên khoa--</li>'
|
||||
for spec in specialties:
|
||||
spec_list_html += f'<li class="text-gray-900 cursor-pointer select-none relative py-2 pl-3 pr-9 hover:bg-gray-100" onclick="selectSpecialty(\'{spec}\')">{spec}</li>'
|
||||
spec_list_html += '</ul>'
|
||||
|
||||
gender_list_html = '<ul id="list-gender" class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" style="display: none;">'
|
||||
gender_list_html += '<li class="text-gray-900 cursor-pointer select-none relative py-2 pl-3 pr-9 hover:bg-gray-100" onclick="selectGender(\'\')">--Tất cả giới tính--</li>'
|
||||
gender_list_html += '<li class="text-gray-900 cursor-pointer select-none relative py-2 pl-3 pr-9 hover:bg-gray-100" onclick="selectGender(\'Nam\')">Nam</li>'
|
||||
gender_list_html += '<li class="text-gray-900 cursor-pointer select-none relative py-2 pl-3 pr-9 hover:bg-gray-100" onclick="selectGender(\'Nữ\')">Nữ</li>'
|
||||
gender_list_html += '</ul>'
|
||||
|
||||
# 5. Inject the lists after the buttons
|
||||
# Find the div containing the specialty button
|
||||
spec_btn_end = html.find('</button>', html.find('id="btn-specialty"')) + 9
|
||||
html = html[:spec_btn_end] + spec_list_html + html[spec_btn_end:]
|
||||
|
||||
gender_btn_end = html.find('</button>', html.find('id="btn-gender"')) + 9
|
||||
html = html[:gender_btn_end] + gender_list_html + html[gender_btn_end:]
|
||||
|
||||
# Add an ID to the doctor cards container
|
||||
html = html.replace(
|
||||
'<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 xl:gap-4 md:gap-3 gap-2">',
|
||||
'<div id="doctors-grid" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 xl:gap-4 md:gap-3 gap-2">'
|
||||
)
|
||||
|
||||
# 6. Inject the JavaScript at the end of the file
|
||||
js_code = """
|
||||
<script>
|
||||
let currentSpecialty = '';
|
||||
let currentGender = '';
|
||||
|
||||
function toggleDropdown(id) {
|
||||
const el = document.getElementById(id);
|
||||
el.style.display = el.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function selectSpecialty(spec) {
|
||||
currentSpecialty = spec;
|
||||
document.getElementById('text-specialty').innerText = spec || '--Theo chuyên khoa--';
|
||||
document.getElementById('list-specialty').style.display = 'none';
|
||||
filterDoctors();
|
||||
}
|
||||
|
||||
function selectGender(gender) {
|
||||
currentGender = gender;
|
||||
document.getElementById('text-gender').innerText = gender || '--Theo giới tính--';
|
||||
document.getElementById('list-gender').style.display = 'none';
|
||||
filterDoctors();
|
||||
}
|
||||
|
||||
function filterDoctors() {
|
||||
const searchText = document.getElementById('search-input').value.toLowerCase();
|
||||
const cards = document.querySelectorAll('#doctors-grid > div');
|
||||
|
||||
cards.forEach(card => {
|
||||
const textContent = card.innerText || '';
|
||||
const textLower = textContent.toLowerCase();
|
||||
|
||||
let matchSearch = searchText === '' || textLower.includes(searchText);
|
||||
let matchSpecialty = currentSpecialty === '' || textContent.includes(currentSpecialty);
|
||||
// Since gender isn't clearly marked in the text, we'll just ignore it for now or do a rudimentary check if 'Nam'/'Nữ' is somehow present.
|
||||
// For now, gender filter is a UI dummy unless data provides it.
|
||||
|
||||
if (matchSearch && matchSpecialty) {
|
||||
card.style.display = '';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Close dropdowns when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
const specBtn = document.getElementById('btn-specialty');
|
||||
const specList = document.getElementById('list-specialty');
|
||||
if (specBtn && !specBtn.contains(event.target) && !specList.contains(event.target)) {
|
||||
specList.style.display = 'none';
|
||||
}
|
||||
|
||||
const genderBtn = document.getElementById('btn-gender');
|
||||
const genderList = document.getElementById('list-gender');
|
||||
if (genderBtn && !genderBtn.contains(event.target) && !genderList.contains(event.target)) {
|
||||
genderList.style.display = 'none';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
"""
|
||||
|
||||
html = html.replace('</div>\n</body>', js_code + '\n</div>\n</body>')
|
||||
|
||||
with open('src/main/resources/templates/doctor.html', 'w', encoding='utf-8') as f:
|
||||
f.write(html)
|
||||
|
||||
print("Injected JS and dropdown HTML")
|
||||
Reference in New Issue
Block a user